ifmapper 0.6 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,71 @@
1
1
 
2
+ v0.7 - Fix the issue of users trying to fire up the application from another
3
+ directory, without cd'ing themselves to the IFMapper directory.
4
+
5
+ Added IFM export.
6
+ Note that albeit you can load and save out an IFM map file, if you load
7
+ and then save an ifm file, the integrity of the ifm file created by hand
8
+ will not be kept.
9
+ Things such as ifm variables, task and object dependencies will be
10
+ lost if you do that. It is unlikely I will add full walkthru
11
+ functionality to IFMapper.
12
+ IFMapper's main format remains its propietary .map format (which is
13
+ just marshalling of its internal ruby classes).
14
+
15
+ Added a popup menu to the use of the right mouse button. The main
16
+ functionality of this is to allow you to easily shift or switch
17
+ a connection from one room exit to another.
18
+ To use it, select a single connection among rooms, then press RMB
19
+ and select the room and the exit you want to shift the connection to.
20
+ Voila! Much easier than having to delete and recreate the connection.
21
+
22
+ Improved selection mechanism a tad to be more accurate. Now you
23
+ can create complex connections near other complex connections, allowing
24
+ you to make, for example, intercrossing connections like:
25
+ R---R
26
+ |\ /|
27
+ | X |
28
+ |/ \|
29
+ R---R
30
+ This still needs some minor work as you cannot select long connections
31
+ near room transition areas.
32
+
33
+ Fixed a startup crashing bug when HOME variable was not defined.
34
+ If it is, IFMapper will use the value of it. If not, user preferences
35
+ will get saved to %USERPROFILE% if defined or %HOMEDRIVE%/%HOMEPATH%.
36
+ If all fails, prefs will get saved to the current directory.
37
+
38
+ Searching rooms, objects or tasks now also tries to center the window
39
+ on the current room being found. The Next/Previous button now make
40
+ a tad more sense. Also, rooms in other sections are not selected
41
+ until you move to that section using Next/Previous.
42
+
43
+ There's now an additional search item to allow searching for keywords
44
+ in tasks.
45
+
46
+ You can now select all nodes or none of them from a menu option.
47
+
48
+ Zooming with mousewheel is a tad faster.
49
+
50
+ There's now some very bare bones Postscript exporting, albeit no
51
+ printing yet. Printing is really a tough cookie to crack across
52
+ platforms.
53
+
54
+ Fixed a minor bug regarding failed connections that could show with some
55
+ ifm files (mainly Zork1.ifm)
56
+
57
+ Added some more documentation and also some thank yous.
58
+
59
+ Added proper cut/copy/pasting of rooms and connections to the same
60
+ section, to new sections or even to different maps. Just select the
61
+ rooms and connections you want to copy and do cut/copy and then paste.
62
+ Rooms are pasted to the first map area that has enough free nodes to
63
+ support them.
64
+
65
+ Made SHIFT-DRAG selection also select connections.
66
+
67
+ Updated my email.
68
+
2
69
  v0.6 - Fixed a crashing bug when nodes were moved too far to the right or
3
70
  to the bottom of the map area.
4
71
 
@@ -22,7 +89,7 @@ v0.6 - Fixed a crashing bug when nodes were moved too far to the right or
22
89
  Added the ability to set the map width/height from the Map Information
23
90
  panel.
24
91
 
25
- Fixed an obscure bug when loading IMF maps that were bigger than the
92
+ Fixed an obscure bug when loading IFM maps that were bigger than the
26
93
  default map size.
27
94
 
28
95
  v0.5 - First public release on Rubyforge.
@@ -2,9 +2,9 @@ require "rubygems"
2
2
 
3
3
  spec = Gem::Specification.new do |spec|
4
4
  spec.name = "ifmapper"
5
- spec.version = '0.6'
5
+ spec.version = '0.7'
6
6
  spec.author = "Gonzalo Garramuno"
