ifmapper 0.8.5 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY.txt CHANGED
@@ -1,3 +1,113 @@
1
+ v0.9 - What's new:
2
+
3
+ - Fixed a very idiotic bug in Cut algorithm. It was basically destroying
4
+ all cut connections right after they were cut.
5
+
6
+ - Editing rooms' names or objects now results in a much, much
7
+ faster redraw of the map, even if map is very complex.
8
+
9
+ - You can now display and edit a location's description. This
10
+ description is often used by the the automapper during transcript
11
+ mapping to determine the uniqueness of a room, albeit the main
12
+ use of editing a room description is to allow creation of
13
+ new games (currently, Inform).
14
+
15
+ - You can now optionally avoid displaying the task or description
16
+ section on rooms.
17
+ This is mainly to allow you to read maps that have walkthru tasks
18
+ in them without giving away the game.
19
+
20
+ - IFMapper now supports the concept of closed but unlocked doors.
21
+ These will be displayed as a white square between connections.
22
+ The main interest of placing these doors is for creation
23
+ of new Inform games, not so much for solving games.
24
+
25
+ - You can now select exit stubs. Useful for deleting them when
26
+ automapper detects them incorrectly.
27
+
28
+ - Saving and Loading a map will now verify the integrity of the map
29
+ is correct. This prevets autosave from saving a corrupt map if
30
+ there is a bug in the program.
31
+
32
+ - CTRL-Selection is a toggle selection. That is, if you click once
33
+ on something already selected, it is deselected.
34
+
35
+ - Automapper improvements
36
+ * Automapper will now deal with complex commands involving 'then'
37
+ a tad better.
38
+ * An additional fix to dark rooms. Looking in a dark room would
39
+ result in the dark room being created, even if the room had
40
+ already been tagged as dark. Fixed.
41
+ * When choosing an up/down/in/out/random move direction, a new
42
+ connection will not be generated if there is already a similar
43
+ n/s/e/w/etc. link between the two rooms. This helps to simplify
44
+ some maps.
45
+ * Automapper now will try to automatically remove stub exits that
46
+ are not really exits if the user tries to move in that direction
47
+ and fails.
48
+ * Automapping was incorrectly reporting a maze when a new exit
49
+ would point in the same direction as a visited up/down or in/out
50
+ exit.
51
+ * Room descriptions are now compared (and stored) without \n at the
52
+ end of each line. This helps with comparing descriptions with any
53
+ description the user typed manually.
54
+ * Automapper will no longer crash if a link is attempted between
55
+ two rooms in different sections, nor will it corrupt your map as
56
+ it did before.
57
+ * Automapper is now a tad smarter in not considering words such
58
+ as open/close in descriptions. This helps the automapper not
59
+ get confused if a door which is part of the room description
60
+ changes state.
61
+
62
+ - Inform support.
63
+
64
+ * You can now read an inform file and automap it, with
65
+ some limitations.
66
+ The main limitation deals with how doors are handled, as Inform
67
+ offers an almost infinite number of ways of dealing with doors,
68
+ including the use of at least one of 4 popular libraries. Some
69
+ code will actually resolve issues of where doors lead to within,
70
+ for example, the "Enter" or "Exit" command, making parsing .inf
71
+ files impossible without writing a full inform compiler.
72
+ A second limitation is that Inform allows doors that exist only
73
+ on one "side" of a room. These exits will be ignored.
74
+ The inform automapper should prove compatible with most door
75
+ libraries, but will probably choke on a lot of hand-coded code.
76
+
77
+ * You can now export a map as an Inform .inf (source code) file.
78
+ This can be helpful at the beginning of designing a game to place
79
+ all objects/characters, doors and room descriptions easily.
80
+ Obviously, these source code files will later get hacked heavily
81
+ manually to provide the real game functionality.
82
+ IFMapper just gives you a start, helping you design
83
+ the landscape a tad more easily.
84
+ Doors are created in Inform using Andrew MacKinnon's easydoors
85
+ Inform extension.
86
+
87
+ NOTE: A word of warning about reading Inform or TADS maps.
88
+ Since these file formats have no concept of 'sections', they
89
+ can lead to some **HUGE** maps. The original Adventure, for
90
+ example, results in a 100x100 map. These huge maps may run
91
+ into memory issues and currently make the path-finding algorithm
92
+ work extremely slow.
93
+
94
+ - TADS 3 support
95
+
96
+ * NOTE: TADS 2 is currently *NOT* supported.
97
+
98
+ * You can now read a TADS 3 file and automap it, with some
99
+ limitations.
100
+ Basically, TADS 3 templates are currently *NOT* supported.
101
+ Instead, the standard library templates are kind of hard-coded
102
+ in the reader.
103
+ TADS 3's standard lib is more sensible than Inform when it
104
+ comes to doors, so it should hopefully lead to less
105
+ problems there.
106
+
107
+ * You can now export a TADS 3 (.t) file. Same concept as Inform,
108
+ albeit doors are created using TADS3 standard door classes.
109
+
110
+
1
111
 
2
112
  v0.8.5 - This is mainly a bug fix release.
3
113
 
data/IFMapper.gemspec CHANGED
@@ -2,7 +2,7 @@ require "rubygems"
2
2
 
3
3
  spec = Gem::Specification.new do |spec|
4
4
  spec.name = "ifmapper"
5
- spec.version = '0.8.5'
5
+ spec.version = '0.9'
6
6
  spec.author = "Gonzalo Garramuno"
7
7
  spec.email = 'ggarram@advance.dsl.com.ar'
8
8
  spec.homepage = 'http://www.rubyforge.org/projects/ifmapper/'
data/TODO.txt CHANGED
@@ -1,7 +1,11 @@
1
1
 
2
+ - Ability to remove stub exits if moving in a certain direction fails.
3
+ Need to trap 'fail' messages for moving such as:
4
+ You can't go that way.
5
+
6
+
2
7
  - Add SVG output
3
8
  - Do Printing
4
- - Cleanup code (lots of ugly and duplicated code
5
- between PDF/PS output)
9
+ - Cleanup code
6
10
  - Improve window scrolling (seems like an FXRuby bug)
7
11
  - Add Undo (at least for delete)
@@ -13,6 +13,7 @@ class Connection
13
13
  FREE = 0
14
14
  LOCKED_DOOR = 1
15
15
  SPECIAL = 2
16
+ CLOSED_DOOR = 3
16
17
 
17
18
  # Direction constants
18
19
  BOTH = 0
@@ -73,7 +74,7 @@ class Connection
73
74
  dirB = Room::DIRECTIONS[idx] if idx
74
75
  end
75
76
  end
76
- "#{a} #{dirA.upcase}<->#{b} #{dirB.upcase}"
77
+ "#{a} #{dirA}<->#{b} #{dirB}"
77
78
  end
78
79
  end
79
80
 
@@ -60,7 +60,7 @@ class FXConnection < Connection
60
60
  # window is open, copy the connection data to it.
61
61
  #
62
62
  def selected=(value)
63
- @@win.copy_from(self) if value and @@win
63
+ @@win.copy_from(self) if value and @@win and @@win.shown?
64
64
  @selected = value
65
65
  end
66
66
 
@@ -205,6 +205,37 @@ class FXConnection < Connection
205
205
  return dc.drawBSpline( p )
206
206
  end
207
207
 
208
+ #
209
+ # Draw a door
210
+ #
211
+ def draw_door(dc, zoom, x1, y1, x2, y2)
212
+ v = [ (x2-x1), (y2-y1) ]
213
+ t = 10 * zoom / Math.sqrt(v[0]*v[0]+v[1]*v[1])
214
+ v = [ (v[0]*t).to_i, (v[1]*t).to_i ]
215
+ m = [ (x2+x1)/2, (y2+y1)/2 ]
216
+ x1, y1 = [m[0] + v[1], m[1] - v[0]]
217
+ x2, y2 = [m[0] - v[1], m[1] + v[0]]
218
+ if @type == LOCKED_DOOR
219
+ # Locked door
220
+ dc.drawLine(x1, y1, x2, y2)
221
+ else
222
+ # open door
223
+ v = [ v[0] / 3, v[1] / 3]
224
+ x1, y1 = x1.to_i, y1.to_i
225
+ x2, y2 = x2.to_i, y2.to_i
226
+ pts = []
227
+ pts << FXPoint.new(x1 - v[0], y1 - v[1])
228
+ pts << FXPoint.new(x1 + v[0], y1 + v[1])
229
+ pts << FXPoint.new(x2 + v[0], y2 + v[1])
230
+ pts << FXPoint.new(x2 - v[0], y2 - v[1])
231
+ pts << pts[0]
232
+ width = dc.lineWidth
233
+ dc.lineWidth = 0
234
+ dc.drawLines(pts)
235
+ dc.lineWidth = width
236
+ end
237
+ end
238
+
208
239
  #
