Class: RDGC::Maker::DivideDungeonMaker

Inherits:
Object
  • Object
show all
Includes:
DungeonMaker
Defined in:
lib/rdgc/maker/divide_dungeon_maker.rb

Constant Summary collapse

DEFAULT_CROSS_ROAD_RATIO =
2

Class Method Summary collapse

Instance Method Summary collapse

Methods included from DungeonMaker

#blocks, #clear_block, #create_pure_blocks, #make, #params

Class Method Details

.create(width, height, params = nil) ⇒ Object



7
8
9
10
11
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 7

def self.create(width, height, params = nil)
  dm = self.new
  list = dm.make(width, height, params)
  Map::Board.create_from_blocks(list)
end

Instance Method Details

#add_road_for_dead_endObject



384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 384

def add_road_for_dead_end
  deadends = blocks.select{|b| b.dead_end?}
  return if deadends.empty?

  deadends.each do |target|
    # まだ作成していない方向に何か接しているか?
    c_list = []
    block_list = blocks.select{|b| b.has_road?}
    target.remain_direction.each do |d|
      ret = collect_cling_block(target, block_list, d)
      c_list += ret.flatten
    end
    next if c_list.empty?
    target.create_road_to(c_list.pickup!)
  end
end

#collect_cling_block(block, list, direction) ⇒ Object



346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 346

def collect_cling_block(block, list, direction)
  case direction
  when :top
    list.select{|b| block.cling_to_top?(b)}
  when :bottom
    list.select{|b| block.cling_to_bottom?(b)}
  when :left
    list.select{|b| block.cling_to_left?(b)}
  when :right
    list.select{|b| block.cling_to_right?(b)}
  end
end

#connect_cling_block_has_roadObject



285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 285

def connect_cling_block_has_road
  # 道がない部屋を探す
  remains = blocks.select{|b| b.has_room? && ! b.has_road?}
  return if remains.empty?

  # すでに完了しているblockの数を確認
  done_count = blocks.select{|b| b.has_room? && b.has_road?}.size

  remains.each do |target|
    # min_room_countを満たすならランダム
    if done_count >= min_room_count
      next unless bool_rand
    end

    # 接しているblockに道があるならつなぐ
    c_list = create_cling_list(target, blocks.select{|b| b.has_road?}).flatten
    return if c_list.empty?
    target.create_road_to(c_list.pickup!)
    c_list.each{|b| target.add_remain_cling_blocks(b)}

    done_count += 1
  end
end

#connect_cling_blocks(target, cling_list) ⇒ Object



359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 359

def connect_cling_blocks(target, cling_list)
  return unless cling_list
  return if cling_list.size <= 0

  # 4方向で選択可能なblock配列から一つ選ぶ
  direction_list = cling_list.pickup!
  next_block = direction_list.pickup!

  # ブロックに道をつなぐ
  target.create_road_to(next_block)

  # その方向の残りを接しているblockとして記録
  direction_list.each{|b| target.add_remain_cling_blocks(b)}

  # 残りの方向もランダムにつなぐ
  cling_list.each do |d_list|
    bl = d_list.pickup! if bool_rand
    target.create_road_to(bl) if bl
    d_list.each{|b| target.add_remain_cling_blocks(b)}
  end

  # 次の対象返却
  next_block
end

#create_cling_list(block, list) ⇒ Object



337
338
339
340
341
342
343
344
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 337

def create_cling_list(block, list)
  top_list = collect_cling_block(block, list,:top)
  bottom_list = collect_cling_block(block, list, :bottom)
  left_list = collect_cling_block(block, list, :left)
  right_list = collect_cling_block(block, list, :right)

  [top_list, bottom_list, left_list, right_list].select{|a| a.size > 0}
end

#create_roadObject

override



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 40

def create_road
  return if blocks.size <= 1

  # 再帰的に道を作成
  recursive_road_create(blocks.choice)

  # 道がない部屋で、既存と接しているところを処理
  connect_cling_block_has_road

  # 道がなく、孤立した部屋を移動
  move_room_and_connect

  # 行き止まりの交差点を処理
  add_road_for_dead_end
