mirror of
https://github.com/LDDestroier/CC/
synced 2024-10-31 23:26:19 +00:00
532 lines
13 KiB
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()
|