209
240
  # Draw a complex connection
210
241
  #
@@ -219,18 +250,11 @@ class FXConnection < Connection
219
250
  x2, y2 = [p[-1].x, p[-1].y]
220
251
  draw_arrow(dc, zoom, x1, y1, x2, y2)
221
252
 
222
- if @type == LOCKED_DOOR
253
+ if @type == LOCKED_DOOR or @type == CLOSED_DOOR
223
254
  t = p.size / 2
224
255
  x1, y1 = [ p[t].x, p[t].y ]
225
256
  x2, y2 = [ p[t-2].x, p[t-2].y ]
226
-
227
- v = [ (x2-x1), (y2-y1) ]
228
- t = 10 * zoom / Math.sqrt(v[0]*v[0]+v[1]*v[1])
229
- v = [ (v[0]*t).to_i, (v[1]*t).to_i ]
230
- m = [ (x2+x1)/2, (y2+y1)/2 ]
231
- x1, y1 = [m[0] + v[1], m[1] - v[0]]
232
- x2, y2 = [m[0] - v[1], m[1] + v[0]]
233
- dc.drawLine(x1, y1, x2, y2)
257
+ draw_door(dc, zoom, x1, y1, x2, y2)
234
258
  end
235
259
  end
236
260
 
@@ -245,14 +269,8 @@ class FXConnection < Connection
245
269
  x2, y2 = @roomB.corner(self, zoom)
246
270
  dc.drawLine( x1, y1, x2, y2 )
247
271
  draw_arrow(dc, zoom, x1, y1, x2, y2)
248
- if @type == LOCKED_DOOR
249
- v = [ (x2-x1)*0.25, (y2-y1)*0.25 ]
250
- t = 10 * zoom / Math.sqrt(v[0]*v[0]+v[1]*v[1])
251
- v = [ (v[0]*t).to_i, (v[1]*t).to_i ]
252
- m = [ (x2+x1)/2, (y2+y1)/2 ]
253
- x1, y1 = [m[0] + v[1], m[1] - v[0]]
254
- x2, y2 = [m[0] - v[1], m[1] + v[0]]
255
- dc.drawLine(x1, y1, x2, y2)
272
+ if @type == LOCKED_DOOR or @type == CLOSED_DOOR
273
+ draw_door(dc, zoom, x1, y1, x2, y2)
256
274
  end
257
275
  else
258
276
  # Complex connection in progress or "stub" exit
@@ -6,10 +6,18 @@ class FXConnectionDialogBox < FXDialogBox
6
6
 
7
7
  TYPE_TEXT = [
8
8
  'Free',
9
+ 'Door',
9
10
  'Locked',
10
11
  'Special',
11
12
  ]
12
13
 
