/* Siege By: Christian Pamaong Defend the core for as long as possible. Controls: [Mouse] - Shields the cardinal direction the mouse is closest to. (Shield Block) Mechanics: Any attack will destroy the enemy. Death is achieved after six enemies collide with the core. Difficulty: Enemies move and spawn increasingly faster over time. */ //////Stats int lives = 5; int killcount = 0; int timesurvived = 0; int level = 1; int time = 0; int totaltime = 0; boolean invincible = false; float invincibleTimer = 0; //time = millis(); //////Player /********************************************************************************************* Mouse detection area (Create vector using math instead of built-in function). Point A (Core) = (400,400) Point B (Mouse) = (mouseX,mouseY); vX = (mouseX - 400); vY = (mouseY - 400); Theta = tan^-1(-vY/vX); > ^-1 in this case is just the inverse. *********************************************************************************************/ float coreX = 400; float coreY = 400; float vectorX = mouseX - coreX; float vectorY = mouseY - coreY; float direction = atan(-vectorY / vectorX); ////Shield Block //Shield location (0 - 3, EAST NORTH WEST SOUTH) int shielddir = 0; //Shield Positions (X,Y of center) float shieldWX = 300; float shieldWY = 400; float shieldNX = 400; float shieldNY = 300; float shieldEX = 500; float shieldEY = 400; float shieldSX = 400; float shieldSY = 500; //Shield dimensions (Vertical = 40x80, Horizontal = 80x40) float shieldVW = 40; float shieldVH = 80; float shieldHW = 80; float shieldHH = 40; ////Beam Attack float rechargetime = 10000; //5 second recharge //////Enemies (Cardinal) ////General stats float enemyspd = 3; //Determine enemy speed float enemysize = 40; //Determine both width & height of enemy, as all are equal and are also squares float scalar = 0.8; //When a new level is created, modify stats by this value //Determine distance to place enemy from core when "killed" float enemydismin = 400; float enemydismax = 1200; //Enemy original spawns are randomized (spawn to 500 units away from center) //Left (West) float enemyWX = random(-500, 0); float enemyWY = 400; float enemyWr = random(50,250); int enemyWg = 50; int enemyWb = 50; //Up (North) float enemyNX = 400; float enemyNY = random(-500, 0); float enemyNr = random(50,250); int enemyNg = 50; int enemyNb = 50; //Right (East) float enemyEX = random(800, 1300); float enemyEY = 400; float enemyEr = random(50,250); int enemyEg = 50; int enemyEb = 50; //Down (South) float enemySX = 400; float enemySY = random(800, 1300); float enemySr = random(50,250); int enemySg = 50; int enemySb = 50; //Game Over boolean alive = true; void setup() { size(800, 800); rectMode(CENTER); noStroke(); frameRate(60); } //Triggered every instance of its lifespan void draw() { background(0, 0, 0); game(); } //The entirety of the game called together. The pinnacle of human creation. void game() { if (alive == true) { //Create game functions //backgroundProperties(); preextraShapes(); vectorMouse(); shieldBlock(); core(); shieldCollision(); coreCollision(); enemyMove(); lifeCounter(); postextraShapes(); difficulty(); invincibility(); fill(255); textSize(15); text("Place your mouse in a direction to block",20,750); } else { //End game, create Game Over screen gameEnd(); } } void backgroundProperties() { background(170, 80, 80); //fill(bgr, bgg, bgb); rect(400, 400, 800, 800); } /*********************************************************************************************** DIFFICULTY: Enemies move faster and spawn "more frequently" as the time increases. Difficulty is tracked in milestones of time, classified as levels. Every time 10 seconds pass, the time is reset to zero and stats are modified acccordingly. ************************************************************************************************/ void difficulty() { time += 1; totaltime += 1; if (time > 300) { time = 0; level += 1; enemyspd = enemyspd*1.2; } } /*********************************************************************************************** INVINCIBILITY: When damaged by an enemy, casts a one-second invulnerability barrier, stopping enemy damage. ************************************************************************************************/ void invincibility() { if (invincibleTimer >= 0) { invincibleTimer -= 1; fill(255,255,255,100); rect(coreX,coreY,80 + sin(frameCount/3)*5,80 + sin(frameCount/3)*5); } else { invincible = false; } } /*********************************************************************************************** INVINCIBILITY CHECK: Checks if the player is invulnerable or not when hit. ************************************************************************************************/ void invincibilitycheck() { if (invincible == false) { invincible = true; //Add 1 second invulnerability invincibleTimer += 60; if (lives > 0) { lives -= 1; } else { alive = false; } } } /*********************************************************************************************** MOUSE DETECTION: In order for the game to detect which direction to cast the shield, the game will calaculate the direction vector from the Core to the Mouse in degrees. Mouse detection area (Create vector using math instead of built-in function). Point A (Core) = (400,400) Point B (Mouse) = (mouseX,mouseY); vX = (mouseX - 400); vY = (mouseY - 400); Theta = atan(-vY/vX); ************************************************************************************************/ void vectorMouse() { vectorX = mouseX - coreX; vectorY = mouseY - coreY; direction = atan2(-vectorY, vectorX); fill(255); textSize(20); text("Difficulty: " + level, 20, 50); text(round((totaltime/60)) + " seconds survived so far.", 20, 100); } /******************************************************************************************** CORE: Just creates the core, lol *********************************************************************************************/ void core() { fill(100); rect(coreX, coreY, 100, 100); fill(50, 50, 200); rect(coreX, coreY, 80, 80); //Spooky visual stuff fill(20, 20, 250,100); rect(coreX, coreY, 70 + sin(frameCount*0.1)*5, 70 + sin(frameCount*0.1)*5); rect(coreX, coreY, 50 + sin(frameCount*0.1 + 1)*5, 50 + sin(frameCount*0.1 + 1)*5); rect(coreX, coreY, 30 + sin(frameCount*0.1 + 2)*5, 30 + sin(frameCount*0.1 + 2)*5); } /*********************************************************************************************** SHIELD BLOCK MECHANIC: Game determines which direction mouse is facing from the core and draws the shield in that direction. ************************************************************************************************/ void shieldBlock() { //IF MOUSE IS ON THE EAST SIDE OF CORE if (direction <= 0.785 && direction > 0 || direction <= 0 && direction > -0.785) { fill(100, 100, 100); rect(shieldEX, shieldEY, shieldVW, shieldVH); rect(shieldEX-40, shieldEY, 40, 40); fill(50, 50, 200); rect(shieldEX-40, shieldEY, 80, 20); shielddir = 0; } //MOUSE ON NORTH else if (direction <= 2.36 && direction > 0.785) { fill(100, 100, 100); rect(shieldNX, shieldNY, shieldHW, shieldHH); rect(shieldNX, shieldNY+40, 40, 40); fill(50, 50, 200); rect(shieldNX, shieldNY+40, 20, 80); shielddir = 1; } //MOUSE ON WEST else if (direction <= 3.14 && direction > 2.36 || direction <= -2.36 && direction > -3.14 ) { fill(100, 100, 100); rect(shieldWX, shieldWY, shieldVW, shieldVH); rect(shieldWX+40, shieldWY, 40, 40); fill(50, 50, 200); rect(shieldWX+40, shieldWY, 80, 20); shielddir = 2; } //MOUSE ON SOUTH else if (direction <= -0.785 && direction > -2.36) { fill(100, 100, 100); rect(shieldSX, shieldSY, shieldHW, shieldHH); rect(shieldSX, shieldSY-40, 40, 40); fill(50, 50, 200); rect(shieldSX, shieldSY-40, 20, 80); shielddir = 3; } } /********************************************************************************************* CHECK ENEMY TO SHIELD COLLISION: Enemies only need to be checked for one side, since each enemy and shield function one-dimensionally. Collide with enemy, teleport them away, change their color. *********************************************************************************************/ void shieldCollision() { //East Side Check if (shielddir == 0 && enemyEX-20 < shieldEX+20) { enemyEX += random(enemydismin, enemydismax); enemyEr = random(50,250); } //North Side Check else if (shielddir == 1 && enemyNY+20 > shieldNY-20) { enemyNY -= random(enemydismin, enemydismax); enemyNr = random(50,250); } //West Side Check else if (shielddir == 2 && enemyWX+20 > shieldWX-20) { enemyWX -= random(enemydismin, enemydismax); enemyWr = random(50,250); } //South Side Check else if (shielddir == 3 && enemySY-20 < shieldSY+20) { enemySY += random(enemydismin, enemydismax); enemySr = random(50,250); } } /********************************************************************************************* CHECK ENEMY TO CORE COLLISION: Same as above. Deducts a life if there are any, creates a invunlerability timer and moves enemy away. Change enemy colour. If there are no lives, end the game. *********************************************************************************************/ void coreCollision() { //East Side Check if (enemyEX-20 < coreX+40) { enemyEX += random(enemydismin, enemydismax); invincibilitycheck(); enemyEr = random(50,250); } //North Side Check else if (enemyNY+20 > coreY-40) { enemyNY -= random(enemydismin, enemydismax); invincibilitycheck(); enemyNr = random(50,250); } //West Side Check else if (enemyWX+20 > coreX-40) { enemyWX -= random(enemydismin, enemydismax); invincibilitycheck(); enemyWr = random(50,250); } else if (enemySY-20 < coreY+40) { enemySY += random(enemydismin, enemydismax); invincibilitycheck(); enemySr = random(50,250); } } /********************************************************************************************* ENEMY MECHANICS: Draw enemies. Enemies move one-dimensionally based on which cardinal direction they are on. *********************************************************************************************/ void enemyMove() { fill(enemyEr,enemyEg,enemyEb); //East Side Enemy enemyEX -= enemyspd; rect(enemyEX, enemyEY, enemysize, enemysize); fill(0); rect(enemyEX-10,enemyEY-10,4,8); rect(enemyEX-10,enemyEY+10,4,8); //North fill(enemyNr,enemyNg,enemyNb); enemyNY += enemyspd; rect(enemyNX, enemyNY, enemysize, enemysize); fill(0); rect(enemyNX-10,enemyNY+10,8,4); rect(enemyNX+10,enemyNY+10,8,4); //West fill(enemyWr,enemyWg,enemyWb); enemyWX += enemyspd; rect(enemyWX, enemyWY, enemysize, enemysize); fill(0); rect(enemyWX+10,enemyWY-10,4,8); rect(enemyWX+10,enemyWY+10,4,8); //South fill(enemySr,enemySg,enemySb); enemySY -= enemyspd; rect(enemySX, enemySY, enemysize, enemysize); fill(0); rect(enemySX-10,enemySY-10,8,4); rect(enemySX+10,enemySY-10,8,4); } /******************************************************************************************** LIFE COUNTER: Visually displays lives in the top right corner. At zero lives you are at death's door and if you are hit once more afterward: thats ggwp. *********************************************************************************************/ void lifeCounter() { //Create gray backplate fill(100, 100, 100); rect(670, 50, 200, 40); //Replaces vacant lives with red for (int i = 1; i <= 5; i++) { fill(200, 50, 50); rect(550 + i*40, 50, 20, 20); } //Display current lives as green squares for (int i = 1; i <= lives; i++) { fill(50, 200 + sin(frameCount*0.1)*20, 50); rect(550 + i*40, 50, 20, 20); } if (lives == 0) { for (int i = 1; i <= 5; i++) { fill(250 + sin(frameCount*0.2)*40, 50, 50); rect(550 + i*40, 50, 20, 20); } } } /******************************************************************************************** EXTRA VISUALS: Other shapes. One function sets them before other functions to display behind, and vise versa. *********************************************************************************************/ void preextraShapes() { //Path to core fill(25); rect(200,400,320,40); rect(400,200,40,320); rect(600,400,320,40); rect(400,600,40,320); //Under the Shield fill(35); rect(shieldEX, shieldEY, shieldVW, shieldVH); rect(shieldEX-40, shieldEY, 40, 40); fill(20); rect(shieldEX-40, shieldEY, 80, 20); fill(35); rect(shieldNX, shieldNY, shieldHW, shieldHH); rect(shieldNX, shieldNY+40, 40, 40); fill(20); rect(shieldNX, shieldNY+40, 20, 80); fill(35); rect(shieldWX, shieldWY, shieldVW, shieldVH); rect(shieldWX+40, shieldWY, 40, 40); fill(20); rect(shieldWX+40, shieldWY, 80, 20); fill(35); rect(shieldSX, shieldSY, shieldHW, shieldHH); rect(shieldSX, shieldSY-40, 40, 40); fill(20); rect(shieldSX, shieldSY-40, 20, 80); //Cornerstones fill(100); rect(coreX-40,coreY-40,60,60); rect(coreX+40,coreY-40,60,60); rect(coreX-40,coreY+40,60,60); rect(coreX+40,coreY+40,60,60); //Inner Cornerstones fill(50); rect(coreX-40,coreY-40,40,40); rect(coreX+40,coreY-40,40,40); rect(coreX-40,coreY+40,40,40); rect(coreX+40,coreY+40,40,40); } void postextraShapes() { //4 rectangles that spawn the enemies fill(50); rect(20, 400, 40, 80); rect(400, 20, 80, 40); rect(780, 400, 40, 80); rect(400, 780, 80, 40); } /******************************************************************************************** GAME END: Create end game screen when player loses all lives. *********************************************************************************************/ void gameEnd() { fill(0); rect(400, 400, 800, 800); fill(255); textSize(20); text("You were unable to survive the onslaught.", 50, 300); text("You lasted " + totaltime/60 + " seconds.", 50, 400); text("Press any key to retry.", 50, 500); //Press to restart gameRestart(); } /******************************************************************************************** RESTART GAME: Press any key to restart the game after dying. *********************************************************************************************/ void gameRestart() { if (keyPressed == true) { level = 1; time = 0; totaltime = 0; lives = 5; enemyspd = 3; enemyWX = random(-500, 0); enemyWY = 400; enemyNX = 400; enemyNY = random(-500, 0); enemyEX = random(800, 1300); enemyEY = 400; enemySX = 400; enemySY = random(800, 1300); alive = true; } }