ifmapper 0.8.5 → 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,349 @@
1
+
2
+
3
+ class InformWriter
4
+
5
+
6
+
7
+ DIRECTIONS = [
8
+ 'n_to',
9
+ 'ne_to',
10
+ 'e_to',
11
+ 'se_to',
12
+ 's_to',
13
+ 'sw_to',
14
+ 'w_to',
15
+ 'nw_to',
16
+ ]
17
+
18
+ OTHERDIRS = [
19
+ '',
20
+ 'u_to',
21
+ 'd_to',
22
+ 'in_to',
23
+ 'out_to',
24
+ ]
25
+
26
+
27
+ def get_door_tag(e)
28
+ dirA = e.roomA.exits.index(e)
29
+ dirB = e.roomB.exits.rindex(e)
30
+ name = Room::DIRECTIONS[dirA] + "_" + Room::DIRECTIONS[dirB]
31
+ name << "_door"
32
+ get_tag(e, name)
33
+ end
34
+
35
+ def new_tag(elem, str)
36
+ tag = str.dup
37
+
38
+ # Invalid tag characters, replaced with _
39
+ tag.gsub!(/[\s"'\/\\\-#\,.:;!\?\n\(\)]/,'_')
40
+
41
+ tag.gsub!(/__/, '') # remove reduntant __ repetitions
42
+ tag.sub!(/^([\d]+)_?(.*)/, '\2\1') # No numbers allowed at start of tag
43
+ tag.downcase! # All tags are lowercase
44
+
45
+
46
+
47
+ tag = tag[0..31] # Max. 32 chars. in tag (inform limit)
48
+
49
+ # tag cannot be keyword (Doorway, Room, Class, etc)
50
+ if tag =~ @keyword
51
+ tag << '1'
52
+ end
53
+
54
+ if @tags.values.include?(tag)
55
+ idx = 2
56
+ root = tag[0..29] # to leave room for number
57
+ while @tags.values.include?(tag)
58
+ tag = "#{root}#{idx}"
59
+ idx += 1
60
+ end
61
+ end
62
+ if elem.kind_of?(String)
63
+ @tags[tag] = tag
64
+ else
65
+ @tags[elem] = tag
66
+ end
67
+ return tag
68
+ end
69
+
70
+ def get_tag(elem, name = elem.name)
71
+ return @tags[elem] if @tags[elem]
72
+ return new_tag(elem, name)
73
+ end
74
+
75
+ def wrap_text(text, width = 65, indent = 79 - width)
76
+ return 'UNDER CONSTRUCTION' unless text
77
+ str = inform_quote( text.dup )
78
+
79
+ if text.size > width
80
+ r = ''
81
+ while str
82
+ idx = str.rindex(/[ -]/, width)
83
+ unless idx
84
+ idx = str.index(/[ -]/, width)
85
+ idx = str.size unless idx
86
+ end
87
+ r << str[0..idx]
88
+ str = str[idx+1..-1]
89
+ r << "\n" << ' ' * indent if str
90
+ end
91
+ return r
92
+ else
93
+ return str
94
+ end
95
+ end
96
+
97
+
98
+ #
99
+ # Take a text and quote it for inform's double-quote text areas.
100
+ #
101
+ def inform_quote(text)
102
+ str = text.dup
103
+ # Quote special characters
104
+ str.gsub!(/@/, '@@64')
105
+ str.gsub!(/"/, '@@34')
106
+ str.gsub!(/~/, '@@126')
107
+ str.gsub!(/\\/, '@@92')
108
+ str.gsub!(/\^/, '@@94')
109
+ return str
110
+ end
111
+
112
+
113
+ def objects(r)
114
+ room = get_tag(r)
115
+ objs = r.objects.split("\n")
116
+ objs.each { |o|
117
+
118
+ tag = new_tag(o, o)
119
+ names = o.dup
120
+ names.gsub!(/"/, '') # remove any quotes
121
+ names.gsub!(/'/, '^') # change ' to ^ (inform convention)
122
+ names.gsub!(/\b\w\b/, '') # remove single letter words
123
+ names = names.split(' ')
124
+ names = "'" + names.join("' '") + "'"
125
+
126
+ name = inform_quote(o)
127
+
128
+ classname = 'Object'
129
+
130
+ # If name begins with uppercase, assume it is an NPC
131
+ if name =~ /[A-Z]/
132
+ classname = 'NPC'
133
+ end
134
+
135
+ @f.print <<"EOF"
136
+
137
+ #{classname} #{tag} "#{name}" #{room}
138
+ with name #{names},
139
+ description "#{name} is UNDER CONSTRUCTION.";
140
+ EOF
141
+ }
142
+
143
+
144
+ end
145
+
146
+ def door(e)
147
+ tag = get_door_tag(e)
148
+ roomA = get_tag(e.roomA)
149
+ roomB = get_tag(e.roomB)
150
+ dirA = e.roomA.exits.index(e)
151
+ dirB = e.roomB.exits.rindex(e)
152
+ dirA = Room::DIRECTIONS[dirA]
153
+ dirB = Room::DIRECTIONS[dirB]
154
+
155
+ props = ''
156
+ if e.type == Connection::LOCKED_DOOR
157
+ props = "\n has lockable locked"
158
+ elsif e.type == Connection::CLOSED_DOOR
159
+ props = "\n has ~open"
160
+ end
161
+
162
+ if e.dir == Connection::BOTH
163
+ found_in = "#{roomA} #{roomB}"
164
+ elsif e.dir == Connection::AtoB
165
+ found_in = roomA
166
+ elsif e.dir == Connection::BtoA
167
+ found_in = roomB
168
+ end
169
+
170
+ @f.puts <<"EOF"
171
+
172
+ ! Door connecting #{e}
173
+ Doorway #{tag} "door"
174
+ with name 'door',
175
+ side1_to #{roomA},
176
+ side1_dir #{dirA}_to,
177
+ side2_to #{roomB},
178
+ side2_dir #{dirB}_to,
179
+ found_in #{found_in}#{props};
180
+ EOF
181
+
182
+ end
183
+
184
+
185
+ def room(r)
186
+ tag = get_tag(r)
187
+ name = inform_quote(r.name)
188
+ @f.puts <<-"EOF"
189
+
190
+ !-------------------- #{r.name}
191
+ Room #{tag} "#{name}"
192
+ EOF
193
+
194
+ @f.puts " with description"
195
+ @f.print " \"#{wrap_text(r.desc)}\""
196
+
197
+ # Now, handle exits...
198
+ r.exits.each_with_index { |e, dir|
199
+ next if (not e) or e.stub? or e.type == Connection::SPECIAL
200
+ if e.roomB == r
201
+ next if e.dir == Connection::AtoB
202
+ text = e.exitBtext
203
+ b = e.roomA
204
+ else
205
+ next if e.dir == Connection::BtoA
206
+ text = e.exitAtext
207
+ b = e.roomB
208
+ end
209
+ @f.puts ','
210
+ @f.print ' '
211
+ if text == 0
212
+ @f.print "#{DIRECTIONS[dir]} "
213
+ else
214
+ @f.print "#{OTHERDIRS[text]} "
215
+ end
216
+ if e.type == Connection::CLOSED_DOOR or
217
+ e.type == Connection::LOCKED_DOOR
218
+ @f.print get_door_tag(e)
219
+ else
220
+ @f.print get_tag(b)
221
+ end
222
+ }
223
+ if r.darkness
224
+ @f.print " has ~light"
225
+ end
226
+ @f.puts ";"
227
+
228
+ objects(r)
229
+ end
230
+
231
+ def section(sect, idx)
232
+ @f = File.open("#@root-#{idx}.inf", "w")
233
+ @f.puts '!' + '=' * 78
234
+ @f.puts "! Section: #{sect.name} "
235
+ @f.puts '!' + '=' * 78
236
+ sect.rooms.each { |r|
237
+ room(r)
238
+ }
239
+
240
+ @f.puts '!' + '-' * 78
241
+ @f.puts '! Doorways'
242
+ @f.puts '!' + '-' * 78
243
+ sect.connections.each { |e|
244
+ next if (e.type != Connection::LOCKED_DOOR and
245
+ e.type != Connection::CLOSED_DOOR)
246
+ door(e)
247
+ }
248
+ @f.close
249
+ end
250
+
251
+ def start
252
+ @f = File.open("#@root.inf", "w")
253
+ @f.puts '!' + '=' * 78
254
+ story = @map.name
255
+ story = 'Untitled' if story == ''
256
+ today = Date.today
257
+ serial = today.strftime("%y%m%d")
258
+ @f.puts <<"EOF"
259
+ Constant Story "#{story}";
260
+ Constant Headline
261
+ "^A game map done with IFMapper
262
+ ^by #{@map.creator}.^";
263
+ Release 1;
264
+ Serial "#{serial}"; ! #{Time.now}
265
+
266
+ ! These constants are to make the game also potentially Glulx compatible
267
+ #ifndef WORDSIZE;
268
+ Constant TARGET_ZCODE;
269
+ Constant WORDSIZE 2;
270
+ #endif;
271
+
272
+ !Constant ZDEBUG;
273
+
274
+ Include "Parser";
275
+ Include "VerbLib";
276
+
277
+
278
+ EOF
279
+ @f.puts '!' + '=' * 78
280
+ @f.puts "! Object classes"
281
+ @f.puts <<"EOF"
282
+
283
+ ! We use Andrew MacKinnon's EasyDoors for our doors.
284
+ Include "easydoors";
285
+
286
+ ! We define a class for NPC (non-player characters)
287
+ class NPC
288
+ has animate;
289
+
290
+ EOF
291
+
292
+ @f.puts '!' + '=' * 78
293
+ @f.puts "! Map sections"
294
+ @map.sections.each_index { |idx|
295
+ @f.puts "#include \"#@base-#{idx}.inf\";"
296
+ }
297
+
298
+ start_location = ''
299
+ if @map.sections.size > 0 and @map.sections[0].rooms[0]
300
+ r = @map.sections[0].rooms[0]
301
+ tag = get_tag(r)
302
+ start_location = "location = #{tag}"
303
+ end
304
+
305
+ @f.puts <<"EOF";
306
+ !============================================================================
307
+ ! Entry point routines
308
+
309
+ [ Initialise;
310
+ #{start_location};
311
+ lookmode = 2; ! verbose
312
+ inventory_style = FULLINV_BIT + ENGLISH_BIT + RECURSE_BIT; ! wide
313
+ ];
314
+ EOF
315
+
316
+ @f.puts <<"EOF"
317
+ !============================================================================
318
+ ! Standard and extended grammar
319
+
320
+ Include "Grammar";
321
+ EOF
322
+
323
+ @f.close
324
+
325
+ @map.sections.each_with_index { |sect, idx|
326
+ section(sect, idx)
327
+ }
328
+ end
329
+
330
+ def initialize(map, fileroot)
331
+ @tags = {}
332
+ @root = fileroot
333
+ @base = File.basename(@root)
334
+ @map = map
335
+
336
+ keywords = [
337
+ 'doorway',
338
+ 'class',
339
+ 'include',
340
+ 'room',
341
+ 'has',
342
+ 'with',
343
+ 'description',
344
+ ] + DIRECTIONS
345
+
346
+ @keyword = /^#{keywords.join('|')}$/i
347
+ start
348
+ end
349
+ end
data/lib/IFMapper/Map.rb CHANGED
@@ -52,9 +52,20 @@ class Map
52
52
  if not sect.connections.include?(e)
53
53
  $stderr.puts "Exit #{e} in room, but not in section #{sect.name}."
54
54
  r.exits[idx] = nil
55
+ sect.connections.delete(e)
55
56
  end
56
57
  }
57
58
  }
59
+
60
+ sect.connections.each { |c|
61
+ a = c.roomA
62
+ b = c.roomB
63
+ if not a.exits.index(c) or
64
+ (b and not b.exits.rindex(c))
65
+ $stderr.puts "Exit #{c} not present in room."
66
+ sect.connections.delete(c)
67
+ end
68
+ }
58
69
  }
59
70
  end
60
71
 
@@ -179,6 +190,7 @@ class Map
179
190
  _check_section
180
191
  end
181
192
 
193
+
182
194
  end
183
195
 
184
196
 
@@ -71,6 +71,37 @@ class FXConnection
71
71
  return p
72
72
  end
73
73
 
74
+ def pdf_draw_door( pdf, x1, y1, x2, y2 )
75
+ v = [ (x2-x1), (y2-y1) ]
76
+ t = 10 / Math.sqrt(v[0]*v[0]+v[1]*v[1])
77
+ v = [ v[0]*t, v[1]*t ]
78
+ m = [ (x2+x1)/2, (y2+y1)/2 ]
79
+ x1, y1 = [m[0] + v[1], m[1] - v[0]]
80
+ x2, y2 = [m[0] - v[1], m[1] + v[0]]
81
+ if @type == LOCKED_DOOR
82
+ pdf.move_to(x1, y1)
83
+ pdf.line_to(x2, y2).stroke
84
+ else
85
+ s = PDF::Writer::StrokeStyle.new(1,
86
+ :cap => :butt,
87
+ :join => :miter,
88
+ :dash => PDF::Writer::StrokeStyle::SOLID_LINE )
89
+ pdf.stroke_style(s)
90
+ v = [ v[0] / 3, v[1] / 3]
91
+ pdf.move_to(x1 - v[0], y1 - v[1])
92
+ pdf.line_to(x1 + v[0], y1 + v[1])
93
+ pdf.line_to(x2 + v[0], y2 + v[1])
94
+ pdf.line_to(x2 - v[0], y2 - v[1])
95
+ pdf.line_to(x1 - v[0], y1 - v[1])
96
+ pdf.stroke
97
+ s = PDF::Writer::StrokeStyle.new(2,
98
+ :cap => :butt,
99
+ :join => :miter,
100
+ :dash => PDF::Writer::StrokeStyle::SOLID_LINE )
101
+ pdf.stroke_style(s)
102
+ end
103
+ end
104
+
74
105
  def pdf_draw_complex( pdf, opts )
75
106
  if opts['Paths as Curves']
76
107
  p = pdf_draw_complex_as_bspline( pdf, opts )
@@ -85,19 +116,11 @@ class FXConnection
85
116
  x2, y2 = [p[-1][0], p[-1][1]]
86
117
  pdf_draw_arrow(pdf, opts, x1, y1, x2, y2)
87
118
 
88
- if @type == LOCKED_DOOR
119
+ if @type == LOCKED_DOOR or @type == CLOSED_DOOR
89
120
  t = p.size / 2
90
121
  x1, y1 = [ p[t].x, p[t].y ]
91
122
  x2, y2 = [ p[t-2].x, p[t-2].y ]
92
-
93
- v = [ (x2-x1), (y2-y1) ]
94
- t = 10 / Math.sqrt(v[0]*v[0]+v[1]*v[1])
95
- v = [ v[0]*t, v[1]*t ]
96
- m = [ (x2+x1)/2, (y2+y1)/2 ]
97
- x1, y1 = [m[0] + v[1], m[1] - v[0]]
98
- x2, y2 = [m[0] - v[1], m[1] + v[0]]
99
- pdf.move_to(x1, y1)
100
- pdf.line_to(x2, y2).stroke
123
+ pdf_draw_door(pdf, x1, y1, x2, y2)
101
124
  end
102
125
  end
103
126
 
@@ -110,13 +133,8 @@ class FXConnection
110
133
  pdf.move_to(x1, y1)
111
134
  pdf.line_to(x2, y2).stroke
112
135
  pdf_draw_arrow(pdf, opts, x1, y1, x2, y2)
113
- if @type == LOCKED_DOOR
114
- v = [ (x2-x1)*0.25, (y2-y1)*0.25 ]
115
- m = [ (x2+x1)/2, (y2+y1)/2 ]
116
- x1, y1 = [m[0] + v[1], m[1] - v[0]]
117
- x2, y2 = [m[0] - v[1], m[1] + v[0]]
118
- pdf.move_to(x1, y1)
119
- pdf.line_to(x2, y2).stroke
136
+ if @type == LOCKED_DOOR or @type == CLOSED_DOOR
137
+ pdf_draw_door(pdf, x1, y1, x2, y2)
120
138
  end
121
139
  end
122
140