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,478 +1,478 @@
1
-
2
- require "IFMapper/MapReader"
3
-
4
-
5
- # Take a quoted Inform string and return a valid ASCII one, replacing
6
- # Inform's special characters.
7
- module InformUnquote
8
- TABLES = {
9
- ":a" => "\204",
10
- ":e" => "\211",
11
- ":i" => "\213",
12
- ":o" => "\224",
13
- ":u" => "\201",
14
- ":y" => "\230",
15
-
16
- ":A" => "\216",
17
- ":E" => "\323",
18
- ":I" => "\330",
19
- ":O" => "\231",
20
- ":U" => "\232",
21
-
22
- "'a" => "\240",
23
- "'e" => "\202",
24
- "'i" => "\241",
25
- "'o" => "\242",
26
- "'u" => "\243",
27
- "'y" => "\354",
28
-
29
- "'A" => "\265",
30
- "'E" => "\220",
31
- "'I" => "\326",
32
- "'O" => "\340",
33
- "'U" => "\351",
34
- "'Y" => "\355",
35
-
36
- "^a" => "\203",
37
- "^e" => "\210",
38
- "^i" => "\214",
39
- "^o" => "\223",
40
- "^u" => "\226",
41
-
42
- "^A" => "\266",
43
- "^E" => "\322",
44
- "^I" => "\327",
45
- "^O" => "\342",
46
- "^U" => "\352",
47
-
48
- "`a" => "\205",
49
- "`e" => "\212",
50
- "`i" => "\215",
51
- "`o" => "\225",
52
- "`u" => "\227",
53
-
54
- "`A" => "\267",
55
- "`E" => "\324",
56
- "`I" => "\336",
57
- "`O" => "\343",
58
- "`U" => "\353",
59
-
60
- ",c" => "\207",
61
- ",C" => "\200",
62
-
63
- "~a" => "\306",
64
- "~A" => "\307",
65
- "~o" => "\344",
66
- "~o" => "\345",
67
-
68
-
69
- "~n" => "\244",
70
- "~N" => "\245",
71
-
72
- "!!" => "\255",
73
- '\?\?' => "\250",
74
-
75
- ">>" => ">>",
76
- "<<" => "<<",
77
-
78
- # Missing
79
- "LL" => "", # British pound
80
-
81
- # greek letters
82
- "ss" => "", # beta
83
-
84
- "et" => "", #
85
- "Et" => "", #
86
- "th" => "", # thorn
87
- "Th" => "", # thorn
88
-
89
- '\\o' => "", #
90
- '\\O' => "", #
91
-
92
- "oa" => "", # o over a
93
- "oA" => "", # o over A
94
- "ae" => "",
95
- "AE" => "",
96
- "oe" => "",
97
- "OE" => "",
98
- }
99
-
100
- def unquote(text)
101
- return '' unless text
102
- # deal with quotes
103
- text.gsub!(/\~/, '"')
104
- # and newlines
105
- text.gsub!(/\^/, "\n")
106
-
107
- # deal with accents and intl. chars
108
- TABLES.each { |k, v|
109
- text.gsub! /@#{k}/, v
110
- }
111
-
112
- # deal with zscii chars as numbers
113
- while text =~ /(@@(\d+))/
114
- text.sub!($1, $2.to_i.chr)
115
- end
116
- return text
117
- end
118
- end
119
-
120
-
121
- #
122
- # Class that allows creating a map from an Inform source file.
123
- #
124
- class InformReader < MapReader
125
-
126
- STORY = /Constant\s+Story\s+\"([^"]+)\"/i
127
-
128
- DIRECTIONS = {
129
- 'n_to' => 0,
130
- 'ne_to' => 1,
131
- 'e_to' => 2,
132
- 'se_to' => 3,
133
- 's_to' => 4,
134
- 'sw_to' => 5,
135
- 'w_to' => 6,
136
- 'nw_to' => 7,
137
- 'u_to' => 8,
138
- 'd_to' => 9,
139
- 'in_to' => 10,
140
- 'out_to' => 11
141
- }
142
-
143
- FUNCTION = /^\[ (\w+);/
144
-
145
- GO_OBJ = /\b(#{DIRECTIONS.keys.join('|').gsub(/_to/, '_obj')})\s*:/i
146
-
147
-
148
- # Direction list in order of positioning preference.
149
- DIRLIST = [ 0, 4, 2, 6, 1, 3, 5, 7 ]
150
-
151
- DIR_TO = /(?:^|\s+)(#{DIRECTIONS.keys.join('|')})\s+/i
152
-
153
- DIR = /(?:^|\s+)(#{DIRECTIONS.keys.join('|')})\s+(\w+)/i
154
- ENTER_DIR = /(?:^|\s+)(#{DIRECTIONS.keys.join('|')})\s+\[;\s*<<\s*Enter\s+(\w+)\s*>>/i
155
-
156
- attr_reader :map
157
-
158
-
159
- include InformUnquote
160
-
161
- class InformRoom < MapRoom
162
- include InformUnquote
163
- end
164
-
165
- class InformObject < MapObject
166
- include InformUnquote
167
- end
168
-
169
-
170
- def new_room
171
- super(InformRoom)
172
- end
173
-
174
- def new_obj(loc = nil)
175
- super(loc, InformObject)
176
- end
177
-
178
- #
179
- # Main parsing loop.
180
- #
181
- def parse(file)
182
- # We start map at 0, 0
183
- @x, @y = [0, 0]
184
- @room = nil
185
-
186
- if @map.kind_of?(FXMap)
187
- @map.options['Edit on Creation'] = false
188
- @map.window.hide
189
- end
190
- @map.section = 0
191
-
192
- @parsing = nil
193
- @last_section = 0
194
- @ignore_first_section = true
195
- @room_idx = 0
196
- line_number = 0
197
-
198
- debug "...Parse... #{file.path}"
199
- while not file.eof?
200
- @line = ''
201
- while not file.eof? and @line == ''
202
- @line << file.readline()
203
- @line.sub!( /^\s*!.*$/, '')
204
- line_number += 1
205
- end
206
- # Remove comments at end of line
207
- @line.sub!( /\s+![^"]*$/, '')
208
- # Remove starting spaces (if any)
209
- @line.sub! /^\s+/, ''
210
- # Replace \n with simple space
211
- @line.gsub! /\n/, ' '
212
- next if @line == ''
213
- full_line = @line.dup
214
- begin
215
- parse_line
216
- rescue ParseError, MapError => e
217
- message = "#{e}.\nat #{file.path}, line #{line_number}:\n>>>> #{full_line};\n"
218
- raise message
219
- rescue => e
220
- message = "#{e}\n#{e.backtrace}"
221
- raise message
222
- end
223
- end
224
- debug "...End Parse..."
225
- end
226
-
227
-
228
- CLASS = /^class\s+(\w+)/i
229
- DOOR = /(?:^|\s+)door_to(?:\s+([^,;\[]*)|$)/i
230
- INCLUDE = /^#?include\s+"([^"]+)"/i
231
- PLAYER_TO = /\bplayerto\((\w+)/i
232
- DESCRIPTION = /(?:^|\s+)description(?:\s*$|\s+"([^"]+)?("?))/i
233
-
234
- STD_LIB = [
235
- 'Parser',
236
- 'VerbLib',
237
- 'Grammar'
238
- ]
239
-
240
-
241
-
242
- def find_file(file)
243
- return file if File.exists?(file)
244
- @include_dirs.each { |d|
245
- [ "#{d}/#{file}",
246
- "#{d}/#{file}.h",
247
- "#{d}/#{file}.inf", ].each { |full|
248
- return full if File.exists?(full)
249
- }
250
- }
251
- return nil
252
- end
253
-
254
- #
255
- # Parse a line of file
256
- #
257
- def parse_line
258
- if @line =~ STORY
259
- @map.name = $1
260
- return
261
- end
262
-
263
- if @line =~ INCLUDE
264
- name = $1
265
- unless STD_LIB.include?(name)
266
- file = find_file(name)
267
- debug "include #{file}"
268
- if file
269
- File.open(file, 'r') { |f| parse(f) }
270
- else
271
- raise ParseError, "Include file '#{name}' not found"
272
- end
273
- end
274
- end
275
-
276
- if @line =~ CLASS
277
- @clas = $1
278
- debug "CLASS: #@clas"
279
- if @classes.has_key?(@clas)
280
- if @obj
281
- @classes[@clas].each { |k, v| @obj.send("#{k}=", v) }
282
- elsif @room
283
- @classes[@clas].each { |k, v| @room.send("#{k}=", v) }
284
- end
285
- else
286
- @classes[@clas] = {}
287
- @tag = @name = nil
288
- end
289
- end
290
-
291
- re = /^(#{@classes.keys.join('|')})((?:\s+->)+)?(\s+(\w+))?(\s+"([^"]+)")?(\s+(\w+))?/
292
- if @line =~ re
293
- @clas = $1
294
- prev = $2
295
- @tag = $4 || $6
296
- @name = $6
297
- @desc = ''
298
- @in_desc = false
299
-
300
- loc = $8
301
- if prev and @room
302
- loc = @room.tag
303
- end
304
-
305
- debug <<"EOF"
306
- Name : #@name
307
- Class : #@clas
308
- Tag : #@tag
309
- Location: #{loc} #{loc.class}
310
- EOF
311
-
312
- @obj = nil
313
-
314
- c = @classes[@clas]
315
- if c[:door]
316
- new_door
317
- elsif @tag and (loc or c[:scenery] or c[:static])
318
- new_obj(loc)
319
- if c[:scenery] or c[:static]
320
- @obj.scenery = true
321
- end
322
- else
323
- @obj = @room = nil
324
- new_room if @tag and @name and c.has_key?(:light)
325
- end
326
- @before = false
327
- @go = false
328
-
329
- end
330
-
331
- if @in_desc
332
- text = @line.scan( /\s*([^"]+)("\s*[,;])?/ )
333
- text.each { |t, q|
334
- @desc << t
335
- @in_desc = nil if q
336
- }
337
- end
338
-
339
- if @desc and @line =~ DESCRIPTION
340
- @desc << $1 if $1
341
- @in_desc = true unless $2
342
- end
343
-
344
-
345
- if @line =~ FUNCTION
346
- @functions << $1
347
- elsif @tag and @line =~ DOOR
348
- old_locations = []
349
- if @obj
350
- old_locations = @obj.location
351
- @objects.delete(@obj) if @obj.tag == @tag
352
- @tags[@tag] = nil
353
- end
354
- new_door
355
- @obj.location = $1.split(' ')
356
- @obj.location += old_locations if old_locations.size > 0
357
- else
358
- dirs = @line.scan(DIR_TO) + @line.scan(DIR) + @line.scan(ENTER_DIR)
359
- if dirs.size > 0
360
- new_room if not @room
361
- dirs.each { |d, room|
362
- dir = DIRECTIONS[d]
363
- @room.exits[dir] = room
364
- }
365
- end
366
- end
367
-
368
-
369
- if @line =~ /\bbefore\b/i
370
- @before = true
371
- end
372
-
373
- if @line =~ /\bgo\s*:/i and @room and @before
374
- if @line =~ GO_OBJ
375
- dir = DIRECTIONS[$1]
376
- if @line =~ PLAYER_TO
377
- @room.exits[dir] = $1
378
- end
379
- end
380
- end
381
-
382
- if @obj.kind_of?(MapObject) and @before
383
- if @line =~ PLAYER_TO
384
- @obj.enterable << $1
385
- @obj.scenery = nil
386
- end
387
- end
388
-
389
-
390
- if @line =~ /(?:^|\s+)has\s+([\w\s]+)[,;]/i
391
- props = $1.split
392
- char = props.include?('male') or props.include?('female')
393
- if @obj and char
394
- @obj.scenery = false
395
- end
396
- props.each { |p|
397
- if not @tag
398
- if p[0,1] == '~'
399
- @classes[@clas][p[1,-1].to_sym] = false
400
- else
401
- @classes[@clas][p.to_sym] = true
402
- end
403
- else
404
- if p == 'locked' and @doors.size > 0
405
- @doors[-1].locked = true
406
- end
407
- if not char
408
- if p =~ /(?:static|scenery)/ and @obj
409
- @objects.delete(@obj)
410
- end
411
- end
412
- if p =~ /(\~)?light/
413
- new_room if not @room
414
- @room.light = ($1 != '~')
415
- end
416
- end
417
- }
418
- end
419
-
420
- if @line =~ /\bfound_in\s+([^;,\[]*)/
421
- if @tag and not @obj
422
- new_obj(loc)
423
- end
424
- if @obj
425
- locs = $1.split
426
- @obj.location = locs
427
- debug "#{@obj} #{@obj.location} FOUND_IN: #{locs.join(' ')}"
428
- end
429
- end
430
- end
431
-
432
-
433
- def set_include_dirs
434
- # Try to find inform(.exe) in path.
435
- paths = ENV['PATH'].split(SEP)
436
- paths.each { |p|
437
- next if not File.directory?(p)
438
- Dir.foreach(p) { |x|
439
- if x =~ /^inform(.exe)?$/i
440
- @include_dirs << p
441
- @include_dirs << p + "/Base"
442
- @include_dirs << p + "/Contrib"
443
- @include_dirs << p + "/../Contrib"
444
- @include_dirs << p + "/../Lib/Contrib"
445
- break
446
- end
447
- }
448
- }
449
- end
450
-
451
-
452
- def initialize(file, map = Map.new('Inform Map'))
453
- debug "Initialize"
454
- @classes = { 'Object' => {} }
455
-
456
- super
457
-
458
- if @map.kind_of?(FXMap)
459
- @map.filename = file.sub(/\.inf$/i, '.map')
460
- @map.options['Location Description'] = true
461
- @map.window.show
462
- end
463
- @objects = nil
464
- @tags = nil # save some memory by clearing the tag list
465
- @rooms = nil # and room list
466
- end
467
- end
468
-
469
-
470
- if $0 == __FILE__
471
- p "Opening file '#{ARGV[0]}'"
472
- BEGIN {
473
- $LOAD_PATH << 'C:\Windows\Escritorio\IFMapper\lib'
474
- }
475
-
476
- require "IFMapper/Map"
477
- InformReader.new(ARGV[0])
478
- end
1
+
2
+ require "IFMapper/MapReader"
3
+
4
+
5
+ # Take a quoted Inform string and return a valid ASCII one, replacing
6
+ # Inform's special characters.
7
+ module InformUnquote
8
+ TABLES = {
9
+ ":a" => "\204",
10
+ ":e" => "\211",
11
+ ":i" => "\213",
12
+ ":o" => "\224",
13
+ ":u" => "\201",
14
+ ":y" => "\230",
15
+
16
+ ":A" => "\216",
17
+ ":E" => "\323",
18
+ ":I" => "\330",
19
+ ":O" => "\231",
20
+ ":U" => "\232",
21
+
22
+ "'a" => "\240",
23
+ "'e" => "\202",
24
+ "'i" => "\241",
25
+ "'o" => "\242",
26
+ "'u" => "\243",
27
+ "'y" => "\354",
28
+
29
+ "'A" => "\265",
30
+ "'E" => "\220",
31
+ "'I" => "\326",
32
+ "'O" => "\340",
33
+ "'U" => "\351",
34
+ "'Y" => "\355",
35
+
36
+ "^a" => "\203",
37
+ "^e" => "\210",
38
+ "^i" => "\214",
39
+ "^o" => "\223",
40
+ "^u" => "\226",
41
+
42
+ "^A" => "\266",
43
+ "^E" => "\322",
44
+ "^I" => "\327",
45
+ "^O" => "\342",
46
+ "^U" => "\352",
47
+
48
+ "`a" => "\205",
49
+ "`e" => "\212",
50
+ "`i" => "\215",
51
+ "`o" => "\225",
52
+ "`u" => "\227",
53
+
54
+ "`A" => "\267",
55
+ "`E" => "\324",
56
+ "`I" => "\336",
57
+ "`O" => "\343",
58
+ "`U" => "\353",
59
+
60
+ ",c" => "\207",
61
+ ",C" => "\200",
62
+
63
+ "~a" => "\306",
64
+ "~A" => "\307",
65
+ "~o" => "\344",
66
+ "~o" => "\345",
67
+
68
+
69
+ "~n" => "\244",
70
+ "~N" => "\245",
71
+
72
+ "!!" => "\255",
73
+ '\?\?' => "\250",
74
+
75
+ ">>" => ">>",
76
+ "<<" => "<<",
77
+
78
+ # Missing
79
+ "LL" => "", # British pound
80
+
81
+ # greek letters
82
+ "ss" => "", # beta
83
+
84
+ "et" => "", #
85
+ "Et" => "", #
86
+ "th" => "", # thorn
87
+ "Th" => "", # thorn
88
+
89
+ '\\o' => "", #
90
+ '\\O' => "", #
91
+
92
+ "oa" => "", # o over a
93
+ "oA" => "", # o over A
94
+ "ae" => "",
95
+ "AE" => "",
96
+ "oe" => "",
97
+ "OE" => "",
98
+ }
99
+
100
+ def unquote(text)
101
+ return '' unless text
102
+ # deal with quotes
103
+ text.gsub!(/\~/, '"')
104
+ # and newlines
105
+ text.gsub!(/\^/, "\n")
106
+
107
+ # deal with accents and intl. chars
108
+ TABLES.each { |k, v|
109
+ text.gsub! /@#{k}/, v
110
+ }
111
+
112
+ # deal with zscii chars as numbers
113
+ while text =~ /(@@(\d+))/
114
+ text.sub!($1, $2.to_i.chr)
115
+ end
116
+ return text
117
+ end
118
+ end
119
+
120
+
121
+ #
122
+ # Class that allows creating a map from an Inform source file.
123
+ #
124
+ class InformReader < MapReader
125
+
126
+ STORY = /Constant\s+Story\s+\"([^"]+)\"/i
127
+
128
+ DIRECTIONS = {
129
+ 'n_to' => 0,
130
+ 'ne_to' => 1,
131
+ 'e_to' => 2,
132
+ 'se_to' => 3,
133
+ 's_to' => 4,
134
+ 'sw_to' => 5,
135
+ 'w_to' => 6,
136
+ 'nw_to' => 7,
137
+ 'u_to' => 8,
138
+ 'd_to' => 9,
139
+ 'in_to' => 10,
140
+ 'out_to' => 11
141
+ }
142
+
143
+ FUNCTION = /^\[ (\w+);/
144
+
145
+ GO_OBJ = /\b(#{DIRECTIONS.keys.join('|').gsub(/_to/, '_obj')})\s*:/i
146
+
147
+
148
+ # Direction list in order of positioning preference.
149
+ DIRLIST = [ 0, 4, 2, 6, 1, 3, 5, 7 ]
150
+
151
+ DIR_TO = /(?:^|\s+)(#{DIRECTIONS.keys.join('|')})\s+/i
152
+
153
+ DIR = /(?:^|\s+)(#{DIRECTIONS.keys.join('|')})\s+(\w+)/i
154
+ ENTER_DIR = /(?:^|\s+)(#{DIRECTIONS.keys.join('|')})\s+\[;\s*<<\s*Enter\s+(\w+)\s*>>/i
155
+
156
+ attr_reader :map
157
+
158
+
159
+ include InformUnquote
160
+
161
+ class InformRoom < MapRoom
162
+ include InformUnquote
163
+ end
164
+
165
+ class InformObject < MapObject
166
+ include InformUnquote
167
+ end
168
+
169
+
170
+ def new_room
171
+ super(InformRoom)
172
+ end
173
+
174
+ def new_obj(loc = nil)
175
+ super(loc, InformObject)
176
+ end
177
+
178
+ #
179
+ # Main parsing loop.
180
+ #
181
+ def parse(file)
182
+ # We start map at 0, 0
183
+ @x, @y = [0, 0]
184
+ @room = nil
185
+
186
+ if @map.kind_of?(FXMap)
187
+ @map.options['Edit on Creation'] = false
188
+ @map.window.hide
189
+ end
190
+ @map.section = 0
191
+
192
+ @parsing = nil
193
+ @last_section = 0
194
+ @ignore_first_section = true
195
+ @room_idx = 0
196
+ line_number = 0
197
+
198
+ debug "...Parse... #{file.path}"
199
+ while not file.eof?
200
+ @line = ''
201
+ while not file.eof? and @line == ''
202
+ @line << file.readline()
203
+ @line.sub!( /^\s*!.*$/, '')
204
+ line_number += 1
205
+ end
206
+ # Remove comments at end of line
207
+ @line.sub!( /\s+![^"]*$/, '')
208
+ # Remove starting spaces (if any)
209
+ @line.sub! /^\s+/, ''
210
+ # Replace \n with simple space
211
+ @line.gsub! /\n/, ' '
212
+ next if @line == ''
213
+ full_line = @line.dup
214
+ begin
215
+ parse_line
216
+ rescue ParseError, MapError => e
217
+ message = "#{e}.\nat #{file.path}, line #{line_number}:\n>>>> #{full_line};\n"
218
+ raise message
219
+ rescue => e
220
+ message = "#{e}\n#{e.backtrace}"
221
+ raise message
222
+ end
223
+ end
224
+ debug "...End Parse..."
225
+ end
226
+
227
+
228
+ CLASS = /^class\s+(\w+)/i
229
+ DOOR = /(?:^|\s+)door_to(?:\s+([^,;\[]*)|$)/i
230
+ INCLUDE = /^#?include\s+"([^"]+)"/i
231
+ PLAYER_TO = /\bplayerto\((\w+)/i
232
+ DESCRIPTION = /(?:^|\s+)description(?:\s*$|\s+"([^"]+)?("?))/i
233
+
234
+ STD_LIB = [
235
+ 'Parser',
236
+ 'VerbLib',
237
+ 'Grammar'
238
+ ]
239
+
240
+
241
+
242
+ def find_file(file)
243
+ return file if File.exists?(file)
244
+ @include_dirs.each { |d|
245
+ [ "#{d}/#{file}",
246
+ "#{d}/#{file}.h",
247
+ "#{d}/#{file}.inf", ].each { |full|
248
+ return full if File.exists?(full)
249
+ }
250
+ }
251
+ return nil
252
+ end
253
+
254
+ #
255
+ # Parse a line of file
256
+ #
257
+ def parse_line
258
+ if @line =~ STORY
259
+ @map.name = $1
260
+ return
261
+ end
262
+
263
+ if @line =~ INCLUDE
264
+ name = $1
265
+ unless STD_LIB.include?(name)
266
+ file = find_file(name)
267
+ debug "include #{file}"
268
+ if file
269
+ File.open(file, 'r') { |f| parse(f) }
270
+ else
271
+ raise ParseError, "Include file '#{name}' not found"
272
+ end
273
+ end
274
+ end
275
+
276
+ if @line =~ CLASS
277
+ @clas = $1
278
+ debug "CLASS: #@clas"
279
+ if @classes.has_key?(@clas)
280
+ if @obj
281
+ @classes[@clas].each { |k, v| @obj.send("#{k}=", v) }
282
+ elsif @room
283
+ @classes[@clas].each { |k, v| @room.send("#{k}=", v) }
284
+ end
285
+ else
286
+ @classes[@clas] = {}
287
+ @tag = @name = nil
288
+ end
289
+ end
290
+
291
+ re = /^(#{@classes.keys.join('|')})((?:\s+->)+)?(\s+(\w+))?(\s+"([^"]+)")?(\s+(\w+))?/
292
+ if @line =~ re
293
+ @clas = $1
294
+ prev = $2
295
+ @tag = $4 || $6
296
+ @name = $6
297
+ @desc = ''
298
+ @in_desc = false
299
+
300
+ loc = $8
301
+ if prev and @room
302
+ loc = @room.tag
303
+ end
304
+
305
+ debug <<"EOF"
306
+ Name : #@name
307
+ Class : #@clas
308
+ Tag : #@tag
309
+ Location: #{loc} #{loc.class}
310
+ EOF
311
+
312
+ @obj = nil
313
+
314
+ c = @classes[@clas]
315
+ if c[:door]
316
+ new_door
317
+ elsif @tag and (loc or c[:scenery] or c[:static])
318
+ new_obj(loc)
319
+ if c[:scenery] or c[:static]
320
+ @obj.scenery = true
321
+ end
322
+ else
323
+ @obj = @room = nil
324
+ new_room if @tag and @name and c.has_key?(:light)
325
+ end
326
+ @before = false
327
+ @go = false
328
+
329
+ end
330
+
331
+ if @in_desc
332
+ text = @line.scan( /\s*([^"]+)("\s*[,;])?/ )
333
+ text.each { |t, q|
334
+ @desc << t
335
+ @in_desc = nil if q
336
+ }
337
+ end
338
+
339
+ if @desc and @line =~ DESCRIPTION
340
+ @desc << $1 if $1
341
+ @in_desc = true unless $2
342
+ end
343
+
344
+
345
+ if @line =~ FUNCTION
346
+ @functions << $1
347
+ elsif @tag and @line =~ DOOR
348
+ old_locations = []
349
+ if @obj
350
+ old_locations = @obj.location
351
+ @objects.delete(@obj) if @obj.tag == @tag
352
+ @tags[@tag] = nil
353
+ end
354
+ new_door
355
+ @obj.location = $1.split(' ')
356
+ @obj.location += old_locations if old_locations.size > 0
357
+ else
358
+ dirs = @line.scan(DIR_TO) + @line.scan(DIR) + @line.scan(ENTER_DIR)
359
+ if dirs.size > 0
360
+ new_room if not @room
361
+ dirs.each { |d, room|
362
+ dir = DIRECTIONS[d]
363
+ @room.exits[dir] = room
364
+ }
365
+ end
366
+ end
367
+
368
+
369
+ if @line =~ /\bbefore\b/i
370
+ @before = true
371
+ end
372
+
373
+ if @line =~ /\bgo\s*:/i and @room and @before
374
+ if @line =~ GO_OBJ
375
+ dir = DIRECTIONS[$1]
376
+ if @line =~ PLAYER_TO
377
+ @room.exits[dir] = $1
378
+ end
379
+ end
380
+ end
381
+
382
+ if @obj.kind_of?(MapObject) and @before
383
+ if @line =~ PLAYER_TO
384
+ @obj.enterable << $1
385
+ @obj.scenery = nil
386
+ end
387
+ end
388
+
389
+
390
+ if @line =~ /(?:^|\s+)has\s+([\w\s]+)[,;]/i
391
+ props = $1.split
392
+ char = props.include?('male') or props.include?('female')
393
+ if @obj and char
394
+ @obj.scenery = false
395
+ end
396
+ props.each { |p|
397
+ if not @tag
398
+ if p[0,1] == '~'
399
+ @classes[@clas][p[1,-1].to_sym] = false
400
+ else
401
+ @classes[@clas][p.to_sym] = true
402
+ end
403
+ else
404
+ if p == 'locked' and @doors.size > 0
405
+ @doors[-1].locked = true
406
+ end
407
+ if not char
408
+ if p =~ /(?:static|scenery)/ and @obj
409
+ @objects.delete(@obj)
410
+ end
411
+ end
412
+ if p =~ /(\~)?light/
413
+ new_room if not @room
414
+ @room.light = ($1 != '~')
415
+ end
416
+ end
417
+ }
418
+ end
419
+
420
+ if @line =~ /\bfound_in\s+([^;,\[]*)/
421
+ if @tag and not @obj
422
+ new_obj(loc)
423
+ end
424
+ if @obj
425
+ locs = $1.split
426
+ @obj.location = locs
427
+ debug "#{@obj} #{@obj.location} FOUND_IN: #{locs.join(' ')}"
428
+ end
429
+ end
430
+ end
431
+
432
+
433
+ def set_include_dirs
434
+ # Try to find inform(.exe) in path.
435
+ paths = ENV['PATH'].split(SEP)
436
+ paths.each { |p|
437
+ next if not File.directory?(p)
438
+ Dir.foreach(p) { |x|
439
+ if x =~ /^inform(.exe)?$/i
440
+ @include_dirs << p
441
+ @include_dirs << p + "/Base"
442
+ @include_dirs << p + "/Contrib"
443
+ @include_dirs << p + "/../Contrib"
444
+ @include_dirs << p + "/../Lib/Contrib"
445
+ break
446
+ end
447
+ }
448
+ }
449
+ end
450
+
451
+
452
+ def initialize(file, map = Map.new('Inform Map'))
453
+ debug "Initialize"
454
+ @classes = { 'Object' => {} }
455
+
456
+ super
457
+
458
+ if @map.kind_of?(FXMap)
459
+ @map.filename = file.sub(/\.inf$/i, '.map')
460
+ @map.options['Location Description'] = true
461
+ @map.window.show
462
+ end
463
+ @objects = nil
464
+ @tags = nil # save some memory by clearing the tag list
465
+ @rooms = nil # and room list
466
+ end
467
+ end
468
+
469
+
470
+ if $0 == __FILE__
471
+ p "Opening file '#{ARGV[0]}'"
472
+ BEGIN {
473
+ $LOAD_PATH << 'C:\Windows\Escritorio\IFMapper\lib'
474
+ }
475
+
476
+ require "IFMapper/Map"
477
+ InformReader.new(ARGV[0])
478
+ end