Min Jun Kim and Han Yu- Final Project

Supreme Invasion!

/*
Min Jun Kim & Han Yu
minjunki && hyu1
15104
Final Project 

*/

//This Project is a spaceship fighting game.

var px = 0; //x location of ship
var py = 240; //starting y location of our ship
var psize = 50; //size of ourship
var keys = []; //for controlling sihp
var Selfprojectiles = []; //array of our projectiles 
var projectileCounter = 100; //for controlling flow of projectiles
var SOE = []; //array of enemy ships
var shipSpeed = 3; //our speed for moving ship
var distanceOne = []; //distance between projectile and enemy
var distanceTwo = []; //distance between projectile and enemy
var SOEnumb = 2; //number of enemies
var timer = 0; //for controlling flow of game
var gameStage = 1; //starting stage of the game
var timerIncrement = 1; //another variable for controlling flow of game
var enemyProjectiles = []; //array of enemy bullets
var EPdistance = []; //distance between enemy bullets and our ship
var ourHealth = 10; //how much health we have left
var gameOver = 0; //activated when user dies
var clickStart = 0; //for controlling start of game
var clickCount = 0; //for making sure no double clicks
var stars = []; //array of stars in background
var imageOne; //image
var imageTwo; //image
var imageThree; //image

//making background stars
function makeStars(x, y, size) {
    return {x: x, y: y, size: size, draw: starRender, update: starUpdate}
}

//draws stars as ellipse
function starRender() {
	fill(255);
    ellipse(this.x, this.y, this.size);
}

//scrolling speed of stars
function starUpdate() {
    this.x -= 3;
}

//making our bullets
function makeOurProjectile(x, y, fast, powerful) {
	return {x: x, y: y, speed: fast, power: powerful, 
		drawIt: OPrender, updateIt: OPupdate}
}

//making enemy bullets
function makeEnemyProjectile(x,y, speedo,status) {
	return {x: x, y: y, speed: speedo, boss: status, 
		updir: random(-5,5), drawIt: EPrender, updateIt: EPupdate}
}

//draws bullets, depending on type of enemy
function EPrender() {
	//if boss, have balls instead of lines
	if (this.boss == true) {
		fill(0);
		stroke(250,0,0);
		ellipse(this.x, this.y, 10, 10);
	}
	//standard line bullets from enemy
	else {
		stroke(250, 0, 0);
		strokeWeight(3);
		line(this.x, this.y, this.x-30, this.y);
	}
	
}

//controls the movement of enemy bullets depends on enemy type
function EPupdate() {
	//if boss, then make bullets bounce and have random speed
	if (this.boss == true) {
		this.y += this.updir;
		this.x -= this.speed*random(1,2);
		//bounces bullets
		if (this.y > height || this.y < 0) {
			this.updir = -this.updir;
		}
	}
	//all other enemies have one dimensional attacks
	else {
		this.x -= this.speed;
	}
	
}

//draws our bullets as green lines
function OPrender() {
	stroke(0,200,0);
	strokeWeight(this.power);
	line(this.x, this.y, this.x+30, this.y);
}

//moves the bullet across map
function OPupdate() {
	this.x += this.speed;
}

//the specs of enemy ships
function makeSOEnemy(x, y, fast, sizing) {
	return {x: x, y: y, big: sizing, speed: fast, 
		drawIt: SOErender, update: SOEupdate, DMG: 0}
}

//controls the movement of enemy ships depending on size and location
function SOEupdate() {
	//moves the enemy along the map
	if (gameStage == 1)	 {
		this.y += this.speed;
	}
	if (gameStage == 2)	 {
		this.y += this.speed;
	}
	//if 2nd round 1st enemy, move back as game goes on
	if (gameStage == 2 & this.x < 500) {
		this.x += 0.1;
	}

	if (this.y > height) {
		this.speed = -this.speed;
	}
	if (this.y < 0) {
		this.speed = -this.speed;
	}
	
	//first round enemy with easy projectiles
	if (gameStage == 1) {
		if (random(1) > 0.98 & this.y < 600) {
			enemyProjectiles.push(makeEnemyProjectile(this.x, this.y,5));	
		}

	}
	//2nd round enemy, with more frequent bullets
	if (gameStage == 2 & this.big < 90) {
		if (random(1) > 0.93 && this.y < 600) {
			enemyProjectiles.push(makeEnemyProjectile(this.x, this.y, 5));	
		}

	}
	//pulls our ship closer and make projectiles more frequently
	if (gameStage == 2 & this.big > 90) {
		if (random(1) > 0.95 && this.y < 600) {
			enemyProjectiles.push(makeEnemyProjectile(this.x, this.y, 
				random(4,6,1), true));	
		}
		px+=0.3;

	}
}

