Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local component = require("component")
- local event = require("event")
- local keyboard = require("keyboard")
- local gpu = component.gpu
- -- Set screen resolution to fit a Tier 3 GPU
- local width, height = 160, 50
- gpu.setResolution(width, height)
- -- Create two buffers
- local frontBuffer = {}
- local backBuffer = {}
- -- Initialize buffers
- for y = 1, height do
- frontBuffer[y] = {}
- backBuffer[y] = {}
- for x = 1, width do
- frontBuffer[y][x] = 0x000000
- backBuffer[y][x] = 0x000000
- end
- end
- -- Function to clear the screen
- local function clearScreen()
- gpu.setBackground(0x000000)
- gpu.fill(1, 1, width, height, " ")
- end
- -- Clear the screen before starting
- clearScreen()
- -- Function to set a pixel in the back buffer
- local function setPixel(x, y, color)
- if x >= 1 and x <= width and y >= 1 and y <= height then
- backBuffer[y][x] = color
- end
- end
- -- Function to clear the back buffer
- local function clearBackBuffer()
- for y = 1, height do
- for x = 1, width do
- backBuffer[y][x] = 0x000000
- end
- end
- end
- -- Function to swap buffers and draw to screen efficiently
- local function swapBuffers()
- local changes = {}
- for y = 1, height do
- for x = 1, width do
- if frontBuffer[y][x] ~= backBuffer[y][x] then
- table.insert(changes, {x = x, y = y, color = backBuffer[y][x]})
- frontBuffer[y][x] = backBuffer[y][x]
- end
- end
- end
- -- Sort changes by color to minimize GPU calls
- table.sort(changes, function(a, b) return a.color < b.color end)
- local currentColor = nil
- for _, change in ipairs(changes) do
- if currentColor ~= change.color then
- gpu.setBackground(change.color)
- currentColor = change.color
- end
- gpu.set(change.x, change.y, " ")
- end
- end
- -- Maze generation using Recursive Division
- local function generateMaze(width, height)
- local maze = {}
- for y = 1, height do
- maze[y] = {}
- for x = 1, width do
- maze[y][x] = 1
- end
- end
- local function carve(x, y)
- local directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}
- for i = #directions, 2, -1 do
- local j = math.random(i)
- directions[i], directions[j] = directions[j], directions[i]
- end
- for _, dir in ipairs(directions) do
- local nx, ny = x + dir[1] * 2, y + dir[2] * 2
- if nx > 0 and nx <= width and ny > 0 and ny <= height and maze[ny][nx] == 1 then
- maze[ny][nx] = 0
- maze[y + dir[2]][x + dir[1]] = 0
- carve(nx, ny)
- end
- end
- end
- maze[2][2] = 0
- carve(2, 2)
- return maze
- end
- local map = generateMaze(15, 15)
- local player = {
- x = 2.5,
- y = 2.5,
- angle = 0
- }
- local enemies = {}
- local bullets = {}
- local enemyBullets = {}
- -- New function to spawn enemies randomly
- local MAX_ENEMIES = 10
- local function spawnEnemy()
- if #enemies < MAX_ENEMIES then
- local x = math.random(1, #map[1])
- local y = math.random(1, #map)
- -- Ensure the enemy doesn't spawn inside a wall
- while map[y][x] == 1 do
- x = math.random(1, #map[1])
- y = math.random(1, #map)
- end
- table.insert(enemies, {x = x + 0.5, y = y + 0.5})
- end
- end
- local function draw()
- clearBackBuffer()
- -- Draw walls
- for x = 1, width do
- local cameraX = 2 * x / width - 1
- local rayDirX = math.cos(player.angle) + cameraX * math.sin(player.angle)
- local rayDirY = math.sin(player.angle) - cameraX * math.cos(player.angle)
- local mapX, mapY = math.floor(player.x), math.floor(player.y)
- local sideDistX, sideDistY
- local deltaDistX, deltaDistY = math.abs(1 / rayDirX), math.abs(1 / rayDirY)
- local perpWallDist
- local stepX, stepY
- local hit, side = 0, 0
- if rayDirX < 0 then stepX, sideDistX = -1, (player.x - mapX) * deltaDistX
- else stepX, sideDistX = 1, (mapX + 1 - player.x) * deltaDistX end
- if rayDirY < 0 then stepY, sideDistY = -1, (player.y - mapY) * deltaDistY
- else stepY, sideDistY = 1, (mapY + 1 - player.y) * deltaDistY end
- while hit == 0 do
- if sideDistX < sideDistY then
- sideDistX, mapX, side = sideDistX + deltaDistX, mapX + stepX, 0
- else
- sideDistY, mapY, side = sideDistY + deltaDistY, mapY + stepY, 1
- end
- if map[mapY] and map[mapY][mapX] and map[mapY][mapX] > 0 then hit = 1 end
- end
- if side == 0 then perpWallDist = (mapX - player.x + (1 - stepX) / 2) / rayDirX
- else perpWallDist = (mapY - player.y + (1 - stepY) / 2) / rayDirY end
- local lineHeight = math.floor(height / perpWallDist)
- local drawStart = math.max(1, -lineHeight / 2 + height / 2)
- local drawEnd = math.min(height, lineHeight / 2 + height / 2)
- local color = side == 1 and 0xAAAAAA or 0xFFFFFF
- for y = math.ceil(drawStart), math.floor(drawEnd) do
- setPixel(x, y, color)
- end
- end
- -- Draw enemies
- for _, enemy in ipairs(enemies) do
- local enemyX, enemyY = enemy.x, enemy.y
- local distX, distY = enemyX - player.x, enemyY - player.y
- local distance = math.sqrt(distX * distX + distY * distY)
- local enemyAngle = math.atan2(distY, distX) - player.angle
- local screenX = math.floor((width / 2) * (1 + math.tan(enemyAngle)))
- local enemyHeight = math.floor(height / distance)
- local drawStartY = math.max(1, height / 2 - enemyHeight / 2)
- local drawEndY = math.min(height, height / 2 + enemyHeight / 2)
- for y = drawStartY, drawEndY do
- setPixel(screenX, y, 0x00FF00)
- end
- end
- -- Draw bullets
- for _, bullet in ipairs(bullets) do
- local bulletX, bulletY = bullet.x, bullet.y
- local distX, distY = bulletX - player.x, bulletY - player.y
- local distance = math.sqrt(distX * distX + distY * distY)
- local bulletAngle = math.atan2(distY, distX) - player.angle
- local screenX = math.floor((width / 2) * (1 + math.tan(bulletAngle)))
- local bulletHeight = math.floor(height / distance)
- local drawStartY = math.max(1, height / 2 - bulletHeight / 2)
- local drawEndY = math.min(height, height / 2 + bulletHeight / 2)
- for y = drawStartY, drawEndY do
- setPixel(screenX, y, 0xFF0000)
- end
- end
- -- Draw enemy bullets
- for _, bullet in ipairs(enemyBullets) do
- local bulletX, bulletY = bullet.x, bullet.y
- local distX, distY = bulletX - player.x, bulletY - player.y
- local distance = math.sqrt(distX * distX + distY * distY)
- local bulletAngle = math.atan2(distY, distX) - player.angle
- local screenX = math.floor((width / 2) * (1 + math.tan(bulletAngle)))
- local bulletHeight = math.floor(height / distance)
- local drawStartY = math.max(1, height / 2 - bulletHeight / 2)
- local drawEndY = math.min(height, height / 2 + bulletHeight / 2)
- for y = drawStartY, drawEndY do
- setPixel(screenX, y, 0xFFFF00)
- end
- end
- swapBuffers()
- end
- local function updateBullets()
- for i = #bullets, 1, -1 do
- local bullet = bullets[i]
- bullet.x = bullet.x + math.cos(bullet.angle) * 0.5
- bullet.y = bullet.y + math.sin(bullet.angle) * 0.5
- -- Check for collisions with walls
- local mapX, mapY = math.floor(bullet.x), math.floor(bullet.y)
- if map[mapY] and map[mapY][mapX] and map[mapY][mapX] > 0 then
- table.remove(bullets, i)
- else
- -- Check for collisions with enemies
- for j = #enemies, 1, -1 do
- local enemy = enemies[j]
- if math.abs(bullet.x - enemy.x) < 0.5 and math.abs(bullet.y - enemy.y) < 0.5 then
- table.remove(enemies, j)
- table.remove(bullets, i)
- break
- end
- end
- end
- -- Remove bullets that are out of bounds
- if bullet.x < 1 or bullet.x > #map[1] or bullet.y < 1 or bullet.y > #map then
- table.remove(bullets, i)
- end
- end
- end
- local function updateEnemyBullets()
- for i = #enemyBullets, 1, -1 do
- local bullet = enemyBullets[i]
- bullet.x = bullet.x + math.cos(bullet.angle) * 0.5
- bullet.y = bullet.y + math.sin(bullet.angle) * 0.5
- if math.abs(bullet.x - player.x) < 0.5 and math.abs(bullet.y - player.y) < 0.5 then
- print("You died!")
- return true
- end
- -- Remove bullets that are out of bounds
- if bullet.x < 1 or bullet.x > #map[1] or bullet.y < 1 or bullet.y > #map then
- table.remove(enemyBullets, i)
- end
- end
- return false
- end
- local function fireBullet()
- if #bullets < 20 then
- table.insert(bullets, {x = player.x, y = player.y, angle = player.angle})
- end
- end
- local function update()
- local moveSpeed = 0.05
- local rotSpeed = 0.03
- local keys = {}
- local spawnTimer = 0
- local spawnInterval = 5 -- Spawn an enemy every 5 seconds on average
- while true do
- local e = {event.pull(0.05)} -- Poll events with a small timeout
- if e[1] == "key_down" then
- keys[e[4]] = true
- elseif e[1] == "key_up" then
- keys[e[4]] = nil
- end
- if keys[keyboard.keys.w] then
- player.x = player.x + math.cos(player.angle) * moveSpeed
- player.y = player.y + math.sin(player.angle) * moveSpeed
- end
- if keys[keyboard.keys.s] then
- player.x = player.x - math.cos(player.angle) * moveSpeed
- player.y = player.y - math.sin(player.angle) * moveSpeed
- end
- if keys[keyboard.keys.a] then
- player.angle = player.angle - rotSpeed
- end
- if keys[keyboard.keys.d] then
- player.angle = player.angle + rotSpeed
- end
- if keys[keyboard.keys.space] then
- fireBullet()
- end
- if keys[keyboard.keys.q] then
- return
- end
- -- Enemy spawning
- spawnTimer = spawnTimer + 0.05
- if spawnTimer >= spawnInterval then
- if math.random() < 0.2 then -- 20% chance to spawn an enemy
- spawnEnemy()
- end
- spawnTimer = 0
- end
- updateBullets()
- if updateEnemyBullets() then
- return
- end
- -- Update enemies
- for _, enemy in ipairs(enemies) do
- local dx = player.x - enemy.x
- local dy = player.y - enemy.y
- local distance = math.sqrt(dx * dx + dy * dy)
- if distance > 0.5 then
- enemy.x = enemy.x + dx / distance * moveSpeed / 2
- enemy.y = enemy.y + dy / distance * moveSpeed / 2
- else
- table.insert(enemyBullets, {x = enemy.x, y = enemy.y, angle = math.atan2(dy, dx)})
- end
- end
- draw()
- end
- end
- gpu.setResolution(width, height)
- draw()
- update()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement