2019-03-09 21:09:52 +00:00
local scr_x , scr_y = term.getSize ( )
local keysDown , miceDown = { } , { }
2019-03-13 04:14:24 +00:00
local config = { mainDir = " ccbn-data " }
config.chipDir = fs.combine ( config.mainDir , " chipdata " )
config.objectDir = fs.combine ( config.mainDir , " objectdata " )
2019-03-09 21:09:52 +00:00
local players = { }
2019-03-11 09:02:27 +00:00
local objects = { }
2019-03-09 21:09:52 +00:00
local projectiles = { }
local you = 1
2019-03-11 04:10:00 +00:00
local yourID = os.getComputerID ( )
2019-03-11 05:33:25 +00:00
-- recommended at 0.1 for netplay, which you'll be doing all the time so yeah
2019-03-12 21:02:26 +00:00
local gameDelayInit = 0.05
2019-03-11 05:33:25 +00:00
local gameID = math.random ( 0 , 2 ^ 30 )
2019-03-11 04:10:00 +00:00
local waitingForGame = false
local isHost = true
local channel = 1024
2019-03-09 21:09:52 +00:00
2019-03-13 04:14:24 +00:00
local chips , objectTypes = { } , { }
local interpretArgs = function ( tInput , tArgs )
2019-03-11 05:33:25 +00:00
local output = { }
local errors = { }
local usedEntries = { }
for aName , aType in pairs ( tArgs ) do
output [ aName ] = false
for i = 1 , # tInput do
if not usedEntries [ i ] then
if tInput [ i ] == aName and not output [ aName ] then
if aType then
usedEntries [ i ] = true
if type ( tInput [ i + 1 ] ) == aType or type ( tonumber ( tInput [ i + 1 ] ) ) == aType then
usedEntries [ i + 1 ] = true
if aType == " number " then
output [ aName ] = tonumber ( tInput [ i + 1 ] )
else
output [ aName ] = tInput [ i + 1 ]
end
else
output [ aName ] = nil
errors [ 1 ] = errors [ 1 ] and ( errors [ 1 ] + 1 ) or 1
errors [ aName ] = " expected " .. aType .. " , got " .. type ( tInput [ i + 1 ] )
end
else
usedEntries [ i ] = true
output [ aName ] = true
end
end
end
end
end
for i = 1 , # tInput do
if not usedEntries [ i ] then
output [ # output + 1 ] = tInput [ i ]
end
end
return output , errors
end
local argList = interpretArgs ( { ... } , {
[ " skynet " ] = false , -- use Skynet HTTP multiplayer
[ " debug " ] = false , -- show various variable values
} )
2019-03-09 21:09:52 +00:00
local FRAME = 0
2019-03-11 05:33:25 +00:00
local useSkynet = argList.skynet
local showDebug = argList.debug
2019-03-09 21:09:52 +00:00
local stage = {
panels = { } ,
2019-03-10 20:12:49 +00:00
damage = { } ,
2019-03-09 21:09:52 +00:00
panelWidth = 6 ,
panelHeight = 2 ,
scrollX = 0 ,
scrollY = 6
}
2019-03-13 04:14:24 +00:00
local stageChanged = true
2019-03-11 01:15:22 +00:00
local round = function ( num )
return math.floor ( 0.5 + num )
end
2019-03-09 21:09:52 +00:00
-- ripped from NFTE
2019-03-11 01:15:22 +00:00
local deepCopy
deepCopy = function ( tbl )
local output = { }
for k , v in pairs ( tbl ) do
if type ( v ) == " table " then
output [ k ] = deepCopy ( v )
else
output [ k ] = v
end
end
return output
end
2019-03-09 22:24:33 +00:00
local getSize = function ( image )
local x , y = 0 , # image [ 1 ]
for y = 1 , # image [ 1 ] do
x = math.max ( x , # image [ 1 ] [ y ] )
end
return x , y
end
local colorSwap = function ( image , text , back )
2019-03-09 22:20:14 +00:00
local output = { { } , { } , { } }
for y = 1 , # image [ 1 ] do
output [ 1 ] [ y ] = image [ 1 ] [ y ]
output [ 2 ] [ y ] = image [ 2 ] [ y ] : gsub ( " . " , text )
output [ 3 ] [ y ] = image [ 3 ] [ y ] : gsub ( " . " , back or text )
end
return output
end
2019-03-09 21:09:52 +00:00
local makeRectangle = function ( width , height , char , text , back )
local output = { { } , { } , { } }
for y = 1 , height do
output [ 1 ] [ y ] = ( char or " " ) : rep ( width )
output [ 2 ] [ y ] = ( text or " " ) : rep ( width )
output [ 3 ] [ y ] = ( back or " " ) : rep ( width )
end
return output
end
local stretchImage = function ( _image , sx , sy , noRepeat )
local output = { { } , { } , { } }
local image = deepCopy ( _image )
sx , sy = math.abs ( sx ) , math.abs ( sy )
local imageX , imageY = getSize ( image )
local tx , ty
if sx == 0 or sy == 0 then
for y = 1 , math.max ( sy , 1 ) do
output [ 1 ] [ y ] = " "
output [ 2 ] [ y ] = " "
output [ 3 ] [ y ] = " "
end
return output
else
for y = 1 , sy do
for x = 1 , sx do
tx = round ( ( x / sx ) * imageX )
ty = math.ceil ( ( y / sy ) * imageY )
if not noRepeat then
output [ 1 ] [ y ] = ( output [ 1 ] [ y ] or " " ) .. image [ 1 ] [ ty ] : sub ( tx , tx )
else
output [ 1 ] [ y ] = ( output [ 1 ] [ y ] or " " ) .. " "
end
output [ 2 ] [ y ] = ( output [ 2 ] [ y ] or " " ) .. image [ 2 ] [ ty ] : sub ( tx , tx )
output [ 3 ] [ y ] = ( output [ 3 ] [ y ] or " " ) .. image [ 3 ] [ ty ] : sub ( tx , tx )
end
end
if noRepeat then
for y = 1 , imageY do
for x = 1 , imageX do
if image [ 1 ] [ y ] : sub ( x , x ) ~= " " then
tx = round ( ( ( x / imageX ) * sx ) - ( ( 0.5 / imageX ) * sx ) )
ty = round ( ( ( y / imageY ) * sy ) - ( ( 0.5 / imageY ) * sx ) )
output [ 1 ] [ ty ] = stringWrite ( output [ 1 ] [ ty ] , tx , image [ 1 ] [ y ] : sub ( x , x ) )
end
end
end
end
return output
end
end
local merge = function ( ... )
local images = { ... }
local output = { { } , { } , { } }
local imageX , imageY = 0 , 0
local imSX , imSY
for i = 1 , # images do
imageY = math.max (
imageY ,
# images [ i ] [ 1 ] [ 1 ] + ( images [ i ] [ 3 ] == true and 0 or ( images [ i ] [ 3 ] - 1 ) )
)
for y = 1 , # images [ i ] [ 1 ] [ 1 ] do
imageX = math.max (
imageX ,
# images [ i ] [ 1 ] [ 1 ] [ y ] + ( images [ i ] [ 2 ] == true and 0 or ( images [ i ] [ 2 ] - 1 ) )
)
end
end
-- if either coordinate is true, center it
for i = 1 , # images do
imSX , imSY = getSize ( images [ i ] [ 1 ] )
if images [ i ] [ 2 ] == true then
images [ i ] [ 2 ] = round ( 1 + ( imageX / 2 ) - ( imSX / 2 ) )
end
if images [ i ] [ 3 ] == true then
images [ i ] [ 3 ] = round ( 1 + ( imageY / 2 ) - ( imSY / 2 ) )
end
end
-- will later add code to adjust X/Y positions if negative values are given
local image , xadj , yadj
local tx , ty
for y = 1 , imageY do
output [ 1 ] [ y ] = { }
output [ 2 ] [ y ] = { }
output [ 3 ] [ y ] = { }
for x = 1 , imageX do
for i = # images , 1 , - 1 do
image , xadj , yadj = images [ i ] [ 1 ] , images [ i ] [ 2 ] , images [ i ] [ 3 ]
tx , ty = x - ( xadj - 1 ) , y - ( yadj - 1 )
output [ 1 ] [ y ] [ x ] = output [ 1 ] [ y ] [ x ] or " "
output [ 2 ] [ y ] [ x ] = output [ 2 ] [ y ] [ x ] or " "
output [ 3 ] [ y ] [ x ] = output [ 3 ] [ y ] [ x ] or " "
if image [ 1 ] [ ty ] then
if ( image [ 1 ] [ ty ] : sub ( tx , tx ) ~= " " ) and ( tx >= 1 ) then
output [ 1 ] [ y ] [ x ] = ( image [ 1 ] [ ty ] : sub ( tx , tx ) == " " and output [ 1 ] [ y ] [ x ] or image [ 1 ] [ ty ] : sub ( tx , tx ) )
output [ 2 ] [ y ] [ x ] = ( image [ 2 ] [ ty ] : sub ( tx , tx ) == " " and output [ 2 ] [ y ] [ x ] or image [ 2 ] [ ty ] : sub ( tx , tx ) )
output [ 3 ] [ y ] [ x ] = ( image [ 3 ] [ ty ] : sub ( tx , tx ) == " " and output [ 3 ] [ y ] [ x ] or image [ 3 ] [ ty ] : sub ( tx , tx ) )
end
end
end
end
output [ 1 ] [ y ] = table.concat ( output [ 1 ] [ y ] )
output [ 2 ] [ y ] = table.concat ( output [ 2 ] [ y ] )
output [ 3 ] [ y ] = table.concat ( output [ 3 ] [ y ] )
end
return output
end
local pixelateImage = function ( image , amntX , amntY )
local imageX , imageY = getSize ( image )
return stretchImage ( stretchImage ( image , imageX / math.max ( amntX , 1 ) , imageY / math.max ( amntY , 1 ) ) , imageX , imageY )
end
local drawImage = function ( image , x , y , terminal )
terminal = terminal or term.current ( )
local cx , cy = terminal.getCursorPos ( )
for iy = 1 , # image [ 1 ] do
terminal.setCursorPos ( x , y + ( iy - 1 ) )
terminal.blit ( image [ 1 ] [ iy ] , image [ 2 ] [ iy ] , image [ 3 ] [ iy ] )
end
terminal.setCursorPos ( cx , cy )
end
2019-03-11 05:33:25 +00:00
local skynet
local skynetPath = " skynet "
local skynetURL = " https://raw.githubusercontent.com/osmarks/skynet/master/client.lua "
2019-03-11 04:10:00 +00:00
local modem
local getModem = function ( )
2019-03-11 05:33:25 +00:00
if useSkynet then
if skynet then
local isOpen = false
for i = 1 , # skynet.open_channels do
if skynet.open_channels == channel then
isOpen = true
end
end
if not isOpen then
skynet.open ( channel )
end
return skynet
2019-03-11 04:10:00 +00:00
else
2019-03-11 05:33:25 +00:00
if fs.exists ( skynetPath ) then
skynet = dofile ( skynetPath )
skynet.open ( channel )
else
local prog = http.get ( skynetURL )
if prog then
local file = fs.open ( skynetPath , " w " )
file.write ( prog.readAll ( ) )
file.close ( )
skynet = dofile ( skynetPath )
skynet.open ( channel )
else
error ( " Skynet can't be downloaded! Use modems instead. " )
end
end
2019-03-11 04:10:00 +00:00
end
else
2019-03-11 05:33:25 +00:00
local modems = { peripheral.find ( " modem " ) }
if # modems == 0 then
if ccemux then
ccemux.attach ( " top " , " wireless_modem " )
modem = peripheral.wrap ( " top " )
else
error ( " A modem is needed. " )
end
else
modem = modems [ 1 ]
end
modem.open ( channel )
return modem
2019-03-11 04:10:00 +00:00
end
end
local transmit = function ( msg )
if useSkynet then
2019-03-11 05:33:25 +00:00
skynet.send ( channel , msg )
2019-03-11 04:10:00 +00:00
else
modem.transmit ( channel , channel , msg )
end
end
local receive = function ( )
if useSkynet then
2019-03-11 05:33:25 +00:00
return ( { skynet.receive ( channel ) } ) [ 2 ]
2019-03-11 04:10:00 +00:00
else
2019-03-11 05:33:25 +00:00
return ( { os.pullEvent ( " modem_message " ) } ) [ 5 ]
2019-03-11 04:10:00 +00:00
end
end
2019-03-09 21:09:52 +00:00
local images = {
2019-03-13 04:14:24 +00:00
win = { { " " , "
" , " " , " " , " " } , { " dd ddfdddfddd dd dd dddddddddf ddfdf " , " ffdfdddd ddddd dd dd fd dd dd ddfd ddfdd " , " dddd dd dddd dd dd ddd dd dd ddddfddddd " , " dd df fdddd dd fdfdfdfd dd dd dfdd dd " , " dd dddddddddddd ddd ddd dddddddd dd dd " } , { " df dfdddddfdf df df dfdddddfdd dfddd " , " ddfddfdf fdfdf df df dd df df dddf dfddd " , " fdff df dfdf df df ddf df df dffdddffdf " , " df dd ddfdf df dddddddd df df fddf ff " , " df fdddfffdddff ddf ddf dddddfdf df df " } } ,
lose = { { " " , " " , "
" , " " , " " } , { " eeeff eeeeeeee eeeeeeeeeeeeeeeeeeeeeff " , " ee eeeee ee ee ee ee ee eee " , " ee eeeeeee ee eeeee ee eeeee ee ee " , " ee feeee ee ee ee ee ee fee " , " eeeee eeeeeeeeeeeeeeeeee ee eeeeeeeeeee " } , { " eeeee eeeeefef eeeeefeeeeefeeeeefeeeee " , " ef fefef ef ef ef ef ef fef " , " ef efeffff ef effff ef effff ef ef " , " ef eefef ef ef ef ef ef eef " , " eeeff eeeeefeeeeefeeeeef ef eeeeefeeeff " } } ,
2019-03-09 21:09:52 +00:00
panel = {
normal = { { " " , " " , " " } , { " eeeee7 " , " e78877 " , " eeeeee " } , { " 77777e " , " 78888e " , " eeeeee " } } ,
2019-03-13 04:14:24 +00:00
cracked = { { " " , " " , " " } , { " eeeee7 " , " e88888 " , " eeeeee " } , { " 77777e " , " 87777e " , " eeeeee " } } ,
broken = { { " " , " " , " " } , { " eeeeef " , " eff8f7 " , " eeeeee " } , { " e " , " 788f8e " , " eeeeee " } }
2019-03-09 21:09:52 +00:00
} ,
2019-03-11 01:15:22 +00:00
player = {
[ " 6 " ] = { { " " , " " , " " , " " , " " } , { " f5ff " , " 4 4 " , " 66ff " , " 2 2 " , " affa " } , { " 5f55 " , " 4 f " , " 6f66 " , " 2 2 " , " aaf " } } ,
[ " 7 " ] = { { " " , " " , "
" , " " , " " } , { " 5555 " , " f4 " , " ffff " , " 2f22 " , " fa " } , { " 5ff5 " , " 4f " , " 6666 " , " 2ff " , " af " } } ,
} ,
2019-03-11 09:02:27 +00:00
rockcube = { { " " , " " , " " } , { " 7887 " , " 8777 " , " 7777 " } , { " 8778 " , " 7778 " , " 8888 " } } ,
2019-03-09 21:09:52 +00:00
cannon = { { " " , " " } , { " ff " , " 77 " } , { " 77 " , " " , } } ,
2019-03-11 01:15:22 +00:00
buster = { { " " } , { " f4 " } , { " 4f " } }
2019-03-09 21:09:52 +00:00
}
2019-03-13 04:14:24 +00:00
local act = { stage = { } , player = { } , projectile = { } , object = { } }
2019-03-09 21:09:52 +00:00
act.stage . newPanel = function ( x , y , panelType , owner )
stage.panels [ y ] = stage.panels [ y ] or { }
stage.panels [ y ] [ x ] = {
panelType = panelType ,
reserved = false ,
crackedLevel = 0 , -- 0 is okay, 1 is cracked, 2 is broken
owner = owner or ( x > 3 and 2 or 1 ) ,
2019-03-13 04:14:24 +00:00
originalOwner = owner or ( x > 3 and 2 or 1 ) ,
2019-03-09 21:09:52 +00:00
cooldown = {
owner = 0 ,
broken = 0 ,
}
}
end
2019-03-13 04:14:24 +00:00
act.player . checkPlayerAtPos = function ( x , y , ignoreThisOne )
x , y = round ( x ) , round ( y )
for id , player in pairs ( players ) do
if id ~= ignoreThisOne then
if player.x == x and player.y == y then
return id
end
end
end
end
2019-03-09 21:09:52 +00:00
act.stage . checkExist = function ( x , y )
if stage.panels [ y ] then
if stage.panels [ y ] [ x ] then
return true
end
end
return false
end
2019-03-13 04:14:24 +00:00
act.stage . crackPanel = function ( x , y , amount )
local maxCrack
if act.stage . checkExist ( x , y ) then
if act.player . checkPlayerAtPos ( x , y ) then
maxCrack = 1
else
maxCrack = 2
end
if math.max ( 0 , math.min ( maxCrack , stage.panels [ y ] [ x ] . crackedLevel + amount ) ) ~= stage.panels [ y ] [ x ] . crackedLevel then
stage.panels [ y ] [ x ] . crackedLevel = math.max ( 0 , math.min ( maxCrack , stage.panels [ y ] [ x ] . crackedLevel + amount ) )
if stage.panels [ y ] [ x ] . crackedLevel == 2 then
stage.panels [ y ] [ x ] . cooldown.broken = 300
else
stage.panels [ y ] [ x ] . cooldown.broken = 0
end
stageChanged = true
end
end
end
2019-03-12 06:12:34 +00:00
act.stage . setDamage = function ( x , y , damage , owner , time , noFlinch , safePlayers , safeObjects )
2019-03-11 01:15:22 +00:00
x , y = round ( x ) , round ( y )
2019-03-10 20:12:49 +00:00
stage.damage [ y ] = stage.damage [ y ] or { }
stage.damage [ y ] [ x ] = stage.damage [ y ] [ x ] or { }
stage.damage [ y ] [ x ] [ owner ] = {
owner = owner ,
time = time ,
damage = damage ,
2019-03-12 06:12:34 +00:00
flinching = not noFlinch ,
safePlayers = safePlayers or { } ,
safeObjects = safeObjects or { }
2019-03-10 20:12:49 +00:00
}
2019-03-13 04:14:24 +00:00
stageChanged = true
2019-03-09 21:09:52 +00:00
end
2019-03-12 06:12:34 +00:00
act.stage . getDamage = function ( x , y , pID , oID , pIDsafeCheck , oIDsafeCheck )
2019-03-10 20:12:49 +00:00
local totalDamage = 0
2019-03-11 01:15:22 +00:00
local flinching = false
x , y = round ( x ) , round ( y )
2019-03-10 20:12:49 +00:00
if stage.damage [ y ] then
if stage.damage [ y ] [ x ] then
2019-03-11 01:15:22 +00:00
for k , v in pairs ( stage.damage [ y ] [ x ] ) do
2019-03-12 06:12:34 +00:00
if k ~= ( players [ pID ] or { } ) . owner and k ~= ( objects [ oID ] or { } ) . owner and v.damage then
if not ( v.safePlayers [ pIDsafeCheck ] or v.safeObjects [ oIDsafeCheck ] ) then
totalDamage = totalDamage + v.damage
flinching = flinching or v.flinching
end
2019-03-10 20:12:49 +00:00
end
2019-03-09 21:09:52 +00:00
end
end
end
2019-03-11 01:15:22 +00:00
return totalDamage , flinching
2019-03-09 21:09:52 +00:00
end
2019-03-11 04:10:00 +00:00
act.player . newPlayer = function ( x , y , owner , direction , image )
2019-03-13 04:14:24 +00:00
local pID = # players + 1
players [ pID ] = {
x = x , -- X and Y positions are relative to grid, not screen
y = y , -- ditto my man
owner = owner , -- Either 1 or 2, indicates the red/blue alignment
type = " player " , -- Used for quickly identifying a player/object/projectile at a glance
direction = direction or 1 , -- Either -1 or 1, indicates facing left or right
health = 1000 , -- Once it hits 0, your player is deleted
maxHealth = 1000 , -- You cannot regen past this value
image = image , -- Because of CC limitations, I'm just going to have one player sprite
canMove = true , -- If false, pushing the move buttons won't do diddly fuck
canShoot = true , -- If false, pushing the shoot buttons won't do fuckly did
busterPower = 2 , -- Strength of MegaBuster
cooldown = { -- All cooldown values are decremented every tick
move = 0 , -- If above 0, you cannot move
shoot = 0 , -- If above 0, you cannot shoot
iframe = 0 -- If above 0, you will flash and be indestructible
2019-03-09 21:09:52 +00:00
} ,
control = {
moveUp = false ,
moveDown = false ,
moveLeft = false ,
moveRight = false ,
buster = false ,
chip = false ,
custom = false
2019-03-11 01:15:22 +00:00
} ,
2019-03-13 04:14:24 +00:00
chipQueue = { } -- Attacks are used in a queue, which is filled each turn
2019-03-09 21:09:52 +00:00
}
2019-03-13 04:14:24 +00:00
for k , v in pairs ( chips ) do
players [ pID ] . chipQueue [ # players [ pID ] . chipQueue + 1 ] = k
end
return pID
2019-03-09 21:09:52 +00:00
end
2019-03-13 04:14:24 +00:00
act.object . newObject = function ( x , y , owner , direction , objectType )
local oID = # objects + 1
objects [ oID ] = {
2019-03-11 09:02:27 +00:00
x = x ,
y = y ,
2019-03-12 04:58:47 +00:00
image = objectTypes [ objectType ] . image ,
friendlyFire = objectTypes [ objectType ] . friendlyFire or true ,
health = objectTypes [ objectType ] . health or 500 ,
maxHealth = objectTypes [ objectType ] . maxHealth or 500 ,
smackDamage = objectTypes [ objectType ] . smackDamage or 100 ,
doYeet = objectTypes [ objectType ] . doYeet or false ,
2019-03-13 04:14:24 +00:00
delayedTime = objectTypes [ objectType ] . delayedTime or math.huge ,
delayedFunc = objectTypes [ objectType ] . delayedFunc or function ( ) end ,
2019-03-12 04:58:47 +00:00
xvel = 0 ,
yvel = 0 ,
2019-03-11 09:02:27 +00:00
owner = owner ,
direction = direction ,
type = " object " ,
objectType = objectType ,
frame = 0 ,
cooldown = {
iframe = 0 ,
}
}
2019-03-13 04:14:24 +00:00
return oID
2019-03-11 09:02:27 +00:00
end
2019-03-13 04:14:24 +00:00
act.object . checkObjectAtPos = function ( x , y , ignoreThisOne )
2019-03-11 09:02:27 +00:00
x , y = round ( x ) , round ( y )
for id , obj in pairs ( objects ) do
2019-03-12 04:58:47 +00:00
if id ~= ignoreThisOne then
2019-03-11 09:02:27 +00:00
if obj.x == x and obj.y == y then
return id
end
end
end
return false
end
2019-03-11 06:37:04 +00:00
local control = {
moveUp = keys.up ,
moveDown = keys.down ,
moveLeft = keys.left ,
moveRight = keys.right ,
buster = keys.z ,
chip = keys.x ,
custom = keys.c
}
local getControls = function ( )
if players [ you ] then
for k , v in pairs ( control ) do
players [ you ] . control [ k ] = keysDown [ v ] or false
end
end
end
2019-03-13 04:14:24 +00:00
act.stage . checkIfSolid = function ( x , y )
2019-03-11 06:37:04 +00:00
x , y = round ( x ) , round ( y )
if stage.panels [ y ] then
if stage.panels [ y ] [ x ] then
if stage.panels [ y ] [ x ] . crackedLevel < 2 then
return true
end
end
end
return false
end
2019-03-13 04:14:24 +00:00
act.stage . checkIfWalkable = function ( x , y , pID , oID )
2019-03-12 06:12:34 +00:00
if x >= 1 and x <= 6 then
x , y = round ( x ) , round ( y )
2019-03-13 04:14:24 +00:00
if act.stage . checkIfSolid ( x , y ) then
if not act.object . checkObjectAtPos ( x , y , oID ) then
if not act.player . checkPlayerAtPos ( x , y , pID ) and ( not pID or stage.panels [ y ] [ x ] . owner == players [ pID ] . owner ) then
2019-03-12 06:12:34 +00:00
return true
end
2019-03-11 06:37:04 +00:00
end
end
end
return false
end
2019-03-13 04:14:24 +00:00
act.player . movePlayer = function ( pID , xmove , ymove , doCooldown )
2019-03-11 06:37:04 +00:00
local player = players [ pID ]
2019-03-13 04:14:24 +00:00
if ( xmove ~= 0 or ymove ~= 0 ) and act.stage . checkIfWalkable ( player.x + xmove , player.y + ymove , pID ) then
2019-03-11 06:37:04 +00:00
player.x = player.x + xmove
player.y = player.y + ymove
if doCooldown then
2019-03-12 21:02:26 +00:00
if gameDelayInit < 0.1 then
player.cooldown . move = 3
else
player.cooldown . move = 2
end
2019-03-11 06:37:04 +00:00
end
2019-03-13 04:14:24 +00:00
if stage.panels [ player.y - ymove ] [ player.x - xmove ] . crackedLevel == 1 then
act.stage . crackPanel ( player.x - xmove , player.y - ymove , 1 )
end
2019-03-11 06:37:04 +00:00
return true
else
return false
end
end
2019-03-13 04:14:24 +00:00
act.object . moveObject = function ( oID , xmove , ymove )
2019-03-11 09:02:27 +00:00
local object = objects [ oID ]
2019-03-13 04:14:24 +00:00
if ( xmove ~= 0 or ymove ~= 0 ) and act.stage . checkIfWalkable ( object.x + xmove , object.y + ymove , nil , oID ) then
2019-03-11 09:02:27 +00:00
object.x = object.x + xmove
object.y = object.y + ymove
return true
else
return false
end
end
2019-03-11 06:37:04 +00:00
local movePlayers = function ( )
local xmove , ymove , p
for i = 1 , # players do
xmove , ymove = 0 , 0
p = players [ i ]
2019-03-13 04:14:24 +00:00
if p.canMove then
if p.cooldown . move == 0 then
if p.control . moveUp then
ymove = - 1
elseif p.control . moveDown then
ymove = 1
elseif p.control . moveRight then
xmove = 1
elseif p.control . moveLeft then
xmove = - 1
end
act.player . movePlayer ( i , xmove , ymove , true )
end
if stage.panels [ p.y ] then
if stage.panels [ p.y ] [ p.x ] then
if stage.panels [ p.y ] [ p.x ] . owner ~= p.owner then
repeat
if p.owner == 1 then
p.x = p.x - 1
else
p.x = p.x + 1
end
until stage.panels [ p.y ] [ p.x ] . owner == p.owner
end
end
2019-03-11 06:37:04 +00:00
end
end
end
end
local reduceCooldowns = function ( )
2019-03-11 09:02:27 +00:00
for id , player in pairs ( players ) do
for k , v in pairs ( player.cooldown ) do
2019-03-13 04:14:24 +00:00
2019-03-11 09:02:27 +00:00
players [ id ] . cooldown [ k ] = math.max ( 0 , v - 1 )
2019-03-13 04:14:24 +00:00
2019-03-11 09:02:27 +00:00
end
end
for id , object in pairs ( objects ) do
for k , v in pairs ( object.cooldown ) do
2019-03-13 04:14:24 +00:00
2019-03-11 09:02:27 +00:00
objects [ id ] . cooldown [ k ] = math.max ( 0 , v - 1 )
2019-03-13 04:14:24 +00:00
2019-03-11 06:37:04 +00:00
end
end
for y , row in pairs ( stage.damage ) do
for x , panel in pairs ( row ) do
for owner , damageData in pairs ( panel ) do
2019-03-13 04:14:24 +00:00
2019-03-11 06:37:04 +00:00
stage.damage [ y ] [ x ] [ owner ] . time = math.max ( 0 , damageData.time - 1 )
if damageData.time == 0 then
stage.damage [ y ] [ x ] [ owner ] = nil
end
2019-03-13 04:14:24 +00:00
end
end
end
for y , row in pairs ( stage.panels ) do
for x , panel in pairs ( row ) do
for k , v in pairs ( panel.cooldown ) do
stage.panels [ y ] [ x ] . cooldown [ k ] = math.max ( 0 , v - 1 )
if k == " owner " then
if stage.panels [ y ] [ x ] . owner == stage.panels [ y ] [ x ] . originalOwner then
stage.panels [ y ] [ x ] . cooldown.owner = 0
elseif v == 0 then
stageChanged = true
stage.panels [ y ] [ x ] . owner = stage.panels [ y ] [ x ] . originalOwner
end
elseif k == " broken " and v == 0 and panel.crackedLevel == 2 then
stageChanged = true
stage.panels [ y ] [ x ] . crackedLevel = 0
end
2019-03-11 06:37:04 +00:00
end
end
end
end
2019-03-13 04:14:24 +00:00
act.projectile . checkProjectileCollisions = function ( info )
2019-03-11 09:02:27 +00:00
local struckPlayer = false
local struckObject = false
2019-03-13 04:14:24 +00:00
local cPlayer = act.player . checkPlayerAtPos ( info.x , info.y ) --, info.owner)
local cObject = act.object . checkObjectAtPos ( info.x , info.y ) --, info.owner)
2019-03-11 09:02:27 +00:00
if cPlayer then
if players [ cPlayer ] . cooldown.iframe == 0 and players [ cPlayer ] . owner ~= info.owner then
struckPlayer = cPlayer
end
end
if cObject then
if objects [ cObject ] . cooldown.iframe == 0 then
struckObject = cObject
end
end
return struckPlayer , struckObject
end
2019-03-13 04:14:24 +00:00
local readFile = function ( path )
if fs.exists ( path ) then
local file = fs.open ( path , " r " )
local contents = file.readAll ( )
file.close ( )
return contents
end
end
2019-03-11 01:15:22 +00:00
2019-03-13 04:14:24 +00:00
act.projectile . newProjectile = function ( x , y , player , chipType , noFlinch , altDamage )
2019-03-11 01:15:22 +00:00
local id = # projectiles + 1
projectiles [ id ] = {
2019-03-09 21:09:52 +00:00
x = x ,
y = y ,
2019-03-12 06:12:34 +00:00
safeObjects = { } ,
safePlayers = { } ,
2019-03-11 01:15:22 +00:00
type = " projectile " ,
initX = x ,
initY = y ,
id = id ,
owner = player.owner ,
player = player ,
2019-03-11 04:10:00 +00:00
direction = player.direction ,
2019-03-11 01:15:22 +00:00
frame = 0 ,
2019-03-13 04:14:24 +00:00
noFlinch = noFlinch , -- overwrite a projectile's flinchingness
altDamage = altDamage , -- overwrite a projectile's damage
2019-03-11 01:15:22 +00:00
chipType = chipType
2019-03-09 21:09:52 +00:00
}
2019-03-13 04:14:24 +00:00
return id
2019-03-09 21:09:52 +00:00
end
for y = 1 , 3 do
for x = 1 , 6 do
act.stage . newPanel ( x , y , " normal " )
end
end
2019-03-11 01:15:22 +00:00
2019-03-13 04:14:24 +00:00
-- loads all chips and objects from file
local loadChips = function ( env )
local cList = fs.list ( config.chipDir )
local oList = fs.list ( config.objectDir )
local contents
local cOutput , oOutput = { } , { }
for i = 1 , # cList do
if not fs.isDir ( fs.combine ( config.chipDir , cList [ i ] ) ) then
cOutput [ cList [ i ] ] = loadfile ( fs.combine ( config.chipDir , cList [ i ] ) ) (
stage ,
players ,
objects ,
projectiles ,
act ,
images
)
end
end
for i = 1 , # oList do
if not fs.isDir ( fs.combine ( config.objectDir , oList [ i ] ) ) then
oOutput [ oList [ i ] ] = loadfile ( fs.combine ( config.objectDir , oList [ i ] ) ) (
stage ,
players ,
objects ,
projectiles ,
act ,
images
)
end
end
return cOutput , oOutput
end
chips , objectTypes = loadChips ( getfenv ( ) )
2019-03-11 04:10:00 +00:00
act.player . newPlayer ( 2 , 2 , 1 , 1 , " 6 " )
act.player . newPlayer ( 5 , 2 , 2 , - 1 , " 7 " )
2019-03-09 21:09:52 +00:00
2019-03-12 21:02:26 +00:00
local stageImageStitch
local makeStageImageStitch = function ( )
2019-03-13 04:14:24 +00:00
local buffer , im = { }
2019-03-12 21:02:26 +00:00
for y = # stage.panels , 1 , - 1 do
2019-03-13 04:14:24 +00:00
if stage.panels [ y ] then
for x = 1 , # stage.panels [ y ] do
if stage.panels [ y ] [ x ] then
if stage.panels [ y ] [ x ] . crackedLevel == 0 then
im = images.panel [ stage.panels [ y ] [ x ] . panelType ]
elseif stage.panels [ y ] [ x ] . crackedLevel == 1 then
im = images.panel . cracked
elseif stage.panels [ y ] [ x ] . crackedLevel == 2 then
im = images.panel . broken
end
if stage.panels [ y ] [ x ] . owner == 2 then
im = colorSwap ( im , { e = " b " } )
end
if act.stage . getDamage ( x , y ) > 0 then
im = colorSwap ( im , { [ " 7 " ] = " 4 " , [ " 8 " ] = " 4 " } )
end
buffer [ # buffer + 1 ] = {
im ,
( x - 1 ) * stage.panelWidth + 2 ,
( y - 1 ) * stage.panelHeight + 2
}
end
2019-03-12 21:02:26 +00:00
end
end
end
return merge ( table.unpack ( buffer ) )
end
2019-03-09 21:09:52 +00:00
local render = function ( )
local buffer , im = { }
local sx , sy
2019-03-13 04:14:24 +00:00
if stageChanged or true then
2019-03-12 21:02:26 +00:00
stageImageStitch = makeStageImageStitch ( )
2019-03-13 04:14:24 +00:00
stageChanged = false
2019-03-12 21:02:26 +00:00
end
2019-03-11 01:15:22 +00:00
local sortedList = { }
2019-03-11 04:10:00 +00:00
for k , v in pairs ( projectiles ) do
sortedList [ # sortedList + 1 ] = v
end
for k , v in pairs ( players ) do
sortedList [ # sortedList + 1 ] = v
end
2019-03-11 09:02:27 +00:00
for k , v in pairs ( objects ) do
sortedList [ # sortedList + 1 ] = v
end
2019-03-11 04:10:00 +00:00
table.sort ( sortedList , function ( a , b ) return a.y >= b.y end )
for k , v in pairs ( sortedList ) do
if v.type == " player " then
if v.cooldown . iframe == 0 or ( FRAME % 2 == 0 ) then
sx = ( v.x - 1 ) * stage.panelWidth + 3 + stage.scrollX
sy = ( v.y - 1 ) * stage.panelHeight - 1 + stage.scrollY
buffer [ # buffer + 1 ] = {
colorSwap ( images.player [ v.image ] , { [ " f " ] = " " } ) ,
sx ,
sy
}
end
elseif v.type == " projectile " then
2019-03-11 01:15:22 +00:00
sx = math.floor ( ( v.x - 1 ) * stage.panelWidth + 4 + stage.scrollX )
sy = math.floor ( ( v.y - 1 ) * stage.panelHeight + 1 + stage.scrollY )
if sx >= - 1 and sx <= scr_x and v.imageData then
for kk , imd in pairs ( v.imageData ) do
buffer [ # buffer + 1 ] = {
colorSwap ( imd [ 1 ] , { [ " f " ] = " " } ) ,
math.floor ( ( imd [ 2 ] - 1 ) * stage.panelWidth + 4 + stage.scrollX ) ,
math.floor ( ( imd [ 3 ] - 1 ) * stage.panelHeight + 1 + stage.scrollY )
}
end
end
2019-03-11 09:02:27 +00:00
elseif v.type == " object " then
sx = ( v.x - 1 ) * stage.panelWidth + 3 + stage.scrollX
sy = ( v.y - 1 ) * stage.panelHeight - 1 + stage.scrollY
buffer [ # buffer + 1 ] = {
colorSwap ( v.image , { [ " f " ] = " " } ) ,
math.floor ( ( v.x - 1 ) * stage.panelWidth + 3 + stage.scrollX ) ,
math.floor ( ( v.y - 1 ) * stage.panelHeight + 1 + stage.scrollY )
}
2019-03-09 21:09:52 +00:00
end
end
2019-03-12 21:02:26 +00:00
buffer [ # buffer + 1 ] = {
stageImageStitch ,
stage.scrollX + 1 ,
stage.scrollY + 1
}
2019-03-09 21:09:52 +00:00
buffer [ # buffer + 1 ] = { makeRectangle ( scr_x , scr_y , " f " , " f " , " f " ) , 1 , 1 }
drawImage ( colorSwap ( merge ( table.unpack ( buffer ) ) , { [ " " ] = " f " } ) , 1 , 1 )
2019-03-11 01:15:22 +00:00
2019-03-11 06:37:04 +00:00
if players [ you ] then
if chips [ players [ you ] . chipQueue [ 1 ] ] then
term.setCursorPos ( 1 , scr_y )
term.write ( chips [ players [ you ] . chipQueue [ 1 ] ] . info.name )
end
2019-03-09 21:16:36 +00:00
end
2019-03-11 01:15:22 +00:00
local HPs = { { } , { } }
for id , player in pairs ( players ) do
HPs [ player.owner ] = HPs [ player.owner ] or { }
HPs [ player.owner ] [ # HPs [ player.owner ] + 1 ] = player.health
if player.owner == 1 then
term.setCursorPos ( 1 , # HPs [ player.owner ] )
term.write ( player.health )
else
term.setCursorPos ( scr_x - 3 , # HPs [ player.owner ] )
term.write ( player.health )
end
end
2019-03-11 05:33:25 +00:00
if showDebug then
term.setCursorPos ( 1 , scr_y - 1 )
term.write ( " Frame: " .. FRAME .. " , isHost = " .. tostring ( isHost ) .. " , you = " .. tostring ( you ) )
end
2019-03-09 21:09:52 +00:00
end
local getInput = function ( )
local evt
while true do
evt = { os.pullEvent ( ) }
if evt [ 1 ] == " key " then
keysDown [ evt [ 2 ] ] = true
2019-03-11 05:33:25 +00:00
if keysDown [ keys.leftCtrl ] and keysDown [ keys.t ] then
if skynet and useSkynet then
skynet.socket . close ( )
end
return
end
2019-03-09 21:09:52 +00:00
elseif evt [ 1 ] == " key_up " then
keysDown [ evt [ 2 ] ] = nil
elseif evt [ 1 ] == " mouse_click " or evt [ 1 ] == " mouse_drag " then
miceDown [ evt [ 2 ] ] = { evt [ 3 ] , evt [ 4 ] }
elseif evt [ 1 ] == " mouse_up " then
miceDown [ evt [ 2 ] ] = nil
end
end
end
local runGame = function ( )
2019-03-11 06:37:04 +00:00
local evt , getStateInfo
2019-03-09 21:09:52 +00:00
while true do
FRAME = FRAME + 1
2019-03-13 04:14:24 +00:00
render ( )
2019-03-11 04:10:00 +00:00
if isHost then
getControls ( )
for id , proj in pairs ( projectiles ) do
local success , imageData = chips [ proj.chipType ] . logic ( proj )
if success then
projectiles [ id ] . imageData = imageData
projectiles [ id ] . frame = proj.frame + 1
else
projectiles [ id ] = nil
end
2019-03-09 21:09:52 +00:00
end
2019-03-11 04:10:00 +00:00
for y = 1 , # stage.panels do
2019-03-13 04:14:24 +00:00
for x = 1 , # stage.panels [ y ] do
stage.panels [ y ] [ x ] . reserved = false
2019-03-11 04:10:00 +00:00
end
2019-03-09 21:09:52 +00:00
end
2019-03-11 04:10:00 +00:00
for id , player in pairs ( players ) do
2019-03-12 04:58:47 +00:00
if player.canMove then
stage.panels [ player.y ] [ player.x ] . reserved = id
end
2019-03-12 06:12:34 +00:00
local dmg , flinching = act.stage . getDamage ( player.x , player.y , id )
2019-03-11 04:10:00 +00:00
if player.cooldown . iframe == 0 and dmg > 0 then
player.health = player.health - dmg
if player.health <= 0 then
table.remove ( players , id )
elseif flinching then
player.cooldown . iframe = 16
player.cooldown . move = 8
player.cooldown . shoot = 6
end
elseif player.cooldown . shoot == 0 then
2019-03-13 04:14:24 +00:00
if player.canShoot then
if player.control . chip then
if player.chipQueue [ 1 ] then
if chips [ player.chipQueue [ 1 ] ] then
act.projectile . newProjectile ( player.x , player.y , player , player.chipQueue [ 1 ] )
for k , v in pairs ( chips [ player.chipQueue [ 1 ] ] . info.cooldown or { } ) do
player.cooldown [ k ] = v
end
if false then
table.remove ( player.chipQueue , 1 )
else
player.chipQueue [ # player.chipQueue + 1 ] = player.chipQueue [ 1 ]
table.remove ( player.chipQueue , 1 )
end
2019-03-11 06:37:04 +00:00
end
2019-03-11 01:15:22 +00:00
end
2019-03-13 04:14:24 +00:00
elseif player.control . buster then
act.projectile . newProjectile ( player.x , player.y , player , " buster " )
for k , v in pairs ( chips.buster . info.cooldown or { } ) do
player.cooldown [ k ] = v
end
2019-03-11 04:10:00 +00:00
end
2019-03-11 01:15:22 +00:00
end
2019-03-09 21:16:36 +00:00
end
2019-03-09 21:09:52 +00:00
end
2019-03-11 09:02:27 +00:00
for id , object in pairs ( objects ) do
2019-03-12 06:12:34 +00:00
local dmg , flinching = act.stage . getDamage ( object.x , object.y , nil , not object.friendlyFire and id , nil , id )
2019-03-12 04:58:47 +00:00
if object.cooldown . iframe == 0 and dmg > 0 then
2019-03-11 09:02:27 +00:00
object.health = object.health - dmg
if object.health <= 0 then
table.remove ( objects , id )
else
object.cooldown . iframe = 2
end
end
2019-03-13 04:14:24 +00:00
if objects [ id ] then
if object.xvel ~= 0 or object.yvel ~= 0 then
if not act.object . moveObject ( id , object.xvel , object.yvel ) then
if act.player . checkPlayerAtPos ( object.x + object.xvel , object.y ) or act.object . checkObjectAtPos ( object.x + object.xvel , object.y ) then
act.stage . setDamage ( object.x + object.xvel , object.y + object.yvel , object.smackDamage , 0 , 2 , false )
table.remove ( objects , id )
else
object.xvel = 0
object.yvel = 0
object.x = round ( object.x )
object.y = round ( object.y )
end
2019-03-12 04:58:47 +00:00
end
end
2019-03-13 04:14:24 +00:00
object.frame = object.frame + 1
if object.frame > 1 and object.frame % object.delayedTime == 0 then
object.delayedFunc ( object )
end
2019-03-12 04:58:47 +00:00
end
2019-03-11 09:02:27 +00:00
end
2019-03-11 04:10:00 +00:00
reduceCooldowns ( )
movePlayers ( )
2019-03-12 21:02:26 +00:00
sleep ( gameDelayInit )
2019-03-11 04:10:00 +00:00
transmit ( {
gameID = gameID ,
command = " get_state " ,
players = players ,
projectiles = projectiles ,
2019-03-12 04:58:47 +00:00
objects = objects ,
2019-03-11 06:37:04 +00:00
stageDamage = stage.damage ,
stagePanels = stage.panels ,
2019-03-11 04:10:00 +00:00
id = id
} )
else
getControls ( )
2019-03-11 06:37:04 +00:00
if players [ you ] then
transmit ( {
gameID = gameID ,
command = " set_controls " ,
id = yourID ,
pID = you ,
control = players [ you ] . control
} )
end
evt , getStateInfo = os.pullEvent ( " ccbn_get_state " )
players = getStateInfo.players
projectiles = getStateInfo.projectiles
2019-03-12 04:58:47 +00:00
objects = getStateInfo.objects
2019-03-11 06:37:04 +00:00
stage.damage = getStateInfo.stageDamage
stage.panels = getStateInfo.stagePanels
2019-03-09 21:09:52 +00:00
end
end
end
2019-03-11 04:10:00 +00:00
local interpretNetMessage = function ( msg )
if waitingForGame then
if msg.command == " find_game " then
local time = os.epoch ( " utc " )
if msg.time > time then
isHost = false
you = 2
else
isHost = true
you = 1
end
return true
end
elseif msg.gameID == gameID then
if isHost then
if msg.command == " set_controls " then
2019-03-12 21:04:46 +00:00
if players [ msg.pID ] then
players [ msg.pID ] . control = msg.control
end
2019-03-11 04:10:00 +00:00
end
else
if msg.command == " get_state " then
2019-03-11 06:37:04 +00:00
os.queueEvent ( " ccbn_get_state " , {
players = msg.players ,
projectiles = msg.projectiles ,
2019-03-12 04:58:47 +00:00
objects = msg.objects ,
2019-03-11 06:37:04 +00:00
stageDamage = msg.stageDamage ,
stagePanels = msg.stagePanels
} )
2019-03-11 04:10:00 +00:00
end
end
end
end
local networking = function ( )
local msg
while true do
msg = receive ( )
if type ( msg ) == " table " then
interpretNetMessage ( msg )
end
end
end
2019-03-13 04:14:24 +00:00
local cwrite = function ( text , y )
local cx , cy = term.getCursorPos ( )
term.setCursorPos ( scr_x / 2 - # text / 2 , y or ( scr_y / 2 ) )
term.write ( text )
end
2019-03-11 04:10:00 +00:00
local startGame = function ( )
getModem ( )
local time = os.epoch ( " utc " )
transmit ( {
gameID = gameID ,
command = " find_game " ,
respond = false ,
id = yourID ,
time = time ,
2019-03-13 04:14:24 +00:00
chips = chips
2019-03-11 04:10:00 +00:00
} )
local msg
waitingForGame = true
2019-03-11 05:37:21 +00:00
term.clear ( )
cwrite ( " Waiting for game... " )
2019-03-11 04:10:00 +00:00
repeat
msg = receive ( )
until interpretNetMessage ( msg )
2019-03-11 05:33:25 +00:00
gameID = isHost and gameID or msg.gameID
2019-03-13 04:14:24 +00:00
chips = isHost and chips or msg.chips
2019-03-11 04:10:00 +00:00
transmit ( {
gameID = gameID ,
command = " find_game " ,
respond = true ,
id = yourID ,
time = isHost and math.huge or - math.huge ,
2019-03-13 04:14:24 +00:00
chips = isHost and chips
2019-03-11 04:10:00 +00:00
} )
waitingForGame = false
2019-03-11 05:37:21 +00:00
parallel.waitForAny ( runGame , networking )
2019-03-11 04:10:00 +00:00
end
2019-03-11 05:37:21 +00:00
parallel.waitForAny ( startGame , getInput )
term.setBackgroundColor ( colors.black )
term.clear ( )
cwrite ( " Thanks for playing! " )
term.setCursorPos ( 1 , scr_y )