doom 0.2.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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +159 -0
- data/bin/console +15 -0
- data/bin/doom +70 -0
- data/bin/setup +8 -0
- data/bin/wad +152 -0
- data/lib/doom/bsp_renderer.rb +90 -0
- data/lib/doom/game.rb +84 -0
- data/lib/doom/hud.rb +80 -0
- data/lib/doom/map_loader.rb +255 -0
- data/lib/doom/renderer.rb +32 -0
- data/lib/doom/sprite_loader.rb +88 -0
- data/lib/doom/sprite_renderer.rb +56 -0
- data/lib/doom/texture_loader.rb +138 -0
- data/lib/doom/texture_mapper.rb +57 -0
- data/lib/doom/version.rb +5 -0
- data/lib/doom/wad_loader.rb +106 -0
- data/lib/doom/window.rb +41 -0
- data/lib/doom.rb +46 -0
- metadata +105 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 77ff94b86295598915c520657fb36512c6fc091d24d6660cc8dea588a8939062
|
4
|
+
data.tar.gz: 3b3632ab2f9adbd0d8d4078c71f5c9f941943dd30d96c951bb27e803256a87d1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 61d4a1cf4f3542c3ce93d4dc624b2cc36c4d93be39a9d65cc832198be7bbd04b9bd86cc7723d13181b80907809005571d7a9f85217e628d6f5a5b7a02e35908a
|
7
|
+
data.tar.gz: 514bc92b65f0934cbf31828e4553d6978d86932afa690f70b89a69ed6d6bbe71cb98e0d3bfbe86916e0c799498366eedcad2f74904f78be84fc3d559f43a8dd3
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
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/README.md
ADDED
@@ -0,0 +1,159 @@
|
|
1
|
+
# Ruby Doom
|
2
|
+
|
3
|
+
A Ruby gem that ports the classic Doom game to Ruby, focusing on core gameplay without sound or networking features.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'doom'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
$ bundle install
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
$ gem install doom
|
23
|
+
```
|
24
|
+
|
25
|
+
## Requirements
|
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:
|
39
|
+
|
40
|
+
```bash
|
41
|
+
# Show general information about a WAD file
|
42
|
+
$ wad -i DOOM.WAD
|
43
|
+
|
44
|
+
# List all maps in a WAD file
|
45
|
+
$ wad -l DOOM.WAD
|
46
|
+
|
47
|
+
# Show details for a specific map
|
48
|
+
$ wad -m E1M1 DOOM.WAD
|
49
|
+
|
50
|
+
# List all textures in a WAD file
|
51
|
+
$ wad -t DOOM.WAD
|
52
|
+
|
53
|
+
# Show details for a specific texture
|
54
|
+
$ wad -x STARTAN1 DOOM.WAD
|
55
|
+
|
56
|
+
# List all sprites in a WAD file
|
57
|
+
$ wad -s DOOM.WAD
|
58
|
+
|
59
|
+
# Show details for a specific sprite
|
60
|
+
$ wad -p TROOA1 DOOM.WAD
|
61
|
+
|
62
|
+
# Show help
|
63
|
+
$ wad -h
|
64
|
+
```
|
65
|
+
|
66
|
+
#### Game Launcher
|
67
|
+
|
68
|
+
The `doom` command allows you to start the game:
|
69
|
+
|
70
|
+
```bash
|
71
|
+
# Start the game with default settings
|
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
|
85
|
+
```
|
86
|
+
|
87
|
+
### Ruby API
|
88
|
+
|
89
|
+
You can also use the Ruby API in your own code:
|
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(', ')}"
|
117
|
+
```
|
118
|
+
|
119
|
+
## Features
|
120
|
+
|
121
|
+
### Phase 1: WAD File Loader
|
122
|
+
|
123
|
+
- WAD file parsing
|
124
|
+
- Map data extraction
|
125
|
+
- Texture information parsing
|
126
|
+
- Sprite data handling
|
127
|
+
|
128
|
+
### Phase 2: Rendering Engine (Current)
|
129
|
+
|
130
|
+
- Window management
|
131
|
+
- BSP rendering
|
132
|
+
- Texture mapping
|
133
|
+
- Sprite rendering
|
134
|
+
- HUD implementation
|
135
|
+
- Basic game loop
|
136
|
+
|
137
|
+
### Future Phases
|
138
|
+
|
139
|
+
- Game mechanics
|
140
|
+
- Advanced game loop and state management
|
141
|
+
- Polishing
|
142
|
+
|
143
|
+
## Development
|
144
|
+
|
145
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
146
|
+
|
147
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
148
|
+
|
149
|
+
## Contributing
|
150
|
+
|
151
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/khasinski/doom-rb.
|
152
|
+
|
153
|
+
## License
|
154
|
+
|
155
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
156
|
+
|
157
|
+
## Legal
|
158
|
+
|
159
|
+
Users must own a legal copy of Doom for the WAD files. This project complies with id Software's terms regarding Doom content.
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
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/doom
ADDED
@@ -0,0 +1,70 @@
|
|
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
|
+
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
|
38
|
+
end
|
39
|
+
|
40
|
+
parser.parse!
|
41
|
+
|
42
|
+
if ARGV.empty?
|
43
|
+
puts "Error: WAD file is required"
|
44
|
+
puts parser
|
45
|
+
exit 1
|
46
|
+
end
|
47
|
+
|
48
|
+
wad_file = ARGV[0]
|
49
|
+
unless File.exist?(wad_file)
|
50
|
+
puts "Error: WAD file '#{wad_file}' not found"
|
51
|
+
exit 1
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
puts "Starting Doom with WAD file: #{wad_file}"
|
56
|
+
puts "Map: #{options[:map] || 'Default'}"
|
57
|
+
puts "Window size: #{options[:width]}x#{options[:height]}"
|
58
|
+
puts "Fullscreen: #{options[:fullscreen]}"
|
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
|
68
|
+
puts "Error: #{e.message}"
|
69
|
+
exit 1
|
70
|
+
end
|
data/bin/setup
ADDED
data/bin/wad
ADDED
@@ -0,0 +1,152 @@
|
|
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
|
@@ -0,0 +1,90 @@
|
|
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
|
data/lib/doom/game.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doom
|
4
|
+
# Game is the main class that ties everything together
|
5
|
+
class Game
|
6
|
+
attr_reader :window, :loaders, :renderers
|
7
|
+
|
8
|
+
# Initialize a new Game with the given WAD file
|
9
|
+
# @param wad_file [String] the path to the WAD file
|
10
|
+
# @param width [Integer] the width of the window
|
11
|
+
# @param height [Integer] the height of the window
|
12
|
+
# @param fullscreen [Boolean] whether the window should be fullscreen
|
13
|
+
def initialize(wad_file, width = 640, height = 480, fullscreen = false)
|
14
|
+
@wad_file = wad_file
|
15
|
+
@width = width
|
16
|
+
@height = height
|
17
|
+
@fullscreen = fullscreen
|
18
|
+
@loaders = {}
|
19
|
+
@renderers = {}
|
20
|
+
@current_map = nil
|
21
|
+
@player_data = {
|
22
|
+
health: 100,
|
23
|
+
armor: 0,
|
24
|
+
ammo: 50,
|
25
|
+
weapon: "Pistol"
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
# Start the game
|
30
|
+
# @param map_name [String] the name of the map to start on
|
31
|
+
def start(map_name = nil)
|
32
|
+
load_wad
|
33
|
+
setup_window
|
34
|
+
setup_renderers
|
35
|
+
load_map(map_name) if map_name
|
36
|
+
@window.show
|
37
|
+
end
|
38
|
+
|
39
|
+
# Load the WAD file
|
40
|
+
def load_wad
|
41
|
+
@loaders = Doom.load_wad(@wad_file)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Setup the window
|
45
|
+
def setup_window
|
46
|
+
@window = Window.new(@width, @height, @fullscreen)
|
47
|
+
@window.game = self
|
48
|
+
end
|
49
|
+
|
50
|
+
# Setup the renderers
|
51
|
+
def setup_renderers
|
52
|
+
@renderers[:bsp] = BSPRenderer.new(@window, @loaders[:maps])
|
53
|
+
@renderers[:texture] = TextureMapper.new(@loaders[:textures])
|
54
|
+
@renderers[:sprite] = SpriteRenderer.new(@loaders[:sprites])
|
55
|
+
@renderers[:hud] = HUD.new(@window)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Load a specific map
|
59
|
+
# @param map_name [String] the name of the map to load
|
60
|
+
def load_map(map_name)
|
61
|
+
@current_map = map_name
|
62
|
+
@renderers[:bsp].load_map(map_name)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Update the game state
|
66
|
+
def update
|
67
|
+
# Will be implemented in Phase 3
|
68
|
+
end
|
69
|
+
|
70
|
+
# Draw the game
|
71
|
+
def draw
|
72
|
+
@renderers[:bsp].render if @current_map
|
73
|
+
@renderers[:hud].render(@player_data)
|
74
|
+
end
|
75
|
+
|
76
|
+
# Handle button press events
|
77
|
+
def button_down(id)
|
78
|
+
case id
|
79
|
+
when Gosu::KB_ESCAPE
|
80
|
+
@window.close
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/lib/doom/hud.rb
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doom
|
4
|
+
# HUD is responsible for rendering the Heads-Up Display
|
5
|
+
class HUD
|
6
|
+
attr_reader :window
|
7
|
+
|
8
|
+
# Initialize a new HUD with the given window
|
9
|
+
# @param window [Doom::Window] the window to render to
|
10
|
+
def initialize(window)
|
11
|
+
@window = window
|
12
|
+
@font = Gosu::Font.new(20)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Render the HUD
|
16
|
+
# @param player_data [Hash] the player data to display
|
17
|
+
def render(player_data = {})
|
18
|
+
# In a real implementation, this would render:
|
19
|
+
# - Health
|
20
|
+
# - Armor
|
21
|
+
# - Ammo
|
22
|
+
# - Weapons
|
23
|
+
# - Face
|
24
|
+
# - Keys
|
25
|
+
# - etc.
|
26
|
+
|
27
|
+
# For now, we'll just render some placeholder text
|
28
|
+
health = player_data[:health] || 100
|
29
|
+
armor = player_data[:armor] || 0
|
30
|
+
ammo = player_data[:ammo] || 50
|
31
|
+
weapon = player_data[:weapon] || "Pistol"
|
32
|
+
|
33
|
+
# Draw the HUD background
|
34
|
+
draw_background
|
35
|
+
|
36
|
+
# Draw the HUD elements
|
37
|
+
draw_health(health)
|
38
|
+
draw_armor(armor)
|
39
|
+
draw_ammo(ammo)
|
40
|
+
draw_weapon(weapon)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Draw the HUD background
|
46
|
+
def draw_background
|
47
|
+
color = Gosu::Color.new(128, 0, 0, 0) # Semi-transparent black
|
48
|
+
@window.draw_quad(
|
49
|
+
0, @window.height - 80, color,
|
50
|
+
@window.width, @window.height - 80, color,
|
51
|
+
@window.width, @window.height, color,
|
52
|
+
0, @window.height, color
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Draw the health display
|
57
|
+
# @param health [Integer] the player's health
|
58
|
+
def draw_health(health)
|
59
|
+
@font.draw_text("Health: #{health}", 20, @window.height - 70, 0, 1, 1, Gosu::Color::RED)
|
60
|
+
end
|
61
|
+
|
62
|
+
# Draw the armor display
|
63
|
+
# @param armor [Integer] the player's armor
|
64
|
+
def draw_armor(armor)
|
65
|
+
@font.draw_text("Armor: #{armor}", 20, @window.height - 50, 0, 1, 1, Gosu::Color::GREEN)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Draw the ammo display
|
69
|
+
# @param ammo [Integer] the player's ammo
|
70
|
+
def draw_ammo(ammo)
|
71
|
+
@font.draw_text("Ammo: #{ammo}", @window.width - 120, @window.height - 70, 0, 1, 1, Gosu::Color::YELLOW)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Draw the weapon display
|
75
|
+
# @param weapon [String] the player's current weapon
|
76
|
+
def draw_weapon(weapon)
|
77
|
+
@font.draw_text("Weapon: #{weapon}", @window.width - 120, @window.height - 50, 0, 1, 1, Gosu::Color::WHITE)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|