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,778 @@
1
+
2
+ require "IFMapper/Map"
3
+
4
+ class FXMap; end
5
+
6
+ #
7
+ # Class that allows creating a map from an Inform source file.
8
+ #
9
+ class InformReader
10
+
11
+ class ParseError < StandardError; end
12
+ class MapError < StandardError; end
13
+
14
+ # Take a quoted Inform string and return a valid ASCII one, replacing
15
+ # Inform's special characters.
16
+ def self.inform_unquote(text)
17
+ return '' unless text
18
+ text.gsub!(/\~/, '"')
19
+ text.gsub!(/\^/, "\n")
20
+ while text =~ /(@@(\d+))/
21
+ text.sub!($1, $2.to_i.chr)
22
+ end
23
+ return text
24
+ end
25
+
26
+ # Temporary classes used to store inform room information
27
+ class InformObject
28
+ attr_reader :name
29
+ attr_accessor :tag, :location, :enterable
30
+
31
+ def name=(x)
32
+ @name = InformReader::inform_unquote(x)
33
+ end
34
+
35
+ def to_s
36
+ "#@name tag:#@tag"
37
+ end
38
+ def initialize()
39
+ @location = []
40
+ @enterable = []
41
+ end
42
+ end
43
+
44
+ class InformDoor
45
+ attr_accessor :location
46
+ attr_accessor :locked
47
+ attr_accessor :tag
48
+ def method_missing(*x)
49
+ end
50
+ def initialize
51
+ @location = []
52
+ end
53
+ end
54
+
55
+ class InformRoom
56
+ attr_reader :name
57
+ attr_accessor :exits, :tag, :darkness
58
+ def to_s
59
+ "#@name tag:#@tag"
60
+ end
61
+ def name=(x)
62
+ @name = InformReader::inform_unquote(x)
63
+ end
64
+ def initialize
65
+ @exits = Array.new(12, nil)
66
+ @darkness = true
67
+ end
68
+ end
69
+
70
+
71
+ DIRECTIONS = {
72
+ 'n_to' => 0,
73
+ 'ne_to' => 1,
74
+ 'e_to' => 2,
75
+ 'se_to' => 3,
76
+ 's_to' => 4,
77
+ 'sw_to' => 5,
78
+ 'w_to' => 6,
79
+ 'nw_to' => 7,
80
+ 'u_to' => 8,
81
+ 'd_to' => 9,
82
+ 'in_to' => 10,
83
+ 'out_to' => 11
84
+ }
85
+
86
+ FUNCTION = /^\[ (\w+);/
87
+
88
+ GO_OBJ = /\b(#{DIRECTIONS.keys.join('|').gsub(/_to/, '_obj')})\s*:/i
89
+
90
+
91
+ # Direction list in order of positioning preference.
92
+ DIRLIST = [ 0, 4, 2, 6, 1, 3, 5, 7 ]
93
+
94
+ NAME = /(?:^|\s+)p?name\s+/i
95
+ DIR_TO = /(?:^|\s+)(#{DIRECTIONS.keys.join('|')})\s+/i
96
+
97
+ DIR = /(?:^|\s+)(#{DIRECTIONS.keys.join('|')})\s+(\w+)/i
98
+ ENTER_DIR = /(?:^|\s+)(#{DIRECTIONS.keys.join('|')})\s+\[;\s*<<\s*Enter\s+(\w+)\s*>>/i
99
+
100
+ attr_reader :map
101
+
102
+ @@debug = 1
103
+ def debug(*x)
104
+ return unless @@debug
105
+ $stdout.puts x
106
+ $stdout.flush
107
+ end
108
+
109
+ #
110
+ # Main parsing loop. We basically parse the file twice to
111
+ # solve dependencies. Yes, this is inefficient, but the alternative
112
+ # was to build a full parser that understands forward dependencies.
113
+ #
114
+ def parse(file)
115
+ # We start map at 0, 0
116
+ @x, @y = [0, 0]
117
+ @room = nil
118
+
119
+ if @map.kind_of?(FXMap)
120
+ @map.options['Edit on Creation'] = false
121
+ @map.window.hide
122
+ end
123
+ @map.section = 0
124
+
125
+ @parsing = nil
126
+ @last_section = 0
127
+ @ignore_first_section = true
128
+ @room_idx = 0
129
+ line_number = 0
130
+
131
+ debug "...Parse... #{file.path}"
132
+ while not file.eof?
133
+ @line = ''
134
+ while not file.eof? and @line == ''
135
+ @line << file.readline()
136
+ @line.sub!( /^\s*!.*$/, '')
137
+ line_number += 1
138
+ end
139
+ # Remove comments at end of line
140
+ @line.sub!( /\s+![^"]*$/, '')
141
+ # Remove starting spaces (if any)
142
+ @line.sub! /^\s+/, ''
143
+ # Replace \n with simple space
144
+ @line.gsub! /\n/, ' '
145
+ next if @line == ''
146
+ full_line = @line.dup
147
+ begin
148
+ parse_line
149
+ rescue ParseError => e
150
+ $stderr.puts
151
+ $stderr.puts "#{e} at #{file.path}, line #{line_number}:"
152
+ $stderr.puts ">>>> #{full_line};"
153
+ $stderr.puts
154
+ end
155
+ end
156
+ debug "...End Parse..."
157
+ end
158
+
159
+
160
+ CLASS = /^class\s+(\w+)/i
161
+ DOOR = /(?:^|\s+)door_to(?:\s+([^,;]*)|$)/i
162
+ INCLUDE = /^#?include\s+"([^"]+)"/i
163
+ PLAYER_TO = /\bplayerto\((\w+)/i
164
+
165
+
166
+ STD_LIB = [
167
+ 'Parser',
168
+ 'VerbLib',
169
+ 'Grammar'
170
+ ]
171
+
172
+
173
+ def find_file(file)
174
+ return file if File.exists?(file)
175
+ @include_dirs.each { |d|
176
+ [ "#{d}/#{file}",
177
+ "#{d}/#{file}.h",
178
+ "#{d}/#{file}.inf", ].each { |full|
179
+ return full if File.exists?(full)
180
+ }
181
+ }
182
+ return nil
183
+ end
184
+
185
+ #
186
+ # Parse a line of file
187
+ #
188
+ def parse_line
189
+ if @line =~ INCLUDE
190
+ name = $1
191
+ unless STD_LIB.include?(name)
192
+ file = find_file(name)
193
+ if file
194
+ File.open(file, 'r') { |f| parse(f) }
195
+ else
196
+ raise ParseError, "Include file #{name} not found"
197
+ end
198
+ end
199
+ end
200
+
201
+ if @line =~ CLASS
202
+ @clas = $1
203
+ debug "CLASS: #@clas"
204
+ if @classes.has_key?(@clas)
205
+ if @obj
206
+ else
207
+ end
208
+ else
209
+ @classes[@clas] = {}
210
+ @tag = @name = nil
211
+ end
212
+ end
213
+
214
+ re = /^(#{@classes.keys.join('|')})(\s+->)?(\s+(\w+))?(\s+"([^"]+)")?(\s+(\w+))?/
215
+ if @line =~ re
216
+ @clas = $1
217
+ prev = $2
218
+ @tag = $4 || $6
219
+ @name = $6
220
+
221
+ loc = $8
222
+ if prev and @room
223
+ loc = @room.tag
224
+ end
225
+
226
+ debug <<"EOF"
227
+ CURRENT ROOM:#@room
228
+ Class : #@clas
229
+ Tag : #@tag
230
+ Name : #@name
231
+ Location: #{loc} #{loc.class}
232
+ EOF
233
+
234
+ @obj = nil
235
+
236
+ c = @classes[@clas]
237
+ if c[:door]
238
+ @obj = InformDoor.new
239
+ @obj.tag = @tag
240
+ @tags[@tag] = @obj
241
+ @doors << @obj
242
+ debug "+++ DOOR"
243
+ elsif loc or c[:scenery] or c[:static]
244
+ debug "+++ OBJECT"
245
+ if @tag
246
+ @obj = InformObject.new()
247
+ @obj.tag = @tag
248
+ @obj.name = @name
249
+ @obj.location << loc
250
+ @tags[@tag] = @obj
251
+ @objects << @obj
252
+ end
253
+ else
254
+ debug "+++ ROOM?"
255
+ @obj = @room = nil
256
+ if @tag and @name
257
+ # We assume we are a room (albeit we could be an obj)
258
+ @room = InformRoom.new
259
+ @room.tag = @tag
260
+ @room.name = @name
261
+ @room.darkness = !c[:light]
262
+ @tags[@tag] = @room
263
+ @rooms << @room
264
+ end
265
+ end
266
+ @before = false
267
+ @go = false
268
+
269
+ end
270
+
271
+ if @line =~ NAME
272
+ # We have an object. Delete last room we created, as it is not one.
273
+ @rooms.delete_if { |r| r.tag == @tag }
274
+ @obj = InformObject.new()
275
+ @obj.tag = @tag
276
+ @obj.name = @name
277
+ @tags[@tag] = @obj
278
+ @objects << @obj
279
+ end
280
+
281
+ if @line =~ FUNCTION
282
+ @functions << $1
283
+ end
284
+
285
+ dirs = @line.scan(DIR_TO) + @line.scan(DIR) + @line.scan(ENTER_DIR)
286
+ if dirs.size > 0
287
+ dirs.each { |d, room|
288
+ dir = DIRECTIONS[d]
289
+ @room.exits[dir] = room
290
+ }
291
+ end
292
+
293
+ if @line =~ /\bbefore\b/i
294
+ @before = true
295
+ end
296
+
297
+ if @line =~ /\bgo\s*:/i and @room and @before
298
+ if @line =~ GO_OBJ
299
+ dir = DIRECTIONS[$1]
300
+ if @line =~ PLAYER_TO
301
+ @room.exits[dir] = $1
302
+ end
303
+ end
304
+ end
305
+
306
+ if @obj.kind_of?(InformObject) and @before
307
+ if @line =~ PLAYER_TO
308
+ @obj.enterable << $1
309
+ end
310
+ end
311
+
312
+ if @tag and @line =~ DOOR
313
+ door = InformDoor.new
314
+ door.location = $1.split(' ')
315
+ door.location += @obj.location if @obj
316
+ door.tag = @tag
317
+ @obj = door
318
+ @tags[@tag] = door
319
+ @doors << door
320
+ @objects.delete(@obj) if @obj and @obj.tag == @tag
321
+ end
322
+
323
+ if @line =~ /\bhas\s+([^,;]+)[,;]/i
324
+ props = $1.split
325
+ props.each { |p|
326
+ if not @tag
327
+ if p[0,1] == '~'
328
+ @classes[@clas][p[1,-1].to_sym] = false
329
+ else
330
+ @classes[@clas][p.to_sym] = true
331
+ end
332
+ else
333
+ if p =~ /locked/ and @doors.size > 0
334
+ @doors[-1].locked = true
335
+ end
336
+ if p =~ /(static|scenery)/ and @obj
337
+ @objects.delete(@obj)
338
+ end
339
+ if @room and p =~ /(\~)?light/
340
+ dark = ($1 == '~')
341
+ @room.darkness = dark
342
+ end
343
+ end
344
+ }
345
+ end
346
+
347
+ if @line =~ /\bfound_in\s+(.*)[,;]?/
348
+ if not @obj
349
+ puts '-' * 78
350
+ puts "#@name -> obj not defined "
351
+ puts '-' * 78
352
+ else
353
+ locs = $1.split
354
+ @obj.location = locs
355
+ debug "#{@obj} #{@obj.location} FOUND_IN: #{locs.join(' ')}"
356
+ end
357
+ end
358
+ end
359
+
360
+
361
+ def shift_link(room, dir)
362
+ idx = dir + 1
363
+ idx = 0 if idx > 7
364
+ while idx != dir
365
+ break if not room[idx]
366
+ idx += 1
367
+ idx = 0 if idx > 7
368
+ end
369
+ if idx != dir
370
+ room[idx] = room[dir]
371
+ room[dir] = nil
372
+ # get position of other room
373
+ ox, oy = Room::DIR_TO_VECTOR[dir]
374
+ c = room[idx]
375
+ if c.roomA == room
376
+ b = c.roomB
377
+ else
378
+ b = c.roomA
379
+ end
380
+ x, y = [b.x, b.y]
381
+ x -= ox
382
+ y -= oy
383
+ dx, dy = Room::DIR_TO_VECTOR[idx]
384
+ @map.shift(x, y, -dx, -dy)
385
+ else
386
+ # raise "Warning. Cannot shift connection for #{room}."
387
+ end
388
+ end
389
+
390
+
391
+ def oneway_link?(a, b)
392
+ # First, check if room already has exit moving towards other room
393
+ a.exits.each_with_index { |e, idx|
394
+ next if not e or e.dir != Connection::AtoB
395
+ roomA = e.roomA
396
+ roomB = e.roomB
397
+ if roomA == a and roomB == b
398
+ return e
399
+ end
400
+ }
401
+ return nil
402
+ end
403
+
404
+
405
+ # Choose a direction to represent up/down/in/out.
406
+ def choose_dir(a, b, go = nil, exitB = nil)
407
+ if go
408
+ rgo = go % 2 == 0? go - 1 : go + 1
409
+ # First, check if room already has exit moving towards other room
410
+ a.exits.each_with_index { |e, idx|
411
+ next if not e or e.stub?
412
+ roomA = e.roomA
413
+ roomB = e.roomB
414
+ if roomA == a and roomB == b
415
+ e.exitAtext = go
416
+ return idx
417
+ elsif roomB == a and roomA == b
418
+ e.exitBtext = go
419
+ return idx
420
+ end
421
+ }
422
+ end
423
+
424
+ # We prefer directions that travel less... so we need to figure
425
+ # out where we start from...
426
+ if b
427
+ x = b.x
428
+ y = b.y
429
+ else
430
+ x = a.x
431
+ y = a.y
432
+ end
433
+ if exitB
434
+ dx, dy = Room::DIR_TO_VECTOR[exitB]
435
+ x += dx
436
+ y += dy
437
+ end
438
+
439
+ # No such luck... Pick a direction.
440
+ best = nil
441
+ bestscore = nil
442
+
443
+ DIRLIST.each { |dir|
444
+ # We prefer straight directions to diagonal ones
445
+ inc = dir % 2 == 1 ? 100 : 140
446
+ score = 1000
447
+ # We prefer directions where both that dir and the opposite side
448
+ # are empty.
449
+ if (not a[dir]) or a[dir].stub?
450
+ score += inc
451
+ score += 4 if a[dir] #attaching to stubs is better
452
+ end
453
+ # rdir = (dir + 4) % 8
454
+ # score += 1 unless a[rdir]
455
+
456
+ # Measure distance for that exit, we prefer shorter
457
+ # paths
458
+ dx, dy = Room::DIR_TO_VECTOR[dir]
459
+ dx = (a.x + dx) - x
460
+ dy = (a.y + dy) - y
461
+ d = dx * dx + dy * dy
462
+ score -= d
463
+ next if bestscore and score <= bestscore
464
+ bestscore = score
465
+ best = dir
466
+ }
467
+
468
+ if not bestscore
469
+ raise "No free exit for choose_dir"
470
+ end
471
+
472
+ return best
473
+ end
474
+
475
+ def new_room(from, to, x, y, dx = 1, dy = 0 )
476
+ elem = @tags[to.tag]
477
+ if elem.kind_of?(InformRoom)
478
+ if not @map.free?(x, y)
479
+ @map.shift(x, y, dx, dy)
480
+ end
481
+ room = @map.new_room(x, y)
482
+ room.name = to.name
483
+ room.darkness = to.darkness
484
+ @tags[to.tag] = room
485
+ return [room, Connection::FREE]
486
+ elsif elem.kind_of?(InformDoor)
487
+ if elem.locked
488
+ type = Connection::LOCKED_DOOR
489
+ else
490
+ type = Connection::CLOSED_DOOR
491
+ end
492
+
493
+ @rooms.each { |o|
494
+ next if @tags[o.tag] == from
495
+ o.exits.each { |e|
496
+ next unless e
497
+ if @tags[e] == elem
498
+ res = new_room( o, o, x, y, dx, dy )
499
+ return [ res[0], type ]
500
+ end
501
+ }
502
+ }
503
+
504
+ # Okay, connecting room is missing. Check door's locations property
505
+ p elem.location
506
+ elem.location.each { |tag|
507
+ next if @tags[tag] == from
508
+ @rooms.each { |o|
509
+ next if o.tag != tag
510
+ res = new_room( o, o, x, y, dx, dy )
511
+ return [ res[0], type ]
512
+ }
513
+ }
514
+
515
+ #raise "error: no room with door #{to.name} #{elem.name}"
516
+ return [nil, nil]
517
+ else
518
+ return [elem, Connection::FREE]
519
+ end
520
+ end
521
+
522
+ def create_room(r, x, y, dx = 1, dy = 0)
523
+ from, = new_room(r, r, x, y)
524
+ debug "CREATE ROOM #{r.name} SET FROM TO: #{from}"
525
+
526
+ r.exits.each_with_index { |e, exit|
527
+ next unless e
528
+ next if e == 'nothing'
529
+ debug "#{r.name} EXIT:#{exit} points to #{e}"
530
+
531
+ to = @tags[e]
532
+ if not to
533
+ next if @functions.include?(e)
534
+ raise "Room #{e} #{e.class} not found." if not to
535
+ end
536
+
537
+ go = c = nil
538
+
539
+ dir = exit
540
+ type = 0
541
+
542
+ # If exit leads to an enterable object, find out where does that
543
+ # enterable object lead to.
544
+ if to.kind_of?(InformObject)
545
+ rooms = to.enterable
546
+ rooms.each { |room|
547
+ next if room == r
548
+ to = @tags[room]
549
+ break
550
+ }
551
+ # Skip it if we are still an object. This means we are just
552
+ # a container, like the phone booth in the Fate game demo.
553
+ next if to.kind_of?(InformObject)
554
+ end
555
+
556
+ if to.kind_of?(InformRoom) or to.kind_of?(InformDoor)
557
+ if dir > 7
558
+ # choose a dir for up/down/in/out
559
+ go = dir - 7
560
+ dir = choose_dir(from, nil, go)
561
+ end
562
+
563
+ dx, dy = Room::DIR_TO_VECTOR[dir]
564
+ x = from.x + dx
565
+ y = from.y + dy
566
+ debug "#{exit} CREATE TO #{from} -> #{to.tag}"
567
+ to, type = new_room(from, to, x, y, dx, dy)
568
+ next if not to
569
+ puts "---- back: #{to.name} #{to.class}"
570
+ else
571
+ if dir > 7
572
+ # choose a dir for up/down/in/out
573
+ go = dir - 7
574
+ dir = choose_dir(from, to, go)
575
+ end
576
+ end
577
+
578
+ odir = (dir + 4) % 8
579
+
580
+ if from[dir]
581
+ c = from[dir]
582
+ if to[odir] == c and c.roomB == from
583
+ debug "LINK TRAVELLED BOTH"
584
+ c.dir = Connection::BOTH
585
+ c.exitBtext = go if go
586
+ next
587
+ else
588
+ debug "#{exit} FROM #{from}->#{to} BLOCKED DIR: #{dir}"
589
+ shift_link(from, dir)
590
+ end
591
+ end
592
+
593
+ # Check we don't have a connection already
594
+ if to[odir]
595
+ c = to[odir]
596
+ debug "#{from} #{dir} -> #{to} dir:#{odir} filled. Swap..."
597
+
598
+ # We need to change odir to something else
599
+ rgo = 0
600
+ if go
601
+ rgo = go % 2 == 0? go - 1 : go + 1
602
+ end
603
+
604
+ # First, check if we have a dangling one-way link going to->from
605
+ # If we do, we use it.
606
+ c = oneway_link?(from, to)
607
+ if not c
608
+ odir = choose_dir(to, from, rgo, dir)
609
+ debug "Swapped to #{odir}"
610
+ else
611
+ debug "FOUND LINK #{c} -- filling it."
612
+ idx = from.exits.index(c)
613
+ from[idx] = nil
614
+ from[dir] = c
615
+ c.dir = Connection::BOTH
616
+ c.exitBtext = go
617
+ end
618
+ else
619
+ debug "to[odir] empty."
620
+ # First, check if we have a dangling one-way link going to->from
621
+ # If we do, we use it.
622
+ c = oneway_link?(to, from)
623
+ if c
624
+ debug "FOUND LINK #{c} -- filling it."
625
+ idx = from.exits.index(c)
626
+ from[idx] = nil
627
+ from[dir] = c
628
+ c.dir = Connection::BOTH
629
+ c.exitBtext = go if go
630
+ end
631
+ end
632
+
633
+ if not c
634
+ debug "NEW LINK #{from} #{dir} to #{to} #{odir}"
635
+ begin
636
+ c = @map.new_connection(from, dir, to, odir)
637
+ c.exitAtext = go if go
638
+ c.dir = Connection::AtoB
639
+ c.type = type
640
+ rescue Section::ConnectionError
641
+ end
642
+ end
643
+ }
644
+
645
+ return r
646
+ end
647
+
648
+ #
649
+ # Create all the stuff we found
650
+ #
651
+ def create
652
+ @rooms.each { |r| create_room(r, 0, 0) }
653
+ @rooms = []
654
+
655
+ # Add objects to rooms
656
+ @objects.each { |obj|
657
+ obj.location.each { |loc|
658
+ r = @tags[loc]
659
+ next unless r and r.kind_of?(Room)
660
+ r.objects << obj.name + "\n"
661
+ }
662
+ }
663
+ end
664
+
665
+
666
+ if RUBY_PLATFORM =~ /win/
667
+ SEP = ';'
668
+ else
669
+ SEP = ':'
670
+ end
671
+
672
+ #
673
+ # Bring up the Inform properties window, to allow user to change
674
+ # settings
675
+ #
676
+ def properties
677
+ decor = DECOR_TITLE|DECOR_BORDER
678
+
679
+ dlg = FXDialogBox.new( @map.window.parent, "Inform Settings", decor )
680
+ mainFrame = FXVerticalFrame.new(dlg,
681
+ FRAME_SUNKEN|FRAME_THICK|
682
+ LAYOUT_FILL_X|LAYOUT_FILL_Y)
683
+
684
+ frame = FXHorizontalFrame.new(mainFrame, LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
685
+
686
+ FXLabel.new(frame, "Include Dirs: ", nil, 0, LAYOUT_FILL_X)
687
+ inc = FXTextField.new(frame, 80, nil, 0, LAYOUT_FILL_ROW)
688
+ inc.text = @include_dirs.join(SEP)
689
+
690
+ buttons = FXHorizontalFrame.new(mainFrame,
691
+ LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|
692
+ PACK_UNIFORM_WIDTH)
693
+ # Accept
694
+ FXButton.new(buttons, "&Accept", nil, dlg, FXDialogBox::ID_ACCEPT,
695
+ FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT|LAYOUT_CENTER_Y)
696
+
697
+ # Cancel
698
+ FXButton.new(buttons, "&Cancel", nil, dlg, FXDialogBox::ID_CANCEL,
699
+ FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT|LAYOUT_CENTER_Y)
700
+ if dlg.execute != 0
701
+ @include_dirs = inc.text.split(SEP)
702
+ return true
703
+ end
704
+ return false
705
+ end
706
+
707
+
708
+
709
+ def set_include_dirs
710
+ # Try to find inform(.exe) in path.
711
+ paths = ENV['PATH'].split(SEP)
712
+ paths.each { |p|
713
+ next if not File.directory?(p)
714
+ Dir.foreach(p) { |x|
715
+ if x =~ /^inform(.exe)?$/i
716
+ @include_dirs << p
717
+ @include_dirs << p + "/Base"
718
+ @include_dirs << p + "/Contrib"
719
+ @include_dirs << p + "/../Contrib"
720
+ break
721
+ end
722
+ }
723
+ }
724
+ end
725
+
726
+ def initialize(file, map = Map.new('Inform Map'))
727
+ debug "Initialize"
728
+ @classes = { 'Object' => {} }
729
+ @tags = {}
730
+ @map = map
731
+ @objects = []
732
+ @doors = []
733
+ @functions = []
734
+ @rooms = []
735
+
736
+ @include_dirs = [File.dirname(file)]
737
+ set_include_dirs
738
+
739
+
740
+ debug "Get properties"
741
+ if @map.kind_of?(FXMap)
742
+ return unless properties
743
+ end
744
+
745
+ debug "Start parsing #{file}"
746
+ File.open(file) { |f|
747
+ parse(f)
748
+ }
749
+ debug "Done parsing #{file}"
750
+ debug "Rooms: #{@rooms.size}"
751
+ debug "Doors: #{@doors.size}"
752
+ debug "Objects: #{@objects.size}"
753
+
754
+ create
755
+ debug "Done creating #{file}"
756
+
757
+ if @map.kind_of?(FXMap)
758
+ @map.filename = file.sub(/\.inf$/i, '.map')
759
+ # @map.navigation = true
760
+ @map.modified = false
761
+ @map.window.show
762
+ end
763
+ @objects = nil
764
+ @tags = nil # save some memory by clearing the tag list
765
+ @rooms = nil # and room list
766
+ end
767
+ end
768
+
769
+
770
+ if $0 == __FILE__
771
+ p "Opening file '#{ARGV[0]}'"
772
+ BEGIN {
773
+ $LOAD_PATH << 'C:\Windows\Escritorio\IFMapper\lib'
774
+ }
775
+
776
+ require "IFMapper/Map"
777
+ InformReader.new(ARGV[0])
778
+ end