Your browser does not support the canvas tag.

previous        Show / Hide Source        Download        next
/*
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;
  }
}