ifmapper 1.0.0 → 1.0.6

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.
Files changed (218) hide show
  1. data/HISTORY.txt +648 -627
  2. data/IFMapper.gemspec +29 -28
  3. data/IFMapper.rbw +31 -31
  4. data/TODO.txt +8 -7
  5. data/bin/IFMapper +31 -31
  6. data/docs/en/index.html +0 -0
  7. data/docs/en/start.html +3 -2
  8. data/docs/en/start.html~ +516 -0
  9. data/docs/es/index.html +0 -0
  10. data/docs/es/start.html +13 -14
  11. data/docs/es/start.html~ +1280 -0
  12. data/docs/images/IFMapper_main.gif +0 -0
  13. data/docs/images/automap.gif +0 -0
  14. data/docs/images/complex_connection.gif +0 -0
  15. data/docs/images/connection.gif +0 -0
  16. data/docs/images/connection_menu.gif +0 -0
  17. data/docs/images/room_description.gif +0 -0
  18. data/docs/images/room_small.gif +0 -0
  19. data/icons/copy.png +0 -0
  20. data/icons/cut.png +0 -0
  21. data/icons/filenew.png +0 -0
  22. data/icons/fileopen.png +0 -0
  23. data/icons/filesave.png +0 -0
  24. data/icons/filesaveas.png +0 -0
  25. data/icons/help.png +0 -0
  26. data/icons/kill.png +0 -0
  27. data/icons/nextpage.png +0 -0
  28. data/icons/paste.png +0 -0
  29. data/icons/prevpage.png +0 -0
  30. data/icons/printicon.png +0 -0
  31. data/icons/redo.png +0 -0
  32. data/icons/room_e.gif +0 -0
  33. data/icons/room_e.xpm +0 -0
  34. data/icons/room_n.gif +0 -0
  35. data/icons/room_n.xpm +0 -0
  36. data/icons/room_ne.gif +0 -0
  37. data/icons/room_ne.xpm +0 -0
  38. data/icons/room_nw.gif +0 -0
  39. data/icons/room_nw.xpm +0 -0
  40. data/icons/room_s.gif +0 -0
  41. data/icons/room_s.xpm +0 -0
  42. data/icons/room_se.gif +0 -0
  43. data/icons/room_se.xpm +0 -0
  44. data/icons/room_sw.gif +0 -0
  45. data/icons/room_sw.xpm +0 -0
  46. data/icons/room_w.gif +0 -0
  47. data/icons/room_w.xpm +0 -0
  48. data/icons/saveas.png +0 -0
  49. data/icons/undo.png +0 -0
  50. data/icons/winapp.png +0 -0
  51. data/icons/zoom.png +0 -0
  52. data/lib/IFMapper/AStar.rb +250 -250
  53. data/lib/IFMapper/Connection.rb +202 -202
  54. data/lib/IFMapper/FXAboutDialogBox.rb +32 -32
  55. data/lib/IFMapper/FXConnection.rb +364 -364
  56. data/lib/IFMapper/FXConnectionDialogBox.rb +124 -124
  57. data/lib/IFMapper/FXDCPostscript.rb +404 -404
  58. data/lib/IFMapper/FXDCPrint.rb +15 -15
  59. data/lib/IFMapper/FXItemList.rb +108 -0
  60. data/lib/IFMapper/FXMap.rb +2147 -2116
  61. data/lib/IFMapper/FXMapColorBox.rb +88 -88
  62. data/lib/IFMapper/FXMapDialogBox.rb +127 -127
  63. data/lib/IFMapper/FXMapFileDialog.rb +34 -34
  64. data/lib/IFMapper/FXMapperSettings.rb +206 -205
  65. data/lib/IFMapper/FXMapperWindow.rb +1592 -1571
  66. data/lib/IFMapper/FXPDFMapExporterOptionsDialogBox.rb +46 -0
  67. data/lib/IFMapper/FXRoom.rb +263 -263
  68. data/lib/IFMapper/FXRoomDialogBox.rb +159 -159
  69. data/lib/IFMapper/FXRoomList.rb +95 -95
  70. data/lib/IFMapper/FXSearchDialogBox.rb +51 -51
  71. data/lib/IFMapper/FXSection.rb +33 -33
  72. data/lib/IFMapper/FXSectionDialogBox.rb +38 -38
  73. data/lib/IFMapper/FXSpline.rb +52 -52
  74. data/lib/IFMapper/FXWarningBox.rb +51 -50
  75. data/lib/IFMapper/GUEReader.rb +445 -445
  76. data/lib/IFMapper/IFMReader.rb +584 -584
  77. data/lib/IFMapper/IFMWriter.rb +245 -227
  78. data/lib/IFMapper/Inform7Writer.rb +579 -573
  79. data/lib/IFMapper/InformReader.rb +478 -478
  80. data/lib/IFMapper/InformWriter.rb +364 -359
  81. data/lib/IFMapper/Map.rb +202 -200
  82. data/lib/IFMapper/MapPrinting.rb +162 -162
  83. data/lib/IFMapper/MapReader.rb +900 -900
  84. data/lib/IFMapper/PDFMapExporter.rb +526 -483
  85. data/lib/IFMapper/Room.rb +153 -151
  86. data/lib/IFMapper/Section.rb +234 -234
  87. data/lib/IFMapper/TADSReader.rb +474 -471
  88. data/lib/IFMapper/TADSWriter.rb +375 -370
  89. data/lib/IFMapper/TranscriptDialogBox.rb +0 -0
  90. data/lib/IFMapper/TranscriptReader.rb +1361 -1359
  91. data/lib/IFMapper/locales/en/Messages.rb +446 -435
  92. data/lib/IFMapper/locales/es/Messages.rb +451 -440
  93. data/lib/IFMapper/locales/es/Messages_iso-8859-1.rb +455 -440
  94. data/lib/IFMapper/locales/es/runme.sh +3 -3
  95. data/maps/A New Life.map b/data/maps/A New → Life.map +0 -0
  96. data/maps/AMFV.map +0 -0
  97. data/maps/AllRoads.map +0 -0
  98. data/maps/Aotearoa.map +0 -0
  99. data/maps/Bronze.map +0 -0
  100. data/maps/Bureaucracy.ifm +0 -0
  101. data/maps/Bureaucracy.map +0 -0
  102. data/maps/CityOfSecrets.map +0 -0
  103. data/maps/DDIV.map +0 -0
  104. data/maps/Following_A_Star.map +0 -0
  105. data/maps/Heated.map +0 -0
  106. data/maps/Heroine.map +0 -0
  107. data/maps/History Repeating.map b/data/maps/History → Repeating.map +0 -0
  108. data/maps/Hollywood_Hijinx.ifm +0 -0
  109. data/maps/Janitor.map +0 -0
  110. data/maps/Jigsaw.ifm +0 -0
  111. data/maps/Jigsaw.map +0 -0
  112. data/maps/LGOP.ifm +0 -0
  113. data/maps/Mercy.ifm +0 -0
  114. data/maps/Ninjas_Fate.map +0 -0
  115. data/maps/Pen_and_Paint.map +0 -0
  116. data/maps/Planetfall.ifm +0 -0
  117. data/maps/Planetfall.map +0 -0
  118. data/maps/Plundered_Hearts.ifm +0 -0
  119. data/maps/QuietEvening.map +0 -0
  120. data/maps/Ralph.ifm +0 -0
  121. data/maps/Reliques_of_Tolti_Alph.map +0 -0
  122. data/maps/Revolution.map +0 -0
  123. data/maps/Robots_of_Dawn.ifm +0 -0
  124. data/maps/SavoirFare.map +0 -0
  125. data/maps/Seastalker.ifm +0 -0
  126. data/maps/Seastalker.map +0 -0
  127. data/maps/Sherlock.ifm +0 -0
  128. data/maps/SoFar.ifm +0 -0
  129. data/maps/Starcross.ifm +0 -0
  130. data/maps/Suspended.ifm +0 -0
  131. data/maps/Tangle.map +0 -0
  132. data/maps/The_Lost_Sheep.map +0 -0
  133. data/maps/Unforgotten.map +0 -0
  134. data/maps/Warbler's Nest.map +0 -0
  135. data/maps/Warbler's_Nest.map +0 -0
  136. data/maps/Westminster_Abbey.map +0 -0
  137. data/maps/WinterWonderland.map +0 -0
  138. data/maps/Wishbringer.ifm +0 -0
  139. data/maps/Wishbringer2.ifm +0 -0
  140. data/maps/Zork1.ifm +0 -0
  141. data/maps/Zork2.ifm +0 -0
  142. data/maps/Zork3.ifm +0 -0
  143. data/maps/Zork_Zero.ifm +0 -0
  144. data/maps/anchor.ifm +0 -0
  145. data/maps/anchor.map +0 -0
  146. data/maps/atrox.ifm +0 -0
  147. data/maps/awaken.ifm +0 -0
  148. data/maps/babel.ifm +0 -0
  149. data/maps/balances.map +0 -0
  150. data/maps/ballerina.map +0 -0
  151. data/maps/bear.map +0 -0
  152. data/maps/bluechairs.map +0 -0
  153. data/maps/break_in.map +0 -0
  154. data/maps/bse.ifm +0 -0
  155. data/maps/building.map +0 -0
  156. data/maps/change.ifm +0 -0
  157. data/maps/christminster.map +0 -0
  158. data/maps/curses.ifm +0 -0
  159. data/maps/curves.ifm +0 -0
  160. data/maps/deadline.map +0 -0
  161. data/maps/delusions.map +0 -0
  162. data/maps/devours.map +0 -0
  163. data/maps/distress.map +0 -0
  164. data/maps/djinni.map +0 -0
  165. data/maps/dreamhold.map +0 -0
  166. data/maps/drift3.map +0 -0
  167. data/maps/eas.map +0 -0
  168. data/maps/eas2.map +0 -0
  169. data/maps/eas3.map +0 -0
  170. data/maps/edifice.ifm +0 -0
  171. data/maps/fallacy.map +0 -0
  172. data/maps/frozen.ifm +0 -0
  173. data/maps/gamlet.map +0 -0
  174. data/maps/glow.ifm +0 -0
  175. data/maps/guilty_bastards.map +0 -0
  176. data/maps/heist.map +0 -0
  177. data/maps/heroes.map +0 -0
  178. data/maps/inhumane.map +0 -0
  179. data/maps/kaged.map +0 -0
  180. data/maps/library.ifm +0 -0
  181. data/maps/lurkinghorror.map +0 -0
  182. data/maps/metamorphoses.map +0 -0
  183. data/maps/mindelec.ifm +0 -0
  184. data/maps/minster.ifm +0 -0
  185. data/maps/mite.map +0 -0
  186. data/maps/moonmist.map +0 -0
  187. data/maps/muldoon_legacy.map +0 -0
  188. data/maps/muse.ifm +0 -0
  189. data/maps/paperchase.ifm +0 -0
  190. data/maps/party.map +0 -0
  191. data/maps/pawn.map +0 -0
  192. data/maps/photograph.map +0 -0
  193. data/maps/pkgirl.map +0 -0
  194. data/maps/pytho.map +0 -0
  195. data/maps/risorgimento.map +0 -0
  196. data/maps/sherbet.map +0 -0
  197. data/maps/simple.map +0 -0
  198. data/maps/slouch.map +0 -0
  199. data/maps/space_st.ifm +0 -0
  200. data/maps/splashdown.map +0 -0
  201. data/maps/spring.map +0 -0
  202. data/maps/squarecircle.map +0 -0
  203. data/maps/stationfall.ifm +0 -0
  204. data/maps/theatre.ifm +0 -0
  205. data/maps/toonesia.ifm +0 -0
  206. data/maps/tortoise.ifm +0 -0
  207. data/maps/trinity.map +0 -0
  208. data/maps/vespers.map +0 -0
  209. data/maps/vgame.ifm +0 -0
  210. data/maps/wasp.map +0 -0
  211. data/maps/weather.ifm +0 -0
  212. data/maps/windhall.ifm +0 -0
  213. data/maps/worlds.map +0 -0
  214. data/maps/xtcontest.map +0 -0
  215. data/maps/zdungeon.map +0 -0
  216. data/maps/zebulon.ifm +0 -0
  217. data/maps/zerosum.map +0 -0
  218. metadata +226 -183
