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,370 +1,375 @@
1
-
2
-
3
- class TADSWriter
4
-
5
-
6
-
7
- DIRECTIONS = [
8
- 'north',
9
- 'northeast',
10
- 'east',
11
- 'southeast',
12
- 'south',
13
- 'southwest',
14
- 'west',
15
- 'northwest',
16
- ]
17
-
18
- OTHERDIRS = [
19
- '',
20
- 'up',
21
- 'down',
22
- 'in',
23
- 'out',
24
- ]
25
-
26
-
27
- def get_door_tag(e, r = nil)
28
- dirA = e.roomA.exits.index(e)
29
- dirB = e.roomB.exits.rindex(e)
30
- name = Room::DIRECTIONS[dirA] + "_" + Room::DIRECTIONS[dirB]
31
- name << "_door"
32
- tag = get_tag(e, name)
33
- tag += 'B' if r and r == e.roomB and e.dir == Connection::BOTH
34
- return tag
35
- end
36
-
37
- def new_tag(elem, str)
38
- tag = str.dup
39
-
40
- # Take text from Unicode utf-8 to iso-8859-1
41
- utf = Iconv.new( 'iso-8859-1', 'utf-8' )
42
- tag = utf.iconv( tag )
43
-
44
- # Invalid tag characters, replaced with _
45
- tag.gsub!(/[\s"'\/\\\-#\,.:;!\?\n\(\)]/,'_')
46
-
47
- tag.gsub!(/__/, '') # remove reduntant __ repetitions
48
- tag.sub!(/^([\d]+)_?(.*)/, '\2\1') # No numbers allowed at start of tag
49
- # tag.downcase! # All tags are lowercase
50
-
51
-
52
-
53
- tag = tag[0..31] # Max. 32 chars. in tag
54
-
55
- # tag cannot be keyword (Doorway, Room, Class, etc)
56
- if tag =~ @keyword
57
- tag << '1'
58
- end
59
-
60
- if @tags.values.include?(tag)
61
- idx = 2
62
- root = tag[0..29] # to leave room for number
63
- while @tags.values.include?(tag)
64
- tag = "#{root}#{idx}"
65
- idx += 1
66
- end
67
- end
68
-
69
- if elem.kind_of?(String)
70
- @tags[tag] = tag
71
- else
72
- @tags[elem] = tag
73
- end
74
-
75
- return tag
76
- end
77
-
78
-
79
-
80
- def get_tag(elem, name = elem.name)
81
- return @tags[elem] if @tags[elem]
82
- return new_tag(elem, name)
83
- end
84
-
85
-
86
-
87
- def wrap_text(text, width = 74, indent = 79 - width)
88
- return 'UNDER CONSTRUCTION' if not text or text == ''
89
- str = TADSWriter::quote( text.dup )
90
-
91
- if text.size > width
92
- r = ''
93
- while str
94
- if str.size >= width
95
- idx = str.rindex(/[ -]/, width)
96
- idx = str.size unless idx
97
- else
98
- idx = str.size
99
- end
100
- r << str[0..idx]
101
- str = str[idx+1..-1]
102
- r << "\n" << ' ' * indent if str
103
- end
104
- return r
105
- else
106
- return str
107
- end
108
- end
109
-
110
-
111
- #
112
- # Take a text and quote it for TADS's double/single-quote text areas.
113
- #
114
- def self.quote(text)
115
- str = text.dup
116
-
117
- # Take text from Unicode utf-8 to iso-8859-1
118
- utf = Iconv.new( 'iso-8859-1', 'utf-8' )
119
- str = utf.iconv( str )
120
-
121
- # Quote special characters
122
- str.gsub!(/"/, '\"')
123
- str.gsub!(/'/, "\\\\'")
124
- return str
125
- end
126
-
127
-
128
- def objects(r)
129
- room = get_tag(r)
130
- objs = r.objects.split("\n")
131
- objs.each { |o|
132
-
133
- tag = new_tag(o, o)
134
- name = TADSWriter::quote(o)
135
-
136
- classname = 'Thing'
137
-
138
- # If name begins with uppercase, assume it is an NPC
139
- if name =~ /[A-Z]/
140
- classname = 'Person'
141
- end
142
-
143
- @f.print <<"EOF"
144
-
145
- #{tag} : #{classname} '#{name}' '#{name}'
146
- @#{room}
147
- "#{name} is UNDER CONSTRUCTION."
148
- ;
149
- EOF
150
- }
151
-
152
-
153
- end
154
-
155
-
156
-
157
- def door(e)
158
- tag = get_door_tag(e)
159
-
160
- b = nil
161
- destination = ''
162
- if e.dir == Connection::BOTH
163
- a = e.roomA
164
- t = e.exitAtext
165
- b = e.roomB
166
- elsif e.dir == Connection::AtoB
167
- a = e.roomA
168
- destination = "\n destination = #{get_tag(e.roomB)}"
169
- t = e.exitAtext
170
- elsif e.dir == Connection::BtoA
171
- a = e.roomB
172
- destination = "\n destination = #{get_tag(e.roomA)}"
173
- t = e.exitBtext
174
- end
175
-
176
- roomA = get_tag(a)
177
-
178
- dirA = a.exits.index(e)
179
- if t > 0
180
- dirA = dirAern = ''
181
- else
182
- d = DIRECTIONS[dirA]
183
- dirA = "#{d} "
184
- dirAern = "#{d}ern "
185
- end
186
-
187
- prop = ''
188
- if e.type == Connection::LOCKED_DOOR
189
- prop = 'LockableWithKey, '
190
- elsif e.type == Connection::CLOSED_DOOR
191
- end
192
-
193
-
194
- @f.puts <<"EOF"
195
-
196
- // Door connecting #{e}
197
- #{tag} : #{prop}Door '#{dirA}#{dirAern}door' '#{dirAern}door'
198
- location = #{roomA}#{destination}
199
- ;
200
- EOF
201
-
202
- if b
203
- roomB = get_tag(b)
204
- dirB = b.exits.rindex(e)
205
- if e.exitBtext > 0
206
- dirB = dirBern = ''
207
- else
208
- d = DIRECTIONS[dirB]
209
- dirB = "#{d} "
210
- dirBern = "#{d}ern "
211
- end
212
-
213
- @f.puts <<"EOF"
214
-
215
- #{tag}B : #{prop}Door -> #{tag} '#{dirB}#{dirBern}door' '#{dirBern}door'
216
- location = #{roomB}
217
- ;
218
-
219
- EOF
220
- end
221
-
222
- end
223
-
224
-
225
- def room(r)
226
- tag = get_tag(r)
227
- name = TADSWriter::quote(r.name)
228
- dark = r.darkness ? "Dark" : ""
229
- @f.puts <<-"EOF"
230
-
231
- //-------------------- #{r.name}
232
- #{tag} : #{dark}Room '#{name}'
233
- \"#{wrap_text(r.desc)}\"
234
- EOF
235
-
236
- # Now, handle exits...
237
- r.exits.each_with_index { |e, dir|
238
- next if (not e) or e.stub? or e.type == Connection::SPECIAL
239
- if e.roomB == r
240
- next if e.dir == Connection::AtoB
241
- text = e.exitBtext
242
- a = e.roomB
243
- b = e.roomA
244
- else
245
- next if e.dir == Connection::BtoA
246
- text = e.exitAtext
247
- a = e.roomA
248
- b = e.roomB
249
- end
250
- @f.print ' '
251
- if text == 0
252
- @f.print "#{DIRECTIONS[dir]} = "
253
- else
254
- @f.print "#{OTHERDIRS[text]} = "
255
- end
256
- if e.type == Connection::CLOSED_DOOR or
257
- e.type == Connection::LOCKED_DOOR
258
- @f.puts get_door_tag(e, r )
259
- else
260
- @f.puts get_tag(b)
261
- end
262
- }
263
- @f.puts ";"
264
-
265
- objects(r)
266
- end
267
-
268
-
269
-
270
- def section(sect, idx)
271
- @f = File.open("#@root-#{idx}.t", "w")
272
- @f.puts '//' + '=' * 78
273
- @f.puts "// Section: #{sect.name} "
274
- @f.puts '//' + '=' * 78
275
- sect.rooms.each { |r|
276
- room(r)
277
- }
278
-
279
- @f.puts
280
- @f.puts '//' + '-' * 78
281
- @f.puts '// Doorways'
282
- @f.puts '//' + '-' * 78
283
- sect.connections.each { |e|
284
- next if (e.type != Connection::LOCKED_DOOR and
285
- e.type != Connection::CLOSED_DOOR)
286
- door(e)
287
- }
288
- @f.close
289
- end
290
-
291
-
292
-
293
- def start
294
- @f = File.open("#@root.t", "w")
295
- @f.puts '#charset "us-ascii"'
296
- @f.puts
297
- @f.puts '//' + '=' * 78
298
-
299
- story = TADSWriter::quote(@map.name)
300
- story = 'Untitled' if story == ''
301
- today = Date.today
302
-
303
- start_location = ''
304
- if @map.sections.size > 0 and @map.sections[0].rooms[0]
305
- r = @map.sections[0].rooms[0]
306
- tag = get_tag(r)
307
- start_location = "location = #{tag}"
308
- end
309
-
310
- @f.puts <<"EOF"
311
-
312
- #include <adv3.h>
313
- #include <en_us.h>
314
-
315
- versionInfo: GameID
316
- name = '#{story}'
317
- byline = '#{@map.creator}'
318
- version = '1.0'
319
- release = '#{today}'
320
- ;
321
-
322
- me: Actor
323
- #{start_location}
324
- ;
325
-
326
- gameMain: GameMainDef
327
- initialPlayerChar = me
328
- ;
329
-
330
- EOF
331
- @f.puts '//' + '=' * 78
332
- @f.puts "// Object classes"
333
-
334
- @f.puts '//' + '=' * 78
335
- @f.puts "// Map sections"
336
- @map.sections.each_index { |idx|
337
- @f.puts "#include \"#@base-#{idx}.t\""
338
- }
339
- @f.close
340
-
341
- @map.sections.each_with_index { |sect, idx|
342
- section(sect, idx)
343
- }
344
- end
345
-
346
-
347
- def initialize(map, fileroot)
348
- @tags = {}
349
- @root = fileroot
350
- @base = File.basename(@root)
351
- @map = map
352
-
353
- keywords = [
354
- 'Door',
355
- 'class',
356
- 'include',
357
- 'DarkRoom',
358
- 'Room',
359
- 'Thing',
360
- 'case',
361
- 'match',
362
- 'Platform',
363
- ] + DIRECTIONS
364
-
365
- @keyword = /^(?:#{keywords.join('|')})$/i
366
- start
367
- end
368
-
369
-
370
- end
1
+
2
+
3
+ class TADSWriter
4
+
5
+
6
+
7
+ DIRECTIONS = [
8
+ 'north',
9
+ 'northeast',
10
+ 'east',
11
+ 'southeast',
12
+ 'south',
13
+ 'southwest',
14
+ 'west',
15
+ 'northwest',
16
+ ]
17
+
18
+ OTHERDIRS = [
19
+ '',
20
+ 'up',
21
+ 'down',
22
+ 'in',
23
+ 'out',
24
+ ]
25
+
26
+
27
+ def get_door_tag(e, r = nil)
28
+ dirA = e.roomA.exits.index(e)
29
+ dirB = e.roomB.exits.rindex(e)
30
+ name = Room::DIRECTIONS[dirA] + "_" + Room::DIRECTIONS[dirB]
31
+ name << "_door"
32
+ tag = get_tag(e, name)
33
+ tag += 'B' if r and r == e.roomB and e.dir == Connection::BOTH
34
+ return tag
35
+ end
36
+
37
+ def new_tag(elem, str)
38
+ tag = str.dup
39
+
40
+ # Take text from Unicode utf-8 to iso-8859-1
41
+ utf = Iconv.new( 'iso-8859-1', 'utf-8' )
42
+ tag = utf.iconv( tag )
43
+
44
+ # Invalid tag characters, replaced with _
45
+ tag.gsub!(/[\s"'\/\\\-#\,.:;!\?\n\(\)]/,'_')
46
+
47
+ tag.gsub!(/__/, '') # remove reduntant __ repetitions
48
+ tag.sub!(/^([\d]+)_?(.*)/, '\2\1') # No numbers allowed at start of tag
49
+ # tag.downcase! # All tags are lowercase
50
+
51
+
52
+
53
+ tag = tag[0..31] # Max. 32 chars. in tag
54
+
55
+ # tag cannot be keyword (Doorway, Room, Class, etc)
56
+ if tag =~ @keyword
57
+ tag << '1'
58
+ end
59
+
60
+ if @tags.values.include?(tag)
61
+ idx = 2
62
+ root = tag[0..29] # to leave room for number
63
+ while @tags.values.include?(tag)
64
+ tag = "#{root}#{idx}"
65
+ idx += 1
66
+ end
67
+ end
68
+
69
+ if elem.kind_of?(String)
70
+ @tags[tag] = tag
71
+ else
72
+ @tags[elem] = tag
73
+ end
74
+
75
+ return tag
76
+ end
77
+
78
+
79
+
80
+ def get_tag(elem, name = elem.name)
81
+ return @tags[elem] if @tags[elem]
82
+ return new_tag(elem, name)
83
+ end
84
+
85
+
86
+
87
+ def wrap_text(text, width = 74, indent = 79 - width)
88
+ return 'UNDER CONSTRUCTION' if not text or text == ''
89
+ str = TADSWriter::quote( text.dup )
90
+
91
+ if text.size > width
92
+ r = ''
93
+ while str
94
+ if str.size >= width
95
+ idx = str.rindex(/[ -]/, width)
96
+ idx = str.size unless idx
97
+ else
98
+ idx = str.size
99
+ end
100
+ r << str[0..idx]
101
+ str = str[idx+1..-1]
102
+ r << "\n" << ' ' * indent if str
103
+ end
104
+ return r
105
+ else
106
+ return str
107
+ end
108
+ end
109
+
110
+
111
+ #
112
+ # Take a text and quote it for TADS's double/single-quote text areas.
113
+ #
114
+ def self.quote(text)
115
+ str = text.dup
116
+
117
+ # Take text from Unicode utf-8 to iso-8859-1
118
+ utf = Iconv.new( 'iso-8859-1', 'utf-8' )
119
+ str = utf.iconv( str )
120
+
121
+ # Quote special characters
122
+ str.gsub!(/"/, '\"')
123
+ str.gsub!(/'/, "\\\\'")
124
+ return str
125
+ end
126
+
127
+
128
+ def objects(r)
129
+ room = get_tag(r)
130
+ objs = r.objects.split("\n")
131
+ objs.each { |o|
132
+
133
+ tag = new_tag(o, o)
134
+ name = TADSWriter::quote(o)
135
+
136
+ classname = 'Thing'
137
+
138
+ # If name begins with uppercase, assume it is an NPC
139
+ if name =~ /[A-Z]/
140
+ classname = 'Person'
141
+ end
142
+
143
+ @f.print <<"EOF"
144
+
145
+ #{tag} : #{classname} '#{name}' '#{name}'
146
+ @#{room}
147
+ "#{name} is UNDER CONSTRUCTION."
148
+ ;
149
+ EOF
150
+ }
151
+
152
+
153
+ end
154
+
155
+
156
+
157
+ def door(e)
158
+ tag = get_door_tag(e)
159
+
160
+ b = nil
161
+ destination = ''
162
+ if e.dir == Connection::BOTH
163
+ a = e.roomA
164
+ t = e.exitAtext
165
+ b = e.roomB
166
+ elsif e.dir == Connection::AtoB
167
+ a = e.roomA
168
+ destination = "\n destination = #{get_tag(e.roomB)}"
169
+ t = e.exitAtext
170
+ elsif e.dir == Connection::BtoA
171
+ a = e.roomB
172
+ destination = "\n destination = #{get_tag(e.roomA)}"
173
+ t = e.exitBtext
174
+ end
175
+
176
+ roomA = get_tag(a)
177
+
178
+ dirA = a.exits.index(e)
179
+ if t > 0
180
+ dirA = dirAern = ''
181
+ else
182
+ d = DIRECTIONS[dirA]
183
+ dirA = "#{d} "
184
+ dirAern = "#{d}ern "
185
+ end
186
+
187
+ prop = ''
188
+ if e.type == Connection::LOCKED_DOOR
189
+ prop = 'LockableWithKey, '
190
+ elsif e.type == Connection::CLOSED_DOOR
191
+ end
192
+
193
+
194
+ @f.puts <<"EOF"
195
+
196
+ // Door connecting #{e}
197
+ #{tag} : #{prop}Door '#{dirA}#{dirAern}door' '#{dirAern}door'
198
+ location = #{roomA}#{destination}
199
+ ;
200
+ EOF
201
+
202
+ if b
203
+ roomB = get_tag(b)
204
+ dirB = b.exits.rindex(e)
205
+ if e.exitBtext > 0
206
+ dirB = dirBern = ''
207
+ else
208
+ d = DIRECTIONS[dirB]
209
+ dirB = "#{d} "
210
+ dirBern = "#{d}ern "
211
+ end
212
+
213
+ @f.puts <<"EOF"
214
+
215
+ #{tag}B : #{prop}Door -> #{tag} '#{dirB}#{dirBern}door' '#{dirBern}door'
216
+ location = #{roomB}
217
+ ;
218
+
219
+ EOF
220
+ end
221
+
222
+ end
223
+
224
+
225
+ def room(r)
226
+ tag = get_tag(r)
227
+ name = TADSWriter::quote(r.name)
228
+ dark = r.darkness ? "Dark" : ""
229
+ @f.puts <<-"EOF"
230
+
231
+ //-------------------- #{r.name}
232
+ #{tag} : #{dark}Room '#{name}'
233
+ \"#{wrap_text(r.desc)}\"
234
+ EOF
235
+
236
+ # Now, handle exits...
237
+ r.exits.each_with_index { |e, dir|
238
+ next if (not e) or e.stub? or e.type == Connection::SPECIAL
239
+ if e.loop?
240
+ text = e.exitAtext
241
+ b = e.roomB
242
+ else
243
+ if e.roomB == r
244
+ next if e.dir == Connection::AtoB
245
+ text = e.exitBtext
246
+ a = e.roomB
247
+ b = e.roomA
248
+ else
249
+ next if e.dir == Connection::BtoA
250
+ text = e.exitAtext
251
+ a = e.roomA
252
+ b = e.roomB
253
+ end
254
+ end
255
+ @f.print ' '
256
+ if text == 0
257
+ @f.print "#{DIRECTIONS[dir]} = "
258
+ else
259
+ @f.print "#{OTHERDIRS[text]} = "
260
+ end
261
+ if e.type == Connection::CLOSED_DOOR or
262
+ e.type == Connection::LOCKED_DOOR
263
+ @f.puts get_door_tag(e, r )
264
+ else
265
+ @f.puts get_tag(b)
266
+ end
267
+ }
268
+ @f.puts ";"
269
+
270
+ objects(r)
271
+ end
272
+
273
+
274
+
275
+ def section(sect, idx)
276
+ @f = File.open("#@root-#{idx}.t", "w")
277
+ @f.puts '//' + '=' * 78
278
+ @f.puts "// Section: #{sect.name} "
279
+ @f.puts '//' + '=' * 78
280
+ sect.rooms.each { |r|
281
+ room(r)
282
+ }
283
+
284
+ @f.puts
285
+ @f.puts '//' + '-' * 78
286
+ @f.puts '// Doorways'
287
+ @f.puts '//' + '-' * 78
288
+ sect.connections.each { |e|
289
+ next if (e.type != Connection::LOCKED_DOOR and
290
+ e.type != Connection::CLOSED_DOOR)
291
+ door(e)
292
+ }
293
+ @f.close
294
+ end
295
+
296
+
297
+
298
+ def start
299
+ @f = File.open("#@root.t", "w")
300
+ @f.puts '#charset "us-ascii"'
301
+ @f.puts
302
+ @f.puts '//' + '=' * 78
303
+
304
+ story = TADSWriter::quote(@map.name)
305
+ story = 'Untitled' if story == ''
306
+ today = Date.today
307
+
308
+ start_location = ''
309
+ if @map.sections.size > 0 and @map.sections[0].rooms[0]
310
+ r = @map.sections[0].rooms[0]
311
+ tag = get_tag(r)
312
+ start_location = "location = #{tag}"
313
+ end
314
+
315
+ @f.puts <<"EOF"
316
+
317
+ #include <adv3.h>
318
+ #include <en_us.h>
319
+
320
+ versionInfo: GameID
321
+ name = '#{story}'
322
+ byline = '#{@map.creator}'
323
+ version = '1.0'
324
+ release = '#{today}'
325
+ ;
326
+
327
+ me: Actor
328
+ #{start_location}
329
+ ;
330
+
331
+ gameMain: GameMainDef
332
+ initialPlayerChar = me
333
+ ;
334
+
335
+ EOF
336
+ @f.puts '//' + '=' * 78
337
+ @f.puts "// Object classes"
338
+
339
+ @f.puts '//' + '=' * 78
340
+ @f.puts "// Map sections"
341
+ @map.sections.each_index { |idx|
342
+ @f.puts "#include \"#@base-#{idx}.t\""
343
+ }
344
+ @f.close
345
+
346
+ @map.sections.each_with_index { |sect, idx|
347
+ section(sect, idx)
348
+ }
349
+ end
350
+
351
+
352
+ def initialize(map, fileroot)
353
+ @tags = {}
354
+ @root = fileroot
355
+ @base = File.basename(@root)
356
+ @map = map
357
+
358
+ keywords = [
359
+ 'Door',
360
+ 'class',
361
+ 'include',
362
+ 'DarkRoom',
363
+ 'Room',
364
+ 'Thing',
365
+ 'case',
366
+ 'match',
367
+ 'Platform',
368
+ ] + DIRECTIONS
369
+
370
+ @keyword = /^(?:#{keywords.join('|')})$/i
371
+ start
372
+ end
373
+
374
+
375
+ end