rdgc-dm 0.1.0
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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +22 -0
- data/README.rdoc +148 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/lib/rdgc/maker/divide_dungeon_maker.rb +371 -0
- data/lib/rdgc/maker/divide_temp_block.rb +269 -0
- data/lib/rdgc/maker/dungeon_maker.rb +57 -0
- data/lib/rdgc/maker/temp_block.rb +23 -0
- data/lib/rdgc/map/area.rb +103 -0
- data/lib/rdgc/map/block.rb +155 -0
- data/lib/rdgc/map/board.rb +81 -0
- data/lib/rdgc/map/road.rb +18 -0
- data/lib/rdgc/map/room.rb +69 -0
- data/lib/rdgc/map/tile.rb +35 -0
- data/lib/rdgc/map/tile_type.rb +11 -0
- data/lib/rdgc/util/config.rb +74 -0
- data/lib/rdgc/util/random_util.rb +104 -0
- data/lib/rdgc-dm.rb +17 -0
- data/rdgc-dm.gemspec +93 -0
- data/spec/rdgc/maker/01_temp_block_spec.rb +45 -0
- data/spec/rdgc/maker/02_divide_temp_block_spec.rb +241 -0
- data/spec/rdgc/maker/03_divide_dungeon_maker_divide_spec.rb +224 -0
- data/spec/rdgc/maker/04_divide_dungeon_maker_create_spec.rb +244 -0
- data/spec/rdgc/map/01_tile_spec.rb +56 -0
- data/spec/rdgc/map/02_area_spec.rb +118 -0
- data/spec/rdgc/map/03_road_spec.rb +23 -0
- data/spec/rdgc/map/04_room_spec.rb +190 -0
- data/spec/rdgc/map/05_block_spec.rb +273 -0
- data/spec/rdgc/map/06_board_spec.rb +132 -0
- data/spec/rdgc/util/01_config_spec.rb +44 -0
- data/spec/rdgc/util/02_random_util_spec.rb +153 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +11 -0
- metadata +124 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2010 parrot_studio<parrot *at* users.sourceforge.jp>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,148 @@
|
|
1
|
+
= rdgc-dm
|
2
|
+
Author:: parrot_studio <parrot *at* users.sourceforge.jp>
|
3
|
+
License:: The MIT License
|
4
|
+
|
5
|
+
* 要求されたサイズのランダムダンジョンを生成
|
6
|
+
* ダンジョンは部屋と道で構成される
|
7
|
+
|
8
|
+
== Install
|
9
|
+
gem install gemcutter # インストール済みなら不要
|
10
|
+
gem install rdgc-dm
|
11
|
+
|
12
|
+
== Usage
|
13
|
+
require 'rubygems'
|
14
|
+
require 'rdgc-dm'
|
15
|
+
|
16
|
+
include RDGC::Maker
|
17
|
+
|
18
|
+
board = DivideDungeonMaker.create(30, 40) # width=30, height=40のMap::Boardを作る場合
|
19
|
+
board = DivideDungeonMaker.create(30, 40, :min_room_count = 4) # パラメータ指定(4つ以上の部屋数を期待)
|
20
|
+
|
21
|
+
board.each do |x, y| # each で各座標を順に処理
|
22
|
+
t = board.tile(x, y) # Tileオブジェクト取得
|
23
|
+
case
|
24
|
+
when t.wall? # 壁
|
25
|
+
...
|
26
|
+
when t.room? # 部屋
|
27
|
+
...
|
28
|
+
when t.road? # 道
|
29
|
+
...
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
board.each_tile do |x, y, t| # each_tileで座標とTileを一緒に取得
|
34
|
+
...
|
35
|
+
end
|
36
|
+
|
37
|
+
rooms = board.rooms # Map::Roomオブジェクトの配列取得
|
38
|
+
roads = board.roads # Map::Roadオブジェクトの配列取得
|
39
|
+
|
40
|
+
r = rooms.choice
|
41
|
+
x, y = r.random_point # あるエリアのランダムな座標を取得
|
42
|
+
|
43
|
+
board.room?(2, 3) # 指定座標(x, y)が部屋か判定
|
44
|
+
board.road?(2, 3) # 指定座標(x, y)が道か判定
|
45
|
+
board.movable?(2, 3) # 指定座標(x, y)が移動可能(=部屋or道)か判定
|
46
|
+
|
47
|
+
# その他、Map::Areaに定義されたメソッドは全て使える
|
48
|
+
|
49
|
+
# RDGC::Util::RandomUtilで定義され、top-levelにinclude済みのメソッド
|
50
|
+
# 数値は全て整数を指定すること
|
51
|
+
|
52
|
+
bool_rand # trueかfalseを返す
|
53
|
+
range_rand(min, max) # minからmaxまでのどれかの整数値を返す
|
54
|
+
select_rand(:a => 3, :b => 2, :c => 1) # :aを3/(3+2+1)、:bを2/(3+2+1)...の確率で返す
|
55
|
+
|
56
|
+
dice(5, 10) # 10面のサイコロを5回振った合計を返す
|
57
|
+
5.dice(10) # Integer#dice(max)が定義済みで、この場合はdice(5, 10)と同じ
|
58
|
+
5.d10 # TRPGプレイヤーにおなじみの書き方
|
59
|
+
|
60
|
+
# その他、細かなメソッドはソースやspec等参照
|
61
|
+
|
62
|
+
== Create Parameters
|
63
|
+
|
64
|
+
前提として、生成パラメータは努力目標
|
65
|
+
できるだけ指定を満たそうとはするが、ランダムなので保証はできない
|
66
|
+
|
67
|
+
まず全体を一つのBlockとして定義し、それを再帰的に分割した後、
|
68
|
+
各Blockに部屋か交差点を作るため、Blockとは1:1の関係になる
|
69
|
+
|
70
|
+
* :min_block_size => 分割Blockの最低サイズ
|
71
|
+
* :max_block_count => Blockの最大生成数
|
72
|
+
* :min_room_size => 部屋の最低サイズ(デフォルトは4で、4以下は強制的に4)
|
73
|
+
* :max_room_size => 部屋の最大サイズ
|
74
|
+
* :min_room_count => 部屋の最低生成数(デフォルトは2)
|
75
|
+
* :max_room_count => 部屋の最大生成数
|
76
|
+
* :max_depth => 分割再帰の深さ max_depth=nのとき、Blockの最大数は2^nになる
|
77
|
+
* :cross_road_ratio => 交差点生成率(0 <= x <= 9)
|
78
|
+
|
79
|
+
== FAQ
|
80
|
+
|
81
|
+
=== パラメータが適用されない
|
82
|
+
|
83
|
+
パラメータには適用優先順位があります
|
84
|
+
|
85
|
+
1. min_block_size
|
86
|
+
2. max_block_count
|
87
|
+
3. max_depth
|
88
|
+
4. min_room_count
|
89
|
+
5. max_room_count
|
90
|
+
6. cross_road_ratio
|
91
|
+
7. max_room_size
|
92
|
+
8. min_room_size
|
93
|
+
|
94
|
+
上位のパラメータに対し、下位のパラメータが矛盾した場合、
|
95
|
+
無視はしませんが、保証はされません
|
96
|
+
|
97
|
+
=== max_room_count=1なのに部屋が2個できる
|
98
|
+
|
99
|
+
min_room_countのデフォルト値が2なので、
|
100
|
+
上記の優先順位に従い、部屋が2個できます
|
101
|
+
(デフォルトが2個なのは、スタート地点とゴール地点を作るためです)
|
102
|
+
明示的にmin_room_count=1をあわせて指定すると、一つだけできるはずです
|
103
|
+
|
104
|
+
=== 道に(部屋でない)行き止まりができる
|
105
|
+
|
106
|
+
仕様です
|
107
|
+
|
108
|
+
それで片付けるのもあれなので補足すると、
|
109
|
+
つなげられそうな交差点を、できるだけつなぐようにしているものの、
|
110
|
+
周囲に残りBlockがない等、どうしようもない場合に行き止まりができます
|
111
|
+
|
112
|
+
あまりたくさんできると問題ですが、
|
113
|
+
たまにあるくらいはゲームとしていいんじゃないかと
|
114
|
+
|
115
|
+
=== 最大分割深度(max_depth)って何?
|
116
|
+
|
117
|
+
DivideDungeonMakerは最初のBlockを起点にして、再帰的な分割をしようとします
|
118
|
+
|
119
|
+
この分割回数の最大値がmax_depthの指定で、これを小さくすることで、
|
120
|
+
小さいBlockだらけになるのを防げます
|
121
|
+
max_depth=nの時、作られるBlockの最大値は2^nです
|
122
|
+
|
123
|
+
=== イベントやBOSS用に、広い部屋が1つだけ欲しい
|
124
|
+
|
125
|
+
min_room_sizeをx/y以上にしたうえで、
|
126
|
+
min_block_sizeをx/y以上にするか、max_block_count=1を指定してください
|
127
|
+
全体が1Blockになり、限界までRoomを大きくしようとします
|
128
|
+
|
129
|
+
=== スタートとゴール(階段)って無いの?
|
130
|
+
|
131
|
+
rdgc-dmはあくまで部屋と道(の座標系)を作るための仕組みです
|
132
|
+
スタートやゴールの概念は各ゲームによって異なるため、
|
133
|
+
rdgc-dmには含んでいません(RDGCとしては存在します)
|
134
|
+
|
135
|
+
Area#ramdom_pointで各Areaのランダムな座標が取れますので、
|
136
|
+
それを使ってスタートやゴールやモンスターを配置してください
|
137
|
+
|
138
|
+
=== 自分でロジックを書きたい
|
139
|
+
|
140
|
+
* RDGC::Maker::DungeonMakerをincludeしたクラス
|
141
|
+
* RDGC::Maker::TempBlockを継承したクラス
|
142
|
+
|
143
|
+
これらを組み合わせると自分のロジックが書けます
|
144
|
+
詳しくはDivideDungeonMaker/DivideTempBlockのソースを見てください
|
145
|
+
|
146
|
+
== Copyright
|
147
|
+
|
148
|
+
Copyright (c) 2010 parrot_studio. See LICENSE for details
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "rdgc-dm"
|
8
|
+
gem.summary = %Q{Random Dungeon Maker from RDGC}
|
9
|
+
gem.description = <<-TXT
|
10
|
+
This gem is part of RDGC - Ruby(Random) Dungeon Game Core.
|
11
|
+
RDGC is core of random dungeon game (like rogue), make dungeon, manage mnsters etc.
|
12
|
+
TXT
|
13
|
+
gem.email = "parrot@users.sourceforge.jp"
|
14
|
+
gem.homepage = "http://github.com/parrot-studio/rdgc-dm"
|
15
|
+
gem.authors = ["parrot_studio"]
|
16
|
+
gem.required_ruby_version = ">= 1.8.7"
|
17
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
18
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
19
|
+
end
|
20
|
+
Jeweler::GemcutterTasks.new
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'spec/rake/spectask'
|
26
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
27
|
+
spec.libs << 'lib' << 'spec'
|
28
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
29
|
+
end
|
30
|
+
|
31
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
32
|
+
spec.libs << 'lib' << 'spec'
|
33
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
34
|
+
spec.rcov = true
|
35
|
+
end
|
36
|
+
|
37
|
+
task :spec => :check_dependencies
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rake/rdoctask'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
44
|
+
|
45
|
+
rdoc.rdoc_dir = 'rdoc'
|
46
|
+
rdoc.title = "rdgc-dm #{version}"
|
47
|
+
rdoc.rdoc_files.include('README*')
|
48
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
49
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,371 @@
|
|
1
|
+
# coding: UTF-8
|
2
|
+
module RDGC
|
3
|
+
module Maker
|
4
|
+
class DivideDungeonMaker
|
5
|
+
include DungeonMaker
|
6
|
+
|
7
|
+
def self.create(width, height, params = nil)
|
8
|
+
dm = self.new
|
9
|
+
list = dm.make(width, height, params)
|
10
|
+
Map::Board.create_from_blocks(list)
|
11
|
+
end
|
12
|
+
|
13
|
+
DEFAULT_CROSS_ROAD_RATIO = 2
|
14
|
+
|
15
|
+
# override
|
16
|
+
def create_whole_block(width, height)
|
17
|
+
tb = DivideTempBlock.create_whole_block(width, height)
|
18
|
+
tb.min_size = min_block_size
|
19
|
+
tb.dividable
|
20
|
+
tb
|
21
|
+
end
|
22
|
+
|
23
|
+
def make_blocks(tb)
|
24
|
+
# 分割キューに最初のBlockを入れる
|
25
|
+
divide_queue << tb
|
26
|
+
|
27
|
+
# 再帰分割
|
28
|
+
divide
|
29
|
+
|
30
|
+
# 完了キューの中身を返す
|
31
|
+
done_queue
|
32
|
+
end
|
33
|
+
|
34
|
+
# override
|
35
|
+
def create_room
|
36
|
+
# 部屋と交差点を分ける
|
37
|
+
room_blocks = []
|
38
|
+
cross_blocks = []
|
39
|
+
force_room_blocks = []
|
40
|
+
|
41
|
+
param = {
|
42
|
+
:room => (10 - cross_road_ratio),
|
43
|
+
:cross => cross_road_ratio
|
44
|
+
}
|
45
|
+
|
46
|
+
blocks.each do |b|
|
47
|
+
# 初回分割のBlockは行き止まりになるので避ける
|
48
|
+
if b.depth < 2
|
49
|
+
force_room_blocks << b
|
50
|
+
next
|
51
|
+
end
|
52
|
+
|
53
|
+
r = select_rand(param)
|
54
|
+
case r
|
55
|
+
when :room
|
56
|
+
room_blocks << b
|
57
|
+
when :cross
|
58
|
+
cross_blocks << b
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
room_count = room_blocks.size + force_room_blocks.size
|
63
|
+
if max_room_count > 0 && room_count > max_room_count
|
64
|
+
# 超えた分だけ移動する
|
65
|
+
(room_count - max_room_count).times do
|
66
|
+
break if room_blocks.size + force_room_blocks.size <= 1
|
67
|
+
break if room_blocks.empty?
|
68
|
+
b = room_blocks.pickup!
|
69
|
+
cross_blocks << b if b
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
room_count = room_blocks.size + force_room_blocks.size
|
74
|
+
if room_count < min_room_count
|
75
|
+
# 足りない分を移動する
|
76
|
+
(min_room_count - room_count).times do
|
77
|
+
break if cross_blocks.empty?
|
78
|
+
b = cross_blocks.pickup!
|
79
|
+
room_blocks << b if b
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# それぞれのblockを処理
|
84
|
+
[room_blocks, force_room_blocks].flatten.each do |b|
|
85
|
+
b.create_room(:min => min_room_size, :max => max_room_size)
|
86
|
+
end
|
87
|
+
cross_blocks.each{|b| b.create_cross_point}
|
88
|
+
end
|
89
|
+
|
90
|
+
# override
|
91
|
+
def create_road
|
92
|
+
return if blocks.size <= 1
|
93
|
+
|
94
|
+
# 再帰的に道を作成
|
95
|
+
recursive_road_create(blocks.choice)
|
96
|
+
|
97
|
+
# 道がない部屋で、既存と接しているところを処理
|
98
|
+
connect_cling_block_has_road
|
99
|
+
|
100
|
+
# 道がなく、孤立した部屋を移動
|
101
|
+
move_room_and_connect
|
102
|
+
|
103
|
+
# 行き止まりの交差点を処理
|
104
|
+
add_road_for_dead_end
|
105
|
+
end
|
106
|
+
|
107
|
+
# -------------------------------------------------------------
|
108
|
+
|
109
|
+
def min_block_size
|
110
|
+
unless @min_block_size
|
111
|
+
val = params[:min_block_size]
|
112
|
+
if val
|
113
|
+
# 指定がある場合はそれを評価
|
114
|
+
val = val.to_i
|
115
|
+
else
|
116
|
+
# 指定が無く、min_room_sizeが存在するならそちらに合わせる
|
117
|
+
val = (min_room_size ? min_room_size + 3 : 0)
|
118
|
+
end
|
119
|
+
val = Util::Config.min_block_size if val < Util::Config.min_block_size
|
120
|
+
@min_block_size = val
|
121
|
+
end
|
122
|
+
@min_block_size
|
123
|
+
end
|
124
|
+
|
125
|
+
def min_room_size
|
126
|
+
params[:min_room_size]
|
127
|
+
end
|
128
|
+
|
129
|
+
def max_room_size
|
130
|
+
params[:max_room_size]
|
131
|
+
end
|
132
|
+
|
133
|
+
def max_block_count
|
134
|
+
params[:max_block_count].to_i
|
135
|
+
end
|
136
|
+
|
137
|
+
def min_room_count
|
138
|
+
unless @min_room_count
|
139
|
+
val = params[:min_room_count].to_i
|
140
|
+
# 明示的に「1」という指定がない限り2部屋は作る
|
141
|
+
val = 2 if val <= 0
|
142
|
+
@min_room_count = val
|
143
|
+
end
|
144
|
+
@min_room_count
|
145
|
+
end
|
146
|
+
|
147
|
+
def max_room_count
|
148
|
+
params[:max_room_count].to_i
|
149
|
+
end
|
150
|
+
|
151
|
+
def max_depth
|
152
|
+
params[:max_depth].to_i
|
153
|
+
end
|
154
|
+
|
155
|
+
def cross_road_ratio
|
156
|
+
unless @cross_road_ratio
|
157
|
+
val = params[:cross_road_ratio]
|
158
|
+
if val
|
159
|
+
val = val.to_i
|
160
|
+
# 交差点生成率は 1<=x<=9 / 10
|
161
|
+
val = DEFAULT_CROSS_ROAD_RATIO if (val < 0 || val > 9)
|
162
|
+
else
|
163
|
+
# 指定なし => デフォルト
|
164
|
+
val = DEFAULT_CROSS_ROAD_RATIO
|
165
|
+
end
|
166
|
+
@cross_road_ratio = val
|
167
|
+
end
|
168
|
+
@cross_road_ratio
|
169
|
+
end
|
170
|
+
|
171
|
+
def divide_queue
|
172
|
+
@divide_queue ||= []
|
173
|
+
@divide_queue
|
174
|
+
end
|
175
|
+
|
176
|
+
def done_queue
|
177
|
+
@done_queue ||= []
|
178
|
+
@done_queue
|
179
|
+
end
|
180
|
+
|
181
|
+
def queue_size
|
182
|
+
divide_queue.size + done_queue.size
|
183
|
+
end
|
184
|
+
|
185
|
+
def finish?
|
186
|
+
return true if divide_queue.empty?
|
187
|
+
return true if (max_block_count > 0 && queue_size >= max_block_count)
|
188
|
+
false
|
189
|
+
end
|
190
|
+
|
191
|
+
def dividable_block?(b)
|
192
|
+
# そもそも分割対象ではない => false
|
193
|
+
return false unless b.dividable?
|
194
|
+
|
195
|
+
# 最大深度の指定がない => true
|
196
|
+
return true if max_depth <= 0
|
197
|
+
|
198
|
+
# 最大深度に達したら分割しない
|
199
|
+
b.depth >= max_depth ? false : true
|
200
|
+
end
|
201
|
+
|
202
|
+
def divide
|
203
|
+
# 再帰処理
|
204
|
+
loop do
|
205
|
+
break if finish?
|
206
|
+
|
207
|
+
tb = divide_queue.shift
|
208
|
+
break unless tb
|
209
|
+
|
210
|
+
list = tb.divide
|
211
|
+
unless list
|
212
|
+
# 分割できなかったので、元をdone_queueへ
|
213
|
+
done_queue << tb
|
214
|
+
break
|
215
|
+
end
|
216
|
+
|
217
|
+
list.each do |b|
|
218
|
+
if dividable_block?(b)
|
219
|
+
divide_queue << b
|
220
|
+
else
|
221
|
+
done_queue << b
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# queueをまとめる
|
227
|
+
divide_queue.each{|b| done_queue << b}
|
228
|
+
end
|
229
|
+
|
230
|
+
# -------------------------------------------------------------
|
231
|
+
|
232
|
+
def recursive_road_create(target)
|
233
|
+
# 全部道がつながったら終了
|
234
|
+
return if blocks.all?{|b| b.has_road?}
|
235
|
+
|
236
|
+
# まだ道の処理をしてない、接しているblockを探す
|
237
|
+
yet_block = blocks.reject{|b| b.road_created?}
|
238
|
+
cling_list = create_cling_list(target, yet_block)
|
239
|
+
|
240
|
+
# 行き止まり => 終了
|
241
|
+
return if cling_list.size <= 0
|
242
|
+
|
243
|
+
# 接しているblockに道を作る
|
244
|
+
next_block = connect_cling_blocks(target, cling_list)
|
245
|
+
|
246
|
+
# 作成完了
|
247
|
+
target.road_created
|
248
|
+
|
249
|
+
# 次を再帰的呼び出し
|
250
|
+
recursive_road_create(next_block)
|
251
|
+
end
|
252
|
+
|
253
|
+
def connect_cling_block_has_road
|
254
|
+
# 道がない部屋を探す
|
255
|
+
remains = blocks.select{|b| b.has_room? && ! b.has_road?}
|
256
|
+
return if remains.empty?
|
257
|
+
|
258
|
+
# すでに完了しているblockの数を確認
|
259
|
+
done_count = blocks.select{|b| b.has_room? && b.has_road?}.size
|
260
|
+
|
261
|
+
remains.each do |target|
|
262
|
+
# min_room_countを満たすならランダム
|
263
|
+
if done_count >= min_room_count
|
264
|
+
next unless bool_rand
|
265
|
+
end
|
266
|
+
|
267
|
+
# 接しているblockに道があるならつなぐ
|
268
|
+
c_list = create_cling_list(target, blocks.select{|b| b.has_road?}).flatten
|
269
|
+
return if c_list.empty?
|
270
|
+
target.create_road_to(c_list.pickup!)
|
271
|
+
c_list.each{|b| target.add_remain_cling_blocks(b)}
|
272
|
+
|
273
|
+
done_count += 1
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
def move_room_and_connect
|
278
|
+
# 完了してしたblockの数を確認し、min_room_countを満たしていたら終わり
|
279
|
+
done_count = blocks.select{|b| b.has_room? && b.has_road?}.size
|
280
|
+
return if done_count >= min_room_count
|
281
|
+
|
282
|
+
# まだ道がない部屋を探す
|
283
|
+
remains = blocks.select{|b| b.has_room? && ! b.has_road?}
|
284
|
+
return if remains.empty?
|
285
|
+
|
286
|
+
remains.each do |target|
|
287
|
+
# 元の部屋等の削除
|
288
|
+
target.remove_all
|
289
|
+
|
290
|
+
# 改めて部屋を作る先を決める
|
291
|
+
enable_blocks = blocks.select{|b| b.has_remain_cling_blocks?}
|
292
|
+
next if enable_blocks.empty?
|
293
|
+
|
294
|
+
org_block = enable_blocks.choice
|
295
|
+
room_block = org_block.remain_cling_blocks.pickup!
|
296
|
+
|
297
|
+
# 部屋作成
|
298
|
+
room_block.create_room(:min => min_room_size, :max => max_room_size)
|
299
|
+
|
300
|
+
# 接続
|
301
|
+
room_block.create_road_to(org_block)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
def create_cling_list(block, list)
|
306
|
+
top_list = collect_cling_block(block, list,:top)
|
307
|
+
bottom_list = collect_cling_block(block, list, :bottom)
|
308
|
+
left_list = collect_cling_block(block, list, :left)
|
309
|
+
right_list = collect_cling_block(block, list, :right)
|
310
|
+
|
311
|
+
[top_list, bottom_list, left_list, right_list].select{|a| a.size > 0}
|
312
|
+
end
|
313
|
+
|
314
|
+
def collect_cling_block(block, list, direction)
|
315
|
+
case direction
|
316
|
+
when :top
|
317
|
+
list.select{|b| block.cling_to_top?(b)}
|
318
|
+
when :bottom
|
319
|
+
list.select{|b| block.cling_to_bottom?(b)}
|
320
|
+
when :left
|
321
|
+
list.select{|b| block.cling_to_left?(b)}
|
322
|
+
when :right
|
323
|
+
list.select{|b| block.cling_to_right?(b)}
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
def connect_cling_blocks(target, cling_list)
|
328
|
+
return unless cling_list
|
329
|
+
return if cling_list.size <= 0
|
330
|
+
|
331
|
+
# 4方向で選択可能なblock配列から一つ選ぶ
|
332
|
+
direction_list = cling_list.pickup!
|
333
|
+
next_block = direction_list.pickup!
|
334
|
+
|
335
|
+
# ブロックに道をつなぐ
|
336
|
+
target.create_road_to(next_block)
|
337
|
+
|
338
|
+
# その方向の残りを接しているblockとして記録
|
339
|
+
direction_list.each{|b| target.add_remain_cling_blocks(b)}
|
340
|
+
|
341
|
+
# 残りの方向もランダムにつなぐ
|
342
|
+
cling_list.each do |d_list|
|
343
|
+
bl = d_list.pickup! if bool_rand
|
344
|
+
target.create_road_to(bl) if bl
|
345
|
+
d_list.each{|b| target.add_remain_cling_blocks(b)}
|
346
|
+
end
|
347
|
+
|
348
|
+
# 次の対象返却
|
349
|
+
next_block
|
350
|
+
end
|
351
|
+
|
352
|
+
def add_road_for_dead_end
|
353
|
+
deadends = blocks.select{|b| b.dead_end?}
|
354
|
+
return if deadends.empty?
|
355
|
+
|
356
|
+
deadends.each do |target|
|
357
|
+
# まだ作成していない方向に何か接しているか?
|
358
|
+
c_list = []
|
359
|
+
block_list = blocks.select{|b| b.has_road?}
|
360
|
+
target.remain_direction.each do |d|
|
361
|
+
ret = collect_cling_block(target, block_list, d)
|
362
|
+
c_list += ret.flatten
|
363
|
+
end
|
364
|
+
next if c_list.empty?
|
365
|
+
target.create_road_to(c_list.pickup!)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
end
|
370
|
+
end
|
371
|
+
end
|