//draws the enemies depending on the size input.
function SOErender() {
		fill(100)
		//draw the boss and health bar
		if (gameStage == 2 & this.big > 90) {
			push();
			fill(250,0,0);
			rectMode(CORNER);
			rect(5,5,width-this.DMG*6.4,20);
			pop();
			
			push();
			translate(this.x-85, this.y-60);
			scale(0.37, 0.37);
			image(imageThree,0, 0);
			pop();

		}
		//the standard enemy 
		else {
			//health bar
			push();
			fill(220, 0, 0);
			rect(this.x, this.y - this.big/1.5, 
				this.big - this.DMG*this.big/20, 10);
			pop();
			
			//standard enemy
			push();
			translate(this.x-this.big*1.4, this.y-this.big*0.5);
			scale(0.0035*this.big, 0.0035*this.big);
			image(imageTwo, 0, 0);
			pop();
		}
}


//==============================================================

//sets up the keys and the initial stars
function setup() {
   createCanvas(640,480)
   keys = [false, false, false, false, false]
   for (i = 0; i < 200; i++) {
        stars[i]=makeStars(random(width), random(height), random(5));
    }
   imageOne = loadImage("https://i.imgur.com/GHtg2vU.png");
   imageTwo = loadImage("https://i.imgur.com/svK8OkI.png");
   imageThree = loadImage("https://i.ibb.co/n0G0yGC/eae.png");
}

