<< Back to Code Snippets & Analyses

Galactic Frigate (JavaScript code)

////////////////////////////////

//INIT (NOT FUNCTIONS)//////////

////////////////////////////////


//canvas

var canvas = document.getElementById("myCanvas");

var ctx = canvas.getContext("2d");


//audio variables (only work on website, but doesn't crash local)

var audioMainSongPath = 'https://lovablesightsandsounds.com/wp-content/uploads/song_general.mp3';

var audioShootPath = 'https://lovablesightsandsounds.com/wp-content/uploads/SmallSwordHit.wav';

var audioHitPath = 'https://lovablesightsandsounds.com/wp-content/uploads/CFalcon-Boom.wav';

var audioChoosePath = 'https://lovablesightsandsounds.com/wp-content/uploads/Highlight.wav';

var audioSelectorPath = 'https://lovablesightsandsounds.com/wp-content/uploads/Highlight2.wav';


var audioMainSong = new Audio(audioMainSongPath);

var audioShoot = new Audio(audioShootPath);

var audioHit = new Audio(audioHitPath);

var audioChoose = new Audio(audioChoosePath);

var audioSelector = new Audio(audioSelectorPath);


audioMainSong.loop = true;

audioMainSong.volume = 0.7;


var initSongVar = false;


//keyboard event listeners

document.addEventListener('keydown', keyDownHandler, false);

document.addEventListener('keyup', keyUpHandler, false);


//fps and milliseconds-per-frame

var fps = 30;

var mpf = 1000/fps;


//loops/modes/"rooms"

var titleLoopVar = true;

var twoPlayerLoopVar = false;

var playerVsCpuLoopVar = false;

var cpuVsCpuLoopVar = false;

var instructionsLoopVar = false;


//text sizes

//e.g. ctx.font = victoryTextSize + ' ' + 'serif';

var victoryTextSize = canvas.height*.2 + 'px';

var titleTextSize = canvas.height*.2 + 'px';

var titleSelectionSize = canvas.height*.1 + 'px';

var titleSelectionModifier = .07;

var titleSelectionSize = canvas.height*titleSelectionModifier + 'px';


//input button checks

var wPressed = false;

var aPressed = false;

var sPressed = false;

var dPressed = false;

var upPressed = false;

var downPressed = false;

var leftPressed = false;

var rightPressed = false;

var spacePressed = false;

var shiftPressed = false;


var wPrevState = false;

var aPrevState = false;

var sPrevState = false;

var dPrevState = false;

var upPrevState = false;

var downPrevState = false;

var leftPrevState = false;

var rightPrevState = false;

var spacePrevState = false;

var shiftPrevState = false;


var wJustPressed = false;

var aJustPressed = false;

var sJustPressed = false;

var dJustPressed = false;

var upJustPressed = false;

var downJustPressed = false;

var leftJustPressed = false;

var rightJustPressed = false;

var spaceJustPressed = false;

var shiftJustPressed = false;


///////////////////////

//TITLE VARIABLES//////

///////////////////////

var selectorOption = 0;


var choiceX = canvas.width*.34;

var choice0Y = canvas.height*.7;

var choice1Y = canvas.height*.766;

var choice2Y = canvas.height*.833;

var choice3Y = canvas.height*.9;


var selectorFrontX = canvas.width*.32;

var selectorBackX = canvas.width*.3;

var selector0Y1 = choice0Y-(canvas.height*(titleSelectionModifier*.3575));

var selector0Y2 = choice0Y-(canvas.height*(titleSelectionModifier*.715));

var selector0Y3 = choice0Y;



var selector1Y1 = choice1Y-(canvas.height*(titleSelectionModifier*.3575));

var selector1Y2 = choice1Y-(canvas.height*(titleSelectionModifier*.715));

var selector1Y3 = choice1Y;


var selector2Y1 = choice2Y-(canvas.height*(titleSelectionModifier*.3575));

var selector2Y2 = choice2Y-(canvas.height*(titleSelectionModifier*.715));

var selector2Y3 = choice2Y;


var selector3Y1 = choice3Y-(canvas.height*(titleSelectionModifier*.3575));

var selector3Y2 = choice3Y-(canvas.height*(titleSelectionModifier*.715));

var selector3Y3 = choice3Y;


//////////////////////////////////

//GAMEPLAY STATE VARIABLES, ETC.//

//////////////////////////////////


//variables to (dis)allow player control

var player1alive = true;

var player2alive = true;


//victory check: 0=n/a, 1=p1, 2=p2, 3=draw

var winner = 0;


//true when it's time to show postgame commands

var showPostgameCommands = false;


//auto-rematch variables

var autoRematchEnabled = 0;

var autoRematchState = 9;

var autoRematchText = "";

var autoRematchTimer = 0;


///////////////////////

//SHIP & PARTS FEATURES

///////////////////////

//ship body

var shipBodyRadius = canvas.width/24;

//ship turret

var turretHeight = canvas.width/40;

var turretWidth = canvas.width/13.333;

//bullet

var bulletHeight = canvas.width/60;

var bulletWidth = canvas.width/15;

//ammo tally rectangle

var tallyHeight = canvas.width/26.666;

var tallyWidth = canvas.width/160;

var tallyBuffer = canvas.width/80;


//ammo counters

var maxAmmo = 4;

var leftAmmoCount = maxAmmo;

var rightAmmoCount = maxAmmo;


//ship body coordinates

var leftShipY = canvas.height/2;

var rightShipY = canvas.height/2;

var leftShipX = shipBodyRadius;

var rightShipX = (canvas.width – shipBodyRadius);


//turret coordinates

var leftTurretY = leftShipY – turretHeight*.5;

var rightTurretY = rightShipY – turretHeight*.5;

var leftTurretX = leftShipX;

var rightTurretX = rightShipX;


//movement speeds

var shipSpeed = 3*(fps/30);

var fastShot = 9*(fps/30);

var slowShot = 3*(fps/30);

var slowTimer = 4*fps; //4 seconds


//ammo tally creation & coordinates

var leftAmmoTallies = [];

var leftTallyStartX = leftShipX – shipBodyRadius*.5;

for (var t=0; t<leftAmmoCount; t++) {

    leftAmmoTallies[t] = { x: leftTallyStartX + t*tallyBuffer, y: leftShipY – tallyHeight*.5 };

}

var rightAmmoTallies = [];

var rightTallyStartX = rightShipX + (shipBodyRadius*.5-tallyWidth);

for (var t=0; t<rightAmmoCount; t++) {

    rightAmmoTallies[t] = { x: rightTallyStartX – t*tallyBuffer, y: rightShipY – tallyHeight*.5 };

}


var bulletArray = [];

for (var b=0; b<maxAmmo; b++) {

    bulletArray[b] = new Bullet(0, 0, 1, 1, b, 0);

}

for (var b=maxAmmo; b<maxAmmo*2; b++) {

    bulletArray[b] = new Bullet(0, 0, 1, 2, b, 0);

}


//decay line creation & coordinates

var baseDecayTimer = 0.6*fps;

var leftDecayTimer = baseDecayTimer;

var rightDecayTimer = baseDecayTimer;

var leftDecayState = 0;

var rightDecayState = 0;

var decayDistance = (shipBodyRadius + turretWidth)/7;

var decayHeight = shipBodyRadius*2;

var decayBaseWidth = tallyWidth;

var decayWidthMod = 2;


var leftDecayLines = [];

for (var d=0; d<7; d++) {

    leftDecayLines[d] = { x: 0, y: 0 };

}

var leftDecayStartX = leftShipX – shipBodyRadius;


var rightDecayLines = [];

for (var d=0; d<7; d++) {

    rightDecayLines[d] = { x: 0, y: 0 };

}

var rightDecayStartX = rightShipX – shipBodyRadius*2 + decayBaseWidth;



////////////////////////////////

//INIT FUNCTIONS////////////////

////////////////////////////////


function initSong() {

    if (initSongVar == false) {

        audioMainSong.play()

        initSongVar = true;

    }

}


function initTitle() {

    //always start rooms with song check

    audioMainSong.play()

    

    //selector

    selectorOption = 0;

    

    //ship body coordinates

    leftShipY = canvas.height/2;

    rightShipY = canvas.height/2;

    leftShipX = shipBodyRadius;

    rightShipX = (canvas.width – shipBodyRadius);


    //turret coordinates

    leftTurretY = leftShipY – turretHeight*.5;

    rightTurretY = rightShipY – turretHeight*.5;

    leftTurretX = leftShipX;

    rightTurretX = rightShipX;

    

    //ammo counters

    leftAmmoCount = maxAmmo;

    rightAmmoCount = maxAmmo;


    //ammo tally creation & coordinates

    leftAmmoTallies = [];

    leftTallyStartX = leftShipX – shipBodyRadius*.5;

    for (var t=0; t<leftAmmoCount; t++) {

        leftAmmoTallies[t] = { x: leftTallyStartX + t*tallyBuffer, y: leftShipY – tallyHeight*.5 };

    }

    rightAmmoTallies = [];

    rightTallyStartX = rightShipX + (shipBodyRadius*.5-tallyWidth);

    for (var t=0; t<rightAmmoCount; t++) {

        rightAmmoTallies[t] = { x: rightTallyStartX – t*tallyBuffer, y: rightShipY – tallyHeight*.5 };

    }

    

    //reset decay variables

    leftDecayTimer = baseDecayTimer;

    rightDecayTimer = baseDecayTimer;

    leftDecayState = 0;

    rightDecayState = 0;

    

    //reset bullets

    for (var b=0; b<maxAmmo*2; b++) {

        bulletArray[b].state = 0;

        bulletArray[b].hitYet = 0;

    }

}


