Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- ClassiCube 1.3.8 -> OpenComputers (single file, braille raycaster)
- This is NOT a line-by-line translation of the C client (that needs OpenGL, a
- filesystem, sockets and ~130k lines). It is a self-contained Lua re-implementation
- of the parts of ClassiCube that matter for singleplayer, ported from the 1.3.8
- sources:
- * java.util.Random-compatible RNG, ImprovedNoise / OctaveNoise / CombinedNoise
- * "Notchy" classic map generator (Generator.c): heightmap, strata, caves, ore
- veins, water/lava flood fill, sand/gravel/grass surface, flowers, mushrooms, trees
- * classic block IDs 0..49 and CPE blocks 50..65, collide types, face shading
- (top 1.0, Z 0.8, X 0.6, bottom 0.5) and classic sun/shadow lighting (heightmap)
- * player physics (EntityComponents.c): gravity .08, drag .91/.98/.91, jump .42,
- ground friction .6, water/lava drag, liquid jump, fly / noclip / speed hacks
- * block picking with 5 block reach, default hotbar, sand/gravel falling
- Rendering is a voxel DDA raycaster drawn with braille characters (2x4 dots per cell).
- REQUIREMENTS : OpenOS, GPU+screen (tier 3 recommended), keyboard. ~2 MB RAM is plenty
- for the default 64x32x64 world. Run: classicube
- CONTROLS
- W A S D move Space jump / swim up / fly up
- Arrows look Left Shift fly down
- Q / click break block E / right-click place block
- 1-9 / wheel select hotbar slot [ ] change block in slot
- F fly X noclip Z (hold) speed
- R respawn G new world Esc / Ctrl+C quit
- Original ClassiCube license (BSD-3-Clause):
- Copyright (c) 2014 - 2024, UnknownShadow200. All rights reserved.
- Redistribution and use in source and binary forms, with or without modification,
- are permitted provided that the following conditions are met:
- 1. Redistributions of source code must retain the above copyright notice, this
- list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright notice, this
- list of conditions and the following disclaimer in the documentation and/or
- other materials provided with the distribution.
- 3. Neither the name of ClassiCube nor the names of its contributors may be used to
- endorse or promote products derived from this software without specific prior
- written permission.
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
- EXPRESS OR IMPLIED WARRANTIES ARE DISCLAIMED. (full text: ClassiCube license.txt)
- ]]
- local component = require("component")
- local computer = require("computer")
- local unicode = require("unicode")
- local gpu = component.gpu
- local floor, abs, sqrt, sin, cos, tan = math.floor, math.abs, math.sqrt, math.sin, math.cos, math.tan
- local min, max, huge, pi, exp = math.min, math.max, math.huge, math.pi, math.exp
- local byte, char, sub, concat, rep, fmt = string.byte, string.char, string.sub, table.concat, string.rep, string.format
- ---------------------------------------------------------------------------------------
- -- Configuration (tweak for your hardware)
- ---------------------------------------------------------------------------------------
- local CFG = {
- WIDTH = 64, HEIGHT = 32, LENGTH = 64, -- world size X, Y, Z (ClassiCube "normal" is 128x64x128: needs lots of RAM)
- SEED = nil, -- nil = random
- RES_W = 80, RES_H = 25, -- text resolution to use (clamped to the GPU maximum)
- VIEW = 28, -- view distance in blocks
- FOV = 70, -- horizontal field of view (degrees)
- Q_MOVE = 1, -- vertical rays per braille cell while moving (1, 2 or 4); OC Lua is slow, raise if fast
- Q_STILL = 4, -- ... and when standing still (a sharp frame is drawn after you stop)
- REACH = 5,
- LOOK = 110, -- arrow-key turn speed, degrees / second
- }
- local W, H, L = CFG.WIDTH, CFG.HEIGHT, CFG.LENGTH
- local EYE, HW, HT = 1.625, 0.3, 1.8 -- player eye height, half width, height (ClassiCube human model)
- local function trunc(v) if v >= 0 then return floor(v) end return -floor(-v) end
- ---------------------------------------------------------------------------------------
- -- Cooperative yielding (OpenComputers kills programs that run ~5 s without yielding)
- ---------------------------------------------------------------------------------------
- local pump
- local lastYield = computer.uptime()
- local function maybeYield()
- if computer.uptime() - lastYield > 1.5 then
- pump(0)
- lastYield = computer.uptime()
- end
- end
- ---------------------------------------------------------------------------------------
- -- java.util.Random compatible RNG (ExtMath.c). 48 bit LCG done in 24 bit halves so it
- -- is exact on Lua 5.2 (doubles) as well as Lua 5.3 (integers).
- ---------------------------------------------------------------------------------------
- local Mlo, Mhi, P24 = 0xECE66D, 0x5DE, 16777216
- local function xor24(a, b)
- local r, bit = 0, 1
- for _ = 1, 24 do
- local x, y = a % 2, b % 2
- if x ~= y then r = r + bit end
- a = (a - x) / 2; b = (b - y) / 2; bit = bit * 2
- end
- return r
- end
- local function newRng(seed)
- seed = floor(seed) % 4294967296
- return { lo = xor24(seed % P24, Mlo), hi = xor24(floor(seed / P24), Mhi) }
- end
- local function rstep(r)
- local sl, sh = r.lo, r.hi
- local t = sl * Mlo + 11
- r.lo = t % P24
- r.hi = (sh * Mlo + sl * Mhi + floor(t / P24)) % P24
- end
- local POW2 = {}
- for i = 0, 20 do POW2[2 ^ i] = true end
- local function rnext(r, n) -- Random_Next: [0, n)
- rstep(r)
- local bits = r.hi * 128 + floor(r.lo / 131072) -- seed >> 17
- if POW2[n] then return floor(n * bits / 2147483648) end
- return bits % n
- end
- local function rfloat(r) rstep(r); return r.hi / 16777216 end
- ---------------------------------------------------------------------------------------
- -- Perlin noise (Generator.c)
- ---------------------------------------------------------------------------------------
- local GX, GY = {}, {}
- for i = 0, 15 do
- local sh = 2 ^ (i * 2)
- GX[i] = floor(0x46552222 / sh) % 4 - 1
- GY[i] = floor(0x2222550A / sh) % 4 - 1
- end
- local function perm(r)
- local p = {}
- for i = 0, 255 do p[i] = i end
- for i = 0, 255 do
- local j = i + rnext(r, 256 - i)
- p[i], p[j] = p[j], p[i]
- end
- return p
- end
- local function inoise(p, x, y)
- local xi, yi = floor(x), floor(y)
- local X, Y = xi % 256, yi % 256
- x = x - xi; y = y - yi
- local u = x * x * x * (x * (x * 6 - 15) + 10)
- local v = y * y * y * (y * (y * 6 - 15) + 10)
- local A = p[X] + Y
- local B = p[(X + 1) % 256] + Y
- local h = p[p[A % 256]] % 16
- local g22 = GX[h] * x + GY[h] * y
- h = p[p[B % 256]] % 16
- local g12 = GX[h] * (x - 1) + GY[h] * y
- local c1 = g22 + u * (g12 - g22)
- h = p[p[(A + 1) % 256]] % 16
- local g21 = GX[h] * x + GY[h] * (y - 1)
- h = p[p[(B + 1) % 256]] % 16
- local g11 = GX[h] * (x - 1) + GY[h] * (y - 1)
- local c2 = g21 + u * (g11 - g21)
- return c1 + v * (c2 - c1)
- end
- local function octInit(r, n) local o = { n = n }; for i = 1, n do o[i] = perm(r) end; return o end
- local function octCalc(o, x, y)
- local amp, fr, sum = 1, 1, 0
- for i = 1, o.n do
- sum = sum + inoise(o[i], x * fr, y * fr) * amp
- amp = amp * 2; fr = fr * 0.5
- end
- return sum
- end
- local function combInit(r, a, b) local n1 = octInit(r, a); local n2 = octInit(r, b); return { n1, n2 } end
- local function combCalc(c, x, y) local off = octCalc(c[2], x, y); return octCalc(c[1], x + off, y) end
- ---------------------------------------------------------------------------------------
- -- Blocks (BlockID.h / Block.c)
- ---------------------------------------------------------------------------------------
- local NAMES = { [0] = "Air", "Stone", "Grass", "Dirt", "Cobblestone", "Wood", "Sapling", "Bedrock", "Water",
- "Still water", "Lava", "Still lava", "Sand", "Gravel", "Gold ore", "Iron ore", "Coal ore", "Log", "Leaves",
- "Sponge", "Glass", "Red", "Orange", "Yellow", "Lime", "Green", "Teal", "Aqua", "Cyan", "Blue", "Indigo",
- "Violet", "Magenta", "Pink", "Black", "Gray", "White", "Dandelion", "Rose", "Brown shroom", "Red shroom",
- "Gold", "Iron", "Double slab", "Slab", "Brick", "TNT", "Bookshelf", "Mossy rocks", "Obsidian",
- "Cobblestone slab", "Rope", "Sandstone", "Snow", "Fire", "Light pink", "Forest green", "Brown",
- "Deep blue", "Turquoise", "Ice", "Ceramic tile", "Magma", "Pillar", "Crate", "Stone brick" }
- local MAXBLOCK = 65
- -- COL: 0 passable, 1 solid, 2 water, 3 lava
- local COL, SPRITE, NOLIGHT = {}, {}, {}
- for b = 0, MAXBLOCK do COL[b] = 1 end
- for _, b in ipairs { 0, 6, 37, 38, 39, 40, 51, 54 } do COL[b] = 0 end
- for _, b in ipairs { 6, 37, 38, 39, 40, 51, 54 } do SPRITE[b] = true end
- COL[8], COL[9], COL[10], COL[11] = 2, 2, 3, 3
- for _, b in ipairs { 0, 6, 18, 20, 37, 38, 39, 40, 51, 54 } do NOLIGHT[b] = true end
- ---------------------------------------------------------------------------------------
- -- Procedural 8x8 textures (4 bytes per texel: r g b a)
- ---------------------------------------------------------------------------------------
- local function th(x, y, s)
- local h = (x * 7919 + y * 104729 + s * 1299709) % 65536
- h = (h * h + h * 31 + 17) % 1021
- return h / 1021
- end
- local function cc(v) v = floor(v + 0.5); if v < 0 then return 0 elseif v > 255 then return 255 end; return v end
- local function mk(fn)
- local t = {}
- for y = 0, 7 do
- for x = 0, 7 do
- local r, g, b, a = fn(x, y)
- t[#t + 1] = char(cc(r), cc(g), cc(b), a or 255)
- end
- end
- return concat(t)
- end
- local function noisy(r, g, b, var, s)
- return mk(function(x, y) local d = (th(x, y, s) - 0.5) * var; return r + d, g + d, b + d end)
- end
- local function spr(fn)
- return mk(function(x, y)
- local r, g, b = fn(x, y)
- if r then return r, g, b, 255 end
- return 0, 0, 0, 0
- end)
- end
- local TOP, SIDE, BOT = {}, {}, {}
- local function setT(b, top, side, bot)
- TOP[b] = top; SIDE[b] = side or top; BOT[b] = bot or side or top
- end
- local function cobble(s, moss)
- return mk(function(x, y)
- local row = floor(y / 4)
- local m = (y % 4 == 0) or ((x + row * 2) % 4 == 0)
- local d = (th(x, y, s) - 0.5) * 30
- if moss and th(x, y, s + 50) > 0.6 then return 70 + d, 130 + d, 55 + d end
- local v = m and 80 or 128
- return v + d, v + d, v + d
- end)
- end
- local function oreTex(sr, sg, sb, s)
- return mk(function(x, y)
- local d = (th(x, y, s) - 0.5) * 34
- if th(floor(x / 2), floor(y / 2), s + 7) > 0.7 and th(x, y, s + 3) > 0.25 then return sr, sg, sb end
- return 128 + d, 128 + d, 128 + d
- end)
- end
- local function metalBlock(r, g, b, s)
- return mk(function(x, y)
- local edge = (x == 0 or y == 0 or x == 7 or y == 7)
- local d = (th(x, y, s) - 0.5) * 14
- if edge then return r * 0.82 + d, g * 0.82 + d, b * 0.82 + d end
- return r + d, g + d, b + d
- end)
- end
- setT(1, noisy(128, 128, 128, 34, 1))
- setT(2, noisy(100, 170, 55, 36, 3),
- mk(function(x, y)
- local d = (th(x, y, 4) - 0.5) * 30
- if y < 2 or (y == 2 and th(x, y, 5) > 0.5) then return 100 + d, 170 + d, 55 + d end
- return 134 + d, 96 + d, 67 + d
- end),
- noisy(134, 96, 67, 30, 2))
- setT(3, noisy(134, 96, 67, 30, 2))
- setT(4, cobble(6))
- setT(5, mk(function(x, y)
- local d = (th(x, y, 8) - 0.5) * 14
- if y % 4 == 3 then return 120 + d, 92 + d, 50 + d end
- return 170 + d, 135 + d, 80 + d
- end))
- setT(6, spr(function(x, y)
- if y <= 4 and x >= 2 and x <= 5 and th(x, y, 9) > 0.2 then return 50, 150, 45 end
- if (x == 3 or x == 4) and y >= 5 then return 100, 75, 40 end
- end))
- setT(7, noisy(70, 70, 70, 60, 5))
- setT(8, noisy(48, 96, 230, 8, 21)); setT(9, TOP[8])
- local lava = mk(function(x, y)
- local d = th(x, y, 22)
- return 220 + d * 35, 80 + d * 120, 10
- end)
- setT(10, lava); setT(11, lava)
- setT(12, noisy(220, 210, 160, 18, 7))
- setT(13, noisy(135, 130, 128, 80, 24))
- setT(14, oreTex(255, 215, 60, 10))
- setT(15, oreTex(216, 175, 147, 11))
- setT(16, oreTex(35, 35, 35, 12))
- setT(17, mk(function(x, y) -- log top
- local c = max(abs(x - 3.5), abs(y - 3.5))
- if c >= 3 then return 105, 82, 52 end
- if floor(c) % 2 == 0 then return 160, 128, 78 end
- return 140, 108, 64
- end), mk(function(x, y) -- log side
- local d = (th(x, 0, 12) - 0.5) * 40
- return 105 + d, 82 + d, 52 + d
- end))
- setT(18, mk(function(x, y)
- if th(x, y, 13) < 0.2 then return 0, 0, 0, 0 end
- local d = (th(x, y, 14) - 0.5) * 50
- return 50 + d, 140 + d, 35 + d
- end))
- setT(19, mk(function(x, y)
- local d = (th(x, y, 15) - 0.5) * 30
- if th(x, y, 16) > 0.8 then return 150, 150, 30 end
- return 205 + d, 205 + d, 60 + d
- end))
- setT(20, mk(function(x, y)
- if x == 0 or y == 0 or x == 7 or y == 7 then return 215, 235, 240 end
- return 0, 0, 0, 0
- end))
- local WOOL = {
- [21] = { 196, 52, 52 }, [22] = { 216, 128, 48 }, [23] = { 226, 216, 56 }, [24] = { 118, 198, 40 },
- [25] = { 64, 146, 52 }, [26] = { 50, 148, 110 }, [27] = { 60, 182, 176 }, [28] = { 56, 150, 206 },
- [29] = { 56, 86, 206 }, [30] = { 96, 64, 196 }, [31] = { 142, 68, 196 }, [32] = { 196, 64, 172 },
- [33] = { 226, 128, 150 }, [34] = { 32, 32, 32 }, [35] = { 128, 128, 128 }, [36] = { 232, 232, 232 },
- [55] = { 240, 175, 195 }, [56] = { 40, 110, 40 }, [57] = { 110, 70, 40 }, [58] = { 25, 35, 120 },
- [59] = { 40, 200, 170 } }
- for b, c in pairs(WOOL) do setT(b, noisy(c[1], c[2], c[3], 16, 30 + b)) end
- setT(37, spr(function(x, y)
- if (x == 3 or x == 4) and y >= 3 then return 50, 140, 40 end
- if y <= 3 and x >= 2 and x <= 5 and not ((x == 2 or x == 5) and (y == 0 or y == 3)) then return 245, 225, 40 end
- end))
- setT(38, spr(function(x, y)
- if (x == 3 or x == 4) and y >= 3 then return 50, 140, 40 end
- if y <= 3 and x >= 2 and x <= 5 and not ((x == 2 or x == 5) and (y == 0 or y == 3)) then return 215, 40, 40 end
- end))
- setT(39, spr(function(x, y)
- if y >= 5 and (x == 3 or x == 4) then return 215, 205, 185 end
- if y >= 2 and y <= 4 and x >= 1 and x <= 6 then return 140, 100, 70 end
- end))
- setT(40, spr(function(x, y)
- if y >= 5 and (x == 3 or x == 4) then return 225, 215, 195 end
- if y >= 2 and y <= 4 and x >= 1 and x <= 6 then return 205, 50, 45 end
- end))
- setT(41, metalBlock(250, 222, 80, 41))
- setT(42, metalBlock(222, 222, 222, 42))
- local slabTop = noisy(165, 165, 165, 20, 43)
- local slabSide = mk(function(x, y)
- local d = (th(x, y, 44) - 0.5) * 24
- if y == 3 or y == 4 then return 105 + d, 105 + d, 105 + d end
- return 150 + d, 150 + d, 150 + d
- end)
- setT(43, slabTop, slabSide); setT(44, slabTop, slabSide)
- setT(45, mk(function(x, y)
- local d = (th(x, y, 45) - 0.5) * 22
- local row = floor(y / 4)
- if y % 4 == 0 or (x + row * 4) % 8 == 0 then return 190, 185, 175 end
- return 150 + d, 75 + d, 60 + d
- end))
- setT(46, noisy(150, 110, 70, 16, 46), mk(function(x, y)
- local d = (th(x, y, 47) - 0.5) * 16
- if y == 3 or y == 4 then return 236 + d, 230 + d, 220 + d end
- return 200 + d, 45 + d, 35 + d
- end), noisy(150, 110, 70, 16, 46))
- local planks = TOP[5]
- setT(47, planks, mk(function(x, y)
- if y == 0 or y == 7 or y == 3 or y == 4 then return 150, 118, 70 end
- local k = floor(th(floor(x / 2), floor(y / 3.5), 48) * 6)
- local cols = { { 180, 50, 50 }, { 50, 90, 180 }, { 60, 150, 70 }, { 200, 170, 60 }, { 140, 70, 160 }, { 90, 60, 40 } }
- local c = cols[k + 1] or cols[1]
- return c[1], c[2], c[3]
- end), planks)
- setT(48, cobble(49, true))
- setT(49, mk(function(x, y)
- local d = (th(x, y, 50) - 0.5) * 30
- if th(x, y, 51) > 0.85 then return 80, 60, 130 end
- return 25 + d, 15 + d, 45 + d
- end))
- setT(50, TOP[4])
- setT(51, spr(function(x, y) if x == 3 or x == 4 then return 150, 120, 70 end end))
- local sand2 = noisy(215, 200, 150, 16, 52)
- setT(52, sand2, mk(function(x, y)
- local d = (th(x, y, 53) - 0.5) * 14
- if y % 4 == 3 then return 190 + d, 175 + d, 125 + d end
- return 215 + d, 200 + d, 150 + d
- end), sand2)
- setT(53, noisy(245, 248, 250, 10, 54))
- setT(54, spr(function(x, y)
- if th(x, y, 55) + y / 9 > 0.55 and y > 0 then return 245, 110 + y * 16, 20 end
- end))
- setT(60, noisy(160, 200, 255, 16, 60))
- setT(61, mk(function(x, y)
- if x == 0 or y == 0 or x == 4 or y == 4 then return 170, 170, 170 end
- return 235, 235, 235
- end))
- setT(62, mk(function(x, y)
- local d = th(x, y, 62)
- if d > 0.7 then return 255, 150, 30 end
- return 70 + d * 60, 20, 15
- end))
- setT(63, mk(function(x, y)
- if x == 0 or x == 7 then return 170, 170, 170 end
- return 225 + (th(x, y, 63) - 0.5) * 12, 225, 225
- end), noisy(225, 225, 225, 12, 63))
- setT(64, mk(function(x, y)
- local d = (th(x, y, 64) - 0.5) * 14
- if x == 0 or y == 0 or x == 7 or y == 7 or x == y or x + y == 7 then return 120 + d, 85 + d, 45 + d end
- return 175 + d, 140 + d, 85 + d
- end))
- setT(65, mk(function(x, y)
- local d = (th(x, y, 65) - 0.5) * 20
- if y % 4 == 0 or (x + floor(y / 4) * 4) % 8 == 0 then return 95, 95, 95 end
- return 135 + d, 135 + d, 135 + d
- end))
- -- average colour of each block (for the hotbar)
- local AVGP = {}
- for b = 1, MAXBLOCK do
- local tx = SIDE[b]
- local sr, sg, sb, n = 0, 0, 0, 0
- if tx then
- for i = 0, 63 do
- local r, g, bl, a = byte(tx, i * 4 + 1, i * 4 + 4)
- if a > 0 then sr = sr + r; sg = sg + g; sb = sb + bl; n = n + 1 end
- end
- end
- if n == 0 then n = 1 end
- AVGP[b] = floor(sr / n) * 65536 + floor(sg / n) * 256 + floor(sb / n)
- end
- ---------------------------------------------------------------------------------------
- -- World storage: one string of W bytes per (y, z) row. rows[y*L + z + 1]
- ---------------------------------------------------------------------------------------
- local CH = {}
- for i = 0, 255 do CH[i] = char(i) end
- local rows = {}
- local hm = {} -- light heightmap: top light-blocking y per column
- local function getb(x, y, z) return byte(rows[y * L + z + 1], x + 1) end
- local function setb(x, y, z, b)
- local i = y * L + z + 1
- local s = rows[i]
- rows[i] = sub(s, 1, x) .. CH[b] .. sub(s, x + 2)
- end
- local function colHM(x, z)
- local y = H - 1
- while y >= 0 do
- if not NOLIGHT[byte(rows[y * L + z + 1], x + 1)] then break end
- y = y - 1
- end
- hm[z * W + x + 1] = y
- end
- local function computeHM()
- for z = 0, L - 1 do for x = 0, W - 1 do colHM(x, z) end; maybeYield() end
- end
- ---------------------------------------------------------------------------------------
- -- "Notchy" map generator (Generator.c)
- ---------------------------------------------------------------------------------------
- local statusFn = function() end
- local function generate(seed)
- local r = newRng(seed)
- local waterLevel = floor(H / 2)
- local minHeight = H
- local OneY = W * L
- local heightmap = {}
- local function status(s) statusFn(s) end
- -- 0: heightmap
- status("Building heightmap")
- do
- local n1 = combInit(r, 8, 8); local n2 = combInit(r, 8, 8); local n3 = octInit(r, 6)
- for z = 0, L - 1 do
- for x = 0, W - 1 do
- local hLow = combCalc(n1, x * 1.3, z * 1.3) / 6 - 4
- local height = hLow
- if octCalc(n3, x, z) <= 0 then
- local hHigh = combCalc(n2, x * 1.3, z * 1.3) / 5 + 6
- height = max(hLow, hHigh)
- end
- height = height * 0.5
- if height < 0 then height = height * 0.8 end
- local adj = trunc(height + waterLevel)
- if adj < minHeight then minHeight = adj end
- heightmap[z * W + x + 1] = adj
- end
- maybeYield()
- end
- end
- -- 1: strata
- status("Creating strata")
- do
- local n = octInit(r, 8)
- local sTop, dTop = {}, {}
- for z = 0, L - 1 do
- for x = 0, W - 1 do
- local i = z * W + x + 1
- local dirtThickness = trunc(octCalc(n, x, z) / 24 - 4)
- local dirtHeight = heightmap[i]
- local stoneHeight = dirtHeight + dirtThickness
- sTop[i] = min(stoneHeight, H - 1)
- dTop[i] = min(dirtHeight, H - 1)
- end
- maybeYield()
- end
- for y = 0, H - 1 do
- for z = 0, L - 1 do
- local t, zi = {}, z * W
- for x = 1, W do
- local b
- if y == 0 then b = 11
- elseif y <= sTop[zi + x] then b = 1
- elseif y <= dTop[zi + x] then b = 3
- else b = 0 end
- t[x] = CH[b]
- end
- rows[y * L + z + 1] = concat(t)
- end
- maybeYield()
- end
- end
- local function fillSpheroid(x, y, z, radius, block)
- local xBeg = floor(max(x - radius, 0)); local xEnd = floor(min(x + radius, W - 1))
- local yBeg = floor(max(y - radius, 0)); local yEnd = floor(min(y + radius, H - 1))
- local zBeg = floor(max(z - radius, 0)); local zEnd = floor(min(z + radius, L - 1))
- local rsq = radius * radius
- for yy = yBeg, yEnd do
- local dy = yy - y
- for zz = zBeg, zEnd do
- local dz = zz - z
- local row = rows[yy * L + zz + 1]
- for xx = xBeg, xEnd do
- local dx = xx - x
- if (dx * dx + 2 * dy * dy + dz * dz) < rsq and byte(row, xx + 1) == 1 then
- setb(xx, yy, zz, block)
- row = rows[yy * L + zz + 1]
- end
- end
- end
- end
- end
- -- 2: caves
- status("Carving caves")
- do
- local count = floor(W * H * L / 8192)
- for _ = 1, count do
- local cx, cy, cz = rnext(r, W), rnext(r, H), rnext(r, L)
- local len = floor(rfloat(r) * rfloat(r) * 200)
- local theta = rfloat(r) * 2 * pi; local dTheta = 0
- local phi = rfloat(r) * 2 * pi; local dPhi = 0
- local caveRadius = rfloat(r) * rfloat(r)
- for j = 0, len - 1 do
- cx = cx + sin(theta) * cos(phi)
- cz = cz + cos(theta) * cos(phi)
- cy = cy + sin(phi)
- theta = theta + dTheta * 0.2
- dTheta = dTheta * 0.9 + rfloat(r) - rfloat(r)
- phi = phi * 0.5 + dPhi * 0.25
- dPhi = dPhi * 0.75 + rfloat(r) - rfloat(r)
- if rfloat(r) >= 0.25 then
- local cenX = trunc(cx + (rnext(r, 4) - 2) * 0.2)
- local cenY = trunc(cy + (rnext(r, 4) - 2) * 0.2)
- local cenZ = trunc(cz + (rnext(r, 4) - 2) * 0.2)
- local radius = (H - cenY) / H
- radius = 1.2 + (radius * 3.5 + 1.0) * caveRadius
- radius = radius * sin(j * pi / len)
- fillSpheroid(cenX, cenY, cenZ, radius, 0)
- end
- maybeYield()
- end
- end
- end
- -- 3..5: ore veins
- local function veins(abundance, name, block)
- status(name)
- local num = floor(W * H * L * abundance / 16384)
- for _ = 1, num do
- local vx, vy, vz = rnext(r, W), rnext(r, H), rnext(r, L)
- local len = floor(rfloat(r) * rfloat(r) * 75 * abundance)
- local theta = rfloat(r) * 2 * pi; local dTheta = 0
- local phi = rfloat(r) * 2 * pi; local dPhi = 0
- for j = 0, len - 1 do
- vx = vx + sin(theta) * cos(phi)
- vz = vz + cos(theta) * cos(phi)
- vy = vy + sin(phi)
- theta = dTheta * 0.2
- dTheta = dTheta * 0.9 + rfloat(r) - rfloat(r)
- phi = phi * 0.5 + dPhi * 0.25
- dPhi = dPhi * 0.9 + rfloat(r) - rfloat(r)
- local radius = abundance * sin(j * pi / len) + 1.0
- fillSpheroid(trunc(vx), trunc(vy), trunc(vz), radius, block)
- maybeYield()
- end
- end
- end
- veins(0.9, "Carving coal ore", 16)
- veins(0.7, "Carving iron ore", 15)
- veins(0.5, "Carving gold ore", 14)
- -- 6..8: flooding
- local function floodFill(idx, block)
- if idx < 0 then return end
- local stack, n, steps = { idx }, 1, 0
- while n > 0 do
- idx = stack[n]; n = n - 1
- local x = idx % W
- local y = floor(idx / OneY)
- local z = floor(idx / W) % L
- if getb(x, y, z) == 0 then
- setb(x, y, z, block)
- if x > 0 then n = n + 1; stack[n] = idx - 1 end
- if x < W - 1 then n = n + 1; stack[n] = idx + 1 end
- if z > 0 then n = n + 1; stack[n] = idx - W end
- if z < L - 1 then n = n + 1; stack[n] = idx + W end
- if y > 0 then n = n + 1; stack[n] = idx - OneY end
- end
- steps = steps + 1
- if steps % 2048 == 0 then maybeYield() end
- end
- end
- local function pack(x, y, z) return (y * L + z) * W + x end
- status("Flooding edge water")
- do
- local waterY = waterLevel - 1
- for x = 0, W - 1 do
- floodFill(pack(x, waterY, 0), 9)
- floodFill(pack(x, waterY, L - 1), 9)
- end
- for z = 0, L - 1 do
- floodFill(pack(0, waterY, z), 9)
- floodFill(pack(W - 1, waterY, z), 9)
- end
- end
- status("Flooding water")
- do
- local num = floor(W * L / 800)
- for _ = 1, num do
- local x, z = rnext(r, W), rnext(r, L)
- local y = waterLevel - (1 + rnext(r, 2))
- floodFill(pack(x, y, z), 9)
- end
- end
- status("Flooding lava")
- do
- local num = floor(W * L / 20000)
- for _ = 1, num do
- local x, z = rnext(r, W), rnext(r, L)
- local y = trunc((waterLevel - 3) * rfloat(r) * rfloat(r))
- floodFill(pack(x, y, z), 11)
- end
- end
- -- 9: surface
- status("Creating surface")
- do
- local n1 = octInit(r, 8); local n2 = octInit(r, 8)
- for z = 0, L - 1 do
- for x = 0, W - 1 do
- local y = heightmap[z * W + x + 1]
- if y >= 0 and y < H then
- local above = (y >= H - 1) and 0 or getb(x, y + 1, z)
- if above == 9 and octCalc(n2, x, z) > 12 then
- setb(x, y, z, 13)
- elseif above == 0 then
- if y <= waterLevel and octCalc(n1, x, z) > 8 then setb(x, y, z, 12) else setb(x, y, z, 2) end
- end
- end
- end
- maybeYield()
- end
- end
- -- 10: flowers
- status("Planting flowers")
- do
- local num = floor(W * L / 3000)
- for _ = 1, num do
- local block = 37 + rnext(r, 2)
- local patchX, patchZ = rnext(r, W), rnext(r, L)
- for _ = 1, 10 do
- local fx, fz = patchX, patchZ
- for _ = 1, 5 do
- fx = fx + rnext(r, 6) - rnext(r, 6)
- fz = fz + rnext(r, 6) - rnext(r, 6)
- if fx >= 0 and fx < W and fz >= 0 and fz < L then
- local fy = heightmap[fz * W + fx + 1] + 1
- if fy > 0 and fy < H then
- if getb(fx, fy, fz) == 0 and getb(fx, fy - 1, fz) == 2 then setb(fx, fy, fz, block) end
- end
- end
- end
- end
- end
- end
- -- 11: mushrooms
- status("Planting mushrooms")
- do
- local num = floor(W * H * L / 2000)
- for _ = 1, num do
- local block = 39 + rnext(r, 2)
- local patchX, patchY, patchZ = rnext(r, W), rnext(r, H), rnext(r, L)
- for _ = 1, 20 do
- local mx, mz = patchX, patchZ
- for _ = 1, 5 do
- mx = mx + rnext(r, 6) - rnext(r, 6)
- mz = mz + rnext(r, 6) - rnext(r, 6)
- if mx >= 0 and mx < W and mz >= 0 and mz < L then
- local ground = heightmap[mz * W + mx + 1]
- if patchY < ground - 1 and patchY > 0 then
- if getb(mx, patchY, mz) == 0 and getb(mx, patchY - 1, mz) == 1 then setb(mx, patchY, mz, block) end
- end
- end
- end
- end
- end
- maybeYield()
- end
- -- 12: trees
- status("Planting trees")
- do
- local function canGrow(tx, ty, tz, th_)
- local baseH = th_ - 4
- for y = ty, ty + baseH - 1 do
- for z = tz - 1, tz + 1 do
- for x = tx - 1, tx + 1 do
- if x < 0 or x >= W or y < 0 or y >= H or z < 0 or z >= L then return false end
- if getb(x, y, z) ~= 0 then return false end
- end
- end
- end
- for y = ty + baseH, ty + th_ - 1 do
- for z = tz - 2, tz + 2 do
- for x = tx - 2, tx + 2 do
- if x < 0 or x >= W or y < 0 or y >= H or z < 0 or z >= L then return false end
- if getb(x, y, z) ~= 0 then return false end
- end
- end
- end
- return true
- end
- local function grow(tx, ty, tz, height)
- local list = {}
- local function put(x, y, z, b) list[#list + 1] = { x, y, z, b } end
- local topStart = ty + (height - 2)
- local y = ty + (height - 4)
- while y < topStart do
- for zz = -2, 2 do
- for xx = -2, 2 do
- local x, z = tx + xx, tz + zz
- if abs(xx) == 2 and abs(zz) == 2 then
- if rfloat(r) >= 0.5 then put(x, y, z, 18) end
- else
- put(x, y, z, 18)
- end
- end
- end
- y = y + 1
- end
- while y < ty + height do
- for zz = -1, 1 do
- for xx = -1, 1 do
- local x, z = xx + tx, zz + tz
- if xx == 0 or zz == 0 then put(x, y, z, 18)
- elseif y == topStart and rfloat(r) >= 0.5 then put(x, y, z, 18) end
- end
- end
- y = y + 1
- end
- for yy = 0, height - 2 do put(tx, ty + yy, tz, 17) end
- put(tx, ty - 1, tz, 3)
- return list
- end
- local num = floor(W * L / 4000)
- for _ = 1, num do
- local patchX, patchZ = rnext(r, W), rnext(r, L)
- for _ = 1, 20 do
- local tx, tz = patchX, patchZ
- for _ = 1, 20 do
- tx = tx + rnext(r, 6) - rnext(r, 6)
- tz = tz + rnext(r, 6) - rnext(r, 6)
- if tx >= 0 and tx < W and tz >= 0 and tz < L and rfloat(r) < 0.25 then
- local ty = heightmap[tz * W + tx + 1] + 1
- if ty < H then
- local height = 5 + rnext(r, 3)
- local under = ty > 0 and getb(tx, ty - 1, tz) or 0
- if under == 2 and canGrow(tx, ty, tz, height) then
- for _, c in ipairs(grow(tx, ty, tz, height)) do setb(c[1], c[2], c[3], c[4]) end
- end
- end
- end
- end
- end
- maybeYield()
- end
- end
- end
- ---------------------------------------------------------------------------------------
- -- Screen / palette setup
- ---------------------------------------------------------------------------------------
- local RW, RH, VCOLS, VROWS, DEPTH = 80, 25, 80, 24, 8
- local oldW, oldH, oldDepth, oldFG, oldBG
- local QR, QG, QB = {}, {}, {}
- local BR = {}
- for i = 0, 255 do BR[i] = unicode.char(0x2800 + i) end
- local function buildQuant()
- for i = 0, 255 do
- if DEPTH >= 8 then -- snap to the OpenComputers 240 colour cube (6 x 8 x 5 levels)
- QR[i] = floor(i / 51 + 0.5) * 51 * 65536
- QG[i] = floor(floor(i / (255 / 7) + 0.5) * (255 / 7) + 0.5) * 256
- QB[i] = floor(floor(i / 63.75 + 0.5) * 63.75 + 0.5)
- else
- QR[i] = i * 65536; QG[i] = i * 256; QB[i] = i
- end
- end
- end
- local function packc(r, g, b) return QR[floor(r)] + QG[floor(g)] + QB[floor(b)] end
- ---------------------------------------------------------------------------------------
- -- Raycaster
- ---------------------------------------------------------------------------------------
- local ex, ey, ez = 0, 0, 0
- local selX, selY, selZ = -1, -1, -1
- local startWater = false
- local MAXD = CFG.VIEW
- local FOGS = MAXD * 0.55
- local SKYR, SKYG, SKYB = 153, 204, 255
- local FR, FG, FB = 200, 226, 255
- local WR, WG, WB = 40, 88, 220
- local SHADOW = 0x9B / 255
- local EDGEH = floor(H / 2)
- local CLOUD = {}
- do
- for ix = 0, 31 do for iz = 0, 31 do
- CLOUD[ix * 32 + iz + 1] = th(ix, iz, 99) > 0.62
- end end
- end
- local function skyColor(dx, dy, dz)
- local tc = (H + 2 - ey) / dy
- if tc > 0 and tc < 160 then
- local cx, cz = floor((ex + dx * tc) / 8), floor((ez + dz * tc) / 8)
- if CLOUD[(cx % 32) * 32 + (cz % 32) + 1] then return 245, 248, 255 end
- end
- return SKYR, SKYG, SKYB
- end
- local function edgeColor(dy, cy, t)
- if cy < EDGEH then return WR * 0.9, WG * 0.9, WB * 0.9, t end
- if dy < 0 then
- local tp = (EDGEH - ey) / dy
- if tp > 0 and tp < MAXD then return WR * 0.9, WG * 0.9, WB * 0.9, tp end
- end
- return FR, FG, FB, MAXD
- end
- local function spriteHit(b, x, y, z, dx, dy, dz, tin, tout)
- local tx = SIDE[b]
- local t1, t2
- local d1 = dx - dz
- if d1 > 1e-6 or d1 < -1e-6 then t1 = ((x - z) - (ex - ez)) / d1 end
- local d2 = dx + dz
- if d2 > 1e-6 or d2 < -1e-6 then t2 = ((x + z + 1) - (ex + ez)) / d2 end
- if t1 and (t1 < tin or t1 > tout) then t1 = nil end
- if t2 and (t2 < tin or t2 > tout) then t2 = nil end
- if t1 and t2 and t2 < t1 then t1, t2 = t2, t1 end
- for k = 1, 2 do
- local tt = (k == 1) and t1 or t2
- if tt then
- local hx, hy = ex + dx * tt, ey + dy * tt
- local u = hx - floor(hx)
- local v = 1 - (hy - floor(hy))
- local i = (floor(v * 7.999) * 8 + floor(u * 7.999)) * 4 + 1
- local r, g, bl, a = byte(tx, i, i + 3)
- if a > 0 then return r, g, bl, tt end
- end
- end
- end
- local function cast(dx, dy, dz)
- local x, y, z = floor(ex), floor(ey), floor(ez)
- local sx, sy, sz, tdx, tdy, tdz, tmx, tmy, tmz
- if dx > 0 then sx = 1; tdx = 1 / dx; tmx = (x + 1 - ex) * tdx
- elseif dx < 0 then sx = -1; tdx = -1 / dx; tmx = (ex - x) * tdx
- else sx = 0; tdx = huge; tmx = huge end
- if dy > 0 then sy = 1; tdy = 1 / dy; tmy = (y + 1 - ey) * tdy
- elseif dy < 0 then sy = -1; tdy = -1 / dy; tmy = (ey - y) * tdy
- else sy = 0; tdy = huge; tmy = huge end
- if dz > 0 then sz = 1; tdz = 1 / dz; tmz = (z + 1 - ez) * tdz
- elseif dz < 0 then sz = -1; tdz = -1 / dz; tmz = (ez - z) * tdz
- else sz = 0; tdz = huge; tmz = huge end
- local t, face = 0, 0
- local inw, tw, wsum = startWater, 0, 0
- local r, g, bl, fogt
- while true do
- local px, py, pz = x, y, z
- if tmx < tmy then
- if tmx < tmz then t = tmx; x = x + sx; tmx = tmx + tdx; face = 1
- else t = tmz; z = z + sz; tmz = tmz + tdz; face = 3 end
- else
- if tmy < tmz then t = tmy; y = y + sy; tmy = tmy + tdy; face = 2
- else t = tmz; z = z + sz; tmz = tmz + tdz; face = 3 end
- end
- if t > MAXD then r, g, bl = FR, FG, FB; t = MAXD; break end
- local b
- if x < 0 or x >= W or z < 0 or z >= L then
- r, g, bl, fogt = edgeColor(dy, y, t)
- t = fogt; break
- elseif y >= H then
- if dy > 0 then r, g, bl = skyColor(dx, dy, dz); break end
- b = 0
- elseif y < 0 then
- r, g, bl = 40, 40, 40; fogt = t; break
- else
- b = byte(rows[y * L + z + 1], x + 1)
- end
- if b == 8 or b == 9 then
- if not inw then inw = true; tw = t end
- else
- if inw then inw = false; wsum = wsum + (t - tw) end
- if b ~= 0 then
- if SPRITE[b] then
- local tout = tmx
- if tmy < tout then tout = tmy end
- if tmz < tout then tout = tmz end
- local sr, sg, sb, st = spriteHit(b, x, y, z, dx, dy, dz, t, tout)
- if sr then
- local f = 0.95
- local hv = hm[z * W + x + 1]
- if y <= hv then f = f * SHADOW end
- r, g, bl, t, fogt = sr * f, sg * f, sb * f, st, st
- break
- end
- else
- local tx, sh, u, v
- if face == 1 then
- local hz, hy = ez + dz * t, ey + dy * t
- u = hz - floor(hz); v = 1 - (hy - floor(hy)); tx = SIDE[b]; sh = 0.6
- elseif face == 3 then
- local hx, hy = ex + dx * t, ey + dy * t
- u = hx - floor(hx); v = 1 - (hy - floor(hy)); tx = SIDE[b]; sh = 0.8
- else
- local hx, hz = ex + dx * t, ez + dz * t
- u = hx - floor(hx); v = hz - floor(hz)
- if sy > 0 then tx = BOT[b]; sh = 0.5 else tx = TOP[b]; sh = 1 end
- end
- local i = (floor(v * 7.999) * 8 + floor(u * 7.999)) * 4 + 1
- local r0, g0, b0, a0 = byte(tx, i, i + 3)
- if a0 > 0 then
- local light = sh
- if b == 10 or b == 11 then
- light = 1
- else
- local hv = -1
- if px >= 0 and px < W and pz >= 0 and pz < L then hv = hm[pz * W + px + 1] end
- if py <= hv then light = light * SHADOW end
- end
- if x == selX and y == selY and z == selZ then
- light = light * 1.35
- r, g, bl = r0 * light, g0 * light, b0 * light
- if r > 255 then r = 255 end
- if g > 255 then g = 255 end
- if bl > 255 then bl = 255 end
- else
- r, g, bl = r0 * light, g0 * light, b0 * light
- end
- fogt = t
- break
- end
- end
- end
- end
- end
- if fogt and fogt > FOGS then
- local k = (fogt - FOGS) / (MAXD - FOGS)
- if k > 1 then k = 1 end
- r = r + (FR - r) * k; g = g + (FG - g) * k; bl = bl + (FB - bl) * k
- end
- if inw then wsum = wsum + (t - tw) end
- if wsum > 0 then
- local a = 1 - exp(-0.7 * wsum)
- r = r + (WR * 0.9 - r) * a; g = g + (WG * 0.9 - g) * a; bl = bl + (WB * 0.9 - bl) * a
- end
- return r, g, bl
- end
- ---------------------------------------------------------------------------------------
- -- Player state
- ---------------------------------------------------------------------------------------
- local px, py, pz = W / 2, H, L / 2
- local vx, vy, vz = 0, 0, 0
- local yaw, pitch = 0, 0
- local onGround, fly, noclip, canLiqJump, hitH = false, false, false, false, false
- local down = {}
- local hotbar = { 1, 4, 45, 3, 5, 17, 18, 20, 44 } -- stone, cobble, brick, dirt, wood, log, leaves, glass, slab
- hotbar[2] = 4; hotbar[3] = 45; hotbar[4] = 3
- local slot = 1
- ---------------------------------------------------------------------------------------
- -- Rendering
- ---------------------------------------------------------------------------------------
- local U, V, SR, SG, SB, SL = {}, {}, {}, {}, {}, {}
- local DOTMAP = {}
- for _, sy in ipairs { 1, 2, 4 } do
- local m = {}
- local c0 = { 1, 2, 4, 64 }
- local c1 = { 8, 16, 32, 128 }
- for c = 0, 1 do
- for dr = 0, 3 do
- m[#m + 1] = { (c == 0) and c0[dr + 1] or c1[dr + 1], floor(dr * sy / 4) * 2 + c + 1 }
- end
- end
- DOTMAP[sy] = m
- end
- local gfg, gbg = -1, -1
- local function setFG(c) if c ~= gfg then gpu.setForeground(c); gfg = c end end
- local function setBG(c) if c ~= gbg then gpu.setBackground(c); gbg = c end end
- local function pick()
- local cp = cos(pitch)
- local dx, dy, dz = sin(yaw) * cp, sin(pitch), cos(yaw) * cp
- local ox, oy, oz = px, py + EYE, pz
- local x, y, z = floor(ox), floor(oy), floor(oz)
- local sx, sy, sz, tdx, tdy, tdz, tmx, tmy, tmz
- if dx > 0 then sx = 1; tdx = 1 / dx; tmx = (x + 1 - ox) * tdx
- elseif dx < 0 then sx = -1; tdx = -1 / dx; tmx = (ox - x) * tdx else sx = 0; tdx = huge; tmx = huge end
- if dy > 0 then sy = 1; tdy = 1 / dy; tmy = (y + 1 - oy) * tdy
- elseif dy < 0 then sy = -1; tdy = -1 / dy; tmy = (oy - y) * tdy else sy = 0; tdy = huge; tmy = huge end
- if dz > 0 then sz = 1; tdz = 1 / dz; tmz = (z + 1 - oz) * tdz
- elseif dz < 0 then sz = -1; tdz = -1 / dz; tmz = (oz - z) * tdz else sz = 0; tdz = huge; tmz = huge end
- while true do
- local qx, qy, qz = x, y, z
- local t
- if tmx < tmy then
- if tmx < tmz then t = tmx; x = x + sx; tmx = tmx + tdx else t = tmz; z = z + sz; tmz = tmz + tdz end
- else
- if tmy < tmz then t = tmy; y = y + sy; tmy = tmy + tdy else t = tmz; z = z + sz; tmz = tmz + tdz end
- end
- if t > CFG.REACH then return nil end
- if x >= 0 and x < W and y >= 0 and y < H and z >= 0 and z < L then
- local b = byte(rows[y * L + z + 1], x + 1)
- if b ~= 0 and COL[b] ~= 2 and COL[b] ~= 3 then return x, y, z, qx, qy, qz end
- elseif not (y >= H and dy < 0) then
- return nil
- end
- end
- end
- local lastMs = 0
- local function render(SY)
- local t0 = computer.uptime()
- local cp, sp, cyw, syw = cos(pitch), sin(pitch), cos(yaw), sin(yaw)
- local fx, fy, fz = syw * cp, sp, cyw * cp
- local rx, rz = -cyw, syw
- local ux, uy, uz = -syw * sp, cp, -cyw * sp
- ex, ey, ez = px, py + EYE, pz
- local feetB = (ey >= 0 and ey < H and ex >= 0 and ex < W and ez >= 0 and ez < L) and getb(floor(ex), floor(ey), floor(ez)) or 0
- startWater = (feetB == 8 or feetB == 9)
- local sx, sy_, sz_ = pick()
- if sx then selX, selY, selZ = sx, sy_, sz_ else selX, selY, selZ = -1, -1, -1 end
- local cols, rws = VCOLS, VROWS
- local SW, NS = cols * 2, rws * SY
- local tanH = tan(CFG.FOV * pi / 360)
- local tanV = tanH * (rws * 4) / SW
- for i = 0, SW - 1 do U[i + 1] = ((i + 0.5) / SW * 2 - 1) * tanH end
- for j = 0, NS - 1 do V[j + 1] = (1 - (j + 0.5) / NS * 2) * tanV end
- local n = 2 * SY
- local dotmap = DOTMAP[SY]
- local buf = {}
- for cy = 0, rws - 1 do
- local rfg, rbg, rx0, rn = -1, -1, 1, 0
- for cx = 0, cols - 1 do
- local k = 0
- for sj = 0, SY - 1 do
- local vv = V[cy * SY + sj + 1]
- local ddy = fy + uy * vv
- local bx, bz = fx + ux * vv, fz + uz * vv
- for sc = 0, 1 do
- local uu = U[cx * 2 + sc + 1]
- local r, g, b = cast(bx + rx * uu, ddy, bz + rz * uu)
- k = k + 1
- SR[k] = r; SG[k] = g; SB[k] = b; SL[k] = r * 0.3 + g * 0.59 + b * 0.11
- end
- end
- local sum, mn, mx = 0, 1e9, -1
- for i = 1, n do
- local l = SL[i]
- sum = sum + l
- if l < mn then mn = l end
- if l > mx then mx = l end
- end
- local fgc, bgc, bits
- local m = sum / n
- if mx - mn < 12 or DEPTH == 1 then
- if DEPTH == 1 then
- fgc = 0xFFFFFF; bgc = 0; bits = 0
- for i = 1, 8 do if SL[dotmap[i][2]] > 100 then bits = bits + dotmap[i][1] end end
- else
- local r, g, b = 0, 0, 0
- for i = 1, n do r = r + SR[i]; g = g + SG[i]; b = b + SB[i] end
- fgc = packc(r / n, g / n, b / n); bgc = fgc; bits = 255
- end
- else
- local fr, fg_, fb, nf, br, bg_, bb, nb = 0, 0, 0, 0, 0, 0, 0, 0
- for i = 1, n do
- if SL[i] > m then fr = fr + SR[i]; fg_ = fg_ + SG[i]; fb = fb + SB[i]; nf = nf + 1
- else br = br + SR[i]; bg_ = bg_ + SG[i]; bb = bb + SB[i]; nb = nb + 1 end
- end
- fgc = packc(fr / nf, fg_ / nf, fb / nf)
- bgc = packc(br / nb, bg_ / nb, bb / nb)
- bits = 0
- for i = 1, 8 do
- local d = dotmap[i]
- if SL[d[2]] > m then bits = bits + d[1] end
- end
- end
- if fgc == rfg and bgc == rbg then
- rn = rn + 1; buf[rn] = BR[bits]
- else
- if rn > 0 then setFG(rfg); setBG(rbg); gpu.set(rx0, cy + 1, concat(buf, "", 1, rn)) end
- rfg, rbg, rx0, rn = fgc, bgc, cx + 1, 1
- buf[1] = BR[bits]
- end
- end
- if rn > 0 then setFG(rfg); setBG(rbg); gpu.set(rx0, cy + 1, concat(buf, "", 1, rn)) end
- maybeYield()
- end
- -- crosshair
- local ccx, ccy = floor(VCOLS / 2) + 1, floor(VROWS / 2) + 1
- local _, cfg, cbg = gpu.get(ccx, ccy)
- if cfg then
- setFG(0xFFFFFF - (cbg or 0)); setBG(cbg or 0)
- gpu.set(ccx, ccy, "+")
- end
- lastMs = floor((computer.uptime() - t0) * 1000)
- end
- local function contrast(c)
- local r, g, b = floor(c / 65536) % 256, floor(c / 256) % 256, c % 256
- if r * 0.3 + g * 0.59 + b * 0.11 > 130 then return 0x000000 end
- return 0xFFFFFF
- end
- local function drawHUD()
- local y = RH
- setBG(0); setFG(0xFFFFFF)
- gpu.fill(1, y, RW, 1, " ")
- local x = 1
- for i = 1, 9 do
- local c = AVGP[hotbar[i]] or 0x808080
- setBG(c); setFG(contrast(c))
- gpu.set(x, y, (i == slot) and (">" .. i .. "<") or (" " .. i .. " "))
- x = x + 3
- end
- setBG(0); setFG(0xFFFFFF)
- local flags = (fly and " FLY" or "") .. (noclip and " NOCLIP" or "")
- local text = fmt(" %s %.1f %.1f %.1f%s %dms", NAMES[hotbar[slot]] or "?", px, py, pz, flags, lastMs)
- gpu.set(x, y, sub(text, 1, RW - x + 1))
- end
- ---------------------------------------------------------------------------------------
- -- Physics (EntityComponents.c / Collisions)
- ---------------------------------------------------------------------------------------
- local function pblock(x, y, z)
- if x < 0 or x >= W or z < 0 or z >= L then return 0 end
- if y < 0 then return 7 end
- if y >= H then return 0 end
- return byte(rows[y * L + z + 1], x + 1)
- end
- local function solidAt(x, y, z)
- if x < 0 or x >= W or z < 0 or z >= L then return true end -- invisible walls at the map edge
- if y < 0 then return true end
- if y >= H then return false end
- return COL[byte(rows[y * L + z + 1], x + 1)] == 1
- end
- local EPS = 1e-5
- local function clipY(dy)
- local x0, x1 = floor(px - HW + EPS), floor(px + HW - EPS)
- local z0, z1 = floor(pz - HW + EPS), floor(pz + HW - EPS)
- if dy < 0 then
- for yy = floor(py - 1e-7), floor(py + dy), -1 do
- for zz = z0, z1 do for xx = x0, x1 do
- if solidAt(xx, yy, zz) then
- local lim = (yy + 1) - py
- if lim > 0 then lim = 0 end
- return max(dy, lim)
- end
- end end
- end
- elseif dy > 0 then
- local head = py + HT
- for yy = floor(head + 1e-7), floor(head + dy) do
- for zz = z0, z1 do for xx = x0, x1 do
- if solidAt(xx, yy, zz) then
- local lim = yy - head
- if lim < 0 then lim = 0 end
- return min(dy, lim)
- end
- end end
- end
- end
- return dy
- end
- local function clipX(dx)
- local y0, y1 = floor(py + EPS), floor(py + HT - EPS)
- local z0, z1 = floor(pz - HW + EPS), floor(pz + HW - EPS)
- if dx > 0 then
- local e = px + HW
- for xx = floor(e + 1e-7), floor(e + dx) do
- for yy = y0, y1 do for zz = z0, z1 do
- if solidAt(xx, yy, zz) then local lim = xx - e; if lim < 0 then lim = 0 end; return min(dx, lim) end
- end end
- end
- elseif dx < 0 then
- local e = px - HW
- for xx = floor(e - 1e-7), floor(e + dx), -1 do
- for yy = y0, y1 do for zz = z0, z1 do
- if solidAt(xx, yy, zz) then local lim = (xx + 1) - e; if lim > 0 then lim = 0 end; return max(dx, lim) end
- end end
- end
- end
- return dx
- end
- local function clipZ(dz)
- local y0, y1 = floor(py + EPS), floor(py + HT - EPS)
- local x0, x1 = floor(px - HW + EPS), floor(px + HW - EPS)
- if dz > 0 then
- local e = pz + HW
- for zz = floor(e + 1e-7), floor(e + dz) do
- for yy = y0, y1 do for xx = x0, x1 do
- if solidAt(xx, yy, zz) then local lim = zz - e; if lim < 0 then lim = 0 end; return min(dz, lim) end
- end end
- end
- elseif dz < 0 then
- local e = pz - HW
- for zz = floor(e - 1e-7), floor(e + dz), -1 do
- for yy = y0, y1 do for xx = x0, x1 do
- if solidAt(xx, yy, zz) then local lim = (zz + 1) - e; if lim > 0 then lim = 0 end; return max(dz, lim) end
- end end
- end
- end
- return dz
- end
- local function collide()
- local dy = clipY(vy)
- onGround = (vy < 0 and dy > vy)
- py = py + dy; vy = dy
- local dx = clipX(vx)
- hitH = (dx ~= vx)
- px = px + dx; vx = dx
- local dz = clipZ(vz)
- if dz ~= vz then hitH = true end
- pz = pz + dz; vz = dz
- end
- -- does the player's body touch block kind a or b (liquid surface sits 1.5/16 below the cell top)
- local function touch(a, b)
- for y = floor(py), floor(py + HT) do
- for z = floor(pz - HW), floor(pz + HW) do
- for x = floor(px - HW), floor(px + HW) do
- local id = pblock(x, y, z)
- if (id == a or id == b) and py < y + 0.906 and py + HT > y then return true end
- end
- end
- end
- return false
- end
- local function liquidRow(y0, y1)
- for y = y0, y1 do
- for z = floor(pz - HW), floor(pz + HW) do
- for x = floor(px - HW), floor(px + HW) do
- local c = COL[pblock(x, y, z)]
- if c == 2 or c == 3 then return true end
- end
- end
- end
- return false
- end
- local function moveHor(ivx, ivz, factor)
- local dist = sqrt(ivx * ivx + ivz * ivz)
- if dist < 0.00001 then return end
- if dist < 1 then dist = 1 end
- vx = vx + ivx * (factor / dist)
- vz = vz + ivz * (factor / dist)
- end
- local function moveStep(dx_, dy_, dz_, grav, yMul)
- vy = vy * yMul
- if noclip then
- px = min(max(px + vx, HW), W - HW); py = py + vy; pz = min(max(pz + vz, HW), L - HW)
- else
- collide()
- end
- vy = vy / yMul
- vx = vx * dx_; vy = vy * dy_; vz = vz * dz_
- vy = vy - grav
- end
- local function tick()
- local fwd = (down[17] and 1 or 0) - (down[31] and 1 or 0)
- local str = (down[32] and 1 or 0) - (down[30] and 1 or 0)
- local jumping = down[57] and true or false
- local flyDown = down[42] and true or false
- local speeding = down[44] and true or false
- local floating = fly or noclip
- if noclip then onGround = false end
- local tw, tl = touch(8, 9), touch(10, 11)
- -- PhysicsComp_UpdateVelocityState
- if floating then
- vy = 0
- local dir = jumping and 1 or (flyDown and -1 or 0)
- vy = vy + 0.12 * dir
- if speeding then vy = vy + 0.12 * dir end
- end
- if not jumping then
- canLiqJump = false
- elseif tw or tl then
- local feetY = floor(py)
- local bodyY = feetY + 1
- local headY = floor(py + HT)
- if bodyY > headY then bodyY = headY end
- local liquidFeet = liquidRow(feetY, feetY)
- local liquidRest = liquidRow(min(bodyY, headY), max(bodyY, headY))
- local past = liquidFeet and not liquidRest and (py % 1 >= 0.4)
- if not past then
- canLiqJump = true
- vy = vy + 0.04
- if speeding then vy = vy + 0.04 end
- else
- if hitH then vy = vy + (tl and 0.30 or 0.13)
- elseif canLiqJump then vy = vy + (tl and 0.20 or 0.10) end
- canLiqJump = false
- end
- elseif onGround and not floating then
- vy = 0.42
- if speeding then vy = vy + 0.42 end
- canLiqJump = false
- end
- -- PhysicsComp_PhysicsTick
- local sf = speeding and 10 or 0
- local verSpeed = (floating and 8 or 1) * (1 + sf) / 5
- if verSpeed < 1 then verSpeed = 1 end
- local horSpeed = (floating and 1.6 or 1) * (1 + sf)
- local cy_, sy_ = cos(yaw), sin(yaw)
- local xm, zm = str * 0.98, fwd * 0.98
- local ivx = xm * (-cy_) + zm * sy_
- local ivz = xm * sy_ + zm * cy_
- if tw and not floating then
- moveHor(ivx, ivz, 0.02 * horSpeed)
- moveStep(0.8, 0.8, 0.8, 0.02, verSpeed)
- elseif tl and not floating then
- moveHor(ivx, ivz, 0.02 * horSpeed)
- moveStep(0.5, 0.5, 0.5, 0.02, verSpeed)
- else
- local factor = (floating or onGround) and 0.1 or 0.02
- local gravity = (tw or tl) and 0.02 or 0.08
- if floating then
- moveHor(ivx, ivz, factor * horSpeed)
- local yv = sqrt(vx * vx + vz * vz)
- local ym = verSpeed
- if (ivx ~= 0 or ivz ~= 0) and yv > 0.001 then
- vy = 0; ym = 1
- if jumping then vy = vy + yv end
- if flyDown then vy = vy - yv end
- end
- moveStep(0.91, 0.98, 0.91, gravity, ym)
- else
- moveHor(ivx, ivz, factor * horSpeed)
- moveStep(0.91, 0.98, 0.91, gravity, verSpeed)
- end
- if onGround or fly then vx = vx * 0.6; vz = vz * 0.6 end
- end
- end
- ---------------------------------------------------------------------------------------
- -- Editing
- ---------------------------------------------------------------------------------------
- local function settle(x, y, z)
- local b = getb(x, y, z)
- if b ~= 12 and b ~= 13 then return end
- local ny = y
- while ny > 0 do
- local below = getb(x, ny - 1, z)
- if below == 0 or COL[below] == 2 or COL[below] == 3 or SPRITE[below] then ny = ny - 1 else break end
- end
- if ny ~= y then setb(x, y, z, 0); setb(x, ny, z, b) end
- end
- local function afterEdit(x, y, z)
- local yy = y + 1
- while yy < H do
- local b = getb(x, yy, z)
- if b == 12 or b == 13 then settle(x, yy, z); yy = yy + 1 else break end
- end
- colHM(x, z)
- end
- local function doBreak()
- local x, y, z = pick()
- if not x then return end
- setb(x, y, z, 0)
- afterEdit(x, y, z)
- end
- local function doPlace()
- local x, y, z, qx, qy, qz = pick()
- if not x then return end
- if qx < 0 or qx >= W or qy < 0 or qy >= H or qz < 0 or qz >= L then return end
- local cur = getb(qx, qy, qz)
- if cur ~= 0 and COL[cur] ~= 2 and COL[cur] ~= 3 then return end
- local b = hotbar[slot]
- if COL[b] == 1 and not noclip then
- if px - HW < qx + 1 and px + HW > qx and py < qy + 1 and py + HT > qy and pz - HW < qz + 1 and pz + HW > qz then
- return
- end
- end
- setb(qx, qy, qz, b)
- settle(qx, qy, qz)
- afterEdit(qx, qy, qz)
- end
- local function findSpawn()
- local cx, cz = floor(W / 2), floor(L / 2)
- local function topOf(x, z)
- for y = H - 1, 0, -1 do
- local b = getb(x, y, z)
- if b ~= 0 and not SPRITE[b] then return y, b end
- end
- return 0, 7
- end
- for rad = 0, 16 do
- for dz = -rad, rad do
- for dx = -rad, rad do
- if max(abs(dx), abs(dz)) == rad then
- local x, z = cx + dx, cz + dz
- if x >= 0 and x < W and z >= 0 and z < L then
- local y, b = topOf(x, z)
- if b == 2 or b == 12 or b == 3 or b == 1 then return x + 0.5, y + 1, z + 0.5 end
- end
- end
- end
- end
- end
- local y = topOf(cx, cz)
- return cx + 0.5, y + 1, cz + 0.5
- end
- local function respawn()
- px, py, pz = findSpawn()
- vx, vy, vz = 0, 0, 0
- end
- ---------------------------------------------------------------------------------------
- -- Input
- ---------------------------------------------------------------------------------------
- local running = true
- local queue = {}
- local lastAct = 0
- local function cycleBlock(dir)
- local b = hotbar[slot]
- repeat
- b = b + dir
- if b > MAXBLOCK then b = 1 elseif b < 1 then b = MAXBLOCK end
- until b ~= 8 and b ~= 10
- hotbar[slot] = b
- end
- local function onKey(code)
- if code >= 2 and code <= 10 then slot = code - 1
- elseif code == 33 then fly = not fly
- elseif code == 45 then noclip = not noclip
- elseif code == 19 then queue[#queue + 1] = "respawn"
- elseif code == 34 then queue[#queue + 1] = "regen"
- elseif code == 26 then cycleBlock(-1)
- elseif code == 27 then cycleBlock(1)
- elseif code == 1 then running = false end
- end
- local function handle(name, a, b, c, d)
- if name == "key_down" then
- local code = c
- local was = down[code]
- down[code] = true
- if not was then onKey(code) end
- if code == 16 or code == 18 then
- local now = computer.uptime()
- if now - lastAct > 0.22 then lastAct = now; queue[#queue + 1] = (code == 16) and "break" or "place" end
- end
- elseif name == "key_up" then
- down[c] = nil
- elseif name == "touch" then
- queue[#queue + 1] = (d == 0) and "break" or "place"
- elseif name == "scroll" then
- slot = slot - d
- if slot < 1 then slot = 9 elseif slot > 9 then slot = 1 end
- elseif name == "interrupted" then
- running = false
- end
- end
- pump = function(timeout)
- local t = timeout or 0
- while true do
- local name, a, b, c, d = computer.pullSignal(t)
- if not name then break end
- handle(name, a, b, c, d)
- t = 0
- end
- end
- ---------------------------------------------------------------------------------------
- -- Main
- ---------------------------------------------------------------------------------------
- local function setupScreen()
- oldW, oldH = gpu.getResolution()
- oldFG, oldBG = gpu.getForeground(), gpu.getBackground()
- oldDepth = gpu.getDepth()
- local mw, mh = gpu.maxResolution()
- RW, RH = min(CFG.RES_W, mw), min(CFG.RES_H, mh)
- gpu.setResolution(RW, RH)
- if gpu.maxDepth() >= 8 then gpu.setDepth(8) end
- DEPTH = gpu.getDepth()
- VCOLS, VROWS = RW, RH - 1
- buildQuant()
- end
- local function restoreScreen()
- pcall(function()
- if oldDepth then gpu.setDepth(oldDepth) end
- if oldW then gpu.setResolution(oldW, oldH) end
- gpu.setBackground(oldBG or 0x000000); gpu.setForeground(oldFG or 0xFFFFFF)
- gpu.fill(1, 1, oldW or RW, oldH or RH, " ")
- end)
- end
- local function status(msg)
- gfg, gbg = -1, -1
- setBG(0x000000); setFG(0xFFFFFF)
- gpu.fill(1, 1, RW, RH, " ")
- gpu.set(2, 2, "ClassiCube 1.3.8 - OpenComputers port")
- gpu.set(2, 4, "Generating level (" .. W .. "x" .. H .. "x" .. L .. ")...")
- gpu.set(2, 5, msg)
- end
- local seedCounter = 0
- local function newWorld()
- seedCounter = seedCounter + 1
- local seed = CFG.SEED and (CFG.SEED + seedCounter - 1) or (floor(os.time()) % 2147483647 + seedCounter)
- statusFn = status
- rows = {}
- generate(seed)
- collectgarbage()
- statusFn = function() end
- computeHM()
- respawn()
- yaw, pitch = 0, 0
- collectgarbage()
- end
- local function main()
- setupScreen()
- newWorld()
- local last = computer.uptime()
- local acc = 0
- local dirty = true
- local lastMove = last
- local lastQ = 0
- local lastFrame = 0
- gfg, gbg = -1, -1
- gpu.setBackground(0); gpu.setForeground(0xFFFFFF); gpu.fill(1, 1, RW, RH, " ")
- while running do
- local now = computer.uptime()
- local idle = (not dirty) and (lastQ == CFG.Q_STILL)
- pump(idle and 0.05 or 0)
- for i = 1, #queue do
- local a = queue[i]
- if a == "break" then doBreak(); dirty = true
- elseif a == "place" then doPlace(); dirty = true
- elseif a == "respawn" then respawn(); dirty = true
- elseif a == "regen" then newWorld(); dirty = true; gfg, gbg = -1, -1; last = computer.uptime() end
- queue[i] = nil
- end
- now = computer.uptime()
- local dt = now - last
- last = now
- if dt > 0.25 then dt = 0.25 end
- local ox, oy, oz, oyaw, opitch = px, py, pz, yaw, pitch
- local turn = CFG.LOOK * pi / 180 * dt
- if down[203] then yaw = yaw - turn end
- if down[205] then yaw = yaw + turn end
- if down[200] then pitch = min(pitch + turn, 1.5) end
- if down[208] then pitch = max(pitch - turn, -1.5) end
- acc = acc + dt
- local ticks = 0
- while acc >= 0.05 and ticks < 5 do tick(); acc = acc - 0.05; ticks = ticks + 1 end
- if ticks >= 5 then acc = 0 end
- if py < -20 then respawn() end
- if abs(px - ox) + abs(py - oy) + abs(pz - oz) > 1e-4 or yaw ~= oyaw or pitch ~= opitch then
- dirty = true; lastMove = now
- end
- if dirty then
- render(CFG.Q_MOVE); drawHUD()
- dirty = false; lastQ = CFG.Q_MOVE
- elseif lastQ ~= CFG.Q_STILL and now - lastMove > 0.25 then
- render(CFG.Q_STILL); drawHUD()
- lastQ = CFG.Q_STILL
- end
- end
- end
- local ok, err = pcall(main)
- restoreScreen()
- if not ok then error(err, 0) end
Advertisement