meiro 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +97 -0
- data/Rakefile +11 -0
- data/lib/meiro.rb +20 -0
- data/lib/meiro/block.rb +239 -0
- data/lib/meiro/dungeon.rb +64 -0
- data/lib/meiro/floor.rb +198 -0
- data/lib/meiro/map_layer.rb +117 -0
- data/lib/meiro/options.rb +56 -0
- data/lib/meiro/partition.rb +20 -0
- data/lib/meiro/passage.rb +24 -0
- data/lib/meiro/room.rb +233 -0
- data/lib/meiro/tile.rb +806 -0
- data/lib/meiro/tile_manager.rb +92 -0
- data/lib/meiro/tile_manager/binary_tile_manager.rb +15 -0
- data/lib/meiro/tile_manager/detailed_tile_manager.rb +347 -0
- data/lib/meiro/tile_manager/rogue_like_tile_manager.rb +57 -0
- data/lib/meiro/version.rb +3 -0
- data/meiro.gemspec +25 -0
- data/spec/block_spec.rb +460 -0
- data/spec/dungeon_spec.rb +153 -0
- data/spec/floor_spec.rb +526 -0
- data/spec/map_layer_spec.rb +296 -0
- data/spec/meiro_spec.rb +15 -0
- data/spec/room_spec.rb +564 -0
- data/spec/spec_helper.rb +8 -0
- metadata +137 -0
data/lib/meiro/floor.rb
ADDED
@@ -0,0 +1,198 @@
|
|
1
|
+
module Meiro
|
2
|
+
class FloorError < StandardError; end
|
3
|
+
class TrySeparateLimitError < FloorError; end
|
4
|
+
|
5
|
+
class Floor
|
6
|
+
|
7
|
+
TRY_SEPARATE_LIMIT = 1000000
|
8
|
+
|
9
|
+
attr_reader :dungeon, :width, :height,
|
10
|
+
:min_room_width, :min_room_height,
|
11
|
+
:max_room_width, :max_room_height
|
12
|
+
|
13
|
+
class Config < Options
|
14
|
+
option :width, Integer, nil, lambda {|w,o| !w.nil? && w >= FLOOR_MIN_WIDTH }
|
15
|
+
option :height, Integer, nil, lambda {|h,o| !h.nil? && h >= FLOOR_MIN_HEIGHT }
|
16
|
+
option :min_room_width, Integer, nil, lambda {|w,o| !w.nil? && w >= ROOM_MIN_WIDTH }
|
17
|
+
option :min_room_height, Integer, nil, lambda {|h,o| !h.nil? && h >= ROOM_MIN_HEIGHT }
|
18
|
+
option :max_room_width, Integer, nil, lambda {|w,o| !w.nil? && w >= o[:min_room_width] }
|
19
|
+
option :max_room_height, Integer, nil, lambda {|h,o| !h.nil? && h >= o[:min_room_height] }
|
20
|
+
end
|
21
|
+
|
22
|
+
def initialize(d, w, h, min_rw, min_rh, max_rw, max_rh)
|
23
|
+
opts = {
|
24
|
+
width: w,
|
25
|
+
height: h,
|
26
|
+
min_room_width: min_rw,
|
27
|
+
min_room_height: min_rh,
|
28
|
+
max_room_width: max_rw,
|
29
|
+
max_room_height: max_rh,
|
30
|
+
}
|
31
|
+
config = Config.new(opts)
|
32
|
+
@dungeon = d
|
33
|
+
@min_room_width = config.min_room_width
|
34
|
+
@min_room_height = config.min_room_height
|
35
|
+
@max_room_width = config.max_room_width
|
36
|
+
@max_room_height = config.max_room_height
|
37
|
+
@root_block = Block.new(self, 0, 0, config.width, config.height)
|
38
|
+
fill_floor_by_wall(config.width, config.height)
|
39
|
+
end
|
40
|
+
|
41
|
+
def width
|
42
|
+
@base_map.width
|
43
|
+
end
|
44
|
+
|
45
|
+
def height
|
46
|
+
@base_map.height
|
47
|
+
end
|
48
|
+
|
49
|
+
def [](x, y)
|
50
|
+
@base_map[x, y]
|
51
|
+
end
|
52
|
+
|
53
|
+
def all_blocks
|
54
|
+
@root_block.flatten
|
55
|
+
end
|
56
|
+
|
57
|
+
def all_rooms
|
58
|
+
@all_rooms ||= all_blocks.map{|b| b.room }.compact
|
59
|
+
end
|
60
|
+
|
61
|
+
def each_line(&block)
|
62
|
+
@base_map.each_line(&block)
|
63
|
+
end
|
64
|
+
|
65
|
+
def each_tile(&block)
|
66
|
+
@base_map.each_tile(&block)
|
67
|
+
end
|
68
|
+
|
69
|
+
# このフロアに属するすべての部屋のマスのxy座標を返す
|
70
|
+
def all_room_tiles_xy
|
71
|
+
res = []
|
72
|
+
all_rooms.each do |room|
|
73
|
+
res << room.get_all_abs_coordinate
|
74
|
+
end
|
75
|
+
res.flatten(1)
|
76
|
+
end
|
77
|
+
|
78
|
+
# 指定したx座標、y座標を含むBlockを返す。そのBlockが親(分割済)であっ
|
79
|
+
# た場合は、分割されたいずれかのうち、x座標、y座標を含む方を返す。
|
80
|
+
# 該当するものがない場合はnilを返す。
|
81
|
+
def get_block(x, y, from=nil)
|
82
|
+
from ||= @root_block
|
83
|
+
if from.include?(x, y)
|
84
|
+
if from.separated?
|
85
|
+
get_block(x, y, from.upper_left) ||
|
86
|
+
get_block(x, y, from.lower_right)
|
87
|
+
else
|
88
|
+
from
|
89
|
+
end
|
90
|
+
else
|
91
|
+
nil
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# 指定したx座標、y座標を含むRoomを返す。
|
96
|
+
# 該当するものがない場合はnilを返す。
|
97
|
+
def get_room(x, y)
|
98
|
+
block = get_block(x, y)
|
99
|
+
return nil if block.nil?
|
100
|
+
if block.room && block.room.include?(x, y)
|
101
|
+
block.room
|
102
|
+
else
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def classify!(type=:rogue_like)
|
108
|
+
@base_map.classify!(type)
|
109
|
+
end
|
110
|
+
|
111
|
+
def to_s
|
112
|
+
res = []
|
113
|
+
@base_map.each_line do |line|
|
114
|
+
res << (line.map(&:to_s) << "\n").join
|
115
|
+
end
|
116
|
+
res.join
|
117
|
+
end
|
118
|
+
|
119
|
+
def fill_floor_by_wall(w, h)
|
120
|
+
@base_map = BaseMap.new(w, h, Tile.wall)
|
121
|
+
end
|
122
|
+
|
123
|
+
# ランダムで部屋と通路を生成する
|
124
|
+
def generate_random_room(r_min, r_max, factor, randomizer)
|
125
|
+
separate_blocks(r_min, r_max, factor, randomizer)
|
126
|
+
all_blocks.each do |block|
|
127
|
+
block.put_room(randomizer)
|
128
|
+
end
|
129
|
+
connect_rooms(randomizer)
|
130
|
+
apply_rooms_to_map
|
131
|
+
self
|
132
|
+
end
|
133
|
+
|
134
|
+
# このFloorに設置された部屋に対し、互いを通路で結ぶ処理をかける
|
135
|
+
def connect_rooms(randomizer=nil)
|
136
|
+
randomizer ||= Random.new(Time.now.to_i)
|
137
|
+
all_rooms.each do |room|
|
138
|
+
room.create_passage(randomizer)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# このFloorに設置された部屋全てとそれに紐づく通路・出口を@base_map
|
143
|
+
# に反映させる
|
144
|
+
def apply_rooms_to_map
|
145
|
+
all_rooms.each do |room|
|
146
|
+
@base_map.apply_room(room, Tile.flat)
|
147
|
+
end
|
148
|
+
@base_map.apply_passage(all_rooms, Tile.gate, Tile.passage)
|
149
|
+
end
|
150
|
+
|
151
|
+
# 設定された部屋数のMIN,MAXの範囲に収まるよう区画をランダムで分割
|
152
|
+
def separate_blocks(r_min, r_max, factor, randomizer)
|
153
|
+
new_block = [@root_block]
|
154
|
+
try_count = 0
|
155
|
+
|
156
|
+
while new_block.any?
|
157
|
+
tried = []
|
158
|
+
separated = []
|
159
|
+
next_new_block = []
|
160
|
+
|
161
|
+
while b = new_block.shift
|
162
|
+
if b.separatable? && do_separate?(b, factor, randomizer)
|
163
|
+
b.separate
|
164
|
+
separated << b
|
165
|
+
next_new_block << b.upper_left
|
166
|
+
next_new_block << b.lower_right
|
167
|
+
end
|
168
|
+
tried << b
|
169
|
+
end
|
170
|
+
|
171
|
+
block_num = @root_block.flatten.size
|
172
|
+
if block_num > r_max
|
173
|
+
# MAXを越えてしまったら直前の分割を取り消してやり直し
|
174
|
+
separated.each {|b| b.unify }
|
175
|
+
new_block = tried
|
176
|
+
elsif next_new_block.empty? && block_num < r_min
|
177
|
+
# 全ての分割が完了し、MINに到達しない場合は直前の分割をやり直し
|
178
|
+
new_block = tried
|
179
|
+
else
|
180
|
+
new_block = next_new_block
|
181
|
+
end
|
182
|
+
|
183
|
+
try_count += 1
|
184
|
+
if try_count > TRY_SEPARATE_LIMIT
|
185
|
+
raise TrySeparateLimitError, "could not create expected floor"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
self
|
189
|
+
end
|
190
|
+
|
191
|
+
private
|
192
|
+
|
193
|
+
def do_separate?(block, factor, randomizer)
|
194
|
+
# Blockが細分化するほど、分割されづらくなる
|
195
|
+
block.generation / factor < randomizer.rand(10)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Meiro
|
2
|
+
class MapLayer
|
3
|
+
def initialize(width, height)
|
4
|
+
@map = Array.new(height)
|
5
|
+
@map.map! {|line| Array.new(width) }
|
6
|
+
end
|
7
|
+
|
8
|
+
def [](x, y)
|
9
|
+
if 0 <= x && 0 <= y && x < width && y < height
|
10
|
+
@map[y][x]
|
11
|
+
else
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def []=(x, y, obj)
|
17
|
+
@map[y][x] = obj
|
18
|
+
end
|
19
|
+
|
20
|
+
def width
|
21
|
+
@map.first.size
|
22
|
+
end
|
23
|
+
|
24
|
+
def height
|
25
|
+
@map.size
|
26
|
+
end
|
27
|
+
|
28
|
+
def each_line(&block)
|
29
|
+
@map.each do |line|
|
30
|
+
yield(line)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def each_tile(&block)
|
35
|
+
@map.each_with_index do |line, y|
|
36
|
+
line.each_with_index do |tile, x|
|
37
|
+
yield(x, y, tile)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_around(x, y)
|
43
|
+
new = MapLayer.new(width, height)
|
44
|
+
around = [
|
45
|
+
[self[x-1, y-1], self[ x, y-1], self[x+1, y-1]],
|
46
|
+
[self[x-1, y], self[ x, y], self[x+1, y]],
|
47
|
+
[self[x-1, y+1], self[ x, y+1], self[x+1, y+1]],
|
48
|
+
]
|
49
|
+
new.instance_variable_set(:@map, around)
|
50
|
+
new
|
51
|
+
end
|
52
|
+
|
53
|
+
def dup
|
54
|
+
new = self.class.new(width, height)
|
55
|
+
new.each_tile do |x, y, tile|
|
56
|
+
new[x, y] = self[x, y].dup
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class BaseMap < MapLayer
|
62
|
+
def initialize(width, height, klass=nil)
|
63
|
+
proc = klass.nil? ? lambda { nil } : lambda { klass.new }
|
64
|
+
@map = Array.new(height)
|
65
|
+
@map.map! {|line| Array.new(width).map! {|e| proc.call } }
|
66
|
+
end
|
67
|
+
|
68
|
+
def fill_rect(x1, y1, x2, y2, klass)
|
69
|
+
x_begin, x_end = x1 <= x2 ? [x1, x2] : [x2, x1]
|
70
|
+
y_begin, y_end = y1 <= y2 ? [y1, y2] : [y2, y1]
|
71
|
+
|
72
|
+
(y_begin..y_end).each do |y|
|
73
|
+
(x_begin..x_end).each do |x|
|
74
|
+
self[x, y] = klass.new
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def apply_room(room, klass)
|
80
|
+
room.each_coordinate do |x, y|
|
81
|
+
self[x, y] = klass.new
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def apply_passage(rooms, gate_klass, pass_klass)
|
86
|
+
all_pass = []
|
87
|
+
all_gates = []
|
88
|
+
rooms.each do |room|
|
89
|
+
all_pass << room.all_pass
|
90
|
+
all_gates << room.gate_coordinates
|
91
|
+
end
|
92
|
+
|
93
|
+
all_pass.flatten.uniq.each do |p|
|
94
|
+
self.fill_rect(p.start_x, p.start_y, p.end_x, p.end_y, pass_klass)
|
95
|
+
end
|
96
|
+
|
97
|
+
all_gates.flatten(1).each do |x, y|
|
98
|
+
self[x, y] = gate_klass.new
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def classify(type=:rogue_like)
|
103
|
+
res = self.class.new(width, height)
|
104
|
+
res.each_tile do |x, y, tile|
|
105
|
+
res[x, y] = Tile.classify(self.get_around(x, y), type)
|
106
|
+
end
|
107
|
+
res
|
108
|
+
end
|
109
|
+
|
110
|
+
def classify!(type=:rogue_like)
|
111
|
+
self.each_tile do |x, y, tile|
|
112
|
+
self[x, y] = Tile.classify(self.get_around(x, y), type)
|
113
|
+
end
|
114
|
+
self
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Meiro
|
2
|
+
class Options
|
3
|
+
class << self
|
4
|
+
attr_reader :keys, :validators
|
5
|
+
|
6
|
+
def option(symbol, klass, default=nil, check=nil)
|
7
|
+
@keys ||= []
|
8
|
+
@keys << symbol
|
9
|
+
@validators ||= {}
|
10
|
+
@validators[symbol] ||= {}
|
11
|
+
@validators[symbol][:class] = klass
|
12
|
+
@validators[symbol][:check] = check if check && check.kind_of?(Proc)
|
13
|
+
define_method(symbol) do
|
14
|
+
option_value = @config[symbol] ||
|
15
|
+
(default.kind_of?(Proc) ? default.call(self) : default)
|
16
|
+
@applied_config[symbol] = option_value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def initialize(config)
|
22
|
+
@config = validate(config)
|
23
|
+
@applied_config = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def keys
|
27
|
+
self.class.keys
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def validate(config)
|
33
|
+
validate_keys(config)
|
34
|
+
validate_values(config)
|
35
|
+
config
|
36
|
+
end
|
37
|
+
|
38
|
+
def validate_keys(config)
|
39
|
+
valid_symbols = self.class.keys
|
40
|
+
config.each_key do |k|
|
41
|
+
raise "Invalid option: #{k}" unless valid_symbols.include? k
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def validate_values(config)
|
46
|
+
validators = self.class.validators
|
47
|
+
config.each do |k, v|
|
48
|
+
klass = validators[k][:class]
|
49
|
+
proc = validators[k][:check] || lambda {|v, config| true }
|
50
|
+
unless v.kind_of?(klass) && proc.call(v, config)
|
51
|
+
raise "Invalid option: #{k}(#{klass}) => #{v}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Meiro
|
2
|
+
class Partition
|
3
|
+
attr_reader :x, :y, :length
|
4
|
+
|
5
|
+
def initialize(x, y, length, shape)
|
6
|
+
@x = x
|
7
|
+
@y = y
|
8
|
+
@length = length
|
9
|
+
@shape = shape
|
10
|
+
end
|
11
|
+
|
12
|
+
def horizontal?
|
13
|
+
@shape == :horizontal
|
14
|
+
end
|
15
|
+
|
16
|
+
def vertical?
|
17
|
+
@shape == :vertical
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Meiro
|
2
|
+
class Passage
|
3
|
+
def initialize(x1, y1, x2, y2)
|
4
|
+
@start = [x1, y1]
|
5
|
+
@end = [x2, y2]
|
6
|
+
end
|
7
|
+
|
8
|
+
def start_x
|
9
|
+
@start[0]
|
10
|
+
end
|
11
|
+
|
12
|
+
def start_y
|
13
|
+
@start[1]
|
14
|
+
end
|
15
|
+
|
16
|
+
def end_x
|
17
|
+
@end[0]
|
18
|
+
end
|
19
|
+
|
20
|
+
def end_y
|
21
|
+
@end[1]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/meiro/room.rb
ADDED
@@ -0,0 +1,233 @@
|
|
1
|
+
module Meiro
|
2
|
+
class Room
|
3
|
+
attr_reader :relative_x, :relative_y,
|
4
|
+
:width, :height, :block
|
5
|
+
attr_accessor :connected_rooms, :all_pass
|
6
|
+
|
7
|
+
def initialize(width, height)
|
8
|
+
if width < ROOM_MIN_WIDTH || height < ROOM_MIN_HEIGHT
|
9
|
+
raise "width/height is too small for Meiro::Room"
|
10
|
+
end
|
11
|
+
@width = width
|
12
|
+
@height = height
|
13
|
+
@connected_rooms = {}
|
14
|
+
@all_pass = []
|
15
|
+
end
|
16
|
+
|
17
|
+
# floorにおける絶対x座標
|
18
|
+
# ブロックのx座標に、ブロックのx座標を基準とした相対x座標を足しあわ
|
19
|
+
# せたものを返す
|
20
|
+
def x
|
21
|
+
return nil if @block.nil?
|
22
|
+
@block.x + @relative_x
|
23
|
+
end
|
24
|
+
|
25
|
+
# floorにおける絶対y座標
|
26
|
+
# ブロックのy座標に、ブロックのy座標を基準とした相対y座標を足しあわ
|
27
|
+
# せたものを返す
|
28
|
+
def y
|
29
|
+
return nil if @block.nil?
|
30
|
+
@block.y + @relative_y
|
31
|
+
end
|
32
|
+
|
33
|
+
# ブロックのx座標を基準とした相対x座標
|
34
|
+
def relative_x=(x)
|
35
|
+
if @block
|
36
|
+
if x < available_x_min || x > available_x_max
|
37
|
+
raise "could not set relative_x-coordinate [#{x}] in this block.\n" \
|
38
|
+
"you can use #{available_x_min}..#{available_x_max}"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
@relative_x = x
|
42
|
+
end
|
43
|
+
|
44
|
+
# ブロックのy座標を基準とした相対y座標
|
45
|
+
def relative_y=(y)
|
46
|
+
if @block
|
47
|
+
if y < available_y_min || y > available_y_max
|
48
|
+
raise "could not set relative_y-coordinate [#{y}] in this block.\n" \
|
49
|
+
"you can use #{available_y_min}..#{available_y_max}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
@relative_y = y
|
53
|
+
end
|
54
|
+
|
55
|
+
# 部屋のマスの全ての絶対xy座標を返す
|
56
|
+
def get_all_abs_coordinate
|
57
|
+
res = []
|
58
|
+
self.each_coordinate do |_x, _y|
|
59
|
+
res << [_x, _y]
|
60
|
+
end
|
61
|
+
res
|
62
|
+
end
|
63
|
+
|
64
|
+
# 引数で渡した座標を部屋の内部に含むかどうかを返す
|
65
|
+
def include?(_x, _y)
|
66
|
+
self.get_all_abs_coordinate.include?([_x, _y])
|
67
|
+
end
|
68
|
+
|
69
|
+
def block=(block)
|
70
|
+
@block = block
|
71
|
+
if @relative_x && @relative_y
|
72
|
+
begin
|
73
|
+
# 再代入して x, y が適切かチェック
|
74
|
+
self.relative_x = @relative_x
|
75
|
+
self.relative_y = @relative_y
|
76
|
+
rescue => e
|
77
|
+
@block = nil
|
78
|
+
raise e
|
79
|
+
end
|
80
|
+
end
|
81
|
+
@block
|
82
|
+
end
|
83
|
+
|
84
|
+
def set_random_coordinate(randomizer=nil)
|
85
|
+
if @block.nil?
|
86
|
+
raise "Block is not found"
|
87
|
+
else
|
88
|
+
randomizer ||= Random.new(Time.now.to_i)
|
89
|
+
self.relative_x = randomizer.rand(available_x_min..available_x_max)
|
90
|
+
self.relative_y = randomizer.rand(available_y_min..available_y_max)
|
91
|
+
end
|
92
|
+
return [@relative_x, @relative_y]
|
93
|
+
end
|
94
|
+
|
95
|
+
def available_x_min
|
96
|
+
Block::MARGIN
|
97
|
+
end
|
98
|
+
|
99
|
+
def available_x_max
|
100
|
+
@block.width - (@width + Block::MARGIN)
|
101
|
+
end
|
102
|
+
|
103
|
+
def available_y_min
|
104
|
+
Block::MARGIN
|
105
|
+
end
|
106
|
+
|
107
|
+
def available_y_max
|
108
|
+
@block.height - (@height + Block::MARGIN)
|
109
|
+
end
|
110
|
+
|
111
|
+
def each_coordinate(&block)
|
112
|
+
@height.times do |h|
|
113
|
+
@width.times do |w|
|
114
|
+
yield(x + w, y + h)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def generation
|
120
|
+
@block.generation if @block
|
121
|
+
end
|
122
|
+
|
123
|
+
def partition
|
124
|
+
@block.partition if @block
|
125
|
+
end
|
126
|
+
|
127
|
+
def brother
|
128
|
+
@block.brother.room if @block && @block.brother
|
129
|
+
end
|
130
|
+
|
131
|
+
def gate_coordinates
|
132
|
+
@connected_rooms.keys
|
133
|
+
end
|
134
|
+
|
135
|
+
def all_connected_rooms
|
136
|
+
@connected_rooms.values
|
137
|
+
end
|
138
|
+
|
139
|
+
def create_passage(randomizer=nil)
|
140
|
+
randomizer ||= Random.new(Time.now.to_i)
|
141
|
+
connectable_rooms.each do |room|
|
142
|
+
create_passage_to(room, randomizer)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# この部屋と通路で接続可能な部屋を返す
|
147
|
+
def connectable_rooms
|
148
|
+
rooms = []
|
149
|
+
# 隣接するブロックに所属する部屋を接続可能とする
|
150
|
+
@block.neighbors.each do |b|
|
151
|
+
rooms << b.room if b.has_room?
|
152
|
+
end
|
153
|
+
rooms
|
154
|
+
end
|
155
|
+
|
156
|
+
# 接続対象の部屋との仕切り(Partition)を返す
|
157
|
+
def select_partition(room)
|
158
|
+
@block.find_ancestor(room.block).partition
|
159
|
+
end
|
160
|
+
|
161
|
+
# 部屋からPartitionに向けて伸ばす通路の出口を決める
|
162
|
+
def get_random_gate(partition, randomizer=nil)
|
163
|
+
randomizer ||= Random.new(Time.now.to_i)
|
164
|
+
if partition.horizontal?
|
165
|
+
if self.y < partition.y
|
166
|
+
# Partitionがこの部屋より下にある
|
167
|
+
gate_y = self.y + @height
|
168
|
+
else
|
169
|
+
# Partitionがこの部屋より上にある
|
170
|
+
gate_y = self.y - 1
|
171
|
+
end
|
172
|
+
gate_x = randomizer.rand((self.x + 1)...(self.x + @width - 1))
|
173
|
+
checker = [[1, 0], [-1, 0]]
|
174
|
+
else
|
175
|
+
if self.x < partition.x
|
176
|
+
# Partitionがこの部屋より右にある
|
177
|
+
gate_x = self.x + @width
|
178
|
+
else
|
179
|
+
# Partitionがこの部屋より左にある
|
180
|
+
gate_x = self.x - 1
|
181
|
+
end
|
182
|
+
gate_y = randomizer.rand((self.y + 1)...(self.y + @height - 1))
|
183
|
+
checker = [[0, 1], [0, -1]]
|
184
|
+
end
|
185
|
+
|
186
|
+
retry_flg = false
|
187
|
+
checker.each do |dx, dy|
|
188
|
+
# となり合うGateが作られた場合はやり直し
|
189
|
+
retry_flg |= true if @connected_rooms[[gate_x + dx, gate_y + dy]]
|
190
|
+
end
|
191
|
+
gate_x, gate_y = get_random_gate(partition, randomizer) if retry_flg
|
192
|
+
|
193
|
+
[gate_x, gate_y]
|
194
|
+
end
|
195
|
+
|
196
|
+
# 自身と引数で渡した部屋とを接続する通路を作成する
|
197
|
+
def create_passage_to(room, randomizer=nil)
|
198
|
+
# 接続済みの部屋とは何もしない
|
199
|
+
return true if all_connected_rooms.include?(room)
|
200
|
+
# 同じ親の同世代の部屋と接続済みなら何もしない
|
201
|
+
return true if brother && brother.all_connected_rooms.include?(room)
|
202
|
+
|
203
|
+
randomizer ||= Random.new(Time.now.to_i)
|
204
|
+
partition = select_partition(room)
|
205
|
+
|
206
|
+
# 部屋から通路への出口を決定
|
207
|
+
gate_xy = self.get_random_gate(partition, randomizer)
|
208
|
+
o_gate_xy = room.get_random_gate(partition, randomizer)
|
209
|
+
|
210
|
+
created_pass = []
|
211
|
+
# 各部屋のGateからPartitionへ伸びる通路を作成
|
212
|
+
[gate_xy, o_gate_xy].each do |gx, gy|
|
213
|
+
if partition.horizontal?
|
214
|
+
created_pass << Passage.new(gx, gy, gx, partition.y)
|
215
|
+
else
|
216
|
+
created_pass << Passage.new(gx, gy, partition.x, gy)
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
# 上で作られた通路同士を連結する通路を作成
|
221
|
+
created_pass << Passage.new(created_pass[0].end_x, created_pass[0].end_y,
|
222
|
+
created_pass[1].end_x, created_pass[1].end_y)
|
223
|
+
|
224
|
+
created_pass.each do |p|
|
225
|
+
@all_pass << p
|
226
|
+
room.all_pass << p
|
227
|
+
end
|
228
|
+
@connected_rooms[gate_xy] = room
|
229
|
+
room.connected_rooms[o_gate_xy] = self
|
230
|
+
true
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|