ifmapper 0.9 → 0.9.5

Sign up to get free protection for your applications and to get access to all the features.
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
  #