nonogamer9

e

Feb 26th, 2026 (edited)
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.14 KB | None | 0 0
  1. -- Detect version and compatibility
  2. local osVer = os.version()
  3. local isLegacy = (osVer == "CraftOS 1.8")
  4.  
  5. local mode = 2
  6. -- Safety check: Graphics mode is a CC:Tweaked feature.
  7. -- Legacy CraftOS 1.8 will not have this function.
  8. if term.setGraphicsMode then
  9. term.setGraphicsMode(mode)
  10. else
  11. error("Graphics Mode not supported. Please use CC:Tweaked on a newer Minecraft version.")
  12. end
  13.  
  14. local floor, abs, math_cos, math_sin = math.floor, math.abs, math.cos, math.sin
  15. local m_min, m_max, m_random = math.min, math.max, math.random
  16. local term_setPixel = term.setPixel
  17. local term_setFrozen = term.setFrozen
  18. local term_clear = term.clear
  19. local pullEvent = os.pullEvent
  20.  
  21. local speaker = peripheral.find("speaker")
  22. local musicStopFlag = {false}
  23. local MUSIC_URL = "https://files.catbox.moe/fybww1.raw"
  24. local MUSIC_FILE = "bgm.raw"
  25.  
  26. local rawChunkBuffer = {}
  27. local processedChunkBuffer = {}
  28.  
  29. local function playPCM(filename, inputSampleRate, stopFlag)
  30. if not speaker then return end
  31. local localRaw = rawChunkBuffer
  32. local localProc = processedChunkBuffer
  33.  
  34. while not stopFlag[1] do
  35. local file = fs.open(filename, "rb")
  36. if not file then break end
  37. local chunkSize = 1024
  38. local outputSampleRate = 48000
  39. local ratio = inputSampleRate / outputSampleRate
  40.  
  41. while not stopFlag[1] do
  42. for i = 1, #localRaw do localRaw[i] = nil end
  43. for i = 1, #localProc do localProc[i] = nil end
  44.  
  45. for i = 1, chunkSize do
  46. local byte = file.read()
  47. if not byte then break end
  48. localRaw[i] = byte - 128
  49. end
  50. if #localRaw == 0 then break end
  51.  
  52. if inputSampleRate ~= outputSampleRate then
  53. for i = 1, floor(#localRaw / ratio) do
  54. local src = (i - 1) * ratio + 1
  55. local idx = floor(src)
  56. local frac = src - idx
  57. local s1 = localRaw[idx] or 0
  58. local s2 = localRaw[idx + 1] or s1
  59. localProc[i] = floor(s1 + (s2 - s1) * frac + 0.5)
  60. end
  61. else
  62. for i=1, #localRaw do localProc[i] = localRaw[i] end
  63. end
  64.  
  65. while not speaker.playAudio(localProc) do
  66. pullEvent("speaker_audio_empty")
  67. end
  68. end
  69. file.close()
  70. if stopFlag[1] then break end
  71. end
  72. end
  73.  
  74. local function setupColorCube(baseIndex)
  75. local i = baseIndex
  76. for r = 0, 5 do
  77. for g = 0, 5 do
  78. for b = 0, 5 do
  79. term.setPaletteColor(i, r/5, g/5, b/5)
  80. i = i + 1
  81. end
  82. end
  83. end
  84. end
  85. setupColorCube(16)
  86.  
  87. local w, h = term.getSize(mode)
  88.  
  89. local function downloadFile(url, filename)
  90. if not fs.exists(filename) then
  91. local response = http.get(url)
  92. if response then
  93. local f = fs.open(filename, "wb")
  94. f.write(response.readAll())
  95. f.close()
  96. response.close()
  97. end
  98. end
  99. end
  100.  
  101. -- Download textures logic
  102. -- Check for legacy to skip loading.bmp download
  103. if not isLegacy then
  104. downloadFile("https://files.catbox.moe/tfdp1m.bmp", "loading.bmp")
  105. end
  106.  
  107. downloadFile("https://files.catbox.moe/x7tf9n.bmp", "grass.bmp")
  108. downloadFile("https://files.catbox.moe/zs25m6.bmp", "stone.bmp")
  109.  
  110. -- Only download music file if speaker is present
  111. if speaker then
  112. downloadFile(MUSIC_URL, MUSIC_FILE)
  113. end
  114.  
  115. local function loadTexture(filename)
  116. if not fs.exists(filename) then return nil end
  117. local file = fs.open(filename, "rb")
  118. file.seek("set", 10)
  119. local dataOffset = file.read() + file.read()*256
  120. file.seek("set", 18)
  121. local tw = file.read() + file.read()*256
  122. file.read(2)
  123. local th = file.read() + file.read()*256
  124. file.seek("set", dataOffset)
  125. local pixels = {}
  126. for y = th - 1, 0, -1 do
  127. for x = 0, tw - 1 do
  128. local b, g, r = file.read(), file.read(), file.read()
  129. local ri, gi, bi = floor(r/255*5+0.5), floor(g/255*5+0.5), floor(b/255*5+0.5)
  130. pixels[y * tw + x] = 16 + (ri * 36) + (gi * 6) + bi
  131. end
  132. local padding = (4 - (tw * 3 % 4)) % 4
  133. for i = 1, padding do file.read() end
  134. end
  135. file.close()
  136. return {w = tw, h = th, pixels = pixels}
  137. end
  138.  
  139. local textures = {
  140. [1] = loadTexture("grass.bmp") or {w=1, h=1, pixels={60}},
  141. [2] = loadTexture("stone.bmp") or {w=1, h=1, pixels={140}}
  142. }
  143.  
  144. -- Ensure loadingBmp is nil if on CraftOS 1.8
  145. local loadingBmp = (not isLegacy) and loadTexture("loading.bmp") or nil
  146. local selectedBlock = 1
  147.  
  148. term.setPaletteColor(1, 0, 0.8, 1)
  149. term.setPaletteColor(255, 1, 1, 0.5)
  150.  
  151. local MAP_W, MAP_H, MAP_D = 256, 64, 256
  152. local map = {}
  153. for y = 0, MAP_H - 1 do
  154. local plane = y * 65536
  155. for z = 0, MAP_D - 1 do
  156. local row = plane + (z * 256)
  157. for x = 0, MAP_W - 1 do
  158. local b = (y == 4) and 1 or (y < 4 and 2 or 0)
  159. map[row + x] = b
  160. end
  161. end
  162. end
  163.  
  164. local camX, camY, camZ = 128, 6.5, 128
  165. local lookDir, pitch = 0, 0
  166. local velocityY, gravity, isOnGround, jumpPower = 0, -20.0, false, 7.5
  167.  
  168. local function raycastInteract(maxSteps)
  169. local localMap = map
  170. local cL, sL, cP, sP = math_cos(lookDir), math_sin(lookDir), math_cos(pitch), math_sin(pitch)
  171. local dX, dZ = cL * cP, sL * cP
  172. local dY = sP
  173. local x, y, z = camX, camY, camZ
  174. local stepSize = 0.05
  175.  
  176. for i = 1, (maxSteps or 8) / stepSize do
  177. local ix, iy, iz = floor(x), floor(y), floor(z)
  178. if ix >= 0 and ix < MAP_W and iy >= 0 and iy < MAP_H and iz >= 0 and iz < MAP_D then
  179. if localMap[(iy * 65536) + (iz * 256) + ix] > 0 then
  180. local lix, liy, liz = floor(x - dX*stepSize), floor(y - dY*stepSize), floor(z - dZ*stepSize)
  181. local face = "north"
  182. if lix ~= ix then
  183. face = (lix < ix) and "west" or "east"
  184. elseif liy ~= iy then
  185. face = (liy < iy) and "bottom" or "top"
  186. elseif liz ~= iz then
  187. face = (liz < iz) and "north" or "south"
  188. end
  189. return ix, iy, iz, face
  190. end
  191. end
  192. x, y, z = x + dX * stepSize, y + dY * stepSize, z + dZ * stepSize
  193. end
  194. end
  195.  
  196. local function checkCollision(x, y, z)
  197. local localMap = map
  198. local minX, maxX = floor(x - 0.3), floor(x + 0.3)
  199. local minZ, maxZ = floor(z - 0.3), floor(z + 0.3)
  200. local fY, hY = floor(y - 1.4), floor(y)
  201. for ix = minX, maxX do
  202. for iz = minZ, maxZ do
  203. for iy = fY, hY do
  204. if ix >= 0 and ix < MAP_W and iy >= 0 and iy < MAP_H and iz >= 0 and iz < MAP_D then
  205. if localMap[(iy * 65536) + (iz * 256) + ix] > 0 then return true end
  206. end
  207. end
  208. end
  209. end
  210. return false
  211. end
  212.  
  213. local function render()
  214. local localMap = map
  215. term_setFrozen(true)
  216. term_clear(1)
  217.  
  218. local hX, hY, hZ = raycastInteract(8)
  219. local cL, sL, cP, sP = math_cos(lookDir), math_sin(lookDir), math_cos(pitch), math_sin(pitch)
  220. local step, fov = 2, 1.1
  221. local aspect = h / w
  222.  
  223. for x = 0, w - 1, step do
  224. local vX = (x / w - 0.5) * fov
  225. for y = 0, h - 1, step do
  226. local vY = (0.5 - (y / h)) * fov * aspect
  227.  
  228. local rDX = cL * cP - vX * sL - vY * cL * sP
  229. local rDZ = sL * cP + vX * cL - vY * sL * sP
  230. local rDY = sP + vY * cP
  231.  
  232. local mX, mY, mZ = floor(camX), floor(camY), floor(camZ)
  233. local dX, dY, dZ = abs(1/(rDX+1e-7)), abs(1/(rDY+1e-7)), abs(1/(rDZ+1e-7))
  234. local sX, sY, sZ = (rDX<0 and -1 or 1), (rDY<0 and -1 or 1), (rDZ<0 and -1 or 1)
  235. local sdX = (rDX<0 and (camX-mX)*dX or (mX+1-camX)*dX)
  236. local sdY = (rDY<0 and (camY-mY)*dY or (mY+1-camY)*dY)
  237. local sdZ = (rDZ<0 and (camZ-mZ)*dZ or (mZ+1-camZ)*dZ)
  238.  
  239. local hit = false
  240. for i = 1, 64 do
  241. local side = 0
  242. if sdX < sdY and sdX < sdZ then sdX, mX, side = sdX + dX, mX + sX, 0
  243. elseif sdY < sdZ then sdY, mY, side = sdY + dY, mY + sY, 1
  244. else sdZ, mZ, side = sdZ + dZ, mZ + sZ, 2 end
  245.  
  246. if mX<0 or mX>=MAP_W or mY<0 or mY>=MAP_H or mZ<0 or mZ>=MAP_D then break end
  247. local bType = localMap[(mY*65536)+(mZ*256)+mX]
  248.  
  249. if bType > 0 then
  250. local tex = textures[bType]
  251. local wX, wY
  252. if side == 1 then
  253. wX, wY = camX + ((mY-camY+(1-sY)/2)/rDY)*rDX, camZ + ((mY-camY+(1-sY)/2)/rDY)*rDZ
  254. else
  255. wX = (side == 0) and camZ + ((mX-camX+(1-sX)/2)/rDX)*rDZ or camX + ((mZ-camZ+(1-sZ)/2)/rDZ)*rDX
  256. 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
  257. end
  258. local tx, ty = floor((wX - floor(wX)) * (tex.w - 1)), floor((1 - (wY - floor(wY))) * (tex.h - 1))
  259. local color = tex.pixels[ty * tex.w + tx + 1] or 0
  260.  
  261. local isShaded = false
  262. for sy = mY + 2, m_min(mY + 15, MAP_H - 1) do
  263. if localMap[(sy*65536)+(mZ*256)+mX] > 0 then
  264. isShaded = true
  265. break
  266. end
  267. end
  268.  
  269. local base = color - 16
  270. local r = floor(base / 36)
  271. local g = floor((base % 36) / 6)
  272. local b = base % 6
  273.  
  274. if isShaded then
  275. r, g, b = m_max(0, r-2), m_max(0, g-2), m_max(0, b-2)
  276. elseif side ~= 1 then
  277. r, g, b = m_max(0, r-1), m_max(0, g-1), m_max(0, b-1)
  278. end
  279. color = 16 + (r * 36) + (g * 6) + b
  280.  
  281. local isHigh = (hX==mX and hY==mY and hZ==mZ)
  282. local finalCol = isHigh and 255 or color
  283. for i=0,step-1 do
  284. for j=0,step-1 do
  285. if x+i<w and y+j<h then term_setPixel(x+i, y+j, finalCol) end
  286. end
  287. end
  288. hit = true break
  289. end
  290. end
  291. if not hit then
  292. for i=0,step-1 do
  293. for j=0,step-1 do
  294. if x+i<w and y+j<h then term_setPixel(x+i, y+j, 1) end
  295. end
  296. end
  297. end
  298. end
  299. end
  300. term_setFrozen(false)
  301. end
  302.  
  303. if loadingBmp then
  304. term_setFrozen(true)
  305. term_clear(0)
  306. local sX, sY = loadingBmp.w / w, loadingBmp.h / h
  307. for sy = 0, h - 1 do
  308. local row = floor(sy * sY) * loadingBmp.w
  309. for sx = 0, w - 1 do
  310. term_setPixel(sx, sy, loadingBmp.pixels[row + floor(sx * sX)])
  311. end
  312. end
  313. term_setFrozen(false)
  314. pullEvent("key")
  315. end
  316.  
  317. local function gameLoop()
  318. local lastTime = os.clock()
  319. local localMap = map
  320. while true do
  321. local dT = m_min(os.clock() - lastTime, 0.033)
  322. lastTime = os.clock()
  323. velocityY = velocityY + gravity * dT
  324. local nY = camY + velocityY * dT
  325. if not checkCollision(camX, nY, camZ) then camY = nY else velocityY = 0 end
  326. isOnGround = (camY <= 1.41 or checkCollision(camX, camY-0.1, camZ))
  327. if camY < 1.4 then camY, velocityY, isOnGround = 1.4, 0, true end
  328.  
  329. render()
  330.  
  331. local ev, p1 = pullEvent()
  332. local cL, sL = math_cos(lookDir), math_sin(lookDir)
  333. if ev == "key" then
  334. 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
  335. 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
  336. 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
  337. 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
  338. elseif p1 == keys.one then selectedBlock = 1
  339. elseif p1 == keys.two then selectedBlock = 2
  340. elseif p1 == keys.r then camX, camZ, camY, velocityY = m_random(20, 230), m_random(20, 230), 15, 0
  341. elseif p1 == keys.space and isOnGround then velocityY = jumpPower
  342. elseif p1 == keys.left then lookDir = lookDir - 0.1
  343. elseif p1 == keys.right then lookDir = lookDir + 0.1
  344. elseif p1 == keys.up then pitch = m_min(pitch + 0.1, 1.4)
  345. elseif p1 == keys.down then pitch = m_max(pitch - 0.1, -1.4)
  346. elseif p1 == keys.e or p1 == keys.b then
  347. local bx, by, bz, face = raycastInteract(8)
  348. if bx then
  349. if p1 == keys.e then localMap[(by*65536)+(bz*256)+bx] = 0
  350. else
  351. if face == "north" then bz=bz-1 elseif face == "south" then bz=bz+1
  352. elseif face == "west" then bx=bx-1 elseif face == "east" then bx=bx+1
  353. elseif face == "bottom" then by=by-1 elseif face == "top" then by=by+1 end
  354. if bx>=0 and bx<MAP_W and by>=0 and by<MAP_H and bz>=0 and bz<MAP_D then
  355. localMap[(by*65536)+(bz*256)+bx] = selectedBlock
  356. end
  357. end
  358. end
  359. elseif p1 == keys.q then
  360. musicStopFlag[1] = true
  361. break
  362. end
  363. end
  364. end
  365. end
  366.  
  367. -- Only run music if speaker exists, otherwise just run game
  368. if speaker then
  369. parallel.waitForAny(
  370. function() playPCM(MUSIC_FILE, 16000, musicStopFlag) end,
  371. gameLoop
  372. )
  373. else
  374. gameLoop()
  375. end
  376.  
  377. term.setGraphicsMode(0)
Add Comment
Please, Sign In to add comment