doom 0.6.0 → 0.11.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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +17 -0
  3. data/README.md +65 -4
  4. data/bin/doom +138 -25
  5. data/lib/doom/benchmark.rb +282 -0
  6. data/lib/doom/game/animations.rb +9 -2
  7. data/lib/doom/game/combat.rb +377 -105
  8. data/lib/doom/game/framebuffer_blitter.rb +38 -0
  9. data/lib/doom/game/geometry.rb +68 -0
  10. data/lib/doom/game/intermission.rb +231 -0
  11. data/lib/doom/game/item_pickup.rb +108 -60
  12. data/lib/doom/game/menu.rb +347 -0
  13. data/lib/doom/game/monster_ai.rb +304 -91
  14. data/lib/doom/game/player.rb +105 -0
  15. data/lib/doom/game/player_physics.rb +384 -0
  16. data/lib/doom/game/player_state.rb +47 -64
  17. data/lib/doom/game/random.rb +82 -0
  18. data/lib/doom/game/sector_actions.rb +466 -29
  19. data/lib/doom/game/sector_effects.rb +25 -18
  20. data/lib/doom/game/snapshot.rb +584 -0
  21. data/lib/doom/game/sound_engine.rb +176 -0
  22. data/lib/doom/game/state_hash.rb +125 -0
  23. data/lib/doom/game/ticcmd.rb +61 -0
  24. data/lib/doom/game/world.rb +420 -0
  25. data/lib/doom/map/data.rb +95 -0
  26. data/lib/doom/net/client.rb +204 -0
  27. data/lib/doom/net/desync_monitor.rb +101 -0
  28. data/lib/doom/net/game_server.rb +232 -0
  29. data/lib/doom/net/lockstep.rb +170 -0
  30. data/lib/doom/net/protocol.rb +350 -0
  31. data/lib/doom/net/session.rb +282 -0
  32. data/lib/doom/net/transport.rb +110 -0
  33. data/lib/doom/platform/gosu_window.rb +820 -640
  34. data/lib/doom/platform/sdl.rb +74 -0
  35. data/lib/doom/platform/window_logic.rb +61 -0
  36. data/lib/doom/render/font.rb +80 -0
  37. data/lib/doom/render/hardware_renderer.rb +547 -0
  38. data/lib/doom/render/ray_tracing/bvh.rb +103 -0
  39. data/lib/doom/render/ray_tracing/material_state.rb +66 -0
  40. data/lib/doom/render/ray_tracing/texture_atlas.rb +78 -0
  41. data/lib/doom/render/ray_tracing_renderer.rb +940 -0
  42. data/lib/doom/render/renderer.rb +684 -330
  43. data/lib/doom/render/renderer_factory.rb +50 -0
  44. data/lib/doom/render/screen_melt.rb +71 -0
  45. data/lib/doom/render/spinel_native/kernel.rb +780 -0
  46. data/lib/doom/render/spinel_native_renderer.rb +162 -0
  47. data/lib/doom/render/status_bar.rb +14 -10
  48. data/lib/doom/render/weapon_renderer.rb +15 -17
  49. data/lib/doom/render/world_mesh.rb +270 -0
  50. data/lib/doom/render/zbuffer_renderer.rb +151 -0
  51. data/lib/doom/version.rb +1 -1
  52. data/lib/doom/wad/flat.rb +1 -1
  53. data/lib/doom/wad/hud_graphics.rb +7 -3
  54. data/lib/doom/wad/palette.rb +3 -3
  55. data/lib/doom/wad/patch.rb +1 -1
  56. data/lib/doom/wad/reader.rb +59 -10
  57. data/lib/doom/wad/sound.rb +93 -0
  58. data/lib/doom/wad/sprite.rb +60 -34
  59. data/lib/doom/wad/texture.rb +5 -5
  60. data/lib/doom/wad_downloader.rb +38 -56
  61. data/lib/doom.rb +235 -12
  62. metadata +100 -7
@@ -9,7 +9,7 @@ module Doom
9
9
  class WadDownloader
10
10
  # Doom shareware WAD v1.9 (freely distributable)
11
11
  SHAREWARE_URL = 'https://raw.githubusercontent.com/Akbar30Bill/DOOM_wads/master/doom1.wad'
12
- SHAREWARE_SIZE = 4_196_020 # Expected size in bytes
12
+ SHAREWARE_SIZE = 4_196_020 # Expected size in bytes
13
13
  SHAREWARE_FILENAME = 'doom1.wad'