function initTwoPlayer() {

    //always start rooms with song check

    audioMainSong.play()

    

    //variables to (dis)allow player control

    player1alive = true;

    player2alive = true;

    

    //victory check: 0=n/a, 1=p1, 2=p2, 3=draw

    winner = 0;

    

    //true when it's time to show postgame commands

    showPostgameCommands = false;

    

    //auto-rematch variables

    autoRematchState = 9;

    autoRematchText = "";

    autoRematchTimer = 0;

    

    //ship body coordinates

    leftShipY = canvas.height/2;

    rightShipY = canvas.height/2;

    leftShipX = shipBodyRadius;

    rightShipX = (canvas.width – shipBodyRadius);


    //turret coordinates

    leftTurretY = leftShipY – turretHeight*.5;

    rightTurretY = rightShipY – turretHeight*.5;

    leftTurretX = leftShipX;

    rightTurretX = rightShipX;

    

    //ammo counters

    leftAmmoCount = maxAmmo;

    rightAmmoCount = maxAmmo;

    

    //ammo tally creation & coordinates

    leftAmmoTallies = [];

    leftTallyStartX = leftShipX – shipBodyRadius*.5;

    for (var t=0; t<leftAmmoCount; t++) {

        leftAmmoTallies[t] = { x: leftTallyStartX + t*tallyBuffer, y: leftShipY – tallyHeight*.5 };

    }

    rightAmmoTallies = [];

    rightTallyStartX = rightShipX + (shipBodyRadius*.5-tallyWidth);

    for (var t=0; t<rightAmmoCount; t++) {

        rightAmmoTallies[t] = { x: rightTallyStartX – t*tallyBuffer, y: rightShipY – tallyHeight*.5 };

    }

    

    //reset decay variables

    leftDecayTimer = baseDecayTimer;

    rightDecayTimer = baseDecayTimer;

    leftDecayState = 0;

    rightDecayState = 0;

    

    //reset bullets

    for (var b=0; b<maxAmmo*2; b++) {

        bulletArray[b].state = 0;

        bulletArray[b].hitYet = 0;

    }

}


////////////////////////////////////////////

//OBJECT CONSTRUCTORS (NOT PART OF A LOOP)//

////////////////////////////////////////////


//'type' = 1 or 2 (fast or slow, respectively)

//'player' = 1 or 2

//'state' = 0, 1, 2 (unpresent, traveling, stopped)

function Bullet(x, y, type, player, ID, state) {

    this.timer = slowTimer;

    this.slowWalled = 0;

    this.hitYet = 0;

    this.x = x;

    this.y = y;

    this.type = type;

    if (player == 1) {

        //this.direction = 1;

    }

    else if (player == 2) {

        //this.direction = -1;

    }

    this.id = ID;

    this.state = state;

    

    this.drawSelf = function() {

        ctx.beginPath();

        ctx.rect(this.x, this.y, bulletWidth, bulletHeight);

        ctx.fillStyle = 'red';

        ctx.fill();

        ctx.closePath();

    }

    this.leftsFastShot = function() {

        if (this.state == 1) {

            this.x += fastShot;

        }

    }

    this.leftsSlowShot = function() {

        if (this.state == 1) {

            this.x += slowShot;

            if ((this.x + bulletWidth >= canvas.width) && (this.slowWalled == 0)) {

                this.state = 2;

                this.slowWalled = 1;

            }

        }

        if (this.state == 2) {

            this.timer -= 1;

            if (this.timer <= 0) {

                this.state = 1;

            }

        }

    }

    this.rightsFastShot = function() {

        if (this.state == 1) {

            this.x -= fastShot;

        }

    }

    this.rightsSlowShot = function() {

        if (this.state == 1) {

            this.x -= slowShot;

            if ((this.x – bulletWidth <= 0) && (this.slowWalled == 0)) {

                this.state = 2;

                this.slowWalled = 1;

            }

        }

        if (this.state == 2) {

            this.timer -= 1;

            if (this.timer <= 0) {

                this.state = 1;

            }

        }

    }

}


/////////////////////////////////////////

//EVENT LISTENERS (NOT PART OF A LOOP)///

/////////////////////////////////////////

function keyDownHandler(e) {

    if(e.code  == 'KeyW') {

        wPressed = true;

    }

    else if(e.code == 'KeyS') {

        sPressed = true;

    }

    if(e.code  == 'KeyA') {

        aPressed = true;

    }

    else if(e.code == 'KeyD') {

        dPressed = true;

    }

    if(e.code  == 'ArrowUp') {

        upPressed = true;

        e.preventDefault();

    }

    else if(e.code == 'ArrowDown') {

        downPressed = true;

        e.preventDefault();

    }

    if(e.code  == 'ArrowLeft') {

        leftPressed = true;

        e.preventDefault();

    }

    else if(e.code == 'ArrowRight') {

        rightPressed = true;

        e.preventDefault();

    }

    if(e.code  == 'Space') {

        spacePressed = true;

        e.preventDefault();

    }

    if((e.code == 'ShiftLeft') || (e.code == 'ShiftRight')) {

        shiftPressed = true;

        e.preventDefault();

    }

}


function keyUpHandler(e) {

    if(e.code  == 'KeyW') {

        wPressed = false;

    }

    else if(e.code == 'KeyS') {

        sPressed = false;

    }

    if(e.code  == 'KeyA') {

        aPressed = false;

    }

    else if(e.code == 'KeyD') {

        dPressed = false;

    }

    if(e.code  == 'ArrowUp') {

        upPressed = false;

    }

    else if(e.code == 'ArrowDown') {

        downPressed = false;

    }

    if(e.code  == 'ArrowLeft') {

        leftPressed = false;

    }

    else if(e.code == 'ArrowRight') {

        rightPressed = false;

    }

    if(e.code == 'Space') {

        spacePressed = false;

    }

    if((e.code == 'ShiftLeft') || (e.code == 'ShiftRight')) {

        shiftPressed = false;

    }

}


