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,50 @@
|
|
|
1
|
+
module RTanque
|
|
2
|
+
class Bot
|
|
3
|
+
# Commands the associated {RTanque::Bot}. This class should be inherited from and {NAME} and {#tick!} overridden.
|
|
4
|
+
#
|
|
5
|
+
# See {RTanque::Bot::BrainHelper} for a useful mixin
|
|
6
|
+
#
|
|
7
|
+
# Sample bots:
|
|
8
|
+
#
|
|
9
|
+
# * {file:sample_bots/seek_and_destroy.rb SeekAndDestroy}
|
|
10
|
+
# * {file:sample_bots/camper.rb Camper}
|
|
11
|
+
# * {file:sample_bots/keyboard.rb Keyboard} Special bot controlled by the keyboard
|
|
12
|
+
#
|
|
13
|
+
class Brain
|
|
14
|
+
# Bot's display name
|
|
15
|
+
# @!parse NAME = 'bot name'
|
|
16
|
+
|
|
17
|
+
# @!attribute [r] sensors
|
|
18
|
+
# @return [RTanque::Bot::Sensors]
|
|
19
|
+
# @!attribute [r] command
|
|
20
|
+
# @return [RTanque::Bot::Command]
|
|
21
|
+
attr_accessor :sensors, :command
|
|
22
|
+
# @return [RTanque::Arena]
|
|
23
|
+
attr_reader :arena
|
|
24
|
+
|
|
25
|
+
# @!visibility private
|
|
26
|
+
def initialize(arena)
|
|
27
|
+
@arena = arena
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# @!visibility private
|
|
31
|
+
def tick(sensors)
|
|
32
|
+
self.sensors = sensors
|
|
33
|
+
RTanque::Bot::Command.new.tap do |empty_command|
|
|
34
|
+
self.command = empty_command
|
|
35
|
+
self.tick!
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Main logic goes here
|
|
40
|
+
#
|
|
41
|
+
# Get input from {#sensors}. See {RTanque::Bot::Sensors}
|
|
42
|
+
#
|
|
43
|
+
# Give output to {#command}. See {RTanque::Bot::Command}
|
|
44
|
+
# @abstract
|
|
45
|
+
def tick!
|
|
46
|
+
# Sweet bot logic
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module RTanque
|
|
2
|
+
class Bot
|
|
3
|
+
# Some helpful constants and methods for use as mixin in {RTanque::Bot::Brain}
|
|
4
|
+
module BrainHelper
|
|
5
|
+
BOT_RADIUS = Bot::RADIUS
|
|
6
|
+
MAX_FIRE_POWER = Bot::MAX_FIRE_POWER
|
|
7
|
+
MIN_FIRE_POWER = Bot::MIN_FIRE_POWER
|
|
8
|
+
MAX_HEALTH = Bot::MAX_HEALTH
|
|
9
|
+
MAX_BOT_SPEED = Bot::MAX_SPEED
|
|
10
|
+
MAX_BOT_ROTATION = Configuration.bot.turn_step
|
|
11
|
+
MAX_TURRET_ROTATION = Configuration.turret.turn_step
|
|
12
|
+
MAX_RADAR_ROTATION = Configuration.radar.turn_step
|
|
13
|
+
|
|
14
|
+
# Run block every 'num_of_ticks'
|
|
15
|
+
# @param [Integer] num_of_ticks tick interval at which to execute block
|
|
16
|
+
# @yield
|
|
17
|
+
# @return [void]
|
|
18
|
+
def at_tick_interval(num_of_ticks)
|
|
19
|
+
yield if sensors.ticks % num_of_ticks == 0
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module RTanque
|
|
2
|
+
class Bot
|
|
3
|
+
# Command provide output from the {RTanque::Bot::Brain} about the current state of the {RTanque::Match}
|
|
4
|
+
#
|
|
5
|
+
# They are made available to {RTanque::Bot::Brain} via {RTanque::Bot::Brain#command}
|
|
6
|
+
#
|
|
7
|
+
# All values are bound. Setting an out-of-bounds value will result in it being set to the max/min allowed value.
|
|
8
|
+
#
|
|
9
|
+
# @attr_writer [Float] speed
|
|
10
|
+
# @attr_writer [Float, RTanque::Heading] heading
|
|
11
|
+
# @attr_writer [Float, RTanque::Heading] radar_heading
|
|
12
|
+
# @attr_writer [Float, RTanque::Heading] turret_heading
|
|
13
|
+
# @attr_writer [Float, nil] fire_power sets firing power. Setting to nil will stop firing. See {#fire}
|
|
14
|
+
#
|
|
15
|
+
# @param [Float] power alias to {#fire_power=}
|
|
16
|
+
# @!method fire(power)
|
|
17
|
+
Command = Struct.new(:speed, :heading, :radar_heading, :turret_heading, :fire_power) do
|
|
18
|
+
def fire(power = 3)
|
|
19
|
+
self.fire_power = power
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
module RTanque
|
|
2
|
+
class Bot
|
|
3
|
+
class Radar
|
|
4
|
+
include Enumerable
|
|
5
|
+
include Movable
|
|
6
|
+
extend NormalizedAttr
|
|
7
|
+
VISION_RANGE = Configuration.radar.vision
|
|
8
|
+
attr_normalized(:heading, Heading::FULL_RANGE, Configuration.radar.turn_step)
|
|
9
|
+
|
|
10
|
+
# A Reflection is the information obtained for a bot detected by {RTanque::Bot::Radar}
|
|
11
|
+
#
|
|
12
|
+
# @attr_reader [RTanque::Heading] heading
|
|
13
|
+
# @attr_reader [Float] distance
|
|
14
|
+
# @attr_reader [String] name
|
|
15
|
+
Reflection = Struct.new(:heading, :distance, :name) do
|
|
16
|
+
def self.new_from_points(from_position, to_position, &tap)
|
|
17
|
+
self.new(from_position.heading(to_position), from_position.distance(to_position)).tap(&tap)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def initialize(bot, heading)
|
|
22
|
+
@bot = bot
|
|
23
|
+
@heading = heading
|
|
24
|
+
@reflections = []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def position
|
|
28
|
+
@bot.position
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def each(&block)
|
|
32
|
+
@reflections.each(&block)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def empty?
|
|
36
|
+
self.count == 0
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def scan(bots)
|
|
40
|
+
@reflections.clear
|
|
41
|
+
bots.each do |other_bot|
|
|
42
|
+
if self.can_detect?(other_bot)
|
|
43
|
+
@reflections << Reflection.new_from_points(self.position, other_bot.position) { |reflection| reflection.name = other_bot.name }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def can_detect?(other_bot)
|
|
50
|
+
VISION_RANGE.include?(Heading.delta_between_points(self.position, self.heading, other_bot.position))
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module RTanque
|
|
2
|
+
class Bot
|
|
3
|
+
# Sensors provide input to the {RTanque::Bot::Brain} about the current state of the {RTanque::Match}
|
|
4
|
+
#
|
|
5
|
+
# They are made available to {RTanque::Bot::Brain} via {RTanque::Bot::Brain#sensors}
|
|
6
|
+
#
|
|
7
|
+
# @attr_reader [Integer] ticks number of ticks, starts at 0
|
|
8
|
+
# @attr_reader [Float] health health of bot. if == 0, dead
|
|
9
|
+
# @attr_reader [Float] gun_energy energy of cannon. if < 0, cannot fire
|
|
10
|
+
# @attr_reader [Float] speed
|
|
11
|
+
# @attr_reader [RTanque::Point] position
|
|
12
|
+
# @attr_reader [RTanque::Heading] heading
|
|
13
|
+
# @attr_reader [RTanque::Heading] radar_heading
|
|
14
|
+
# @attr_reader [RTanque::Heading] turret_heading
|
|
15
|
+
# @attr_reader [Enumerator] radar enumerates all bots scanned by the radar, yielding {RTanque::Bot::Radar::Reflection}
|
|
16
|
+
Sensors = Struct.new(:ticks, :health, :speed, :position, :heading, :radar, :radar_heading, :turret_heading, :gun_energy) do
|
|
17
|
+
def initialize(*args, &block)
|
|
18
|
+
super(*args)
|
|
19
|
+
block.call(self) if block
|
|
20
|
+
self.freeze
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def button_down?(button_id)
|
|
24
|
+
button_id = Gosu::Window.char_to_button_id(button_id) unless button_id.kind_of?(Integer)
|
|
25
|
+
@gui_window && @gui_window.button_down?(button_id)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def gui_window=(gui_window)
|
|
29
|
+
@gui_window = gui_window
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module RTanque
|
|
2
|
+
class Bot
|
|
3
|
+
class Turret
|
|
4
|
+
include Movable
|
|
5
|
+
extend NormalizedAttr
|
|
6
|
+
LENGTH = Configuration.turret.length
|
|
7
|
+
attr_normalized(:heading, Heading::FULL_RANGE, Configuration.turret.turn_step)
|
|
8
|
+
|
|
9
|
+
def initialize(heading)
|
|
10
|
+
@heading = heading
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require 'configuration'
|
|
2
|
+
|
|
3
|
+
module RTanque
|
|
4
|
+
one_degree = (Math::PI / 180.0)
|
|
5
|
+
|
|
6
|
+
# @!visibility private
|
|
7
|
+
Configuration = ::Configuration.for('default') do
|
|
8
|
+
raise_brain_tick_errors true
|
|
9
|
+
|
|
10
|
+
bot do
|
|
11
|
+
radius 19
|
|
12
|
+
health_reduction_on_exception 2
|
|
13
|
+
health 0..100
|
|
14
|
+
speed -3..3
|
|
15
|
+
speed_step 0.05
|
|
16
|
+
turn_step one_degree * 1.5
|
|
17
|
+
fire_power 1..5
|
|
18
|
+
gun_energy_max 10
|
|
19
|
+
gun_energy_factor 10
|
|
20
|
+
end
|
|
21
|
+
turret do
|
|
22
|
+
length 28
|
|
23
|
+
turn_step (one_degree * 2.0)
|
|
24
|
+
end
|
|
25
|
+
radar do
|
|
26
|
+
turn_step 0.05
|
|
27
|
+
vision -(one_degree * 10.0)..(one_degree * 10.0)
|
|
28
|
+
end
|
|
29
|
+
shell do
|
|
30
|
+
speed_factor 4.5
|
|
31
|
+
ratio 1.5 # used by Bot#adjust_fire_power and to calculate damage done by shell to bot
|
|
32
|
+
end
|
|
33
|
+
explosion do
|
|
34
|
+
life_span 70 * 1 # should be multiple of the number of frames in the explosion animation
|
|
35
|
+
end
|
|
36
|
+
gui do
|
|
37
|
+
update_interval 16.666666 # in milliseconds. 16.666666 == 60 FPS
|
|
38
|
+
fonts do
|
|
39
|
+
small 16
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
def Configuration.config(&block)
|
|
44
|
+
::Configuration::DSL.evaluate(self, &block)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module RTanque
|
|
2
|
+
class Explosion
|
|
3
|
+
include Movable
|
|
4
|
+
attr_reader :position
|
|
5
|
+
LIFE_SPAN = Configuration.explosion.life_span # ticks
|
|
6
|
+
def initialize(position)
|
|
7
|
+
@position = position
|
|
8
|
+
@ticks = 0
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def percent_dead
|
|
12
|
+
@ticks / LIFE_SPAN.to_f
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def tick
|
|
16
|
+
@ticks += 1
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def dead?
|
|
20
|
+
@ticks > LIFE_SPAN
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/rtanque/gui.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module RTanque
|
|
2
|
+
module Gui
|
|
3
|
+
RESOURCE_DIR = File.expand_path('../../../resources', __FILE__)
|
|
4
|
+
def self.resource_path(*components)
|
|
5
|
+
File.join(RESOURCE_DIR, *components)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
module ZOrder
|
|
9
|
+
BACKGROUND = -1
|
|
10
|
+
BOT_NAME = 0
|
|
11
|
+
BOT_BODY = 1
|
|
12
|
+
BOT_TURRET = 2
|
|
13
|
+
BOT_RADAR = 3
|
|
14
|
+
SHELL = 4
|
|
15
|
+
EXPLOSION = 10
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
require 'rtanque/gui/draw_group'
|
|
21
|
+
require 'rtanque/gui/window'
|
|
22
|
+
require 'rtanque/gui/bot'
|
|
23
|
+
require 'rtanque/gui/shell'
|
|
24
|
+
require 'rtanque/gui/explosion'
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'gosu'
|
|
2
|
+
|
|
3
|
+
module RTanque
|
|
4
|
+
module Gui
|
|
5
|
+
class Bot
|
|
6
|
+
attr_reader :bot
|
|
7
|
+
|
|
8
|
+
def initialize(window, bot)
|
|
9
|
+
@window = window
|
|
10
|
+
@bot = bot
|
|
11
|
+
@body_image = Gosu::Image.new(@window, Gui.resource_path("images/body.png"))
|
|
12
|
+
@turret_image = Gosu::Image.new(@window, Gui.resource_path("images/turret.png"))
|
|
13
|
+
@radar_image = Gosu::Image.new(@window, Gui.resource_path("images/radar.png"))
|
|
14
|
+
@name_font = Gosu::Font.new(@window, Window::FONT_NAME, Window::SMALL_FONT_SIZE)
|
|
15
|
+
@x_factor = 1
|
|
16
|
+
@y_factor = 1
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def draw
|
|
20
|
+
position = [@bot.position.x, @window.height - @bot.position.y]
|
|
21
|
+
draw_bot(position)
|
|
22
|
+
draw_name(position)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def grow(factor = 2, step = 0.002)
|
|
26
|
+
@x_factor += step unless @x_factor > factor
|
|
27
|
+
@y_factor += step unless @y_factor > factor
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def draw_bot(position)
|
|
31
|
+
@body_image.draw_rot(position[0], position[1], ZOrder::BOT_BODY, Gosu.radians_to_degrees(@bot.heading.to_f), 0.5, 0.5, @x_factor, @y_factor)
|
|
32
|
+
@turret_image.draw_rot(position[0], position[1], ZOrder::BOT_TURRET, Gosu.radians_to_degrees(@bot.turret.heading.to_f), 0.5, 0.5, @x_factor, @y_factor)
|
|
33
|
+
@radar_image.draw_rot(position[0], position[1], ZOrder::BOT_RADAR, Gosu.radians_to_degrees(@bot.radar.heading.to_f), 0.5, 0.5, @x_factor, @y_factor)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def draw_name(position)
|
|
37
|
+
x,y = *position
|
|
38
|
+
@name_font.draw_rel(self.bot.name, x, y + (RTanque::Bot::RADIUS * @y_factor) + Window::SMALL_FONT_SIZE.to_i, ZOrder::BOT_NAME, 0.5, 0.5, @x_factor, @y_factor)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module RTanque
|
|
2
|
+
module Gui
|
|
3
|
+
class DrawGroup
|
|
4
|
+
include Enumerable
|
|
5
|
+
|
|
6
|
+
def initialize(tick_group, &create_drawable)
|
|
7
|
+
@tick_group = tick_group
|
|
8
|
+
@mapped_drawables = Hash.new do |h, tickable|
|
|
9
|
+
h[tickable] = create_drawable.call(tickable)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def each(&block)
|
|
14
|
+
@tick_group.each do |tickable|
|
|
15
|
+
if tickable.dead?
|
|
16
|
+
@mapped_drawables.delete(tickable)
|
|
17
|
+
else
|
|
18
|
+
block.call(@mapped_drawables[tickable]) # This invokes @mapped_drawables's block if tickable not already in the hash
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def draw
|
|
24
|
+
self.each do |drawable|
|
|
25
|
+
drawable.draw
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'gosu'
|
|
2
|
+
|
|
3
|
+
module RTanque
|
|
4
|
+
module Gui
|
|
5
|
+
class Explosion
|
|
6
|
+
FRAMES = (1..71)
|
|
7
|
+
|
|
8
|
+
def initialize(window, explosion)
|
|
9
|
+
@window = window
|
|
10
|
+
@explosion = explosion
|
|
11
|
+
@position = [explosion.position.x, window.height - explosion.position.y]
|
|
12
|
+
@explosion_images = FRAMES.map { |i| Gosu::Image.new(@window, Gui.resource_path("images/explosions/explosion2-#{i}.png")) }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def draw
|
|
16
|
+
frame.draw_rot(@position[0], @position[1], 5, ZOrder::EXPLOSION)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def frame
|
|
20
|
+
@frames_length ||= @explosion_images.length - 1
|
|
21
|
+
@explosion_images[(@explosion.percent_dead * @frames_length).floor]
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require 'gosu'
|
|
2
|
+
|
|
3
|
+
module RTanque
|
|
4
|
+
module Gui
|
|
5
|
+
class Shell
|
|
6
|
+
attr_reader :shell
|
|
7
|
+
|
|
8
|
+
def initialize(window, shell)
|
|
9
|
+
@window = window
|
|
10
|
+
@shell = shell
|
|
11
|
+
@shell_image = Gosu::Image.new(@window, Gui.resource_path("images/bullet.png"))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def draw
|
|
15
|
+
position = [self.shell.position.x, @window.height - self.shell.position.y]
|
|
16
|
+
@shell_image.draw_rot(position[0], position[1], ZOrder::SHELL, 0, 0.5, 0.5)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
require 'gosu'
|
|
2
|
+
|
|
3
|
+
module RTanque
|
|
4
|
+
module Gui
|
|
5
|
+
class Window < Gosu::Window
|
|
6
|
+
UPDATE_INTERVAL = Configuration.gui.update_interval
|
|
7
|
+
FONT_NAME = Gosu::default_font_name
|
|
8
|
+
SMALL_FONT_SIZE = Configuration.gui.fonts.small
|
|
9
|
+
|
|
10
|
+
attr_reader :gui_bots, :gui_shells, :gui_explosions
|
|
11
|
+
|
|
12
|
+
def initialize(match)
|
|
13
|
+
@match = match
|
|
14
|
+
@arena = match.arena
|
|
15
|
+
match.bots.each { |bot| bot.gui_window = self }
|
|
16
|
+
@gui_bots = DrawGroup.new(match.bots) do |bot|
|
|
17
|
+
RTanque::Gui::Bot.new(self, bot)
|
|
18
|
+
end
|
|
19
|
+
@gui_shells = DrawGroup.new(match.shells) do |shell|
|
|
20
|
+
RTanque::Gui::Shell.new(self, shell)
|
|
21
|
+
end
|
|
22
|
+
@gui_explosions = DrawGroup.new(match.explosions) do |explosion|
|
|
23
|
+
RTanque::Gui::Explosion.new(self, explosion)
|
|
24
|
+
end
|
|
25
|
+
# Fullscreen: https://github.com/jlnr/gosu/issues/159#issuecomment-12473172
|
|
26
|
+
super(@arena.width, @arena.height, false, UPDATE_INTERVAL)
|
|
27
|
+
self.caption = self.class.name.split('::').first
|
|
28
|
+
@background = Gosu::Image.new(self, Gui.resource_path("images/grass.png"))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def update
|
|
32
|
+
@match.tick
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def draw
|
|
36
|
+
self.close if button_down?(Gosu::Button::KbEscape)
|
|
37
|
+
@background.draw(0, 0, ZOrder::BACKGROUND)
|
|
38
|
+
if @match.finished?
|
|
39
|
+
self.gui_bots.each { |bot| bot.grow(2) }
|
|
40
|
+
end
|
|
41
|
+
self.draw_drawables
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def draw_drawables
|
|
45
|
+
self.gui_bots.draw
|
|
46
|
+
self.gui_shells.draw
|
|
47
|
+
self.gui_explosions.draw
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|