14
14
 
15
15
  class DownloadError < StandardError; end
@@ -27,20 +27,18 @@ module Doom
27
27
  return home_wad if File.exist?(home_wad)
28
28
 
29
29
  # No WAD found - offer to download
30
- if custom_path
31
- raise DownloadError, "WAD file not found: #{custom_path}"
32
- end
30
+ raise DownloadError, "WAD file not found: #{custom_path}" if custom_path
33
31
 
34
32
  prompt_and_download(home_wad)
35
33
  end
36
34
 
37
35
  def self.prompt_and_download(destination)
38
- puts "No DOOM WAD file found."
36
+ puts 'No DOOM WAD file found.'
39
37
  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."
38
+ puts 'Would you like to download the shareware version of DOOM (4 MB)?'
39
+ puts 'This is the free, legally distributable version with Episode 1.'
42
40
  puts
43
- print "Download shareware DOOM? [Y/n] "
41
+ print 'Download shareware DOOM? [Y/n] '
44
42
 
45
43
  response = $stdin.gets&.strip&.downcase
46
44
  if response.nil? || response.empty? || response == 'y' || response == 'yes'
@@ -48,56 +46,22 @@ module Doom
48
46
  destination
49
47
  else
50
48
  puts
51
- puts "To play DOOM, you need a WAD file. Options:"
49
+ puts 'To play DOOM, you need a WAD file. Options:'
52
50
  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"
51
+ puts ' 2. Copy your own doom1.wad or doom.wad to the current directory'
52
+ puts ' 3. Specify a WAD path: doom /path/to/your.wad'
55
53
  exit 1
56
54
  end
57
55
  end
58
56
 
59
57
  def self.download_shareware(destination)
60
58
  puts
61
- puts "Downloading DOOM shareware..."
59
+ puts 'Downloading DOOM shareware...'
62
60
 
63
61
  # Create directory if needed
64
62
  FileUtils.mkdir_p(File.dirname(destination))
65
63
 
66
- uri = URI.parse(SHAREWARE_URL)
67
- downloaded = 0
68
-
69
- ssl_opts = { use_ssl: uri.scheme == 'https' }
70
- # Workaround for SSL certificate issues on some systems
71
- begin
72
- Net::HTTP.start(uri.host, uri.port, **ssl_opts) { |h| h.head(uri) }
73
- rescue OpenSSL::SSL::SSLError
74
- puts "SSL certificate verification failed, retrying without verification..."
75
- ssl_opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE
76
- end
77
-
78
- Net::HTTP.start(uri.host, uri.port, **ssl_opts) do |http|
79
- request = Net::HTTP::Get.new(uri)
80
-
81
- http.request(request) do |response|
82
- case response
83
- when Net::HTTPRedirection
84
- # Follow redirect
85
- return download_from_url(response['location'], destination)
86
- when Net::HTTPSuccess
87
- total_size = response['content-length']&.to_i || SHAREWARE_SIZE
88
-
89
- File.open(destination, 'wb') do |file|
90
- response.read_body do |chunk|
91
- file.write(chunk)
92
- downloaded += chunk.size
93
- print_progress(downloaded, total_size)
94
- end
95
- end
96
- else
97
- raise DownloadError, "Download failed: #{response.code} #{response.message}"
98
- end
99
- end
100
- end
64
+ download_from_url(SHAREWARE_URL, destination)
101
65
 
102
66
  puts
103
67
  puts "Downloaded to: #{destination}"
@@ -105,23 +69,41 @@ module Doom
105
69
 
106
70
  # Verify file size
107
71
  actual_size = File.size(destination)
108
- if actual_size < 1_000_000 # Less than 1MB is suspicious
109
- File.delete(destination)
110
- raise DownloadError, "Download appears incomplete (#{actual_size} bytes)"
111
- end
72
+ return unless actual_size < 1_000_000 # Less than 1MB is suspicious
73
+
74
+ File.delete(destination)
75
+ raise DownloadError, "Download appears incomplete (#{actual_size} bytes)"
112
76
  end
113
77
 
114
- def self.download_from_url(url, destination)
78
+ # Fetch a URL to a destination file, following redirects and streaming a
79
+ # progress bar. Retries without SSL verification if the certificate cannot
80
+ # be verified (a workaround for broken system cert stores) -- and because
81
+ # the check runs here, it applies to redirect targets too, not just the
82
+ # first URL.
83
+ def self.download_from_url(url, destination, redirect_limit: 5)
84
+ raise DownloadError, 'Too many redirects' if redirect_limit.negative?
85
+
115
86
  uri = URI.parse(url)
116
- downloaded = 0
87
+ ssl_opts = { use_ssl: uri.scheme == 'https' }
117
88
 
118
- Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
89
+ if ssl_opts[:use_ssl]
90
+ begin
91
+ Net::HTTP.start(uri.host, uri.port, **ssl_opts) { |h| h.head(uri) }
92
+ rescue OpenSSL::SSL::SSLError
93
+ puts 'SSL certificate verification failed, retrying without verification...'
94
+ ssl_opts[:verify_mode] = OpenSSL::SSL::VERIFY_NONE
95
+ end
96
+ end
97
+
98
+ downloaded = 0
99
+ Net::HTTP.start(uri.host, uri.port, **ssl_opts) do |http|
119
100
  request = Net::HTTP::Get.new(uri)
120
101
 
121
102
  http.request(request) do |response|
122
103
  case response
123
104
  when Net::HTTPRedirection
124
- return download_from_url(response['location'], destination)
105
+ return download_from_url(response['location'], destination,
106
+ redirect_limit: redirect_limit - 1)
125
107
  when Net::HTTPSuccess
126
108
  total_size = response['content-length']&.to_i || SHAREWARE_SIZE
127
109
 
@@ -143,7 +125,7 @@ module Doom
143
125
  percent = (downloaded.to_f / total * 100).to_i
144
126
  bar_width = 40
145
127
  filled = (percent * bar_width / 100)
146
- bar = '=' * filled + '-' * (bar_width - filled)
128
+ bar = ('=' * filled) + ('-' * (bar_width - filled))
147
129
  mb_downloaded = (downloaded / 1_048_576.0).round(1)
148
130
  mb_total = (total / 1_048_576.0).round(1)
149
131
  print "\r[#{bar}] #{percent}% (#{mb_downloaded}/#{mb_total} MB)"
data/lib/doom.rb CHANGED
@@ -11,14 +11,43 @@ require_relative 'doom/wad/texture'
11
11
  require_relative 'doom/wad/sprite'
12
12
  require_relative 'doom/wad/hud_graphics'
13
13
  require_relative 'doom/map/data'
14
+ require_relative 'doom/game/random'
15
+ require_relative 'doom/game/geometry'
16
+ require_relative 'doom/game/state_hash'
14
17
  require_relative 'doom/game/player_state'
18
+ require_relative 'doom/game/player'
19
+ require_relative 'doom/game/ticcmd'
20
+ require_relative 'doom/game/player_physics'
15
21
  require_relative 'doom/game/sector_actions'
16
22
  require_relative 'doom/game/animations'
17
23
  require_relative 'doom/game/item_pickup'
18
24
  require_relative 'doom/game/combat'
19
25
  require_relative 'doom/game/sector_effects'
20
26
  require_relative 'doom/game/monster_ai'
27
+ require_relative 'doom/game/world'
28
+ require_relative 'doom/game/snapshot'
29
+ require_relative 'doom/net/protocol'
30
+ require_relative 'doom/net/transport'
31
+ require_relative 'doom/net/desync_monitor'
32
+ require_relative 'doom/net/lockstep'
33
+ require_relative 'doom/net/session'
34
+ require_relative 'doom/net/game_server'
35
+ require_relative 'doom/net/client'
36
+ require_relative 'doom/game/menu'
37
+ require_relative 'doom/game/intermission'
38
+ require_relative 'doom/wad/sound'
39
+ require_relative 'doom/game/sound_engine'
40
+ require_relative 'doom/render/font'
21
41
  require_relative 'doom/render/renderer'
42
+ require_relative 'doom/render/zbuffer_renderer'
43
+ require_relative 'doom/render/world_mesh'
44
+ require_relative 'doom/render/hardware_renderer'
45
+ require_relative 'doom/render/ray_tracing/bvh'
46
+ require_relative 'doom/render/ray_tracing/material_state'
47
+ require_relative 'doom/render/ray_tracing/texture_atlas'
48
+ require_relative 'doom/render/ray_tracing_renderer'
49
+ require_relative 'doom/render/renderer_factory'
50
+ require_relative 'doom/render/screen_melt'
22
51
  require_relative 'doom/render/status_bar'
