doom 0.2.0 → 0.3.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.
- checksums.yaml +4 -4
- data/README.md +75 -115
- data/bin/doom +47 -58
- data/lib/doom/map/data.rb +280 -0
- data/lib/doom/platform/gosu_window.rb +237 -0
- data/lib/doom/render/renderer.rb +1218 -0
- data/lib/doom/version.rb +1 -1
- data/lib/doom/wad/colormap.rb +38 -0
- data/lib/doom/wad/flat.rb +61 -0
- data/lib/doom/wad/palette.rb +37 -0
- data/lib/doom/wad/patch.rb +61 -0
- data/lib/doom/wad/reader.rb +79 -0
- data/lib/doom/wad/sprite.rb +205 -0
- data/lib/doom/wad/texture.rb +163 -0
- data/lib/doom/wad_downloader.rb +143 -0
- data/lib/doom.rb +56 -37
- metadata +32 -35
- data/LICENSE.txt +0 -21
- data/bin/console +0 -15
- data/bin/setup +0 -8
- data/bin/wad +0 -152
- data/lib/doom/bsp_renderer.rb +0 -90
- data/lib/doom/game.rb +0 -84
- data/lib/doom/hud.rb +0 -80
- data/lib/doom/map_loader.rb +0 -255
- data/lib/doom/renderer.rb +0 -32
- data/lib/doom/sprite_loader.rb +0 -88
- data/lib/doom/sprite_renderer.rb +0 -56
- data/lib/doom/texture_loader.rb +0 -138
- data/lib/doom/texture_mapper.rb +0 -57
- data/lib/doom/wad_loader.rb +0 -106
- data/lib/doom/window.rb +0 -41
data/lib/doom/texture_loader.rb
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Doom
|
|
4
|
-
# TextureLoader is responsible for loading and parsing texture data from a WAD file
|
|
5
|
-
class TextureLoader
|
|
6
|
-
attr_reader :textures, :patch_names
|
|
7
|
-
|
|
8
|
-
# Initialize a new TextureLoader with the given WADLoader
|
|
9
|
-
# @param wad_loader [WADLoader] the WAD loader to use
|
|
10
|
-
def initialize(wad_loader)
|
|
11
|
-
@wad_loader = wad_loader
|
|
12
|
-
@textures = {}
|
|
13
|
-
@patch_names = []
|
|
14
|
-
load_patch_names
|
|
15
|
-
load_textures
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# Load the PNAMES lump which contains patch names
|
|
19
|
-
def load_patch_names
|
|
20
|
-
pnames_data = @wad_loader.get_lump("PNAMES")
|
|
21
|
-
return unless pnames_data
|
|
22
|
-
|
|
23
|
-
# First 4 bytes is the number of patch names
|
|
24
|
-
num_patches = pnames_data[0..3].unpack("V")[0]
|
|
25
|
-
|
|
26
|
-
# Each patch name is 8 bytes
|
|
27
|
-
num_patches.times do |i|
|
|
28
|
-
offset = 4 + (i * 8)
|
|
29
|
-
patch_name = pnames_data[offset..offset + 7].unpack("Z*")[0]
|
|
30
|
-
@patch_names << patch_name
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Load the TEXTURE1 and TEXTURE2 lumps which contain texture definitions
|
|
35
|
-
def load_textures
|
|
36
|
-
load_texture_lump("TEXTURE1")
|
|
37
|
-
load_texture_lump("TEXTURE2")
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Load a specific texture lump
|
|
41
|
-
# @param lump_name [String] the name of the texture lump (TEXTURE1 or TEXTURE2)
|
|
42
|
-
def load_texture_lump(lump_name)
|
|
43
|
-
texture_data = @wad_loader.get_lump(lump_name)
|
|
44
|
-
return unless texture_data
|
|
45
|
-
|
|
46
|
-
# First 4 bytes is the number of textures
|
|
47
|
-
num_textures = texture_data[0..3].unpack("V")[0]
|
|
48
|
-
|
|
49
|
-
# Next num_textures * 4 bytes are offsets to each texture definition
|
|
50
|
-
offsets = []
|
|
51
|
-
num_textures.times do |i|
|
|
52
|
-
offset = 4 + (i * 4)
|
|
53
|
-
texture_offset = texture_data[offset..offset + 3].unpack("V")[0]
|
|
54
|
-
offsets << texture_offset
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Parse each texture definition
|
|
58
|
-
offsets.each do |offset|
|
|
59
|
-
parse_texture_definition(texture_data, offset)
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# Parse a texture definition
|
|
64
|
-
# @param texture_data [String] the texture lump data
|
|
65
|
-
# @param offset [Integer] the offset to the texture definition
|
|
66
|
-
def parse_texture_definition(texture_data, offset)
|
|
67
|
-
# Texture definition structure:
|
|
68
|
-
# - 8 bytes: texture name
|
|
69
|
-
# - 4 bytes: flags (unused)
|
|
70
|
-
# - 2 bytes: width
|
|
71
|
-
# - 2 bytes: height
|
|
72
|
-
# - 4 bytes: column directory (unused)
|
|
73
|
-
# - 2 bytes: number of patches
|
|
74
|
-
# - patches: array of patch definitions
|
|
75
|
-
|
|
76
|
-
texture_name = texture_data[offset..offset + 7].unpack("Z*")[0]
|
|
77
|
-
# Skip flags (4 bytes)
|
|
78
|
-
width = texture_data[offset + 12..offset + 13].unpack("v")[0]
|
|
79
|
-
height = texture_data[offset + 14..offset + 15].unpack("v")[0]
|
|
80
|
-
# Skip column directory (4 bytes)
|
|
81
|
-
num_patches = texture_data[offset + 20..offset + 21].unpack("v")[0]
|
|
82
|
-
|
|
83
|
-
patches = []
|
|
84
|
-
|
|
85
|
-
# Parse each patch definition
|
|
86
|
-
num_patches.times do |i|
|
|
87
|
-
patch_offset = offset + 22 + (i * 10)
|
|
88
|
-
|
|
89
|
-
# Patch definition structure:
|
|
90
|
-
# - 2 bytes: x offset
|
|
91
|
-
# - 2 bytes: y offset
|
|
92
|
-
# - 2 bytes: patch number (index into PNAMES)
|
|
93
|
-
# - 2 bytes: step dir (unused)
|
|
94
|
-
# - 2 bytes: color map (unused)
|
|
95
|
-
|
|
96
|
-
x_offset = texture_data[patch_offset..patch_offset + 1].unpack("s<")[0]
|
|
97
|
-
y_offset = texture_data[patch_offset + 2..patch_offset + 3].unpack("s<")[0]
|
|
98
|
-
patch_num = texture_data[patch_offset + 4..patch_offset + 5].unpack("v")[0]
|
|
99
|
-
|
|
100
|
-
patch_name = @patch_names[patch_num] if patch_num < @patch_names.size
|
|
101
|
-
|
|
102
|
-
patches << {
|
|
103
|
-
x_offset: x_offset,
|
|
104
|
-
y_offset: y_offset,
|
|
105
|
-
patch_num: patch_num,
|
|
106
|
-
patch_name: patch_name
|
|
107
|
-
}
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
@textures[texture_name] = {
|
|
111
|
-
name: texture_name,
|
|
112
|
-
width: width,
|
|
113
|
-
height: height,
|
|
114
|
-
patches: patches
|
|
115
|
-
}
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
# Get a specific texture by name
|
|
119
|
-
# @param name [String] the name of the texture
|
|
120
|
-
# @return [Hash, nil] the texture data or nil if not found
|
|
121
|
-
def get_texture(name)
|
|
122
|
-
@textures[name]
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
# List all texture names
|
|
126
|
-
# @return [Array<String>] array of texture names
|
|
127
|
-
def texture_names
|
|
128
|
-
@textures.keys
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# Check if a texture exists
|
|
132
|
-
# @param name [String] the name of the texture
|
|
133
|
-
# @return [Boolean] true if the texture exists
|
|
134
|
-
def texture_exists?(name)
|
|
135
|
-
@textures.key?(name)
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
end
|
data/lib/doom/texture_mapper.rb
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Doom
|
|
4
|
-
# TextureMapper is responsible for mapping textures onto walls
|
|
5
|
-
class TextureMapper
|
|
6
|
-
attr_reader :texture_loader
|
|
7
|
-
|
|
8
|
-
# Initialize a new TextureMapper with the given texture loader
|
|
9
|
-
# @param texture_loader [Doom::TextureLoader] the texture loader to use
|
|
10
|
-
def initialize(texture_loader)
|
|
11
|
-
@texture_loader = texture_loader
|
|
12
|
-
@textures = {}
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# Load a specific texture
|
|
16
|
-
# @param texture_name [String] the name of the texture to load
|
|
17
|
-
# @return [Hash, nil] the texture data or nil if not found
|
|
18
|
-
def load_texture(texture_name)
|
|
19
|
-
return @textures[texture_name] if @textures.key?(texture_name)
|
|
20
|
-
|
|
21
|
-
texture_data = @texture_loader.get_texture(texture_name)
|
|
22
|
-
return nil unless texture_data
|
|
23
|
-
|
|
24
|
-
# In a real implementation, this would convert the texture data
|
|
25
|
-
# into a format that can be efficiently rendered
|
|
26
|
-
@textures[texture_name] = texture_data
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Map a texture onto a wall
|
|
30
|
-
# @param texture_name [String] the name of the texture to map
|
|
31
|
-
# @param x [Integer] the x coordinate to start mapping
|
|
32
|
-
# @param y [Integer] the y coordinate to start mapping
|
|
33
|
-
# @param width [Integer] the width of the wall
|
|
34
|
-
# @param height [Integer] the height of the wall
|
|
35
|
-
# @param u_offset [Integer] the u offset into the texture
|
|
36
|
-
# @param v_offset [Integer] the v offset into the texture
|
|
37
|
-
# @param window [Doom::Window] the window to render to
|
|
38
|
-
def map_texture(texture_name, x, y, width, height, u_offset, v_offset, window)
|
|
39
|
-
texture = load_texture(texture_name)
|
|
40
|
-
return unless texture
|
|
41
|
-
|
|
42
|
-
# In a real implementation, this would:
|
|
43
|
-
# 1. Calculate the texture coordinates for each pixel of the wall
|
|
44
|
-
# 2. Sample the texture at those coordinates
|
|
45
|
-
# 3. Draw the resulting pixel to the screen
|
|
46
|
-
|
|
47
|
-
# For now, we'll just draw a placeholder rectangle
|
|
48
|
-
color = Gosu::Color.new(255, 128, 128, 128) # Semi-transparent gray
|
|
49
|
-
window.draw_quad(
|
|
50
|
-
x, y, color,
|
|
51
|
-
x + width, y, color,
|
|
52
|
-
x + width, y + height, color,
|
|
53
|
-
x, y + height, color
|
|
54
|
-
)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
data/lib/doom/wad_loader.rb
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Doom
|
|
4
|
-
# WADLoader is responsible for loading and parsing WAD files
|
|
5
|
-
class WADLoader
|
|
6
|
-
# WAD file header structure:
|
|
7
|
-
# - 4 bytes: WAD type (IWAD or PWAD)
|
|
8
|
-
# - 4 bytes: number of lumps
|
|
9
|
-
# - 4 bytes: directory offset
|
|
10
|
-
HEADER_SIZE = 12
|
|
11
|
-
|
|
12
|
-
# Directory entry structure:
|
|
13
|
-
# - 4 bytes: lump offset
|
|
14
|
-
# - 4 bytes: lump size
|
|
15
|
-
# - 8 bytes: lump name (null-terminated)
|
|
16
|
-
DIRECTORY_ENTRY_SIZE = 16
|
|
17
|
-
|
|
18
|
-
attr_reader :wad_type, :lumps, :directory, :filename, :num_lumps
|
|
19
|
-
|
|
20
|
-
# Initialize a new WADLoader with the given WAD file
|
|
21
|
-
# @param filename [String] path to the WAD file
|
|
22
|
-
def initialize(filename)
|
|
23
|
-
@filename = filename
|
|
24
|
-
@lumps = {}
|
|
25
|
-
@directory = []
|
|
26
|
-
load_wad
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Load and parse the WAD file
|
|
30
|
-
def load_wad
|
|
31
|
-
File.open(@filename, "rb") do |file|
|
|
32
|
-
read_header(file)
|
|
33
|
-
read_directory(file)
|
|
34
|
-
load_lumps(file)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Read the WAD file header
|
|
39
|
-
# @param file [File] the WAD file
|
|
40
|
-
def read_header(file)
|
|
41
|
-
header = file.read(HEADER_SIZE)
|
|
42
|
-
@wad_type = header[0..3]
|
|
43
|
-
@num_lumps = header[4..7].unpack("V")[0]
|
|
44
|
-
directory_offset = header[8..11].unpack("V")[0]
|
|
45
|
-
|
|
46
|
-
# Validate WAD type
|
|
47
|
-
unless ["IWAD", "PWAD"].include?(@wad_type)
|
|
48
|
-
raise Error, "Invalid WAD type: #{@wad_type}"
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Seek to the directory
|
|
52
|
-
file.seek(directory_offset)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Read the WAD directory
|
|
56
|
-
# @param file [File] the WAD file
|
|
57
|
-
def read_directory(file)
|
|
58
|
-
# The file position should already be at the directory after read_header
|
|
59
|
-
# We'll use the number of lumps from the instance variable
|
|
60
|
-
|
|
61
|
-
@num_lumps.times do
|
|
62
|
-
entry = file.read(DIRECTORY_ENTRY_SIZE)
|
|
63
|
-
lump_offset = entry[0..3].unpack("V")[0]
|
|
64
|
-
lump_size = entry[4..7].unpack("V")[0]
|
|
65
|
-
lump_name = entry[8..15].unpack("Z*")[0]
|
|
66
|
-
|
|
67
|
-
@directory << {
|
|
68
|
-
name: lump_name,
|
|
69
|
-
offset: lump_offset,
|
|
70
|
-
size: lump_size
|
|
71
|
-
}
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
# Load all lumps from the WAD file
|
|
76
|
-
# @param file [File] the WAD file
|
|
77
|
-
def load_lumps(file)
|
|
78
|
-
@directory.each do |entry|
|
|
79
|
-
next if entry[:size] == 0 # Skip markers
|
|
80
|
-
|
|
81
|
-
file.seek(entry[:offset])
|
|
82
|
-
@lumps[entry[:name]] = file.read(entry[:size])
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Get a specific lump by name
|
|
87
|
-
# @param name [String] the name of the lump
|
|
88
|
-
# @return [String, nil] the lump data or nil if not found
|
|
89
|
-
def get_lump(name)
|
|
90
|
-
@lumps[name]
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# List all lump names
|
|
94
|
-
# @return [Array<String>] array of lump names
|
|
95
|
-
def lump_names
|
|
96
|
-
@lumps.keys
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Check if a lump exists
|
|
100
|
-
# @param name [String] the name of the lump
|
|
101
|
-
# @return [Boolean] true if the lump exists
|
|
102
|
-
def lump_exists?(name)
|
|
103
|
-
@lumps.key?(name)
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
end
|
data/lib/doom/window.rb
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "gosu"
|
|
4
|
-
|
|
5
|
-
module Doom
|
|
6
|
-
# Window class for managing the game window
|
|
7
|
-
class Window < Gosu::Window
|
|
8
|
-
attr_reader :width, :height
|
|
9
|
-
attr_accessor :game
|
|
10
|
-
|
|
11
|
-
# Initialize a new Window with the given width and height
|
|
12
|
-
# @param width [Integer] the width of the window
|
|
13
|
-
# @param height [Integer] the height of the window
|
|
14
|
-
# @param fullscreen [Boolean] whether the window should be fullscreen
|
|
15
|
-
def initialize(width = 640, height = 480, fullscreen = false)
|
|
16
|
-
super(width, height, fullscreen)
|
|
17
|
-
@width = width
|
|
18
|
-
@height = height
|
|
19
|
-
self.caption = "Ruby Doom"
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# Update the game state
|
|
23
|
-
def update
|
|
24
|
-
@game.update if @game
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Draw the game
|
|
28
|
-
def draw
|
|
29
|
-
@game.draw if @game
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Handle button press events
|
|
33
|
-
def button_down(id)
|
|
34
|
-
if @game
|
|
35
|
-
@game.button_down(id)
|
|
36
|
-
else
|
|
37
|
-
close if id == Gosu::KB_ESCAPE
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|