Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- Code Runner
- Inspired by the game by Doug
- Written by: Nitrogen Fingers
- ]]--
- running = true
- started = false
- nextLevel = false
- local drawOffsetX = 1
- local drawOffsetY = 0
- local map = {}
- local goldMap = {}
- local blockTimers = {}
- local blockIntv = 5
- local monks = {}
- local monkTimer = -1
- local monkSpawnIntv = 3
- local monkTrapIntv = blockIntv/2
- local goldCount = 0
- local maxGoldCount = 0
- local playerLives = 3
- local playerScore = 0
- local plspawnX = 0
- local plspawnY = 0
- local plX = 0
- local plY = 0
- local pfalling = false
- local moveTimer = -1
- local shootTimer = -1
- local spawnTimer = -1
- local moveIntv = 0.15
- local exX = 0
- local exY = 0
- local levelList = {}
- local currentLevel = 1
- local function loadMap(_sPath)
- if not fs.exists(_sPath) then return end
- map = {}
- goldMap = {}
- monks = {}
- goldCount = 0
- local file = fs.open(_sPath, "r")
- local line = file:readLine()
- while line do
- goldMap[#map+1] = {}
- map[#map+1] = {}
- for i=1,math.min(#line,49) do
- local lchar = string.sub(line,i,i)
- if tonumber(lchar, 16) then
- lchar = math.pow(2, tonumber(lchar,16))
- if lchar == colours.blue then
- map[#map][i] = 0
- elseif lchar == colours.brown then
- map[#map][i] = 'H'
- elseif lchar == colours.yellow then
- goldMap[#map][i] = 1
- goldCount = goldCount + 1
- elseif lchar == colours.orange then
- map[#map][i] = 0
- goldMap[#map][i] = 1
- goldCount = goldCount + 1
- elseif lchar == colours.green then
- map[#map][i] = '-'
- elseif lchar == colours.lightGrey then
- map[#map][i] = 'h'
- elseif lchar == colours.grey then
- map[#map][i] = '#'
- elseif lchar == colours.white then
- plX = i
- plspawnX = i
- plY = #map
- plspawnY = #map
- elseif lchar == colours.lime then
- exX = i
- exY = #map
- elseif lchar == colours.red then
- table.insert(monks, {
- --X and Y, clear enough
- x = i, y = #map;
- --Where they spawn when they die
- spawnX = i, spawnY = #map;
- -- Any gold they're carring- it's a 1 in 5
- gold = false;
- -- Whether or not they're falling
- falling = false;
- -- Timer if they're dead to respawn
- dead = nil;
- --Whether or not the monk has just spawned
- justSpawned = true;
- --Whether or not the monk has just escaped
- justEscaped = false;
- -- Current aim- it's "up", "down", "across" or "none"
- behaviour = "none";
- -- The desired x position to travel to, when one is necessary.
- desX = nil;
- -- The escape timer
- trapped = nil;
- })
- end
- end
- end
- if #map == 18 then break end
- line = file:readLine()
- end
- file:close()
- maxGoldCount = goldCount
- return true
- end
- --When something moves or something needs to be drawn, we
- --just change the appropriate tile with this method.
- local function updateMap(x,y)
- term.setCursorPos(x + drawOffsetX, y + drawOffsetY)
- term.setBackgroundColour(colours.black)
- if plX == x and plY == y and map[y][x] ~= 0 then
- term.setTextColour(colours.white)
- if map[y][x] == 1 then term.setBackgroundColour(colours.lightBlue) end
- term.write("&")
- elseif map[y][x] == 'H' or (map[y][x] == 'h' and
- goldCount == 0) then
- term.setTextColour(colours.brown)
- term.write("H")
- elseif map[y][x] == '-' then
- term.setTextColour(colours.brown)
- term.write(map[y][x])
- elseif map[y][x] == '#' then
- term.setBackgroundColour(colours.grey)
- term.write(" ")
- elseif type(map[y][x]) == "number" then
- local uchar = ' '
- if map[y][x] == 3 then
- term.setBackgroundColour(colours.lightBlue)
- elseif map[y][x] == 2 and goldMap[y][x] == 1 then
- term.setTextColour(colours.yellow)
- uchar = '$'
- elseif map[y][x] == 1 then
- term.setBackgroundColour(colours.lightBlue)
- elseif map[y][x] == 0 then
- term.setBackgroundColour(colours.blue)
- end
- term.write(uchar)
- elseif goldMap[y][x] == 1 then
- term.setTextColour(colours.yellow)
- term.write('$')
- elseif exX == x and exY == y and goldCount == 0 then
- term.setTextColour(colours.lime)
- term.write("@")
- else
- term.write(" ")
- end
- end
- --It's silly to iterate through all monks when drawing tiles, so
- --we do it separately.
- local function drawMonk(monk)
- term.setCursorPos(monk.x + drawOffsetX, monk.y + drawOffsetY)
- if monk.justSpawned then term.setTextColour(colours.pink)
- else term.setTextColour(colours.red) end
- if map[monk.y][monk.x] == 1 then term.setBackgroundColour(colours.lightBlue)
- else term.setBackgroundColour(colours.black) end
- term.write("&")
- end
- --Draws the map for the first time. It barely changes, so we really
- --only call this the once.
- local function drawMap()
- for y=1,#map do
- for x=1,49 do
- updateMap(x,y)
- end
- end
- for _,monk in pairs(monks) do drawMonk(monk)end
- end
- --When all coins have been collected, we add in invisble ladders and
- --the end game portal.
- local function drawEndgameMap()
- for y=1,#map do
- for x=1,49 do
- if map[y][x] == 'h' or (exX == x and exY == y) then
- updateMap(x,y)
- end
- end
- end
- end
- --Sets the map back to defaults, so we can start afresh
- local function resetMap()
- goldCount = maxGoldCount
- for i=1,#goldMap do
- for j=1,49 do
- if goldMap[i][j] == 0 then goldMap[i][j] = 1 end
- end
- end
- for _,monk in pairs(monks) do
- monk.justSpawned = true
- monk.dead = nil
- monk.trapped = nil
- monk.justEscaped = false
- monk.falling = false
- monk.behaviour = "none"
- monk.x = monk.spawnX
- monk.y = monk.spawnY
- end
- for _,timer in pairs(blockTimers) do
- map[timer.y][timer.x] = 0
- end
- blockTimers = {}
- plX = plspawnX
- plY = plspawnY
- moveTimer = -1
- shootTimer = -1
- spawnTimer = -1
- monkTimer = -1
- pfalling = false
- end
- --Draws the HUD. This also rarely changes, so we update it when something happens.
- local function drawHUD()
- term.setCursorPos(2,19)
- term.setBackgroundColour(colours.black)
- term.clearLine()
- term.setTextColour(colours.blue)
- term.write("Score: ")
- term.setTextColour(colours.yellow)
- term.write(string.rep("0", 5-math.floor(math.log10(playerScore + 1)))
- ..playerScore)
- term.setTextColour(colours.yellow)
- term.setCursorPos(25 - #levelList[currentLevel]/2, 19)
- term.write(levelList[currentLevel])
- local lstr = "Men: "
- term.setCursorPos(50 - #lstr - math.floor(math.log10(playerLives)), 19)
- term.setTextColour(colours.blue)
- term.write(lstr)
- term.setTextColour(colours.yellow)
- term.write(playerLives.."")
- end
- --Checks to see if any given desired move is legal. Monks and players both use this.
- local function isLegalMove(initX,initY,finX,finY)
- if map[finY][finX] ~= 0 and map[finY][finX] ~= '#' then
- --This reports 'self moves' as being illegal, but that's fine
- for _,monk in pairs(monks) do
- if monk.x == finX and monk.y == finY then return false end
- end
- if finY == initY-1 and (map[initY][initX] == "H" or (map[initY][initX] == "h" and goldCount == 0))
- then return true
- elseif finY == initY+1 and (map[finY][finX] == "H" or (map[finY][finX] == "h" and goldCount == 0)
- or (type(map[finY][finX]) == "number" and map[finY][finX] > 0) or map[finY][finX] == nil or
- map[finY][finX] == "-")
- then return true
- elseif finX == initX-1 or finX == initX+1 then return true
- end
- end
- end
- --Moves the player to a given step.
- local function movePlayer(x,y,ignoreLegal)
- if not ignoreLegal and not isLegalMove(plX,plY,x,y) then return false end
- local ox = plX
- local oy = plY
- plX = x
- plY = y
- updateMap(ox,oy)
- updateMap(x,y)
- if goldMap[y][x] == 1 then
- goldMap[y][x] = 0
- goldCount = goldCount - 1
- playerScore = playerScore + 5
- if playerScore % 200 == 0 then playerLives = playerLives + 1 end
- drawHUD()
- if (goldCount == 0) then
- drawEndgameMap()
- end
- elseif exX == plX and exY == plY and goldCount == 0 then
- started = false
- nextLevel = true
- end
- pfalling = (y < #map and map[y][x] ~= '-' and map[y][x] ~= 'H' and not (map[y][x] == 'h' and goldCount == 0)
- and (map[y+1][x] == nil or map[y+1][x] == 2 or map[y+1][x] == '-'))
- for _,monk in pairs(monks) do
- if monk.x == plX and monk.y == plY + 1 then pfalling = false break end
- end
- return true
- end
- local function updateMonks()
- for _,monk in pairs(monks) do
- --Absolute first step- if he's trapped or dead, he's going nowhere
- if monk.trapped or monk.dead then
- --If he's just spawned he takes a second to orient himself
- elseif monk.justSpawned then
- monk.justSpawned = false
- --We evaluate their falling behaviour here (as freed monks CAN stand on air)
- monk.falling = (monk.y < #map and map[monk.y][monk.x] ~= '-' and (map[monk.y+1][monk.x] == nil or
- map[monk.y+1][monk.x] == 2 or map[monk.y+1][monk.x] == '-') and type(map[monk.y][monk.x] ~= "number"))
- for _,omonk in pairs(monks) do
- if omonk.x == monk.x and omonk.y == monk.y + 1 then monk.falling = false break end
- end
- if monk.x == plX and monk.y == plY + 1 then monk.falling = false end
- --Then we consider if he's just gotten out of a hole
- elseif monk.justEscaped then
- monk.justEscaped = false
- --He tries the player side first
- local playerSide = (plX-monk.x) / math.abs(plX-monk.x)
- if isLegalMove(monk.x, monk.y, monk.x + playerSide, monk.y) then
- monk.x = monk.x + playerSide
- updateMap(monk.x - playerSide, monk.y)
- elseif isLegalMove(monk.x, monk.y, monk.x - playerSide, monk.y) then
- monk.x = monk.x - playerSide
- updateMap(monk.x + playerSide, monk.y)
- end
- drawMonk(monk)
- --Then we evaluate falling
- elseif monk.falling then
- monk.behaviour = "none"
- monk.y = monk.y + 1
- updateMap(monk.x, monk.y-1)
- drawMonk(monk)
- monk.desX = nil
- if type(map[monk.y][monk.x]) == "number" then
- monk.trapped = os.startTimer(monkTrapIntv)
- monk.falling = false
- else
- monk.falling = (monk.y < #map and map[monk.y][monk.x] ~= '-' and (map[monk.y+1][monk.x] == nil or
- map[monk.y+1][monk.x] == 2 or map[monk.y+1][monk.x] == '-') and type(map[monk.y][monk.x] ~= "number"))
- for _,omonk in pairs(monks) do
- if omonk.x == monk.x and omonk.y == monk.y + 1 then monk.falling = false break end
- end
- if monk.x == plX and monk.y == plY + 1 then monk.falling = false end
- if monk.justEscaped then monk.falling = false end
- end
- --If he's on his feet and not trapped, he's allowed to think about where to move
- elseif monk.y == plY then
- --Is the monk on the same level as the player? How lucky! They'll just walk towards him
- monk.desX = plX
- monk.behaviour = "across"
- --Y difference takes precedence over X (as in the original, makes them a bit smarter)
- elseif monk.y < plY then
- --If they can move up, they will
- if isLegalMove(monk.x,monk.y,monk.x,monk.y+1) and not monk.justEscaped then
- monk.y = monk.y+1
- updateMap(monk.x, monk.y-1)
- drawMonk(monk)
- monk.desX = nil
- --A down move can lead to a fall, so we check if they're now falling.
- monk.falling = (monk.y < #map and map[monk.y][monk.x] ~= '-' and (map[monk.y+1][monk.x] == nil or
- map[monk.y+1][monk.x] == 2 or map[monk.y+1][monk.x] == '-') and type(map[monk.y][monk.x] ~= "number"))
- monk.falling = (monk.y < #map and map[monk.y][monk.x] ~= '-' and (map[monk.y+1][monk.x] == nil or
- map[monk.y+1][monk.x] == 2 or map[monk.y+1][monk.x] == '-') and type(map[monk.y][monk.x] ~= "number"))
- for _,omonk in pairs(monks) do
- if omonk.x == monk.x and omonk.y == monk.y + 1 then monk.falling = false break end
- end
- if monk.x == plX and monk.y == plY + 1 then monk.falling = false end
- --Otherwise, it's off to the nearest ladder, monkey bars or perilous ledge to jump off
- --assuming they haven't found one already
- elseif monk.desX == nil then
- if monk.behaviour ~= "down" then monk.desX = nil end
- monk.behaviour = "down"
- monk.desX = nil
- local cmLeft = true
- local cmRight = true
- --We try to find the nearest by searching alternate left and right at variable distance
- for i=1,math.max(monk.x - 1, 49 - monk.x) do
- if monk.x-i > 0 and cmLeft then
- --If a wall blocks the monks path, they can't keep going left or right
- cmLeft = map[monk.y][monk.x-i] ~= 0
- --But if it's all clear, they look for something to climb/jump down
- if cmLeft and (map[monk.y+1][monk.x-i] == "H" or (map[monk.y+1][monk.x-i] == 'h' and goldCount == 0)
- or map[monk.y+1][monk.x-i] == nil or map[monk.y][monk.x-i] == '-') then
- monk.desX = monk.x-i
- break
- end
- end
- if monk.x+i < 50 and cmRight then
- --If a wall blocks the monks path, they can't keep going left or right
- cmRight = map[monk.y][monk.x+i] ~= 0
- --But if it's all clear, they look for something to climb/jump down
- if cmRight and (map[monk.y+1][monk.x+i] == "H" or (map[monk.y+1][monk.x+i] == 'h' and goldCount == 0)
- or map[monk.y+1][monk.x+i] == nil or map[monk.y][monk.x+i] == '-') then
- monk.desX = monk.x+i
- break
- end
- end
- end
- end
- elseif monk.y > plY then
- if monk.behaviour ~= "up" then monk.desX = nil end
- monk.behaviour = "up"
- --Same deal again- try moving up first
- if isLegalMove(monk.x,monk.y,monk.x,monk.y-1) then
- monk.y = monk.y-1
- updateMap(monk.x, monk.y+1)
- drawMonk(monk)
- monk.desX = nil
- --You can never move up and start falling, so we don't bother to check
- --Otherwise they need ladders to climb up
- elseif monk.desX == nil then
- monk.behaviour = "up"
- monk.desX = nil
- local cmLeft = true
- local cmRight = true
- --We try to find the nearest by searching alternate left and right at variable distance
- for i=1,math.max(monk.x - 1, 49 - monk.x) do
- if monk.x-i > 0 and cmLeft then
- --If a wall blocks the monks path or a pit is in the way, they can't keep going left or right
- cmLeft = map[monk.y][monk.x-i] ~= 0 and (monk.y == #map or map[monk.y+1][monk.x-i] ~= nil
- or map[monk.y][monk.x-i] == '-')
- --But if it's all clear, they look for a ladder
- if cmLeft and (map[monk.y][monk.x-i] == "H" or (map[monk.y][monk.x-i] == 'h' and goldCount == 0)) then
- monk.desX = monk.x-i
- break
- end
- end
- if monk.x+i < 50 and cmRight then
- cmRight = map[monk.y][monk.x+i] ~= 0 and (monk.y == #map or map[monk.y+1][monk.x+i] ~= nil
- or map[monk.y][monk.x+i] == '-')
- if cmRight and (map[monk.y][monk.x+i] == "H" or (map[monk.y][monk.x+i] == 'h' and goldCount == 0)) then
- monk.desX = monk.x+i
- break
- end
- end
- end
- end
- end
- if not (monk.trapped or monk.dead) then
- --Has the monk decided on moving left or right? If so we try to move him
- if monk.desX and not monk.falling then
- local mdir = monk.desX - monk.x
- local mdir = mdir / math.abs(mdir)
- if isLegalMove(monk.x,monk.y,monk.x+mdir,monk.y) then
- monk.x = monk.x + mdir
- updateMap(monk.x - mdir, monk.y)
- drawMonk(monk)
- else
- --This allows re-evaluations if they get stuck- not ideal but good enough
- monk.desX = nil
- end
- end
- monk.falling = (monk.y < #map and map[monk.y][monk.x] ~= '-' and (map[monk.y+1][monk.x] == nil or
- map[monk.y+1][monk.x] == 2 or map[monk.y+1][monk.x] == '-') and type(map[monk.y][monk.x] ~= "number"))
- for _,omonk in pairs(monks) do
- if omonk.x == monk.x and omonk.y == monk.y + 1 then monk.falling = false break end
- end
- if monk.x == plX and monk.y == plY + 1 then monk.falling = false end
- --We have caught and killed the player
- if monk.x == plX and monk.y == plY and spawnTimer == -1 then
- spawnTimer = os.startTimer(2)
- end
- end
- end
- end
- local function updateBlockTimer(tid)
- local remAt = nil
- for i,v in ipairs(blockTimers) do
- if v.timer == tid then
- if map[v.y][v.x] == 3 then
- map[v.y][v.x] = 2
- v.timer = os.startTimer(blockIntv)
- elseif map[v.y][v.x] == 2 then
- map[v.y][v.x] = 1
- v.timer = os.startTimer(0.1)
- elseif map[v.y][v.x] == 1 then
- map[v.y][v.x] = 0
- --If the player is caught in a block, he dies
- if v.y == plY and v.x == plX then
- spawnTimer = os.startTimer(2)
- end
- for _,monk in pairs(monks) do
- if monk.x == v.x and monk.y == v.y then
- monk.dead = os.startTimer(monkSpawnIntv)
- --Easiest way to get them out of the way rather than evaluation
- monk.x = -1
- monk.y = -1
- monk.trapped = nil
- end
- end
- remAt = i
- end
- updateMap(v.x,v.y)
- break
- end
- end
- if remAt then table.remove(blockTimers,remAt) end
- end
- local function shootBlock(x,y)
- if y <= #map and map[y][x] == 0 and (map[y-1][x] == nil
- or map[y-1][x] == 2 or (map[y-1][x] == 'h' and goldCount > 0)) then
- map[y][x] = 3
- table.insert(blockTimers, {x = x; y = y; timer = os.startTimer(0.1);} )
- updateMap(x,y)
- end
- end
- local function handleEvents()
- local id,p1,p2,p3 = os.pullEvent()
- if id == "key" then
- if p1 == keys.a and moveTimer == -1 and spawnTimer == -1 then
- movePlayer(plX-1,plY)
- moveTimer = os.startTimer(moveIntv)
- elseif p1 == keys.d and moveTimer == -1 and spawnTimer == -1 then
- movePlayer(plX+1,plY)
- moveTimer = os.startTimer(moveIntv)
- elseif p1 == keys.w and moveTimer == -1 and spawnTimer == -1 then
- movePlayer(plX,plY-1)
- moveTimer = os.startTimer(moveIntv)
- elseif p1 == keys.s and moveTimer == -1 and spawnTimer == -1 then
- movePlayer(plX,plY+1)
- moveTimer = os.startTimer(moveIntv)
- elseif p1 == keys.q and shootTimer == -1 and not pfalling and spawnTimer == -1 then
- shootBlock(plX-1,plY+1)
- shootTimer = os.startTimer(moveIntv)
- elseif p1 == keys.e and shootTimer == -1 and not pfalling and spawnTimer == -1 then
- shootBlock(plX+1,plY+1)
- shootTimer = os.startTimer(moveIntv)
- elseif p1 == keys.enter then
- started = false
- elseif p1 == keys.t then
- started = false
- running = false
- end
- elseif id == "timer" then
- if p1 == shootTimer then shootTimer = -1
- elseif p1 == spawnTimer then
- started = false
- elseif p1 == moveTimer then
- if pfalling then
- movePlayer(plX,plY+1)
- moveTimer = os.startTimer(moveIntv)
- else
- moveTimer = -1
- end
- elseif p1 == monkTimer then
- updateMonks()
- monkTimer = os.startTimer(moveIntv * 2)
- elseif updateBlockTimer(p1) then
- else
- for _,monk in pairs(monks) do
- if p1 == monk.trapped then
- --You can stand on a monk to force them to be killed- so we check for that
- --along with being buried in tunnels, etc.
- local stillTrapped = map[monk.y-1][monk.x] == 0 or (plX == monk.x and plY == monk.y-1)
- for _,omonk in pairs(monks) do
- if omonk.x == monk.x and omonk.y == monk.y-1 then
- stillTrapped = true
- break
- end
- end
- --Perpetually trapped monks will try to excape much more quickly
- if stillTrapped then
- --This needs to be tweaked
- monk.trapped = os.startTimer(0.75)
- else
- --When free, they head in your general direction, re-evaluate later
- monk.y = monk.y - 1
- --This is necessary to stop 'double jumping'
- monk.desX = nil
- monk.trapped = nil
- monk.behaviour = "none"
- monk.justEscaped = true
- updateMap(monk.x, monk.y+1)
- drawMonk(monk)
- end
- break
- elseif p1 == monk.dead then
- --Same deal- you can camp spawn
- local stillDead = plX == monk.spawnX and plY == monk.spawnY
- for _,omonk in pairs(monks) do
- if omonk.x == monk.spawnX and omonk.y == monk.spawnY then
- stillDead = true
- break
- end
- end
- --They'll spawn the second you give them the chance
- if stillDead then
- monk.dead = os.startTimer(0.5)
- else
- monk.x = monk.spawnX
- monk.y = monk.spawnY
- monk.dead = nil
- monk.justSpawned = true
- monk.behaviour = "none"
- drawMonk(monk)
- break
- end
- end
- end
- end
- end
- end
- term.clear()
- if not fs.exists(shell.resolve(".").."/levels") then
- error("Level directory not present!")
- end
- levelList = fs.list(shell.resolve(".").."/levels")
- if #levelList == 0 then
- error("Level directory is empty!")
- end
- loadMap(shell.resolve(".").."/levels/"..levelList[currentLevel])
- while running do
- drawMap()
- drawHUD()
- os.pullEvent("key")
- monkTimer = os.startTimer(moveIntv * 1.5)
- started = true
- while started do
- handleEvents()
- end
- if nextLevel then
- if currentLevel == #levelList then
- running = false
- break
- else
- currentLevel = currentLevel + 1
- resetMap()
- loadMap(shell.resolve(".").."/levels/"..levelList[currentLevel])
- end
- nextLevel = false
- else
- playerLives = playerLives-1
- if playerLives > 0 then resetMap()
- else running = false end
- end
- end
- if nextLevel then
- local msg = "All levels defeated, Code Runner!"
- term.setBackgroundColour(colours.black)
- term.setTextColour(colours.lime)
- term.setCursorPos(25 - #msg/2, 2)
- term.write(msg)
- else
- local msg = "Game over!"
- term.setBackgroundColour(colours.black)
- term.setTextColour(colours.red)
- term.setCursorPos(25 - #msg/2, 2)
- term.write(msg)
- end
- sleep(2)
- term.setCursorPos(19,51)
- print("")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement