ifmapper 0.9 → 0.9.5

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.
Files changed (49) hide show
  1. data/HISTORY.txt +132 -5
  2. data/IFMapper.gemspec +2 -2
  3. data/TODO.txt +0 -5
  4. data/lib/IFMapper/AStar.rb +15 -8
  5. data/lib/IFMapper/Connection.rb +137 -25
  6. data/lib/IFMapper/FXConnection.rb +54 -40
  7. data/lib/IFMapper/FXConnectionDialogBox.rb +12 -4
  8. data/lib/IFMapper/FXMap.rb +161 -60
  9. data/lib/IFMapper/FXMapperWindow.rb +205 -70
  10. data/lib/IFMapper/FXRoom.rb +7 -2
  11. data/lib/IFMapper/FXRoomDialogBox.rb +2 -1
  12. data/lib/IFMapper/FXRoomList.rb +95 -0
  13. data/lib/IFMapper/PDFMapExporter.rb +10 -1
  14. data/lib/IFMapper/Room.rb +22 -0
  15. data/lib/IFMapper/TranscriptDialogBox.rb +117 -0
  16. data/lib/IFMapper/TranscriptReader.rb +320 -96
  17. data/maps/AllRoads.map +0 -0
  18. data/maps/Bureaucracy.map +0 -0
  19. data/maps/CityOfSecrets.map +0 -0
  20. data/maps/DDIV.map +0 -0
  21. data/maps/Heroine.map +0 -0
  22. data/maps/SavoirFare.map +0 -0
  23. data/maps/Tangle.map +0 -0
  24. data/maps/anchor.map +0 -0
  25. data/maps/ballerina.map +0 -0
  26. data/maps/bluechairs.map +0 -0
  27. data/maps/break_in.map +0 -0
  28. data/maps/christminster.map +0 -0
  29. data/maps/deadline.map +0 -0
  30. data/maps/delusions.map +0 -0
  31. data/maps/dreamhold.map +0 -0
  32. data/maps/eas.map +0 -0
  33. data/maps/eas2.map +0 -0
  34. data/maps/eas3.map +0 -0
  35. data/maps/inhumane.map +0 -0
  36. data/maps/lurkinghorror.map +0 -0
  37. data/maps/metamorphoses.map +0 -0
  38. data/maps/moonmist.map +0 -0
  39. data/maps/muldoon_legacy.map +0 -0
  40. data/maps/pawn.map +0 -0
  41. data/maps/pytho.map +0 -0
  42. data/maps/risorgimento.map +0 -0
  43. data/maps/sherbet.map +0 -0
  44. data/maps/slouch.map +0 -0
  45. data/maps/spring.map +0 -0
  46. data/maps/trinity.map +0 -0
  47. data/maps/worlds.map +0 -0
  48. data/maps/zdungeon.map +0 -0
  49. metadata +42 -8
@@ -6,10 +6,7 @@ begin
6
6
  rescue LoadError
7
7
  end
8
8
 
9
- begin
10
- require 'fox12'
11
- require 'fox12/colors'
12
- rescue LoadError => e
9
+ def no_fox
13
10
  $stderr.puts "Please install the FXRuby (FOX) library v1.2 or later."
14
11
  if $rubygems
15
12
  $stderr.puts "You can usually do so if you do 'gem install fxruby'."
@@ -17,6 +14,28 @@ rescue LoadError => e
17
14
  exit(1)
18
15
  end
19
16
 
17
+ def get_fox
18
+ ##### ARRRGH!!!! Why does Lyle keep changing the fxruby name on each
19
+ ##### release!
20
+ foxes = [ 'fox14', 'fox12', 'fox' ]
21
+ foxes.each { |fox|
22
+ begin
23
+ require "#{fox}"
24
+ require "#{fox}/colors"
25
+ break
26
+ rescue LoadError
27
+ no_fox if fox == foxes[-1]
28
+ end
29
+ }
30
+
31
+ # verify fxruby version
32
+ ver, rev, = Fox::fxrubyversion().split('.')
33
+ no_fox if ver.to_i < 1 or rev.to_i < 2
34
+ end
35
+
36
+ get_fox
37
+ include Fox
38
+
20
39
  begin
21
40
  require 'yaml'
