Advertisement
Guest User

Untitled

a guest
Feb 9th, 2016
63
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.48 KB | None | 0 0
  1.  
  2. require 'colorize'
  3.  
  4. CHIKEI_CELL = { 0 => ' ', 1 => ' '.on_white, 2 => '■', 3 => '◆', 4 => ' '.on_blue, 5 => '濡' }
  5.  
  6. # マップは66×42。
  7. def render_chikei(chikei)
  8. floor = chikei.unpack("C1386").flat_map { |byte|
  9. [byte & 0x0f, (byte & 0xf0) >> 4]
  10. }.each_slice(66).to_a
  11.  
  12. return floor.map { |row| row.map { |nibble| CHIKEI_CELL[nibble] } }
  13. end
  14.  
  15. def print_grid(grid)
  16. puts grid.map(&:join)
  17. end
  18.  
  19. def print_rooms(rooms)
  20. rooms.scan(/.{4}/m).each.with_index do |room, room_num|
  21. x1, y1, x2, y2 = room.unpack('CCCC')
  22. break if y1 == 0xff
  23.  
  24. puts "部屋#{room_num}: (#{x1},#{y1})〜(#{x2},#{y2})"
  25. end
  26. end
  27.  
  28. CHARACTER_STATUS = ["起きている", "浅い眠り", "深い眠り", "金縛り"]
  29. def chara_status(status)
  30. CHARACTER_STATUS[status]
  31. end
  32. CHARACTER_DIR = %w[上 右上 右 右下 下 左下 左 左上]
  33.  
  34. CHARACTER_KIND = {
  35. 75 => ['盗賊番'],
  36. 107 => ['ひまガッパ', 'いやすぎガッパ', 'たまらんガッパ'],
  37. 88 => ['マムル', 'あなぐらマムル', '洞窟マムル'],
  38. 83 => ['トド'],
  39. 80 => ['タウロス', 'ミノタウロス', 'メガタウロス'],
  40. 122 => ['うしわか丸', 'クロウ丸', 'ヨシツネ丸'],
  41. 116 => ['プチフェニックス'],
  42. 66 => ['怪盗ペリカンのエレキ', '怪盗ペリカン2世のエレキ', '怪盗ペリカン3世のエレキ', '怪盗ペリカン4世のエレキ'],
  43. 92 => %w[エーテルデビル ファントムデビル ミラージュデビル アストラルデビル],
  44. 89 => %w[しろがねマムル おうごんマムル],
  45. 95 => ['キグニ族'],
  46. 105 => %w[ダイキライ エレーキライ チョーキライ],
  47. 73 => %w[店主 店長 大店長],
  48. 74 => ['番犬'],
  49. 131 => %w[とげドラゴン アースニードル サンダーランス],
  50. 90 => ['にぎりみならい', 'にぎりへんげ', 'にぎり親方', 'にぎりもとじめ'],
  51. 86 => ['コドモ戦車'],
  52. 109 => ['パ王', 'パオパ王', 'パオパ王ーン'],
  53. 111 => ['タイガーウッホ', 'タイガーウホーン', 'タイガーウホホーン'],
  54. 101 => ['わらうポリゴン', 'まわるポリゴン', 'おどるポリゴン', 'うたうポリゴン'],
  55. 115 => ['オトト兵', 'オトト軍曹', 'オトト大将', 'オトト元帥'],
  56. 103 => ['チンタラ', 'ちゅうチンタラ', 'おおチンタラ'],
  57. }
  58. def character_name(kind, level)
  59. names = CHARACTER_KIND[kind]
  60. if names.nil?
  61. return "レベル#{level}番号#{kind}"
  62. end
  63.  
  64. name = names[level - 1]
  65. if names.size == 1 or name.nil?
  66. return "レベル#{level}#{names[0]}"
  67. end
  68.  
  69. return name
  70. end
  71.  
  72. def print_charas(charas)
  73. raise ArgumentError unless charas.bytesize == 320
  74.  
  75. puts "キャラクター:"
  76. charas.scan(/.{10}/m).each do |chara|
  77. y, x, dir, kind, level, status = chara.unpack('CCvvvv')
  78. break if kind == 0
  79. puts " #{character_name(kind, level)}(#{x},#{y})\t#{CHARACTER_DIR[dir]}向き, #{chara_status(status)}"
  80. end
  81. end
  82.  
  83. def print_floor_info(floor_info)
  84. _, difficulty = floor_info.unpack('a18C')
  85. puts "難易度: #{'★'*difficulty}"
  86. end
  87.  
  88. # NUL終端されたShift_JISの文字列をUTF8に変換する
  89. def to_utf8(str)
  90. str.sub(/\0.*/, '').force_encoding('CP932').encode('UTF-8')
  91. end
  92.  
  93. def print_book_info(header)
  94. magic_number, version_number, num_floors, book_name = header.unpack("a3 n N a27")
  95. book_name = to_utf8(book_name)
  96. puts "全#{num_floors}階「#{book_name}」"
  97. end
  98.  
  99. ARGF.set_encoding('ASCII-8BIT')
  100. header = ARGF.read(36)
  101.  
  102. # ファイルの先頭に36バイトの情報。
  103. # "FEI"
  104. # 16 ビットで 1。
  105. # フロア数、32ビット。
  106. # 27バイトNUL終端されたShift_JISの問題集名。
  107.  
  108. print_book_info(header)
  109.  
  110. # フロアごとに2716バイトある。
  111. # 部屋の定義が60バイトある。1部屋4バイト。左上の座標に2バイト(x, y の順)と、右下の座標(部屋に含まれる)に2バイトで15部屋まで。
  112. # 座標が 0xff だとデータの終了を意味する。
  113. # 地形情報。1386バイト。
  114.  
  115. # 1235バイトのブロック?がある。
  116. # オフセット0から10バイト単位でキャラクターの情報を配置する部分がある。
  117. # キャラクター:
  118. # y x 向き 00 種類 00 レベル 00 状態 00 (バイト×2とショート×4かもしれない)
  119.  
  120. # オフセット320から8バイト単位でアイテムの情報を配置する部分がある。
  121. # アイテム
  122. # y x 種類 00 数 00 フラグ 00
  123. # 例 05 05 81 00 20 00 01 00
  124. # アイテムのフラグ:
  125. # 01 識別済み
  126. # 02 祝福
  127. # 04 呪い
  128.  
  129. def print_items(items)
  130. res = []
  131. items.scan(/.{8}/m).each do |item|
  132. y, x, kind, num, flags = item.unpack('CCvsv')
  133. break if y == kind
  134. res << "#{item_name(kind, num, flags)}(#{x},#{y})"
  135. end
  136. puts "アイテム: #{res.join(', ')}"
  137. end
  138.  
  139. def item_status(flags)
  140. return "祝福された" if flags & 0x2 != 0
  141. return "呪われた" if flags & 0x4 != 0
  142. return ''
  143. end
  144.  
  145. ITEM_KIND = {
  146. 214 => '聖域の巻物',
  147. 368 => 'トンネルの杖',
  148. 170 => '超不幸の種',
  149. 566 => 'ギタン',
  150. 157 => '薬草',
  151. 173 => '毒草',
  152. 8 => 'ドラゴン草',
  153. 160 => 'つるはし',
  154. 352 => 'ふきとばしの杖',
  155. 142 => 'デブータの石',
  156. 364 => '引きよせの杖',
  157. 176 => '高とび草',
  158. 356 => '場所替えの杖',
  159. 283 => '軟投の秘技書',
  160. 25 => 'モーニングスター',
  161. 9 => 'サトリのつるはし',
  162. 145 => '大砲の弾',
  163. 309 => '岩石割りの秘技書',
  164. 569 => 'ンドゥバ',
  165. 108 => '身代わりの腕輪',
  166. 374 => 'クォーターの杖',
  167. 361 => '鈍足の杖',
  168. 357 => 'かなしばりの杖',
  169. 567 => '風魔石',
  170. 4 => 'ドラゴンキラー',
  171. 372 => 'ばくだんの杖',
  172. 209 => 'ゾワゾワの巻物',
  173. 445 => 'マムルの箱',
  174. 365 => '火ばしらの杖',
  175. 219 => '落石の巻物',
  176. 94 => 'まがりの腕輪',
  177. 102 => 'ワナ師の腕輪',
  178. 376 => 'イカリの杖',
  179. 168 => 'くねくね草',
  180. 166 => 'しあわせ草',
  181. 277 => '鬼月の秘技書',
  182. 26 => '如意棒',
  183. 62 => 'サトリの盾',
  184. 177 => 'すばやさの種',
  185. 301 => '四股の秘技書',
  186. 358 => '一時しのぎの杖',
  187. 129 => '木の矢',
  188. }
  189.  
  190. def item_name(kind, num, flags)
  191. status = item_status(flags)
  192. basename = ITEM_KIND[kind] || "アイテム#{kind}"
  193. quantity = (num != 0 ? "[#{num}]" : '')
  194. return "#{status}#{basename}#{quantity}"
  195. end
  196.  
  197. # オフセット832からワナの情報を6バイト単位で配置する部分がある。
  198. # ワナ:
  199. # y x 種類 00 フラグ 00 (フラグはショートかもしれない)
  200. # ワナのフラグ:
  201. # 01 可視
  202. # 02 強制バネ(不動+耐水)
  203. # 04 拾えず(赤くなる)
  204. # 例えば、拾えずで可視の場合は 05 になる。
  205.  
  206. def print_traps(traps)
  207. res = []
  208. traps.scan(/.{6}/m).map do |trap|
  209. y, x, kind, flags = trap.unpack('CCvv')
  210. break if kind == 0
  211.  
  212. res << "#{trap_name(kind, flags)}(#{x},#{y})"
  213. end
  214. puts "罠: " + res.join(', ')
  215. end
  216.  
  217. def trap_status(flags)
  218. res = []
  219. res << "拾えない" if flags & 0x4 != 0
  220. res << "不可視の" if flags & 0x1 == 0
  221. res << "強制" if flags & 0x2 != 0
  222. return res.join
  223. end
  224.  
  225. TRAP_KIND = {
  226. 35 => '丸太のワナ',
  227. 6 => '落石のワナ',
  228. 27 => '毒矢のワナ',
  229. 30 => '地雷',
  230. 25 => '木の矢のワナ',
  231. 28 => '落し穴',
  232. 12 => '一方通行のワナ(左)',
  233. 13 => '一方通行のワナ(右)',
  234. 14 => '一方通行のワナ(下)',
  235. 15 => '一方通行のワナ(上)',
  236. 7 => '大落石のワナ',
  237. 34 => '水滴ポットン',
  238. 19 => '警報スイッチ',
  239. 32 => 'デロデロの湯',
  240. 39 => 'いかずちのワナ',
  241. 18 => 'モンスターのワナ',
  242. 31 => '大型地雷'
  243. }
  244. def trap_name(kind, flags)
  245. status = trap_status(flags)
  246. basename = TRAP_KIND[kind] || "TRAP#{kind}"
  247. return "#{status}#{basename}"
  248. end
  249.  
  250. # アスカの座標(オフセット1216から2バイト)や
  251. # 階段(オフセット1222から2バイト)の座標を記録する部分がある。
  252.  
  253. # フロア名。32バイト?
  254. # 3バイト
  255. loop.with_index(1) do |_, floor_num|
  256. block = ARGF.read(2716)
  257. break unless block
  258. if block.bytesize != 2716
  259. raise 'premature end of block'
  260. end
  261. rooms, chikei, charas, items, traps, floor_info, floor_name, three_byte = block.unpack("a60 a1386 a320 a512 a384 a19 a32 a3")
  262. floor_name = to_utf8(floor_name)
  263.  
  264. puts
  265. puts "#{floor_num}F「#{floor_name}」"
  266. print_floor_info(floor_info)
  267.  
  268. print_rooms(rooms)
  269. print_charas(charas)
  270.  
  271. print_items(items)
  272. print_traps(traps)
  273.  
  274. asuka_y, asuka_x = floor_info.unpack("CC")
  275. _, stairs_y, stairs_x = floor_info.unpack("a6CC")
  276.  
  277. grid = render_chikei(chikei)
  278. grid[stairs_y][stairs_x] = "階"
  279. grid[asuka_y][asuka_x] = "@".bold.magenta
  280. print_grid grid
  281. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement