Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local component = require("component")
- local event = require("event")
- local term = require("term")
- local gpu = component.gpu
- local computer = require("computer")
- -- Constants
- local WIDTH, HEIGHT = 160, 50
- local FOV, RENDER_DISTANCE = math.pi / 3, 20
- local WORLD_SIZE, WORLD_HEIGHT = 32, 32
- local GRAVITY, JUMP_STRENGTH = -9.81, 5
- local PLAYER_WIDTH, PLAYER_HEIGHT = 0.6, 1.8
- -- Colors
- local colors = {
- WHITE = 0xFFFFFF, GRAY = 0x808080, DARK_GRAY = 0x404040,
- STONE = 0x7F7F7F, DIRT = 0x8B4513, GRASS = 0x00FF00,
- WOOD = 0x8B4513, LEAVES = 0x228B22
- }
- -- Block types
- local BLOCK_AIR, BLOCK_STONE, BLOCK_DIRT, BLOCK_GRASS, BLOCK_WOOD, BLOCK_LEAVES = 0, 1, 2, 3, 4, 5
- -- Setup
- gpu.setResolution(WIDTH, HEIGHT)
- local back_buffer = gpu.allocateBuffer(WIDTH, HEIGHT)
- -- World generation
- local function noise(x, z)
- return math.sin(x * 0.1) * math.cos(z * 0.1) * 0.5 + 0.5
- end
- local world, highest_y = {}, {}
- for x = 1, WORLD_SIZE do
- world[x], highest_y[x] = {}, {}
- for z = 1, WORLD_SIZE do
- world[x][z] = {}
- local height = math.floor(noise(x, z) * (WORLD_HEIGHT / 2 - 1)) + WORLD_HEIGHT / 2
- highest_y[x][z] = height
- for y = 1, WORLD_HEIGHT do
- world[x][z][y] = y == height and BLOCK_GRASS or
- (y < height and y > height - 3 and BLOCK_DIRT or
- (y < height and BLOCK_STONE or BLOCK_AIR))
- end
- end
- end
- -- Generate trees
- local function generateTree(x, y, z)
- if world[x][z][y] == BLOCK_GRASS then
- for ty = 1, 5 do world[x][z][y + ty] = BLOCK_WOOD end
- for tx = -2, 2 do
- for tz = -2, 2 do
- for ty = 4, 6 do
- if x + tx >= 1 and x + tx <= WORLD_SIZE and
- z + tz >= 1 and z + tz <= WORLD_SIZE and
- y + ty <= WORLD_HEIGHT and world[x + tx][z + tz][y + ty] == BLOCK_AIR then
- world[x + tx][z + tz][y + ty] = BLOCK_LEAVES
- end
- end
- end
- end
- end
- end
- for _ = 1, 10 do
- local x, z = math.random(1, WORLD_SIZE), math.random(1, WORLD_SIZE)
- generateTree(x, highest_y[x][z], z)
- end
- -- Find a safe spawn position
- local function findSafeSpawn()
- local center_x, center_z = math.floor(WORLD_SIZE / 2), math.floor(WORLD_SIZE / 2)
- local max_distance = math.floor(WORLD_SIZE / 4)
- for distance = 0, max_distance do
- for dx = -distance, distance do
- for dz = -distance, distance do
- local x, z = center_x + dx, center_z + dz
- if x >= 1 and x <= WORLD_SIZE and z >= 1 and z <= WORLD_SIZE then
- local y = highest_y[x][z]
- -- Check if there's enough space for the player
- if y < WORLD_HEIGHT - 2 and
- world[x][z][y] == BLOCK_GRASS and
- world[x][z][y + 1] == BLOCK_AIR and
- world[x][z][y + 2] == BLOCK_AIR then
- return x, y, z
- end
- end
- end
- end
- end
- -- If no safe spawn is found, return the center of the world
- return center_x, highest_y[center_x][center_z], center_z
- end
- -- Find spawn position
- local spawn_x, spawn_y, spawn_z = findSafeSpawn()
- -- Player setup
- local player = {
- x = spawn_x + 0.5,
- y = spawn_y + PLAYER_HEIGHT,
- z = spawn_z + 0.5,
- rotH = 0,
- rotV = 0,
- vy = 0,
- onGround = false
- }
- local inventory = {[BLOCK_STONE] = 0, [BLOCK_DIRT] = 0, [BLOCK_GRASS] = 0, [BLOCK_WOOD] = 0, [BLOCK_LEAVES] = 0}
- local selected_slot = BLOCK_STONE
- local playerMovement = {forward = false, backward = false, left = false, right = false}
- -- Optimized raycast function
- local function raycast(x, y, z, dx, dy, dz)
- local stepX, stepY, stepZ = dx > 0 and 1 or -1, dy > 0 and 1 or -1, dz > 0 and 1 or -1
- local tMaxX = dx ~= 0 and (math.floor(x + (stepX > 0 and 1 or 0)) - x) / dx or math.huge
- local tMaxY = dy ~= 0 and (math.floor(y + (stepY > 0 and 1 or 0)) - y) / dy or math.huge
- local tMaxZ = dz ~= 0 and (math.floor(z + (stepZ > 0 and 1 or 0)) - z) / dz or math.huge
- local tDeltaX, tDeltaY, tDeltaZ = dx ~= 0 and stepX / dx or math.huge, dy ~= 0 and stepY / dy or math.huge, dz ~= 0 and stepZ / dz or math.huge
- local bx, by, bz = math.floor(x), math.floor(y), math.floor(z)
- for _ = 1, RENDER_DISTANCE do
- if bx < 1 or bx > WORLD_SIZE or by < 1 or by > WORLD_HEIGHT or bz < 1 or bz > WORLD_SIZE then return nil end
- if world[bx] and world[bx][bz] and world[bx][bz][by] and world[bx][bz][by] ~= BLOCK_AIR then
- return bx, by, bz, math.sqrt((bx-x)^2 + (by-y)^2 + (bz-z)^2)
- end
- if tMaxX < tMaxY and tMaxX < tMaxZ then
- bx, tMaxX = bx + stepX, tMaxX + tDeltaX
- elseif tMaxY < tMaxZ then
- by, tMaxY = by + stepY, tMaxY + tDeltaY
- else
- bz, tMaxZ = bz + stepZ, tMaxZ + tDeltaZ
- end
- end
- return nil
- end
- -- Rendering function
- local function render()
- gpu.setActiveBuffer(back_buffer)
- gpu.fill(1, 1, WIDTH, HEIGHT, " ")
- local sinH, cosH = math.sin(player.rotH), math.cos(player.rotH)
- local sinV, cosV = math.sin(player.rotV), math.cos(player.rotV)
- for sx = 1, WIDTH do
- local rayAngleH = player.rotH - FOV/2 + (sx / WIDTH) * FOV
- local rayX, rayZ = math.sin(rayAngleH), math.cos(rayAngleH)
- for sy = 1, HEIGHT do
- local rayAngleV = player.rotV - FOV/2 + (sy / HEIGHT) * FOV
- local rayY, rayLen = math.sin(rayAngleV), math.cos(rayAngleV)
- local hitX, hitY, hitZ, dist = raycast(player.x, player.y, player.z, rayX * rayLen, rayY, rayZ * rayLen)
- if hitX then
- local block = world[hitX][hitZ][hitY]
- local color = dist < RENDER_DISTANCE / 3 and
- (block == BLOCK_STONE and colors.STONE or
- block == BLOCK_DIRT and colors.DIRT or
- block == BLOCK_GRASS and colors.GRASS or
- block == BLOCK_WOOD and colors.WOOD or
- block == BLOCK_LEAVES and colors.LEAVES) or
- (dist < RENDER_DISTANCE * 2 / 3 and colors.GRAY or colors.DARK_GRAY)
- local char = (block == BLOCK_STONE) and "▓" or (block == BLOCK_DIRT) and "▒" or "█"
- gpu.setForeground(color)
- gpu.set(sx, sy, char)
- end
- end
- end
- -- Draw UI
- gpu.setForeground(colors.WHITE)
- gpu.set(1, 1, string.format("X: %.2f Y: %.2f Z: %.2f Selected: %d", player.x, player.y, player.z, selected_slot))
- -- Draw inventory
- local invText = "Inventory: "
- for block, count in pairs(inventory) do
- invText = invText .. string.format("%d:%d ", block, count)
- end
- gpu.set(1, HEIGHT, invText)
- end
- -- Collision detection function
- local function checkCollision(x, y, z)
- local minX, maxX = math.floor(x - PLAYER_WIDTH / 2), math.floor(x + PLAYER_WIDTH / 2)
- local minY, maxY = math.floor(y - PLAYER_HEIGHT), math.floor(y)
- local minZ, maxZ = math.floor(z - PLAYER_WIDTH / 2), math.floor(z + PLAYER_WIDTH / 2)
- for checkX = minX, maxX do
- for checkY = minY, maxY do
- for checkZ = minZ, maxZ do
- if world[checkX] and world[checkX][checkZ] and world[checkX][checkZ][checkY] and world[checkX][checkZ][checkY] ~= BLOCK_AIR then
- return true
- end
- end
- end
- end
- return false
- end
- -- Move player function
- local function movePlayer(dt)
- local moveSpeed = 4 -- blocks per second
- local dx, dz = 0, 0
- if playerMovement.forward then
- dx = dx + math.sin(player.rotH) * moveSpeed * dt
- dz = dz + math.cos(player.rotH) * moveSpeed * dt
- end
- if playerMovement.backward then
- dx = dx - math.sin(player.rotH) * moveSpeed * dt
- dz = dz - math.cos(player.rotH) * moveSpeed * dt
- end
- if playerMovement.left then
- dx = dx - math.cos(player.rotH) * moveSpeed * dt
- dz = dz + math.sin(player.rotH) * moveSpeed * dt
- end
- if playerMovement.right then
- dx = dx + math.cos(player.rotH) * moveSpeed * dt
- dz = dz - math.sin(player.rotH) * moveSpeed * dt
- end
- local dy = player.vy * dt
- -- Apply movement with collision detection
- if not checkCollision(player.x + dx, player.y, player.z) then
- player.x = player.x + dx
- end
- if not checkCollision(player.x, player.y + dy, player.z) then
- player.y = player.y + dy
- else
- player.vy = 0
- end
- if not checkCollision(player.x, player.y, player.z + dz) then
- player.z = player.z + dz
- end
- player.onGround = checkCollision(player.x, player.y - 0.1, player.z)
- end
- -- Physics update
- local function updatePhysics(dt)
- player.vy = player.vy + GRAVITY * dt
- movePlayer(dt)
- if player.y < 1 + PLAYER_HEIGHT then
- player.y = 1 + PLAYER_HEIGHT
- player.vy = 0
- player.onGround = true
- end
- end
- -- Main game loop
- local running = true
- local lastTime = computer.uptime()
- while running do
- local currentTime = computer.uptime()
- local dt = currentTime - lastTime
- lastTime = currentTime
- updatePhysics(dt)
- render()
- gpu.setActiveBuffer(0)
- gpu.bitblt(0, 1, 1, WIDTH, HEIGHT, back_buffer, 1, 1)
- local e = {event.pull(0.05)}
- if e[1] == "key_down" or e[1] == "key_up" then
- local _, _, _, key, isDown = table.unpack(e)
- isDown = e[1] == "key_down"
- if key == 17 then playerMovement.forward = isDown
- elseif key == 31 then playerMovement.backward = isDown
- elseif key == 30 then playerMovement.left = isDown
- elseif key == 32 then playerMovement.right = isDown
- elseif key == 57 and isDown and player.onGround then player.vy, player.onGround = JUMP_STRENGTH, false
- elseif key == 200 then player.rotV = math.min(player.rotV + 0.1, math.pi/2 - 0.1)
- elseif key == 208 then player.rotV = math.max(player.rotV - 0.1, -math.pi/2 - 0.1)
- elseif key == 203 then player.rotH = player.rotH - 0.1
- elseif key == 205 then player.rotH = player.rotH + 0.1
- elseif key == 28 and isDown then
- local hitX, hitY, hitZ = raycast(player.x, player.y, player.z, math.sin(player.rotH), math.sin(player.rotV), math.cos(player.rotH))
- if hitX and world[hitX] and world[hitX][hitZ] then
- local block = world[hitX][hitZ][hitY]
- world[hitX][hitZ][hitY] = BLOCK_AIR
- inventory[block] = (inventory[block] or 0) + 1
- end
- elseif key == 46 and isDown then
- local hitX, hitY, hitZ, dist = raycast(player.x, player.y, player.z, math.sin(player.rotH), math.sin(player.rotV), math.cos(player.rotH))
- if hitX then
- local px, py, pz = player.x + math.sin(player.rotH) * (dist - 0.5), player.y + math.sin(player.rotV) * (dist - 0.5), player.z + math.cos(player.rotH) * (dist - 0.5)
- local bx, by, bz = math.floor(px), math.floor(py), math.floor(pz)
- if world[bx] and world[bx][bz] and world[bx][bz][by] == BLOCK_AIR and inventory[selected_slot] > 0 then
- world[bx][bz][by] = selected_slot
- inventory[selected_slot] = inventory[selected_slot] - 1
- end
- end
- elseif key >= 2 and key <= 6 and isDown then
- selected_slot = key - 1
- elseif key == 16 and isDown then
- running = false
- end
- end
- local endTime = computer.uptime()
- local frameTime = endTime - lastTime
- local fps = 1 / frameTime
- gpu.setForeground(colors.WHITE)
- gpu.set(WIDTH - 20, 1, string.format("FPS: %.2f", fps))
- end
- gpu.freeBuffer(back_buffer)
- term.clear()
- gpu.setForeground(colors.WHITE)
- gpu.setBackground(0x000000)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement