ruby-doom 0.9.0
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.
- data/History.txt +4 -0
- data/Manifest.txt +7 -0
- data/README.txt +45 -0
- data/Rakefile +11 -0
- data/bin/bsp +0 -0
- data/bin/introspector +0 -0
- data/lib/ruby-doom.rb +920 -0
- metadata +75 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
data/README.txt
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
= ruby_doom
|
|
2
|
+
|
|
3
|
+
* http://ruby-doom.rubyforge.org
|
|
4
|
+
|
|
5
|
+
== DESCRIPTION:
|
|
6
|
+
|
|
7
|
+
Generates DOOM maps.
|
|
8
|
+
|
|
9
|
+
== FEATURES/PROBLEMS:
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
== SYNOPSIS:
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
== REQUIREMENTS:
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
== INSTALL:
|
|
19
|
+
|
|
20
|
+
sudo gem install ruby-doom
|
|
21
|
+
|
|
22
|
+
== LICENSE:
|
|
23
|
+
|
|
24
|
+
(The MIT License)
|
|
25
|
+
|
|
26
|
+
Copyright (c) 2009 Tom Copeland
|
|
27
|
+
|
|
28
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
29
|
+
a copy of this software and associated documentation files (the
|
|
30
|
+
'Software'), to deal in the Software without restriction, including
|
|
31
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
32
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
33
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
34
|
+
the following conditions:
|
|
35
|
+
|
|
36
|
+
The above copyright notice and this permission notice shall be
|
|
37
|
+
included in all copies or substantial portions of the Software.
|
|
38
|
+
|
|
39
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
40
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
41
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
42
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
43
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
44
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
45
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/bin/bsp
ADDED
|
Binary file
|
data/bin/introspector
ADDED
|
Binary file
|
data/lib/ruby-doom.rb
ADDED
|
@@ -0,0 +1,920 @@
|
|
|
1
|
+
#!/usr/local/bin/ruby
|
|
2
|
+
|
|
3
|
+
class RubyDoom
|
|
4
|
+
VERSION = '0.9.0'
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
class RGBQuad
|
|
8
|
+
def initialize(bytes)
|
|
9
|
+
@r,@g,@b,@res = *bytes
|
|
10
|
+
end
|
|
11
|
+
def to_s
|
|
12
|
+
return @r.to_s + "," + @g.to_s + "," + @b.to_s
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Finder
|
|
17
|
+
def initialize(points, max_radius=5, debug=false)
|
|
18
|
+
@debug=debug
|
|
19
|
+
@max_radius = max_radius
|
|
20
|
+
@points = points
|
|
21
|
+
end
|
|
22
|
+
def next(current, sofar)
|
|
23
|
+
1.upto(@max_radius) do |x|
|
|
24
|
+
points_at_radius(current, x).each {|p| return p if good(p, sofar) }
|
|
25
|
+
end
|
|
26
|
+
raise "Couldn't find next point!"
|
|
27
|
+
end
|
|
28
|
+
def points_at_radius(p, r)
|
|
29
|
+
res = []
|
|
30
|
+
puts "checking for points around " + p.to_s + " at radius " + r.to_s unless !@debug
|
|
31
|
+
# move up and then west to the upper left hand corner of the search box
|
|
32
|
+
p = p.translate(-r, r)
|
|
33
|
+
res << p
|
|
34
|
+
# move east
|
|
35
|
+
1.upto(r*2) {
|
|
36
|
+
p = p.translate(1,0)
|
|
37
|
+
res << p
|
|
38
|
+
}
|
|
39
|
+
# move south
|
|
40
|
+
1.upto(r*2) {
|
|
41
|
+
p = p.translate(0,-1)
|
|
42
|
+
res << p
|
|
43
|
+
}
|
|
44
|
+
# move west
|
|
45
|
+
1.upto(r*2) {
|
|
46
|
+
p = p.translate(-1,0)
|
|
47
|
+
res << p
|
|
48
|
+
}
|
|
49
|
+
# move north
|
|
50
|
+
1.upto((r*2)-1) {
|
|
51
|
+
p = p.translate(0,1)
|
|
52
|
+
res << p
|
|
53
|
+
}
|
|
54
|
+
puts "points array = " + res.to_s unless !@debug
|
|
55
|
+
res
|
|
56
|
+
end
|
|
57
|
+
def good(candidate, sofar)
|
|
58
|
+
puts "Testing " + candidate.to_s unless !@debug
|
|
59
|
+
@points.include?(candidate) && !sofar.include?(candidate)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class PointThinner
|
|
64
|
+
def initialize(p, factor)
|
|
65
|
+
@points = p
|
|
66
|
+
@factor = factor
|
|
67
|
+
end
|
|
68
|
+
def thin
|
|
69
|
+
newline = [@points[0]]
|
|
70
|
+
1.upto(@points.size-1) {|x| newline << @points[x] if x % @factor == 0 }
|
|
71
|
+
newline
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
class PointsToLine
|
|
76
|
+
def initialize(points, debug=false)
|
|
77
|
+
@points = points
|
|
78
|
+
@debug = debug
|
|
79
|
+
end
|
|
80
|
+
def lower_left
|
|
81
|
+
@points.min {|a,b| a.distance_to(Point.new(0,0)) <=> b.distance_to(Point.new(0,0)) }
|
|
82
|
+
end
|
|
83
|
+
def line
|
|
84
|
+
@f = Finder.new(@points, 5, @debug)
|
|
85
|
+
found_so_far = [lower_left]
|
|
86
|
+
current = @f.next(found_so_far[0], found_so_far)
|
|
87
|
+
while found_so_far.size != @points.size - 1
|
|
88
|
+
found_so_far << current
|
|
89
|
+
puts "Current = " + current.to_s + "; points so far: " + found_so_far.size.to_s unless !@debug
|
|
90
|
+
begin
|
|
91
|
+
current = @f.next(current, found_so_far)
|
|
92
|
+
rescue
|
|
93
|
+
puts "Couldn't find next point, so skipping back to the origin" unless !@debug
|
|
94
|
+
break
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
found_so_far << current
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
class ArrayToPoints
|
|
102
|
+
def initialize(width, height, raw_data)
|
|
103
|
+
@width = width
|
|
104
|
+
@height = height
|
|
105
|
+
@raw_data = raw_data
|
|
106
|
+
end
|
|
107
|
+
def points
|
|
108
|
+
pts = []
|
|
109
|
+
# for each byte in the image
|
|
110
|
+
idx = 0
|
|
111
|
+
@raw_data.each do |byte|
|
|
112
|
+
# for each bit in the image
|
|
113
|
+
0.upto(7) do |bit|
|
|
114
|
+
if (byte & 128 >> bit) == 0
|
|
115
|
+
tmp_pt = convert(idx, @width)
|
|
116
|
+
pts << Point.new(tmp_pt.x, @height-1-tmp_pt.y)
|
|
117
|
+
end
|
|
118
|
+
idx += 1
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
return pts
|
|
122
|
+
end
|
|
123
|
+
# converts an index into an array of width blah to a point on an x/y coordinate plane
|
|
124
|
+
# with 0,0 in the upper left hand corner
|
|
125
|
+
# so in an array that's 8 units wide, unit 12 converts to a point 4 across, 1 down
|
|
126
|
+
def convert(idx, width)
|
|
127
|
+
Point.new(idx % width, idx/width)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
class BMPDecoder
|
|
132
|
+
attr_reader :type, :size, :offset_to_image_data, :info_header_size, :width, :height, :bit_planes, :bits_per_pixel, :compression, :size_of_image, :xpixels_per_meter, :ypixels_per_meter, :colors_used, :colors_important
|
|
133
|
+
attr_accessor :scale_factor, :thinning_factor
|
|
134
|
+
def initialize(filename, debug=false)
|
|
135
|
+
@debug = debug
|
|
136
|
+
@filename = filename
|
|
137
|
+
@scale_factor = 1
|
|
138
|
+
@thinning_factor = 5
|
|
139
|
+
end
|
|
140
|
+
def decode
|
|
141
|
+
# hm, wouldn't File.read(filename, "rb") do this?
|
|
142
|
+
bytes = []
|
|
143
|
+
File.open(@filename, "r").each_byte {|x| bytes << x }
|
|
144
|
+
|
|
145
|
+
# decode BITMAPFILEHEADER
|
|
146
|
+
@type = decode_word(bytes.slice(0,2))
|
|
147
|
+
raise "Can only decode bitmaps" unless @type == 19778
|
|
148
|
+
|
|
149
|
+
@size = decode_dword(bytes.slice(2,4))
|
|
150
|
+
res1 = decode_word(bytes.slice(6,2))
|
|
151
|
+
res1 = decode_word(bytes.slice(8,2))
|
|
152
|
+
@offset_to_image_data = decode_dword(bytes.slice(10,4))
|
|
153
|
+
|
|
154
|
+
# decode BITMAPINFOHEADER
|
|
155
|
+
@info_header_size = decode_dword(bytes.slice(14,4))
|
|
156
|
+
@width = decode_dword(bytes.slice(18,4)) # specified as a LONG... but DWORD works... (?)
|
|
157
|
+
@height = decode_dword(bytes.slice(22,4)) # specified as a LONG... but DWORD works... (?)
|
|
158
|
+
@bit_planes = decode_word(bytes.slice(26,2))
|
|
159
|
+
@bits_per_pixel = decode_word(bytes.slice(28,2))
|
|
160
|
+
raise "Can't process bitmaps with more than one bit per pixel. Save it as a monochrome bitmap to fix this." unless @bits_per_pixel == 1
|
|
161
|
+
|
|
162
|
+
@compression = decode_dword(bytes.slice(30,4))
|
|
163
|
+
raise "Can't process compressed bitmaps" unless @compression == 0
|
|
164
|
+
|
|
165
|
+
@size_of_image = decode_dword(bytes.slice(34,4))
|
|
166
|
+
@xpixels_per_meter = decode_dword(bytes.slice(38,4)) # specified as a LONG... but DWORD works... (?)
|
|
167
|
+
@ypixels_per_meter = decode_dword(bytes.slice(42,4)) # specified as a LONG... but DWORD works... (?)
|
|
168
|
+
@colors_used = decode_dword(bytes.slice(46,4))
|
|
169
|
+
@colors_important = decode_dword(bytes.slice(50,4))
|
|
170
|
+
|
|
171
|
+
# color table - assume monochrome bitmap for now
|
|
172
|
+
rgb1 = RGBQuad.new(bytes.slice(54,4))
|
|
173
|
+
rgb2 = RGBQuad.new(bytes.slice(58,4))
|
|
174
|
+
|
|
175
|
+
@raw_image = bytes.slice(62, bytes.size-62)
|
|
176
|
+
self
|
|
177
|
+
end
|
|
178
|
+
def raw_points
|
|
179
|
+
decode if @raw_image.nil?
|
|
180
|
+
ArrayToPoints.new(@width, @height, @raw_image).points
|
|
181
|
+
end
|
|
182
|
+
def line
|
|
183
|
+
PointsToLine.new(raw_points, @debug).line
|
|
184
|
+
end
|
|
185
|
+
def thin
|
|
186
|
+
pts = PointThinner.new(line, @thinning_factor).thin
|
|
187
|
+
pts.collect {|p| p.scale(@scale_factor) }
|
|
188
|
+
end
|
|
189
|
+
def decode_word(bytes)
|
|
190
|
+
bytes.pack("C2").unpack("S")[0]
|
|
191
|
+
end
|
|
192
|
+
def decode_dword(bytes)
|
|
193
|
+
bytes.pack("C4").unpack("L")[0]
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
class Codec
|
|
198
|
+
# Accepts a format string like "sl48" and a byte array
|
|
199
|
+
# s - short
|
|
200
|
+
# l - long
|
|
201
|
+
# 4 - 4 byte string
|
|
202
|
+
# 8 - 8 bytes string
|
|
203
|
+
def Codec.decode(format, bytes)
|
|
204
|
+
res = []
|
|
205
|
+
ptr = 0
|
|
206
|
+
format.split(//).each {|x|
|
|
207
|
+
if x == "s"
|
|
208
|
+
res << bytes.slice(ptr,2).pack("c2").unpack("s")[0]
|
|
209
|
+
ptr += 2
|
|
210
|
+
elsif x == "l"
|
|
211
|
+
res << bytes.slice(ptr,4).pack("C4").unpack("V")[0]
|
|
212
|
+
ptr += 4
|
|
213
|
+
elsif x == "4"
|
|
214
|
+
res << Codec.unmarshal_string(bytes.slice(ptr,4))
|
|
215
|
+
ptr += 4
|
|
216
|
+
elsif x == "8"
|
|
217
|
+
res << Codec.unmarshal_string(bytes.slice(ptr,8))
|
|
218
|
+
ptr += 8
|
|
219
|
+
else
|
|
220
|
+
raise "Unknown character in decode format string " + format
|
|
221
|
+
end
|
|
222
|
+
}
|
|
223
|
+
return res
|
|
224
|
+
end
|
|
225
|
+
# Accepts a format string like "sl48" and an array of values
|
|
226
|
+
def Codec.encode(format, values)
|
|
227
|
+
bytes = []
|
|
228
|
+
ptr = 0
|
|
229
|
+
format.split(//).each {|x|
|
|
230
|
+
if x == "s"
|
|
231
|
+
bytes += [values[ptr]].pack("S").unpack("C2")
|
|
232
|
+
elsif x == "l"
|
|
233
|
+
bytes += [values[ptr]].pack("N").unpack("C4").reverse
|
|
234
|
+
elsif x == "4"
|
|
235
|
+
bytes += Codec.marshal_string(values[ptr],4)
|
|
236
|
+
elsif x == "8"
|
|
237
|
+
bytes += Codec.marshal_string(values[ptr],8)
|
|
238
|
+
else
|
|
239
|
+
raise "Unknown character in decode format string " + format
|
|
240
|
+
end
|
|
241
|
+
ptr += 1
|
|
242
|
+
}
|
|
243
|
+
return bytes
|
|
244
|
+
end
|
|
245
|
+
def Codec.unmarshal_string(a)
|
|
246
|
+
a.pack("C*").strip
|
|
247
|
+
end
|
|
248
|
+
def Codec.marshal_string(n,len)
|
|
249
|
+
arr = n.unpack("C#{len}").compact
|
|
250
|
+
if arr.size < len
|
|
251
|
+
arr += Array.new(len-arr.size, 0)
|
|
252
|
+
end
|
|
253
|
+
arr
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
class ThingInfo
|
|
258
|
+
attr_reader :id, :name, :symbol
|
|
259
|
+
def initialize(id,name,symbol)
|
|
260
|
+
@id, @name, @symbol = id,name,symbol
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
class Dictionary
|
|
265
|
+
def Dictionary.get
|
|
266
|
+
if @self.nil?
|
|
267
|
+
@self = Dictionary.new
|
|
268
|
+
end
|
|
269
|
+
return @self
|
|
270
|
+
end
|
|
271
|
+
def initialize
|
|
272
|
+
@things = []
|
|
273
|
+
@things << ThingInfo.new(1, "Player 1", "@")
|
|
274
|
+
@things << ThingInfo.new(9, "Sergeant", "s")
|
|
275
|
+
@things << ThingInfo.new(65, "Commando", "c")
|
|
276
|
+
@things << ThingInfo.new(2001, "Shotgun", "g")
|
|
277
|
+
@things << ThingInfo.new(3001, "Imp", "i")
|
|
278
|
+
@things << ThingInfo.new(2035, "Barrel", "b")
|
|
279
|
+
end
|
|
280
|
+
def thing_for_type_id(id)
|
|
281
|
+
t = @things.find {|x| x.id == id}
|
|
282
|
+
return ThingInfo.new(-999, "Unknown thing", "?") if t.nil?
|
|
283
|
+
t
|
|
284
|
+
end
|
|
285
|
+
def thing_for_name(name)
|
|
286
|
+
@things.find {|x| x.name == name}
|
|
287
|
+
end
|
|
288
|
+
def direction_for_angle(angle)
|
|
289
|
+
case angle
|
|
290
|
+
when (0..45 or 316..360) then return "east"
|
|
291
|
+
when 46..135 then return "north"
|
|
292
|
+
when 136..225 then return "west"
|
|
293
|
+
when 225..315 then return "south"
|
|
294
|
+
end
|
|
295
|
+
raise "Angle must be between 0 and 360"
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
class Point
|
|
300
|
+
attr_accessor :x, :y
|
|
301
|
+
def initialize(x,y)
|
|
302
|
+
@x=x
|
|
303
|
+
@y=y
|
|
304
|
+
end
|
|
305
|
+
def scale(factor)
|
|
306
|
+
Point.new(x * factor, y * factor)
|
|
307
|
+
end
|
|
308
|
+
def ==(other)
|
|
309
|
+
return other.x == @x && other.y == @y
|
|
310
|
+
end
|
|
311
|
+
def slope_to(p1)
|
|
312
|
+
if (p1.x - @x) == 0
|
|
313
|
+
return nil
|
|
314
|
+
end
|
|
315
|
+
(p1.y - @y) / (p1.x - @x)
|
|
316
|
+
end
|
|
317
|
+
def distance_to(p1)
|
|
318
|
+
Math.sqrt(((p1.x - @x) ** 2) + ((p1.y - @y) ** 2))
|
|
319
|
+
end
|
|
320
|
+
def lineto(p1)
|
|
321
|
+
res = []
|
|
322
|
+
current_x = @x
|
|
323
|
+
current_y = @y
|
|
324
|
+
slope = slope_to(p1)
|
|
325
|
+
res << Point.new(current_x, current_y)
|
|
326
|
+
distance_to(p1).to_i.times {|s|
|
|
327
|
+
if slope == 0
|
|
328
|
+
current_x += (p1.x < @x ? -1 : 1)
|
|
329
|
+
res << Point.new(current_x, current_y)
|
|
330
|
+
next
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
if slope == nil
|
|
334
|
+
current_y += (p1.y < @y ? -1 : 1)
|
|
335
|
+
res << Point.new(current_x, current_y)
|
|
336
|
+
next
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
current_x += slope
|
|
340
|
+
cur += (1-slope)
|
|
341
|
+
res << Point.new(current_x, current_y)
|
|
342
|
+
}
|
|
343
|
+
res << p1
|
|
344
|
+
return res
|
|
345
|
+
end
|
|
346
|
+
def translate(x,y)
|
|
347
|
+
Point.new(@x + x, @y + y)
|
|
348
|
+
end
|
|
349
|
+
def to_s
|
|
350
|
+
"(" + @x.to_s + "," + @y.to_s + ")"
|
|
351
|
+
end
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
class Lump
|
|
355
|
+
attr_reader :name
|
|
356
|
+
def initialize(name)
|
|
357
|
+
@name = name
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
class DecodedLump < Lump
|
|
362
|
+
attr_reader :items
|
|
363
|
+
def initialize(name)
|
|
364
|
+
super(name)
|
|
365
|
+
@items = []
|
|
366
|
+
@index = 0
|
|
367
|
+
end
|
|
368
|
+
def add(i)
|
|
369
|
+
i.id = @index
|
|
370
|
+
@index += 1
|
|
371
|
+
@items << i
|
|
372
|
+
return i
|
|
373
|
+
end
|
|
374
|
+
def write
|
|
375
|
+
out = []
|
|
376
|
+
@items.each {|i| out += i.write }
|
|
377
|
+
out
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
class UndecodedLump < Lump
|
|
382
|
+
def initialize(name)
|
|
383
|
+
super(name)
|
|
384
|
+
@bytes = []
|
|
385
|
+
end
|
|
386
|
+
def read(bytes)
|
|
387
|
+
@bytes = bytes
|
|
388
|
+
end
|
|
389
|
+
def write
|
|
390
|
+
@bytes
|
|
391
|
+
end
|
|
392
|
+
def size
|
|
393
|
+
@bytes.size
|
|
394
|
+
end
|
|
395
|
+
def items
|
|
396
|
+
[]
|
|
397
|
+
end
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
class Sectors < DecodedLump
|
|
401
|
+
BYTES_EACH=26
|
|
402
|
+
NAME="SECTORS"
|
|
403
|
+
def initialize
|
|
404
|
+
super(NAME)
|
|
405
|
+
end
|
|
406
|
+
def read(bytes)
|
|
407
|
+
(bytes.size / BYTES_EACH).times {|index|
|
|
408
|
+
s = Sector.new
|
|
409
|
+
s.read(bytes.slice(index*BYTES_EACH, BYTES_EACH))
|
|
410
|
+
@items << s
|
|
411
|
+
}
|
|
412
|
+
end
|
|
413
|
+
def size
|
|
414
|
+
@items.size * BYTES_EACH
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
class Sector
|
|
419
|
+
FORMAT="ss88sss"
|
|
420
|
+
attr_accessor :floor_height, :ceiling_height, :floor_texture, :ceiling_texture, :light_level, :special, :tag, :id
|
|
421
|
+
def initialize
|
|
422
|
+
@floor_height, @ceiling_height, @floor_texture, @ceiling_texture, @light_level, @special, @tag = 0, 128, "FLAT14", "FLAT14", 160, 0, 0
|
|
423
|
+
end
|
|
424
|
+
def read(bytes)
|
|
425
|
+
@floor_height, @ceiling_height, @floor_texture, @ceiling_texture, @light_level, @special, @tag = Codec.decode(FORMAT, bytes)
|
|
426
|
+
end
|
|
427
|
+
def write
|
|
428
|
+
Codec.encode(FORMAT, [@floor_height, @ceiling_height, @floor_texture, @ceiling_texture, @light_level, @special, @tag])
|
|
429
|
+
end
|
|
430
|
+
def to_s
|
|
431
|
+
" Sector floor/ceiling heights " + @floor_height.to_s + "/" + @ceiling_height.to_s + "; floor/ceiling textures " + @floor_texture.to_s + "/" + @ceiling_texture.to_s + "; light = " + @light_level.to_s + "; special = " + @special.to_s + "; tag = " + @tag.to_s
|
|
432
|
+
end
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
class Vertexes < DecodedLump
|
|
436
|
+
BYTES_EACH=4
|
|
437
|
+
NAME="VERTEXES"
|
|
438
|
+
def initialize
|
|
439
|
+
super(NAME)
|
|
440
|
+
end
|
|
441
|
+
def read(bytes)
|
|
442
|
+
(bytes.size / BYTES_EACH).times {|index|
|
|
443
|
+
v = Vertex.new
|
|
444
|
+
v.read(bytes.slice(index*BYTES_EACH, BYTES_EACH))
|
|
445
|
+
@items << v
|
|
446
|
+
}
|
|
447
|
+
end
|
|
448
|
+
def size
|
|
449
|
+
@items.size * BYTES_EACH
|
|
450
|
+
end
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
class Vertex
|
|
454
|
+
FORMAT="ss"
|
|
455
|
+
attr_reader :location
|
|
456
|
+
attr_accessor :id
|
|
457
|
+
def initialize(location=nil)
|
|
458
|
+
@location = location
|
|
459
|
+
end
|
|
460
|
+
def read(bytes)
|
|
461
|
+
@location = Point.new(*Codec.decode(FORMAT, bytes))
|
|
462
|
+
end
|
|
463
|
+
def write
|
|
464
|
+
Codec.encode(FORMAT, [@location.x, @location.y])
|
|
465
|
+
end
|
|
466
|
+
def to_s
|
|
467
|
+
" Vertex at " + @location.to_s
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
class Sidedefs < DecodedLump
|
|
472
|
+
BYTES_EACH=30
|
|
473
|
+
NAME="SIDEDEFS"
|
|
474
|
+
def initialize
|
|
475
|
+
super(NAME)
|
|
476
|
+
end
|
|
477
|
+
def read(bytes)
|
|
478
|
+
(bytes.size / BYTES_EACH).times {|index|
|
|
479
|
+
s = Sidedef.new
|
|
480
|
+
s.read(bytes.slice(index*BYTES_EACH, BYTES_EACH))
|
|
481
|
+
@items << s
|
|
482
|
+
}
|
|
483
|
+
end
|
|
484
|
+
def size
|
|
485
|
+
@items.size * BYTES_EACH
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
class Sidedef
|
|
490
|
+
FORMAT="ss888s"
|
|
491
|
+
attr_accessor :x_offset, :y_offset, :upper_texture, :lower_texture, :middle_texture, :sector_id, :id
|
|
492
|
+
def initialize()
|
|
493
|
+
@x_offset=0
|
|
494
|
+
@y_offset=0
|
|
495
|
+
@upper_texture="-"
|
|
496
|
+
@lower_texture="-"
|
|
497
|
+
@middle_texture="BROWN96"
|
|
498
|
+
@sector_id=0
|
|
499
|
+
end
|
|
500
|
+
def read(bytes)
|
|
501
|
+
@x_offset, @y_offset, @upper_texture, @lower_texture, @middle_texture, @sector_id = Codec.decode(FORMAT, bytes)
|
|
502
|
+
end
|
|
503
|
+
def write
|
|
504
|
+
Codec.encode(FORMAT, [@x_offset, @y_offset, @upper_texture, @lower_texture, @middle_texture, @sector_id])
|
|
505
|
+
end
|
|
506
|
+
def to_s
|
|
507
|
+
" Sidedef for sector " + @sector_id.to_s + "; upper/lower/middle textures are " + @upper_texture + "/" + @lower_texture + "/" + @middle_texture + " with offsets of " + @x_offset.to_s + "/" + @y_offset.to_s
|
|
508
|
+
end
|
|
509
|
+
end
|
|
510
|
+
|
|
511
|
+
class Things < DecodedLump
|
|
512
|
+
BYTES_EACH=10
|
|
513
|
+
NAME="THINGS"
|
|
514
|
+
def initialize
|
|
515
|
+
super(NAME)
|
|
516
|
+
end
|
|
517
|
+
def read(bytes)
|
|
518
|
+
(bytes.size / BYTES_EACH).times {|index|
|
|
519
|
+
thing = Thing.new
|
|
520
|
+
thing.read(bytes.slice(index*BYTES_EACH, BYTES_EACH))
|
|
521
|
+
@items << thing
|
|
522
|
+
}
|
|
523
|
+
end
|
|
524
|
+
def size
|
|
525
|
+
@items.size * BYTES_EACH
|
|
526
|
+
end
|
|
527
|
+
def add_sergeant(p)
|
|
528
|
+
items << Thing.new(p, Dictionary.get.thing_for_name("Sergeant").id)
|
|
529
|
+
end
|
|
530
|
+
def add_commando(p)
|
|
531
|
+
items << Thing.new(p, Dictionary.get.thing_for_name("Commando").id)
|
|
532
|
+
end
|
|
533
|
+
def add_barrel(p)
|
|
534
|
+
items << Thing.new(p, Dictionary.get.thing_for_name("Barrel").id)
|
|
535
|
+
end
|
|
536
|
+
def add_shotgun(p)
|
|
537
|
+
items << Thing.new(p, Dictionary.get.thing_for_name("Shotgun").id)
|
|
538
|
+
end
|
|
539
|
+
def add_imp(p)
|
|
540
|
+
items << Thing.new(p, Dictionary.get.thing_for_name("Imp").id)
|
|
541
|
+
end
|
|
542
|
+
def add_player(p)
|
|
543
|
+
items << Thing.new(p, Dictionary.get.thing_for_name("Player 1").id)
|
|
544
|
+
end
|
|
545
|
+
def player
|
|
546
|
+
@items.find {|t| t.type_id == Dictionary.get.thing_for_name("Player 1").id}
|
|
547
|
+
end
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
class Thing
|
|
551
|
+
attr_reader :type_id, :location
|
|
552
|
+
attr_accessor :facing_angle, :id
|
|
553
|
+
def initialize(p=nil,type_id=0)
|
|
554
|
+
@location = p
|
|
555
|
+
@facing_angle = 0
|
|
556
|
+
@type_id = type_id
|
|
557
|
+
@flags = 7
|
|
558
|
+
end
|
|
559
|
+
def read(bytes)
|
|
560
|
+
x, y, @facing_angle, @type_id, @flags = Codec.decode("sssss", bytes)
|
|
561
|
+
@location = Point.new(x,y)
|
|
562
|
+
end
|
|
563
|
+
def write
|
|
564
|
+
Codec.encode("sssss", [@location.x, @location.y, @facing_angle, @type_id, @flags])
|
|
565
|
+
end
|
|
566
|
+
def to_s
|
|
567
|
+
Dictionary.get.thing_for_type_id(@type_id).name + " at " + @location.to_s + " facing " + Dictionary.get.direction_for_angle(@facing_angle) + "; flags = " + @flags.to_s
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
class Linedefs < DecodedLump
|
|
572
|
+
BYTES_EACH=14
|
|
573
|
+
NAME="LINEDEFS"
|
|
574
|
+
def initialize
|
|
575
|
+
super(NAME)
|
|
576
|
+
end
|
|
577
|
+
def read(bytes)
|
|
578
|
+
(bytes.size / BYTES_EACH).times {|index|
|
|
579
|
+
linedef = Linedef.new
|
|
580
|
+
linedef.read(bytes.slice(index*BYTES_EACH, BYTES_EACH))
|
|
581
|
+
@items << linedef
|
|
582
|
+
}
|
|
583
|
+
end
|
|
584
|
+
def size
|
|
585
|
+
@items.size * BYTES_EACH
|
|
586
|
+
end
|
|
587
|
+
end
|
|
588
|
+
|
|
589
|
+
class Linedef
|
|
590
|
+
FORMAT="sssssss"
|
|
591
|
+
attr_accessor :start_vertex, :end_vertex, :attributes, :special_effects_type, :right_sidedef, :left_sidedef, :id
|
|
592
|
+
def initialize(v1=0,v2=0,s=0)
|
|
593
|
+
@start_vertex=v1
|
|
594
|
+
@end_vertex=v2
|
|
595
|
+
@attributes=1
|
|
596
|
+
@special_effects_type=0
|
|
597
|
+
@right_sidedef=s
|
|
598
|
+
@left_sidedef=-1
|
|
599
|
+
end
|
|
600
|
+
def read(bytes)
|
|
601
|
+
@start_vertex, @end_vertex, @attributes, @special_effects_type, @tag, @right_sidedef, @left_sidedef = Codec.decode(FORMAT, bytes)
|
|
602
|
+
end
|
|
603
|
+
def write
|
|
604
|
+
Codec.encode(FORMAT, [@start_vertex.id, @end_vertex.id, @attributes, @special_effects_type, @tag, @right_sidedef.id, -1])
|
|
605
|
+
end
|
|
606
|
+
def to_s
|
|
607
|
+
"Linedef from " + @start_vertex.to_s + " to " + @end_vertex.to_s + "; attribute flag is " + @attributes.to_s + "; special fx is " + @special_effects_type.to_s + "; tag is " + @tag.to_s + "; right sidedef is " + @right_sidedef.to_s + "; left sidedef is " + @left_sidedef.to_s
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
class DirectoryEntry
|
|
612
|
+
BYTES_EACH=16
|
|
613
|
+
attr_accessor :offset, :size, :name
|
|
614
|
+
def initialize(offset=nil,size=nil,name=nil)
|
|
615
|
+
@offset = offset
|
|
616
|
+
@size = size
|
|
617
|
+
@name = name
|
|
618
|
+
end
|
|
619
|
+
def read(array)
|
|
620
|
+
@offset, @size, @name = Codec.decode("ll8", array)
|
|
621
|
+
end
|
|
622
|
+
def write
|
|
623
|
+
Codec.encode("ll8", [@offset, @size, @name])
|
|
624
|
+
end
|
|
625
|
+
def create_lump(bytes)
|
|
626
|
+
lump=nil
|
|
627
|
+
if @name == Things::NAME
|
|
628
|
+
lump=Things.new
|
|
629
|
+
elsif @name == Linedefs::NAME
|
|
630
|
+
lump=Linedefs.new
|
|
631
|
+
elsif @name == Sidedefs::NAME
|
|
632
|
+
lump=Sidedefs.new
|
|
633
|
+
elsif @name == Vertexes::NAME
|
|
634
|
+
lump=Vertexes.new
|
|
635
|
+
elsif @name == Sectors::NAME
|
|
636
|
+
lump=Sectors.new
|
|
637
|
+
else
|
|
638
|
+
lump=UndecodedLump.new(@name)
|
|
639
|
+
end
|
|
640
|
+
lump.read(bytes.slice(@offset, @size))
|
|
641
|
+
return lump
|
|
642
|
+
end
|
|
643
|
+
def to_s
|
|
644
|
+
@offset.to_s + "," + @size.to_s + "," + @name
|
|
645
|
+
end
|
|
646
|
+
end
|
|
647
|
+
|
|
648
|
+
class Header
|
|
649
|
+
BYTES_EACH=12
|
|
650
|
+
attr_reader :type
|
|
651
|
+
attr_accessor :directory_offset, :lump_count
|
|
652
|
+
def initialize(type=nil)
|
|
653
|
+
@type = type
|
|
654
|
+
end
|
|
655
|
+
def read(array)
|
|
656
|
+
@type, @lump_count, @directory_offset = Codec.decode("4ll", array)
|
|
657
|
+
end
|
|
658
|
+
def write
|
|
659
|
+
Codec.encode("4ll", [@type, @lump_count, @directory_offset])
|
|
660
|
+
end
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
class Wad
|
|
664
|
+
attr_reader :directory_offset, :header, :bytes, :lumps
|
|
665
|
+
def initialize(verbose=false)
|
|
666
|
+
@verbose = verbose
|
|
667
|
+
@bytes = []
|
|
668
|
+
@lumps = []
|
|
669
|
+
end
|
|
670
|
+
def read(filename)
|
|
671
|
+
puts "Reading WAD into memory" unless !@verbose
|
|
672
|
+
File.new(filename).each_byte {|b|
|
|
673
|
+
@bytes << b
|
|
674
|
+
puts "Read " + (@bytes.size/1000).to_s + " KB so far " unless (!@verbose or @bytes.size % 500000 != 0)
|
|
675
|
+
}
|
|
676
|
+
puts "Done reading, building the object model" unless !@verbose
|
|
677
|
+
@header = Header.new
|
|
678
|
+
@header.read(@bytes.slice(0,Header::BYTES_EACH))
|
|
679
|
+
@header.lump_count.times {|directory_entry_index|
|
|
680
|
+
de = DirectoryEntry.new
|
|
681
|
+
de.read(@bytes.slice((directory_entry_index*DirectoryEntry::BYTES_EACH)+@header.directory_offset,DirectoryEntry::BYTES_EACH))
|
|
682
|
+
lump = de.create_lump(@bytes)
|
|
683
|
+
puts "Created " + lump.name unless !@verbose
|
|
684
|
+
@lumps << lump
|
|
685
|
+
}
|
|
686
|
+
puts "Object model built" unless !@verbose
|
|
687
|
+
puts "The file " + filename + " is a " + @bytes.size.to_s + " byte " + @header.type unless !@verbose
|
|
688
|
+
puts "It's got " + @lumps.size.to_s + " lumps, the directory started at byte " + @header.directory_offset.to_s unless !@verbose
|
|
689
|
+
end
|
|
690
|
+
def player
|
|
691
|
+
@lumps.find {|x| x.name == "THINGS"}.player
|
|
692
|
+
end
|
|
693
|
+
def write(filename=nil)
|
|
694
|
+
puts "Writing WAD" unless !@verbose
|
|
695
|
+
out = []
|
|
696
|
+
ptr = Header::BYTES_EACH
|
|
697
|
+
entries = []
|
|
698
|
+
@lumps.each {|lump|
|
|
699
|
+
entries << DirectoryEntry.new(ptr, lump.size, lump.name)
|
|
700
|
+
out += lump.write
|
|
701
|
+
ptr += lump.size
|
|
702
|
+
}
|
|
703
|
+
entries.each {|e| out += e.write }
|
|
704
|
+
# now go back and fill in the directory offset in the header
|
|
705
|
+
h = Header.new("PWAD")
|
|
706
|
+
h.directory_offset = ptr
|
|
707
|
+
h.lump_count = @lumps.size
|
|
708
|
+
out = h.write + out
|
|
709
|
+
File.open(filename, "w") {|f| out.each {|b| f.putc(b) } } unless filename == nil
|
|
710
|
+
puts "Done" unless !@verbose
|
|
711
|
+
return out
|
|
712
|
+
end
|
|
713
|
+
end
|
|
714
|
+
|
|
715
|
+
class SimpleLineMap
|
|
716
|
+
attr_reader :path
|
|
717
|
+
def initialize(path)
|
|
718
|
+
@path = path
|
|
719
|
+
@things = Things.new
|
|
720
|
+
end
|
|
721
|
+
def set_player(p)
|
|
722
|
+
@things.add_player p
|
|
723
|
+
end
|
|
724
|
+
def add_barrel(p)
|
|
725
|
+
@things.add_barrel p
|
|
726
|
+
end
|
|
727
|
+
def add_sergeant(p)
|
|
728
|
+
@things.add_sergeant p
|
|
729
|
+
end
|
|
730
|
+
def add_commando(p)
|
|
731
|
+
@things.add_commando p
|
|
732
|
+
end
|
|
733
|
+
def add_imp(p)
|
|
734
|
+
@things.add_imp p
|
|
735
|
+
end
|
|
736
|
+
def add_shotgun(p)
|
|
737
|
+
@things.add_shotgun p
|
|
738
|
+
end
|
|
739
|
+
def nethack(size=Nethack::DEFAULT_SIZE)
|
|
740
|
+
n = Nethack.new(@path.start, size)
|
|
741
|
+
@path.visit(n)
|
|
742
|
+
@things.items.each {|t| n.thing(t) }
|
|
743
|
+
return n.render
|
|
744
|
+
end
|
|
745
|
+
def create_wad(filename)
|
|
746
|
+
w = Wad.new
|
|
747
|
+
w.lumps << UndecodedLump.new("MAP01")
|
|
748
|
+
w.lumps << @things
|
|
749
|
+
pc = PathCompiler.new(@path)
|
|
750
|
+
w.lumps.concat pc.lumps
|
|
751
|
+
w.write(filename)
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
class BMPMap
|
|
756
|
+
attr_accessor :scale_factor, :thinning_factor
|
|
757
|
+
def initialize(file)
|
|
758
|
+
@things = Things.new
|
|
759
|
+
@scale_factor = 1
|
|
760
|
+
@thinning_factor = 5
|
|
761
|
+
@bitmap_file = file
|
|
762
|
+
end
|
|
763
|
+
def set_player(p)
|
|
764
|
+
@things.add_player p
|
|
765
|
+
end
|
|
766
|
+
def add_sergeant(p)
|
|
767
|
+
@things.add_sergeant p
|
|
768
|
+
end
|
|
769
|
+
def add_commando(p)
|
|
770
|
+
@things.add_commando p
|
|
771
|
+
end
|
|
772
|
+
def add_imp(p)
|
|
773
|
+
@things.add_imp p
|
|
774
|
+
end
|
|
775
|
+
def add_shotgun(p)
|
|
776
|
+
@things.add_shotgun p
|
|
777
|
+
end
|
|
778
|
+
def create_wad(filename)
|
|
779
|
+
b = BMPDecoder.new(@bitmap_file)
|
|
780
|
+
b.scale_factor = @scale_factor
|
|
781
|
+
b.thinning_factor = @thinning_factor
|
|
782
|
+
w = Wad.new
|
|
783
|
+
w.lumps << UndecodedLump.new("MAP01")
|
|
784
|
+
w.lumps << @things
|
|
785
|
+
pc = PathCompiler.new(PointsPath.new(b.thin))
|
|
786
|
+
w.lumps.concat(pc.lumps)
|
|
787
|
+
w.write(filename)
|
|
788
|
+
end
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
class PathCompiler
|
|
792
|
+
def initialize(path)
|
|
793
|
+
@path = path
|
|
794
|
+
@sectors = Sectors.new
|
|
795
|
+
@sectors.add(Sector.new)
|
|
796
|
+
@vertexes = Vertexes.new
|
|
797
|
+
@vertexes.add(Vertex.new(@path.start))
|
|
798
|
+
@path.visit(self)
|
|
799
|
+
@sidedefs = Sidedefs.new
|
|
800
|
+
@path.segment_count.times do |v|
|
|
801
|
+
s = @sidedefs.add Sidedef.new
|
|
802
|
+
s.sector_id = @sectors.items[0].id
|
|
803
|
+
end
|
|
804
|
+
@linedefs = Linedefs.new
|
|
805
|
+
last = nil
|
|
806
|
+
@vertexes.items.each do |v|
|
|
807
|
+
if last.nil?
|
|
808
|
+
last = v
|
|
809
|
+
next
|
|
810
|
+
end
|
|
811
|
+
@linedefs.add Linedef.new(last, v, @sidedefs.items[last.id])
|
|
812
|
+
last = v
|
|
813
|
+
end
|
|
814
|
+
@linedefs.add(Linedef.new(@vertexes.items.last, @vertexes.items.first, @sidedefs.items.last))
|
|
815
|
+
end
|
|
816
|
+
def line_to(p)
|
|
817
|
+
@vertexes.add(Vertex.new(p)) if (@vertexes.items.find {|x| x.location == p }).nil?
|
|
818
|
+
end
|
|
819
|
+
def lumps
|
|
820
|
+
[@vertexes, @sectors, @linedefs, @sidedefs]
|
|
821
|
+
end
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
class PointsPath
|
|
825
|
+
def initialize(points)
|
|
826
|
+
@points = points
|
|
827
|
+
end
|
|
828
|
+
def segment_count
|
|
829
|
+
@points.size
|
|
830
|
+
end
|
|
831
|
+
def visit(visitor)
|
|
832
|
+
@points.each {|p| visitor.line_to(p) }
|
|
833
|
+
end
|
|
834
|
+
def start
|
|
835
|
+
@points[0]
|
|
836
|
+
end
|
|
837
|
+
end
|
|
838
|
+
|
|
839
|
+
class Path
|
|
840
|
+
attr_reader :path, :start
|
|
841
|
+
def initialize(startx, starty, path="")
|
|
842
|
+
@path = path
|
|
843
|
+
@start = Point.new(startx, starty)
|
|
844
|
+
end
|
|
845
|
+
def add(p,count=1)
|
|
846
|
+
count.times {@path += p }
|
|
847
|
+
end
|
|
848
|
+
def segments
|
|
849
|
+
@path.split(/\//)
|
|
850
|
+
end
|
|
851
|
+
def segment_count
|
|
852
|
+
segments.size
|
|
853
|
+
end
|
|
854
|
+
def visit(visitor)
|
|
855
|
+
cur = @start
|
|
856
|
+
segments.each do |x|
|
|
857
|
+
dir = x[0].chr
|
|
858
|
+
len = x.slice(1, x.length-1).to_i
|
|
859
|
+
if dir == "e"
|
|
860
|
+
cur = cur.translate(len, 0)
|
|
861
|
+
elsif dir == "n"
|
|
862
|
+
cur = cur.translate(0, len)
|
|
863
|
+
elsif dir == "w"
|
|
864
|
+
cur = cur.translate(-len, 0)
|
|
865
|
+
elsif dir == "s"
|
|
866
|
+
cur = cur.translate(0, -len)
|
|
867
|
+
else
|
|
868
|
+
raise "Unrecognized direction " + dir.to_s + " in segment " + x.to_s
|
|
869
|
+
end
|
|
870
|
+
visitor.line_to(cur)
|
|
871
|
+
end
|
|
872
|
+
end
|
|
873
|
+
def nethack(size=Nethack::DEFAULT_SIZE)
|
|
874
|
+
n = Nethack.new(@start, size)
|
|
875
|
+
visit(n)
|
|
876
|
+
return n.render
|
|
877
|
+
end
|
|
878
|
+
def to_s
|
|
879
|
+
@path
|
|
880
|
+
end
|
|
881
|
+
end
|
|
882
|
+
|
|
883
|
+
class Nethack
|
|
884
|
+
DEFAULT_SIZE=20
|
|
885
|
+
def initialize(start,size)
|
|
886
|
+
@start = start
|
|
887
|
+
@map = Array.new(size)
|
|
888
|
+
@map.each_index {|x| @map[x] = Array.new(size, ".") }
|
|
889
|
+
@prev = @start
|
|
890
|
+
end
|
|
891
|
+
def line_to(current)
|
|
892
|
+
@prev.lineto(current).each {|pt| @map[pt.y/100][pt.x/100] = "X" }
|
|
893
|
+
@prev = current
|
|
894
|
+
end
|
|
895
|
+
def thing(t)
|
|
896
|
+
puts "#{(t.location.y/100)},#{((@map.size-1) - (t.location.x/100))} -> #{Dictionary.get.thing_for_type_id(t.type_id).symbol}"
|
|
897
|
+
@map[t.location.y/100][(@map.size-1) - (t.location.x/100)] = Dictionary.get.thing_for_type_id(t.type_id).symbol
|
|
898
|
+
end
|
|
899
|
+
def render
|
|
900
|
+
res = ""
|
|
901
|
+
@map.each_index do |x|
|
|
902
|
+
@map[x].each_index do
|
|
903
|
+
|y| res << @map[@map.size-1-x][y-1] + " "
|
|
904
|
+
end
|
|
905
|
+
res << "\n"
|
|
906
|
+
end
|
|
907
|
+
res
|
|
908
|
+
end
|
|
909
|
+
end
|
|
910
|
+
|
|
911
|
+
if __FILE__ == $0
|
|
912
|
+
file = ARGV.include?("-f") ? ARGV[ARGV.index("-f") + 1] : "../test_wads/simple.wad"
|
|
913
|
+
w = Wad.new(ARGV.include?("-v"))
|
|
914
|
+
w.read(file)
|
|
915
|
+
w.lumps.each do |lump|
|
|
916
|
+
puts lump.name + " (" + lump.size.to_s + " bytes)"
|
|
917
|
+
lump.items.each { |t| puts " - " + t.to_s }
|
|
918
|
+
end
|
|
919
|
+
end
|
|
920
|
+
|
metadata
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ruby-doom
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.9.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Tom Copeland
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-09-18 00:00:00 -04:00
|
|
13
|
+
default_executable:
|
|
14
|
+
dependencies:
|
|
15
|
+
- !ruby/object:Gem::Dependency
|
|
16
|
+
name: hoe
|
|
17
|
+
type: :development
|
|
18
|
+
version_requirement:
|
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
20
|
+
requirements:
|
|
21
|
+
- - ">="
|
|
22
|
+
- !ruby/object:Gem::Version
|
|
23
|
+
version: 2.3.3
|
|
24
|
+
version:
|
|
25
|
+
description: Generates DOOM maps.
|
|
26
|
+
email:
|
|
27
|
+
- tom@infoether.com
|
|
28
|
+
executables:
|
|
29
|
+
- introspector
|
|
30
|
+
- bsp
|
|
31
|
+
extensions: []
|
|
32
|
+
|
|
33
|
+
extra_rdoc_files:
|
|
34
|
+
- History.txt
|
|
35
|
+
- Manifest.txt
|
|
36
|
+
- README.txt
|
|
37
|
+
files:
|
|
38
|
+
- History.txt
|
|
39
|
+
- Manifest.txt
|
|
40
|
+
- README.txt
|
|
41
|
+
- Rakefile
|
|
42
|
+
- bin/introspector
|
|
43
|
+
- bin/bsp
|
|
44
|
+
- lib/ruby-doom.rb
|
|
45
|
+
has_rdoc: true
|
|
46
|
+
homepage: http://ruby-doom.rubyforge.org
|
|
47
|
+
licenses: []
|
|
48
|
+
|
|
49
|
+
post_install_message:
|
|
50
|
+
rdoc_options:
|
|
51
|
+
- --main
|
|
52
|
+
- README.txt
|
|
53
|
+
require_paths:
|
|
54
|
+
- lib
|
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
56
|
+
requirements:
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: "0"
|
|
60
|
+
version:
|
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: "0"
|
|
66
|
+
version:
|
|
67
|
+
requirements: []
|
|
68
|
+
|
|
69
|
+
rubyforge_project: ruby-doom
|
|
70
|
+
rubygems_version: 1.3.4
|
|
71
|
+
signing_key:
|
|
72
|
+
specification_version: 3
|
|
73
|
+
summary: Generates DOOM maps.
|
|
74
|
+
test_files: []
|
|
75
|
+
|