22
41
  rescue LoadError => e
@@ -25,7 +44,6 @@ rescue LoadError => e
25
44
  end
26
45
 
27
46
 
28
- include Fox
29
47
 
30
48
  require 'IFMapper/FXMap'
31
49
  require 'IFMapper/FXMapperSettings'
@@ -34,13 +52,14 @@ require 'IFMapper/FXAboutDialogBox'
34
52
  require 'IFMapper/FXSearchDialogBox'
35
53
  require 'IFMapper/FXMapFileDialog'
36
54
  require 'IFMapper/FXMapColorBox'
55
+ require 'IFMapper/FXRoomList'
37
56
 
38
57
 
39
58
 
40
59
  class FXMapperWindow < FXMainWindow
41
60
 
42
61
  PROGRAM_NAME = "Interactive Fiction Mapper"
43
- VERSION = '0.9'
62
+ VERSION = '0.9.5'
44
63
  AUTHOR = "Gonzalo Garramuno"
45
64
  TITLE = "#{PROGRAM_NAME} v#{VERSION} - Written by #{AUTHOR}"
46
65
 
@@ -62,28 +81,52 @@ class FXMapperWindow < FXMainWindow
62
81
 
63
82
  def open_ifm(file, map)
64
83
  require 'IFMapper/IFMReader'
65
- IFMReader.new(file, map)
84
+ begin
85
+ IFMReader.new(file, map)
86
+ rescue => e
87
+ return e
88
+ end
66
89
  return map
67
90
  end
68
91
 
69
92
  def open_tads(file, map)
70
93
  require 'IFMapper/TADSReader'
71
- TADSReader.new(file, map)
94
+ begin
95
+ TADSReader.new(file, map)
96
+ rescue => e
97
+ return e
98
+ end
72
99
  return map
73
100
  end
74
101
 
75
102
  def open_inform(file, map)
76
103
  require 'IFMapper/InformReader'
77
- InformReader.new(file, map)
104
+ begin
105
+ InformReader.new(file, map)
106
+ rescue => e
107
+ return e
108
+ end
78
109
  return map
79
110
  end
80
111
 
112
+ #
113
+ # Start automapping from a transcript
114
+ #
81
115
  def start_automap_cb(sender, sel, ptr)
82
116
  map = current_map
83
117
  return if not map
84
118
  map.start_automap
85
119
  end
86
120
 
121
+ def automap_properties_cb(sender, sel, ptr)
122
+ map = current_map
123
+ return if not map or not map.automap
124
+ map.automap.properties(true)
125
+ end
126
+
127
+ #
128
+ # Stop automapping from a transcript
129
+ #
87
130
  def stop_automap_cb(sender, sel, ptr)
88
131
  map = current_map
89
132
  return if not map
@@ -136,10 +179,12 @@ class FXMapperWindow < FXMainWindow
136
179
  end
137
180
 
138
181
  if not tmp.kind_of?(Map) and not tmp.kind_of?(FXMap)
139
- status "Could not load '#{file}'. Error: #{tmp}."
140
- if map.close_cb
141
- @maps.delete(map) if make_new_map
142
- GC.start
182
+ status "Could not load '#{file}'. Error: #{tmp}."
183
+ if make_new_map
184
+ if map.close_cb
185
+ @maps.delete(map)
186
+ GC.start
187
+ end
143
188
  end
144
189
  sleep 2
145
190
  return
@@ -151,7 +196,7 @@ class FXMapperWindow < FXMainWindow
151
196
  map.fit
152
197
  map.window.create
153
198
  map.modified = false
154
- update_section
199
+ update_map
155
200
  status "Loaded '#{file}'."
156
201
  end
157
202
 
@@ -186,25 +231,32 @@ class FXMapperWindow < FXMainWindow
186
231
  m.window.create
187
232
  end
188
233
 
234
+ #
235
+ # Create a new map
236
+ #
189
237
  def new_map
190
238
  mapname = "Empty Map \##{@maps.size+1}"
191
239
  @maps.push( FXMap.new(mapname, @mdiclient, @@default_options.dup,
192
240
  @mdiicon, @mdimenu, MDI_NORMAL, 0, 0, 790, 500) )