///////////////////////////////////////////

//CALLABLE FUNCTIONS (NOT PART OF A LOOP)//

///////////////////////////////////////////

function roomSender(room) {

    if (room == 0) {

        titleLoopVar = true;

        twoPlayerLoopVar = false;

        playerVsCpuLoopVar = false;

        cpuVsCpuLoopVar = false;

        instructionsLoopVar = false;

        

        initTitle();

    }

    else if (room == 1) {

        titleLoopVar = false;

        twoPlayerLoopVar = true;

        playerVsCpuLoopVar = false;

        cpuVsCpuLoopVar = false;

        instructionsLoopVar = false;

        

        initTwoPlayer();

    }

}



/////////////////////////////////////////

//INPUT FUNCTIONS////////////////////////

/////////////////////////////////////////


//run this at the start of main loop

function justPressed() {

    if ((wPrevState == false) && (wPressed == true)) {

        wJustPressed = true;

    }

    if ((aPrevState == false) && (aPressed == true)) {

        aJustPressed = true;

    }

    if ((sPrevState == false) && (sPressed == true)) {

        sJustPressed = true;

    }

    if ((dPrevState == false) && (dPressed == true)) {

        dJustPressed = true;

    }

    if ((upPrevState == false) && (upPressed == true)) {

        upJustPressed = true;

    }

    if ((downPrevState == false) && (downPressed == true)) {

        downJustPressed = true;

    }

    if ((leftPrevState == false) && (leftPressed == true)) {

        leftJustPressed = true;

    }

    if ((rightPrevState == false) && (rightPressed == true)) {

        rightJustPressed = true;

    }

    if ((spacePrevState == false) && (spacePressed == true)) {

        spaceJustPressed = true;

    }

    if ((shiftPrevState == false) && (shiftPressed == true)) {

        shiftJustPressed = true;

    }

}

        

//run this directly before draw functions in main loop

function justPressedResetter() {

    wPrevState = wPressed;

    aPrevState = aPressed;

    sPrevState = sPressed;

    dPrevState = dPressed;

    upPrevState = upPressed;

    downPrevState = downPressed;

    leftPrevState = leftPressed;

    rightPrevState = rightPressed;

    spacePrevState = spacePressed;

    shiftPrevState = shiftPressed;

    

    wJustPressed = false;

    aJustPressed = false;

    sJustPressed = false;

    dJustPressed = false;

    upJustPressed = false;

    downJustPressed = false;

    leftJustPressed = false;

    rightJustPressed = false;

    spaceJustPressed = false;

    shiftJustPressed = false;

}



/////////////////////////////////////////

//TITLE LOGIC FUNCTIONS//////////////////

/////////////////////////////////////////

function titleLogic() {

    if (titleLoopVar) {

        if (sJustPressed || downJustPressed) {

            selectorOption += 1;

            let blip = new Audio(audioSelectorPath);

            blip.play();

        }

        else if (wJustPressed || upJustPressed) {

            selectorOption -= 1;

            let blip = new Audio(audioSelectorPath);

            blip.play();

        }

        

        if (selectorOption < 0) {

            selectorOption = 3;

        }

        if (selectorOption > 3) {

            selectorOption = 0;

        }

        

        if (spaceJustPressed) {

            roomSender(selectorOption+1);

            let blip = new Audio(audioChoosePath);

            blip.play();

        }

    }

}




/////////////////////////////////////////

//GAMEPLAY LOGIC FUNCTIONS///////////////

/////////////////////////////////////////

function leftShipMove() {

    if (wPressed) {

        leftShipY -= shipSpeed;

    }

    if (sPressed) {

        leftShipY += shipSpeed;

    }

    if (leftShipY – shipBodyRadius < 0) {

        leftShipY = shipBodyRadius;

    }

    if (leftShipY + shipBodyRadius > canvas.height) {

        leftShipY = canvas.height – shipBodyRadius;

    }

    leftTurretY = leftShipY – turretHeight*.5;

}


function rightShipMove() {

    if (upPressed) {

        rightShipY -= shipSpeed;

    }

    if (downPressed) {

        rightShipY += shipSpeed;

    }

    if (rightShipY – shipBodyRadius < 0) {

        rightShipY = shipBodyRadius;

    }

    if (rightShipY + shipBodyRadius > canvas.height) {

        rightShipY = canvas.height – shipBodyRadius;

    }

    rightTurretY = rightShipY – turretHeight*.5;

}


function leftAmmoMove() {

    for (var t=0; t<maxAmmo; t++) {

        var tallyX = leftTallyStartX + t*tallyBuffer;

        var tallyY = leftShipY – tallyHeight*.5;

        leftAmmoTallies[t].x = tallyX;

        leftAmmoTallies[t].y = tallyY;

    }        

}


function rightAmmoMove() {

    for (var t=0; t<maxAmmo; t++) {

        var tallyX = rightTallyStartX – t*tallyBuffer;

        var tallyY = rightShipY – tallyHeight*.5;

        rightAmmoTallies[t].x = tallyX;

        rightAmmoTallies[t].y = tallyY;

    }        

}


