Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Detect version and compatibility
- local osVer = os.version()
- local isLegacy = (osVer == "CraftOS 1.8")
- local mode = 2
- -- Safety check: Graphics mode is a CC:Tweaked feature.
- -- Legacy CraftOS 1.8 will not have this function.
- if term.setGraphicsMode then
- term.setGraphicsMode(mode)
- else
- error("Graphics Mode not supported. Please use CC:Tweaked on a newer Minecraft version.")
- end
- local floor, abs, math_cos, math_sin = math.floor, math.abs, math.cos, math.sin
- local m_min, m_max, m_random = math.min, math.max, math.random
- local term_setPixel = term.setPixel
- local term_setFrozen = term.setFrozen
- local term_clear = term.clear
- local pullEvent = os.pullEvent
- local speaker = peripheral.find("speaker")
- local musicStopFlag = {false}
- local MUSIC_URL = "https://files.catbox.moe/fybww1.raw"
- local MUSIC_FILE = "bgm.raw"
- local rawChunkBuffer = {}
- local processedChunkBuffer = {}
- local function playPCM(filename, inputSampleRate, stopFlag)
- if not speaker then return end
- local localRaw = rawChunkBuffer
- local localProc = processedChunkBuffer
- while not stopFlag[1] do
- local file = fs.open(filename, "rb")
- if not file then break end
- local chunkSize = 1024
- local outputSampleRate = 48000
- local ratio = inputSampleRate / outputSampleRate
- while not stopFlag[1] do
- for i = 1, #localRaw do localRaw[i] = nil end
- for i = 1, #localProc do localProc[i] = nil end
- for i = 1, chunkSize do
- local byte = file.read()
- if not byte then break end
- localRaw[i] = byte - 128
- end
- if #localRaw == 0 then break end
- if inputSampleRate ~= outputSampleRate then
- for i = 1, floor(#localRaw / ratio) do
- local src = (i - 1) * ratio + 1
- local idx = floor(src)
- local frac = src - idx
- local s1 = localRaw[idx] or 0
- local s2 = localRaw[idx + 1] or s1
- localProc[i] = floor(s1 + (s2 - s1) * frac + 0.5)
- end
- else
- for i=1, #localRaw do localProc[i] = localRaw[i] end
- end
- while not speaker.playAudio(localProc) do
- pullEvent("speaker_audio_empty")
- end
- end
- file.close()
- if stopFlag[1] then break end
- end
- end
- local function setupColorCube(baseIndex)
- local i = baseIndex
- for r = 0, 5 do
- for g = 0, 5 do
- for b = 0, 5 do
- term.setPaletteColor(i, r/5, g/5, b/5)
- i = i + 1
- end
- end
- end
- end
- setupColorCube(16)
- local w, h = term.getSize(mode)
- local function downloadFile(url, filename)
- if not fs.exists(filename) then
- local response = http.get(url)
- if response then
- local f = fs.open(filename, "wb")
- f.write(response.readAll())
- f.close()
- response.close()
- end
- end
- end
- -- Download textures logic
- -- Check for legacy to skip loading.bmp download
- if not isLegacy then
- downloadFile("https://files.catbox.moe/tfdp1m.bmp", "loading.bmp")
- end
- downloadFile("https://files.catbox.moe/x7tf9n.bmp", "grass.bmp")
- downloadFile("https://files.catbox.moe/zs25m6.bmp", "stone.bmp")
- -- Only download music file if speaker is present
- if speaker then
- downloadFile(MUSIC_URL, MUSIC_FILE)
- end
- local function loadTexture(filename)
- if not fs.exists(filename) then return nil end
- local file = fs.open(filename, "rb")
- file.seek("set", 10)
- local dataOffset = file.read() + file.read()*256
- file.seek("set", 18)
- local tw = file.read() + file.read()*256
- file.read(2)
- local th = file.read() + file.read()*256
- file.seek("set", dataOffset)
- local pixels = {}
- for y = th - 1, 0, -1 do
- for x = 0, tw - 1 do
- local b, g, r = file.read(), file.read(), file.read()
- local ri, gi, bi = floor(r/255*5+0.5), floor(g/255*5+0.5), floor(b/255*5+0.5)
- pixels[y * tw + x] = 16 + (ri * 36) + (gi * 6) + bi
- end
- local padding = (4 - (tw * 3 % 4)) % 4
- for i = 1, padding do file.read() end
- end
- file.close()
- return {w = tw, h = th, pixels = pixels}
- end
- local textures = {
- [1] = loadTexture("grass.bmp") or {w=1, h=1, pixels={60}},
- [2] = loadTexture("stone.bmp") or {w=1, h=1, pixels={140}}
- }
- -- Ensure loadingBmp is nil if on CraftOS 1.8
- local loadingBmp = (not isLegacy) and loadTexture("loading.bmp") or nil
- local selectedBlock = 1
- term.setPaletteColor(1, 0, 0.8, 1)
- term.setPaletteColor(255, 1, 1, 0.5)
- local MAP_W, MAP_H, MAP_D = 256, 64, 256
- local map = {}
- for y = 0, MAP_H - 1 do
- local plane = y * 65536
- for z = 0, MAP_D - 1 do
- local row = plane + (z * 256)
- for x = 0, MAP_W - 1 do
- local b = (y == 4) and 1 or (y < 4 and 2 or 0)
- map[row + x] = b
- end
- end
- end
- local camX, camY, camZ = 128, 6.5, 128
- local lookDir, pitch = 0, 0
- local velocityY, gravity, isOnGround, jumpPower = 0, -20.0, false, 7.5
- local function raycastInteract(maxSteps)
- local localMap = map
- local cL, sL, cP, sP = math_cos(lookDir), math_sin(lookDir), math_cos(pitch), math_sin(pitch)
- local dX, dZ = cL * cP, sL * cP
- local dY = sP
- local x, y, z = camX, camY, camZ
- local stepSize = 0.05
- for i = 1, (maxSteps or 8) / stepSize do
- local ix, iy, iz = floor(x), floor(y), floor(z)
- if ix >= 0 and ix < MAP_W and iy >= 0 and iy < MAP_H and iz >= 0 and iz < MAP_D then
- if localMap[(iy * 65536) + (iz * 256) + ix] > 0 then
- local lix, liy, liz = floor(x - dX*stepSize), floor(y - dY*stepSize), floor(z - dZ*stepSize)
- local face = "north"
- if lix ~= ix then
- face = (lix < ix) and "west" or "east"
- elseif liy ~= iy then
- face = (liy < iy) and "bottom" or "top"
- elseif liz ~= iz then
- face = (liz < iz) and "north" or "south"
- end
- return ix, iy, iz, face
- end
- end
- x, y, z = x + dX * stepSize, y + dY * stepSize, z + dZ * stepSize
- end
- end
- local function checkCollision(x, y, z)
- local localMap = map
- local minX, maxX = floor(x - 0.3), floor(x + 0.3)
- local minZ, maxZ = floor(z - 0.3), floor(z + 0.3)
- local fY, hY = floor(y - 1.4), floor(y)
- for ix = minX, maxX do
- for iz = minZ, maxZ do
- for iy = fY, hY do
- if ix >= 0 and ix < MAP_W and iy >= 0 and iy < MAP_H and iz >= 0 and iz < MAP_D then
- if localMap[(iy * 65536) + (iz * 256) + ix] > 0 then return true end
- end
- end
- end
- end
- return false
- end
- local function render()
- local localMap = map
- term_setFrozen(true)
- term_clear(1)
- local hX, hY, hZ = raycastInteract(8)
- local cL, sL, cP, sP = math_cos(lookDir), math_sin(lookDir), math_cos(pitch), math_sin(pitch)
- local step, fov = 2, 1.1
- local aspect = h / w
- for x = 0, w - 1, step do
- local vX = (x / w - 0.5) * fov
- for y = 0, h - 1, step do
- local vY = (0.5 - (y / h)) * fov * aspect
- local rDX = cL * cP - vX * sL - vY * cL * sP
- local rDZ = sL * cP + vX * cL - vY * sL * sP
- local rDY = sP + vY * cP
- local mX, mY, mZ = floor(camX), floor(camY), floor(camZ)
- local dX, dY, dZ = abs(1/(rDX+1e-7)), abs(1/(rDY+1e-7)), abs(1/(rDZ+1e-7))
- local sX, sY, sZ = (rDX<0 and -1 or 1), (rDY<0 and -1 or 1), (rDZ<0 and -1 or 1)
- local sdX = (rDX<0 and (camX-mX)*dX or (mX+1-camX)*dX)
- local sdY = (rDY<0 and (camY-mY)*dY or (mY+1-camY)*dY)
- local sdZ = (rDZ<0 and (camZ-mZ)*dZ or (mZ+1-camZ)*dZ)
- local hit = false
- for i = 1, 64 do
- local side = 0
- if sdX < sdY and sdX < sdZ then sdX, mX, side = sdX + dX, mX + sX, 0
- elseif sdY < sdZ then sdY, mY, side = sdY + dY, mY + sY, 1
- else sdZ, mZ, side = sdZ + dZ, mZ + sZ, 2 end
- if mX<0 or mX>=MAP_W or mY<0 or mY>=MAP_H or mZ<0 or mZ>=MAP_D then break end
- local bType = localMap[(mY*65536)+(mZ*256)+mX]
- if bType > 0 then
- local tex = textures[bType]
- local wX, wY
- if side == 1 then
- wX, wY = camX + ((mY-camY+(1-sY)/2)/rDY)*rDX, camZ + ((mY-camY+(1-sY)/2)/rDY)*rDZ
- else
- wX = (side == 0) and camZ + ((mX-camX+(1-sX)/2)/rDX)*rDZ or camX + ((mZ-camZ+(1-sZ)/2)/rDZ)*rDX
- wY = camY + (((side==0 and mX or mZ)-(side==0 and camX or camZ)+(1-(side==0 and sX or sZ))/2)/(side==0 and rDX or rDZ))*rDY
- end
- local tx, ty = floor((wX - floor(wX)) * (tex.w - 1)), floor((1 - (wY - floor(wY))) * (tex.h - 1))
- local color = tex.pixels[ty * tex.w + tx + 1] or 0
- local isShaded = false
- for sy = mY + 2, m_min(mY + 15, MAP_H - 1) do
- if localMap[(sy*65536)+(mZ*256)+mX] > 0 then
- isShaded = true
- break
- end
- end
- local base = color - 16
- local r = floor(base / 36)
- local g = floor((base % 36) / 6)
- local b = base % 6
- if isShaded then
- r, g, b = m_max(0, r-2), m_max(0, g-2), m_max(0, b-2)
- elseif side ~= 1 then
- r, g, b = m_max(0, r-1), m_max(0, g-1), m_max(0, b-1)
- end
- color = 16 + (r * 36) + (g * 6) + b
- local isHigh = (hX==mX and hY==mY and hZ==mZ)
- local finalCol = isHigh and 255 or color
- for i=0,step-1 do
- for j=0,step-1 do
- if x+i<w and y+j<h then term_setPixel(x+i, y+j, finalCol) end
- end
- end
- hit = true break
- end
- end
- if not hit then
- for i=0,step-1 do
- for j=0,step-1 do
- if x+i<w and y+j<h then term_setPixel(x+i, y+j, 1) end
- end
- end
- end
- end
- end
- term_setFrozen(false)
- end
- if loadingBmp then
- term_setFrozen(true)
- term_clear(0)
- local sX, sY = loadingBmp.w / w, loadingBmp.h / h
- for sy = 0, h - 1 do
- local row = floor(sy * sY) * loadingBmp.w
- for sx = 0, w - 1 do
- term_setPixel(sx, sy, loadingBmp.pixels[row + floor(sx * sX)])
- end
- end
- term_setFrozen(false)
- pullEvent("key")
- end
- local function gameLoop()
- local lastTime = os.clock()
- local localMap = map
- while true do
- local dT = m_min(os.clock() - lastTime, 0.033)
- lastTime = os.clock()
- velocityY = velocityY + gravity * dT
- local nY = camY + velocityY * dT
- if not checkCollision(camX, nY, camZ) then camY = nY else velocityY = 0 end
- isOnGround = (camY <= 1.41 or checkCollision(camX, camY-0.1, camZ))
- if camY < 1.4 then camY, velocityY, isOnGround = 1.4, 0, true end
- render()
- local ev, p1 = pullEvent()
- local cL, sL = math_cos(lookDir), math_sin(lookDir)
- if ev == "key" then
- if p1 == keys.w then if not checkCollision(camX+cL*0.5, camY, camZ+sL*0.5) then camX, camZ = camX+cL*0.5, camZ+sL*0.5 end
- elseif p1 == keys.s then if not checkCollision(camX-cL*0.5, camY, camZ-sL*0.5) then camX, camZ = camX-cL*0.5, camZ-sL*0.5 end
- elseif p1 == keys.a then if not checkCollision(camX+sL*0.5, camY, camZ-cL*0.5) then camX, camZ = camX+sL*0.5, camZ-cL*0.5 end
- elseif p1 == keys.d then if not checkCollision(camX-sL*0.5, camY, camZ+cL*0.5) then camX, camZ = camX-sL*0.5, camZ+cL*0.5 end
- elseif p1 == keys.one then selectedBlock = 1
- elseif p1 == keys.two then selectedBlock = 2
- elseif p1 == keys.r then camX, camZ, camY, velocityY = m_random(20, 230), m_random(20, 230), 15, 0
- elseif p1 == keys.space and isOnGround then velocityY = jumpPower
- elseif p1 == keys.left then lookDir = lookDir - 0.1
- elseif p1 == keys.right then lookDir = lookDir + 0.1
- elseif p1 == keys.up then pitch = m_min(pitch + 0.1, 1.4)
- elseif p1 == keys.down then pitch = m_max(pitch - 0.1, -1.4)
- elseif p1 == keys.e or p1 == keys.b then
- local bx, by, bz, face = raycastInteract(8)
- if bx then
- if p1 == keys.e then localMap[(by*65536)+(bz*256)+bx] = 0
- else
- if face == "north" then bz=bz-1 elseif face == "south" then bz=bz+1
- elseif face == "west" then bx=bx-1 elseif face == "east" then bx=bx+1
- elseif face == "bottom" then by=by-1 elseif face == "top" then by=by+1 end
- if bx>=0 and bx<MAP_W and by>=0 and by<MAP_H and bz>=0 and bz<MAP_D then
- localMap[(by*65536)+(bz*256)+bx] = selectedBlock
- end
- end
- end
- elseif p1 == keys.q then
- musicStopFlag[1] = true
- break
- end
- end
- end
- end
- -- Only run music if speaker exists, otherwise just run game
- if speaker then
- parallel.waitForAny(
- function() playPCM(MUSIC_FILE, 16000, musicStopFlag) end,
- gameLoop
- )
- else
- gameLoop()
- end
- term.setGraphicsMode(0)
Add Comment
Please, Sign In to add comment