diff --git a/platform-test/game.lua b/platform-test/game.lua new file mode 100644 index 0000000..c57b391 --- /dev/null +++ b/platform-test/game.lua @@ -0,0 +1,513 @@ +local game = {} +game.path = fs.combine(fs.getDir(shell.getRunningProgram()),".game") +game.apiPath = fs.combine(game.path, "api") +game.spritePath = fs.combine(game.path, "sprites") +game.mapPath = fs.combine(game.path, "maps") +game.imagePath = fs.combine(game.path, "image") +game.configPath = fs.combine(game.path, "config.cfg") + +local scr_x, scr_y = term.getSize() +local mapname = "testmap" + +local scrollX = 0 +local scrollY = 0 + +local keysDown = {} + +local tsv = function(visible) + if term.current().setVisible then + term.current().setVisible(visible) + end +end + +local getAPI = function(apiName, apiPath, apiURL, doDoFile) + if not fs.exists(apiPath) then + write("Getting " .. apiName .. "...") + local prog = http.get(apiURL) + if prog then + print("success!") + local file = fs.open(apiPath, "w") + file.write(prog.readAll()) + file.close() + else + error("fail!") + end + end + if doDoFile then + _ENV[fs.getName(apiPath)] = dofile(apiPath) + else + os.loadAPI(apiPath) + end +end + +getAPI("NFT Extra", "nfte", "https://github.com/LDDestroier/NFT-Extra/raw/master/nfte", false) + +-- load sprites from sprite folder +-- sprites are separated into "sets", but the only one here is "megaman" so whatever + +local sprites, maps = {}, {} +for k, set in pairs(fs.list(game.spritePath)) do + sprites[set] = {} + for num, name in pairs(fs.list(fs.combine(game.spritePath, set))) do + sprites[set][name:gsub(".nft", "")] = nfte.loadImage(fs.combine(game.spritePath, set .. "/" .. name)) + print("Loaded sprite " .. name:gsub(".nft","")) + end +end +for num, name in pairs(fs.list(game.mapPath)) do + maps[name:gsub(".nft", "")] = nfte.loadImage(fs.combine(game.mapPath, name)) + print("Loaded map " .. name:gsub(".nft","")) +end + +local projectiles = {} +local players = {} + +local newPlayer = function(name, spriteset, x, y) + return { + name = name, -- player name + spriteset = spriteset, -- set of sprites to use + sprite = "stand", -- current sprite + direction = 1, -- 1 is right, -1 is left + xsize = 10, -- hitbox x size + ysize = 8, -- hitbox y size + x = x, -- x position + y = y, -- y position + xadj = 0, -- adjust x for good looks + yadj = 0, -- adjust y for good looks + xvel = 0, -- x velocity + yvel = 0, -- y velocity + jumpHeight = 3, -- height of jump + jumpAssist = 0.5, -- assists jump while in air + moveSpeed = 1.5, -- speed of walking + gravity = 0.75, -- force of gravity + slideSpeed = 4, -- speed of sliding + grounded = false, -- is on solid ground + shots = 0, -- how many shots onscreen + maxShots = 3, -- maximum shots onscreen + lemonSpeed = 3, -- speed of megabuster shots + chargeLevel = 0, -- current charged buster level + cycle = { -- used for animation cycles + run = 0, -- used for run sprite + shoot = 0, -- determines duration of shoot sprite + shootHold = 0, -- forces user to release then push shoot + stand = 0, -- used for high-octane eye blinking action + slide = 0, -- used to limit slide length + jump = 0, -- used to prevent auto-bunnyhopping + shootCharge = 0, -- records how charged your megabuster is + ouch = 0, -- records hitstun + iddqd = 0 -- records invincibility frames + }, + chargeDiscolor = { -- swaps colors during buster charging + [0] = {{}}, + [1] = { -- charge level one + { + ["b"] = "a" + }, + { + ["b"] = "b" + } + }, + [2] = { -- woAH charge level two + { + --["f"] = "b", + ["b"] = "3", + ["3"] = "f" + }, + { + --["f"] = "3", + ["3"] = "b", + ["b"] = "f" + }, + { + --["f"] = "3", + ["3"] = "b", + ["b"] = "8" + } + } + }, + control = { -- inputs + up = false, -- move up ladders + down = false, -- move down ladders, or slide + left = false, -- point and walk left + right = false, -- point and walk right + jump = false, -- jump, or slide + shoot = false -- fire your weapon + } + } +end + +local deriveControls = function(keyList) + return { + up = keyList[keys.up], + down = keyList[keys.down], + left = keyList[keys.left], + right = keyList[keys.right], + jump = keyList[keys.x], + shoot = keyList[keys.z] + } +end + +-- main colision function +local isSolid = function(x, y) + x = math.floor(x) + y = math.floor(y) + if (not maps[mapname][1][y]) or (x < 1) then + return false + else + if (maps[mapname][1][y]:sub(x,x) == " " or + maps[mapname][1][y]:sub(x,x) == "") and + (maps[mapname][3][y]:sub(x,x) == " " or + maps[mapname][3][y]:sub(x,x) == "") then + return false + else + return true + end + end +end + +local isPlayerTouchingSolid = function(player, xmod, ymod, ycutoff) + for y = player.y + (ycutoff or 0), player.ysize + player.y - 1 do + for x = player.x, player.xsize + player.x - 1 do + if isSolid(x + (xmod or 0), y + (ymod or 0)) then + return "map" + end + end + end + return false +end + +you = 1 +players[you] = newPlayer("LDD", "megaman", 40, 8) + +local movePlayer = function(player, x, y) + i = player.yvel / math.abs(player.yvel) + for y = 1, math.abs(player.yvel) do + if isPlayerTouchingSolid(player, 0, -i, (player.cycle.slide > 0 and 2 or 0)) then + if player.yvel < 0 then + player.grounded = true + end + player.yvel = 0 + break + else + player.y = player.y - i + player.grounded = false + end + end + i = player.xvel / math.abs(player.xvel) + for x = 1, math.abs(player.xvel) do + if isPlayerTouchingSolid(player, i, 0, (player.cycle.slide > 0 and 2 or 0)) then + if player.grounded and not isPlayerTouchingSolid(player, i, -1) then -- upward slope detection + player.y = player.y - 1 + player.x = player.x + i + grounded = true + else + player.xvel = 0 + break + end + else + player.x = player.x + i + end + end +end + +-- types of projectiles + +local bullet = { + lemon = { + damage = 1, + element = "neutral", + sprites = { + sprites["megaman"]["buster1"] + }, + }, + lemon2 = { + damage = 1, + element = "neutral", + sprites = { + sprites["megaman"]["buster2-1"], + sprites["megaman"]["buster2-2"] + } + }, + lemon3 = { + damage = 4, + element = "neutral", + sprites = { + sprites["megaman"]["buster3-1"], + sprites["megaman"]["buster3-2"], + sprites["megaman"]["buster3-3"], + sprites["megaman"]["buster3-4"], + } + } +} + +local spawnProjectile = function(boolit, owner, x, y, xvel, yvel) + projectiles[#projectiles+1] = { + owner = owner, + bullet = boolit, + x = x, + y = y, + xvel = xvel, + yvel = yvel, + direction = xvel / math.abs(xvel), + life = 32, + cycle = 0, + phaze = false, + } +end + +local moveTick = function() + local i + for num, player in pairs(players) do + + -- falling + + player.yvel = player.yvel - player.gravity + + -- jumping + + if player.control.jump then + if player.grounded then + if player.cycle.jump == 0 then + if player.control.down and player.cycle.slide == 0 then + player.cycle.slide = 6 + elseif not isPlayerTouchingSolid(player, 0, 0, 0) then + player.yvel = player.yvel + player.jumpHeight + player.cycle.slide = 0 + player.grounded = false + end + end + player.cycle.jump = 1 + end + if player.yvel > 0 and not player.grounded then + player.yvel = player.yvel + player.jumpAssist + end + else + player.cycle.jump = 0 + end + + -- walking + + if player.control.right then + player.direction = 1 + player.xvel = player.moveSpeed + elseif player.control.left then + player.direction = -1 + player.xvel = -player.moveSpeed + else + player.xvel = 0 + end + if player.cycle.slide > 0 then + player.xvel = player.direction * player.slideSpeed + end + + -- shooting + + if player.control.shoot then + if player.cycle.shootHold == 0 then + if player.shots < player.maxShots then + spawnProjectile( + bullet.lemon, + player, + player.x + player.xsize * player.direction, + player.y + 2, + player.lemonSpeed * player.direction, + 0 + ) + player.cycle.shoot = 5 + player.shots = player.shots + 1 + end + player.cycle.shootHold = 1 + end + if player.cycle.shootHold == 1 then + player.cycle.shootCharge = player.cycle.shootCharge + 1 + if player.cycle.shootCharge < 16 then + player.chargeLevel = 0 + elseif player.cycle.shootCharge < 32 then + player.chargeLevel = 1 + else + player.chargeLevel = 2 + end + end + else + player.cycle.shootHold = 0 + if player.shots < player.maxShots then + if player.cycle.shootCharge > 16 then + if player.cycle.shootCharge >= 32 then + spawnProjectile( + bullet.lemon3, + player, + player.x + math.max(0, player.direction * player.xsize), + player.y, + player.lemonSpeed * player.direction, + 0 + ) + else + spawnProjectile( + bullet.lemon2, + player, + player.x + math.max(0, player.direction * player.xsize), + player.y + 1, + player.lemonSpeed * player.direction, + 0 + ) + end + player.shots = player.shots + 1 + player.cycle.shoot = 5 + end + end + player.cycle.shootCharge = 0 + player.chargeLevel = 0 + end + + -- movement + + movePlayer(player, xvel, yvel) + + scrollX = player.x - math.floor(scr_x / 2) + math.floor(player.xsize / 2) + scrollY = player.y - math.floor(scr_y / 2) + math.floor(player.ysize / 2) + + -- projectile management + + for i = #projectiles, 1, -1 do + projectiles[i].x = projectiles[i].x + projectiles[i].xvel + projectiles[i].y = projectiles[i].y + projectiles[i].yvel + projectiles[i].cycle = projectiles[i].cycle + 1 + projectiles[i].life = projectiles[i].life - 1 + if projectiles[i].life <= 0 then + table.remove(projectiles, i) + player.shots = player.shots - 1 + end + end + + end +end + +local render = function() + tsv(false) + term.clear() + nfte.drawImage(maps[mapname], -scrollX + 1, -scrollY + 1) + for num,player in pairs(players) do + term.setCursorPos(1,num) + print("(" .. player.x .. ", " .. player.y .. ", " .. tostring(player.shots) .. ")") + if player.direction == -1 then + nfte.drawImageTransparent( + nfte.colorSwap( + nfte.flipX( + sprites[player.spriteset][player.sprite] + ), + player.chargeDiscolor[player.chargeLevel][ + (math.floor(player.cycle.shootCharge / 2) % #player.chargeDiscolor[player.chargeLevel]) + 1 + ] + ), + player.x - scrollX + player.xadj, + player.y - scrollY + player.yadj + ) + else + nfte.drawImageTransparent( + nfte.colorSwap( + sprites[player.spriteset][player.sprite], + player.chargeDiscolor[player.chargeLevel][ + (math.floor(player.cycle.shootCharge / 2) % #player.chargeDiscolor[player.chargeLevel]) + 1 + ] + ), + player.x - scrollX, + player.y - scrollY + ) + end + end + for num,p in pairs(projectiles) do + if p.direction == -1 then + nfte.drawImageTransparent( + nfte.flipX(p.bullet.sprites[(p.cycle % #p.bullet.sprites) + 1]), + p.x - scrollX, + p.y - scrollY + ) + else + nfte.drawImageTransparent( + p.bullet.sprites[(p.cycle % #p.bullet.sprites) + 1], + p.x - scrollX, + p.y - scrollY + ) + end + end + tsv(true) +end + +-- determines what sprite a player uses +local determineSprite = function(player) + local output + player.xadj = 0 + player.yadj = 0 + if player.grounded then + if player.cycle.slide > 0 then + player.cycle.slide = math.max(player.cycle.slide - 1, isPlayerTouchingSolid(player, 0, 0, 0) and 1 or 0) + output = "slide" + else + if player.xvel == 0 then + player.cycle.run = -1 + player.cycle.stand = (player.cycle.stand + 1) % 40 + if player.cycle.shoot > 0 then + player.cycle.shoot = player.cycle.shoot - 1 + output = "shoot" + if player.direction == -1 then + player.xadj = -5 + end + else + output = player.cycle.stand == 39 and "stand2" or "stand1" + end + else + if player.cycle.run == -1 and player.cycle.shoot == 0 then + player.cycle.run = 0 + output = "walk0" + else + player.cycle.run = (player.cycle.run + 0.35) % 4 + if player.cycle.shoot > 0 then + output = "walkshoot" .. (math.floor(player.cycle.run) + 1) + else + output = "walk" .. (math.floor(player.cycle.run) + 1) + end + end + end + end + else + player.cycle.slide = isPlayerTouchingSolid(player, 0, 0, 0) and 1 or 0 + if player.cycle.shoot > 0 then + output = "jumpshoot" + if player.direction == -1 then + player.xadj = -1 + end + else + output = "jump" + end + end + return output +end + +local getInput = function() + local evt + while true do + evt = {os.pullEvent()} + if evt[1] == "key" then + keysDown[evt[2]] = true + elseif evt[1] == "key_up" then + keysDown[evt[2]] = false + end + end +end + +local main = function() + while true do + players[you].control = deriveControls(keysDown) + moveTick() + players[you].sprite = determineSprite(players[you]) + render() + if keysDown[keys.q] then + return + end + sleep(0.05) + end +end + +parallel.waitForAny(getInput, main) + +term.setCursorPos(1, scr_y) +term.clearLine()