function shootBullet() {

    if (dJustPressed && player1alive) {

        for (var b=0; b<maxAmmo; b++) {

            if (bulletArray[b].state == 0) {

                bulletArray[b].x = leftShipX + bulletWidth*.5;

                bulletArray[b].y = leftShipY – bulletHeight*.5;

                bulletArray[b].type = 1;

                bulletArray[b].player = 1;

                bulletArray[b].state = 1;

                leftAmmoCount -= 1;

                

                let blip = new Audio(audioShootPath);

                blip.play();

                break;

            }

        }

    }

    if (aJustPressed && player1alive) {

        for (var b=0; b<maxAmmo; b++) {

            if (bulletArray[b].state == 0) {

                bulletArray[b].x = leftShipX + bulletWidth*.75;

                bulletArray[b].y = leftShipY – bulletHeight*.5;

                bulletArray[b].type = 2;

                bulletArray[b].player = 1;

                bulletArray[b].state = 1;

                bulletArray[b].timer = slowTimer;

                bulletArray[b].slowWalled = 0;

                leftAmmoCount -= 1;

                

                let blip = new Audio(audioShootPath);

                blip.play();

                break;

            }

        }

    }

    if (leftJustPressed && player2alive) {

        for (var b=maxAmmo; b<maxAmmo*2; b++) {

            if (bulletArray[b].state == 0) {

                bulletArray[b].x = rightShipX – bulletWidth*.5;

                bulletArray[b].y = rightShipY – bulletHeight*.5;

                bulletArray[b].type = 1;

                bulletArray[b].player = 2;

                bulletArray[b].state = 1;

                rightAmmoCount -= 1;

                

                let blip = new Audio(audioShootPath);

                blip.play();

                break;

            }

        }

    }

    if (rightJustPressed && player2alive) {

        for (var b=maxAmmo; b<maxAmmo*2; b++) {

            if (bulletArray[b].state == 0) {

                bulletArray[b].x = rightShipX – bulletWidth*.75;

                bulletArray[b].y = rightShipY – bulletHeight*.5;

                bulletArray[b].type = 2;

                bulletArray[b].player = 2;

                bulletArray[b].state = 1;

                bulletArray[b].timer = slowTimer;

                bulletArray[b].slowWalled = 0;

                rightAmmoCount -= 1;

                

                let blip = new Audio(audioShootPath);

                blip.play();

                break;

            }

        }

    }

}


function travelingBullets() {

    for (var b=0; b<maxAmmo*2; b++) {

        if ((bulletArray[b].state > 0) && (bulletArray[b].player == 1)) {

            if (bulletArray[b].type == 1) {

                bulletArray[b].leftsFastShot();

            }

            else if (bulletArray[b].type == 2) {

                bulletArray[b].leftsSlowShot();

            }

        }

        if ((bulletArray[b].state > 0) && (bulletArray[b].player == 2)) {

            if (bulletArray[b].type == 1) {

                bulletArray[b].rightsFastShot();

            }

            else if (bulletArray[b].type == 2) {

                bulletArray[b].rightsSlowShot();

            }

        }

    }

}


function destroyBullet() {

    for (var b=0; b<maxAmmo*2; b++) {

        if (((bulletArray[b].x < 0 ) || (bulletArray[b].x > canvas.width)) && (bulletArray[b].state == 1)) {

            bulletArray[b].state = 0;

            if (b<maxAmmo) {

                leftAmmoCount += 1;

            }

            else if (b>=maxAmmo) {

                rightAmmoCount += 1;

            }

        }

    }

}


function setLeftDecayLines() {

    //position them

    for (var d=0; d<7; d++) {

        var decayX = leftDecayStartX + d*decayDistance;

        var decayY = leftShipY – decayHeight*.5;

        leftDecayLines[d].x = decayX;

        leftDecayLines[d].y = decayY;

    }

    if (player1alive == false) {

        leftDecayTimer -= 1;

        if (leftDecayTimer <= 0 && leftDecayState < 3) {

            leftDecayTimer = baseDecayTimer;

            leftDecayState += 1;

        }

    }

}


function setRightDecayLines() {

    //position them

    for (var d=0; d<7; d++) {

        var decayX = rightDecayStartX + d*decayDistance;

        var decayY = rightShipY – decayHeight*.5;

        rightDecayLines[d].x = decayX;

        rightDecayLines[d].y = decayY;

    }

    if (player2alive == false) {

        rightDecayTimer -= 1;

        if (rightDecayTimer <= 0 && rightDecayState < 3) {

            rightDecayTimer = baseDecayTimer;

            rightDecayState += 1;

        }

    }

}




//run collision detection in physics function AFTER all move functions

function collisionDetection() {

    for (var b=0; b<maxAmmo*2; b++) {

        var bc = bulletArray[b];

        //check collision with left's bullets against right ship

        if (bc.player == 1 && bc.state > 0 && bc.hitYet == 0 && player2alive) {

            var bcLeft = bc.x;

            var bcRight = bc.x + bulletWidth;

            var bcTop = bc.y;

            var bcBottom = bc.y + bulletHeight;

            

            var rightTurretLeft = rightTurretX – turretWidth;

            var rightTurretRight = rightTurretX;

            var rightTurretTop = rightTurretY;

            var rightTurretBottom = rightTurretY + turretHeight;

            

            var bulletC = { x: bc.x, y: bc.y, w: bulletWidth, h: bulletHeight };

            var rightTurretC = { x: rightTurretX, y: rightTurretY, w: -turretWidth, h: turretHeight };

            var rightShipC = { x: rightShipX, y: rightShipY, r: shipBodyRadius };

            

            

            //test for collision with circular ship body

            var distX = Math.abs(rightShipC.x – bulletC.x-bulletC.w/2);

            var distY = Math.abs(rightShipC.y – bulletC.y-bulletC.h/2);

            var circleHit = true;

            

            if (distX > (bulletC.w/2 + rightShipC.r)) {

                circleHit = false;

            }

            if (distY > (bulletC.h/2 + rightShipC.r)) {

                circleHit = false;

            }


            if (distX <= (bulletC.w/2) && circleHit == true) {

                player2alive = false;

                bc.hitYet = 1;

                let blip = new Audio(audioHitPath);

                blip.play();

            }

            if (distY <= (bulletC.h/2) && circleHit == true) {

                player2alive = false;

                bc.hitYet = 1;

                let blip = new Audio(audioHitPath);

                blip.play();

            }

            //end test collision with circular ship body

            

            //test for collision with turret

            if (bcRight > rightTurretLeft && bcLeft < rightTurretRight && bcBottom > rightTurretTop && bcTop < rightTurretBottom) {

                player2alive = false;

                bc.hitYet = 1;

                let blip = new Audio(audioHitPath);

                blip.play();

            }

        }

        //check collision with right's bullets against left ship

        if (bc.player == 2 && bc.state > 0 && bc.hitYet == 0 && player1alive) {

            var bcLeft = bc.x – bulletWidth;

            var bcRight = bc.x;

            var bcTop = bc.y;

            var bcBottom = bc.y + bulletHeight;

            

            var leftTurretLeft = leftTurretX;

            var leftTurretRight = leftTurretX + turretWidth;

            var leftTurretTop = leftTurretY;

            var leftTurretBottom = leftTurretY + turretHeight;

            

            var bulletC = { x: bc.x-bulletWidth, y: bc.y, w: bulletWidth, h: bulletHeight };

            var leftTurretC = { x: leftTurretX, y: leftTurretY, w: turretWidth, h: turretHeight };

            var leftShipC = { x: leftShipX, y: leftShipY, r: shipBodyRadius };

            

            

            //test for collision with circular ship body

            var distX = Math.abs(leftShipC.x – bulletC.x-bulletC.w/2);

            var distY = Math.abs(leftShipC.y – bulletC.y-bulletC.h/2);

            var circleHit = true;

            

            if (distX > (bulletC.w/2 + leftShipC.r)) {

                circleHit = false;

            }

            if (distY > (bulletC.h/2 + leftShipC.r)) {

                circleHit = false;

            }


            if (distX <= (bulletC.w/2) && circleHit == true) {

                player1alive = false;

                bc.hitYet = 1;

                let blip = new Audio(audioHitPath);

                blip.play();

            }

            if (distY <= (bulletC.h/2) && circleHit == true) {

                player1alive = false;

                bc.hitYet = 1;

                let blip = new Audio(audioHitPath);

                blip.play();

            }

            //end test collision with circular ship body

            

            //test for collision with turret

            if (bcRight > leftTurretLeft && bcLeft < leftTurretRight && bcBottom > leftTurretTop && bcTop < leftTurretBottom) {

                player1alive = false;

                bc.hitYet = 1;

                let blip = new Audio(audioHitPath);

                blip.play();

            }

        }

    }

}


function victoryCheck() {

    if (winner == 0) {

        if (player1alive == true && player2alive == false) {

            winner = 1;

        }

        else if (player1alive == false && player2alive == true) {

            winner = 2;

        }

        else if (player1alive == false && player2alive == false) {

            winner = 3;

        }

    }

}


function victoryDeclaration() {

    if (winner == 1) {

        //do something maybe;

    }

    else if (winner == 2) {

        //do something maybe;

    }

    else if (winner == 3) {

        //do something maybe;

    }

    

    if ((leftDecayState == 3 || rightDecayState == 3) && !(showPostgameCommands)) {

        showPostgameCommands = true;

    }

    

    if (autoRematchEnabled == 1) {

        if (showPostgameCommands) {

            autoRematchTimer += 1;

        }

        

        if ((autoRematchTimer >= 1) && (autoRematchTimer < fps*1)) {

            autoRematchText = autoRematchState.toString();

        }

        else if ((autoRematchTimer >= fps*1) && (autoRematchTimer < fps*2) && (autoRematchState == 9)) {

            autoRematchState -= 1;

            autoRematchText = autoRematchState.toString();

        }

        else if ((autoRematchTimer >= fps*2) && (autoRematchTimer < fps*3) && (autoRematchState == 8)) {

            autoRematchState -= 1;

            autoRematchText = autoRematchState.toString();

        }

        else if ((autoRematchTimer >= fps*3) && (autoRematchTimer < fps*4) && (autoRematchState == 7)) {

            autoRematchState -= 1;

            autoRematchText = autoRematchState.toString();

        }

        else if ((autoRematchTimer >= fps*4) && (autoRematchTimer < fps*5) && (autoRematchState == 6)) {

            autoRematchState -= 1;

            autoRematchText = autoRematchState.toString();

        }

        else if ((autoRematchTimer >= fps*5) && (autoRematchTimer < fps*6) && (autoRematchState == 5)) {

            autoRematchState -= 1;

            autoRematchText = autoRematchState.toString();

        }

        else if ((autoRematchTimer >= fps*6) && (autoRematchTimer < fps*7) && (autoRematchState == 4)) {

            autoRematchState -= 1;

            autoRematchText = autoRematchState.toString();

        }

        else if ((autoRematchTimer >= fps*7) && (autoRematchTimer < fps*8) && (autoRematchState == 3)) {

            autoRematchState -= 1;

            autoRematchText = autoRematchState.toString();

        }

        else if ((autoRematchTimer >= fps*8) && (autoRematchTimer < fps*9) && (autoRematchState == 2)) {

            autoRematchState -= 1;

            autoRematchText = autoRematchState.toString();

        }

        else if ((autoRematchTimer >= fps*9) && (autoRematchTimer < fps*10) && (autoRematchState == 1)) {

            autoRematchState -= 1;

            autoRematchText = autoRematchState.toString();

            initTwoPlayer();

        }

    }

}