23
52
  require_relative 'doom/render/weapon_renderer'
24
53
  require_relative 'doom/platform/gosu_window'
@@ -27,11 +56,40 @@ module Doom
27
56
  class Error < StandardError; end
28
57
 
29
58
  class << self
30
- def run(wad_path, map_name: 'E1M1')
59
+ def run(wad_path, map_name: 'E1M1', rubykaigi: false, net: nil, frag_limit: nil, renderer: :classic)
60
+ return run_server(wad_path, map_name, net, frag_limit) if net && net[:role] == :serve
61
+ return run_client(wad_path, map_name, net, renderer) if net && net[:role] == :join
62
+
63
+ session = build_session(net, map_name)
64
+ if session
65
+ # The host decides the map, seed and mode; a client is told them. Wait
66
+ # for that before loading anything, so both sides load the same level.
67
+ map_name = wait_for_session(session) || map_name
68
+ end
69
+
31
70
  puts "Loading WAD: #{wad_path}"
32
71
  wad = Wad::Reader.new(wad_path)
33
72
  puts " #{wad.type}: #{wad.num_lumps} lumps"
34
73
 
74
+ # If it's a PWAD, we need a base IWAD for resources (palette, textures, etc.)
75
+ if wad.pwad?
76
+ iwad_path = find_iwad
77
+ unless iwad_path
78
+ raise Error,
79
+ 'PWAD requires a base IWAD (doom1.wad). Place it in the current directory or ~/.doom/'
80
+ end
81
+
82
+ puts " PWAD detected, loading base IWAD: #{iwad_path}"
83
+ base_wad = Wad::Reader.new(iwad_path)
84
+ base_wad.merge_pwad(wad)
85
+ wad = base_wad
86
+
87
+ # Auto-detect map name from PWAD lumps
88
+ pwad_map = wad.directory.find { |e| e.name =~ /^(E\dM\d|MAP\d\d)$/ }
89
+ map_name = pwad_map.name if pwad_map
90
+ puts " Using map: #{map_name}"
91
+ end
92
+
35
93
  puts 'Loading palette...'
36
94
  palette = Wad::Palette.load(wad)
37
95
 
@@ -63,7 +121,7 @@ module Doom
63
121
  puts " Player start: (#{player_start.x}, #{player_start.y}) angle #{player_start.angle}"
64
122
  else
65
123
  puts ' Warning: No player start found!'
66
- player_start = Map::Thing.new(0, 0, 90, 1, 0)
124
+ Map::Thing.new(0, 0, 90, 1, 0)
67
125
  end
68
126
 
69
127
  puts 'Initializing animations...'
@@ -71,22 +129,187 @@ module Doom
71
129
  animations = Game::Animations.new(textures.texture_names, flat_names)
72
130
 
73
131
  puts 'Creating renderer...'
74
- renderer = Render::Renderer.new(wad, map, textures, palette, colormap, flats, sprites, animations)
75
- renderer.set_player(player_start.x, player_start.y, 41, player_start.angle)
132
+ # No initial camera pose needed: the world owns the player, and draw aims
133
+ # the camera at it every frame.
134
+ renderer = Render::RendererFactory.build(renderer, wad, map, textures, palette, colormap,
135
+ flats, sprites, animations)
76
136
 
77
- puts 'Setting up player state and HUD...'
78
- player_state = Game::PlayerState.new
137
+ puts 'Building world...'
138
+ sound_mgr = Wad::SoundManager.new(wad)
139
+ sound_engine = Game::SoundEngine.new(sound_mgr)
140
+ # One RNG shared by every subsystem that touches game state -- its index is
141
+ # part of the simulation, so all peers must draw from the same sequence.
142
+ # In a session the seed and mode come from the host, so every peer builds
143
+ # the same world; solo play picks its own.
144
+ random = Game::Random.new(session ? session.seed : 0)
145
+ world = Game::World.new(map, sprites: sprites, sound: sound_engine, random: random,
146
+ mode: session ? session.mode : :coop,
147
+ frag_limit: frag_limit)
148
+ ids = session ? session.player_ids : [0]
149
+ ids.each { |id| world.add_player(id: id) }
150
+ player = world.player(session ? session.local_id : 0)
151
+ player_state = player.state
152
+
153
+ puts 'Setting up HUD...'
79
154
  status_bar = Render::StatusBar.new(hud_graphics, player_state)
