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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 70d72175c1f115b3363e188d7075047bc32cece4f3a3dd1feda17dc6f965b07f
|
|
4
|
+
data.tar.gz: 22eb9172267c578a6fbaa3dd526ec19e8d4587dba9a349080ad1626d3e04fd92
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6f5fdd281119ba4bec5a29b8f9cd32f8d0aa37d1674125c3095835ba24942e66e6356037c3e424f320578cac5b1ed94dc7738388d3ced296f8de22ce78e9e79e
|
|
7
|
+
data.tar.gz: 35de5ff2f711aa4084a54d811d26b919cf8f1b00be1d9b06597b7e7aac09eab3ee8ff597b1ece2ac451d175dad2faf3c16bc73f3dbf56b50867407509fc4f234
|
data/README.md
CHANGED
|
@@ -1,159 +1,119 @@
|
|
|
1
|
-
# Ruby
|
|
1
|
+
# DOOM Ruby
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A faithful ruby port of the DOOM (1993) rendering engine to Ruby.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+

|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Features
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
- Pure Ruby implementation of DOOM's BSP rendering engine
|
|
10
|
+
- Accurate wall, floor, and ceiling rendering with proper texture mapping
|
|
11
|
+
- Sprite rendering with depth-correct clipping
|
|
12
|
+
- Original DOOM lighting and colormap support
|
|
13
|
+
- Mouse look and WASD movement controls
|
|
14
|
+
- Supports original WAD files (shareware and registered)
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
## Installation
|
|
14
17
|
|
|
15
18
|
```bash
|
|
16
|
-
|
|
19
|
+
gem install doom
|
|
17
20
|
```
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
## Quick Start
|
|
23
|
+
|
|
24
|
+
Just run `doom` - it will offer to download the free shareware version:
|
|
20
25
|
|
|
21
26
|
```bash
|
|
22
|
-
|
|
27
|
+
doom
|
|
23
28
|
```
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
- Ruby 2.6 or higher
|
|
28
|
-
- A legal copy of Doom for the WAD files (e.g., DOOM.WAD)
|
|
29
|
-
|
|
30
|
-
## Usage
|
|
31
|
-
|
|
32
|
-
### Command Line Interface
|
|
33
|
-
|
|
34
|
-
The gem includes two command-line tools:
|
|
35
|
-
|
|
36
|
-
#### WAD Explorer
|
|
37
|
-
|
|
38
|
-
The `wad` command allows you to explore WAD files:
|
|
30
|
+
Or specify your own WAD file:
|
|
39
31
|
|
|
40
32
|
```bash
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
# List all maps in a WAD file
|
|
45
|
-
$ wad -l DOOM.WAD
|
|
33
|
+
doom /path/to/doom.wad
|
|
34
|
+
```
|
|
46
35
|
|
|
47
|
-
|
|
48
|
-
$ wad -m E1M1 DOOM.WAD
|
|
36
|
+
## Controls
|
|
49
37
|
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
| Key | Action |
|
|
39
|
+
|-----|--------|
|
|
40
|
+
| W / Up Arrow | Move forward |
|
|
41
|
+
| S / Down Arrow | Move backward |
|
|
42
|
+
| A | Strafe left |
|
|
43
|
+
| D | Strafe right |
|
|
44
|
+
| Left Arrow | Turn left |
|
|
45
|
+
| Right Arrow | Turn right |
|
|
46
|
+
| Mouse | Look around (click to capture) |
|
|
47
|
+
| Escape | Release mouse / Quit |
|
|
52
48
|
|
|
53
|
-
|
|
54
|
-
$ wad -x STARTAN1 DOOM.WAD
|
|
49
|
+
## Requirements
|
|
55
50
|
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
- Ruby 3.1 or higher
|
|
52
|
+
- Gosu gem (for window/graphics)
|
|
53
|
+
- SDL2 (native library required by Gosu)
|
|
58
54
|
|
|
59
|
-
|
|
60
|
-
$ wad -p TROOA1 DOOM.WAD
|
|
55
|
+
### Installing SDL2
|
|
61
56
|
|
|
62
|
-
|
|
63
|
-
|
|
57
|
+
**macOS:**
|
|
58
|
+
```bash
|
|
59
|
+
brew install sdl2
|
|
64
60
|
```
|
|
65
61
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
The `doom` command allows you to start the game:
|
|
69
|
-
|
|
62
|
+
**Ubuntu/Debian:**
|
|
70
63
|
```bash
|
|
71
|
-
|
|
72
|
-
$ doom DOOM.WAD
|
|
73
|
-
|
|
74
|
-
# Start the game with a specific map
|
|
75
|
-
$ doom -m E1M1 DOOM.WAD
|
|
76
|
-
|
|
77
|
-
# Start the game with a custom window size
|
|
78
|
-
$ doom -w 800 -h 600 DOOM.WAD
|
|
79
|
-
|
|
80
|
-
# Start the game in fullscreen mode
|
|
81
|
-
$ doom -f DOOM.WAD
|
|
82
|
-
|
|
83
|
-
# Show help
|
|
84
|
-
$ doom --help
|
|
64
|
+
sudo apt-get install build-essential libsdl2-dev libgl1-mesa-dev libfontconfig1-dev
|
|
85
65
|
```
|
|
86
66
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
```ruby
|
|
92
|
-
require 'doom'
|
|
93
|
-
|
|
94
|
-
# Load a WAD file
|
|
95
|
-
loaders = Doom.load_wad('path/to/DOOM.WAD')
|
|
96
|
-
|
|
97
|
-
# Access WAD information
|
|
98
|
-
wad_loader = loaders[:wad]
|
|
99
|
-
puts "WAD type: #{wad_loader.wad_type}"
|
|
100
|
-
puts "Number of lumps: #{wad_loader.lumps.size}"
|
|
101
|
-
|
|
102
|
-
# Access map information
|
|
103
|
-
map_loader = loaders[:maps]
|
|
104
|
-
puts "Available maps: #{map_loader.maps.join(', ')}"
|
|
105
|
-
|
|
106
|
-
# Load a specific map
|
|
107
|
-
map_data = map_loader.load_map('E1M1')
|
|
108
|
-
puts "Number of things in E1M1: #{map_data[:things].size}"
|
|
109
|
-
|
|
110
|
-
# Access texture information
|
|
111
|
-
texture_loader = loaders[:textures]
|
|
112
|
-
puts "Available textures: #{texture_loader.texture_names.join(', ')}"
|
|
113
|
-
|
|
114
|
-
# Access sprite information
|
|
115
|
-
sprite_loader = loaders[:sprites]
|
|
116
|
-
puts "Available sprites: #{sprite_loader.sprite_names.join(', ')}"
|
|
67
|
+
**Fedora:**
|
|
68
|
+
```bash
|
|
69
|
+
sudo dnf install SDL2-devel mesa-libGL-devel fontconfig-devel gcc-c++
|
|
117
70
|
```
|
|
118
71
|
|
|
119
|
-
|
|
72
|
+
**Arch Linux:**
|
|
73
|
+
```bash
|
|
74
|
+
sudo pacman -S sdl2 mesa
|
|
75
|
+
```
|
|
120
76
|
|
|
121
|
-
|
|
77
|
+
**Windows:**
|
|
78
|
+
No additional setup needed - the gem includes SDL2.
|
|
122
79
|
|
|
123
|
-
|
|
124
|
-
- Map data extraction
|
|
125
|
-
- Texture information parsing
|
|
126
|
-
- Sprite data handling
|
|
80
|
+
## Development
|
|
127
81
|
|
|
128
|
-
|
|
82
|
+
```bash
|
|
83
|
+
git clone https://github.com/khasinski/doom-rb.git
|
|
84
|
+
cd doom-rb
|
|
85
|
+
bundle install
|
|
86
|
+
ruby bin/doom doom1.wad
|
|
87
|
+
```
|
|
129
88
|
|
|
130
|
-
|
|
131
|
-
- BSP rendering
|
|
132
|
-
- Texture mapping
|
|
133
|
-
- Sprite rendering
|
|
134
|
-
- HUD implementation
|
|
135
|
-
- Basic game loop
|
|
89
|
+
Run specs:
|
|
136
90
|
|
|
137
|
-
|
|
91
|
+
```bash
|
|
92
|
+
bundle exec rspec
|
|
93
|
+
```
|
|
138
94
|
|
|
139
|
-
|
|
140
|
-
- Advanced game loop and state management
|
|
141
|
-
- Polishing
|
|
95
|
+
## Technical Details
|
|
142
96
|
|
|
143
|
-
|
|
97
|
+
This implementation includes:
|
|
144
98
|
|
|
145
|
-
|
|
99
|
+
- **BSP Traversal**: Front-to-back rendering using the map's BSP tree
|
|
100
|
+
- **Visplanes**: Floor/ceiling rendering with R_CheckPlane splitting
|
|
101
|
+
- **Drawsegs**: Wall segment tracking for proper sprite clipping
|
|
102
|
+
- **Texture Mapping**: Perspective-correct texture coordinates
|
|
103
|
+
- **Lighting**: Distance-based light diminishing with colormaps
|
|
146
104
|
|
|
147
|
-
|
|
105
|
+
## Legal
|
|
148
106
|
|
|
149
|
-
|
|
107
|
+
DOOM is a registered trademark of id Software LLC. This is an unofficial fan project.
|
|
150
108
|
|
|
151
|
-
|
|
109
|
+
The shareware version of DOOM (Episode 1) is freely distributable. For the full game,
|
|
110
|
+
please purchase DOOM from [Steam](https://store.steampowered.com/app/2280/Ultimate_Doom/),
|
|
111
|
+
[GOG](https://www.gog.com/pl/game/doom_doom_ii), or other retailers.
|
|
152
112
|
|
|
153
113
|
## License
|
|
154
114
|
|
|
155
|
-
|
|
115
|
+
GPL-2.0 - Same license as the original DOOM source code.
|
|
156
116
|
|
|
157
|
-
##
|
|
117
|
+
## Author
|
|
158
118
|
|
|
159
|
-
|
|
119
|
+
Chris Hasinski ([@khasinski](https://github.com/khasinski))
|
data/bin/doom
CHANGED
|
@@ -1,70 +1,59 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
options = {
|
|
9
|
-
map: nil,
|
|
10
|
-
width: 640,
|
|
11
|
-
height: 480,
|
|
12
|
-
fullscreen: false
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
parser = OptionParser.new do |opts|
|
|
16
|
-
opts.banner = "Usage: doom [options] WAD_FILE"
|
|
17
|
-
|
|
18
|
-
opts.on("-m", "--map MAP", "Start the game with a specific map") do |map|
|
|
19
|
-
options[:map] = map
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
opts.on("-w", "--width WIDTH", Integer, "Set the window width (default: 640)") do |width|
|
|
23
|
-
options[:width] = width
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
opts.on("-h", "--height HEIGHT", Integer, "Set the window height (default: 480)") do |height|
|
|
27
|
-
options[:height] = height
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
opts.on("-f", "--fullscreen", "Start in fullscreen mode") do
|
|
31
|
-
options[:fullscreen] = true
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
opts.on("--help", "Show this help message") do
|
|
35
|
-
puts opts
|
|
36
|
-
exit
|
|
37
|
-
end
|
|
4
|
+
# Enable YJIT if available (Ruby 3.1+) for better performance
|
|
5
|
+
if defined?(RubyVM::YJIT) && RubyVM::YJIT.respond_to?(:enable)
|
|
6
|
+
RubyVM::YJIT.enable
|
|
38
7
|
end
|
|
39
8
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
9
|
+
# Parse arguments before loading heavy dependencies
|
|
10
|
+
wad_path = ARGV[0]
|
|
11
|
+
|
|
12
|
+
# Show help (before loading anything)
|
|
13
|
+
if wad_path == '-h' || wad_path == '--help'
|
|
14
|
+
puts "DOOM - Ruby port of the classic 1993 game"
|
|
15
|
+
puts
|
|
16
|
+
puts "Usage: doom [OPTIONS] [WAD_FILE]"
|
|
17
|
+
puts
|
|
18
|
+
puts "Options:"
|
|
19
|
+
puts " -h, --help Show this help message"
|
|
20
|
+
puts " -v, --version Show version"
|
|
21
|
+
puts
|
|
22
|
+
puts "If no WAD file is specified, doom will look for doom1.wad in:"
|
|
23
|
+
puts " 1. Current directory"
|
|
24
|
+
puts " 2. ~/.doom/"
|
|
25
|
+
puts
|
|
26
|
+
puts "If not found, you'll be prompted to download the shareware version."
|
|
27
|
+
puts
|
|
28
|
+
puts "Controls:"
|
|
29
|
+
puts " W/Up - Move forward"
|
|
30
|
+
puts " S/Down - Move backward"
|
|
31
|
+
puts " A - Strafe left"
|
|
32
|
+
puts " D - Strafe right"
|
|
33
|
+
puts " Left - Turn left"
|
|
34
|
+
puts " Right - Turn right"
|
|
35
|
+
puts " Mouse - Look around (click to capture)"
|
|
36
|
+
puts " Escape - Release mouse / Quit"
|
|
37
|
+
exit 0
|
|
46
38
|
end
|
|
47
39
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
puts "
|
|
51
|
-
exit
|
|
40
|
+
if wad_path == '-v' || wad_path == '--version'
|
|
41
|
+
require_relative '../lib/doom/version'
|
|
42
|
+
puts "DOOM Ruby v#{Doom::VERSION}"
|
|
43
|
+
exit 0
|
|
52
44
|
end
|
|
53
45
|
|
|
46
|
+
# Now load the full library
|
|
47
|
+
require_relative '../lib/doom'
|
|
48
|
+
|
|
54
49
|
begin
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
Doom.start_game(
|
|
61
|
-
wad_file,
|
|
62
|
-
options[:map],
|
|
63
|
-
options[:width],
|
|
64
|
-
options[:height],
|
|
65
|
-
options[:fullscreen]
|
|
66
|
-
)
|
|
67
|
-
rescue Doom::Error => e
|
|
50
|
+
# Find or download WAD
|
|
51
|
+
wad_path = Doom::WadDownloader.ensure_wad_available(wad_path)
|
|
52
|
+
Doom.run(wad_path)
|
|
53
|
+
rescue Doom::WadDownloader::DownloadError => e
|
|
68
54
|
puts "Error: #{e.message}"
|
|
69
55
|
exit 1
|
|
70
|
-
|
|
56
|
+
rescue Interrupt
|
|
57
|
+
puts "\nQuitting..."
|
|
58
|
+
exit 0
|
|
59
|
+
end
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Doom
|
|
4
|
+
module Map
|
|
5
|
+
Vertex = Struct.new(:x, :y)
|
|
6
|
+
|
|
7
|
+
Thing = Struct.new(:x, :y, :angle, :type, :flags)
|
|
8
|
+
|
|
9
|
+
Linedef = Struct.new(:v1, :v2, :flags, :special, :tag, :sidedef_right, :sidedef_left) do
|
|
10
|
+
FLAGS = {
|
|
11
|
+
BLOCKING: 0x0001,
|
|
12
|
+
BLOCKMONSTERS: 0x0002,
|
|
13
|
+
TWOSIDED: 0x0004,
|
|
14
|
+
DONTPEGTOP: 0x0008,
|
|
15
|
+
DONTPEGBOTTOM: 0x0010,
|
|
16
|
+
SECRET: 0x0020,
|
|
17
|
+
SOUNDBLOCK: 0x0040,
|
|
18
|
+
DONTDRAW: 0x0080,
|
|
19
|
+
MAPPED: 0x0100
|
|
20
|
+
}.freeze
|
|
21
|
+
|
|
22
|
+
def two_sided?
|
|
23
|
+
(flags & FLAGS[:TWOSIDED]) != 0
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def upper_unpegged?
|
|
27
|
+
(flags & FLAGS[:DONTPEGTOP]) != 0
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def lower_unpegged?
|
|
31
|
+
(flags & FLAGS[:DONTPEGBOTTOM]) != 0
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Sidedef = Struct.new(:x_offset, :y_offset, :upper_texture, :lower_texture, :middle_texture, :sector)
|
|
36
|
+
|
|
37
|
+
Sector = Struct.new(:floor_height, :ceiling_height, :floor_texture, :ceiling_texture, :light_level, :special, :tag)
|
|
38
|
+
|
|
39
|
+
Seg = Struct.new(:v1, :v2, :angle, :linedef, :direction, :offset)
|
|
40
|
+
|
|
41
|
+
Subsector = Struct.new(:seg_count, :first_seg)
|
|
42
|
+
|
|
43
|
+
class Node
|
|
44
|
+
SUBSECTOR_FLAG = 0x8000
|
|
45
|
+
|
|
46
|
+
attr_reader :x, :y, :dx, :dy, :bbox_right, :bbox_left, :child_right, :child_left
|
|
47
|
+
|
|
48
|
+
BBox = Struct.new(:top, :bottom, :left, :right)
|
|
49
|
+
|
|
50
|
+
def initialize(x, y, dx, dy, bbox_right, bbox_left, child_right, child_left)
|
|
51
|
+
@x = x
|
|
52
|
+
@y = y
|
|
53
|
+
@dx = dx
|
|
54
|
+
@dy = dy
|
|
55
|
+
@bbox_right = bbox_right
|
|
56
|
+
@bbox_left = bbox_left
|
|
57
|
+
@child_right = child_right
|
|
58
|
+
@child_left = child_left
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def right_is_subsector?
|
|
62
|
+
(@child_right & SUBSECTOR_FLAG) != 0
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def left_is_subsector?
|
|
66
|
+
(@child_left & SUBSECTOR_FLAG) != 0
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def right_index
|
|
70
|
+
@child_right & ~SUBSECTOR_FLAG
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def left_index
|
|
74
|
+
@child_left & ~SUBSECTOR_FLAG
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
class MapData
|
|
79
|
+
attr_reader :name, :things, :vertices, :linedefs, :sidedefs, :sectors, :segs, :subsectors, :nodes
|
|
80
|
+
|
|
81
|
+
def initialize(name)
|
|
82
|
+
@name = name
|
|
83
|
+
@things = []
|
|
84
|
+
@vertices = []
|
|
85
|
+
@linedefs = []
|
|
86
|
+
@sidedefs = []
|
|
87
|
+
@sectors = []
|
|
88
|
+
@segs = []
|
|
89
|
+
@subsectors = []
|
|
90
|
+
@nodes = []
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def self.load(wad, map_name)
|
|
94
|
+
map = new(map_name)
|
|
95
|
+
|
|
96
|
+
lump_idx = wad.directory.index { |e| e.name == map_name.upcase }
|
|
97
|
+
raise Error, "Map #{map_name} not found" unless lump_idx
|
|
98
|
+
|
|
99
|
+
map.load_things(wad.read_lump_at(wad.directory[lump_idx + 1]))
|
|
100
|
+
map.load_linedefs(wad.read_lump_at(wad.directory[lump_idx + 2]))
|
|
101
|
+
map.load_sidedefs(wad.read_lump_at(wad.directory[lump_idx + 3]))
|
|
102
|
+
map.load_vertices(wad.read_lump_at(wad.directory[lump_idx + 4]))
|
|
103
|
+
map.load_segs(wad.read_lump_at(wad.directory[lump_idx + 5]))
|
|
104
|
+
map.load_subsectors(wad.read_lump_at(wad.directory[lump_idx + 6]))
|
|
105
|
+
map.load_nodes(wad.read_lump_at(wad.directory[lump_idx + 7]))
|
|
106
|
+
map.load_sectors(wad.read_lump_at(wad.directory[lump_idx + 8]))
|
|
107
|
+
|
|
108
|
+
map
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def load_things(data)
|
|
112
|
+
count = data.size / 10
|
|
113
|
+
count.times do |i|
|
|
114
|
+
offset = i * 10
|
|
115
|
+
@things << Thing.new(
|
|
116
|
+
data[offset, 2].unpack1('s<'),
|
|
117
|
+
data[offset + 2, 2].unpack1('s<'),
|
|
118
|
+
data[offset + 4, 2].unpack1('v'),
|
|
119
|
+
data[offset + 6, 2].unpack1('v'),
|
|
120
|
+
data[offset + 8, 2].unpack1('v')
|
|
121
|
+
)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def load_vertices(data)
|
|
126
|
+
count = data.size / 4
|
|
127
|
+
count.times do |i|
|
|
128
|
+
offset = i * 4
|
|
129
|
+
@vertices << Vertex.new(
|
|
130
|
+
data[offset, 2].unpack1('s<'),
|
|
131
|
+
data[offset + 2, 2].unpack1('s<')
|
|
132
|
+
)
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def load_linedefs(data)
|
|
137
|
+
count = data.size / 14
|
|
138
|
+
count.times do |i|
|
|
139
|
+
offset = i * 14
|
|
140
|
+
@linedefs << Linedef.new(
|
|
141
|
+
data[offset, 2].unpack1('v'),
|
|
142
|
+
data[offset + 2, 2].unpack1('v'),
|
|
143
|
+
data[offset + 4, 2].unpack1('v'),
|
|
144
|
+
data[offset + 6, 2].unpack1('v'),
|
|
145
|
+
data[offset + 8, 2].unpack1('v'),
|
|
146
|
+
data[offset + 10, 2].unpack1('s<'),
|
|
147
|
+
data[offset + 12, 2].unpack1('s<')
|
|
148
|
+
)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def load_sidedefs(data)
|
|
153
|
+
count = data.size / 30
|
|
154
|
+
count.times do |i|
|
|
155
|
+
offset = i * 30
|
|
156
|
+
@sidedefs << Sidedef.new(
|
|
157
|
+
data[offset, 2].unpack1('s<'),
|
|
158
|
+
data[offset + 2, 2].unpack1('s<'),
|
|
159
|
+
data[offset + 4, 8].delete("\x00").strip,
|
|
160
|
+
data[offset + 12, 8].delete("\x00").strip,
|
|
161
|
+
data[offset + 20, 8].delete("\x00").strip,
|
|
162
|
+
data[offset + 28, 2].unpack1('v')
|
|
163
|
+
)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def load_sectors(data)
|
|
168
|
+
count = data.size / 26
|
|
169
|
+
count.times do |i|
|
|
170
|
+
offset = i * 26
|
|
171
|
+
@sectors << Sector.new(
|
|
172
|
+
data[offset, 2].unpack1('s<'),
|
|
173
|
+
data[offset + 2, 2].unpack1('s<'),
|
|
174
|
+
data[offset + 4, 8].delete("\x00").strip,
|
|
175
|
+
data[offset + 12, 8].delete("\x00").strip,
|
|
176
|
+
data[offset + 20, 2].unpack1('v'),
|
|
177
|
+
data[offset + 22, 2].unpack1('v'),
|
|
178
|
+
data[offset + 24, 2].unpack1('v')
|
|
179
|
+
)
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def load_segs(data)
|
|
184
|
+
count = data.size / 12
|
|
185
|
+
count.times do |i|
|
|
186
|
+
offset = i * 12
|
|
187
|
+
@segs << Seg.new(
|
|
188
|
+
data[offset, 2].unpack1('v'),
|
|
189
|
+
data[offset + 2, 2].unpack1('v'),
|
|
190
|
+
data[offset + 4, 2].unpack1('s<'),
|
|
191
|
+
data[offset + 6, 2].unpack1('v'),
|
|
192
|
+
data[offset + 8, 2].unpack1('v'),
|
|
193
|
+
data[offset + 10, 2].unpack1('s<')
|
|
194
|
+
)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def load_subsectors(data)
|
|
199
|
+
count = data.size / 4
|
|
200
|
+
count.times do |i|
|
|
201
|
+
offset = i * 4
|
|
202
|
+
@subsectors << Subsector.new(
|
|
203
|
+
data[offset, 2].unpack1('v'),
|
|
204
|
+
data[offset + 2, 2].unpack1('v')
|
|
205
|
+
)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def load_nodes(data)
|
|
210
|
+
count = data.size / 28
|
|
211
|
+
count.times do |i|
|
|
212
|
+
offset = i * 28
|
|
213
|
+
bbox_right = Node::BBox.new(
|
|
214
|
+
data[offset + 8, 2].unpack1('s<'),
|
|
215
|
+
data[offset + 10, 2].unpack1('s<'),
|
|
216
|
+
data[offset + 12, 2].unpack1('s<'),
|
|
217
|
+
data[offset + 14, 2].unpack1('s<')
|
|
218
|
+
)
|
|
219
|
+
bbox_left = Node::BBox.new(
|
|
220
|
+
data[offset + 16, 2].unpack1('s<'),
|
|
221
|
+
data[offset + 18, 2].unpack1('s<'),
|
|
222
|
+
data[offset + 20, 2].unpack1('s<'),
|
|
223
|
+
data[offset + 22, 2].unpack1('s<')
|
|
224
|
+
)
|
|
225
|
+
@nodes << Node.new(
|
|
226
|
+
data[offset, 2].unpack1('s<'),
|
|
227
|
+
data[offset + 2, 2].unpack1('s<'),
|
|
228
|
+
data[offset + 4, 2].unpack1('s<'),
|
|
229
|
+
data[offset + 6, 2].unpack1('s<'),
|
|
230
|
+
bbox_right,
|
|
231
|
+
bbox_left,
|
|
232
|
+
data[offset + 24, 2].unpack1('v'),
|
|
233
|
+
data[offset + 26, 2].unpack1('v')
|
|
234
|
+
)
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def player_start
|
|
239
|
+
@things.find { |t| t.type == 1 }
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Find the sector at a given position by traversing the BSP tree
|
|
243
|
+
def sector_at(x, y)
|
|
244
|
+
subsector = subsector_at(x, y)
|
|
245
|
+
return nil unless subsector
|
|
246
|
+
|
|
247
|
+
# Get sector from first seg of subsector
|
|
248
|
+
seg = @segs[subsector.first_seg]
|
|
249
|
+
return nil unless seg
|
|
250
|
+
|
|
251
|
+
linedef = @linedefs[seg.linedef]
|
|
252
|
+
sidedef_idx = seg.direction == 0 ? linedef.sidedef_right : linedef.sidedef_left
|
|
253
|
+
return nil if sidedef_idx < 0
|
|
254
|
+
|
|
255
|
+
@sectors[@sidedefs[sidedef_idx].sector]
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# Find the subsector containing a point
|
|
259
|
+
def subsector_at(x, y)
|
|
260
|
+
node_idx = @nodes.size - 1
|
|
261
|
+
while (node_idx & Node::SUBSECTOR_FLAG) == 0
|
|
262
|
+
node = @nodes[node_idx]
|
|
263
|
+
side = point_on_side(x, y, node)
|
|
264
|
+
node_idx = side == 0 ? node.child_right : node.child_left
|
|
265
|
+
end
|
|
266
|
+
@subsectors[node_idx & ~Node::SUBSECTOR_FLAG]
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
private
|
|
270
|
+
|
|
271
|
+
def point_on_side(x, y, node)
|
|
272
|
+
dx = x - node.x
|
|
273
|
+
dy = y - node.y
|
|
274
|
+
left = dy * node.dx
|
|
275
|
+
right = dx * node.dy
|
|
276
|
+
right >= left ? 0 : 1
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|