function draw() {
	//basic initial specs
    background(17,21,61);
    rectMode(CENTER);
    noStroke();
    fill(100);
    
    //projectile counter increases by 1
	projectileCounter += 1;
	
	//increase timer by timer increment
	timer += timerIncrement;

	//makes first round of enemies when timer is 1
	if (timer == 1 & gameStage == 1) {
		for (i = 0; i < SOEnumb; i ++) {
    		SOE[i] = makeSOEnemy(500,random(height), random(5), random(40,60));
    	}
    	timer += 1;

	}

	//states the first round
	if (timer > 1 & timer < 40) {
		textAlign(CENTER);
		push();
		stroke(255);
		fill(255);
		textSize(20);
		text("Round One!", width/2, height/2-100);
		pop();
		
	}

	//pauses the increase in timer
	if (timer > 41) {
		timerIncrement = 0;
	}

	//resumes timer once 1st round is finished
	if (gameStage == 2) {
		timerIncrement = 1;
	}

	//once timer has started moving again, write text stating the stage
	if (timer > 50 & timer < 150) {
		push();
		noStroke();
		fill(255);
		textSize(20);
		text("Final Stage!", width/2, height/2-100);
		pop();
	}

	//makes 2 more enemies once timer reaches 60
	if (timer == 60 & gameStage == 2) {
    	SOE[0] = makeSOEnemy(400,random(height), 6.5, 30);
    	SOE[1] = makeSOEnemy(550, height/2, 3, 100);
    	timer += 1;

	}

	//move right if rightarrow is pressed
    if (keys[0] == true) {
    	px += shipSpeed;	
    }

    //move left if leftarrow is pressed
    if (keys[1] == true) {
    	px -= shipSpeed;
    }

    //move up if uparrow is pressed
    if (keys[2] == true) {
    	py -= shipSpeed;
    }

    //move down if downarrow is pressed
    if (keys[3] == true) {
    	py += shipSpeed;
    }

    //add projectiles once shift is pressed and reset counter
    if (keys[4] == true & projectileCounter > 5) { //8
    	Selfprojectiles.push(makeOurProjectile(px+50, py, 10, 3))
    	projectileCounter = 0;
    }
    
    //for teleporting up and down once limit is reached
    if (py > height) {
    	py = 0;
    }
    if (py < 0) {
    	py = height;
    }
    //prevents user from going behind map by pushing back
    if (px < -5) {
    	px += 3;
    }
    //prevents user from moving too forward by pushing back
    if (px > width/2) {
    	px -= 3.3;
    }

    //makes random star objects, and initializes the functions
    noStroke()
    for (i = 0; i < 1; i++) {
        stars.push(makeStars(width, random(height), random(5)));
    }
    for (i = 0; i < stars.length; i++) {
        stars[i].draw();
        stars[i].update();

        //if stars if too far behind map, shift the array
        if (stars[i].x < -500) {
            stars.shift();
        }

    }
    

    //initializes the enemoies
    for (i = 0; i < SOEnumb; i ++) {
    	SOE[i].drawIt();
    	SOE[i].update();
    	
    }
    
    //initializes the enemy projectiles
    for (i = 0; i < enemyProjectiles.length; i++) {
    	enemyProjectiles[i].drawIt();
    	enemyProjectiles[i].updateIt();
    }

    //initializes our projectiles
    for (i = 0; i < Selfprojectiles.length; i++) {
    	Selfprojectiles[i].drawIt();
    	Selfprojectiles[i].updateIt();
    		//calculates distance if the array is nonzero and projectiles
    		//are still on map
			if (Selfprojectiles.length > 0 & Selfprojectiles[i].x < 700) {
				//calculate the distance between enemy 1 and projectile
				distanceOne[i] = dist(Selfprojectiles[i].x, 
				Selfprojectiles[i].y, SOE[0].x, SOE[0].y);
				//calculate distance between enemy 2 and projectiles
				distanceTwo[i] = dist(Selfprojectiles[i].x, 
				Selfprojectiles[i].y, SOE[1].x, SOE[1].y);

				//if off map shift distances and projectiles
				if (Selfprojectiles[i].x > width) {
					Selfprojectiles.shift();
					distanceOne.shift();
					distanceTwo.shift();
				}
				//if distance is smaller than the size of enemy 1 shift
				if (distanceOne[i] < SOE[0].big) {
					Selfprojectiles.shift();
					distanceOne.shift();
					distanceTwo.shift();
					SOE[0].DMG ++;
				}
				//if distance is smaller than the size of enemy 2 shift
				if (distanceTwo[i] < SOE[1].big) {
					Selfprojectiles.shift();
					distanceOne.shift();
					distanceTwo.shift();
					SOE[1].DMG ++;
				}
				//if enemy 1 reach damage cap, move off map prevent shooting
				if (SOE[0].DMG > 20) {
					SOE[0].y = 700;
					SOE[0].x = 700;
					SOE[0].speed = 0;
				}
				//if enemy 2 reach damage cap, move off map prevent shooting
				if (SOE[1].DMG > 20 & gameStage == 1) {
					SOE[1].y = 700;
					SOE[1].x = 700;
					SOE[1].speed = 0;
				}
				//if boss reach damage cap move off stage
				if (SOE[1].DMG > 100 & gameStage == 2) {
					SOE[1].y = 700;
					SOE[1].x = 700;
					SOE[1].speed = 0;
				}
				//if both enemies are killed during 2nd stage, end game
				if (SOE[0].y == 700 & SOE[1].y == 700 && SOE[1].DMG > 99) {
					gameStage = 3;
				}
				//if both enemnies killed during 1st stage, move up stage
				if (SOE[0].y == 700 & SOE[1].y == 700 && gameStage == 1) {
					gameStage =2;
				}

				stroke(255);
		}    
	}

    
    for (z = 0; z < enemyProjectiles.length; z++) {
    		//if enemy projectile array is nonzero, calculate distance
			if (enemyProjectiles.length > 0) {
				EPdistance[z] = dist(enemyProjectiles[z].x, enemyProjectiles[z].y,
					px, py);
				//if bullet goes off map, shift distance and bullet
				if (enemyProjectiles[z].x < 0) {
					enemyProjectiles.shift();
					EPdistance.shift();
				}
				
				//if projectile too close to ourship, shift arrays and do damage
				if (EPdistance[z] < 20 & timer != 0) {
					enemyProjectiles.shift();
					EPdistance.shift();
					ourHealth -= 1;
				}
				//if health is zero, end game
				if (ourHealth === 0) {
					gameOver = 1;
				}

			}
		}

	//draw the health bar
	push();
    rectMode(CENTER);
    noStroke();
    fill(0,200,0);
    rect(px+psize/2, py-psize/2, ourHealth*5,5);
    pop();

    //draw our ship
    push();
    noStroke();
    fill(100);
    translate(px-37,py-25);
    scale(0.15,0.15);
    image(imageOne, 0, 0);
    pop();


    //initial stage specs. Gives instructions
	if (clickStart == 0) {
		timer = 0;
		timerIncrement = 0;
		//black background
		push();
		fill(0);
		rect(width/2, height/2, 1000, 1000);
		pop();

		//design and title
		push();
		fill(255, 253, 83, 140);
		noStroke();
		triangle(150, 100, 280, 600, 550, 500);
		triangle(110, 100, 20, 500, 130, 500);
		triangle(170, 105, 700, 50, 700, 300);
		textFont("futura");
		textStyle(ITALIC);
		fill(0);
		textSize(50);
		text("Supreme", 500-85, 130-3);
		text("Invasion", 541-85, 180-3);
		pop();

		//decoration
		push();
		translate(-150,90);
		scale(0.6, 0.6);
		rotate(-0.5);
		image(imageOne, 0, 100);
		pop();

		//instructions
		push();
		noStroke();
		fill(0,0,200);
		fill(255);
		textSize(30);
		text("click to begin!", width/2+25, 440);
		fill(0);
		textSize(20);
		text("Arrow Keys to move", width/2+5, 390);
		text("Or WASD to move", width/2+5, 410);
		text("Shift to shoot", width/2-35, 370);
		pop();
	}

	//once clicked, the opening screen is changed
	if (mouseIsPressed & clickCount == 0) {
		clickCount += 1;
		timerIncrement = 1;
		clickStart = 1;	
	}
	//if game over is active, then cover screen with message
	if (gameOver > 0) {
		fill(0);
		rect(0,0, width*2, height*2);
		fill(255);
		noStroke();
		textSize(50);
		text("Game Over!", width/2, height/2);
		textSize(30);
		text("Press 'r' to restart", width/2, height/2+50);
		push();
		translate(350, 10);
		scale(0.5, 0.5);
		image(imageTwo, 0, 0);
		pop();
	}
	//if end game is activated then cover screen and say end message
	if (gameStage == 3) {
		fill(0);
		rect(width/2, height/2, 1000,1000);
		push();
		translate(460, 200);
		rotate(0.3);
		scale(0.8, 0.8);
		image(imageThree, 0, 0);
		pop();
		push();
		noStroke();
		fill(255);
		textSize(50);
		text("You Win! :)", width/2, height/2);
		textSize(20);
		text("Press 'r' to play again", width/2, height/2+50);
		pop();
	}	
}

