Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- DOOM engine for OpenComputers (single file, Lua 5.2/5.3)
- -- Usage: doom [file.wad] [mapname] (default WAD: /home/doom1.wad or doom.wad)
- -- Needs: Tier 2+ GPU & screen (Tier 3 recommended), keyboard. IWAD (doom1.wad/doom.wad/doom2.wad/freedoom).
- -- Controls: W/S or Up/Down move, Left/Right (or A/D with Q/E strafe) turn,
- -- Ctrl/Space/F = fire, E / Enter = use (doors, lifts, switches),
- -- Shift = run, 1-7 = weapon, Tab = toggle minimap, Esc/X = quit.
- local okc, component = pcall(require, "component")
- local okv, computer = pcall(require, "computer")
- local oke, event = pcall(require, "event")
- local floor, ceil, abs, sin, cos, sqrt, atan2, min, max =
- math.floor, math.ceil, math.abs, math.sin, math.cos, math.sqrt, math.atan2 or math.atan, math.min, math.max
- local PI = math.pi
- ------------------------------------------------------------ binary helpers
- local function u8(s, o) return s:byte(o + 1) end
- local function u16(s, o) local a, b = s:byte(o + 1, o + 2); return a + b * 256 end
- local function s16(s, o) local v = u16(s, o); if v >= 32768 then v = v - 65536 end; return v end
- 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
- local function cstr(s, o, n)
- local r = s:sub(o + 1, o + n); local z = r:find("\0", 1, true)
- if z then r = r:sub(1, z - 1) end
- return r:upper()
- end
- ------------------------------------------------------------ WAD loading
- local wad = { lumps = {}, names = {}, fh = nil }
- local function openWad(path)
- local f, err = io.open(path, "rb")
- if not f then error("cannot open WAD: " .. tostring(path) .. " (" .. tostring(err) .. ")") end
- wad.fh = f
- local hdr = f:read(12)
- local id = hdr:sub(1, 4)
- if id ~= "IWAD" and id ~= "PWAD" then error("not a WAD file") end
- local n, dirofs = u32(hdr, 4), u32(hdr, 8)
- f:seek("set", dirofs)
- local dir = f:read(n * 16)
- for i = 0, n - 1 do
- local o = i * 16
- local l = { pos = u32(dir, o), size = u32(dir, o + 4), name = cstr(dir, o + 8, 8) }
- wad.lumps[i + 1] = l
- wad.names[l.name] = i + 1 -- later lumps override earlier (PWAD support)
- end
- wad.count = n
- end
- -- merge extra WAD (PWAD) over base
- local function lumpData(i)
- local l = wad.lumps[i]; if not l or l.size == 0 then return "" end
- local f = l.fh or wad.fh
- f:seek("set", l.pos)
- return f:read(l.size) or ""
- end
- local function addPwad(path)
- local f = io.open(path, "rb"); if not f then return false end
- local hdr = f:read(12)
- local n, dirofs = u32(hdr, 4), u32(hdr, 8)
- f:seek("set", dirofs)
- local dir = f:read(n * 16)
- for i = 0, n - 1 do
- local o = i * 16
- local l = { pos = u32(dir, o), size = u32(dir, o + 4), name = cstr(dir, o + 8, 8), fh = f }
- wad.lumps[#wad.lumps + 1] = l
- wad.names[l.name] = #wad.lumps
- end
- return true
- end
- local function findLump(name) return wad.names[name] end
- local function getLump(name)
- local i = wad.names[name]; if not i then return nil end
- return lumpData(i)
- end
- -- lump index search starting after a marker index (for map lumps)
- local function mapLump(mapIdx, name)
- for i = mapIdx + 1, min(mapIdx + 12, #wad.lumps) do
- if wad.lumps[i].name == name then return lumpData(i) end
- end
- return ""
- end
- ------------------------------------------------------------ cooperative yield (OC kills long-running code)
- local lastYield = 0
- local function yieldNow(force)
- if not okv then return end
- local t = computer.uptime()
- if force or t - lastYield > 1.5 then
- computer.pullSignal(0)
- lastYield = computer.uptime()
- end
- end
- ------------------------------------------------------------ palette / colormap
- local pal = {} -- pal[i] -> 0xRRGGBB
- local rgbcm = {} -- rgbcm[cm*256 + i + 1] -> 0xRRGGBB (cm 0..33)
- local function initPalette()
- local pp = getLump("PLAYPAL"); local cmap = getLump("COLORMAP")
- if not pp or not cmap then error("WAD has no PLAYPAL/COLORMAP") end
- for i = 0, 255 do
- local r, g, b = pp:byte(i * 3 + 1, i * 3 + 3)
- pal[i] = r * 65536 + g * 256 + b
- end
- for cm = 0, 33 do
- for i = 0, 255 do
- rgbcm[cm * 256 + i + 1] = pal[cmap:byte(cm * 256 + i + 1)]
- end
- end
- end
- ------------------------------------------------------------ patches / textures / flats / sprites
- local unpack = table.unpack or unpack
- local function parsePatch(data)
- if #data < 8 then return nil end
- local w, h, lo, to = u16(data, 0), u16(data, 2), s16(data, 4), s16(data, 6)
- local cols = {}
- for x = 0, w - 1 do
- local p = u32(data, 8 + x * 4)
- local posts = {}
- while true do
- local td = data:byte(p + 1)
- if not td or td == 255 then break end
- local len = data:byte(p + 2)
- posts[#posts + 1] = { td, len, data:sub(p + 4, p + 3 + len) }
- p = p + len + 4
- end
- cols[x + 1] = posts
- end
- return { w = w, h = h, lo = lo, to = to, cols = cols }
- end
- local texdefs, texcache = {}, {}
- local pnames = {}
- local function initTextures()
- local pn = getLump("PNAMES")
- if pn then
- local n = u32(pn, 0)
- for i = 0, n - 1 do pnames[i] = cstr(pn, 4 + i * 8, 8) end
- end
- for _, lname in ipairs({ "TEXTURE1", "TEXTURE2" }) do
- local d = getLump(lname)
- if d and #d > 4 then
- local n = u32(d, 0)
- for i = 0, n - 1 do
- local o = u32(d, 4 + i * 4)
- local def = { w = u16(d, o + 12), h = u16(d, o + 14), patches = {} }
- local pc = u16(d, o + 20)
- for k = 0, pc - 1 do
- local po = o + 22 + k * 10
- def.patches[k + 1] = { ox = s16(d, po), oy = s16(d, po + 2), pn = u16(d, po + 4) }
- end
- texdefs[cstr(d, o, 8)] = def
- end
- end
- end
- end
- local patchCache = {}
- local function getPatch(name)
- local p = patchCache[name]
- if p == nil then
- local d = getLump(name)
- p = d and parsePatch(d) or false
- patchCache[name] = p
- end
- return p or nil
- end
- -- texture returns {w,h,cols={[x+1]=string(h bytes)}} (255 = transparent gap)
- local function getTexture(name)
- if name == "-" or name == "" then return nil end
- local t = texcache[name]
- if t ~= nil then return t or nil end
- local def = texdefs[name]
- if not def then texcache[name] = false; return nil end
- local w, h = def.w, def.h
- local buf = {}
- for x = 1, w do
- local c = {}
- for y = 1, h do c[y] = 255 end
- buf[x] = c
- end
- for _, pr in ipairs(def.patches) do
- local pat = getPatch(pnames[pr.pn] or "")
- if pat then
- for px = 0, pat.w - 1 do
- local x = pr.ox + px
- if x >= 0 and x < w then
- local c = buf[x + 1]
- for _, post in ipairs(pat.cols[px + 1]) do
- local td, len, str = post[1], post[2], post[3]
- for k = 0, len - 1 do
- local y = pr.oy + td + k
- if y >= 0 and y < h then c[y + 1] = str:byte(k + 1) end
- end
- end
- end
- end
- end
- end
- local cols = {}
- for x = 1, w do cols[x] = string.char(unpack(buf[x])) end
- t = { w = w, h = h, cols = cols }
- texcache[name] = t
- yieldNow()
- return t
- end
- local flatCache = {}
- local flatAnim = {} -- name -> current name (animation)
- local function getFlat(name)
- name = flatAnim[name] or name
- local f = flatCache[name]
- if f == nil then
- local d = getLump(name)
- f = (d and #d >= 4096) and d or false
- flatCache[name] = f
- end
- return f or nil
- end
- local flatCycles = {
- { "NUKAGE1", "NUKAGE2", "NUKAGE3" }, { "FWATER1", "FWATER2", "FWATER3", "FWATER4" },
- { "LAVA1", "LAVA2", "LAVA3", "LAVA4" }, { "BLOOD1", "BLOOD2", "BLOOD3" },
- { "SLIME01", "SLIME02", "SLIME03", "SLIME04" }, { "SLIME05", "SLIME06", "SLIME07", "SLIME08" },
- { "SLIME09", "SLIME10", "SLIME11", "SLIME12" }, { "RROCK05", "RROCK06", "RROCK07", "RROCK08" },
- }
- local function animateFlats(tic)
- if tic % 8 ~= 0 then return end
- local step = floor(tic / 8)
- for _, cyc in ipairs(flatCycles) do
- local n = #cyc
- for i = 1, n do flatAnim[cyc[i]] = cyc[(i - 1 + step) % n + 1] end
- end
- end
- -- sprites -----------------------------------------------------------
- local sprtab = {}
- local function addSpriteLump(name)
- local pre = name:sub(1, 4)
- local t = sprtab[pre]; if not t then t = {}; sprtab[pre] = t end
- local function put(f, r, flip)
- t[f] = t[f] or {}
- if r == 0 then
- for k = 0, 8 do t[f][k] = { name = name, flip = flip } end
- elseif r then
- t[f][r] = { name = name, flip = flip }
- end
- end
- put(name:sub(5, 5), tonumber(name:sub(6, 6)), false)
- if #name >= 8 then put(name:sub(7, 7), tonumber(name:sub(8, 8)), true) end
- end
- local function initSprites()
- local inS = false
- for i = 1, #wad.lumps do
- local n = wad.lumps[i].name
- if n == "S_START" or n == "SS_START" then inS = true
- elseif n == "S_END" or n == "SS_END" then inS = false
- elseif inS and wad.lumps[i].size > 8 and (#n == 6 or #n == 8) then addSpriteLump(n) end
- end
- end
- local function spriteFrame(pre, frame, rot)
- local t = sprtab[pre]; if not t then return nil end
- local f = t[frame]; if not f then return nil end
- return f[rot] or f[1] or f[0]
- end
Advertisement
Add Comment
Please, Sign In to add comment