ifmapper 0.6 → 0.7

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.
@@ -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