ifmapper 0.8.5 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,360 @@
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
+ # Invalid tag characters, replaced with _
41
+ tag.gsub!(/[\s"'\/\\\-#\,.:;!\?\n\(\)]/,'_')
42
+
43
+ tag.gsub!(/__/, '') # remove reduntant __ repetitions
44
+ tag.sub!(/^([\d]+)_?(.*)/, '\2\1') # No numbers allowed at start of tag
45
+ # tag.downcase! # All tags are lowercase
46
+
47
+
48
+
49
+ tag = tag[0..31] # Max. 32 chars. in tag
50
+
51
+ # tag cannot be keyword (Doorway, Room, Class, etc)
52
+ if tag =~ @keyword
53
+ tag << '1'
54
+ end
55
+
56
+ if @tags.values.include?(tag)
57
+ idx = 2
58
+ root = tag[0..29] # to leave room for number
59
+ while @tags.values.include?(tag)
60
+ tag = "#{root}#{idx}"
61
+ idx += 1
62
+ end
63
+ end
64
+
65
+ if elem.kind_of?(String)
66
+ @tags[tag] = tag
67
+ else
68
+ @tags[elem] = tag
69
+ end
70
+
71
+ return tag
72
+ end
73
+
74
+
75
+
76
+ def get_tag(elem, name = elem.name)
77
+ return @tags[elem] if @tags[elem]
78
+ return new_tag(elem, name)
79
+ end
80
+
81
+
82
+
83
+ def wrap_text(text, width = 74, indent = 79 - width)
84
+ return 'UNDER CONSTRUCTION' if not text or text == ''
85
+ str = TADSWriter::quote( text.dup )
86
+
87
+ if text.size > width
88
+ r = ''
89
+ while str
90
+ idx = str.rindex(/[ -]/, width)
91
+ unless idx
92
+ idx = str.index(/[ -]/, width)
93
+ idx = str.size unless idx
94
+ end
95
+ r << str[0..idx]
96
+ str = str[idx+1..-1]
97
+ r << "\n" << ' ' * indent if str
98
+ end
99
+ return r
100
+ else
101
+ return str
102
+ end
103
+ end
104
+
105
+
106
+ #
107
+ # Take a text and quote it for TADS's double/single-quote text areas.
108
+ #
109
+ def self.quote(text)
110
+ str = text.dup
111
+ # Quote special characters
112
+ str.gsub!(/"/, '\"')
113
+ str.gsub!(/'/, "\\\\'")
114
+ return str
115
+ end
116
+
117
+
118
+ def objects(r)
119
+ room = get_tag(r)
120
+ objs = r.objects.split("\n")
121
+ objs.each { |o|
122
+
123
+ tag = new_tag(o, o)
124
+ name = TADSWriter::quote(o)
125
+
126
+ classname = 'Thing'
127
+
128
+ # If name begins with uppercase, assume it is an NPC
129
+ if name =~ /[A-Z]/
130
+ classname = 'Person'
131
+ end
132
+
133
+ @f.print <<"EOF"
134
+
135
+ #{tag} : #{classname} '#{name}' '#{name}'
136
+ @#{room}
137
+ "#{name} is UNDER CONSTRUCTION."
138
+ ;
139
+ EOF
140
+ }
141
+
142
+
143
+ end
144
+
145
+
146
+
147
+ def door(e)
148
+ tag = get_door_tag(e)
149
+
150
+ b = nil
151
+ destination = ''
152
+ if e.dir == Connection::BOTH
153
+ a = e.roomA
154
+ t = e.exitAtext
155
+ b = e.roomB
156
+ elsif e.dir == Connection::AtoB
157
+ a = e.roomA
158
+ destination = "\n destination = #{get_tag(e.roomB)}"
159
+ t = e.exitAtext
160
+ elsif e.dir == Connection::BtoA
161
+ a = e.roomB
162
+ destination = "\n destination = #{get_tag(e.roomA)}"
163
+ t = e.exitBtext
164
+ end
165
+
166
+ roomA = get_tag(a)
167
+
168
+ dirA = a.exits.index(e)
169
+ if t > 0
170
+ dirA = dirAern = ''
171
+ else
172
+ d = DIRECTIONS[dirA]
173
+ dirA = "#{d} "
174
+ dirAern = "#{d}ern "
175
+ end
176
+
177
+ prop = ''
178
+ if e.type == Connection::LOCKED_DOOR
179
+ prop = 'LockableWithKey, '
180
+ elsif e.type == Connection::CLOSED_DOOR
181
+ end
182
+
183
+
184
+ @f.puts <<"EOF"
185
+
186
+ // Door connecting #{e}
187
+ #{tag} : #{prop}Door '#{dirA}#{dirAern}door' '#{dirAern}door'
188
+ location = #{roomA}#{destination}
189
+ ;
190
+ EOF
191
+
192
+ if b
193
+ roomB = get_tag(b)
194
+ dirB = b.exits.rindex(e)
195
+ if e.exitBtext > 0
196
+ dirB = dirBern = ''
197
+ else
198
+ d = DIRECTIONS[dirB]
199
+ dirB = "#{d} "
200
+ dirBern = "#{d}ern "
201
+ end
202
+
203
+ @f.puts <<"EOF"
204
+
205
+ #{tag}B : #{prop}Door -> #{tag} '#{dirB}#{dirBern}door' '#{dirBern}door'
206
+ location = #{roomB}
207
+ ;
208
+
209
+ EOF
210
+ end
211
+
212
+ end
213
+
214
+
215
+ def room(r)
216
+ tag = get_tag(r)
217
+ name = TADSWriter::quote(r.name)
218
+ dark = r.darkness ? "Dark" : ""
219
+ @f.puts <<-"EOF"
220
+
221
+ //-------------------- #{r.name}
222
+ #{tag} : #{dark}Room '#{name}'
223
+ \"#{wrap_text(r.desc)}\"
224
+ EOF
225
+
226
+ # Now, handle exits...
227
+ r.exits.each_with_index { |e, dir|
228
+ next if (not e) or e.stub? or e.type == Connection::SPECIAL
229
+ if e.roomB == r
230
+ next if e.dir == Connection::AtoB
231
+ text = e.exitBtext
232
+ a = e.roomB
233
+ b = e.roomA
234
+ else
235
+ next if e.dir == Connection::BtoA
236
+ text = e.exitAtext
237
+ a = e.roomA
238
+ b = e.roomB
239
+ end
240
+ @f.print ' '
241
+ if text == 0
242
+ @f.print "#{DIRECTIONS[dir]} = "
243
+ else
244
+ @f.print "#{OTHERDIRS[text]} = "
245
+ end
246
+ if e.type == Connection::CLOSED_DOOR or
247
+ e.type == Connection::LOCKED_DOOR
248
+ @f.puts get_door_tag(e, r )
249
+ else
250
+ @f.puts get_tag(b)
251
+ end
252
+ }
253
+ @f.puts ";"
254
+
255
+ objects(r)
256
+ end
257
+
258
+
259
+
260
+ def section(sect, idx)
261
+ @f = File.open("#@root-#{idx}.t", "w")
262
+ @f.puts '//' + '=' * 78
263
+ @f.puts "// Section: #{sect.name} "
264
+ @f.puts '//' + '=' * 78
265
+ sect.rooms.each { |r|
266
+ room(r)
267
+ }
268
+
269
+ @f.puts
270
+ @f.puts '//' + '-' * 78
271
+ @f.puts '// Doorways'
272
+ @f.puts '//' + '-' * 78
273
+ sect.connections.each { |e|
274
+ next if (e.type != Connection::LOCKED_DOOR and
275
+ e.type != Connection::CLOSED_DOOR)
276
+ door(e)
277
+ }
278
+ @f.close
279
+ end
280
+
281
+
282
+
283
+ def start
284
+ @f = File.open("#@root.t", "w")
285
+ @f.puts '#charset "us-ascii"'
286
+ @f.puts
287
+ @f.puts '//' + '=' * 78
288
+
289
+ story = TADSWriter::quote(@map.name)
290
+ story = 'Untitled' if story == ''
291
+ today = Date.today
292
+
293
+ start_location = ''
294
+ if @map.sections.size > 0 and @map.sections[0].rooms[0]
295
+ r = @map.sections[0].rooms[0]
296
+ tag = get_tag(r)
297
+ start_location = "location = #{tag}"
298
+ end
299
+
300
+ @f.puts <<"EOF"
301
+
302
+ #include <adv3.h>
303
+ #include <en_us.h>
304
+
305
+ versionInfo: GameID
306
+ name = '#{story}'
307
+ byline = '#{@map.creator}'
308
+ version = '1.0'
309
+ release = '#{today}'
310
+ ;
311
+
312
+ me: Actor
313
+ #{start_location}
314
+ ;
315
+
316
+ gameMain: GameMainDef
317
+ initialPlayerChar = me
318
+ ;
319
+
320
+ EOF
321
+ @f.puts '//' + '=' * 78
322
+ @f.puts "// Object classes"
323
+
324
+ @f.puts '//' + '=' * 78
325
+ @f.puts "// Map sections"
326
+ @map.sections.each_index { |idx|
327
+ @f.puts "#include \"#@base-#{idx}.t\""
328
+ }
329
+ @f.close
330
+
331
+ @map.sections.each_with_index { |sect, idx|
332
+ section(sect, idx)
333
+ }
334
+ end
335
+
336
+
337
+ def initialize(map, fileroot)
338
+ @tags = {}
339
+ @root = fileroot
340
+ @base = File.basename(@root)
341
+ @map = map
342
+
343
+ keywords = [
344
+ 'Door',
345
+ 'class',
346
+ 'include',
347
+ 'DarkRoom',
348
+ 'Room',
349
+ 'Thing',
350
+ 'case',
351
+ 'match',
352
+ 'Platform',
353
+ ] + DIRECTIONS
354
+
355
+ @keyword = /^#{keywords.join('|')}$/i
356
+ start
357
+ end
358
+
359
+
360
+ end
@@ -24,10 +24,12 @@ class TranscriptReader
24
24
  TAKE = /^(take|get)\s+(a\s+|the\s+)?(.*)/i
25
25
  DROP = /^(drop|leave)\s+()/i
26
26
  STARTUP = /(^[A-Z]+$|Copyright|\([cC]\)\s*\d|Trademark|Release|Version|[Ss]erial [Nn]umber|Written by)/
27
- DARKNESS = /^dark(ness)?$|It is pitch black/i
27
+ DARKNESS = /^dark(ness)?$|In the dark|It is pitch black/i
28
28
  DEAD = /(You die|You have died|You are dead)/i
29
29
  YES = /^y(es)/i
30
30
 
31
+ THEN = /\bthen\b/i
32
+
31
33
  # Compass direction command -> direction mapping.
32
34
  DIRMAP = {
33
35
  "n" => 0, "north" => 0, "ne" => 1, "northeast" => 1,
@@ -45,8 +47,20 @@ class TranscriptReader
45
47
  # Direction list in order of positioning preference.
46
48
  DIRLIST = [ 0, 4, 2, 6, 1, 3, 5, 7 ]
47
49
 
50
+ # No exit replies
51
+ NO_EXIT = [
52
+ /you\s+can't\s+go\s+that\s+way/i,
53
+ /doorways\s+lead\s+/i,
54
+ /that's\s+a\s+wall/i,
55
+ ]
56
+
57
+ # Closed exit replies
58
+ CLOSED_EXIT = [
59
+ /door\s+is\s+closed/i,
60
+ ]
61
+
48
62
  NAME_REMOVE = /(\s+\(.+\)|,\s+[io]n\s+.+)/ # remove things like (on the bed)
49
- NAME_INVALID = /[\[\]!\.\?\000]/
63
+ NAME_INVALID = /[:;\[\]!\.\?\000]/
50
64
  SALUTATIONS = /\b(Mr|Mr?s|Miss|Jr|Sr)\./
51
65
  NAME_MAXWORDS = 20
52
66
  NAME_MAXUNCAP = 4 # so that lowercase room/end will be accepted
@@ -54,9 +68,15 @@ class TranscriptReader
54
68
  # Default room description recognition parameters.
55
69
  DESC_MINWORDS = 10
56
70
 
71
+ # Ignore these words when matching words in description
72
+ # To avoid cases, like: An open gate vs. A closed gate
73
+ DESC_IGNORE = /(?:an|a|open(ed)?|closed?)/i
74
+
75
+
76
+ RESTORE_OK = /\b(Ok|completed?)\b/
57
77
 
58
78
  ## Change this to non-dil to print out debugging info
59
- @@debug = nil
79
+ @@debug = 1
60
80
 
61
81
  def debug(*msg)
62
82
  if @@debug
@@ -64,14 +84,21 @@ class TranscriptReader
64
84
  end
65
85
  end
66
86
 
87
+ ##
88
+ THE = '\b(the|a|your|some)\b'
89
+
90
+ ARTICLE = /^#{THE}\s+/i
91
+
67
92
  ## Possible nessages indicating get/take succeeded
68
93
  TAKE_OK = [
69
94
  /taken/i,
70
95
  /you\s+pick\s+up\s+the\s+?/i,
71
96
  /you\s+now\s+have\s+(got\s+)?/i,
97
+ /you\s+(grab|pick|take)\s+#{THE}\s+\w+/i
72
98
  ]
73
99
 
74
- IT = /^(it|them)$/
100
+ IT = /^(it|them)$/i
101
+
75
102
 
76
103
  #
77
104
  # Handle a take action
@@ -90,10 +117,11 @@ class TranscriptReader
90
117
  next if cmd == 'get' and o == 'up'
91
118
  o = @last_obj if o =~ IT
92
119
  next if not o
120
+ o.sub!(ARTICLE, '')
93
121
  status = move[:reply].to_s
94
122
  TAKE_OK.each { |re|
95
123
  if status =~ re and not @objects.has_key?(o) and
96
- not @here.objects =~ /#{o}/
124
+ not @here.objects =~ /\b#{o}\b/
97
125
  @here.objects << endl << o << "\n"
98
126
  @objects[o] = 1
99
127
  @last_obj = o
@@ -106,8 +134,9 @@ class TranscriptReader
106
134
  o, status = reply.split(':')
107
135
  next if not status
108
136
  o = @last_obj if o =~ IT
137
+ o.sub!(ARTICLE, '')
109
138
  if o and not @objects.has_key?(o) and
110
- not @here.objects =~ /#{o}/
139
+ not @here.objects =~ /\b#{o}\b/
111
140
  @here.objects << endl << o << "\n"
112
141
  @objects[o] = 1
113
142
  @last_obj = o
@@ -164,7 +193,7 @@ class TranscriptReader
164
193
  # to the east
165
194
  /to\s+the\s+#{DIR}\b/i,
166
195
  # paths lead west and north
167
- /(run|lead|wander|winds)\s+#{DIR}\s+#{OR}\s+#{DIR}\b/i,
196
+ /(run|bears|lead|wander|winds)\s+#{DIR}\s+#{OR}\s+#{DIR}\b/i,
168
197
  # east-west corridor
169
198
  /[\.\s+]#{DIR}[\/-]#{DIR}\b/i,
170
199
  # East is the postoffice
@@ -172,7 +201,7 @@ class TranscriptReader
172
201
  # Directly north
173
202
  /\bDirectly\s+#{DIR}/i,
174
203
  # continues|lies|etc... east
175
- /(runs|leads|heads|opens|winds|continues|branches|lies|wanders|bends|curves)\s+#{DIR}\b/i,
204
+ /(runs|bears|leads|heads|opens|winds|continues|branches|lies|wanders|bends|curves)\s+#{DIR}\b/i,
176
205
  /(running|leading|heading|opening|branching|lying|wandering|looking|bending)\s+#{DIR}\b/i,
177
206
  ]
178
207
 
@@ -190,9 +219,6 @@ class TranscriptReader
190
219
  return if not desc
191
220
  exits = []
192
221
 
193
- # Remove \n from descriptions
194
- desc.gsub(/\n/, ' ')
195
-
196
222
  # Now, start searching for stuff
197
223
 
198
224
  # First try the special directions...
@@ -257,6 +283,11 @@ class TranscriptReader
257
283
  @moves.pop
258
284
  else
259
285
  move = { }
286
+
287
+ # Replace all 'THEN' in command for periods
288
+ cmd.sub!(THEN, '.')
289
+ cmd.sub!(/\s*\.+\s*/, '.') # and multiple periods for just one
290
+
260
291
  move[:cmd] = cmd
261
292
  move[:reply] = reply
262
293
  @moves << move
@@ -290,7 +321,7 @@ class TranscriptReader
290
321
  end
291
322
 
292
323
  if cmd =~ RESTORE
293
- if move[:reply] =~ /Ok/
324
+ if move[:reply].join() =~ RESTORE_OK
294
325
  @here = nil
295
326
  tele = 1
296
327
  else
@@ -305,6 +336,7 @@ class TranscriptReader
305
336
  startup = false
306
337
  rooms = []
307
338
  move[:reply].each { |r|
339
+ puts "LINE: #{r}"
308
340
  tele = 1 if r =~ DEAD
309
341
 
310
342
  if r =~ STARTUP
@@ -328,28 +360,37 @@ class TranscriptReader
328
360
  if not roomflag and r !~ BLANK and room_name(r)
329
361
  debug "set roomflag with #{r}"
330
362
  roomflag = true
363
+ desc_gap = false
331
364
  name = r
365
+ desc = ''
332
366
  next
333
367
  end
334
368
 
335
- break if roomflag and desc_gap and r =~ BLANK
336
-
337
- desc_gap = true if roomflag and r =~ BLANK
369
+ if not desc_gap and roomflag and r =~ BLANK and desc == ''
370
+ desc_gap = true
371
+ next
372
+ end
338
373
 
339
374
 
340
375
  if roomflag and r !~ BLANK
376
+ puts "ADD TO DESC: #{r}"
341
377
  desc << r << "\n"
378
+ next
342
379
  end
343
380
 
344
- if r =~ BLANK and roomflag # and desc != ''
345
-
381
+ if r =~ BLANK and roomflag
382
+ puts "DONE DESC"
346
383
  if desc.count("\n") == 1 and desc =~ /\?$/
347
384
  # A "What next?" type of prompt, not a room description
348
385
  desc = ''
349
386
  end
350
- desc = nil if desc == ''
387
+ if desc == ''
388
+ desc = nil
389
+ else
390
+ desc.gsub!(/\n/, ' ')
391
+ end
351
392
 
352
- puts "name: #{name} tele:#{tele}"
393
+ puts "DESC: #{desc}"
353
394
  rooms << {
354
395
  :name => name,
355
396
  :desc => desc,
@@ -377,7 +418,41 @@ class TranscriptReader
377
418
  take(move, objs)
378
419
  end
379
420
 
380
- next unless move[:rooms]
421
+ if not move[:rooms]
422
+ # Check if a moving command failed
423
+ if cmd =~ GO
424
+ dir = $3
425
+ # See if a stub direction exits in that direction
426
+ if @here and DIRMAP[dir] then
427
+ dir = DIRMAP[dir]
428
+ next unless @here[dir]
429
+
430
+ if @here[dir].stub?
431
+ # Check if reply was that there's no exit there.
432
+ NO_EXIT.each { |re|
433
+ if move[:reply][0] =~ re
434
+ # If so, remove it... automapper got it wrong. Not an exit.
435
+ @map.delete_connection(@here[dir])
436
+ break
437
+ end
438
+ }
439
+ end
440
+
441
+ # Check if there is a closed door
442
+ if @here[dir].type == Connection::FREE
443
+ CLOSED_EXIT.each { |re|
444
+ if move[:reply][0] =~ re
445
+ # If so, flag it
446
+ @here[dir].type = Connection::CLOSED_DOOR
447
+ break
448
+ end
449
+ }
450
+ end
451
+ end
452
+ end
453
+ next
454
+ end
455
+
381
456
  move[:rooms].each { |room|
382
457
  name = room[:name]
383
458
  debug "SECTION: #{@map.section}"
@@ -415,7 +490,11 @@ class TranscriptReader
415
490
  # If it is a look command or we don't know where we are yet,
416
491
  # set current room.
417
492
  if move[:look] or not @here
418
- @here = find_room(name, desc)
493
+ if move[:look] and name =~ DARKNESS
494
+ @here = find_room(name, desc) unless @here.darkness
495
+ else
496
+ @here = find_room(name, desc)
497
+ end
419
498
  @here = new_room(move, name, desc) unless @here
420
499
  @here.selected = true
421
500
  next
@@ -423,6 +502,10 @@ class TranscriptReader
423
502
 
424
503
  cmd.sub!(GO, '')
425
504
  dir = $3
505
+
506
+ # Swallow everything until next command (separated by .)
507
+ cmd.sub!(/.*\./, '')
508
+
426
509
  debug "MOVED IN DIRECTION: #{dir} CMD LEFT:#{cmd}"
427
510
  if not cmd
428
511
  debug "Oops... run out of commands."
@@ -469,10 +552,11 @@ class TranscriptReader
469
552
  dir = DIRMAP[dir]
470
553
  elsif ODIRMAP[dir]
471
554
  go = ODIRMAP[dir]
555
+ debug "#{dir} move #{go}"
472
556
  dir = choose_dir(@here, there, go)
473
557
  else
474
558
  # special move --- teleport/death/etc.
475
- dir = choose_dir(@here, there)
559
+ dir = choose_dir(@here, there, 0)
476
560
  end
477
561
  debug "MOVED IN DIR INDEX: #{dir}"
478
562
 
@@ -482,7 +566,7 @@ class TranscriptReader
482
566
  @here = new_room(move, name, desc, dir, @here, go)
483
567
  else
484
568
  # Visited before -- new link
485
- break unless new_link(move, @here, there, dir, go)
569
+ new_link(move, @here, there, dir, go)
486
570
  @here = there
487
571
  end
488
572
  }
@@ -493,6 +577,7 @@ class TranscriptReader
493
577
  if @map.kind_of?(FXMap)
494
578
  @map.clear_selection
495
579
  @here.selected = true if @here
580
+ @map.create_pathmap
496
581
  @map.zoom = @map.zoom
497
582
  @map.center_view_on_room(@here) if @here
498
583
  @map.verify_integrity
@@ -516,12 +601,22 @@ class TranscriptReader
516
601
  # Try substring match
517
602
  score += 5 if room.desc.index(desc)
518
603
 
604
+ # If we have a room where both name and desc match,
605
+ # we get a better score than just description only.
606
+ # This is to help, for example, Trinity, where two rooms have
607
+ # the exact description but different name.
608
+ score += 1 if room.name == name and score > 0
609
+
519
610
  # If still no luck, try first N words
520
611
  if score == 0
521
612
  dwords = room.desc.split(' ')
522
613
  words = desc.split(' ')
523
614
  match = true
524
615
  0.upto(DESC_MINWORDS) { |i|
616
+ # Ignore some words (like open/close, which may just mean
617
+ # some doors changed state)
618
+ next if words[i] =~ DESC_IGNORE
619
+
525
620
  if words[i] != dwords[i]
526
621
  match = false
527
622
  break
@@ -547,7 +642,11 @@ class TranscriptReader
547
642
  @map.fit
548
643
  @map.section = best[1]
549
644
  end
550
- best[0].desc = desc if not best[0].desc and desc
645
+ puts "DESC: #{desc} RDESC:#{best[0].desc}"
646
+ if desc and (not best[0].desc or best[0].desc == '')
647
+ best[0].desc = desc
648
+ puts "CHANGED: #{best[0].desc}"
649
+ end
551
650
  return best[0]
552
651
  end
553
652
 
@@ -555,6 +654,9 @@ class TranscriptReader
555
654
  # Determine if line corresponds to a room name
556
655
  #
557
656
  def room_name(line)
657
+ # Check if user/game has created a room with that name already
658
+ return true if find_room(line, nil)
659
+
558
660
  # Remove unwanted stuff line (on the bed)
559
661
  line.sub!(NAME_REMOVE, '')
560
662
 
@@ -661,9 +763,12 @@ class TranscriptReader
661
763
  end
662
764
  end
663
765
  if c == nil
664
- c = @map.new_connection( from, dir, r )
665
- c.exitAtext = go if go
666
- c.dir = Connection::AtoB
766
+ begin
767
+ c = @map.new_connection( from, dir, r )
768
+ c.exitAtext = go if go
769
+ c.dir = Connection::AtoB
770
+ rescue Section::ConnectionError
771
+ end
667
772
  end
668
773
  end
669
774
 
@@ -699,7 +804,7 @@ class TranscriptReader
699
804
  dx, dy = Room::DIR_TO_VECTOR[idx]
700
805
  @map.shift(x, y, -dx, -dy)
701
806
  else
702
- raise "Warning. Cannot shift connection."
807
+ debug "Warning. Cannot shift connection."
703
808
  end
704
809
  end
705
810
 
@@ -753,10 +858,15 @@ class TranscriptReader
753
858
  to.darkness = true
754
859
  c = nil
755
860
  else
756
- debug "*** CANNOT AUTOMAP --- MAZE ***"
757
- dir = Room::DIRECTIONS[dir]
758
- @map.cannot_automap "Maze detected.\n'#{from}' #{dir} leads to '#{c.roomB}',\nnot to this '#{to}'."
759
- self.stop
861
+ if c.exitAtext != 0
862
+ shift_link(from, dir)
863
+ c = nil
864
+ else
865
+ debug "*** CANNOT AUTOMAP --- MAZE ***"
866
+ dir = Room::DIRECTIONS[dir]
867
+ @map.cannot_automap "Maze detected.\n'#{from}' #{dir} leads to '#{c.roomB}',\nnot to this '#{to}'."
868
+ end
869
+ # self.stop
760
870
  return nil
761
871
  end
762
872
  else
@@ -812,13 +922,12 @@ class TranscriptReader
812
922
  end
813
923
 
814
924
  if not c
815
- # No link exists -- create new one
925
+ # No link exists -- create new one.
816
926
  begin
817
927
  c = @map.new_connection( from, dir, to, odir )
818
928
  c.exitAtext = go if go
819
929
  c.dir = Connection::AtoB
820
- rescue => e
821
- $stderr.puts e
930
+ rescue Section::ConnectionError
822
931
  end
823
932
  end
824
933
 
@@ -828,6 +937,8 @@ class TranscriptReader
828
937
 
829
938
  # Choose a direction to represent up/down/in/out.
830
939
  def choose_dir(a, b, go = nil, exitB = nil)
940
+ # Don't add a new connection if we already have a normal connection
941
+ # to the room and we are moving up/down/etc.
831
942
  if go
832
943
  rgo = go % 2 == 0? go - 1 : go + 1
833
944
  debug "#{Connection::EXIT_TEXT[go]} <=> #{Connection::EXIT_TEXT[rgo]}"
@@ -836,13 +947,11 @@ class TranscriptReader
836
947
  next if not e
837
948
  roomA = e.roomA
838
949
  roomB = e.roomB
839
- if roomA == a and roomB == b and
840
- (e.exitBtext == rgo or e.exitAtext == go)
841
- e.exitAtext = go
950
+ if roomA == a and roomB == b
951
+ e.exitAtext = go if e.exitBtext == rgo
842
952
  return idx
843
- elsif roomB == a and roomA == b and (e.exitAtext == rgo or
844
- e.exitBtext == go)
845
- e.exitBtext = go
953
+ elsif roomB == a and roomA == b
954
+ e.exitBtext = go if e.exitAtext == rgo
846
955
  return idx
847
956
  end
848
957
  }
@@ -917,19 +1026,28 @@ class TranscriptReader
917
1026
  @here = nil
918
1027
  @section = -1
919
1028
  @last_obj = nil
920
- end
921
1029
 
922
- def start
923
1030
  @f = File.open(@file, 'r')
1031
+ while line = @f.gets
1032
+ break if PROMPT =~ line
1033
+ end
924
1034
  parse
1035
+ end
1036
+
1037
+ # Step one user command at a time
1038
+ def step
1039
+ begin
1040
+ parse_line(@f.gets)
1041
+ rescue => e
1042
+ $stderr.puts e
1043
+ $stderr.puts e.backtrace
1044
+ end
1045
+ end
1046
+
1047
+ def start
925
1048
  @t = Thread.new {
926
1049
  loop do
927
- begin
928
- parse_line(@f.gets)
929
- rescue => e
930
- puts e
931
- puts e.backtrace
932
- end
1050
+ self.step
933
1051
  Thread.pass
934
1052
  end
935
1053
  }