function postgameSelection() {

    if (showPostgameCommands) {

        if (spaceJustPressed) {

            let blip = new Audio(audioChoosePath);

            blip.play();

            

            roomSender(0);

        }

        else if (shiftJustPressed) {

            let blip = new Audio(audioChoosePath);

            blip.play();

            

            initTwoPlayer();

        }

    }

}




/////////////////////////////////////////

//DRAW FUNCTIONS/////////////////////////

/////////////////////////////////////////

function drawBackground() {

    ctx.fillStyle = 'black';

    ctx.fillRect(0, 0, canvas.width, canvas.height);

}


function drawLeftShip() {

    //body

    ctx.beginPath();

    ctx.arc(leftShipX, leftShipY, shipBodyRadius, 0, Math.PI*2);

    ctx.fillStyle = 'white';

    ctx.fill();

    ctx.closePath();

    //turret

    ctx.beginPath();

    ctx.rect(leftTurretX, leftTurretY, turretWidth, turretHeight);

    ctx.fillStyle = 'white';

    ctx.fill();

    ctx.closePath();

}


function drawRightShip() {

    //body

    ctx.beginPath();

    ctx.arc(rightShipX, rightShipY, shipBodyRadius, 0, Math.PI*2);

    ctx.fillStyle = 'white'

    ctx.fill();

    ctx.closePath();

    //turret

    ctx.beginPath();

    ctx.rect(rightTurretX, rightTurretY, -turretWidth, turretHeight);

    ctx.fillStyle = 'white';

    ctx.fill();

    ctx.closePath();

}


function drawLeftAmmo() {

    for (var t=0; t<leftAmmoCount; t++) {

        ctx.beginPath();

        ctx.rect(leftAmmoTallies[t].x, leftAmmoTallies[t].y, tallyWidth, tallyHeight);

        ctx.fillStyle = 'black';

        ctx.fill();

        ctx.closePath();

    }

}


function drawRightAmmo() {

    for (var t=0; t<rightAmmoCount; t++) {

        ctx.beginPath();

        ctx.rect(rightAmmoTallies[t].x, rightAmmoTallies[t].y, tallyWidth, tallyHeight);

        ctx.fillStyle = 'black';

        ctx.fill();

        ctx.closePath();

    }

}


function drawLeftDecayLines() {

    if (player1alive == false) {

        for (var d=0; d<7; d++) {

            ctx.beginPath();

            ctx.rect(leftDecayLines[d].x, leftDecayLines[d].y, decayBaseWidth+decayWidthMod*leftDecayState, decayHeight);

            ctx.fillStyle = 'black';

            ctx.fill();

            ctx.closePath();

        }

    }

}


function drawRightDecayLines() {

    if (player2alive == false) {

        for (var d=0; d<7; d++) {

            ctx.beginPath();

            ctx.rect(rightDecayLines[d].x, rightDecayLines[d].y, decayBaseWidth+decayWidthMod*rightDecayState, decayHeight);

            ctx.fillStyle = 'black';

            ctx.fill();

            ctx.closePath();

        }

    }

}


function drawLeftBullet() {

    for (var b=0; b<maxAmmo; b++) {

        if (bulletArray[b].state > 0) {

            ctx.beginPath();

            ctx.rect(bulletArray[b].x, bulletArray[b].y, bulletWidth, bulletHeight);

            ctx.fillStyle = 'white';

            ctx.fill();

            ctx.closePath();

        }

    }

}


function drawRightBullet() {

    for (var b=maxAmmo; b<maxAmmo*2; b++) {

        if (bulletArray[b].state > 0) {

            ctx.beginPath();

            ctx.rect(bulletArray[b].x, bulletArray[b].y, -bulletWidth, bulletHeight);

            ctx.fillStyle = 'white';

            ctx.fill();

            ctx.closePath();

        }

    }

}