//Controls======================================================
//==============================================================

//if key is pressed changes the array for controls
function keyPressed() {
	if (key == "ArrowRight" || key == "d" || key == "D") {
		keys[0] = true;
	}
	if (key == "ArrowLeft" || key == "a" || key == "A") {
		keys[1] = true;
	}
	if (key == "ArrowUp" || key == "w" || key == "W") {
		keys[2] = true;
	}
	if (key == "ArrowDown" || key == "s" || key == "S") {
		keys[3] = true;
	}
	if (key == "Shift") {
		keys[4] = true;
	}
	//resets game if press r
	if (key == "r" || key == "R") {
		gameOver = 0;
		gameStage = 1;
		timer = 0;
		timerIncrement = 1;
		clickCount = 0;
		clickStart = 0;
		ourHealth = 10;
		px = 0;
		py = 240;
	}
}

//releases the keys
function keyReleased() {
	if (key == "ArrowRight" || key == "d" || key == "D") {
		keys[0] = false;
	}
	if (key == "ArrowLeft" || key == "a" || key == "A") {
		keys[1] = false;
	}
	if (key == "ArrowUp" || key == "w" || key == "W") {
		keys[2] = false;
	}
	if (key == "ArrowDown" || key == "s" || key == "S") {
		keys[3] = false;
	}
	if (key == "Shift") {
		keys[4] = false;
	}
}

Instructions:
To play the game, click the initial screen to start the game. You can use either the arrow keys or “w” to move up, “a” to move left, “s” to move down, and “d” to move right (WASD). To shoot projectiles press SHIFT. The point of the game is to defeat all enemies. If you hit the enemy with your projectiles their health will go down, and once the enemy’s health hits zero, the enemy is considered defeated. On the second stage, the boss will pull the user towards the right side of the map, so be mindful. Lastly, the user cannot go horizontally beyond certain limits, and once the user reaches the top of the map the user will be teleported to the bottom of the map.

Statements:
For this final project, I focused a lot on the structure and gameplay (the backbone of the game), while my partner focused on the design aspect and some functionality of the game. My partner personally drew all the characters, which made the game look more unique. The code spans around 600 lines, and in my perspective, it was certainly difficult because of the number of objects (4) and their interaction with each other. I made sure to limit the number in arrays to make the gameplay run more smoothly and cleanly.

The gameplay is slightly different from the initial proposal, but for good reason. We initially planned to have three stages in the game, however that either made the game extremely long or difficult. We also didn’t implement a sound source for this game, because we wanted the users to be able to play the game directly in the browser. Despite these minute changes, the main concept and paradigms are true to the original.

All in all, this project was extremely fun to program. It certainly did take hours and hours to get it right, but in the end it all paid off because of the aesthetics and fun gameplay.

Leave a Reply