7
- spec.email = 'GGarramuno@aol.com or ggarram@advance.dsl.com.ar'
7
+ spec.email = 'ggarram@advance.dsl.com.ar'
8
8
  spec.homepage = 'http://www.rubyforge.org/projects/ifmapper/'
9
9
  spec.summary = 'Interactive Fiction Mapping Tool.'
10
10
  spec.require_path = "lib"
@@ -1,6 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # Add current path, so modules are found locally
3
+
4
+ # cd to install path, so modules are found locally
5
+ install_loc = $0.sub(/\/?[^\/]*$/, '')
6
+ install_loc = '.' if install_loc == ''
7
+ Dir.chdir(install_loc)
4
8
  $LOAD_PATH << './lib'
5
9
  require 'IFMapper/FXMapperWindow'
6
10
 
@@ -230,13 +230,13 @@ class MapNode
230
230
  def cost( successor )
231
231
  g = get_map(@x,@y)
232
232
 
233
- # dx = (@x - successor.x).abs
234
- # dy = (@y - successor.y).abs
235
- # if dx + dy == 1
236
- # g += 0 # straight move
237
- # else
238
- # g += 4 # diagonal move
239
- # end
233
+ dx = (@x - successor.x).abs
234
+ dy = (@y - successor.y).abs
235
+ if dx + dy == 1
236
+ g += 10 # straight move
237
+ else
238
+ g += 14 # diagonal move
239
+ end
240
240
  return g
241
241
  end
242
242
 
@@ -42,7 +42,7 @@ class Connection
42
42
  a = @roomA? @roomA.name : 'nil'
43
43
  b = @roomB? @roomB.name : 'nil'
44
44
  if exitAtext > 0
45
- dirA = EXIT_TEXT[exitBtext]
45
+ dirA = EXIT_TEXT[exitAtext]
46
46
  else
47
47
  if @roomA
48
48
  idx = @roomA.exits.index(self)
@@ -57,7 +57,7 @@ class Connection
57
57
  dirB = Room::DIRECTIONS[idx] if idx
58
58
  end
59
59
  end
60
- "#{a} #{dirA}<->#{b} #{dirB}"
60
+ "#{a} #{dirA.upcase}<->#{b} #{dirB.upcase}"
61
61
  end
62
62
  end
63
63
 
@@ -133,15 +133,18 @@ class FXConnection < Connection
133
133
  end
134
134
 
135
135
 
136
+ def update_properties(map)
137
+ return if not @@win or not @@win.shown?
138
+ @@win.map = map
139
+ @@win.copy_from(self)
140
+ end
141
+
136
142
  def properties(map, event = nil)
137
143
  if not @@win
138
144
  @@win = FXConnectionDialogBox.new(map, self, event)
139
- else
140
- @@win.map = map
141
145
  end
142
- @@win.copy_from(self)
143
146
  @@win.show
144
- return
147
+ update_properties(map)
145
148
  end
146
149
 
147
150
  protected
