survivor 0.1.0.alpha2
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|