end

#create_roomObject

override



35
36
37
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 35

def create_room
  force_room_count ? create_room_force : create_room_normal
end

#create_room_forceObject



252
253
254
255
256
257
258
259
260
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 252

def create_room_force
  blocks.shuffle.each.with_index do |b, i|
    if (i+1) <= force_room_count.to_i
      b.create_room(:min => min_room_size, :max => max_room_size)
    else
      b.create_cross_point
    end
  end
end

#create_room_normalObject



197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 197

def create_room_normal
  # 部屋と交差点を分ける
  room_blocks = []
  cross_blocks = []
  force_room_blocks = []

  param = {
    :room => (10 - cross_road_ratio),
    :cross => cross_road_ratio
  }

  blocks.each do |b|
    # 初回分割のBlockは行き止まりになるので避ける
    if b.depth < 2
      force_room_blocks << b
      next
    end

    r = select_rand(param)
    case r
    when :room
      room_blocks << b
    when :cross
      cross_blocks << b
    end
  end

  room_count = room_blocks.size + force_room_blocks.size
  if max_room_count > 0 && room_count > max_room_count
    # 超えた分だけ移動する
    (room_count - max_room_count).times do
      break if room_blocks.size + force_room_blocks.size <= 1
      break if room_blocks.empty?
      b = room_blocks.pickup!
      cross_blocks << b if b
    end
  end

  room_count = room_blocks.size + force_room_blocks.size
  if room_count < min_room_count
    # 足りない分を移動する
    (min_room_count - room_count).times do
      break if cross_blocks.empty?
      b = cross_blocks.pickup!
      room_blocks << b if b
    end
  end

  # それぞれのblockを処理
  [room_blocks, force_room_blocks].flatten.each do |b|
    b.create_room(:min => min_room_size, :max => max_room_size)
  end
  cross_blocks.each{|b| b.create_cross_point}
end

#create_whole_block(width, height) ⇒ Object

override



16
17
18
19
20
21
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 16

def create_whole_block(width, height)
  tb = DivideTempBlock.create_whole_block(width, height)
  tb.min_size = min_block_size
  tb.dividable
  tb
end

#cross_road_ratioObject



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 108

def cross_road_ratio
  unless @cross_road_ratio
    val = params[:cross_road_ratio]
    if val
      val = val.to_i
      # 交差点生成率は 1<=x<=9 / 10
      val = DEFAULT_CROSS_ROAD_RATIO if (val < 0 || val > 9)
    else
      # 指定なし => デフォルト
      val = DEFAULT_CROSS_ROAD_RATIO
    end
    @cross_road_ratio = val
  end
  @cross_road_ratio
end

#dividable_block?(b) ⇒ Boolean

Returns:

  • (Boolean)


148
149
150
151
152
153
154
155
156
157
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 148

def dividable_block?(b)
  # そもそも分割対象ではない => false
  return false unless b.dividable?

  # 最大深度の指定がない => true
  return true if max_depth <= 0

  # 最大深度に達したら分割しない
  b.depth >= max_depth ? false : true
end

#divideObject



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 159

def divide
  # 再帰処理
  loop do
    break if finish?

    tb = divide_queue.shift
    break unless tb

    list = tb.divide
    unless list
      # 分割できなかったので、元をdone_queueへ
      done_queue << tb
      break
    end

    list.each do |b|
      if dividable_block?(b)
        divide_queue << b
      else
        done_queue << b
      end
    end
  end

  # queueをまとめる
  divide_queue.each{|b| done_queue << b}
    
  # 作成した結果、まだdividableのものがあり、数が足りないなら分割
  if min_block_count > 0 && done_queue.size < min_block_count
    (min_block_count - queue_size).times do
      tb = done_queue.select{|b| b.dividable_size?}.shuffle.shift
      break unless tb
      done_queue.delete(tb)
      tb.divide.each{|b| done_queue << b}
    end
  end
end

