nonogamer9

t

Oct 13th, 2026
38
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.05 KB | None | 0 0
  1. -- DOOM engine for OpenComputers (single file, Lua 5.2/5.3)
  2. -- Usage: doom [file.wad] [mapname] (default WAD: /home/doom1.wad or doom.wad)
  3. -- Needs: Tier 2+ GPU & screen (Tier 3 recommended), keyboard. IWAD (doom1.wad/doom.wad/doom2.wad/freedoom).
  4. -- Controls: W/S or Up/Down move, Left/Right (or A/D with Q/E strafe) turn,
  5. -- Ctrl/Space/F = fire, E / Enter = use (doors, lifts, switches),
  6. -- Shift = run, 1-7 = weapon, Tab = toggle minimap, Esc/X = quit.
  7.  
  8. local okc, component = pcall(require, "component")
  9. local okv, computer = pcall(require, "computer")
  10. local oke, event = pcall(require, "event")
  11. local floor, ceil, abs, sin, cos, sqrt, atan2, min, max =
  12. math.floor, math.ceil, math.abs, math.sin, math.cos, math.sqrt, math.atan2 or math.atan, math.min, math.max
  13. local PI = math.pi
  14.  
  15. ------------------------------------------------------------ binary helpers
  16. local function u8(s, o) return s:byte(o + 1) end
  17. local function u16(s, o) local a, b = s:byte(o + 1, o + 2); return a + b * 256 end
  18. local function s16(s, o) local v = u16(s, o); if v >= 32768 then v = v - 65536 end; return v end
  19. local function u32(s, o) local a, b, c, d = s:byte(o + 1, o + 4); return a + b * 256 + c * 65536 + d * 16777216 end
  20. local function cstr(s, o, n)
  21. local r = s:sub(o + 1, o + n); local z = r:find("\0", 1, true)
  22. if z then r = r:sub(1, z - 1) end
  23. return r:upper()
  24. end
  25.  
  26. ------------------------------------------------------------ WAD loading
  27. local wad = { lumps = {}, names = {}, fh = nil }
  28. local function openWad(path)
  29. local f, err = io.open(path, "rb")
  30. if not f then error("cannot open WAD: " .. tostring(path) .. " (" .. tostring(err) .. ")") end
  31. wad.fh = f
  32. local hdr = f:read(12)
  33. local id = hdr:sub(1, 4)
  34. if id ~= "IWAD" and id ~= "PWAD" then error("not a WAD file") end
  35. local n, dirofs = u32(hdr, 4), u32(hdr, 8)
  36. f:seek("set", dirofs)
  37. local dir = f:read(n * 16)
  38. for i = 0, n - 1 do
  39. local o = i * 16
  40. local l = { pos = u32(dir, o), size = u32(dir, o + 4), name = cstr(dir, o + 8, 8) }
  41. wad.lumps[i + 1] = l
  42. wad.names[l.name] = i + 1 -- later lumps override earlier (PWAD support)
  43. end
  44. wad.count = n
  45. end
  46. -- merge extra WAD (PWAD) over base
  47. local function lumpData(i)
  48. local l = wad.lumps[i]; if not l or l.size == 0 then return "" end
  49. local f = l.fh or wad.fh
  50. f:seek("set", l.pos)
  51. return f:read(l.size) or ""
  52. end
  53. local function addPwad(path)
  54. local f = io.open(path, "rb"); if not f then return false end
  55. local hdr = f:read(12)
  56. local n, dirofs = u32(hdr, 4), u32(hdr, 8)
  57. f:seek("set", dirofs)
  58. local dir = f:read(n * 16)
  59. for i = 0, n - 1 do
  60. local o = i * 16
  61. local l = { pos = u32(dir, o), size = u32(dir, o + 4), name = cstr(dir, o + 8, 8), fh = f }
  62. wad.lumps[#wad.lumps + 1] = l
  63. wad.names[l.name] = #wad.lumps
  64. end
  65. return true
  66. end
  67. local function findLump(name) return wad.names[name] end
  68. local function getLump(name)
  69. local i = wad.names[name]; if not i then return nil end
  70. return lumpData(i)
  71. end
  72. -- lump index search starting after a marker index (for map lumps)
  73. local function mapLump(mapIdx, name)
  74. for i = mapIdx + 1, min(mapIdx + 12, #wad.lumps) do
  75. if wad.lumps[i].name == name then return lumpData(i) end
  76. end
  77. return ""
  78. end
  79.  
  80. ------------------------------------------------------------ cooperative yield (OC kills long-running code)
  81. local lastYield = 0
  82. local function yieldNow(force)
  83. if not okv then return end
  84. local t = computer.uptime()
  85. if force or t - lastYield > 1.5 then
  86. computer.pullSignal(0)
  87. lastYield = computer.uptime()
  88. end
  89. end
  90.  
  91. ------------------------------------------------------------ palette / colormap
  92. local pal = {} -- pal[i] -> 0xRRGGBB
  93. local rgbcm = {} -- rgbcm[cm*256 + i + 1] -> 0xRRGGBB (cm 0..33)
  94. local function initPalette()
  95. local pp = getLump("PLAYPAL"); local cmap = getLump("COLORMAP")
  96. if not pp or not cmap then error("WAD has no PLAYPAL/COLORMAP") end
  97. for i = 0, 255 do
  98. local r, g, b = pp:byte(i * 3 + 1, i * 3 + 3)
  99. pal[i] = r * 65536 + g * 256 + b
  100. end
  101. for cm = 0, 33 do
  102. for i = 0, 255 do
  103. rgbcm[cm * 256 + i + 1] = pal[cmap:byte(cm * 256 + i + 1)]
  104. end
  105. end
  106. end
  107.  
  108. ------------------------------------------------------------ patches / textures / flats / sprites
  109. local unpack = table.unpack or unpack
  110.  
  111. local function parsePatch(data)
  112. if #data < 8 then return nil end
  113. local w, h, lo, to = u16(data, 0), u16(data, 2), s16(data, 4), s16(data, 6)
  114. local cols = {}
  115. for x = 0, w - 1 do
  116. local p = u32(data, 8 + x * 4)
  117. local posts = {}
  118. while true do
  119. local td = data:byte(p + 1)
  120. if not td or td == 255 then break end
  121. local len = data:byte(p + 2)
  122. posts[#posts + 1] = { td, len, data:sub(p + 4, p + 3 + len) }
  123. p = p + len + 4
  124. end
  125. cols[x + 1] = posts
  126. end
  127. return { w = w, h = h, lo = lo, to = to, cols = cols }
  128. end
  129.  
  130. local texdefs, texcache = {}, {}
  131. local pnames = {}
  132. local function initTextures()
  133. local pn = getLump("PNAMES")
  134. if pn then
  135. local n = u32(pn, 0)
  136. for i = 0, n - 1 do pnames[i] = cstr(pn, 4 + i * 8, 8) end
  137. end
  138. for _, lname in ipairs({ "TEXTURE1", "TEXTURE2" }) do
  139. local d = getLump(lname)
  140. if d and #d > 4 then
  141. local n = u32(d, 0)
  142. for i = 0, n - 1 do
  143. local o = u32(d, 4 + i * 4)
  144. local def = { w = u16(d, o + 12), h = u16(d, o + 14), patches = {} }
  145. local pc = u16(d, o + 20)
  146. for k = 0, pc - 1 do
  147. local po = o + 22 + k * 10
  148. def.patches[k + 1] = { ox = s16(d, po), oy = s16(d, po + 2), pn = u16(d, po + 4) }
  149. end
  150. texdefs[cstr(d, o, 8)] = def
  151. end
  152. end
  153. end
  154. end
  155.  
  156. local patchCache = {}
  157. local function getPatch(name)
  158. local p = patchCache[name]
  159. if p == nil then
  160. local d = getLump(name)
  161. p = d and parsePatch(d) or false
  162. patchCache[name] = p
  163. end
  164. return p or nil
  165. end
  166.  
  167. -- texture returns {w,h,cols={[x+1]=string(h bytes)}} (255 = transparent gap)
  168. local function getTexture(name)
  169. if name == "-" or name == "" then return nil end
  170. local t = texcache[name]
  171. if t ~= nil then return t or nil end
  172. local def = texdefs[name]
  173. if not def then texcache[name] = false; return nil end
  174. local w, h = def.w, def.h
  175. local buf = {}
  176. for x = 1, w do
  177. local c = {}
  178. for y = 1, h do c[y] = 255 end
  179. buf[x] = c
  180. end
  181. for _, pr in ipairs(def.patches) do
  182. local pat = getPatch(pnames[pr.pn] or "")
  183. if pat then
  184. for px = 0, pat.w - 1 do
  185. local x = pr.ox + px
  186. if x >= 0 and x < w then
  187. local c = buf[x + 1]
  188. for _, post in ipairs(pat.cols[px + 1]) do
  189. local td, len, str = post[1], post[2], post[3]
  190. for k = 0, len - 1 do
  191. local y = pr.oy + td + k
  192. if y >= 0 and y < h then c[y + 1] = str:byte(k + 1) end
  193. end
  194. end
  195. end
  196. end
  197. end
  198. end
  199. local cols = {}
  200. for x = 1, w do cols[x] = string.char(unpack(buf[x])) end
  201. t = { w = w, h = h, cols = cols }
  202. texcache[name] = t
  203. yieldNow()
  204. return t
  205. end
  206.  
  207. local flatCache = {}
  208. local flatAnim = {} -- name -> current name (animation)
  209. local function getFlat(name)
  210. name = flatAnim[name] or name
  211. local f = flatCache[name]
  212. if f == nil then
  213. local d = getLump(name)
  214. f = (d and #d >= 4096) and d or false
  215. flatCache[name] = f
  216. end
  217. return f or nil
  218. end
  219.  
  220. local flatCycles = {
  221. { "NUKAGE1", "NUKAGE2", "NUKAGE3" }, { "FWATER1", "FWATER2", "FWATER3", "FWATER4" },
  222. { "LAVA1", "LAVA2", "LAVA3", "LAVA4" }, { "BLOOD1", "BLOOD2", "BLOOD3" },
  223. { "SLIME01", "SLIME02", "SLIME03", "SLIME04" }, { "SLIME05", "SLIME06", "SLIME07", "SLIME08" },
  224. { "SLIME09", "SLIME10", "SLIME11", "SLIME12" }, { "RROCK05", "RROCK06", "RROCK07", "RROCK08" },
  225. }
  226. local function animateFlats(tic)
  227. if tic % 8 ~= 0 then return end
  228. local step = floor(tic / 8)
  229. for _, cyc in ipairs(flatCycles) do
  230. local n = #cyc
  231. for i = 1, n do flatAnim[cyc[i]] = cyc[(i - 1 + step) % n + 1] end
  232. end
  233. end
  234.  
  235. -- sprites -----------------------------------------------------------
  236. local sprtab = {}
  237. local function addSpriteLump(name)
  238. local pre = name:sub(1, 4)
  239. local t = sprtab[pre]; if not t then t = {}; sprtab[pre] = t end
  240. local function put(f, r, flip)
  241. t[f] = t[f] or {}
  242. if r == 0 then
  243. for k = 0, 8 do t[f][k] = { name = name, flip = flip } end
  244. elseif r then
  245. t[f][r] = { name = name, flip = flip }
  246. end
  247. end
  248. put(name:sub(5, 5), tonumber(name:sub(6, 6)), false)
  249. if #name >= 8 then put(name:sub(7, 7), tonumber(name:sub(8, 8)), true) end
  250. end
  251. local function initSprites()
  252. local inS = false
  253. for i = 1, #wad.lumps do
  254. local n = wad.lumps[i].name
  255. if n == "S_START" or n == "SS_START" then inS = true
  256. elseif n == "S_END" or n == "SS_END" then inS = false
  257. elseif inS and wad.lumps[i].size > 8 and (#n == 6 or #n == 8) then addSpriteLump(n) end
  258. end
  259. end
  260. local function spriteFrame(pre, frame, rot)
  261. local t = sprtab[pre]; if not t then return nil end
  262. local f = t[frame]; if not f then return nil end
  263. return f[rot] or f[1] or f[0]
  264. end
Advertisement
Add Comment
Please, Sign In to add comment