80
155
  weapon_renderer = Render::WeaponRenderer.new(hud_graphics, player_state)
81
- sector_actions = Game::SectorActions.new(map)
82
- sector_effects = Game::SectorEffects.new(map)
83
- item_pickup = Game::ItemPickup.new(map, player_state)
84
- combat = Game::Combat.new(map, player_state, sprites)
85
- monster_ai = Game::MonsterAI.new(map, combat)
156
+ doom_font = Render::Font.new(wad, hud_graphics)
157
+ menu = Game::Menu.new(wad, hud_graphics, doom_font)
158
+ menu.instance_variable_set(:@state, Game::Menu::STATE_NONE) if ENV['DOOM_DEBUG_POSE']
159
+
160
+ # RubyKaigi mode: god mode, no aggression, benchmark HUD
161
+ if rubykaigi
162
+ menu.options[:rubykaigi_mode] = true
163
+ menu.options[:god_mode] = true
164
+ end
86
165
 
87
166
  puts 'Starting game window...'
88
- window = Platform::GosuWindow.new(renderer, palette, map, player_state, status_bar, weapon_renderer, sector_actions, animations, sector_effects, item_pickup, combat, monster_ai)
167
+ window = Platform::GosuWindow.new(renderer, palette, world, status_bar, weapon_renderer,
168
+ animations, menu, sound_engine, session: session)
169
+ # A networked game skips the title screen: the other players are already
170
+ # waiting, and there is nothing to configure that the host has not set.
171
+ menu.instance_variable_set(:@state, Game::Menu::STATE_NONE) if session
89
172
  window.show