@@ -0,0 +1,404 @@
1
+
2
+
3
+ #
4
+ # Class used to do Postscript printing. This class has only the basic
5
+ # functionality needed for doing so and replaces the still somewhat buggy version of
6
+ # Fox's 1.2 FXDCPrint
7
+ #
8
+ class FXDCPostscript < FXDC
9
+ @@printcmd = 'lpr -P%s -o l -' # -#%d'
10
+
11
+ def setContentRange(xmin, ymin, xmax, ymax)
12
+ @pxmin = xmin.to_f
13
+ @pymin = ymin.to_f
14
+ @pxmax = xmax.to_f
15
+ @pymax = ymax.to_f
16
+ end
17
+
18
+ # Transform point
19
+ def tfm(xi, yi)
20
+ pxrange = @pxmax - @pxmin
21
+ pyrange = @pymax - @pymin
22
+
23
+ if @flags & PRINT_LANDSCAPE != 0
24
+ mxmin = @ymin.to_f
25
+ mxmax = @ymax.to_f
26
+ mymin = @width - @xmax
27
+ mymax = @width - @xmin
28
+ mxrange = mxmax - mxmin
29
+ myrange = mymax - mymin
30
+ else
31
+ mxmin = @xmin
32
+ mxmax = @xmax
33
+ mymin = @ymin
34
+ mymax = @ymax
35
+ mxrange = mxmax-mxmin
36
+ myrange = mymax-mymin
37
+ end
38
+
39
+ if pyrange/pxrange <= myrange/mxrange
40
+ # short/wide
41
+ xo = mxmin + ((xi.to_f-@pxmin)/pxrange) * mxrange;
42
+ yo = mymin + 0.5 * (myrange-pyrange * (mxrange/pxrange)) +
43
+ (pyrange-yi.to_f) * (mxrange/pxrange)
44
+ else
45
+ # tall/thin
46
+ xo = mxmin + 0.5 * (mxrange-pxrange * (myrange/pyrange)) +
47
+ xi * (myrange/pyrange)
48
+ yo = mymin + ((pyrange-yi.to_f)/pyrange) * myrange
49
+ end
50
+ return [xo, yo]
51
+ end
52
+
53
+
54
+
55
+
56
+
57
+ def _header
58
+ @f.puts <<'HEADER'
59
+ %%!PS-Adobe-3.0
60
+ %%%%Title: Print Job
61
+ %%%%Creator: IFMapper
62
+ HEADER
63
+
64
+ end
65
+
66
+ def beginPrint(printer, &block)
67
+ @flags = printer.flags
68
+ if @flags & PRINT_DEST_FILE != 0
69
+ @f = File.open( printer.name, 'w' )
70
+ else
71
+ buffer = @@printcmd % [ printer.name, printer.numcopies ]
72
+ puts buffer
73
+ @f = File.popen( buffer, 'w')
74
+ end
75
+
76
+
77
+ @width = printer.mediawidth.to_f
78
+ @height = printer.mediaheight.to_f
79
+
80
+ @xmin = printer.leftmargin.to_f
81
+ @xmax = @width - printer.rightmargin
82
+
83
+ @ymin = printer.bottommargin.to_f
84
+ @ymax = @height - printer.topmargin
85
+
86
+
87
+ _header
88
+ if @flags & PRINT_NOBOUNDS != 0
89
+ xmin = 10000000.0
90
+ xmax = -xmin
91
+ ymin = 10000000.0
92
+ ymax = -ymin
93
+ @f.puts "%%%%BoundingBox: (atend)"
94
+ else
95
+ @f.puts "%%%%BoundingBox: %d %d %d %d" %
96
+ [ @xmin, @ymin, @xmax, @ymax ]
97
+ end
98
+
99
+ @numpages = 1 + printer.firstpage - printer.lastpage
100
+ if @flags & PRINT_PAGES_ODD
101
+ @numpages = 1 + (printer.topage - printer.frompage) / 2
102
+ elsif @flags & PRINT_PAGES_EVEN
103
+ @numpages = 1 + (printer.topage - printer.frompage) / 2
104
+ elsif @pages & PRINT_PAGES_RANGE
105
+ @numpages = 1 + printer.topage - printer.frompage
106
+ end
107
+
108
+ if @numpages == 0
109
+ @f.puts "%%%%Pages: (atend)"
110
+ else
111
+ @f.puts "%%%%Pages: #{@numpages}"
112
+ end
113
+
114
+ @f.puts "%%%%DocumentFonts:"
115
+ @f.puts "%%%%EndComments"
116
+ @f.puts "%%%%BeginProlog\n\n"
117
+
118
+ # Various definitions
119
+ @f.puts "%% h w x y drawRect\n"
120
+ @f.puts "/drawRect {\n\tnewpath moveto dup 0 rlineto exch dup 0 exch\n\trlineto exch neg 0 rlineto neg 0 exch rlineto\n\tclosepath stroke\n} def\n"
121
+ @f.puts "%% h w x y fillRect\n"
122
+ @f.puts "/fillRect {\n\tnewpath moveto dup 0 rlineto exch dup 0 exch\n\trlineto exch neg 0 rlineto neg 0 exch rlineto\n\tclosepath fill stroke\n} def\n"
123
+ @f.puts "%% x y a b drawLine\n"
124
+ @f.puts "/drawLine {\n\tnewpath moveto lineto stroke\n} def\n"
125
+ @f.puts "%% x y ..... npoints drawLines\n"
126
+ @f.puts "/drawLines {\n\t3 1 roll newpath moveto {lineto} repeat stroke\n} def\n"
127
+ @f.puts "%% x y a b ..... nsegments drawSegmt\n"
128
+ @f.puts "/drawSegmt {\n\tnewpath {\n\t\tmoveto lineto\n\t} repeat stroke\n} def\n"
129
+ @f.puts "%% x y drawPoint\n"
130
+ @f.puts "/drawPoint {\n\ttranslate 1 1 scale 8 8 1 [ 8 0 0 8 0 0 ] {<0000>} image\n} def\n"
131
+ @f.puts "%% centerx centery startAngle endAngle radiusX radiusY drawArc\n"
132
+ @f.puts "/drawArc {\n\tgsave dup 3 1 roll div dup 1 scale 6 -1 roll\n\texch div 5 1 roll 3 -2 roll arc stroke grestore\n} def\n"
133
+ @f.puts "/fillChord {\n\tgsave dup 3 1 roll div dup 1 scale 6 -1 roll\n\texch div 5 1 roll 3 -2 roll arc fill grestore\n} def\n"
134
+ @f.puts "%% (string) x y height drawText\n"
135
+ @f.puts "/drawText {\n\tgsave findfont exch scalefont setfont moveto\n\tshow grestore\n} def\n"
136
+
137
+
138
+ @f.puts "%%%%EndProlog"
139
+
140
+ @f.puts "%%%%BeginSetup"
141
+ @f.puts "/#copies #{printer.numcopies} def"
142
+ @f.puts "%%%%EndSetup"
143
+
144
+ @page = 0
145
+
146
+ if block_given?
147
+ yield self
148
+ endPrint()
149
+ end
150
+ end
151
+
152
+
153
+ def endPrint
154
+ @f.puts "%%%%Trailer\n"
155
+
156
+ # We now know the bounding box
157
+ if @flags & PRINT_NOBOUNDS != 0
158
+ if @xmin < @xmax and @ymin < @ymax
159
+ @f.puts "%%%%BoundingBox: %d %d %d %d" %
160
+ [ @xmin, @ymin, @xmax, @ymax ]
161
+ else
162
+ @f.puts "%%%%BoundingBox: 0 0 100 100"
163
+ end
164
+ end
165
+
166
+ # We now know the page count
167
+ if not (@flags & PRINT_PAGES_ODD|PRINT_PAGES_EVEN|PRINT_PAGES_RANGE)
168
+ @f.puts "%%%%Pages: #{page}"
169
+ end
170
+
171
+ @f.puts "%%%%EOF"
172
+ @f.close
173
+ end
174
+
175
+
176
+ def beginPage(num = 1, &block)
177
+ # Output page number
178
+ @f.puts "%%%%Page: #{@page} #{@page}" # Ghostscript requests this, I do not know if it is right
179
+
180
+ # Reset page bounding box
181
+ if @flags & PRINT_NOBOUNDS != 0
182
+ @xmin= 1000000;
183
+ @xmax=-1000000;
184
+ @ymin= 1000000;
185
+ @ymax=-1000000;
186
+ @f.puts "%%%%PageBoundingBox: (atend)"
187
+ else
188
+ # Use the doc bounding box
189
+ @f.puts "%%%%PageBoundingBox: %d %d %d %d" % [@xmin, @ymin, @xmax, @ymax]
190
+ end
191
+
192
+ # Page setup
193
+ @f.puts "%%%%BeginPageSetup"
194
+ @f.puts "%%%%EndPageSetup"
195
+ @f.puts "gsave"
196
+
197
+ # Maybe in landscape?
198
+ if @flags & PRINT_LANDSCAPE != 0
199
+ @f.puts "#{@width} 0.0 translate"
200
+ @f.puts "90 rotate"
201
+ end
202
+
203
+ if block_given?
204
+ yield self
205
+ endPage()
206
+ end
207
+ end
208
+
209
+ def endPage
210
+ @f.puts "%%%%PageTrailer"
211
+
212
+ # We now know the bounding box
213
+ if @flags & PRINT_NOBOUNDS != 0
214
+ if @xmin < @xmax and @ymin < @ymax
215
+ @f.puts "%%%%BoundingBox: %d %d %d %d" [@xmin, @ymin, @xmax, @ymax]
216
+ else
217
+ @f.puts "%%%%BoundingBox: 0 0 100 100" # Gotta come up with something...
218
+ end
219
+ end
220
+ @f.puts "showpage"
221
+ @f.puts "grestore"
222
+ @page += 1
223
+ end
224
+
225
+ # Extends bounding box with point x,y
226
+ def bbox(x, y)
227
+ @xmin = x if x < @xmin
228
+ @ymin = y if y < @ymin
229
+ @xmax = x if x > @xmax
230
+ @ymax = y if y > @ymax
231
+ end
232
+
233
+ def _rect(x, y, w, h)
234
+ xl, yt = tfm(x, y)
235
+ xr, yb = tfm(x + w - 1, y + h - 1)
236
+ bbox(xl,yt)
237
+ bbox(xr,yb)
238
+ @f.print "newpath %g %g moveto %g %g lineto %g %g lineto %g %g lineto %g %g lineto " %
239
+ [xl,yt,xr,yt,xr,yb,xl,yb,xl,yt]
240
+ end
241
+
242
+ def drawRectangle(x, y, w, h)
243
+ _rect(x, y, w, h)
244
+ @f.puts "stroke"
245
+ end
246
+
247
+
248
+ def drawLine(x1, y1, x2, y2)
249
+ xx1, yy1 = tfm(x1, y1)
250
+ xx2, yy2 = tfm(x2, y2)
251
+ bbox(xx1,yy1)
252
+ bbox(xx2,yy2)
253
+ @f.puts "newpath %g %g moveto %g %g lineto stroke" % [xx1, yy1, xx2, yy2]
254
+ end
255
+
256
+ def drawLines(points)
257
+ return if points.size < 2
258
+ xx, yy = tfm(points[0].x, points[0].y)
259
+ bbox(xx,yy)
260
+ @f.print "newpath %g %g moveto" % [xx, yy]
261
+ 1.upto(points.size-1) { |i|
262
+ xx, yy = tfm(points[i].x, points[i].y)
263
+ bbox(xx,yy)
264
+ @f.print " %g %g lineto" % [xx, yy]
265
+ }
266
+ @f.puts " stroke"
267
+ end
268
+
269
+ def drawText(x, y, text)
270
+ # Draw string (only foreground bits)
271
+ # Contributed by S. Ancelot <sancelot@online.fr>
272
+ xx, yy = tfm( x, y )
273
+
274
+ pxrange = @pxmax - @pxmin
275
+ pyrange = @pymax - @pymin
276
+
277
+ if @flags & PRINT_LANDSCAPE != 0
278
+ mxmin = @ymin
279
+ mxmax = @ymax
280
+ mymin = @width - @xmax
281
+ mymax = @width - @xmin
282
+ mxrange = mxmax - mxmin
283
+ myrange = mymax - mymin
284
+ else
285
+ mxmin = @xmin
286
+ mxmax = @xmax
287
+ mymin = @ymin
288
+ mymax = @ymax
289
+ mxrange = mxmax - mxmin
290
+ myrange = mymax - mymin
291
+ end
292
+
293
+ fsize = 0.1 * @font.getSize
294
+
295
+ # Hack...
296
+ # Account for dpi and scale up or down with graph...
297
+ # Perhaps override screen resolution via registry
298
+ # FXint screenres=getApp()->reg().readUnsignedEntry("SETTINGS","screenres",100);
299
+ # Validate
300
+ screenres = 100
301
+ # if(screenres<50) screenres=50;
302
+ # if(screenres>200) screenres=200;
303
+
304
+ if pyrange / pxrange <= myrange / mxrange
305
+ # short/wide
306
+ fsize *= (mxrange/pxrange)*(screenres/72.0)
307
+ else
308
+ # tall/thin
309
+ fsize *= (myrange/pyrange)*(screenres/72.0)
310
+ end
311
+
312
+ fname = @font.name
313
+ if fname =~ /times/i
314
+ fname = 'Times-Roman'
315
+ elsif fname =~ /(helvetica|verdana|tahoma|arial)/i
316
+ fname = 'Helvetica'
317
+ else
318
+ fname = 'Courier'
319
+ end
320
+
321
+ if @font.getWeight == FXFont::FONTWEIGHT_BOLD
322
+ fname << '-Bold'
323
+ if @font.getSlant == FXFont::FONTSLANT_ITALIC
324
+ fname << 'Italic'
325
+ elsif @font.getSlant == FXFont::FONTSLANT_OBLIQUE
326
+ fname << 'Oblique'
327
+ end
328
+ else
329
+ if @font.getSlant == FXFont::FONTSLANT_ITALIC
330
+ fname << '-Italic'
331
+ elsif @font.getSlant == FXFont::FONTSLANT_OBLIQUE
332
+ fname << '-Oblique'
333
+ end
334
+ end
335
+
336
+ @f.puts "(%s) %g %g %d /%s drawText" % [text, xx, yy, fsize, fname]
337
+ end
338
+
339
+ def fillRectangle(x, y, w, h)
340
+ _rect(x, y, w, h)
341
+ @f.puts "fill"
342
+ end
343
+
344
+ def fillPolygon(points)
345
+ return if points.size < 3
346
+ xx, yy = tfm(points[0].x, points[0].y)
347
+ bbox(xx,yy)
348
+ @f.print "newpath %g %g moveto" % [ xx, yy ]
349
+ 1.upto(points.size-1) { |i|
350
+ xx, yy = tfm(points[i].x, points[i].y)
351
+ bbox(xx,yy)
352
+ @f.print " %g %g lineto" % [ xx, yy ]
353
+ }
354
+ @f.puts " fill"
355
+ end
356
+
357
+ def lineStyle=(x)
358
+ if x == LINE_SOLID
359
+ @f.puts "[ ] 0 setdash"
360
+ elsif x == LINE_ONOFF_DASH
361
+ @f.puts "[2 2] 0 setdash"
362
+ end
363
+ end
364
+
365
+ def foreground=(c)
366
+ if c.kind_of?(String)
367
+ clr = fxcolorfromname(c)
368
+ else
369
+ clr = c
370
+ end
371
+ @f.puts "%g %g %g setrgbcolor" % [FXREDVAL(clr)/255.0,FXGREENVAL(clr)/255.0,FXBLUEVAL(clr)/255.0]
372
+ end
373
+
374
+ def lineJoin=(x)
375
+ end
376
+
377
+ def lineCap=(x)
378
+ ncap = 0
379
+ ncap = 1 if CAP_ROUND == x
380
+ ncap = 3 if CAP_PROJECTING == x
381
+ @f.puts "#{ncap} setlinecap"
382
+ end
383
+
384
+ def lineWidth
385
+ return @lineWidth
386
+ end
387
+
388
+ def lineWidth=(x)
389
+ @lineWidth = x
390
+ @f.puts "%f setlinewidth" % [x * 0.5]
391
+ end
392
+
393
+ def initialize(app)
394
+ @font = app.getNormalFont()
395
+ end
396
+
397
+ def font=(x)
398
+ @font = x
399
+ end
400
+
401
+ def font
402
+ return @font
403
+ end
404
+ end