survivor 0.1.0.alpha2
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 +2 -0
- data/.rvmrc +1 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +15 -0
- data/Rakefile +57 -0
- data/bin/survivor +10 -0
- data/lib/survivor.rb +22 -0
- data/lib/survivor/core_ext/hash.rb +15 -0
- data/lib/survivor/core_ext/kernel.rb +11 -0
- data/lib/survivor/game.rb +48 -0
- data/lib/survivor/game/creature.rb +33 -0
- data/lib/survivor/game/creature/character.rb +15 -0
- data/lib/survivor/game/logic.rb +11 -0
- data/lib/survivor/game/logic/movement.rb +33 -0
- data/lib/survivor/game/map.rb +116 -0
- data/lib/survivor/game/map/coordinates.rb +49 -0
- data/lib/survivor/game/map/custom_yaml_format.rb +85 -0
- data/lib/survivor/game/map/tile.rb +44 -0
- data/lib/survivor/options.rb +57 -0
- data/lib/survivor/options/parser.rb +39 -0
- data/lib/survivor/ui.rb +18 -0
- data/lib/survivor/ui/curses.rb +184 -0
- data/lib/survivor/version.rb +20 -0
- data/maps/test.map.yaml +33 -0
- data/survivor.gemspec +22 -0
- metadata +81 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create 1.9.3@survivor
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
this_dir = File.expand_path('..', __FILE__)
|
2
|
+
gem_dir = File.join this_dir, 'gem'
|
3
|
+
spec_file = File.join this_dir, 'survivor.gemspec'
|
4
|
+
|
5
|
+
spec = Gem::Specification.load spec_file
|
6
|
+
|
7
|
+
task :mkdir do
|
8
|
+
FileUtils.mkdir_p gem_dir
|
9
|
+
end
|
10
|
+
|
11
|
+
task :gem => :mkdir do
|
12
|
+
gem_file = File.join this_dir, Gem::Builder.new(spec).build
|
13
|
+
FileUtils.mv gem_file, gem_dir
|
14
|
+
end
|
15
|
+
|
16
|
+
namespace :gem do
|
17
|
+
|
18
|
+
task :build => :gem
|
19
|
+
|
20
|
+
gem_file = File.join gem_dir, "#{spec.name}-#{spec.version}.gem"
|
21
|
+
|
22
|
+
task :push => :gem do
|
23
|
+
sh "gem push #{gem_file}"
|
24
|
+
end
|
25
|
+
|
26
|
+
task :install => :gem do
|
27
|
+
sh "gem install #{gem_file}"
|
28
|
+
end
|
29
|
+
|
30
|
+
task :uninstall do
|
31
|
+
sh "gem uninstall #{spec.name}"
|
32
|
+
end
|
33
|
+
|
34
|
+
task :clean do
|
35
|
+
FileUtils.rm_rf gem_dir
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
task :clean => 'gem:clean'
|
41
|
+
|
42
|
+
task :setup => [ 'gem:install', :clean ]
|
43
|
+
|
44
|
+
task :run => :setup do
|
45
|
+
system 'survivor'
|
46
|
+
end
|
47
|
+
|
48
|
+
namespace :run do
|
49
|
+
|
50
|
+
task :map, [ :file ] => :setup do |task, args|
|
51
|
+
file = args[:file]
|
52
|
+
system "survivor --map #{file}"
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
task :default => :run
|
data/bin/survivor
ADDED
data/lib/survivor.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'survivor/game'
|
2
|
+
require 'survivor/options'
|
3
|
+
|
4
|
+
module Survivor
|
5
|
+
|
6
|
+
def self.run(options = Options.new)
|
7
|
+
Survivor::Game.new(options).run
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.root
|
11
|
+
File.expand_path '../..', __FILE__
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.maps
|
15
|
+
File.join root, 'maps'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.map(filename)
|
19
|
+
File.join maps, filename
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'survivor/game/creature/character'
|
2
|
+
require 'survivor/game/logic/movement'
|
3
|
+
require 'survivor/game/map'
|
4
|
+
require 'survivor/game/map/tile'
|
5
|
+
require 'survivor/ui'
|
6
|
+
|
7
|
+
module Survivor
|
8
|
+
class Game
|
9
|
+
|
10
|
+
attr_reader :character, :map
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
@map = Map.load(options.map_location)
|
14
|
+
@character = Creature::Character.new.tap do |character|
|
15
|
+
character.coordinates = map.starting_point
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
game_loop
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def game_loop
|
26
|
+
UI.run do |ui|
|
27
|
+
loop do
|
28
|
+
ui.display self
|
29
|
+
handle ui.input do |input|
|
30
|
+
ui.message input
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def handle(input)
|
37
|
+
case input
|
38
|
+
when :q then exit
|
39
|
+
when :up then Logic::Movement.move_up character, map
|
40
|
+
when :down then Logic::Movement.move_down character, map
|
41
|
+
when :left then Logic::Movement.move_left character, map
|
42
|
+
when :right then Logic::Movement.move_right character, map
|
43
|
+
else yield input
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'survivor/game/map/coordinates'
|
2
|
+
|
3
|
+
module Survivor
|
4
|
+
class Game
|
5
|
+
class Creature
|
6
|
+
|
7
|
+
attr_accessor :coordinates
|
8
|
+
attr_reader :char, :color
|
9
|
+
|
10
|
+
def initialize(char, color = :white)
|
11
|
+
@char, @color = char, color
|
12
|
+
self.coordinates = Map::Coordinates.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def x=(value)
|
16
|
+
self.coordinates = Map::Coordinates[value, y]
|
17
|
+
end
|
18
|
+
|
19
|
+
def y=(value)
|
20
|
+
self.coordinates = Map::Coordinates[x, value]
|
21
|
+
end
|
22
|
+
|
23
|
+
def x
|
24
|
+
coordinates.x
|
25
|
+
end
|
26
|
+
|
27
|
+
def y
|
28
|
+
coordinates.y
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'survivor/game/map/coordinates'
|
2
|
+
|
3
|
+
module Survivor
|
4
|
+
class Game
|
5
|
+
module Logic
|
6
|
+
module Movement
|
7
|
+
|
8
|
+
def self.move(creature, map, dx, dy)
|
9
|
+
coordinates = Map::Coordinates[dx + creature.x, dy + creature.y]
|
10
|
+
tile = map[coordinates]
|
11
|
+
creature.coordinates = coordinates if tile and tile.passable?
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.move_up(creature, map, n = 1)
|
15
|
+
move creature, map, 0, n
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.move_down(creature, map, n = 1)
|
19
|
+
move creature, map, 0, -n
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.move_left(creature, map, n = 1)
|
23
|
+
move creature, map, -n, 0
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.move_right(creature, map, n = 1)
|
27
|
+
move creature, map, n, 0
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'survivor/core_ext/hash'
|
2
|
+
require 'survivor/game/map/coordinates'
|
3
|
+
require 'survivor/game/map/custom_yaml_format'
|
4
|
+
|
5
|
+
module Survivor
|
6
|
+
class Game
|
7
|
+
class Map
|
8
|
+
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
attr_accessor :starting_point
|
12
|
+
|
13
|
+
def initialize(options = {})
|
14
|
+
options.try_map!(&:abs)
|
15
|
+
@map = Hash.new(options.fetch(:default_tile, nil))
|
16
|
+
0.upto(options.fetch(:lines, 0)) do |y|
|
17
|
+
0.upto(options.fetch(:columns, 0)) do |x|
|
18
|
+
coordinates = Coordinates[x, y]
|
19
|
+
@map[coordinates] = yield coordinates
|
20
|
+
end
|
21
|
+
end if block_given?
|
22
|
+
@starting_point = options.fetch(:starting_point, Coordinates[0, 0])
|
23
|
+
end
|
24
|
+
|
25
|
+
def [](coordinates)
|
26
|
+
@map[coordinates]
|
27
|
+
end
|
28
|
+
|
29
|
+
def []=(coordinates, value)
|
30
|
+
@map[coordinates] = value
|
31
|
+
end
|
32
|
+
|
33
|
+
def tiles
|
34
|
+
@map.values
|
35
|
+
end
|
36
|
+
|
37
|
+
def each
|
38
|
+
@map.each_key { |coordinates| yield coordinates }
|
39
|
+
end
|
40
|
+
|
41
|
+
def each_tile
|
42
|
+
@map.each_value { |tile| yield tile }
|
43
|
+
end
|
44
|
+
|
45
|
+
def each_with_coordinates
|
46
|
+
@map.each { |coordinates, tile| yield tile, coordinates }
|
47
|
+
end
|
48
|
+
|
49
|
+
def lines
|
50
|
+
(Hash.new { |hash, key| hash[key] = Array.new }).tap do |lines|
|
51
|
+
group_by { |coordinate| coordinate.y }.each do |line_number, coordinates|
|
52
|
+
coordinates.each do |coordinate|
|
53
|
+
lines[line_number] << @map[coordinate]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def each_line
|
60
|
+
lines.tap do |lines|
|
61
|
+
lines.keys.each do |line_number|
|
62
|
+
yield lines[line_number]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def each_line_string
|
68
|
+
each_line do |line|
|
69
|
+
yield line.map(&:to_s).join
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def area_around(coordinates, reach = {})
|
74
|
+
cx, cy = *coordinates
|
75
|
+
reach.try_map!(&:abs)
|
76
|
+
default = +(reach.fetch(:default, 1))
|
77
|
+
up = +(reach.fetch(:up, default))
|
78
|
+
down = -(reach.fetch(:down, default))
|
79
|
+
left = -(reach.fetch(:left, default))
|
80
|
+
right = +(reach.fetch(:right, default))
|
81
|
+
#
|
82
|
+
# (cx - n, cy + n) (cx - 1, cy + n) (cx, cy + n) (cx + 1, cy + n) (cx + n, cy + n)
|
83
|
+
# (cx - n, cy + 1) (cx - 1, cy + 1) (cx, cy + 1) (cx + 1, cy + 1) (cx + n, cy + 1)
|
84
|
+
# (cx - n, cy) (cx - 1, cy) (cx, cy) (cx + 1, cy) (cx + n, cy)
|
85
|
+
# (cx - n, cy - 1) (cx - 1, cy - 1) (cx, cy - 1) (cx + 1, cy - 1) (cx + n, cy - 1)
|
86
|
+
# (cx - n, cy - n) (cx - 1, cy - n) (cx, cy - n) (cx + 1, cy - n) (cx + n, cy - n)
|
87
|
+
#
|
88
|
+
Map.new(:starting_point => coordinates).tap do |map|
|
89
|
+
left.upto(right) do |x|
|
90
|
+
down.upto(up) do |y|
|
91
|
+
coordinates = Coordinates[cx + x, cy + y]
|
92
|
+
map[coordinates] = @map[coordinates]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
{ :above => :up, :below => :down, :left => :left, :right => :right }.each do |synonym, direction|
|
99
|
+
define_method(synonym) do |coordinates|
|
100
|
+
area_around coordinates, direction => 1, :default => 0
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.load(filename)
|
105
|
+
raise filename.prepend("Map not found - ") unless File.file? filename
|
106
|
+
raise filename.prepend("Map not readable - ") unless File.readable? filename
|
107
|
+
CustomYamlFormat.from_file filename
|
108
|
+
end
|
109
|
+
|
110
|
+
def save(filename)
|
111
|
+
CustomYamlFormat.save self, filename
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'survivor/core_ext/hash'
|
2
|
+
|
3
|
+
module Survivor
|
4
|
+
class Game
|
5
|
+
class Map
|
6
|
+
class Coordinates
|
7
|
+
|
8
|
+
class << self
|
9
|
+
attr_reader :cache
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :x, :y
|
13
|
+
|
14
|
+
def self.[](*args)
|
15
|
+
(@cache ||= Hash.new { |cache, args| cache[args] = new(*args) })[args]
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(x = 0, y = 0)
|
19
|
+
@x, @y = x, y
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_a
|
23
|
+
[x, y]
|
24
|
+
end
|
25
|
+
|
26
|
+
def ==(coordinates)
|
27
|
+
to_a == coordinates.to_a
|
28
|
+
end
|
29
|
+
|
30
|
+
alias :eql? :==
|
31
|
+
alias :=== :==
|
32
|
+
|
33
|
+
def hash
|
34
|
+
to_a.hash
|
35
|
+
end
|
36
|
+
|
37
|
+
alias :to_ary :to_a
|
38
|
+
alias :to_array :to_a
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
"(#{x}, #{y})"
|
42
|
+
end
|
43
|
+
|
44
|
+
alias :inspect :to_s
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'survivor/game/map'
|
2
|
+
require 'survivor/game/map/coordinates'
|
3
|
+
require 'survivor/game/map/tile'
|
4
|
+
require 'survivor/version'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
module Survivor
|
8
|
+
class Game
|
9
|
+
class Map
|
10
|
+
class CustomYamlFormat
|
11
|
+
|
12
|
+
def self.save(map, filename)
|
13
|
+
File.open(filename, 'w') do |file|
|
14
|
+
file.write custom_structure_for(map).to_yaml
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.from_file(filename)
|
19
|
+
from_yaml File.open(filename) { |file| YAML::load file }
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.from_yaml(string)
|
23
|
+
survivor = string['survivor']
|
24
|
+
version = survivor['version']
|
25
|
+
unless Survivor::Version == version
|
26
|
+
puts "Map meant for Survivor v#{version} - current version is v#{Survivor::Version}"
|
27
|
+
end
|
28
|
+
build_map survivor['map']
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def self.build_map(map_hash)
|
34
|
+
tiles = map_hash['tiles']
|
35
|
+
Map.new.tap do |map|
|
36
|
+
map_hash['data'].split("\n").tap do |lines|
|
37
|
+
lines.each_with_index do |line, y|
|
38
|
+
line.chars.each_with_index do |char, x|
|
39
|
+
args = []
|
40
|
+
if tile_data = tiles[char]
|
41
|
+
args << tile_data.fetch('color', :white).to_sym
|
42
|
+
args << tile_data.fetch('passable', true)
|
43
|
+
end
|
44
|
+
tile = Tile.new char, *args
|
45
|
+
map[Coordinates[x, lines.count - y - 1]] = tile
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
start = map_hash['starting point']
|
50
|
+
x, y = start['x'].to_i, start['y'].to_i
|
51
|
+
map.starting_point = Coordinates[x, y]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.custom_structure_for(map)
|
56
|
+
{}.tap do |data|
|
57
|
+
(data['survivor'] = {}).tap do |survivor|
|
58
|
+
survivor['version'] = Survivor::Version.to_s
|
59
|
+
(survivor['map'] = {}).tap do |map_hash|
|
60
|
+
(map_hash['starting point'] = {}).tap do |starting_point|
|
61
|
+
starting_point['x'] = map.starting_point.x
|
62
|
+
starting_point['y'] = map.starting_point.y
|
63
|
+
end
|
64
|
+
(map_hash['tiles'] = {}).tap do |tiles|
|
65
|
+
map.tiles.uniq.each do |tile|
|
66
|
+
(tiles[tile.char] = {}).tap do |tile_data|
|
67
|
+
tile_data['color'] = tile.color.to_s
|
68
|
+
tile_data['passable'] = tile.passable?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
(map_hash['data'] = '').tap do |data|
|
73
|
+
map.each_line_string do |line|
|
74
|
+
data << "#{line}\n"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Survivor
|
2
|
+
class Game
|
3
|
+
class Map
|
4
|
+
class Tile
|
5
|
+
|
6
|
+
attr_reader :char, :color
|
7
|
+
|
8
|
+
def initialize(string, color = :white, passable = true)
|
9
|
+
@char = string.chars.first
|
10
|
+
@color, @passable = color, passable
|
11
|
+
end
|
12
|
+
|
13
|
+
def passable?
|
14
|
+
@passable
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_a
|
18
|
+
[ @char, @color, @passable ]
|
19
|
+
end
|
20
|
+
|
21
|
+
alias :to_ary :to_a
|
22
|
+
alias :to_array :to_a
|
23
|
+
|
24
|
+
def ==(tile)
|
25
|
+
self.to_a == tile.to_a
|
26
|
+
end
|
27
|
+
|
28
|
+
alias :eql? :==
|
29
|
+
alias :=== :==
|
30
|
+
|
31
|
+
def hash
|
32
|
+
self.to_a.hash
|
33
|
+
end
|
34
|
+
|
35
|
+
alias :to_s :char
|
36
|
+
|
37
|
+
def inspect
|
38
|
+
"['#{char}' #{passable?}]"
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'survivor'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
module Survivor
|
5
|
+
class Options
|
6
|
+
|
7
|
+
def initialize(options = {})
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
BOOLEAN_OPTIONS = [ :development_mode ].freeze.tap do |boolean_options|
|
12
|
+
boolean_options.each do |boolean_option|
|
13
|
+
|
14
|
+
define_method("#{boolean_option}_enabled?".to_sym) do
|
15
|
+
@options[boolean_option]
|
16
|
+
end
|
17
|
+
|
18
|
+
define_method("#{boolean_option}_disabled?".to_sym) do
|
19
|
+
not @options[boolean_option]
|
20
|
+
end
|
21
|
+
|
22
|
+
{ :enable => true, :disable => false }.each_pair do |set, state|
|
23
|
+
define_method("#{set}_#{boolean_option}".to_sym) do
|
24
|
+
@options[boolean_option] = state
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
VALUE_OPTIONS = [ :map ].freeze.tap do |value_options|
|
32
|
+
value_options.each do |value_option|
|
33
|
+
|
34
|
+
define_method(value_option) do |default = nil|
|
35
|
+
@options.fetch(value_option, default)
|
36
|
+
end
|
37
|
+
|
38
|
+
alias :"#{value_option}_with_default" :"#{value_option}"
|
39
|
+
|
40
|
+
define_method("#{value_option}=".to_sym) do |value|
|
41
|
+
@options[value_option] = value
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def map_location(default = 'test.map.yaml')
|
48
|
+
map = map_with_default(default)
|
49
|
+
if Pathname.new(map).relative?
|
50
|
+
File.join(Survivor.maps, map)
|
51
|
+
else
|
52
|
+
map
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'survivor/core_ext/kernel'
|
2
|
+
require 'survivor/version'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
module Survivor
|
6
|
+
class Options
|
7
|
+
class Parser
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@option_parser = OptionParser.new do |parser|
|
11
|
+
with parser do
|
12
|
+
on '--dev', 'Enable development mode' do
|
13
|
+
options.enable_development_mode
|
14
|
+
end
|
15
|
+
|
16
|
+
on '--map MAP', 'Start a game on the given map.' do |map|
|
17
|
+
options.map = map
|
18
|
+
end
|
19
|
+
|
20
|
+
on '-h', '--help', 'Display this help screen and exit' do
|
21
|
+
puts self
|
22
|
+
exit
|
23
|
+
end
|
24
|
+
|
25
|
+
on '--version', 'Display version and exit' do
|
26
|
+
puts Survivor::Version
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
[ :parse, :parse! ].each do |method|
|
34
|
+
define_method(method) { @option_parser.send method }
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/lib/survivor/ui.rb
ADDED
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'survivor/core_ext/kernel'
|
2
|
+
require 'curses'
|
3
|
+
|
4
|
+
module Survivor
|
5
|
+
module UI
|
6
|
+
module Curses
|
7
|
+
|
8
|
+
COLOR_CONSTANT_REGEX = /^COLOR_\w+$/i
|
9
|
+
|
10
|
+
def init
|
11
|
+
curses do
|
12
|
+
init_screen
|
13
|
+
if has_colors?
|
14
|
+
start_color
|
15
|
+
const_get(:COLOR_BLACK).tap do |background_color|
|
16
|
+
constants.grep(COLOR_CONSTANT_REGEX).map do |symbol|
|
17
|
+
const_get symbol
|
18
|
+
end.each do |color|
|
19
|
+
init_pair color, color, background_color
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
noecho
|
24
|
+
cbreak
|
25
|
+
end
|
26
|
+
create_windows
|
27
|
+
end
|
28
|
+
|
29
|
+
def close
|
30
|
+
close_windows
|
31
|
+
curses.close_screen
|
32
|
+
end
|
33
|
+
|
34
|
+
def display(game)
|
35
|
+
clear_all
|
36
|
+
draw_window_borders
|
37
|
+
draw_map game.map
|
38
|
+
draw_creature game.character
|
39
|
+
write_messages
|
40
|
+
refresh_all
|
41
|
+
end
|
42
|
+
|
43
|
+
def input
|
44
|
+
translate_key game_window.getch
|
45
|
+
end
|
46
|
+
|
47
|
+
def messages
|
48
|
+
@@messages ||= []
|
49
|
+
end
|
50
|
+
|
51
|
+
def message(str)
|
52
|
+
messages << str.to_s
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def curses(&block)
|
58
|
+
::Curses.tap { |curses| with(curses, &block) if block }
|
59
|
+
end
|
60
|
+
|
61
|
+
def game_border_window
|
62
|
+
@@game_border_window
|
63
|
+
end
|
64
|
+
|
65
|
+
def game_window
|
66
|
+
@@game_window
|
67
|
+
end
|
68
|
+
|
69
|
+
def messages_border_window
|
70
|
+
@@messages_border_window
|
71
|
+
end
|
72
|
+
|
73
|
+
def messages_window
|
74
|
+
@@messages_window
|
75
|
+
end
|
76
|
+
|
77
|
+
def border_windows
|
78
|
+
[ game_border_window, messages_border_window ]
|
79
|
+
end
|
80
|
+
|
81
|
+
def content_windows
|
82
|
+
[ game_window, messages_window ]
|
83
|
+
end
|
84
|
+
|
85
|
+
def windows
|
86
|
+
border_windows + content_windows
|
87
|
+
end
|
88
|
+
|
89
|
+
def inside_of(window)
|
90
|
+
[ window.maxy - 2, window.maxx - 2, window.begy + 1, window.begx + 1 ]
|
91
|
+
end
|
92
|
+
|
93
|
+
def create_windows
|
94
|
+
height = (curses.lines * 3 / 10).to_i
|
95
|
+
width = curses.cols
|
96
|
+
@@game_border_window = curses::Window.new(curses.lines - height, width, 0, 0)
|
97
|
+
@@game_window = curses::Window.new *inside_of(game_border_window)
|
98
|
+
@@messages_border_window = curses::Window.new(height, width, game_border_window.maxy, 0)
|
99
|
+
@@messages_window = curses::Window.new *inside_of(messages_border_window)
|
100
|
+
windows.each do |window|
|
101
|
+
[ [ :keypad, true ], [ :scrollok, true ] ].each do |args|
|
102
|
+
window.send *args
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def close_windows
|
108
|
+
windows.each &:close
|
109
|
+
end
|
110
|
+
|
111
|
+
def clear_all
|
112
|
+
windows.each &:clear
|
113
|
+
end
|
114
|
+
|
115
|
+
def refresh_all
|
116
|
+
windows.each &:refresh
|
117
|
+
end
|
118
|
+
|
119
|
+
def draw_window_borders
|
120
|
+
border_windows.each { |window| window.box '|', '-' }
|
121
|
+
end
|
122
|
+
|
123
|
+
@@key_map = {
|
124
|
+
::Curses::Key::UP => :up,
|
125
|
+
::Curses::Key::DOWN => :down,
|
126
|
+
::Curses::Key::LEFT => :left,
|
127
|
+
::Curses::Key::RIGHT => :right
|
128
|
+
}
|
129
|
+
|
130
|
+
('a'..'z').each do |char|
|
131
|
+
@@key_map[char] = char.to_sym if char.ascii_only?
|
132
|
+
end
|
133
|
+
|
134
|
+
def translate_key(curses_key)
|
135
|
+
@@key_map[curses_key]
|
136
|
+
end
|
137
|
+
|
138
|
+
@@color_map = {}
|
139
|
+
|
140
|
+
::Curses.constants.grep(COLOR_CONSTANT_REGEX).each do |symbol|
|
141
|
+
symbol.to_s.gsub(/^.*_/, '').downcase.to_sym.tap do |color|
|
142
|
+
@@color_map[color] = ::Curses.const_get symbol
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def translate_color(color)
|
147
|
+
::Curses.color_pair @@color_map[color]
|
148
|
+
end
|
149
|
+
|
150
|
+
def write_on(window, string, line, column, color = nil)
|
151
|
+
with window do
|
152
|
+
setpos line, column
|
153
|
+
attron color if color
|
154
|
+
addstr string.to_s
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def draw_map(map)
|
159
|
+
map.each_with_coordinates do |tile, (column, line)|
|
160
|
+
write_on game_window, tile,
|
161
|
+
normalized(line), column,
|
162
|
+
translate_color(tile.color) if tile
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def draw_creature(creature)
|
167
|
+
write_on game_window, creature.char,
|
168
|
+
normalized(creature.y), creature.x,
|
169
|
+
translate_color(creature.color) if creature
|
170
|
+
end
|
171
|
+
|
172
|
+
def write_messages
|
173
|
+
messages.last(messages_window.maxy).each do |message|
|
174
|
+
messages_window.addstr "#{message}\n"
|
175
|
+
end if messages and not messages.empty?
|
176
|
+
end
|
177
|
+
|
178
|
+
def normalized(line, window = game_window)
|
179
|
+
window.maxy - line - 1
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Survivor
|
2
|
+
module Version
|
3
|
+
|
4
|
+
MAJOR = 0
|
5
|
+
MINOR = 1
|
6
|
+
PATCH = 0
|
7
|
+
BUILD = 'alpha2'
|
8
|
+
|
9
|
+
STRING = [ MAJOR, MINOR, PATCH, BUILD ].compact.join '.'
|
10
|
+
|
11
|
+
def self.==(string)
|
12
|
+
STRING == string
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.to_s
|
16
|
+
STRING
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
data/maps/test.map.yaml
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
---
|
2
|
+
survivor:
|
3
|
+
version: 0.1.0.alpha
|
4
|
+
map:
|
5
|
+
starting point:
|
6
|
+
x: 10
|
7
|
+
y: 10
|
8
|
+
tiles:
|
9
|
+
'#':
|
10
|
+
color: red
|
11
|
+
passable: no
|
12
|
+
' ':
|
13
|
+
color: white
|
14
|
+
passable: yes
|
15
|
+
|
16
|
+
data: |
|
17
|
+
############################################################
|
18
|
+
# # #
|
19
|
+
# # #
|
20
|
+
# # #
|
21
|
+
# # #
|
22
|
+
# # # ########
|
23
|
+
# ### # # #
|
24
|
+
# ## ## #
|
25
|
+
# ## ## #
|
26
|
+
# #### ### #
|
27
|
+
# ############ #
|
28
|
+
######## # #
|
29
|
+
# # #
|
30
|
+
# # #
|
31
|
+
# # #
|
32
|
+
# # #
|
33
|
+
############################################################
|
data/survivor.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/env gem build
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
# Add library into load path
|
5
|
+
$:.unshift File.expand_path('../lib', __FILE__)
|
6
|
+
|
7
|
+
require 'survivor/version'
|
8
|
+
|
9
|
+
Gem::Specification.new do |gem|
|
10
|
+
|
11
|
+
gem.name = 'survivor'
|
12
|
+
gem.version = Survivor::Version::STRING
|
13
|
+
gem.summary = 'Survival horror roguelike game'
|
14
|
+
gem.author = 'Matheus Afonso Martins Moreira'
|
15
|
+
gem.homepage = 'https://github.com/matheusmoreira/survivor'
|
16
|
+
|
17
|
+
gem.files = `git ls-files`.split "\n"
|
18
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map &File.method(:basename)
|
19
|
+
|
20
|
+
gem.add_development_dependency 'bundler'
|
21
|
+
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: survivor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0.alpha2
|
5
|
+
prerelease: 6
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matheus Afonso Martins Moreira
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-11-27 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: &12611440 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *12611440
|
25
|
+
description:
|
26
|
+
email:
|
27
|
+
executables:
|
28
|
+
- survivor
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files: []
|
31
|
+
files:
|
32
|
+
- .gitignore
|
33
|
+
- .rvmrc
|
34
|
+
- Gemfile
|
35
|
+
- Gemfile.lock
|
36
|
+
- Rakefile
|
37
|
+
- bin/survivor
|
38
|
+
- lib/survivor.rb
|
39
|
+
- lib/survivor/core_ext/hash.rb
|
40
|
+
- lib/survivor/core_ext/kernel.rb
|
41
|
+
- lib/survivor/game.rb
|
42
|
+
- lib/survivor/game/creature.rb
|
43
|
+
- lib/survivor/game/creature/character.rb
|
44
|
+
- lib/survivor/game/logic.rb
|
45
|
+
- lib/survivor/game/logic/movement.rb
|
46
|
+
- lib/survivor/game/map.rb
|
47
|
+
- lib/survivor/game/map/coordinates.rb
|
48
|
+
- lib/survivor/game/map/custom_yaml_format.rb
|
49
|
+
- lib/survivor/game/map/tile.rb
|
50
|
+
- lib/survivor/options.rb
|
51
|
+
- lib/survivor/options/parser.rb
|
52
|
+
- lib/survivor/ui.rb
|
53
|
+
- lib/survivor/ui/curses.rb
|
54
|
+
- lib/survivor/version.rb
|
55
|
+
- maps/test.map.yaml
|
56
|
+
- survivor.gemspec
|
57
|
+
homepage: https://github.com/matheusmoreira/survivor
|
58
|
+
licenses: []
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
none: false
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ! '>'
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: 1.3.1
|
75
|
+
requirements: []
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.8.10
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: Survival horror roguelike game
|
81
|
+
test_files: []
|