14
+ TYPE_NUM = [
15
+ Connection::FREE, # free
16
+ Connection::CLOSED_DOOR, # door
17
+ Connection::LOCKED_DOOR, # locked
18
+ Connection::SPECIAL, # special
19
+ ]
20
+
13
21
  DIR_TEXT = [
14
22
  'Both',
15
23
  'A to B',
@@ -28,7 +36,7 @@ class FXConnectionDialogBox < FXDialogBox
28
36
 
29
37
  def copy_to()
30
38
  @conn.dir = @dir.currentNo
31
- @conn.type = @type.currentNo
39
+ @conn.type = TYPE_NUM[ @type.currentNo ]
32
40
  @conn.exitAtext = @exitA.currentNo
33
41
  @conn.exitBtext = @exitB.currentNo
34
42
 
@@ -41,7 +49,7 @@ class FXConnectionDialogBox < FXDialogBox
41
49
  self.title = title
42
50
 
43
51
  @dir.currentNo = conn.dir
44
- @type.currentNo = conn.type || 0
52
+ @type.currentNo = TYPE_NUM.index(conn.type) || 0
45
53
  @exitA.currentNo = conn.exitAtext
46
54
  @exitB.currentNo = conn.exitBtext
47
55
  @conn = conn
@@ -53,13 +53,6 @@ class FXMap < Map
53
53
  end
54
54
  end
55
55
 
56
- #
57
- # Fit all rooms in map, adjusting map size if needed
58
- #
59
- def fit
60
- super
61
- create_pathmap
62
- end
63
56
 
64
57
  #
65
58
  # Jump to a certain section #
@@ -241,10 +234,10 @@ class FXMap < Map
241
234
  b = c.roomB
242
235
 
243
236
  dirA = a.exits.index(c)
244
- raise "connection not found #{c} at #{a}" unless dirA
237
+ raise "A connection not found #{c} at #{a}" unless dirA
245
238
 
246
239
  dirB = b.exits.rindex(c)
247
- raise "connection not found #{c} at #{b}" unless dirB
240
+ raise "B connection not found #{c} at #{b}" unless dirB
248
241
 
249
242
  vA = FXRoom::DIR_TO_VECTOR[dirA]
250
243
  vB = FXRoom::DIR_TO_VECTOR[dirB]
@@ -284,6 +277,12 @@ class FXMap < Map
284
277
  return false
285
278
  end
286
279
 
280
+ if (pA[0] - pB[0]).abs > 30 or
281
+ (pA[1] - pB[1]).abs > 30
282
+ c.failed = true
283
+ return
284
+ end
285
+
287
286
  # No, okay, we need to do true A* path finding
288
287
  c.failed = false
289
288
  aStar = AStar.new
@@ -433,7 +432,7 @@ class FXMap < Map
433
432
  @sections[@section].connections.each { |c|
434
433
  a = c.roomA
435
434
  b = c.roomB
436
- next if not b # Complex connection in progress
435
+ next if not b and @complexConnection
437
436
 
438
437
  if c.gpts.size > 0
439
438
  2.times { |t|
@@ -467,7 +466,15 @@ class FXMap < Map
467
466
  }
468
467
  else
469
468
  x1, y1 = a.corner(c, 1, a.exits.index(c))
470
- x2, y2 = b.corner(c, 1, b.exits.rindex(c))
469
+ if b
470
+ x2, y2 = b.corner(c, 1, b.exits.rindex(c))
471
+ else
472
+ dir = a.exits.index(c)
473
+ v = FXRoom::DIR_TO_VECTOR[dir]
474
+ x2 = x1 + v[0] * WS
475
+ y2 = y1 + v[1] * HS
476
+ end
477
+
471
478
  x1, x2 = x2, x1 if x2 < x1
472
479
  y1, y2 = y2, y1 if y2 < y1
473
480
  if x1 == x2
@@ -530,6 +537,7 @@ class FXMap < Map
530
537
  # Then, create an off-screen image with that same size for double
531
538
  # buffering
532
539
  @image.destroy
540
+ GC.start
533
541
  @image = FXBMPImage.new(@window.getApp, nil, IMAGE_SHMI|IMAGE_SHMP,
534
542
  width, height)
535
543
  @image.create
@@ -731,7 +739,7 @@ class FXMap < Map
731
739
 
732
740
  # Self-explanatory.
733
741
  def zoom_out
734
- if @zoom > 0.25
742
+ if @zoom > 0.1
735
743
  self.zoom -= 0.1
736
744
  end
737
745
  end
@@ -1136,13 +1144,16 @@ class FXMap < Map
1136
1144
  draw
1137
1145
  return
1138
1146
  else
1139
- if event.state & SHIFTMASK == 0 and
1140
- event.state & CONTROLMASK == 0
1147
+ if event.state & SHIFTMASK == 0
1141
1148
  clear_selection
1142
1149
  end
1143
1150
  # Select the stuff
1144
1151
  selection.update_properties(self)
1145
- selection.selected = true
1152
+ if event.state & CONTROLMASK == 0
1153
+ selection.selected ^= true # toggle selection
1154
+ else
1155
+ selection.selected = true
1156
+ end
1146
1157
  end
1147
1158
  end
1148
1159
  draw(sender, sel, event)
@@ -1315,11 +1326,10 @@ class FXMap < Map
1315
1326
  self.zoom = 0.8
1316
1327
  end
1317
1328
 
1318
-
1319
1329
  #
1320
- # Handle deleting selected rooms and/or connections
1330
+ # Handle 'cutting' any selected rooms and/or connections
1321
1331
  #
1322
- def delete_selected
1332
+ def _cut_selected
1323
1333
  rooms = @sections[@section].rooms.find_all { |r| r.selected }
1324
1334
  conns = @sections[@section].connections.find_all { |c| c.selected }
1325
1335
  ############################
@@ -1343,6 +1353,22 @@ class FXMap < Map
1343
1353
  conns.each { |c| clean_path(c) }
1344
1354
  # Remove connections from current section in map
1345
1355
  @sections[@section].connections -= conns
1356
+
1357
+ return conns
1358
+ end
1359
+
1360
+ def cut_selected
1361
+ _cut_selected
1362
+ modified = true
1363
+ draw
1364
+ end
1365
+
1366
+ #
1367
+ # Handle deleting selected rooms and/or connections
1368
+ #
1369
+ def delete_selected
1370
+ conns = _cut_selected
1371
+
1346
1372
  # Remove room exits pointing to any removed connection
1347
1373
  conns.each { |c|
1348
1374
  a = c.roomA
@@ -1354,10 +1380,8 @@ class FXMap < Map
1354
1380
  end
1355
1381
  }
1356
1382
 
1357
- # Recreate pathmap. We need to recreate pathmap for all paths
1358
- # as the removal of one path may help shorten the path of another.
1359
- create_pathmap
1360
- draw()
1383
+ modified = true
1384
+ draw
1361
1385
  end
1362
1386
 
1363
1387
  #
@@ -1705,12 +1729,7 @@ class FXMap < Map
1705
1729
  #
1706
1730
  def draw_background(dc, event = nil)
1707
1731
  dc.foreground = @options['BG Color']
1708
-
1709
- if event
1710
- dc.fillRectangle(event.rect.x, event.rect.y, event.rect.w, event.rect.h)
1711
- else
1712
- dc.fillRectangle(0,0, @canvas.width, @canvas.height)
1713
- end
1732
+ dc.fillRectangle(0,0, @canvas.width, @canvas.height)
1714
1733
  end
1715
1734
 
1716
1735
  #
@@ -1794,6 +1813,22 @@ class FXMap < Map
1794
1813
  draw
1795
1814
  end
1796
1815
 
1816
+ #
1817
+ # Draw a single room (callback used when editing room dialog box)
1818
+ #
1819
+ def draw_room(room)
1820
+ idx = @sections[@section].rooms.index(room)
1821
+ return unless idx
1822
+
1823
+ dc = FXDCWindow.new(@canvas)
1824
+ dc.font = @font
1825
+ data = { }
1826
+ data['font'] = @font
1827
+ data['objfont'] = @objfont
1828
+ room.draw(dc, @zoom, idx, @options, data)
1829
+ dc.end
1830
+ end
1831
+
1797
1832
  #
1798
1833
  # Draw map
1799
1834
  #
@@ -1846,6 +1881,11 @@ class FXMap < Map
1846
1881
  end
1847
1882
 
1848
1883
  status "Saving '#{@filename}'..."
1884
+
1885
+ # Make sure we save a valid map. This is mainly a fail-safe
1886
+ # in case of an autosave due to a bug.
1887
+ verify_integrity
1888
+
1849
1889
  @version = FILE_FORMAT_VERSION
1850
1890
  begin
1851
1891
  f = File.open(@filename, "wb")