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
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'uri'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
|
|
7
|
+
module Doom
|
|
8
|
+
# Downloads the shareware DOOM1.WAD if not present
|
|
9
|
+
class WadDownloader
|
|
10
|
+
# Official Doom shareware WAD, uploaded by id Software to archive.org
|
|
11
|
+
SHAREWARE_URL = 'https://archive.org/download/doom-shareware_1996/DOOM1.WAD'
|
|
12
|
+
SHAREWARE_SIZE = 4_196_020 # Expected size in bytes
|
|
13
|
+
SHAREWARE_FILENAME = 'doom1.wad'
|
|
14
|
+
|
|
15
|
+
class DownloadError < StandardError; end
|
|
16
|
+
|
|
17
|
+
def self.ensure_wad_available(custom_path = nil)
|
|
18
|
+
# If custom path provided and exists, use it
|
|
19
|
+
return custom_path if custom_path && File.exist?(custom_path)
|
|
20
|
+
|
|
21
|
+
# Check current directory for doom1.wad
|
|
22
|
+
local_wad = File.join(Dir.pwd, SHAREWARE_FILENAME)
|
|
23
|
+
return local_wad if File.exist?(local_wad)
|
|
24
|
+
|
|
25
|
+
# Check home directory .doom folder
|
|
26
|
+
home_wad = File.join(Dir.home, '.doom', SHAREWARE_FILENAME)
|
|
27
|
+
return home_wad if File.exist?(home_wad)
|
|
28
|
+
|
|
29
|
+
# No WAD found - offer to download
|
|
30
|
+
if custom_path
|
|
31
|
+
raise DownloadError, "WAD file not found: #{custom_path}"
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
prompt_and_download(home_wad)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.prompt_and_download(destination)
|
|
38
|
+
puts "No DOOM WAD file found."
|
|
39
|
+
puts
|
|
40
|
+
puts "Would you like to download the shareware version of DOOM (4 MB)?"
|
|
41
|
+
puts "This is the free, legally distributable version with Episode 1."
|
|
42
|
+
puts
|
|
43
|
+
print "Download shareware DOOM? [Y/n] "
|
|
44
|
+
|
|
45
|
+
response = $stdin.gets&.strip&.downcase
|
|
46
|
+
if response.nil? || response.empty? || response == 'y' || response == 'yes'
|
|
47
|
+
download_shareware(destination)
|
|
48
|
+
destination
|
|
49
|
+
else
|
|
50
|
+
puts
|
|
51
|
+
puts "To play DOOM, you need a WAD file. Options:"
|
|
52
|
+
puts " 1. Run 'doom' again and accept the shareware download"
|
|
53
|
+
puts " 2. Copy your own doom1.wad or doom.wad to the current directory"
|
|
54
|
+
puts " 3. Specify a WAD path: doom /path/to/your.wad"
|
|
55
|
+
exit 1
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def self.download_shareware(destination)
|
|
60
|
+
puts
|
|
61
|
+
puts "Downloading DOOM shareware..."
|
|
62
|
+
|
|
63
|
+
# Create directory if needed
|
|
64
|
+
FileUtils.mkdir_p(File.dirname(destination))
|
|
65
|
+
|
|
66
|
+
uri = URI.parse(SHAREWARE_URL)
|
|
67
|
+
downloaded = 0
|
|
68
|
+
|
|
69
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
70
|
+
request = Net::HTTP::Get.new(uri)
|
|
71
|
+
|
|
72
|
+
http.request(request) do |response|
|
|
73
|
+
case response
|
|
74
|
+
when Net::HTTPRedirection
|
|
75
|
+
# Follow redirect
|
|
76
|
+
return download_from_url(response['location'], destination)
|
|
77
|
+
when Net::HTTPSuccess
|
|
78
|
+
total_size = response['content-length']&.to_i || SHAREWARE_SIZE
|
|
79
|
+
|
|
80
|
+
File.open(destination, 'wb') do |file|
|
|
81
|
+
response.read_body do |chunk|
|
|
82
|
+
file.write(chunk)
|
|
83
|
+
downloaded += chunk.size
|
|
84
|
+
print_progress(downloaded, total_size)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
else
|
|
88
|
+
raise DownloadError, "Download failed: #{response.code} #{response.message}"
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
puts
|
|
94
|
+
puts "Downloaded to: #{destination}"
|
|
95
|
+
puts
|
|
96
|
+
|
|
97
|
+
# Verify file size
|
|
98
|
+
actual_size = File.size(destination)
|
|
99
|
+
if actual_size < 1_000_000 # Less than 1MB is suspicious
|
|
100
|
+
File.delete(destination)
|
|
101
|
+
raise DownloadError, "Download appears incomplete (#{actual_size} bytes)"
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def self.download_from_url(url, destination)
|
|
106
|
+
uri = URI.parse(url)
|
|
107
|
+
downloaded = 0
|
|
108
|
+
|
|
109
|
+
Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
110
|
+
request = Net::HTTP::Get.new(uri)
|
|
111
|
+
|
|
112
|
+
http.request(request) do |response|
|
|
113
|
+
case response
|
|
114
|
+
when Net::HTTPRedirection
|
|
115
|
+
return download_from_url(response['location'], destination)
|
|
116
|
+
when Net::HTTPSuccess
|
|
117
|
+
total_size = response['content-length']&.to_i || SHAREWARE_SIZE
|
|
118
|
+
|
|
119
|
+
File.open(destination, 'wb') do |file|
|
|
120
|
+
response.read_body do |chunk|
|
|
121
|
+
file.write(chunk)
|
|
122
|
+
downloaded += chunk.size
|
|
123
|
+
print_progress(downloaded, total_size)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
else
|
|
127
|
+
raise DownloadError, "Download failed: #{response.code} #{response.message}"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def self.print_progress(downloaded, total)
|
|
134
|
+
percent = (downloaded.to_f / total * 100).to_i
|
|
135
|
+
bar_width = 40
|
|
136
|
+
filled = (percent * bar_width / 100)
|
|
137
|
+
bar = '=' * filled + '-' * (bar_width - filled)
|
|
138
|
+
mb_downloaded = (downloaded / 1_048_576.0).round(1)
|
|
139
|
+
mb_total = (total / 1_048_576.0).round(1)
|
|
140
|
+
print "\r[#{bar}] #{percent}% (#{mb_downloaded}/#{mb_total} MB)"
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
data/lib/doom.rb
CHANGED
|
@@ -1,46 +1,65 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative
|
|
4
|
-
require_relative
|
|
5
|
-
require_relative
|
|
6
|
-
require_relative
|
|
7
|
-
require_relative
|
|
8
|
-
require_relative
|
|
9
|
-
require_relative
|
|
10
|
-
require_relative
|
|
11
|
-
require_relative
|
|
12
|
-
require_relative
|
|
13
|
-
require_relative
|
|
14
|
-
require_relative
|
|
3
|
+
require_relative 'doom/version'
|
|
4
|
+
require_relative 'doom/wad_downloader'
|
|
5
|
+
require_relative 'doom/wad/reader'
|
|
6
|
+
require_relative 'doom/wad/palette'
|
|
7
|
+
require_relative 'doom/wad/colormap'
|
|
8
|
+
require_relative 'doom/wad/flat'
|
|
9
|
+
require_relative 'doom/wad/patch'
|
|
10
|
+
require_relative 'doom/wad/texture'
|
|
11
|
+
require_relative 'doom/wad/sprite'
|
|
12
|
+
require_relative 'doom/map/data'
|
|
13
|
+
require_relative 'doom/render/renderer'
|
|
14
|
+
require_relative 'doom/platform/gosu_window'
|
|
15
15
|
|
|
16
16
|
module Doom
|
|
17
17
|
class Error < StandardError; end
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
wad
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
19
|
+
class << self
|
|
20
|
+
def run(wad_path, map_name: 'E1M1')
|
|
21
|
+
puts "Loading WAD: #{wad_path}"
|
|
22
|
+
wad = Wad::Reader.new(wad_path)
|
|
23
|
+
puts " #{wad.type}: #{wad.num_lumps} lumps"
|
|
24
|
+
|
|
25
|
+
puts 'Loading palette...'
|
|
26
|
+
palette = Wad::Palette.load(wad)
|
|
27
|
+
|
|
28
|
+
puts 'Loading colormap...'
|
|
29
|
+
colormap = Wad::Colormap.load(wad)
|
|
30
|
+
|
|
31
|
+
puts 'Loading flats...'
|
|
32
|
+
flats = Wad::Flat.load_all(wad)
|
|
33
|
+
puts " #{flats.size} flats"
|
|
34
|
+
|
|
35
|
+
puts 'Loading textures...'
|
|
36
|
+
textures = Wad::TextureManager.new(wad)
|
|
37
|
+
puts " #{textures.textures.size} textures, #{textures.pnames.size} patches"
|
|
38
|
+
|
|
39
|
+
puts 'Loading sprites...'
|
|
40
|
+
sprites = Wad::SpriteManager.new(wad)
|
|
41
|
+
|
|
42
|
+
puts "Loading map #{map_name}..."
|
|
43
|
+
map = Map::MapData.load(wad, map_name)
|
|
44
|
+
puts " #{map.vertices.size} vertices, #{map.linedefs.size} linedefs"
|
|
45
|
+
puts " #{map.segs.size} segs, #{map.subsectors.size} subsectors, #{map.nodes.size} nodes"
|
|
46
|
+
|
|
47
|
+
# Find player start
|
|
48
|
+
player_start = map.player_start
|
|
49
|
+
if player_start
|
|
50
|
+
puts " Player start: (#{player_start.x}, #{player_start.y}) angle #{player_start.angle}"
|
|
51
|
+
else
|
|
52
|
+
puts ' Warning: No player start found!'
|
|
53
|
+
player_start = Map::Thing.new(0, 0, 90, 1, 0)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
puts 'Creating renderer...'
|
|
57
|
+
renderer = Render::Renderer.new(wad, map, textures, palette, colormap, flats, sprites)
|
|
58
|
+
renderer.set_player(player_start.x, player_start.y, 41, player_start.angle)
|
|
35
59
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# @param height [Integer] height of the window (default: 480)
|
|
41
|
-
# @param fullscreen [Boolean] whether to start in fullscreen mode (default: false)
|
|
42
|
-
def self.start_game(wad_file, map_name = nil, width = 640, height = 480, fullscreen = false)
|
|
43
|
-
game = Game.new(wad_file, width, height, fullscreen)
|
|
44
|
-
game.start(map_name)
|
|
60
|
+
puts 'Starting game window...'
|
|
61
|
+
window = Platform::GosuWindow.new(renderer, palette, map)
|
|
62
|
+
window.show
|
|
63
|
+
end
|
|
45
64
|
end
|
|
46
65
|
end
|
metadata
CHANGED
|
@@ -1,57 +1,59 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: doom
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Hasinski
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2025-12-21 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
|
-
name:
|
|
14
|
+
name: gosu
|
|
14
15
|
requirement: !ruby/object:Gem::Requirement
|
|
15
16
|
requirements:
|
|
16
17
|
- - "~>"
|
|
17
18
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '
|
|
19
|
-
type: :
|
|
19
|
+
version: '1.4'
|
|
20
|
+
type: :runtime
|
|
20
21
|
prerelease: false
|
|
21
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
23
|
requirements:
|
|
23
24
|
- - "~>"
|
|
24
25
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '
|
|
26
|
+
version: '1.4'
|
|
26
27
|
- !ruby/object:Gem::Dependency
|
|
27
|
-
name:
|
|
28
|
+
name: rspec
|
|
28
29
|
requirement: !ruby/object:Gem::Requirement
|
|
29
30
|
requirements:
|
|
30
31
|
- - "~>"
|
|
31
32
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '
|
|
33
|
+
version: '3.12'
|
|
33
34
|
type: :development
|
|
34
35
|
prerelease: false
|
|
35
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
37
|
requirements:
|
|
37
38
|
- - "~>"
|
|
38
39
|
- !ruby/object:Gem::Version
|
|
39
|
-
version: '
|
|
40
|
+
version: '3.12'
|
|
40
41
|
- !ruby/object:Gem::Dependency
|
|
41
|
-
name:
|
|
42
|
+
name: chunky_png
|
|
42
43
|
requirement: !ruby/object:Gem::Requirement
|
|
43
44
|
requirements:
|
|
44
45
|
- - "~>"
|
|
45
46
|
- !ruby/object:Gem::Version
|
|
46
47
|
version: '1.4'
|
|
47
|
-
type: :
|
|
48
|
+
type: :development
|
|
48
49
|
prerelease: false
|
|
49
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
51
|
requirements:
|
|
51
52
|
- - "~>"
|
|
52
53
|
- !ruby/object:Gem::Version
|
|
53
54
|
version: '1.4'
|
|
54
|
-
description:
|
|
55
|
+
description: A faithful port of the Doom (1993) rendering engine to Ruby. Supports
|
|
56
|
+
original WAD files with near pixel-perfect BSP rendering.
|
|
55
57
|
email:
|
|
56
58
|
- krzysztof.hasinski@gmail.com
|
|
57
59
|
executables:
|
|
@@ -59,32 +61,26 @@ executables:
|
|
|
59
61
|
extensions: []
|
|
60
62
|
extra_rdoc_files: []
|
|
61
63
|
files:
|
|
62
|
-
- LICENSE.txt
|
|
63
64
|
- README.md
|
|
64
|
-
- bin/console
|
|
65
65
|
- bin/doom
|
|
66
|
-
- bin/setup
|
|
67
|
-
- bin/wad
|
|
68
66
|
- lib/doom.rb
|
|
69
|
-
- lib/doom/
|
|
70
|
-
- lib/doom/
|
|
71
|
-
- lib/doom/
|
|
72
|
-
- lib/doom/map_loader.rb
|
|
73
|
-
- lib/doom/renderer.rb
|
|
74
|
-
- lib/doom/sprite_loader.rb
|
|
75
|
-
- lib/doom/sprite_renderer.rb
|
|
76
|
-
- lib/doom/texture_loader.rb
|
|
77
|
-
- lib/doom/texture_mapper.rb
|
|
67
|
+
- lib/doom/map/data.rb
|
|
68
|
+
- lib/doom/platform/gosu_window.rb
|
|
69
|
+
- lib/doom/render/renderer.rb
|
|
78
70
|
- lib/doom/version.rb
|
|
79
|
-
- lib/doom/
|
|
80
|
-
- lib/doom/
|
|
71
|
+
- lib/doom/wad/colormap.rb
|
|
72
|
+
- lib/doom/wad/flat.rb
|
|
73
|
+
- lib/doom/wad/palette.rb
|
|
74
|
+
- lib/doom/wad/patch.rb
|
|
75
|
+
- lib/doom/wad/reader.rb
|
|
76
|
+
- lib/doom/wad/sprite.rb
|
|
77
|
+
- lib/doom/wad/texture.rb
|
|
78
|
+
- lib/doom/wad_downloader.rb
|
|
81
79
|
homepage: https://github.com/khasinski/doom-rb
|
|
82
80
|
licenses:
|
|
83
|
-
-
|
|
84
|
-
metadata:
|
|
85
|
-
|
|
86
|
-
source_code_uri: https://github.com/khasinski/doom-rb
|
|
87
|
-
changelog_uri: https://github.com/khasinski/doom-rb/blob/master/CHANGELOG.md
|
|
81
|
+
- GPL-2.0
|
|
82
|
+
metadata: {}
|
|
83
|
+
post_install_message:
|
|
88
84
|
rdoc_options: []
|
|
89
85
|
require_paths:
|
|
90
86
|
- lib
|
|
@@ -92,14 +88,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
92
88
|
requirements:
|
|
93
89
|
- - ">="
|
|
94
90
|
- !ruby/object:Gem::Version
|
|
95
|
-
version:
|
|
91
|
+
version: '3.1'
|
|
96
92
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
97
93
|
requirements:
|
|
98
94
|
- - ">="
|
|
99
95
|
- !ruby/object:Gem::Version
|
|
100
96
|
version: '0'
|
|
101
97
|
requirements: []
|
|
102
|
-
rubygems_version: 3.
|
|
98
|
+
rubygems_version: 3.5.22
|
|
99
|
+
signing_key:
|
|
103
100
|
specification_version: 4
|
|
104
|
-
summary:
|
|
101
|
+
summary: Doom engine port in pure Ruby
|
|
105
102
|
test_files: []
|
data/LICENSE.txt
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
The MIT License (MIT)
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2023 Chris Hasiński
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
|
13
|
-
all copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
-
THE SOFTWARE.
|
data/bin/console
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "bundler/setup"
|
|
5
|
-
require "doom"
|
|
6
|
-
|
|
7
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
|
8
|
-
# with your gem easier. You can also use a different console, if you like.
|
|
9
|
-
|
|
10
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
11
|
-
# require "pry"
|
|
12
|
-
# Pry.start
|
|
13
|
-
|
|
14
|
-
require "irb"
|
|
15
|
-
IRB.start(__FILE__)
|
data/bin/setup
DELETED
data/bin/wad
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "bundler/setup"
|
|
5
|
-
require "doom"
|
|
6
|
-
require "optparse"
|
|
7
|
-
|
|
8
|
-
options = {
|
|
9
|
-
action: nil,
|
|
10
|
-
map: nil,
|
|
11
|
-
texture: nil,
|
|
12
|
-
sprite: nil
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
parser = OptionParser.new do |opts|
|
|
16
|
-
opts.banner = "Usage: wad [options] WAD_FILE"
|
|
17
|
-
|
|
18
|
-
opts.on("-l", "--list-maps", "List all maps in the WAD file") do
|
|
19
|
-
options[:action] = :list_maps
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
opts.on("-m", "--map MAP", "Show details for a specific map") do |map|
|
|
23
|
-
options[:action] = :show_map
|
|
24
|
-
options[:map] = map
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
opts.on("-t", "--list-textures", "List all textures in the WAD file") do
|
|
28
|
-
options[:action] = :list_textures
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
opts.on("-x", "--texture TEXTURE", "Show details for a specific texture") do |texture|
|
|
32
|
-
options[:action] = :show_texture
|
|
33
|
-
options[:texture] = texture
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
opts.on("-s", "--list-sprites", "List all sprites in the WAD file") do
|
|
37
|
-
options[:action] = :list_sprites
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
opts.on("-p", "--sprite SPRITE", "Show details for a specific sprite") do |sprite|
|
|
41
|
-
options[:action] = :show_sprite
|
|
42
|
-
options[:sprite] = sprite
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
opts.on("-i", "--info", "Show general information about the WAD file") do
|
|
46
|
-
options[:action] = :show_info
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
opts.on("-h", "--help", "Show this help message") do
|
|
50
|
-
puts opts
|
|
51
|
-
exit
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
parser.parse!
|
|
56
|
-
|
|
57
|
-
if ARGV.empty?
|
|
58
|
-
puts "Error: WAD file is required"
|
|
59
|
-
puts parser
|
|
60
|
-
exit 1
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
wad_file = ARGV[0]
|
|
64
|
-
unless File.exist?(wad_file)
|
|
65
|
-
puts "Error: WAD file '#{wad_file}' not found"
|
|
66
|
-
exit 1
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
begin
|
|
70
|
-
loaders = Doom.load_wad(wad_file)
|
|
71
|
-
|
|
72
|
-
case options[:action]
|
|
73
|
-
when :list_maps
|
|
74
|
-
puts "Maps in #{wad_file}:"
|
|
75
|
-
loaders[:maps].maps.each do |map|
|
|
76
|
-
puts " #{map}"
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
when :show_map
|
|
80
|
-
map_name = options[:map]
|
|
81
|
-
if loaders[:maps].maps.include?(map_name)
|
|
82
|
-
map_data = loaders[:maps].load_map(map_name)
|
|
83
|
-
puts "Map: #{map_name}"
|
|
84
|
-
puts " Things: #{map_data[:things].size}"
|
|
85
|
-
puts " Linedefs: #{map_data[:linedefs].size}"
|
|
86
|
-
puts " Sidedefs: #{map_data[:sidedefs].size}"
|
|
87
|
-
puts " Vertexes: #{map_data[:vertexes].size}"
|
|
88
|
-
puts " Sectors: #{map_data[:sectors].size}"
|
|
89
|
-
else
|
|
90
|
-
puts "Error: Map '#{map_name}' not found"
|
|
91
|
-
exit 1
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
when :list_textures
|
|
95
|
-
puts "Textures in #{wad_file}:"
|
|
96
|
-
loaders[:textures].texture_names.each do |texture|
|
|
97
|
-
puts " #{texture}"
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
when :show_texture
|
|
101
|
-
texture_name = options[:texture]
|
|
102
|
-
if loaders[:textures].texture_exists?(texture_name)
|
|
103
|
-
texture_data = loaders[:textures].get_texture(texture_name)
|
|
104
|
-
puts "Texture: #{texture_name}"
|
|
105
|
-
puts " Width: #{texture_data[:width]}"
|
|
106
|
-
puts " Height: #{texture_data[:height]}"
|
|
107
|
-
puts " Patches: #{texture_data[:patches].size}"
|
|
108
|
-
texture_data[:patches].each_with_index do |patch, i|
|
|
109
|
-
puts " Patch #{i + 1}: #{patch[:patch_name]} (#{patch[:x_offset]}, #{patch[:y_offset]})"
|
|
110
|
-
end
|
|
111
|
-
else
|
|
112
|
-
puts "Error: Texture '#{texture_name}' not found"
|
|
113
|
-
exit 1
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
when :list_sprites
|
|
117
|
-
puts "Sprites in #{wad_file}:"
|
|
118
|
-
loaders[:sprites].sprite_names.each do |sprite|
|
|
119
|
-
puts " #{sprite}"
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
when :show_sprite
|
|
123
|
-
sprite_name = options[:sprite]
|
|
124
|
-
if loaders[:sprites].sprite_exists?(sprite_name)
|
|
125
|
-
sprite_data = loaders[:sprites].get_sprite(sprite_name)
|
|
126
|
-
puts "Sprite: #{sprite_name}"
|
|
127
|
-
puts " Width: #{sprite_data[:width]}"
|
|
128
|
-
puts " Height: #{sprite_data[:height]}"
|
|
129
|
-
puts " Offset: (#{sprite_data[:left_offset]}, #{sprite_data[:top_offset]})"
|
|
130
|
-
else
|
|
131
|
-
puts "Error: Sprite '#{sprite_name}' not found"
|
|
132
|
-
exit 1
|
|
133
|
-
end
|
|
134
|
-
|
|
135
|
-
when :show_info
|
|
136
|
-
puts "WAD File: #{wad_file}"
|
|
137
|
-
puts "Type: #{loaders[:wad].wad_type}"
|
|
138
|
-
puts "Directory entries: #{loaders[:wad].directory.size}"
|
|
139
|
-
puts "Lumps: #{loaders[:wad].lumps.size}"
|
|
140
|
-
puts "Maps: #{loaders[:maps].maps.size}"
|
|
141
|
-
puts "Textures: #{loaders[:textures].texture_names.size}"
|
|
142
|
-
puts "Sprites: #{loaders[:sprites].sprite_names.size}"
|
|
143
|
-
|
|
144
|
-
else
|
|
145
|
-
puts "Please specify an action. Use --help for options."
|
|
146
|
-
exit 1
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
rescue Doom::Error => e
|
|
150
|
-
puts "Error: #{e.message}"
|
|
151
|
-
exit 1
|
|
152
|
-
end
|
data/lib/doom/bsp_renderer.rb
DELETED
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Doom
|
|
4
|
-
# BSPRenderer is responsible for rendering the level using Binary Space Partitioning
|
|
5
|
-
class BSPRenderer < Renderer
|
|
6
|
-
attr_reader :map_loader
|
|
7
|
-
|
|
8
|
-
# Initialize a new BSPRenderer with the given window and map loader
|
|
9
|
-
# @param window [Doom::Window] the window to render to
|
|
10
|
-
# @param map_loader [Doom::MapLoader] the map loader to use
|
|
11
|
-
def initialize(window, map_loader)
|
|
12
|
-
super(window)
|
|
13
|
-
@map_loader = map_loader
|
|
14
|
-
@current_map = nil
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Load a specific map
|
|
18
|
-
# @param map_name [String] the name of the map to load
|
|
19
|
-
def load_map(map_name)
|
|
20
|
-
@current_map = @map_loader.load_map(map_name)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Render the current map
|
|
24
|
-
def render
|
|
25
|
-
return unless @current_map
|
|
26
|
-
|
|
27
|
-
# In a real implementation, this would:
|
|
28
|
-
# 1. Determine the player's position and angle
|
|
29
|
-
# 2. Traverse the BSP tree to find visible walls
|
|
30
|
-
# 3. Render the walls with texture mapping
|
|
31
|
-
# 4. Render sprites (enemies, items, etc.)
|
|
32
|
-
|
|
33
|
-
# For now, we'll just render a simple wireframe of the map
|
|
34
|
-
render_wireframe
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
private
|
|
38
|
-
|
|
39
|
-
# Render a simple wireframe of the map
|
|
40
|
-
def render_wireframe
|
|
41
|
-
# Get the vertexes and linedefs from the current map
|
|
42
|
-
vertexes = @current_map[:vertexes]
|
|
43
|
-
linedefs = @current_map[:linedefs]
|
|
44
|
-
|
|
45
|
-
# Calculate scale and offset to fit the map in the window
|
|
46
|
-
scale, offset_x, offset_y = calculate_scale_and_offset(vertexes)
|
|
47
|
-
|
|
48
|
-
# Draw each linedef as a line
|
|
49
|
-
linedefs.each do |linedef|
|
|
50
|
-
start_vertex = vertexes[linedef[:start_vertex]]
|
|
51
|
-
end_vertex = vertexes[linedef[:end_vertex]]
|
|
52
|
-
|
|
53
|
-
# Transform the vertexes to screen coordinates
|
|
54
|
-
x1 = offset_x + start_vertex[:x] * scale
|
|
55
|
-
y1 = offset_y + start_vertex[:y] * scale
|
|
56
|
-
x2 = offset_x + end_vertex[:x] * scale
|
|
57
|
-
y2 = offset_y + end_vertex[:y] * scale
|
|
58
|
-
|
|
59
|
-
# Draw the line
|
|
60
|
-
@window.draw_line(x1, y1, Gosu::Color::WHITE, x2, y2, Gosu::Color::WHITE)
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# Calculate the scale and offset to fit the map in the window
|
|
65
|
-
# @param vertexes [Array<Hash>] the vertexes of the map
|
|
66
|
-
# @return [Array<Float, Float, Float>] the scale, offset_x, and offset_y
|
|
67
|
-
def calculate_scale_and_offset(vertexes)
|
|
68
|
-
# Find the min and max coordinates
|
|
69
|
-
min_x = vertexes.map { |v| v[:x] }.min
|
|
70
|
-
max_x = vertexes.map { |v| v[:x] }.max
|
|
71
|
-
min_y = vertexes.map { |v| v[:y] }.min
|
|
72
|
-
max_y = vertexes.map { |v| v[:y] }.max
|
|
73
|
-
|
|
74
|
-
# Calculate the width and height of the map
|
|
75
|
-
map_width = max_x - min_x
|
|
76
|
-
map_height = max_y - min_y
|
|
77
|
-
|
|
78
|
-
# Calculate the scale to fit the map in the window
|
|
79
|
-
scale_x = @window.width * 0.8 / map_width
|
|
80
|
-
scale_y = @window.height * 0.8 / map_height
|
|
81
|
-
scale = [scale_x, scale_y].min
|
|
82
|
-
|
|
83
|
-
# Calculate the offset to center the map
|
|
84
|
-
offset_x = (@window.width - map_width * scale) / 2 - min_x * scale
|
|
85
|
-
offset_y = (@window.height - map_height * scale) / 2 - min_y * scale
|
|
86
|
-
|
|
87
|
-
[scale, offset_x, offset_y]
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
end
|