@@ -1,445 +1,445 @@
1
-
2
- require "IFMapper/Map"
3
-
4
- class FXMap; end
5
-
6
- #
7
- # Class that allows importing a GUE map file.
8
- #
9
- class GUEReader
10
-
11
- class ParseError < StandardError; end
12
- class MapError < StandardError; end
13
-
14
- # This is the GUEmap magic number/ID at the beginning of the file.
15
- GUE_MAGIC = "\307U\305m\341p"
16
-
17
- DIRS = {
18
- 0 => 0, # n
19
- 1 => 4, # s
20
- 2 => 2, # e
21
- 3 => 6, # w
22
- 4 => 1, # ne
23
- 5 => 7, # nw
24
- 6 => 3, # se
25
- 7 => 5, # sw
26
-
27
- # GUE Map also supports connections to the sides of centers
28
- #
29
- # -*--*-
30
- # | |
31
- # -*--*-
32
- #
33
- # We translate these as corner connections.
34
- #
35
- 8 => [0, 1], # ----*-
36
- 9 => [0, 7],
37
- 10 => [4, 3], # ----*-
38
- 11 => [4, 5],
39
-
40
- # DOT directions
41
- # 12 => 4,
42
- }
43
-
44
-
45
- class Complex
46
- attr_accessor :roomA, :dirA, :roomB, :dirB
47
- end
48
-
49
-
50
- attr_reader :map
51
-
52
- @@debug = nil
53
-
54
- def debug(x)
55
- if @@debug.to_i > 0
56
- $stderr.puts x
57
- end
58
- end
59
-
60
- #
61
- # Read GUEMap header from file stream
62
- #
63
- def header
64
- magic = @f.read(6)
65
- if magic != GUE_MAGIC
66
- raise ParseError, "Not a GUEMap"
67
- end
68
- @f.read(4)
69
- version = @f.read(2).unpack('s')[0]
70
- debug "VERSION: #{version}"
71
- case version
72
- when 4
73
- crap = @f.read(2)
74
- crap = @f.read(1)[0]
75
- if crap == 32
76
- len = @f.read(1)[0]
77
- @map.name = @f.read(len)
78
- crap = @f.read(1)[0]
79
- end
80
- if crap == 40
81
- len = @f.read(1)[0]
82
- if len == 255
83
- len = @f.read(2).unpack('s')[0]
84
- end
85
- @f.read(len)
86
- crap = @f.read(1)[0]
87
- end
88
- hdr = @f.read(15)
89
- when 3,2
90
- crap = @f.read(2)
91
- crap = @f.read(1)[0]
92
- if crap == 32
93
- len = @f.read(1)[0]
94
- @map.name = @f.read(len)
95
- crap = @f.read(1)[0]
96
- end
97
- if crap == 40
98
- len = @f.read(1)[0]
99
- if len == 255
100
- len = @f.read(2).unpack('s')[0]
101
- end
102
- @f.read(len)
103
- crap = @f.read(1)[0]
104
- end
105
- hdr = @f.read(8)
106
- else
107
- raise ParseError, "Unknown GUEMap version #{version}"
108
- end
109
- end
110
-
111
- #
112
- # Read a connection from file stream
113
- #
114
- def read_connection
115
- type = Connection::FREE
116
- dir = Connection::BOTH
117
- dA = @f.read(1)[0]
118
- a = @f.read(1)[0]
119
- stub = false
120
-
121
- opt = @f.read(1)[0]
122
- exitAtext = exitBtext = 0
123
-
124
- case opt
125
- when 26
126
- #normal connection
127
- when 34
128
- # up/down/in/out connection
129
- exitAtext = @f.read(1)[0]
130
- if exitAtext & 8 != 0
131
- exitAtext -= 8
132
- type = Connection::SPECIAL
133
- end
134
- if exitAtext & 16 != 0
135
- # no exit connection
136
- stub = true
137
- exitAtext -= 16
138
- b = a
139
- dB = dA
140
- end
141
- if exitAtext & 32 != 0
142
- # stub connection
143
- stub = true
144
- exitAtext -= 32
145
- end
146
- if exitAtext & 64 != 0
147
- exitAtext -= 64
148
- dir = Connection::AtoB
149
- end
150
- exitBtext = @f.read(1)[0]
151
- data = @f.read(1)[0]
152
- else
153
- raise ParseError, "option: #{opt} unknown"
154
- end
155
-
156
- unless stub
157
- dB = @f.read(1)[0]
158
- b = @f.read(1)[0]
159
- opt = @f.read(1)[0]
160
- end
161
-
162
- data = @f.read(1)[0]
163
- data = @f.read(1)[0] unless @f.eof?
164
-
165
- section = @map.sections[@map.section]
166
- roomA = section.rooms[a]
167
- roomB = section.rooms[b] if b
168
-
169
-
170
- debug "Index : #{a}->#{b}"
171
- debug "dA: #{dA} dB: #{dB}"
172
- if roomA.name == '*'
173
- if roomB.x != roomA.x or roomB.y != roomA.y
174
- dirA = roomA.exit_to(roomB)
175
- else
176
- dirA = 0
177
- end
178
- else
179
- dirA = DIRS[dA]
180
- if dirA.kind_of?(Array)
181
- dirA.each { |d|
182
- next if roomA[d]
183
- dirA = d
184
- break
185
- }
186
- if dirA.kind_of?(Array)
187
- dirA = dirA[0]
188
- end
189
- end
190
- raise "Unknown direction #{dA}" unless dirA
191
- end
192
-
193
- if b
194
- if roomB.name == '*'
195
- dirB = (dirA + 4) % 8
196
- else
197
- dirB = DIRS[dB]
198
- if dirB.kind_of?(Array)
199
- dirB.each { |d|
200
- next if roomB[d]
201
- dirB = d
202
- break
203
- }
204
- if dirB.kind_of?(Array)
205
- dirB = dirB[0]
206
- end
207
- end
208
- raise "Unknown direction #{dB}" unless dirB
209
- end
210
- end
211
-
212
-
213
- if roomA[dirA]
214
- dirA = roomA.exits.index(nil)
215
- end
216
-
217
- if roomB
218
- if roomB[dirB]
219
- dirB = roomB.exits.rindex(nil)
220
- end
221
- end
222
-
223
-
224
- debug "Connect: #{roomA} ->#{roomB} "
225
- debug "dirA: #{dirA} dirB: #{dirB}"
226
- debug "exitA: #{exitAtext} exitB: #{exitBtext}"
227
-
228
- begin
229
- c = map.new_connection( roomA, dirA, roomB, dirB )
230
-
231
- c.exitAtext = exitAtext
232
- c.exitBtext = exitBtext
233
- c.dir = dir
234
- c.type = type
235
-
236
- debug c
237
- rescue Section::ConnectionError
238
- end
239
-
240
- end
241
-
242
-
243
- #
244
- # Read a room from file stream
245
- #
246
- def read_room
247
- x = @f.read(1)[0]
248
- data = @f.read(1)
249
- y = @f.read(1)[0]
250
- data = @f.read(1)[0]
251
-
252
- type = @f.read(1)[0]
253
-
254
- case type
255
- when 41
256
- r = @map.new_room(x, y)
257
- r.name = "*"
258
- data = @f.read(4)
259
- if data[0] == 0
260
- r.name = ''
261
- end
262
- return
263
- when 0
264
- r = @map.new_room(x, y)
265
- r.name = ''
266
- @f.read(2)
267
- return
268
- end
269
-
270
- len = @f.read(1)[0]
271
- name = ''
272
- comment = ''
273
-
274
- name = @f.read(len)
275
- test = @f.read(1)[0]
276
-
277
- case test
278
- when 41
279
- @f.read(4)
280
- when 0
281
- data = @f.read(2)
282
- else
283
- len = @f.read(1)[0]
284
- if len == 255
285
- len = @f.read(2).unpack('s')[0]
286
- end
287
- comment = @f.read(len)
288
- data = @f.read(3)
289
- end
290
- debug "Room: #{name.inspect} pos: #{x}, #{y}"
291
- debug "Comment: #{comment.inspect}"
292
-
293
- # r = @map.new_room(x, y)
294
- r = @map.new_room(x, y)
295
- name.gsub!(/[\r\n]+/, ' ')
296
- comment.gsub!(/\r/, '')
297
-
298
- r.name = name
299
- r.tasks = comment
300
- end
301
-
302
- #
303
- # Main parsing loop.
304
- #
305
- def parse
306
- header
307
- while not @f.eof?
308
- type = @f.read(1)[0]
309
- data = @f.read(1)[0]
310
-
311
- debug "type: #{type}"
312
- debug "data: #{data}"
313
-
314
- case type
315
- when 2
316
- read_room
317
- when 3
318
- read_connection
319
- when 4
320
- break
321
- else
322
- raise "Unknown type: #{type}"
323
- end
324
- end
325
- end
326
-
327
- def remove_dots
328
- @map.sections.each do |sect|
329
- sect.connections.each do |c|
330
- a, b = c.room
331
- next unless b
332
- next if a.name == '*' or b.name != '*'
333
- # OK. We have a ROOM->* connection. Follow it to find other room.
334
- # And turn it into a complex connection.
335
- dirA = a.exits.index(c)
336
- dirB = b.exits.rindex(c)
337
- origB = dirB
338
- dir = Connection::BOTH
339
- dirB = nil
340
- cc = c
341
- while b.name == '*'
342
- found = false
343
- b.exits.each { |e|
344
- next if not e or e == cc
345
- found = true
346
- idx = e.index(b) ^ 1
347
- b = e.room[idx]
348
- dirB = b.exits.index(e)
349
- dir = e.dir
350
- cc = e
351
- break
352
- }
353
- if not found
354
- debug "not found complement for #{cc}"
355
- break
356
- end
357
- end
358
-
359
- next if b.name == '*'
360
-
361
- # remove the other connection
362
- sect.delete_connection(cc)
363
-
364
- c.roomB[origB] = nil
365
-
366
- # Make connection a complex connection and remove old
367
- # connection in b room
368
- c.roomB = b
369
- c.dir = dir
370
- b[dirB] = c
371
- end
372
- end
373
-
374
-
375
- # Now, remove all DOT rooms and their connections
376
- @map.sections.each { |sect|
377
- del = sect.rooms.find_all { |r| r.name == '*' }
378
- del.each { |r|
379
- sect.delete_room(r)
380
- }
381
- }
382
- end
383
-
384
- def reposition
385
- @map.sections.each do |sect|
386
- minXY, = sect.min_max_rooms
387
- sect.rooms.each do |r|
388
- r.x -= minXY[0]
389
- r.y -= minXY[1]
390
- r.x /= 4.0
391
- r.y /= 4.0
392
- end
393
- end
394
-
395
- @map.sections.each do |sect|
396
- sect.rooms.each_with_index do |r, idx|
397
- x = r.x
398
- y = r.y
399
- xf = x.floor
400
- yf = y.floor
401
- if x != xf or y != yf
402
- sect.rooms.each do |r2|
403
- next if r == r2
404
- if xf == r2.x.floor and yf == r2.y.floor
405
- dx = (x - xf).ceil
406
- dy = (y - yf).ceil
407
- sect.shift(x, y, dx, dy)
408
- break
409
- end
410
- end
411
- end
412
- r.x = r.x.floor.to_i
413
- r.y = r.y.floor.to_i
414
- end
415
- end
416
- end
417
-
418
-
419
- def initialize(file, map = Map.new('GUE Imported Map'))
420
- @map = map
421
- @complex = []
422
-
423
- @f = File.open(file, 'rb')
424
- parse
425
-
426
- reposition
427
- remove_dots
428
-
429
- @map.fit
430
- @map.section = 0
431
- if @map.kind_of?(FXMap)
432
- @map.filename = file.sub(/\.gmp$/i, '.map')
433
- @map.navigation = true
434
- @map.window.show
435
- end
436
- end
437
- end
438
-
439
-
440
- if $0 == __FILE__
441
- puts "Opening file '#{ARGV[0]}'"
442
- require "IFMapper/Map"
443
-
444
- GUEReader.new(ARGV[0])
445
- end
1
+
2
+ require "IFMapper/Map"
3
+
4
+ class FXMap; end
5
+
6
+ #
7
+ # Class that allows importing a GUE map file.
8
+ #
9
+ class GUEReader
10
+
11
+ class ParseError < StandardError; end
12
+ class MapError < StandardError; end
13
+
14
+ # This is the GUEmap magic number/ID at the beginning of the file.
15
+ GUE_MAGIC = "\307U\305m\341p"
16
+
17
+ DIRS = {
18
+ 0 => 0, # n
19
+ 1 => 4, # s
20
+ 2 => 2, # e
21
+ 3 => 6, # w
22
+ 4 => 1, # ne
23
+ 5 => 7, # nw
24
+ 6 => 3, # se
25
+ 7 => 5, # sw
26
+
27
+ # GUE Map also supports connections to the sides of centers
28
+ #
29
+ # -*--*-
30
+ # | |
31
+ # -*--*-
32
+ #
33
+ # We translate these as corner connections.
34
+ #
35
+ 8 => [0, 1], # ----*-
36
+ 9 => [0, 7],
37
+ 10 => [4, 3], # ----*-
38
+ 11 => [4, 5],
39
+
40
+ # DOT directions
41
+ # 12 => 4,
42
+ }
43
+
44
+
45
+ class Complex
46
+ attr_accessor :roomA, :dirA, :roomB, :dirB
47
+ end
48
+
49
+
50
+ attr_reader :map
51
+
52
+ @@debug = nil
53
+
54
+ def debug(x)
55
+ if @@debug.to_i > 0
56
+ $stderr.puts x
57
+ end
58
+ end
59
+
60
+ #
61
+ # Read GUEMap header from file stream
62
+ #
63
+ def header
64
+ magic = @f.read(6)
65
+ if magic != GUE_MAGIC
66
+ raise ParseError, "Not a GUEMap"
67
+ end
68
+ @f.read(4)
69
+ version = @f.read(2).unpack('s')[0]
70
+ debug "VERSION: #{version}"
71
+ case version
72
+ when 4
73
+ crap = @f.read(2)
74
+ crap = @f.read(1)[0]
75
+ if crap == 32
76
+ len = @f.read(1)[0]
77
+ @map.name = @f.read(len)
78
+ crap = @f.read(1)[0]
79
+ end
80
+ if crap == 40
81
+ len = @f.read(1)[0]
82
+ if len == 255
83
+ len = @f.read(2).unpack('s')[0]
84
+ end
85
+ @f.read(len)
86
+ crap = @f.read(1)[0]
87
+ end
88
+ hdr = @f.read(15)
89
+ when 3,2
90
+ crap = @f.read(2)
91
+ crap = @f.read(1)[0]
92
+ if crap == 32
93
+ len = @f.read(1)[0]
94
+ @map.name = @f.read(len)
95
+ crap = @f.read(1)[0]
96
+ end
97
+ if crap == 40
98
+ len = @f.read(1)[0]
99
+ if len == 255
100
+ len = @f.read(2).unpack('s')[0]
101
+ end
102
+ @f.read(len)
103
+ crap = @f.read(1)[0]
104
+ end
105
+ hdr = @f.read(8)
106
+ else
107
+ raise ParseError, "Unknown GUEMap version #{version}"
108
+ end
109
+ end
110
+
111
+ #
112
+ # Read a connection from file stream
113
+ #
114
+ def read_connection
115
+ type = Connection::FREE
116
+ dir = Connection::BOTH
117
+ dA = @f.read(1)[0]
118
+ a = @f.read(1)[0]
119
+ stub = false
120
+
121
+ opt = @f.read(1)[0]
122
+ exitAtext = exitBtext = 0
123
+
124
+ case opt
125
+ when 26
126
+ #normal connection
127
+ when 34
128
+ # up/down/in/out connection
129
+ exitAtext = @f.read(1)[0]
130
+ if exitAtext & 8 != 0
131
+ exitAtext -= 8
132
+ type = Connection::SPECIAL
133
+ end
134
+ if exitAtext & 16 != 0
135
+ # no exit connection
136
+ stub = true
137
+ exitAtext -= 16
138
+ b = a
139
+ dB = dA
140
+ end
141
+ if exitAtext & 32 != 0
142
+ # stub connection
143
+ stub = true
144
+ exitAtext -= 32
145
+ end
146
+ if exitAtext & 64 != 0
147
+ exitAtext -= 64
148
+ dir = Connection::AtoB
149
+ end
150
+ exitBtext = @f.read(1)[0]
151
+ data = @f.read(1)[0]
152
+ else
153
+ raise ParseError, "option: #{opt} unknown"
154
+ end
155
+
156
+ unless stub
157
+ dB = @f.read(1)[0]
158
+ b = @f.read(1)[0]
159
+ opt = @f.read(1)[0]
160
+ end
161
+
162
+ data = @f.read(1)[0]
163
+ data = @f.read(1)[0] unless @f.eof?
164
+
165
+ section = @map.sections[@map.section]
166
+ roomA = section.rooms[a]
167
+ roomB = section.rooms[b] if b
168
+
169
+
170
+ debug "Index : #{a}->#{b}"
171
+ debug "dA: #{dA} dB: #{dB}"
172
+ if roomA.name == '*'
173
+ if roomB.x != roomA.x or roomB.y != roomA.y
174
+ dirA = roomA.exit_to(roomB)
175
+ else
176
+ dirA = 0
177
+ end
178
+ else
179
+ dirA = DIRS[dA]
180
+ if dirA.kind_of?(Array)
181
+ dirA.each { |d|
182
+ next if roomA[d]
183
+ dirA = d
184
+ break
185
+ }
186
+ if dirA.kind_of?(Array)
187
+ dirA = dirA[0]
188
+ end
189
+ end
190
+ raise "Unknown direction #{dA}" unless dirA
191
+ end
192
+
193
+ if b
194
+ if roomB.name == '*'
195
+ dirB = (dirA + 4) % 8
196
+ else
197
+ dirB = DIRS[dB]
198
+ if dirB.kind_of?(Array)
199
+ dirB.each { |d|
200
+ next if roomB[d]
201
+ dirB = d
202
+ break
203
+ }
204
+ if dirB.kind_of?(Array)
205
+ dirB = dirB[0]
206
+ end
207
+ end
208
+ raise "Unknown direction #{dB}" unless dirB
209
+ end
210
+ end
211
+
212
+
213
+ if roomA[dirA]
214
+ dirA = roomA.exits.index(nil)
215
+ end
216
+
217
+ if roomB
218
+ if roomB[dirB]
219
+ dirB = roomB.exits.rindex(nil)
220
+ end
221
+ end
222
+
223
+
224
+ debug "Connect: #{roomA} ->#{roomB} "
225
+ debug "dirA: #{dirA} dirB: #{dirB}"
226
+ debug "exitA: #{exitAtext} exitB: #{exitBtext}"
227
+
228
+ begin
229
+ c = map.new_connection( roomA, dirA, roomB, dirB )
230
+
231
+ c.exitAtext = exitAtext
232
+ c.exitBtext = exitBtext
233
+ c.dir = dir
234
+ c.type = type
235
+
236
+ debug c
237
+ rescue Section::ConnectionError
238
+ end
239
+
240
+ end
241
+
242
+
243
+ #
244
+ # Read a room from file stream
245
+ #
246
+ def read_room
247
+ x = @f.read(1)[0]
248
+ data = @f.read(1)
249
+ y = @f.read(1)[0]
250
+ data = @f.read(1)[0]
251
+
252
+ type = @f.read(1)[0]
253
+
254
+ case type
255
+ when 41
256
+ r = @map.new_room(x, y)
257
+ r.name = "*"
258
+ data = @f.read(4)
259
+ if data[0] == 0
260
+ r.name = ''
261
+ end
262
+ return
263
+ when 0
264
+ r = @map.new_room(x, y)
265
+ r.name = ''
266
+ @f.read(2)
267
+ return
268
+ end
269
+
270
+ len = @f.read(1)[0]
271
+ name = ''
272
+ comment = ''
273
+
274
+ name = @f.read(len)
275
+ test = @f.read(1)[0]
276
+
277
+ case test
278
+ when 41
279
+ @f.read(4)
280
+ when 0
281
+ data = @f.read(2)
282
+ else
283
+ len = @f.read(1)[0]
284
+ if len == 255
285
+ len = @f.read(2).unpack('s')[0]
286
+ end
287
+ comment = @f.read(len)
288
+ data = @f.read(3)
289
+ end
290
+ debug "Room: #{name.inspect} pos: #{x}, #{y}"
291
+ debug "Comment: #{comment.inspect}"
292
+
293
+ # r = @map.new_room(x, y)
294
+ r = @map.new_room(x, y)
295
+ name.gsub!(/[\r\n]+/, ' ')
296
+ comment.gsub!(/\r/, '')
297
+
298
+ r.name = name
299
+ r.tasks = comment
300
+ end
301
+
302
+ #
303
+ # Main parsing loop.
304
+ #
305
+ def parse
306
+ header
307
+ while not @f.eof?
308
+ type = @f.read(1)[0]
309
+ data = @f.read(1)[0]
310
+
311
+ debug "type: #{type}"
312
+ debug "data: #{data}"
313
+
314
+ case type
315
+ when 2
316
+ read_room
317
+ when 3
318
+ read_connection
319
+ when 4
320
+ break
321
+ else
322
+ raise "Unknown type: #{type}"
323
+ end
324
+ end
325
+ end
326
+
327
+ def remove_dots
328
+ @map.sections.each do |sect|
329
+ sect.connections.each do |c|
330
+ a, b = c.room
331
+ next unless b
332
+ next if a.name == '*' or b.name != '*'
333
+ # OK. We have a ROOM->* connection. Follow it to find other room.
334
+ # And turn it into a complex connection.
335
+ dirA = a.exits.index(c)
336
+ dirB = b.exits.rindex(c)
337
+ origB = dirB
338
+ dir = Connection::BOTH
339
+ dirB = nil
340
+ cc = c
341
+ while b.name == '*'
342
+ found = false
343
+ b.exits.each { |e|
344
+ next if not e or e == cc
345
+ found = true
346
+ idx = e.index(b) ^ 1
347
+ b = e.room[idx]
348
+ dirB = b.exits.index(e)
349
+ dir = e.dir
350
+ cc = e
351
+ break
352
+ }
353
+ if not found
354
+ debug "not found complement for #{cc}"
355
+ break
356
+ end
357
+ end
358
+
359
+ next if b.name == '*'
360
+
361
+ # remove the other connection
362
+ sect.delete_connection(cc)
363
+
364
+ c.roomB[origB] = nil
365
+
366
+ # Make connection a complex connection and remove old
367
+ # connection in b room
368
+ c.roomB = b
369
+ c.dir = dir
370
+ b[dirB] = c
371
+ end
372
+ end
373
+
374
+
375
+ # Now, remove all DOT rooms and their connections
376
+ @map.sections.each { |sect|
377
+ del = sect.rooms.find_all { |r| r.name == '*' }
378
+ del.each { |r|
379
+ sect.delete_room(r)
380
+ }
381
+ }
382
+ end
383
+
384
+ def reposition
385
+ @map.sections.each do |sect|
386
+ minXY, = sect.min_max_rooms
387
+ sect.rooms.each do |r|
388
+ r.x -= minXY[0]
389
+ r.y -= minXY[1]
390
+ r.x /= 4.0
391
+ r.y /= 4.0
392
+ end
393
+ end
394
+
395
+ @map.sections.each do |sect|
396
+ sect.rooms.each_with_index do |r, idx|
397
+ x = r.x
398
+ y = r.y
399
+ xf = x.floor
400
+ yf = y.floor
401
+ if x != xf or y != yf
402
+ sect.rooms.each do |r2|
403
+ next if r == r2
404
+ if xf == r2.x.floor and yf == r2.y.floor
405
+ dx = (x - xf).ceil
406
+ dy = (y - yf).ceil
407
+ sect.shift(x, y, dx, dy)
408
+ break
409
+ end
410
+ end
411
+ end
412
+ r.x = r.x.floor.to_i
413
+ r.y = r.y.floor.to_i
414
+ end
415
+ end
416
+ end
417
+
418
+
419
+ def initialize(file, map = Map.new('GUE Imported Map'))
420
+ @map = map
421
+ @complex = []
422
+
423
+ @f = File.open(file, 'rb')
424
+ parse
425
+
426
+ reposition
427
+ remove_dots
428
+
429
+ @map.fit
430
+ @map.section = 0
431
+ if @map.kind_of?(FXMap)
432
+ @map.filename = file.sub(/\.gmp$/i, '.map')
433
+ @map.navigation = true
434
+ @map.window.show
435
+ end
436
+ end
437
+ end
438
+
439
+
440
+ if $0 == __FILE__
441
+ puts "Opening file '#{ARGV[0]}'"
442
+ require "IFMapper/Map"
443
+
444
+ GUEReader.new(ARGV[0])
445
+ end