function drawVictoryDeclaration() {

    if (winner == 1) {

        ctx.font = victoryTextSize + ' ' + 'serif';

        ctx.textAlign = 'center';

        ctx.fillStyle = 'white';

        ctx.fillText('VICTORY', canvas.width/2, canvas.height*.466);

        ctx.fillText('P1', canvas.width/2, canvas.height*.666);

    }

    else if (winner == 2) {

        ctx.font = victoryTextSize + ' ' + 'serif';

        ctx.textAlign = 'center';

        ctx.fillStyle = 'white';

        ctx.fillText('VICTORY', canvas.width/2, canvas.height*.466);

        ctx.fillText('P2', canvas.width/2, canvas.height*.666);

    }

    else if (winner == 3) {

        ctx.font = victoryTextSize + ' ' + 'serif';

        ctx.textAlign = 'center';

        ctx.fillStyle = 'white';

        ctx.fillText('DRAW', canvas.width/2, canvas.height*.5);

    }

    

    if (showPostgameCommands) {

        ctx.font = titleSelectionSize + ' ' + 'serif';

        ctx.textAlign = 'center';

        ctx.fillStyle = 'white';

        ctx.fillText('SPACE for TITLE', canvas.width/2, canvas.height*.8);

        ctx.fillText('SHIFT for REMATCH', canvas.width/2, canvas.height*.866);

        if (autoRematchEnabled == 1) {

            ctx.fillText('AUTO-REMATCH in ' + autoRematchText, canvas.width/2, canvas.height*.933);

        }

    }

}





function drawTitleText() {

    ctx.font = titleTextSize + ' ' + 'serif';

    ctx.textAlign = 'center';

    ctx.fillStyle = 'white';

    ctx.fillText('GALACTIC', canvas.width/2, canvas.height*.333);

    ctx.fillText('FRIGATE', canvas.width/2, canvas.height*.533);

    ctx.font = titleSelectionSize + ' ' + 'serif';

    ctx.textAlign = 'left';

    ctx.fillText('TWO-PLAYER', choiceX, choice0Y);

    ctx.fillText('PLAYER VS. CPU', choiceX, choice1Y);

    ctx.fillText('CPU VS. CPU', choiceX, choice2Y);

    ctx.fillText('RULES', choiceX, choice3Y);

}


function drawTitleSelector() {

    if (selectorOption == 0) {

        ctx.beginPath();

        

        ctx.moveTo(selectorFrontX, selector0Y1);

        ctx.lineTo(selectorBackX, selector0Y2);

        ctx.lineTo(selectorBackX, selector0Y3);

        

        ctx.fillStyle = 'white';

        ctx.fill();

        ctx.closePath();

    }

    else if (selectorOption == 1) {

        ctx.beginPath();

        ctx.moveTo(selectorFrontX, selector1Y1);

        ctx.lineTo(selectorBackX, selector1Y2);

        ctx.lineTo(selectorBackX, selector1Y3);

        ctx.fillStyle = 'white';

        ctx.fill();

        ctx.closePath();

    }

    else if (selectorOption == 2) {

        ctx.beginPath();

        ctx.moveTo(selectorFrontX, selector2Y1);

        ctx.lineTo(selectorBackX, selector2Y2);

        ctx.lineTo(selectorBackX, selector2Y3);

        ctx.fillStyle = 'white';

        ctx.fill();

        ctx.closePath();

    }

    else if (selectorOption == 3) {

        ctx.beginPath();

        ctx.moveTo(selectorFrontX, selector3Y1);

        ctx.lineTo(selectorBackX, selector3Y2);

        ctx.lineTo(selectorBackX, selector3Y3);

        ctx.fillStyle = 'white';

        ctx.fill();

        ctx.closePath();

    }

}


function drawTitleShips() {

    drawLeftShip();

    drawRightShip();

    drawLeftAmmo();

    drawRightAmmo();

}


/////////////////////////////////////////

//COMPOSITE FUNCTIONS////////////////////

/////////////////////////////////////////


//LOGIC

//titleLogic isn't here bc it's not composed of any other functions; it's above

function twoPlayerLogic() {

    if (twoPlayerLoopVar) {

        leftShipMove();

        rightShipMove();

        leftAmmoMove();

        rightAmmoMove();

        setLeftDecayLines();

        setRightDecayLines();

        shootBullet();

        travelingBullets();

        destroyBullet();

        

        collisionDetection();

        victoryCheck();

        victoryDeclaration();

        postgameSelection();

    }

}


//DRAW

function drawTitle() {

    if (titleLoopVar) {

        drawTitleText();

        drawTitleSelector();

        drawTitleShips();

    }

}


function drawTwoPlayer() {

    if (twoPlayerLoopVar) {

        drawLeftShip();

        drawRightShip();

        drawLeftDecayLines();

        drawRightDecayLines();

        drawLeftAmmo();

        drawRightAmmo();

        drawLeftBullet();

        drawRightBullet();

        

        drawVictoryDeclaration();

    }

}


/////////////////////////////////////////

//ULTIMATE COMPOSITE FUNCTIONS///////////

/////////////////////////////////////////

function logic() {

    titleLogic();

    twoPlayerLogic();

}


function draw() {

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    drawBackground();

    

    drawTitle();

    drawTwoPlayer();

}



/////////////////////////////////////////

//MAIN LOOP//////////////////////////////

/////////////////////////////////////////


function main() {

    initSong();

    justPressed();

    logic();

    justPressedResetter();

    draw();

}


setInterval(main, mpf);