meiro 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|