ifmapper 0.8.5 → 0.9

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.
@@ -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
  }