173
+ ensure
174
+ session&.quit
175
+ end
176
+
177
+ private
178
+
179
+ # Headless authoritative server. Loads only what the simulation needs (no
180
+ # renderer, HUD or window), then runs the tic loop forever, letting clients
181
+ # join, play and leave. The one place the engine runs without a display.
182
+ def run_server(wad_path, map_name, net, frag_limit)
183
+ wad = Wad::Reader.new(wad_path)
184
+ wad = merge_pwad_if_needed(wad) { |name| map_name = name }
185
+ sprites = Wad::SpriteManager.new(wad)
186
+ map = Map::MapData.load(wad, map_name)
187
+
188
+ world = Game::World.new(map, sprites: sprites, mode: net[:mode],
189
+ random: Game::Random.new, frag_limit: frag_limit)
190
+ transport = Net::Transport.new(port: net[:port])
191
+ server = Net::GameServer.new(world: world, transport: transport, max_players: net[:players])
192
+
193
+ puts "Serving #{net[:mode]} on #{map_name}, port #{transport.local_port}."
194
+ puts 'Players join with: doom --join <this-host>:%d' % transport.local_port
195
+ puts 'Ctrl-C to stop.'
196
+
197
+ last_report = Time.now
198
+ loop do
199
+ server.poll
200
+ server.advance(limit: 8)
201
+ server.reap
202
+ now = Time.now
203
+ if now - last_report >= 2.0
204
+ puts " tic #{server.tic}, #{server.player_count} players"
205
+ last_report = now
206
+ end
207
+ sleep 0.002
208
+ end
209
+ ensure
210
+ transport&.close
211
+ end
212
+
213
+ # Join an authoritative server. The world comes from the server as a
214
+ # snapshot, so we block on the join (there is nothing to render yet), then
215
+ # open a window that follows the server's frame stream instead of
216
+ # simulating locally.
217
+ def run_client(wad_path, _map_name, net, renderer_type)
218
+ wad = merge_pwad_if_needed(Wad::Reader.new(wad_path)) { |name| name }
219
+ palette = Wad::Palette.load(wad)
220
+ colormap = Wad::Colormap.load(wad)
221
+ flats = Wad::Flat.load_all(wad)
222
+ textures = Wad::TextureManager.new(wad)
223
+ sprites = Wad::SpriteManager.new(wad)
224
+ hud_graphics = Wad::HudGraphics.new(wad)
225
+ animations = Game::Animations.new(textures.texture_names, flats.map(&:name))
226
+ sound_engine = Game::SoundEngine.new(Wad::SoundManager.new(wad))
227
+
228
+ puts "Connecting to #{net[:host]}:#{net[:port]}..."
229
+ client = Net::Client.new(host: net[:host], port: net[:port], sprites: sprites,
230
+ sound: sound_engine,
231
+ load_map: ->(name) { Map::MapData.load(wad, name) })
232
+ wait_for_client(client)
233
+ puts " Joined as player #{client.local_id}."
234
+
235
+ world = client.world
236
+ renderer = Render::RendererFactory.build(renderer_type, wad, world.map, textures, palette, colormap,
237
+ flats, sprites, animations)
238
+ player_state = world.player(client.local_id).state
239
+ status_bar = Render::StatusBar.new(hud_graphics, player_state)
240
+ weapon_renderer = Render::WeaponRenderer.new(hud_graphics, player_state)
241
+ menu = Game::Menu.new(wad, hud_graphics, Render::Font.new(wad, hud_graphics))
242
+ menu.instance_variable_set(:@state, Game::Menu::STATE_NONE)
243
+
244
+ window = Platform::GosuWindow.new(renderer, palette, world, status_bar, weapon_renderer,
245
+ animations, menu, sound_engine, client: client)
246
+ window.show
247
+ ensure
248
+ client&.quit
249
+ end
250
+
251
+ def wait_for_client(client, timeout: 30)
252
+ deadline = Time.now + timeout
253
+ until client.playing?
254
+ raise Error, 'timed out joining the server' if Time.now > deadline
255
+
256
+ client.poll
257
+ sleep 0.01
258
+ end
259
+ end
260
+
261
+ # Shared PWAD handling: if `wad` is a PWAD, merge it onto a base IWAD and
262
+ # yield the auto-detected map name. Returns the wad to use.
263
+ def merge_pwad_if_needed(wad)
264
+ return wad unless wad.pwad?
265
+
266
+ iwad_path = find_iwad
267
+ raise Error, 'PWAD requires a base IWAD (doom1.wad) in the current directory or ~/.doom/' unless iwad_path
268
+
269
+ base = Wad::Reader.new(iwad_path)
270
+ base.merge_pwad(wad)
271
+ pwad_map = base.directory.find { |e| e.name =~ /^(E\dM\d|MAP\d\d)$/ }
272
+ yield pwad_map.name if pwad_map && block_given?
273
+ base
274
+ end
275
+
276
+ def build_session(net, map_name)
277
+ return nil unless net
278
+
279
+ case net[:role]
280
+ when :host
281
+ puts "Hosting on port #{net[:port]}, waiting for #{net[:players]} players..."
282
+ Net::Session.host(port: net[:port], players: net[:players],
283
+ map: map_name, mode: net[:mode])
284
+ when :client
285
+ puts "Connecting to #{net[:host]}:#{net[:port]}..."
286
+ Net::Session.connect(host: net[:host], port: net[:port])
287
+ end
288
+ end
289
+
290
+ # Block until the handshake finishes. This is the one place blocking is
291
+ # right: there is no game to render yet.
292
+ def wait_for_session(session, timeout: 120)
293
+ deadline = Time.now + timeout
294
+ until session.started?
295
+ raise Error, 'timed out waiting for the other players' if Time.now > deadline
296
+
297
+ session.poll
298
+ sleep 0.02
299
+ end
300
+
301
+ puts " Joined as player #{session.local_id} of #{session.num_players} " \
302
+ "(#{session.mode}, map #{session.map_name})"
303
+ session.map_name
304
+ end
305
+
306
+ def find_iwad
307
+ candidates = [
308
+ File.join(Dir.pwd, 'doom1.wad'),
309
+ File.join(Dir.pwd, 'doom.wad'),
310
+ File.join(Dir.home, '.doom', 'doom1.wad')
311
+ ]
312
+ candidates.find { |p| File.exist?(p) }
90
313
  end
91
314
  end
92
315
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: doom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Hasinski
@@ -23,6 +23,34 @@ dependencies:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
25
  version: '1.4'
26
+ - !ruby/object:Gem::Dependency
27
+ name: fiddle
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: opengl-bindings
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.6'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.6'
26
54
  - !ruby/object:Gem::Dependency
27
55
  name: rspec
28
56
  requirement: !ruby/object:Gem::Requirement
@@ -51,8 +79,36 @@ dependencies:
51
79
  - - "~>"
52
80
  - !ruby/object:Gem::Version
53
81
  version: '1.4'
54
- description: A faithful port of the Doom (1993) rendering engine to Ruby. Supports
55
- original WAD files with near pixel-perfect BSP rendering.
82
+ - !ruby/object:Gem::Dependency
83
+ name: rubocop
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.90'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.90'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rubocop-performance
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.27'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.27'
110
+ description: A faithful port of the Doom (1993) engine to Ruby with classic software
111
+ rendering, OpenGL rasterization, GPU ray tracing, and multiplayer.
56
112
  email:
57
113
  - krzysztof.hasinski@gmail.com
58
114
  executables:
@@ -60,21 +116,56 @@ executables:
60
116
  extensions: []
61
117
  extra_rdoc_files: []
62
118
  files:
119
+ - LICENSE
63
120
  - README.md
64
121
  - bin/doom
65
122
  - lib/doom.rb
123
+ - lib/doom/benchmark.rb
66
124
  - lib/doom/game/animations.rb
67
125
  - lib/doom/game/combat.rb
126
+ - lib/doom/game/framebuffer_blitter.rb
127
+ - lib/doom/game/geometry.rb
128
+ - lib/doom/game/intermission.rb
68
129
  - lib/doom/game/item_pickup.rb
130
+ - lib/doom/game/menu.rb
69
131
  - lib/doom/game/monster_ai.rb
132
+ - lib/doom/game/player.rb
133
+ - lib/doom/game/player_physics.rb
70
134
  - lib/doom/game/player_state.rb
135
+ - lib/doom/game/random.rb
71
136
  - lib/doom/game/sector_actions.rb
72
137
  - lib/doom/game/sector_effects.rb
138
+ - lib/doom/game/snapshot.rb
139
+ - lib/doom/game/sound_engine.rb
140
+ - lib/doom/game/state_hash.rb
141
+ - lib/doom/game/ticcmd.rb
142
+ - lib/doom/game/world.rb
73
143
  - lib/doom/map/data.rb
144
+ - lib/doom/net/client.rb
145
+ - lib/doom/net/desync_monitor.rb
146
+ - lib/doom/net/game_server.rb
147
+ - lib/doom/net/lockstep.rb
148
+ - lib/doom/net/protocol.rb
149
+ - lib/doom/net/session.rb
150
+ - lib/doom/net/transport.rb
74
151
  - lib/doom/platform/gosu_window.rb
152
+ - lib/doom/platform/sdl.rb
153
+ - lib/doom/platform/window_logic.rb
154
+ - lib/doom/render/font.rb
155
+ - lib/doom/render/hardware_renderer.rb
156
+ - lib/doom/render/ray_tracing/bvh.rb
157
+ - lib/doom/render/ray_tracing/material_state.rb
158
+ - lib/doom/render/ray_tracing/texture_atlas.rb
159
+ - lib/doom/render/ray_tracing_renderer.rb
75
160
  - lib/doom/render/renderer.rb
161
+ - lib/doom/render/renderer_factory.rb
162
+ - lib/doom/render/screen_melt.rb
163
+ - lib/doom/render/spinel_native/kernel.rb
164
+ - lib/doom/render/spinel_native_renderer.rb
76
165
  - lib/doom/render/status_bar.rb
77
166
  - lib/doom/render/weapon_renderer.rb
167
+ - lib/doom/render/world_mesh.rb
168
+ - lib/doom/render/zbuffer_renderer.rb
78
169
  - lib/doom/version.rb
79
170
  - lib/doom/wad/colormap.rb
80
171
  - lib/doom/wad/flat.rb
@@ -82,13 +173,15 @@ files:
82
173
  - lib/doom/wad/palette.rb
83
174
  - lib/doom/wad/patch.rb
84
175
  - lib/doom/wad/reader.rb
176
+ - lib/doom/wad/sound.rb
85
177
  - lib/doom/wad/sprite.rb
86
178
  - lib/doom/wad/texture.rb
87
179
  - lib/doom/wad_downloader.rb
88
180
  homepage: https://github.com/khasinski/doom
89
181
  licenses:
90
- - GPL-2.0
91
- metadata: {}
182
+ - GPL-2.0-only
183
+ metadata:
184
+ rubygems_mfa_required: 'true'
92
185
  rdoc_options: []
93
186
  require_paths:
94
187
  - lib
@@ -103,7 +196,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
196
  - !ruby/object:Gem::Version
104
197
  version: '0'
105
198
  requirements: []
106
- rubygems_version: 4.0.6
199
+ rubygems_version: 4.0.16
107
200
  specification_version: 4
108
- summary: Doom engine port in pure Ruby
201
+ summary: Doom engine port with multiplayer and modern renderers
109
202
  test_files: []