193
- @maps[-1].window.connect(SEL_PAINT) { |sender, sel, event|
194
- map = current_map
241
+ map = @maps[-1]
242
+ map.window.connect(SEL_PAINT) {
195
243
  map.draw
196
244
  }
197
- @maps[-1].window.connect(SEL_CLOSE) { |sender, sel, event|
198
- map = current_map
245
+ map.window.connect(SEL_CLOSE) {
199
246
  if map.close_cb
200
247
  @maps.delete(map)
201
- GC.start
248
+ end
249
+ if @maps[-1]
250
+ @maps[-1].update_roomlist
251
+ else
252
+ FXMap::no_maps
202
253
  end
203
254
  }
204
255
 
205
256
  # Make it active
206
- @mdiclient.setActiveChild(@maps[-1].window)
207
- return @maps[-1]
257
+ @mdiclient.setActiveChild(map.window)
258
+ map.update_roomlist
259
+ return map
208
260
  end
209
261
 
210
262
  # Load the named PNG icon from a file
@@ -221,12 +273,18 @@ class FXMapperWindow < FXMainWindow
221
273
  end
222
274
  end
223
275
 
276
+ #
277
+ # Start a complex connection
278
+ #
224
279
  def complex_connection_cb(sender, sel, msg)
225
280
  map = current_map
226
281
  return unless map
227
282
  map.complex_connection
228
283
  end
229
284
 
285
+ #
286
+ # Delete selected elements in map
287
+ #
230
288
  def delete_selected_cb(sender, sel, msg)
231
289
  map = current_map
232
290
  return unless map
@@ -244,11 +302,15 @@ class FXMapperWindow < FXMainWindow
244
302
  return false
245
303
  end
246
304
 
305
+ #
306
+ # Print out all the locations in map
307
+ #
247
308
  def print_locations_cb(sender, sel, msg)
248
309
  map = current_map
249
310
  return unless map
250
311
 
251
- w = FXWarningBox.new( self, "Sorry, but printing is not yet implemented\n" +
312
+ w = FXWarningBox.new( self,
313
+ "Sorry, but printing is not yet implemented\n" +
252
314
  "in this version.")
253
315
  w.execute
254
316
  return
@@ -257,41 +319,38 @@ class FXMapperWindow < FXMainWindow
257
319
  map.print_locations( printer ) if printer
258
320
  end
259
321
 
322
+ #
323
+ # Export current map as an IFM file
324
+ #
260
325
  def ifm_export_cb(sender, sel, msg)
261
326
  map = current_map
262
327
  return unless map
263
328
 
264
- require 'IFMapper/IFMWriter'
265
-
266
329
  d = FXMapFileDialog.new(self, "Save Map as IFM File",
267
330
  [
268
331
  "IFM Map (*.ifm)"
269
332
  ])
270
- if d.filename != ''
271
- file = d.filename
272
- file += '.ifm' if file !~ /\.ifm$/
273
- IFMWriter.new(map, file)
274
- end
333
+ map.export_ifm(d.filename) if d.filename != ''
275
334
  end
276
335
 
336
+ #
337
+ # Export current map as an Inform source file
338
+ #
277
339
  def inform_export_cb(sender, sel, msg)
278
340
  map = current_map
279
341
  return unless map
280
342
 
281
- require 'IFMapper/InformWriter'
282
-
283
343
  d = FXMapFileDialog.new(self, "Save Map as Inform Files",
284
344
  [
285
345
  "Inform Source Code (*.inf)"
286
346
  ])
287
- if d.filename != ''
288
- file = d.filename
289
- file.sub!(/(-\d+)?\.inf/, '')
290
- InformWriter.new(map, file)
291
- end
347
+ map.export_inform( d.filename ) if d.filename != ''
292
348
  end
293
349
 
294
350
 
351
+ #
352
+ # Export current map as a TADs source file
353
+ #
295
354
  def tads_export_cb(sender, sel, msg)
296
355
  map = current_map
297
356
  return unless map
@@ -302,14 +361,13 @@ class FXMapperWindow < FXMainWindow
302
361
  [
303
362
  "TADS Source Code (*.t)"
304
363
  ])
305
- if d.filename != ''
306
- file = d.filename
307
- file.sub!(/(-\d+)?\.t/, '')
308
- TADSWriter.new(map, file)
309
- end
364
+ map.export_tads( d.filename ) if d.filename != ''
310
365
  end
311
366
 
312
367
 
368
+ #
369
+ # Export current map as Acrobat PDF
370
+ #
313
371
  def pdf_export_cb(sender, sel, msg)
314
372
  map = current_map
315
373
  return unless map
@@ -322,8 +380,6 @@ class FXMapperWindow < FXMainWindow
322
380
  return
323
381
  end
324
382
 
325
-
326
-
327
383
  d = FXMapFileDialog.new(self, "Save Acrobat PDF File",
328
384
  [
329
385
  "Acrobat PDF (*.pdf)"
@@ -333,11 +389,15 @@ class FXMapperWindow < FXMainWindow
333
389
  end
334
390
  end
335
391
 
392
+ #
393
+ # Print current map graphically
394
+ #
336
395
  def print_cb(sender, sel, msg)
337
396
  map = current_map
338
397
  return unless map
339
398
 
340
- w = FXWarningBox.new( self, "Sorry, but printing is not yet implemented\n" +
399
+ w = FXWarningBox.new( self,
400
+ "Sorry, but printing is not yet implemented\n" +
341
401
  "in this version.")
342
402
  w.execute
343
403
  return
@@ -347,11 +407,24 @@ class FXMapperWindow < FXMainWindow
347
407
  map.print( printer ) if printer
348
408
  end
349
409
 
410
+ def update_map
411
+ map = current_map
412
+ return unless map
413
+ map.update_roomlist
414
+ update_section
415
+ end
416
+
417
+ #
418
+ # Creates the MDI (multi-document) client
419
+ #
350
420
  def create_mdiclient
351
421
  # MDI Client
352
422
  @maps = []
353
423
 
354
424
  @mdiclient = FXMDIClient.new(self, LAYOUT_FILL_X|LAYOUT_FILL_Y)
425
+ @mdiclient.connect(SEL_CHANGED) {
426
+ update_map
427
+ }
355
428
 
356
429
  # MDI buttons in menu:- note the message ID's!!!!!
357
430
  # Normally, MDI commands are simply sensitized or desensitized;
@@ -373,10 +446,16 @@ class FXMapperWindow < FXMainWindow
373
446
  @mdimenu = FXMDIMenu.new(self, @mdiclient)
374
447
  end
375
448
 
449
+ #
450
+ # Return the copied elements
451
+ #
376
452
  def self.copy_buffer
377
453
  return @@copy_buffer
378
454
  end
379
455
 
456
+ #
457
+ # Copy selected elements
458
+ #
380
459
  def self.copy_selected(map)
381
460
  sect = map.sections[map.section]
382
461
 
@@ -391,7 +470,10 @@ class FXMapperWindow < FXMainWindow
391
470
  # those rooms we selected
392
471
  delete = []
393
472
  links.each { |c|
394
- delete << c if not rooms.include?(c.roomA) or not rooms.include?(c.roomB)
473
+ if not rooms.include?(c.roomA) or
474
+ (c.roomB and not rooms.include?(c.roomB))
475
+ delete << c
476
+ end
395
477
  }
396
478
  links -= delete
397
479
 
@@ -400,11 +482,17 @@ class FXMapperWindow < FXMainWindow
400
482
  @@copy_buffer = selection
401
483
  end
402
484
 
485
+ #
486
+ # Cut selected elements
487
+ #
403
488
  def self.cut_selected(map)
404
489
  FXMapperWindow::copy_selected(map)
405
490
  map.cut_selected
406
491
  end
407
492
 
493
+ #
494
+ # Paste selected elements
495
+ #
408
496
  def self.paste_selected(map)
409
497
  return if not @@copy_buffer
410
498
  return map.navigation_warning if map.navigation
@@ -431,33 +519,38 @@ class FXMapperWindow < FXMainWindow
431
519
  if rooms.empty?
432
520
  # Add connections only (no rooms copied)
433
521
  sel[1].each { |c|
434
- exitA = c.roomA.exits.index(c)
435
- exitB = c.roomB.exits.rindex(c)
522
+ exitA, exitB = c.dirs
436
523
  roomA = c.roomA
437
524
  roomB = c.roomB
438
525
  sect = map.sections[map.section]
439
- next unless sect.rooms.include?(roomA) and sect.rooms.include?(roomB)
526
+ if not sect.rooms.include?(roomA) or
527
+ (roomB and not sect.rooms.include?(roomB))
528
+ next
529
+ end
440
530
  begin
441
- c = map.new_connection(roomA, exitA, roomB, exitB)
442
- c.selected = true
531
+ nc = map.new_connection(roomA, exitA, roomB, exitB)
532
+ nc.selected = true
533
+ nc.dir = c.dir
534
+ nc.type = c.type
443
535
  rescue
444
536
  end
445
537
  }
446
538
  else
447
539
  # Add connections
448
540
  sel[1].each { |c|
449
- exitA = c.roomA.exits.index(c)
450
- exitB = c.roomB.exits.rindex(c)
541
+ exitA, exitB = c.dirs
451
542
  roomA = r_to_nr[c.roomA]
452
- roomB = r_to_nr[c.roomB]
453
- next if not roomA or not roomB
454
- if not exitA
455
- puts "WHOA! #{c} #{roomA} #{exitA} is empty"
456
- next
543
+ if c.roomB
544
+ roomB = r_to_nr[c.roomB]
545
+ else
546
+ roomB = nil
457
547
  end
548
+ next if not roomA
458
549
  begin
459
- c = map.new_connection(roomA, exitA, roomB, exitB)
460
- c.selected = true
550
+ nc = map.new_connection(roomA, exitA, roomB, exitB)
551
+ nc.selected = true
552
+ nc.dir = c.dir
553
+ nc.type = c.type
461
554
  rescue Section::ConnectionError => e
462
555
  puts c
463
556
  puts e
@@ -489,6 +582,9 @@ class FXMapperWindow < FXMainWindow
489
582
  end
490
583
 
491
584
 
585
+ #
586
+ # Hilite matches after a search
587
+ #
492
588
  def hilite_matches(map, matches, re, idx = 0 )
493
589
  if matches.size == 0
494
590
  status "No matches for regex '#{re}'."
@@ -518,6 +614,9 @@ class FXMapperWindow < FXMainWindow
518
614
  map.draw
519
615
  end
520
616
 
617
+ #
618
+ # Find location in map
619
+ #
521
620
  def find_in_map(s, m, e)
522
621
  map = current_map
523
622
  return unless map
@@ -549,6 +648,9 @@ class FXMapperWindow < FXMainWindow
549
648
  @search.show
550
649
  end
551
650
 
651
+ #
652
+ # Find location in section
653
+ #
552
654
  def find_in_section(s, m, e)
553
655
  map = current_map
554
656
  return unless map
@@ -562,6 +664,9 @@ class FXMapperWindow < FXMainWindow
562
664
  hilite_matches(map, matches, re)
563
665
  end
564
666
 
667
+ #
668
+ # Callback
669
+ #
565
670
  def find_in_section_cb(s, m, e)
566
671
  map = current_map
567
672
  return unless map
@@ -576,6 +681,9 @@ class FXMapperWindow < FXMainWindow
576
681
  @search.show
577
682
  end
578
683
 
684
+ #
685
+ # Find object in map
686
+ #
579
687
  def find_object_in_map(s, m, e)
580
688
  map = current_map
581
689
  return unless map
@@ -593,6 +701,9 @@ class FXMapperWindow < FXMainWindow
593
701
  hilite_matches(map, matches, re, @search.index)
594
702
  end
595
703
 
704
+ #
705
+ # Find task in map
706
+ #
596
707
  def find_task_in_map(s, m, e)
597
708
  map = current_map
598
709
  return unless map
@@ -610,6 +721,9 @@ class FXMapperWindow < FXMainWindow
610
721
  hilite_matches(map, matches, re, @search.index)
611
722
  end
612
723
 
724
+ #
725
+ # Find object in map
726
+ #
613
727
  def find_object_in_map_cb(s, m, e)
614
728
  map = current_map
615
729
  return unless map
@@ -624,6 +738,9 @@ class FXMapperWindow < FXMainWindow
624
738
  @search.show
625
739
  end
626
740
 
741
+ #
742
+ # Find task in map
743
+ #
627
744
  def find_task_in_map_cb(s, m, e)
628
745
  map = current_map
629
746
  return unless map
@@ -638,6 +755,9 @@ class FXMapperWindow < FXMainWindow
638
755
  @search.show
639
756
  end
640
757
 
758
+ #
759
+ # Pop-up color preferences
760
+ #
641
761
  def colors_cb(sender, id, msg)
642
762
  map = current_map
643
763
  return if not map
@@ -650,20 +770,18 @@ class FXMapperWindow < FXMainWindow
650
770
  @colors.copy_from(map)
651
771
  end
652
772
 
653
-
773
+ #
774
+ # Unselect all
775
+ #
654
776
  def select_none_cb( sender, id, event )
655
777
  map = current_map
656
778
  return if not map
657
- sect = map.sections[map.section]
658
- sect.rooms.each { |r|
659
- r.selected = false
660
- }
661
- sect.connections.each { |c|
662
- c.selected = false
663
- }
779
+ map.clear_selection
664
780
  end
665
781
 
666
-
782
+ #
783
+ # Select all
784
+ #
667
785
  def select_all_cb( sender, id, event )
668
786
  map = current_map
669
787
  return if not map
@@ -676,11 +794,19 @@ class FXMapperWindow < FXMainWindow
676
794
  }
677
795
  end
678
796
 
797
+ def roomlist(sender, sel, event)
798
+ map = current_map
799
+ return unless map
800
+ map.show_roomlist
801
+ end
802
+
679
803
 
680
804
  def about_cb(sender, id, event )
681
805
  FXAboutDialogBox.new(self, "About This Software...", <<"EOF").execute
682
806
  #{TITLE} - #{VERSION}
683
807
 
808
+ FXRuby Version: #{Fox::fxrubyversion}
809
+
684
810
  A WYSIWYG mapping tool for interactive fiction.
685
811
  Written by Gonzalo Garramuno.
686
812
 
@@ -689,6 +815,7 @@ EOF
689
815
  end
690
816
 
691
817
 
818
+
692
819
  def create_menus
693
820
  # Construct these icons
694
821
  newdoc = load_icon("filenew")
@@ -786,9 +913,12 @@ EOF
786
913
  # Map menu
787
914
  mapmenu = FXMenuPane.new(self)
788
915
 
789
- cmd = FXMenuCommand.new(mapmenu, "Map Information\t\tChange Map Information.")
916
+ cmd = FXMenuCommand.new(mapmenu, "Map Information\t\tChange map information.")
790
917
  cmd.connect(SEL_COMMAND) { map_properties }
791
918
 
919
+ cmd = FXMenuCommand.new(mapmenu, "Room List\t\tList all rooms in map.")
920
+ cmd.connect(SEL_COMMAND, method(:roomlist) )
921
+
792
922
  # Automap submenu
793
923
  #
794
924
  submenu = FXMenuPane.new(self)
@@ -796,6 +926,9 @@ EOF
796
926
  cmd.connect(SEL_COMMAND, method(:start_automap_cb))
797
927
  cmd = FXMenuCommand.new(submenu, "S&top...\tCtl-P\tStop autocreating map.")
798
928
  cmd.connect(SEL_COMMAND, method(:stop_automap_cb))
929
+ FXMenuSeparator.new(submenu)
930
+ cmd = FXMenuCommand.new(submenu, "&Properties...\tCtl-H\tTranscript properties.")
931
+ cmd.connect(SEL_COMMAND, method(:automap_properties_cb))
799
932
  FXMenuCascade.new(mapmenu, "Automap", nil, submenu)
800
933
 
801
934
  # Sections submenu
@@ -833,6 +966,7 @@ EOF
833
966
  if map
834
967
  map.delete_section
835
968
  map.modified = true
969
+ update_section
836
970
  end
837
971
  }
838
972
  FXMenuCascade.new(mapmenu, "Sections", nil, submenu)
@@ -1233,7 +1367,8 @@ EOF
1233
1367
  #
1234
1368
  def update_section
1235
1369
  map = current_map
1236
- @section.text = (map.section + 1).to_s if map
1370
+ return unless map
1371
+ @section.text = (map.section + 1).to_s
1237
1372
  end
1238
1373
 
1239
1374
  #