#divide_queueObject



128
129
130
131
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 128

def divide_queue
  @divide_queue ||= []
  @divide_queue
end

#done_queueObject



133
134
135
136
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 133

def done_queue
  @done_queue ||= []
  @done_queue
end

#finish?Boolean

Returns:

  • (Boolean)


142
143
144
145
146
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 142

def finish?
  return true if divide_queue.empty?
  return true if (max_block_count > 0 && queue_size >= max_block_count)
  false
end

#force_room_countObject



124
125
126
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 124

def force_room_count
  params[:force_room_count]
end

#make_blocks(tb) ⇒ Object



23
24
25
26
27
28
29
30
31
32
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 23

def make_blocks(tb)
  # 分割キューに最初のBlockを入れる
  divide_queue << tb

  # 再帰分割
  divide

  # 完了キューの中身を返す
  done_queue
end

#max_block_countObject



86
87
88
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 86

def max_block_count
  params[:max_block_count].to_i
end

#max_depthObject



104
105
106
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 104

def max_depth
  params[:max_depth].to_i
end

#max_room_countObject



100
101
102
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 100

def max_room_count
  params[:max_room_count].to_i
end

#max_room_sizeObject



78
79
80
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 78

def max_room_size
  params[:max_room_size]
end

#min_block_countObject



82
83
84
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 82

def min_block_count
  params[:min_block_count].to_i
end

#min_block_sizeObject




58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 58

def min_block_size
  unless @min_block_size
    val = params[:min_block_size]
    if val
      # 指定がある場合はそれを評価
      val = val.to_i
    else
      # 指定が無く、min_room_sizeが存在するならそちらに合わせる
      val = (min_room_size ? min_room_size + 3 : 0)
    end
    val = Util::Config.min_block_size if val < Util::Config.min_block_size
    @min_block_size = val
  end
  @min_block_size
end

#min_room_countObject



90
91
92
93
94
95
96
97
98
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 90

def min_room_count
  return force_room_count if force_room_count
  unless @min_room_count
    val = params[:min_room_count].to_i
    # force_room_countを使わない限り、1部屋・部屋なしは作れない
    @min_room_count = (val > 2 ? val : 2)
  end
  @min_room_count
end

#min_room_sizeObject



74
75
76
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 74

def min_room_size
  params[:min_room_size]
end

#move_room_and_connectObject



309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 309

def move_room_and_connect
  # 完了してしたblockの数を確認し、min_room_countを満たしていたら終わり
  done_count = blocks.select{|b| b.has_room? && b.has_road?}.size
  return if done_count >= min_room_count

  # まだ道がない部屋を探す
  remains = blocks.select{|b| b.has_room? && ! b.has_road?}
  return if remains.empty?

  remains.each do |target|
    # 元の部屋等の削除
    target.remove_all

    # 改めて部屋を作る先を決める
    enable_blocks = blocks.select{|b| b.has_remain_cling_blocks?}
    next if enable_blocks.empty?

    org_block = enable_blocks.choice
    room_block = org_block.remain_cling_blocks.pickup!

    # 部屋作成
    room_block.create_room(:min => min_room_size, :max => max_room_size)

    # 接続
    room_block.create_road_to(org_block)
  end
end

#queue_sizeObject



138
139
140
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 138

def queue_size
  divide_queue.size + done_queue.size
end

#recursive_road_create(target) ⇒ Object




264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/rdgc/maker/divide_dungeon_maker.rb', line 264

def recursive_road_create(target)
  # 全部道がつながったら終了
  return if blocks.all?{|b| b.has_road?}

  # まだ道の処理をしてない、接しているblockを探す
  yet_block = blocks.reject{|b| b.road_created?}
  cling_list = create_cling_list(target, yet_block)

  # 行き止まり => 終了
  return if cling_list.size <= 0

  # 接しているblockに道を作る
  next_block = connect_cling_blocks(target, cling_list)

  # 作成完了
  target.road_created

  # 次を再帰的呼び出し
  recursive_road_create(next_block)
end