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.
- data/HISTORY.txt +110 -0
- data/IFMapper.gemspec +1 -1
- data/TODO.txt +6 -2
- data/lib/IFMapper/Connection.rb +2 -1
- data/lib/IFMapper/FXConnection.rb +36 -18
- data/lib/IFMapper/FXConnectionDialogBox.rb +10 -2
- data/lib/IFMapper/FXMap.rb +68 -28
- data/lib/IFMapper/FXMapFileDialog.rb +1 -1
- data/lib/IFMapper/FXMapperSettings.rb +15 -5
- data/lib/IFMapper/FXMapperWindow.rb +102 -8
- data/lib/IFMapper/FXRoom.rb +1 -3
- data/lib/IFMapper/FXRoomDialogBox.rb +52 -16
- data/lib/IFMapper/FXSection.rb +9 -0
- data/lib/IFMapper/IFMReader.rb +5 -3
- data/lib/IFMapper/InformReader.rb +803 -0
- data/lib/IFMapper/InformReaderOld.rb +778 -0
- data/lib/IFMapper/InformWriter.rb +349 -0
- data/lib/IFMapper/Map.rb +12 -0
- data/lib/IFMapper/PDFMapExporter.rb +35 -17
- data/lib/IFMapper/Room.rb +4 -1
- data/lib/IFMapper/Section.rb +17 -3
- data/lib/IFMapper/TADSReader.rb +832 -0
- data/lib/IFMapper/TADSWriter.rb +360 -0
- data/lib/IFMapper/TranscriptReader.rb +167 -49
- metadata +16 -11
@@ -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 = /[
|
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 =
|
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 =~
|
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 =~
|
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] =~
|
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
|
-
|
336
|
-
|
337
|
-
|
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
|
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
|
-
|
387
|
+
if desc == ''
|
388
|
+
desc = nil
|
389
|
+
else
|
390
|
+
desc.gsub!(/\n/, ' ')
|
391
|
+
end
|
351
392
|
|
352
|
-
puts "
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
665
|
-
|
666
|
-
|
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
|
-
|
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
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
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
|
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
|
840
|
-
|
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
|
844
|
-
|
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
|
-
|
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
|
}
|