*通常マップの生成 [#d56d480d]
 makelevel()
  +- makerooms()
  |   +- rnd_rect()                     NhRectをランダムに1個選ぶ
  |   +- craete_vault()                 金庫を作る
  |   +- create_room()
  |       +- check_room()
  |       +- split_rects()
  |       +- add_room()
  |           +- do_room_or_subroom()
  +- sort_rooms()                       rooms[]の各要素をX座標順に並べかえる
  +- makecorridors()
  |   +- join()
  |       +- dig_corridor()
  |       +- dodoor()
  |           +- dosdoor()
  |               +- add_door()
  +- mkroom()                           ランダムに選んだ部屋を特殊部屋にする
  +- topologize()                       levl[][]に部屋番号を設定

**構造体 [#k20547e9]
:mkroom rooms[]|その階の部屋情報を格納している~
階データとともに保存される~
lx,ly,hx,hy は 壁(外周)を含まない 床部分のみの部屋の長方形をあらわす
 rooms[0             ] 〜 rooms[MAXNROFROOMS    ]        room用
 rooms[MAXNROFROOMS+1] 〜 rooms[MAXNROFROOMS*2+1]        subroom用

subrooms[i] は実際には rooms[MAXNROFROOMS+1+i] を指す 

:nroom|部屋の数を格納している。rooms[0] 〜 rooms[nroom-1] が使用中~
階データとともに保存される

:nsubroom|サブ部屋の数を格納している。nroomと同様~
階データとともに保存される

:coord doors[]|各部屋の扉の位置を保存している~
rooms[].fdoor から rooms[].doorct 個がその部屋の扉位置情報~
階データとともに保存される~
店主の生成位置決定・店や特殊部屋で扉付近に物やモンスターを置かない処理などで微妙に使われる~

**部屋の生成 [#e8b1378c]
:create_room()| 
《完全ランダム配置の場合》

下図は3x2の部屋に対して最小限必要な領域
 . :ランダムに選んだ部屋の広さ((3〜14 or 3〜10), 3〜6)
 x :必要な余白
 # :部屋配置可能エリア(NhRectが大きければこの部分が増える)

-通常
 xxxxxxxxxxxxxx
 xxxxxxxxxxxxxx
 xxxxxxxxxxxxxx
 xxxx...###xxxx
 xxxx...###xxxx
 xxxx######xxxx
 xxxx######xxxx
 xxxx######xxxx
 xxxxxxxxxxxxxx
 xxxxxxxxxxxxxx
 xxxxxxxxxxxxxx

-左端
 xxxxxxxxxxx
 xxxxxxxxxxx
 xxxxxxxxxxx
 xxx...#xxxx
 xxx...#xxxx
 xxx####xxxx
 xxx####xxxx
 xxx####xxxx
 xxxxxxxxxxx
 xxxxxxxxxxx
 xxxxxxxxxxx

-上端
 xxxxxxxxxxxxxx
 xxxxxxxxxxxxxx
 xxxx...###xxxx
 xxxx...###xxxx
 xxxx######xxxx
 xxxxxxxxxxxxxx
 xxxxxxxxxxxxxx
 xxxxxxxxxxxxxx
 xxxxxxxxxxxxxx

(選んだNhRectが上下いっぱいに伸びている && 作成済み部屋数が少ない && 配置しようとしている部屋の下端が画面中央より下)の場合、上端を Y=(2〜4) に移動、縦幅を1減らす (なるべく上下に詰めて配置するため?)

split_rects() で部屋内部+外周(壁)の領域を予約済みにする

:check_room()|部屋座標が条件を満たせばTRUE, そうでなければFALSEを返す~
部屋(壁含まず)を(3,2)-(77,18)の範囲でクリップ~
かつ、周囲(*部分、画面外可)と既存の部屋が重複しないように部屋を(必要なら)縮小する~
(levl[][].typ != STONE) なら重複していると判定~
縮小せずに諦めることもある(ランダム)
 ***********
 ***********
 ***-----***
 ***|...|***
 ***|...|***
 ***-----***
 ***********
 ***********

:add_room()|do_room_or_subroom() を呼んで部屋を追加する

:do_room_or_subroom()|四角い部屋を作る~
rooms[] の部屋情報を設定~
levl[][] に壁と床を書く(specialを指定すると書かない)~
扉の数は 0~

:topologize()|通常の四角い部屋の床および壁に部屋番号(levl[][].roomno)を設定する~
壁が隣の部屋と共通の場合は部屋番号に SHARED を設定する~
subroom がある場合は再帰的に topologize() を呼び出す

:ROOMOFFSET|levl[][].roomno に設定する値は、(実際の部屋番号 + ROOMOFFSET)~
0 〜 (ROOMOFFSET-1) はフラグとして使われる~

|roomno|意味|
|0|部屋ではない|
|1|複数の部屋で共有されている|
|2|複数の部屋で共有されている|

**通路・扉の生成 [#p7ca121d]
:makecorridors()|まず近い順(rooms[]の隣同士)で通路でつなぐ~
次にrooms[]1個おきに見て、まだ連結されてない部屋があればつなぐ~
さらに総当りで見ていって、まだ連結されてない部屋があればつなぐ~
ランダムに通路追加

部屋同士が連結されているかどうかの判定には配列 smeq[] を使っているが、smeq[] は部屋同士の連結状態を厳密に示すものではないことに注意 

:join()|部屋番号 a, b を通路でつなぎ、扉を生成する~
nxcor = FALSE なら通路の両端(何らかの理由で通路が途中で途切れた場合は、出発点のみ)に扉を生成する~
nxcor = TRUE なら、通路の開始点がSTONEかどうかをチェックし、そうでなければ通路・扉を生成しない。また、dig_corridor() に nxcor = TRUE をそのまま渡しているため、途中でランダムに通路を伸ばすのを止めることがある。それ以外は nxcor = FALSE の場合と同じ~

:dosdoor()|levl[][] に扉を書き、扉情報を設定する~
mimic を置いたりする

:add_door()|doors[] に指定の部屋の扉位置情報を追加(挿入)する~
rooms[], doors[] の構造上、doors[] の中身および rooms[].fdoor をずらさないといけなかったりするので、そのようなこともやる~

***makecorridors() と smeq[] の動作の例 [#f6d57897]
smeq[] を設定しているのは create_room() だけなので注意

初期状態~
makerooms() で部屋を作った直後
|index|0|1|2|3|4|h
|~smeq[]|0|1|2|3|4|

sort_rooms() した後
|index|0|1|2|3|4|h
|~smeq[]|3|4|1|2|0|

1周目 隣同士でつなぐ
room[0] と room[1] を join()
|index|0|1|2|3|4|h
|~smeq[]|3|3|1|2|0|

room[1] と room[2] を join()
|index|0|1|2|3|4|h
|~smeq[]|3|1|1|2|0|

room[2] と room[3] を join()
|index|0|1|2|3|4|h
|~smeq[]|3|1|1|1|0|

room[3] と room[4] を join()
|index|0|1|2|3|4|h
|~smeq[]|3|1|1|0|0|

2周目 1つおきに見て接続済みでなければつなぐ
room[0] と room[2] を join()
|index|0|1|2|3|4|h
|~smeq[]|1|1|1|0|0|

room[1] と room[3] を join()
|index|0|1|2|3|4|h
|~smeq[]|1|0|1|0|0|

room[2] と room[4] を join()
|index|0|1|2|3|4|h
|~smeq[]|1|0|0|0|0|

3周目 総当りで見て未接続があればつなぐ
room[0] と room[1] を join()
|index|0|1|2|3|4|h
|~smeq[]|0|0|0|0|0|

4周目 ランダムにつなぐ
(略)

**注意が必要な関数 [#xf5ca4d3]
***sort_rooms() に注意 [#q9fcccce]
-LEVELタイプの特殊レベルは、店などの特殊部屋の中身を作ってから sort_rooms() してしまっている。これって topologize() で levl[][] に書いた部屋番号と実際の部屋番号が一致しなくなっちゃってまずいんじゃないの?~
→ 実際まずい。下のように店の部屋番号がソートされて移動してしまうようなレベルを作ると、店の挙動がおかしくなる。店は SUBROOM で作ること。

 LEVEL: "testlev"
 ROOM: "shop",     random, (5,5),  random, random
 ROOM: "ordinary", random, (1,1),  random, random
 ROOM: "ordinary", random, random, random, random
 ROOM: "ordinary", random, random, random, random
 RANDOM_CORRIDORS

-doors[] は部屋番号順に並んでいなければならないのに、sort_rooms() しちゃったら破壊されちゃうんじゃないの?~
→ 実はそのとおり。ROOM に DOOR で扉を作るようなレベルを作ると、部屋と扉の関係が正しく保持されない。現状では、LEVELタイプのすべてのレベルは SUBROOM にしか扉を作っておらず、ROOM に扉を作るのは sort_rooms() した後なので、問題がおきていないだけ。MAZEタイプのレベルは sort_rooms() を呼ばないので問題ない。

***add_door() に注意 [#n2e87fa9]
-add_door() で room door と subroom door を交互に登録すると破綻するように見えるけど…~
→ 実はそのとおり。LEVELタイプのレベルは現状 subroom door → room door の順に扉を作るものしか存在しないので問題が発生していないだけ。通常レベルやMAZEタイプのレベルはシステム上 room door しか作らないため問題はない。

-というか subroom door がある状態で room door の挿入が発生しただけで破綻するように見えるけど…~
→ 実はそのとおり。実際 minetown などで rooms[], doors[] をダンプしてみると、部屋と扉の関係は正しくなっていない。主に扉情報を参照する処理である店の生成時点では正しい情報をもっており、その後扉情報が使われないため、とりあえず問題が起きていないだけのようだ。

トップ   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS