ifmapper 0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. data/HISTORY.txt +2 -0
  2. data/IFMapper.gemspec +21 -0
  3. data/IFMapper.rb +27 -0
  4. data/icons/copy.png +0 -0
  5. data/icons/cut.png +0 -0
  6. data/icons/filenew.png +0 -0
  7. data/icons/fileopen.png +0 -0
  8. data/icons/filesave.png +0 -0
  9. data/icons/filesaveas.png +0 -0
  10. data/icons/help.png +0 -0
  11. data/icons/kill.png +0 -0
  12. data/icons/nextpage.png +0 -0
  13. data/icons/paste.png +0 -0
  14. data/icons/prevpage.png +0 -0
  15. data/icons/printicon.png +0 -0
  16. data/icons/redo.png +0 -0
  17. data/icons/saveas.png +0 -0
  18. data/icons/undo.png +0 -0
  19. data/icons/winapp.png +0 -0
  20. data/icons/zoom.png +0 -0
  21. data/lib/IFMapper/Connection.rb +63 -0
  22. data/lib/IFMapper/FXAboutDialogBox.rb +32 -0
  23. data/lib/IFMapper/FXConnection.rb +283 -0
  24. data/lib/IFMapper/FXConnectionDialogBox.rb +126 -0
  25. data/lib/IFMapper/FXMap.rb +1614 -0
  26. data/lib/IFMapper/FXMapDialogBox.rb +51 -0
  27. data/lib/IFMapper/FXMapFileDialog.rb +29 -0
  28. data/lib/IFMapper/FXMapperSettings.rb +45 -0
  29. data/lib/IFMapper/FXMapperWindow.rb +1051 -0
  30. data/lib/IFMapper/FXPage.rb +24 -0
  31. data/lib/IFMapper/FXPageDialogBox.rb +38 -0
  32. data/lib/IFMapper/FXRoom.rb +218 -0
  33. data/lib/IFMapper/FXRoomDialogBox.rb +119 -0
  34. data/lib/IFMapper/FXSearchDialogBox.rb +51 -0
  35. data/lib/IFMapper/FXSpline.rb +54 -0
  36. data/lib/IFMapper/FXWarningBox.rb +45 -0
  37. data/lib/IFMapper/IFMReader.rb +613 -0
  38. data/lib/IFMapper/Map.rb +110 -0
  39. data/lib/IFMapper/PDFMapExporter.rb +315 -0
  40. data/lib/IFMapper/Page.rb +158 -0
  41. data/lib/IFMapper/Room.rb +104 -0
  42. data/maps/Bureaucracy.ifm +75 -0
  43. data/maps/Hollywood_Hijinx.ifm +149 -0
  44. data/maps/Jigsaw.ifm +806 -0
  45. data/maps/LGOP.ifm +705 -0
  46. data/maps/Mercy.ifm +76 -0
  47. data/maps/Planetfall.ifm +186 -0
  48. data/maps/Plundered_Hearts.ifm +251 -0
  49. data/maps/Ralph.ifm +50 -0
  50. data/maps/Robots_of_Dawn.ifm +224 -0
  51. data/maps/Seastalker.ifm +149 -0
  52. data/maps/Sherlock.ifm +209 -0
  53. data/maps/SoFar.ifm +72 -0
  54. data/maps/Starcross.ifm +170 -0
  55. data/maps/Suspended.ifm +82 -0
  56. data/maps/Wishbringer.ifm +277 -0
  57. data/maps/Wishbringer2.ifm +246 -0
  58. data/maps/Zork1.ifm +410 -0
  59. data/maps/Zork2.ifm +150 -0
  60. data/maps/Zork3.ifm +136 -0
  61. data/maps/Zork_Zero.ifm +557 -0
  62. data/maps/anchor.ifm +645 -0
  63. data/maps/atrox.ifm +134 -0
  64. data/maps/awaken.ifm +116 -0
  65. data/maps/babel.ifm +279 -0
  66. data/maps/bse.ifm +150 -0
  67. data/maps/change.ifm +128 -0
  68. data/maps/curses.ifm +307 -0
  69. data/maps/curves.ifm +529 -0
  70. data/maps/edifice.ifm +158 -0
  71. data/maps/frozen.ifm +126 -0
  72. data/maps/glow.ifm +101 -0
  73. data/maps/library.ifm +93 -0
  74. data/maps/mindelec.ifm +89 -0
  75. data/maps/minster.ifm +234 -0
  76. data/maps/muse.ifm +154 -0
  77. data/maps/paperchase.ifm +110 -0
  78. data/maps/space_st.ifm +104 -0
  79. data/maps/stationfall.ifm +320 -0
  80. data/maps/theatre.ifm +182 -0
  81. data/maps/toonesia.ifm +54 -0
  82. data/maps/tortoise.ifm +72 -0
  83. data/maps/vgame.ifm +219 -0
  84. data/maps/weather.ifm +98 -0
  85. data/maps/windhall.ifm +154 -0
  86. data/maps/zebulon.ifm +68 -0
  87. metadata +144 -0
@@ -0,0 +1,54 @@
1
+
2
+
3
+ class FXPoint
4
+ def [](idx)
5
+ return x if idx == 0
6
+ return y if idx == 1
7
+ end
8
+ end
9
+
10
+ class FXSpline
11
+ def self.bspline_solve( r, p, num )
12
+ ax = -1.0/6 * p[0][0] + 0.5 * p[1][0] - 0.5 * p[2][0] + 1.0/6 * p[3][0]
13
+ bx = 0.5 * p[0][0] - p[1][0] + 0.5 * p[2][0]
14
+ cx = -0.5 * p[0][0] + 0.5 * p[2][0]
15
+ dx = 1.0/6 * p[0][0] + 4.0/6 * p[1][0] + 1.0/6 * p[2][0]
16
+
17
+ ay = -1.0/6 * p[0][1] + 0.5 * p[1][1] - 0.5 * p[2][1] + 1.0/6 * p[3][1]
18
+ by = 0.5 * p[0][1] - p[1][1] + 0.5 * p[2][1]
19
+ cy = -0.5 * p[0][1] + 0.5 * p[2][1]
20
+ dy = 1.0/6 * p[0][1] + 4.0/6 * p[1][1] + 1.0/6 * p[2][1]
21
+
22
+ t = 0.0
23
+ tinc = 1.0 / num
24
+ 0.upto(num) {
25
+ x = t * (t * (t * ax + bx) + cx) + dx
26
+ y = t * (t * (t * ay + by) + cy) + dy
27
+ r << [ x, y ]
28
+ t += tinc
29
+ }
30
+ end
31
+
32
+ def self.bspline(p)
33
+ pts = []
34
+ 0.upto(p.size-4) { |x|
35
+ x2 = p[x+2][0] - p[x+1][0]
36
+ x2 = x2 * x2
37
+ y2 = p[x+2][1] - p[x+1][1]
38
+ y2 = y2 * y2
39
+ num = Math.sqrt( x2 + y2 ).to_i / 8
40
+ bspline_solve(pts, p[x..x+3], num)
41
+ }
42
+ return pts
43
+ end
44
+ end
45
+
46
+ class FXDC
47
+ # Draw a bspline curve of any number of segments
48
+ def drawBSpline(p)
49
+ tmp = FXSpline::bspline(p)
50
+ pts = tmp.collect { |x| FXPoint.new( x[0].to_i, x[1].to_i) }
51
+ drawLines(pts)
52
+ return pts
53
+ end
54
+ end
@@ -0,0 +1,45 @@
1
+
2
+
3
+ class FXWarningBox < FXDialogBox
4
+ def initialize(parent, text)
5
+ super( parent, "Warning", DECOR_ALL, 0, 0, 400, 130)
6
+ # Frame
7
+ s = FXVerticalFrame.new(self,
8
+ LAYOUT_SIDE_TOP|LAYOUT_FILL_X)
9
+
10
+ f = FXHorizontalFrame.new(s, LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y)
11
+
12
+ font = FXFont.new(app, "Helvetica", 30)
13
+ oops = FXLabel.new(f, "!", nil, 0, LAYOUT_SIDE_LEFT|LAYOUT_FILL_X|
14
+ LAYOUT_CENTER_Y)
15
+ oops.frameStyle = FRAME_RAISED|FRAME_THICK
16
+ oops.baseColor = 'dark grey'
17
+ oops.textColor = 'red'
18
+ oops.padLeft = oops.padRight = 15
19
+ oops.shadowColor = 'black'
20
+ oops.borderColor = 'white'
21
+ oops.font = font
22
+
23
+ FXLabel.new(f, text, nil, 0)
24
+
25
+ # Separator
26
+ FXHorizontalSeparator.new(s,
27
+ LAYOUT_SIDE_TOP|LAYOUT_FILL_X|SEPARATOR_GROOVE)
28
+
29
+ # Bottom buttons
30
+ buttons = FXHorizontalFrame.new(s,
31
+ LAYOUT_SIDE_BOTTOM|FRAME_NONE|
32
+ LAYOUT_FILL_X|PACK_UNIFORM_WIDTH)
33
+ # Accept
34
+ yes = FXButton.new(buttons, "&Yes", nil, self, FXDialogBox::ID_ACCEPT,
35
+ FRAME_RAISED|FRAME_THICK|LAYOUT_FILL_X|
36
+ LAYOUT_RIGHT|LAYOUT_CENTER_Y)
37
+
38
+ # Cancel
39
+ no = FXButton.new(buttons, "&No", nil, self, FXDialogBox::ID_CANCEL,
40
+ FRAME_RAISED|FRAME_THICK|LAYOUT_FILL_X|
41
+ LAYOUT_RIGHT|LAYOUT_CENTER_Y)
42
+ no.setDefault
43
+ no.setFocus
44
+ end
45
+ end
@@ -0,0 +1,613 @@
1
+
2
+ require "IFMapper/Map"
3
+
4
+ class FXMap; end
5
+
6
+ #
7
+ # Class that allows importing an IFM map file.
8
+ #
9
+ class IFMReader
10
+
11
+ class ParseError < StandardError; end
12
+ class MapError < StandardError; end
13
+
14
+
15
+ DIRECTIONS = {
16
+ 'north' => 0,
17
+ 'n' => 0,
18
+ 'northeast' => 1,
19
+ 'ne' => 1,
20
+ 'east' => 2,
21
+ 'e' => 2,
22
+ 'southeast' => 3,
23
+ 'se' => 3,
24
+ 'south' => 4,
25
+ 's' => 4,
26
+ 'southwest' => 5,
27
+ 'sw' => 5,
28
+ 'west' => 6,
29
+ 'w' => 6,
30
+ 'northwest' => 7,
31
+ 'nw' => 7,
32
+ }
33
+
34
+ GO = {
35
+ 'd' => 2,
36
+ 'down' => 2,
37
+ 'up' => 1,
38
+ 'u' => 1,
39
+ 'i' => 3,
40
+ 'in' => 3,
41
+ 'o' => 4,
42
+ 'out' => 4,
43
+ }
44
+
45
+ attr_reader :map
46
+
47
+ #
48
+ # Main parsing loop. We basically parse the file twice to
49
+ # solve dependencies. Yes, this is inefficient, but the alternative
50
+ # was to build a full parser that understands forward dependencies.
51
+ #
52
+ def parse(file)
53
+ # We start map at 1, 1
54
+ @x, @y = [0, 0]
55
+ @room = @item = @task = nil
56
+ @map.page = 0
57
+ @last_page = 0
58
+ @ignore_first_section = true
59
+ @room_idx = 0
60
+ line_number = 0
61
+
62
+ while not file.eof?
63
+ @line = ''
64
+ while not file.eof? and @line !~ /;\s*(#.*)?$/
65
+ @line << file.readline()
66
+ @line.sub!( /^\s*#.*/, '')
67
+ line_number += 1
68
+ end
69
+ @line.sub!( /;\s*#.*$/, '')
70
+ @line.sub! /^\s+/, ''
71
+ @line.gsub! /;\s*$/, ''
72
+ @line.gsub! /\n/, ' '
73
+ next if @line == ''
74
+ full_line = @line.dup
75
+ # puts "#{line_number}:'#{@line}'"
76
+ begin
77
+ parse_line
78
+ rescue ParseError => e
79
+ $stderr.puts
80
+ $stderr.puts "#{e} for pass #{@resolve_tags}, at line #{line_number}:"
81
+ $stderr.puts ">>>> #{full_line};"
82
+ $stderr.puts
83
+ rescue => e
84
+ $stderr.puts
85
+ $stderr.puts "#{e} for pass #{@resolve_tags}, at line #{line_number}:"
86
+ $stderr.puts ">>>> #{full_line};"
87
+ $stderr.puts e.backtrace if not e.kind_of?(MapError)
88
+ end
89
+ end
90
+ end
91
+
92
+ #
93
+ # see if line is a variable line. if so, return true
94
+ #
95
+ def parse_variable
96
+ @line =~ /^\s*[\w_\-\.]+\s*=.*$/
97
+ end
98
+
99
+ #
100
+ # Get a quoted string from line
101
+ #
102
+ def get_string
103
+ str = nil
104
+ if @line =~ /^"(([^"\\]|\\")*)"/
105
+ str = $1
106
+ @line = @line[str.size+2..-1]
107
+ # Change any quoted " to normal "
108
+ str.gsub! /\\"/, '"'
109
+ end
110
+ return str
111
+ end
112
+
113
+ #
114
+ # Get a new token from line
115
+ #
116
+ def get_token
117
+ return nil if not @line
118
+ token, @line = @line.split(' ', 2)
119
+ return token
120
+ end
121
+
122
+ #
123
+ # Look-ahead a token, without modifying line
124
+ #
125
+ def next_token
126
+ return nil if not @line
127
+ token, = @line.split(' ', 2)
128
+ return token
129
+ end
130
+
131
+ #
132
+ # Return whether next token is a tag or a language keyword
133
+ #
134
+ def is_tag?
135
+ token = next_token
136
+ case token
137
+ when nil,
138
+ 'to', 'cmd', 'from', 'need', 'lose', 'lost', 'tag', 'all', 'except',
139
+ 'before', 'after', 'start', 'style', 'endstyle', 'follow', 'link',
140
+ 'until', 'dir', 'start', 'get', 'drop', 'goto', 'give', 'given',
141
+ 'exit', 'in', 'keep', 'any', 'safe', 'score', 'style', 'go',
142
+ 'hidden', 'oneway', 'finish', 'length', 'nopath', 'note', 'leave',
143
+ 'join', /^#/
144
+ return false
145
+ else
146
+ return true
147
+ end
148
+ end
149
+
150
+ #
151
+ # Get a tag
152
+ #
153
+ def get_tag
154
+ tag = get_token
155
+ if tag == 'it'
156
+ return [tag, (@item or @task or @room)]
157
+ elsif tag == 'last' or tag == 'all' or tag == 'any'
158
+ return [tag, nil]
159
+ else
160
+ if not @tags[tag]
161
+ if @resolve_tags
162
+ raise ParseError, "Tag <#{tag}> not known"
163
+ end
164
+ end
165
+ return [tag, @tags[tag]]
166
+ end
167
+ end
168
+
169
+ #
170
+ # Get a room
171
+ #
172
+ def get_room
173
+ tag, room = get_tag
174
+ return @room if tag == 'last'
175
+ if room and not room.kind_of?(Room)
176
+ raise ParseError, "Not a room tag <#{tag}:#{room}>"
177
+ end
178
+ return room
179
+ end
180
+
181
+ #
182
+ # Get an item
183
+ #
184
+ def get_item
185
+ tag, item = get_tag
186
+ return @item if tag == 'last'
187
+ if item and item.kind_of?(Room)
188
+ raise ParseError, "Not an item tag <#{tag}:#{item}>"
189
+ end
190
+ return item
191
+ end
192
+
193
+ #
194
+ # Get a task
195
+ #
196
+ def get_task
197
+ tag, task = get_tag
198
+ return @task if tag == 'last'
199
+ if task and task.kind_of?(Room)
200
+ raise ParseError, "Not an task tag <#{tag}:#{item}>"
201
+ end
202
+ return task
203
+ end
204
+
205
+ #
206
+ # Parse a line of file
207
+ #
208
+ def parse_line
209
+ return if parse_variable
210
+
211
+ roomname = tagname = nil
212
+ @task = @item = roomA = roomB =
213
+ from = one_way = nolink = go = nil
214
+ styles = []
215
+ links = []
216
+ dir = []
217
+
218
+
219
+ roomA = @room # add item to this room
220
+
221
+ while @line and token = get_token
222
+ case token
223
+ when 'map'
224
+ section = get_string
225
+ # Once we start a new section, we rest room and
226
+ # current position.
227
+ @room = nil
228
+ @x = @y = 1
229
+ # dont' add a page for first 'map' keyword
230
+ if @ignore_first_section
231
+ @ignore_first_section = false
232
+ @map.pages[0].name = section
233
+ next
234
+ end
235
+ if @resolve_tags
236
+ @map.page = @last_page + 1
237
+ @last_page = @map.page
238
+ else
239
+ @map.new_page
240
+ @map.pages[-1].name = section
241
+ end
242
+ when 'title'
243
+ @map.name = get_string
244
+ when 'room'
245
+ roomname = get_string
246
+ @ignore_first_section = false
247
+ when 'tag'
248
+ tagname = get_token
249
+ when 'dir'
250
+ # if not roomname
251
+ # raise ParseError, 'dir directive found but not for a room'
252
+ # end
253
+ token = next_token
254
+ while DIRECTIONS[token]
255
+ get_token
256
+ dir.push(DIRECTIONS[token])
257
+ token = next_token
258
+
259
+ if token =~ /^\d+$/
260
+ get_token
261
+ (token.to_i - 1).times { dir.push(dir[-1]) }
262
+ token = next_token
263
+ end
264
+ end
265
+ when 'nolink'
266
+ if dir.empty?
267
+ raise ParseError, 'nolink directive, but no dir directive before.'
268
+ end
269
+ nolink = true
270
+ when 'oneway'
271
+ one_way = true
272
+ when 'nopath'
273
+ when 'safe'
274
+ when 'exit'
275
+ if not roomname
276
+ raise ParseError, 'exit directive found but not for a room'
277
+ end
278
+ token = next_token
279
+ while DIRECTIONS[token]
280
+ get_token
281
+ token = next_token
282
+ end
283
+ when 'from'
284
+ if dir.empty?
285
+ raise ParseError, "'from' token found but no dir specified before"
286
+ end
287
+ from = get_room
288
+ when 'join'
289
+ # Joins are links that are not drawn.
290
+ # Mainly to give the engine knowledge that two locations
291
+ # are interconnected
292
+ get_room while is_tag?
293
+ to = next_token
294
+ if to == 'to'
295
+ get_token
296
+ get_room
297
+ end
298
+ when 'all'
299
+ when 'lost'
300
+ when 'except'
301
+ get_item while is_tag?
302
+ when 'length'
303
+ get_token
304
+ when 'until'
305
+ get_task while is_tag?
306
+ when 'link'
307
+ if roomname
308
+ while is_tag?
309
+ links.push( get_room )
310
+ end
311
+ else
312
+ roomA = get_room
313
+ to = next_token
314
+ if to == 'to'
315
+ get_token
316
+ roomB = get_room
317
+ end
318
+ end
319
+ when 'goto'
320
+ get_room
321
+ when 'go'
322
+ token = get_token
323
+ go = GO[token]
324
+ if not token
325
+ raise ParseError, "Token <#{token}> is an unknown go direction."
326
+ end
327
+ when 'item'
328
+ @item = get_string
329
+ item_tag = get_token if not @item
330
+ when 'in'
331
+ roomA = get_room # oh, well... this room
332
+ when 'note'
333
+ note = get_string
334
+ when 'keep'
335
+ token = next_token
336
+ if token == 'with'
337
+ get_token
338
+ item_keep = get_item
339
+ end
340
+ when 'given'
341
+ when 'give'
342
+ give_items = [ get_item ]
343
+ give_items.push(get_item) while is_tag?
344
+ when 'start'
345
+ when 'finish'
346
+ when 'follow'
347
+ task = get_task
348
+ when 'need'
349
+ need_items = [ get_item ]
350
+ need_items.push(get_item) while is_tag?
351
+ when 'after'
352
+ after_tasks = [ get_item ]
353
+ after_tasks.push(get_item) while is_tag?
354
+ when 'lose'
355
+ loose_items = [ get_item ]
356
+ loose_items.push(get_item) while is_tag?
357
+ when 'get'
358
+ get_items = [ get_item ]
359
+ get_items.push(get_item) while is_tag?
360
+ when 'drop'
361
+ drop_items = [ get_item ]
362
+ drop_items.push(get_item) while is_tag?
363
+ when 'hidden'
364
+ when 'leave'
365
+ leave_items = [ get_item ]
366
+ leave_items.push(get_item) while is_tag?
367
+ when 'before'
368
+ task = get_task
369
+ when 'cmd'
370
+ token = next_token
371
+ if token == 'to'
372
+ get_token
373
+ cmd = get_string
374
+ elsif token == 'from'
375
+ get_token
376
+ cmd = get_string
377
+ elsif token == 'none'
378
+ get_token
379
+ else
380
+ cmd = get_string
381
+ num = next_token
382
+ get_token if num =~ /\d+/
383
+ end
384
+ when 'score'
385
+ score = get_token.to_i
386
+ when 'length'
387
+ length = get_token.to_i
388
+ when 'task'
389
+ @task = get_string
390
+ task_tag = get_token if not @task
391
+ when 'style'
392
+ styles.push(get_token) while is_tag?
393
+ when 'endstyle'
394
+ get_token while is_tag?
395
+ when '', /^#/
396
+ get_token while @line
397
+ else
398
+ raise ParseError, "Token <#{token.inspect}> not understood"
399
+ end
400
+ end
401
+
402
+ # If a direction, move that way.
403
+ if dir.size > 0
404
+ # If from keyword present, move from that room on.
405
+ if from
406
+ roomA = from
407
+ # 'from' can also connect stuff not in current page...
408
+ # so we check we are in the right page.
409
+ @map.pages.each_with_index { |p, idx|
410
+ if p.rooms.include?(roomA)
411
+ @map.page = idx
412
+ break
413
+ end
414
+ }
415
+ end
416
+ # Okay, we start from roomA... and follow each dir
417
+ if roomA
418
+ @x = roomA.x
419
+ @y = roomA.y
420
+ end
421
+ dir.each { |d|
422
+ x, y = Room::DIR_TO_VECTOR[d]
423
+ @x += x
424
+ @y += y
425
+ }
426
+ end
427
+
428
+ # Create new room
429
+ if roomname
430
+ if @resolve_tags
431
+ # Room is already created. Find it.
432
+ roomB = @rooms[@room_idx]
433
+ @room_idx += 1
434
+ else
435
+ # Verify there's no room in that location yet
436
+ page = @map.pages[@map.page]
437
+ page.rooms.each { |r|
438
+ if r.x == @x and r.y == @y
439
+ err = "Page #{@map.page+1} already has location #{r} at #{@x}, #{@y}.\n"
440
+ err << "Cannot create '#{roomname}'"
441
+ raise MapError, err
442
+ end
443
+ }
444
+
445
+ # Remember original room for connection
446
+ roomB = @map.new_room( @x, @y )
447
+ roomB.name = roomname
448
+ @rooms.push( roomB )
449
+ end
450
+
451
+ # Make roomB the current room
452
+ @room = roomB
453
+ end
454
+
455
+ if @item and roomA and @resolve_tags
456
+ roomA.objects << @item + "\n"
457
+ end
458
+
459
+ if @task and @room and @resolve_tags
460
+ @room.tasks << @task + "\n"
461
+ end
462
+
463
+ # Add a link between rooms
464
+ if roomA and roomB and not nolink and @resolve_tags
465
+ # Establish new simple connection
466
+ dirB = (dir[-1] + 4) % 8 if dir.size > 1
467
+
468
+ if dir.size > 1
469
+ dirB = [ @x - roomB.x, @y - roomB.y ]
470
+ if dirB[0] == 0 and dirB[1] == 0
471
+ dirB = (dir[-1] + 4) % 8
472
+ else
473
+ dirB = roomB.vector_to_dir( dirB[0], dirB[1] )
474
+ end
475
+ end
476
+
477
+ # 'from' and 'link' keywords can also connect stuff not in
478
+ # current page... so we check for that here
479
+ @map.pages.each_with_index { |p, idx|
480
+ if p.rooms.include?(roomA)
481
+ @map.page = idx
482
+ break
483
+ end
484
+ }
485
+ if not @map.pages[@map.page].rooms.include?(roomB)
486
+ raise MapError, "Linking #{roomA} and #{roomB} which are in different pages"
487
+ end
488
+
489
+ begin
490
+ c = map.new_connection( roomA, dir[0], roomB, dirB )
491
+ c.dir = Connection::AtoB if one_way
492
+ c.type = Connection::SPECIAL if styles.include?('special')
493
+
494
+ if go
495
+ c.exitAtext = go
496
+ if go % 2 == 0
497
+ c.exitBtext = go - 1
498
+ else
499
+ c.exitBtext = go + 1
500
+ end
501
+ end
502
+ rescue => e
503
+ puts e
504
+ end
505
+ end
506
+
507
+ if links.size > 0 and @resolve_tags
508
+ # Additional diagonal connection for room
509
+ links.each { |x|
510
+ next if not x
511
+ begin
512
+ c = map.new_connection( @room, nil, x, nil )
513
+ rescue => e
514
+ puts e
515
+ end
516
+ }
517
+ end
518
+
519
+ if tagname # and not @resolve_tags
520
+ if roomname
521
+ @tags[tagname] = @room
522
+ elsif @item
523
+ @tags[tagname] = @item
524
+ elsif @task
525
+ @tags[tagname] = @task
526
+ else
527
+ # join/link tag
528
+ @tags[tagname] = ''
529
+ end
530
+ end
531
+ end
532
+
533
+ #
534
+ # Okay, check all min/max locations in all pages
535
+ # and then do the following:
536
+ # a) Adjust map's width/height
537
+ # b) Shift all rooms so that no rooms are in negative locations
538
+ #
539
+ def adjust_map
540
+ # First, adjust map's width and height
541
+ @map.width = @map.height = 1
542
+ minXY = []
543
+ maxXY = []
544
+ @map.pages.each { |page|
545
+ next if page.rooms.empty?
546
+
547
+ sizes = page.min_max_rooms
548
+ minXY.push sizes[0]
549
+ maxXY.push sizes[1]
550
+
551
+ w = maxXY[-1][0] - minXY[-1][0]
552
+ h = maxXY[-1][1] - minXY[-1][1]
553
+
554
+ # We store +3 to allow for complex connections if needed.
555
+ @map.width = w + 3 if w >= @map.width - 2
556
+ @map.height = h + 3 if h >= @map.height - 2
557
+ }
558
+
559
+
560
+ # Okay, minXY[]/maxXY[] contains all the minXY/maxXY positions of
561
+ # each page. With that info and @map.weight/height, we can
562
+ # start shifting all nodes in the page so that they will fit.
563
+ idx = 0
564
+ @map.pages.each { |p|
565
+ next if p.rooms.size == 0
566
+ x = y = 0
567
+ x = 1 - minXY[idx][0] if minXY[idx][0] < 1
568
+ y = 1 - minXY[idx][1] if minXY[idx][1] < 1
569
+ x = @map.width - maxXY[idx][0] - 1 if maxXY[idx][0] >= @map.width - 1
570
+ y = @map.height - maxXY[idx][1] - 1 if maxXY[idx][1] >= @map.height - 1
571
+ idx += 1
572
+ next if x == 0 and y == 0 # nothing to shift
573
+ p.rooms.each { |r|
574
+ r.x += x
575
+ r.y += y
576
+ }
577
+ }
578
+
579
+ @map.create_pathmap if @map.kind_of?(FXMap)
580
+ end
581
+
582
+ def initialize(file, map = Map.new('IMF Imported Map'))
583
+ @tags = {}
584
+ @map = map
585
+ @rooms = []
586
+ @resolve_tags = false
587
+ # puts '--------------- first pass'
588
+ File.open(file) { |f|
589
+ parse(f)
590
+ }
591
+ # puts '--------------- second pass'
592
+ @resolve_tags = true
593
+ File.open(file) { |f|
594
+ parse(f)
595
+ }
596
+ # puts '--------------- second pass done'
597
+ @map.page = 0
598
+ @map.filename = file.sub(/\.ifm$/i, '.map')
599
+ @map.navigation = true
600
+ adjust_map
601
+ @tags = {} # save some memory by clearing the tag list
602
+ @rooms = nil # and room list
603
+ end
604
+ end
605
+
606
+
607
+ if $0 == __FILE__
608
+ p "Opening file '#{ARGV[0]}'"
609
+ $LOAD_PATH << '..'
610
+ require "IFMapper/Map"
611
+
612
+ IFMReader.new(ARGV[0])
613
+ end