ComputerCraft Archive

FuseFox Platformer Game

computer game unknown forum

Description

This is another demo game I made with my game engine and exported into one file. If you want to start using the engine click here for more info.This is just a simple platformer; you can jump, sneak an...

Installation

Copy one of these commands into your ComputerCraft terminal:

Pastebin:pastebin get dxLk0a9H fusefox_platformer_game
wget:wget https://pastebin.com/raw/dxLk0a9H fusefox_platformer_game
Archive:wget https://cc.shobie.xyz/cc/get/pb-dxLk0a9H fusefox_platformer_game
Quick Install: wget https://cc.shobie.xyz/cc/get/pb-dxLk0a9H FuseFox Platformer Game

Usage

Run the program after downloading

Tags

gameforumgames

Source

View Original Source

Code Preview

  width, height = term.getSize()

  local arguments = {...}
  if arguments and arguments[1] then
    local outputPath = arguments[2] or arguments[1]..".pin"
    local gameFile = fs.open(arguments[1], "r")
    local game = gameFile:readAll()
    gameFile.close()
    local engineFile = fs.open("engine", "r")
    local engine = engineFile.readAll()
    engineFile.close()

    local outputFile = fs.open(outputPath, "w")
    outputFile.write(engine)
    outputFile.write("\n\nengine=getfenv()\n-- Game Code")
    outputFile.write(game)
    outputFile.close()
    term.setTextColor(colors.green)
    print("Exported game "..arguments[1].." to "..outputPath.." successfully")
    error("", 0)
  end

  -- EVENT HANDLER MODULE
  local mouseKeys = {"left", "right", "other"}
  mouse = {x = 0, y = 0}
  keyboard = {lastChar = ""}
  eventHandler = {
    mouse_click = function(button, x, y)
      mouse.x, mouse.y = x, y
      mouse.isDown = true
      mouse.button = mouseKeys[button]
    end,
    mouse_up = function(button, x, y)
      mouse.x, mouse.y = x, y
      mouse.isDown = false
      mouse.isDragged = false
      mouse.button = mouseKeys[button]
    end,
    mouse_drag = function(button, x, y)
      mouse.x, mouse.y = x, y
      mouse.isDown = true
      mouse.dragX, mouse.dragY = x, y
      mouse.button = mouseKeys[button]
    end,

    key = function(key)
      keyboard[keys.getName(key)] = true
    end,
    key_up = function(key)
      keyboard[keys.getName(key)] = false
    end,
    char = function(char)
      keyboard.lastChar = char
      keyboard.newChar = true
    end
  }

  -- DYNAMIC LIGHT MODULE
  dynamicLight = {}
  dynamicLight.positions = {
    [0] = {0, -1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1}
  }
  dynamicLight.lightPixel = function(self, x, y)
    local degree = math.deg(math.atan2(self.lightSource.x-x, self.lightSource.y-y))+180
    local eightDegree = math.floor(degree/45)

    local tileX, tileY = x-dynamicLight.positions[eightDegree][1], y-dynamicLight.positions[eightDegree][2]
    if not (self.light[tileX] and self[tileX] and self[tileX][tileY]) then return end
    if self[tileX] and self[x] then
      if self.light[tileX][tileY] == true and not self[tileX][tileY].solid then
        self.light[x][y] = true
      else
        self.light[x][y] = false
      end
    end
  end


  -- ELEMENT MODULE
  moveTo = function(self, tilemap, xmove, ymove)
    local couldMove = true
    for move = 1, math.abs(xmove)+math.abs(ymove) do
      local canMove = true
      for x = 1, self.w do
        for y = 1, self.h do
          if (self.image and self.image[y][x]) or not self.image then
            local moveToX = self.x+xmove+x-1
            local moveToY = self.y+ymove+y-1
            if moveToX < 1 or moveToX > width or moveToY < 1 or moveToY > height then canMove = false end
            local tile = tilemap:getTile(moveToX, moveToY)
            if (not tile) or tile.solid then couldMove = false canMove = false end
          end
        end
      end
      if canMove then
        self.x = self.x + xmove
        self.y = self.y + ymove
      end
    end
    return couldMove
  end
  isBoxClicked = function(box)
    return mouse.isDown and box.x<=mouse.x and box.y<=mouse.y and box.x+box.w>mouse.x and box.y+box.h>mouse.y
  end
  newElement = function(elementTab)
    return function(tab)
      return setmetatable(
        tab,
        {
          __index = elementTab
        }
      )
    end
  end
  elements = {
    tilemap = newElement({
      saveTilemap = function(self, path)
        local tileTab = {}
        for x = 1, #self do
          tileTab[x] = {}
          for y = 1, #self[x] do
            tileTab[x][y] = self[x][y].tile
          end
        end
        local file = fs.open(path..".til", "w")
        local tilemapData = textutils.serialize(tileTab)
        file.write(tilemapData)
        file.close()
      end,
      loadTilemap = function(self, path)
        local tilemapTab
        if type(path) == "table" then
          tilemapTab = path
        else
          local file = fs.open(path..".til", "r")
          local tilemapData = file.readAll()
          tilemapTab = textutils.unserialize(tilemapData)
        end
        for x = 1, #tilemapTab do
          for y = 1, #tilemapTab[x] do
            self:setTile(x, y, tilemapTab[x][y])
          end
        end
      end,
      generated = false,
      getTile = function(self, x, y)
        if self[x] then
          return self[x][y]
        else
          return {}
        end
      end,
      setTile = function(self, x, y, tile)
        if not self[x] then self[x] = {} end
        self[x][y] = self.tileset[tile]
        self[x][y].tile = tile
      end,
      setRectangle = function(self, rx, ry, w, h, tile)
        for x = rx, rx+w do
          for y = ry, ry+h do
            self:setTile(x, y, tile)
          end
        end
      end,
      [1] = {},
      light = {},
      lightUpdateTime = 7,
      DRAW = function(self)
        for y = 1, #self[1] do
          term.setCursorPos(1, y)
          for x = 1, #self do
            local tile = self[x][y]
            if tile then
              if self.dynamicLight and not tile.solid and not self.light[x][y] then
                term.setBackgroundColor(colors.black)
                term.write(" ")
              else
                term.setBackgroundColor(tile.bc)
                term.setTextColor(tile.tc)
                term.write(tile.char)
              end
            else
              term.write(" ")
            end
          end
        end
      end,
      UPDATE = function(self)
        if self.generate and not self.generated then
          self:generate()
          self.generated = true
          if self.dynamicLight then
            for x = 1, #self do
              self.light[x] = {}
            end
          end
        end
        if self.dynamicLight then
          for x = 1, 3 do
            for y = 1, 3 do
              if self.light[self.lightSource.x+x-2] then
                self.light[self.lightSource.x+x-2][self.lightSource.y+y-2] = true
              end
            end
          end

          for i = 1, self.lightUpdateTime do
            for x = 1, #self do
              for y = 1, #self[1] do
                dynamicLight.lightPixel(self, x, y)
              end
            end
          end
        end
      end
    }),
    button = newElement({
      UPDATE = function(self)
        if isBoxClicked(self) then
          if not self.clicked and self.clickedFunction then self:clickedFunction() end
          self.clicked = true
        else
          self.clicked = false
        end
      end,
      DRAW = function(self)
        if self.clicked then
          term.setBackgroundColor(colors.white)
          term.setTextColor(colors.lightGray)
        else
          term.setBackgroundColor(colors.lightGray)
          term.setTextColor(colors.gray)
        end
        paintutils.drawFilledBox(self.x, self.y, self.w+self.x-1, self.h+self.y-1)
        term.setCursorPos(self.x+self.w/2-#self.label/2, self.y+self.h/2)
        term.write(self.label)
      end
    }),
    sprite = newElement({
      path = "",
      DRAW = function(self)
        if not self.image then
          self.image = paintutils.loadImage(self.path)
          self.w = 1
          for y = 1, #self.image do
            if #self.image[y] > self.w then
              self.w = #self.image[y]
            end
          end
          self.h = #self.image
        end
        paintutils.drawImage(self.image, self.x, self.y)
      end
    }),
    template = newElement({
      UPDATE = function(self)
      end,
      DRAW = function(self)
      end
    }),
  }


  -- LIFECYCLE FUNCTIONS
  lastUpdate = os.clock()
  runProtected = function(func)
    local succ, mess = pcall(func)
    if not succ then
      quit(mess)
    end
  end
  update = function(elements, wantedDt, func)
    runProtected(function()
    local timer = os.startTimer(wantedDt-(os.clock()-lastUpdate))
    local event, var1, var2, var3 = os.pullEventRaw()
    if event == "terminate" then quit(true) end
    os.cancelTimer(timer)

    if event ~= "mouse_drag" then mouse.isDragged = false end
    if event ~= "char" then keyboard.newChar = false end
    if eventHandler[event] then eventHandler[event](var1, var2, var3) end

    if os.clock()-lastUpdate >= wantedDt then
      for elementName, element in pairs(elements) do
        if element.UPDATE then
          element:UPDATE()
        end
        if element.update then
          element:update()
        end
      end
      if func then func() end
      lastUpdate = os.clock()
    end
    end)
  end
  buffer = window.create(term.current(), 1, 1, width, height)
  draw = function(elements, func)
    runProtected(function()
    buffer.setVisible(false)
    local oldterm = term.redirect(buffer)
    term.setBackgroundColor(colors.black)
    term.clear()
    local prioritys = {}
    for elementName, element in pairs(elements) do
      if element.priority then
        if element.priority == true then
          table.insert(prioritys, element)
        else
          prioritys[element.priority] = element
        end
      end
      if element.DRAW then
        element:DRAW()
      end
      if element.draw then
        element:draw()
      end
    end
    for i = 1, #prioritys do
      local element = prioritys[i]
      if element.DRAW then
        element:DRAW()
      end
      if element.draw then
        element:draw()
      end
    end
    if func then func() end
    buffer.setVisible(true)
    term.redirect(oldterm)
    end)
  end
  quit = function(hard)
    buffer.setVisible(true)
    term.setBackgroundColor(colors.black)
    term.setTextColor(colors.yellow)
    term.clear()
    term.setCursorPos(1, 1)
    print("Thanks for using the Pinwyn Game Engine by Jummit!")
    if type(hard) == "string" then
      print("The game crashed! Here is the error:")
      term.setTextColor(colors.red)
      print(hard)
    elseif hard then
      print("The game was forcefully closed")
    else
      print("The game was closed without any errors")
    end
    error()
  end

  engine=getfenv()
  -- Game Codeos.loadAPI("engine")

  local gameEnded = false
  local level = {
    {
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
      "wall",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "wall",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "wall",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "wall",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "floor",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
    },
    {
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
      "wall",
    },
  }
  elements = {
    player = engine.elements.sprite({
      priority = true,
      image = {{1}, {1}}, w = 1, h = 2,
      gravity = 0.5,
      x = 3, y = 16,
      move = {x = 0, y = 0},
      jumpHeight = 6,
      jumpedHeight = 0,
      update = function(self)
        if self.x == 4 and self.y == 4 then
          gameEnded = true
        end
        local tile = elements.tilemap:getTile(self.x, self.y+2)
        if tile and tile.solid then
          self.jumpedHeight = 0
        end
        local jumped
        if engine.keyboard.up or engine.keyboard.w then
          self.jumpedHeight = self.jumpedHeight + 1
          if self.jumpedHeight<self.jumpHeight then
            jumped = true
            if not engine.moveTo(self, elements.tilemap, 0, -1) then
              self.jumpedHeight = self.jumpHeight
            end
          end
        else
          self.jumpedHeight = self.jumpHeight
        end
        self.jumpHeight = 6
        if engine.keyboard.down or engine.keyboard.s then
          self.image[1][1] = nil
          self.jumpHeight = 3
        elseif not elements.tilemap:getTile(self.x, self.y).solid then
          self.image[1][1] = 1
        end
        if not jumped then
          engine.moveTo(self, elements.tilemap, 0, 1)
        end
        if engine.keyboard.left or engine.keyboard.a then engine.moveTo(self, elements.tilemap, -1, 0) end
        if engine.keyboard.right or engine.keyboard.d then engine.moveTo(self, elements.tilemap, 1, 0) end
      end
    }),
    tilemap = engine.elements.tilemap({
      tileset = {
        wall = {bc=colors.gray,tc=colors.lightGray,char=" ",solid=true},
        floor = {bc=colors.lightGray,tc=colors.gray,char="ยท"}
      },
      generate = function(self)
        self:loadTilemap(level)
      end,
      update = function(self)
        if engine.mouse.isDown then
          if engine.mouse.button == "left" then
            self:setTile(engine.mouse.x, engine.mouse.y, "wall")
          else
            self:setTile(engine.mouse.x, engine.mouse.y, "floor")
          end
        end
        if engine.keyboard.k then
          self:saveTilemap("mapsave")
        elseif engine.keyboard.l then
          self:loadTilemap("mapsave")
        end
        if engine.keyboard.c then
          self:setRectangle(1, 1, engine.width, engine.height, "wall")
          self:setRectangle(2, 2, engine.width-3, engine.height-3, "floor")
        end
      end
    }),
    timer = {
      priority = true,
      time = 0,
      red = false,
      draw = function(self)
        term.setCursorPos(engine.width-10, 1)
        term.setBackgroundColor(colors.gray)
        if self.red then
          term.setTextColor(colors.red)
        else
          term.setTextColor(colors.yellow)
        end
        term.write("time: "..self.time)
      end,
      update = function(self)
        self.red = not self.red
        if not gameEnded then
          self.time = self.time+0.1
        end
      end
    },
    hint = {
      draw = function()
        term.setBackgroundColor(colors.gray)
        term.setTextColor(colors.white)
        term.setCursorPos(2, engine.height)
        term.write("Press k to save, l to load and c to clear")
        term.setCursorPos(2, 1)
        term.write("WASD and arrow keys to move")
      end
    },
    endScreen = {
        update = function()
          if gameEnded then
            elements.player = nil
          end
        end,
        draw = function()
          if gameEnded then
            paintutils.drawFilledBox(8, 7, engine.width-7, engine.height-5, colors.lightBlue)
            term.setCursorPos(10, 10)
            term.setBackgroundColor(colors.lightBlue)
            term.setTextColor(colors.white)
            print("You won! Youre time was "..elements.timer.time)
            term.setCursorPos(8, 12)
            print("You can share your time on the forum!")
          end
        end
    }
  }

  while true do
    engine.update(elements, 0.06, function() if engine.keyboard.q then engine.quit() end end)
    engine.draw(elements)
  end