ldd-CC/platform-test/game.lua

532 lines
13 KiB
Lua

local game = {}
game.path = fs.combine(fs.getDir(shell.getRunningProgram()),"data")
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 killY = 100
local keysDown = {}
local tsv = function(visible)
if term.current().setVisible then
term.current().setVisible(visible)
end
end
local getAPI = function(apiName, apiPath, apiURL, doDoFile)
apiPath = fs.combine(game.apiPath, apiPath)
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
maxVelocity = 8, -- highest posible speed in any direction
jumpHeight = 2, -- height of jump
jumpAssist = 0.5, -- assists jump while in air
moveSpeed = 2, -- 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, -1, 0) then
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 and player.cycle.slide == 0 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 and player.cycle.slide == 0 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
if player.xvel > 0 then
player.xvel = math.min(player.xvel, player.maxVelocity)
else
player.xvel = math.max(player.xvel, -player.maxVelocity)
end
if player.yvel > 0 then
player.yvel = math.min(player.yvel, player.maxVelocity)
else
player.yvel = math.max(player.yvel, -player.maxVelocity)
end
if player.y > killY then
player.x = 40
player.y = -80
player.xvel = 0
end
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
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
player.cycle.shoot = math.max(player.cycle.shoot - 1, 0)
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()