Programming project

Programming for designers

A five-leveled shooting game controlled by mouse movement and click programmed in Processing.
Team member
Regitze Breddal Puck
A five-leveled shooting game controlled by mouse movement and click programmed in Processing.
To work in interdisciplinary teams of designers and software developers, I have come to realize the importance and benefits of designers having a basic knowledge of programming. On one hand, it creates an understanding of the difficulties of implementing different features. On the other hand, it allows designers to participate at an early stage in the development. In turn, ease the communication between the two fields.
A glimpse into the code
During autumn 2018, I attended the course Programming for Designers at the IT University of Copenhagen. In this course, I developed basic programming skills in Processing and Java. For my individual exam project, I developed a five-leveled shooting game controlled by mouse movement and click. Check out the code below:
/*
Program title: The Halloween Game
Program description: A five-levelled shooting game controlled by mouse movement and click
By Regitze Breddal Puck
*/

/* GRAPHICS */
//Landscape
PImage gravestoneImg; //sprite sheet of gravestones
PImage cloudSmallImg, cloudMediumImg, cloudLargeImg; //images of clouds
PImage treeImg, treesImg; //sprite sheets of trees
PImage moonImg; //sprite sheet of moons

//Enemies
PImage batImg; //sprite sheet of bat
PImage crowImg; //sprite sheet of crow
PImage ghostImg; //image of ghost
PImage pumpkinImg, pumpkinShotImg; //image of pumpkins
PImage werewolfImg; //sprite sheet of werewolf
PImage boneImg; //image of bone

//Hero
PImage heroImg; //sprite sheet of hero
PImage projectileImg; //image of hero's projectile
PImage heartImg; //sprite sheet of hearts
PImage heartWithWingsImg; //sprite sheet of power up hearts

/* TYPOGRAPHY */
PFont headerText;
PFont menuText;
PFont bodyText;
PFont scoreText;

/* MENU */
String currentMenu;
MenuScreen menu;

/*
main menu = "MAIN MENU"
level completed screen = "LEVEL COMPLETED"
game over screen = "GAME OVER"
new highscore screen = "NEW HIGHSCORE"
highscore screen = "HIGHSCORE LIST"
*/

/* GAME LOGIC */
Level level;
int currentLevel = 1;

int health = 3;
int maxHealth = 3; //health cannot exceed three lives

int score = 0;
Table highscoreList;

String playerName = "";

int timeSinceTransition;

boolean wasKeyTyped = false;

void setup() {
 size(1000, 700);

 /* MAIN MENU */
 menu = new MenuScreen();
 currentMenu = "MAIN MENU";

 /* GRAPHICS */
 //Landscape
 //loading sprite sheet of gravestones
 //original source of gravestone: Maciej Swierczek from the Noun Project
 //original source of blood: Alex Muravev from the Noun Project
 //edited by: Regitze Breddal Puck
 gravestoneImg = loadImage("graphics/landscape/gravestone_sprite.png");

 //loading images of clouds
 cloudSmallImg = loadImage("graphics/landscape/cloud_small.png");
 cloudMediumImg = loadImage("graphics/landscape/cloud_medium.png");
 cloudLargeImg = loadImage("graphics/landscape/cloud_large.png");

 //loading images of trees
 //original source of leafs: evi1000 from the Noun Project
 //original source of tree trunk: parkjisum from the Noun Project
 //edited by: Regitze Breddal Puck
 treeImg = loadImage("graphics/landscape/tree_sprite.png");
 treesImg = loadImage("graphics/landscape/trees_sprite.png");

 //loading sprite sheet of moons
 //original source: MarkieAnn Packer from the Noun Project
 //edited by: Regitze Breddal Puck
 moonImg = loadImage("graphics/landscape/moon_sprite.png");

 //Enemies
 //loading sprite sheet of bat
 //original source: Store Black from the Noun Project
 //edited by: Regitze Breddal Puck
 batImg = loadImage("graphics/enemies/bat_sprite.png");

 //sprite images of crow
 //original source: Bakunetsu Kaito from the Noun Project
 //edited by: Regitze Breddal Puck
 crowImg = loadImage("graphics/enemies/crow_sprite.png");

 //loading image of ghost
 //original source: sobinsergey from the Noun Project
 //edited by: Regitze Breddal Puck
 ghostImg = loadImage("graphics/enemies/ghost.png");

 //loading images of pumpkins
 //original source: chiccabubble from the Noun Project
 //edited by: Regitze Breddal Puck
 pumpkinImg = loadImage("graphics/enemies/pumpkin.png");
 pumpkinShotImg = loadImage("graphics/enemies/mini_pumpkin.png");

 //loading sprite sheet of werewolf
 //original source: parkjisun from the Noun Project
 //edited by: Regitze Breddal Puck
 werewolfImg = loadImage("graphics/enemies/boss_sprite.png");

 //image sheet of bone
 //original source: Lucas Helle from the Noun Project
 //edited by: Regitze Breddal Puck
 boneImg = loadImage("graphics/enemies/bone.png");

 //Hero
 //loading sprite of hero
 //original source of broomstick: flamingo from the Noun Project
 //edited by: Regitze Breddal Puck
 heroImg = loadImage("graphics/hero/hero_sprite.png");

 //loading image of projectile
 projectileImg = loadImage("graphics/hero/projectile.png");

 //loading sprite of lives
 //original source hearts: shashank singh from the Noun Project
 //edited by: Regitze Breddal Puck
 heartImg = loadImage("graphics/hero/heart_sprite.png");

 //original source of wings: Valeriy, RU from the Noun Project
 //edited by: Regitze Breddal Puck
 heartWithWingsImg = loadImage("graphics/hero/heart_wings_sprite.png");
 
/* TYPOGRAPHY */
 headerText = createFont("fonts/scary_halloween_font.ttf", 60);
 bodyText = createFont("fonts/halloween_too.ttf", 40);
 scoreText = createFont("fonts/halloween_too.ttf", 35);

 /* HIGHSCORE */
 highscoreList = loadTable("texts/score.csv", "header");
}

void draw() {
 //if in a menu
 if (!currentMenu.equals("PLAY")) { //if game is NOT playing
   menu.display();
 }

 //if playing
 if (currentMenu.equals("PLAY")) {

   //if level does not exist, create a new level
   if (level == null) {
     level = new Level();
   }    level.update();

   //if level is completed, change screen to "LEVEL COMPLETED"
   if (level.isLevelCompeled()) {
     currentMenu = "LEVEL COMPLETED";
     timeSinceTransition = millis();
   }
 }

 //if in level completed screen transition
 if (currentMenu.equals("LEVEL COMPLETED") && millis() - timeSinceTransition > 3000) {
   if (currentLevel < 5) {
     currentMenu = "PLAY";
     currentLevel++; //increase level
   } else {
     currentMenu = "GAME OVER";
   }
   
   level = null; //reset level
 }

 //if hero dies during play, change screen to "GAME OVER"
 if (currentMenu.equals("PLAY") && !isHeroAlive()) {
   timeSinceTransition = millis();
   currentMenu = "GAME OVER";
   level = null; //reset level
 }

 //change from "GAME OVER" to "NEW HIGHSCORE" screen
 if (currentMenu.equals("GAME OVER") && millis() - timeSinceTransition > 3000) {    

//checking if player's score is higher or equal to any top 10 scores AND higher than 0
   for (int i = 0; i < 10; i++) {
     if (highscoreList.getRow(i).getInt("score") <= score && score > 0) {
       currentMenu = "NEW HIGHSCORE"; //change screen to "NEW HIGHSCORE"
       break; //exit loop
     } else if (i == 9) { //if player's score is not higher than the lowest score, then continue to highscore list
       currentMenu = "HIGHSCORE LIST"; //change screen to "HIGHSCORE LIST"
     }
   }
 }
}

boolean isHeroAlive() {
 if (health > 0) {
   return true;
 } else {
   return false;
 }
}

void heroHit() {
 health--;
}

void keyTyped() {
 if (currentMenu.equals("NEW HIGHSCORE")) { //characters can only be entered when at "NEW HIGHSCORE" screen
   if (key == RETURN || key == ENTER) { //checking for return key on macOS and Windows
     highscoreList.setColumnType("score", Table.INT); //set score column to integer values. Resource: [online] https://forum.processing.org/two/discussion/4721/table-add-colum
     TableRow newEntry = highscoreList.addRow(); //creating a new row. Resource: [online] https://processing.org/reference/Table_addRow_.html
     newEntry.setString("name", playerName); //insert name to new highscore entry
     newEntry.setInt("score", score); //insert score to new highscore entry
     highscoreList.sortReverse("score"); //sort rows from highest to lowest score. Resource: [online] https://github.com/processing/processing-docs/issues/402
     saveTable(highscoreList, "data/texts/score.csv"); //save updated highscore list
     currentMenu = "HIGHSCORE LIST"; //exit to updated highscore list
     playerName = ""; //resetting next player name to blank
   }    if (playerName.length() <= 10) { //player name limited to 10 characters
     if ((key >= 65 && key <= 90) || (key >= 97 && key <= 125)) { //check is key value is within ASCII A-Z or a-z
       playerName += key;
     }

     if ((key == DELETE || key == BACKSPACE) && playerName.length() > 0) { //check is key value is delete and backspace in ASCII
       playerName = playerName.substring(0, playerName.length() - 1); //replace player name with a substring of the name with one less character. Resource: [online] https://forum.processing.org/two/discussion/8317/delete-and-backspace-keys
     }
   }
 }
}
class Bat extends Enemy {
 int timeSinceLastFlap;
 boolean flap = true;
 PImage bat;

 Bat() {
   super();
   w = 69;
   h = 50;
   xPos = width + w;
   yPos = int(random(50, 500)); //random distance over ground
   speed = 7;
   rewardPoints = 10;
   setHitBox();
   lives = 2;
   timeSinceLastFlap = millis();
 }

 void update() {
   //update by using the super class Enemy's update method
   super.update();

   //update flap
   flap();

   //update bat image
   if (flap == false) {
     bat = batImg.get(0, 0, w, h);
   } else {
     bat = batImg.get(w, 0, w, h);
   }    image(bat, xPos, yPos);
 }

 void flap() {
   if (millis() - timeSinceLastFlap > 200) { //using time fix to switch between flaps
     flap = !flap; //switching flap between true and false
     timeSinceLastFlap = millis(); //reset time since last flap
   }
 }
}
class Bone extends Enemy {
 int xSpeed;
 int ySpeed;
 PImage bone;
 float angle;

 Bone(int werewolfXPos, int werewolfYPos) {
   super();
   w = 17; //width
   h = 44; //height
   this.xPos += werewolfXPos;
   this.yPos += werewolfYPos;
   collisionRadius = 35;
   lives = 9999; //cannot die by hero's projectiles
   xSpeed = 5;
   ySpeed = int(random(-7, 3));
   bone = boneImg;
 }

 void update() {
   collisionX = xPos+w/2;
   collisionY = yPos+h/2;

   rotateBone();
   move();
 }
 
 //overwrite the super class Enemy's move method
 void move() {
   xPos -= xSpeed*1.5;
   yPos -= ySpeed;
 }

 void rotateBone() {
   pushMatrix();
   translate(xPos, yPos);
   rotate(angle);
   image(bone, -w/2, -h/2);
   angle += 0.3;
   popMatrix();
 }
}
class Cloud {
 float xPos; //x position
 int yPos; //y position
 int w; //width
 int h; //height
 int cloudSize;
 float speed;
 PImage cloudImg;

 Cloud(int x) {
   xPos = width - int(random(width));
   yPos = int(random(25, 350)); //random distance over ground

   //small cloud
   if (x == 1) {
     cloudImg = cloudSmallImg;
     cloudSize = 117;
     speed = random(2, 3);
   }

   //medium cloud
   if (x == 2) {
     cloudImg = cloudMediumImg;
     cloudSize = 153;
     speed = random(3, 4);
   }

   //large cloud
   if (x == 3) {
     cloudImg = cloudLargeImg;
     cloudSize = 208;
     speed = random(4, 5);
   }
 }

 void display () {
   image(cloudImg, xPos, yPos);
 }

 void move() {
   xPos -= (speed * 0.2);    if (xPos + cloudSize < 0) {
     xPos = width + random(200); //looping the clouds
     yPos = int(random(10, 100)); //random distance over ground
   }
 }
}
class Crow extends Enemy {
 int timeSinceLastFlap;
 boolean flap = true;
 PImage crow;

 Crow() {
   super();
   w = 62;
   h = 119;
   xPos = width + w;
   yPos = int(random(50, 500)); //random distance over ground
   speed = 7;
   rewardPoints = 5;
   setHitBox();
   lives = 1;
   timeSinceLastFlap = millis();
 }

 void update() {
   //update by using the super class Enemy's update method
   super.update();

   //update flap
   flap();

   //update crow image
   if (flap == false) {
     crow = crowImg.get(0, 0, w, h);
   } else {
     crow = crowImg.get(w, 0, w, h);
   }
   image(crow, xPos, yPos);
 }

 //overwrite the super class Enemy's setHitBox method
 void setHitBox() {
   collisionRadius = 25;
 }  void flap() {
   if (millis() - timeSinceLastFlap > 250) { //using time fix to switch between flaps
     flap = !flap; //switching flap between true and false
     timeSinceLastFlap = millis(); //reset time since last flap
   }
 }
}
abstract class Enemy { //class fields and methods inherited by all enemy types. Resource: [online] https://processing.org/examples/inheritance.html
 int xPos; //x position
 int yPos; //y position
 int w; //width
 int h; //height
 int collisionRadius;
 int collisionX;
 int collisionY;
 int rewardPoints;
 int lives;
 float speed;

 Enemy() {
 }  void update() {
   //update position of enemies
   move();

   //update collision position
   collisionX = xPos + w/2;
   collisionY = yPos + h/2;
 }

 void setHitBox() {
   //set size for hit box circle
   if (this.w > this.h) {
     collisionRadius = this.h/2;
   } else {
     collisionRadius = this.w/2;
   }
 }

 void move() {
   xPos -= speed;
 }  boolean isAlive() {
   if (lives > 0) {
     return true;
   } else {
     return false;
   }
 }

 void hit() {
   lives--;
 }

 //collision detection
 boolean isColliding(int projectileX, int projectileY, int projectileRadius) {
   float distance = dist(projectileX, projectileY, collisionX, collisionY);
   if (distance < projectileRadius + collisionRadius) {
     return true;
   } else {
     return false;
   }
 }

 int getCollisionX() {
   return collisionX;
 }

 int getCollisionY() {
   return collisionY;
 }

 int getCollisionRadius() {
   return collisionRadius;
 }
}
class Explosion {
 Spark[] sparks = new Spark[10];
 float xPos;
 float yPos;
 boolean active;

 Explosion(float x, float y) {
   strokeWeight(7);
   for (int i = 0; i < sparks.length; i++) {
     sparks[i] = new Spark(x, y);
   }
   active = true; //true per default
 }

 void update() {
   for (int i = 0; i < sparks.length; i++) {
     sparks[i].update();

     active = false; //assume all sparks are transparent
     if (sparks[i].t > 0) { //check if any sparks is still not transparent
       active = true; //if any sparks are still visible, keep active true
     }
   }
 }
}
class Ghost extends Enemy {
 int offset;
 float fadeSpeed;
 float scalar = 100;
 float sinSpeed = 75.0;
 boolean invisible = false;
 PImage ghost;

 Ghost() {
   super();
   w = 154;
   h = 157;
   xPos = width + w;
   speed = 3;
   setHitBox();
   rewardPoints = 20;
   lives = 5;
   offset = int(random(h/2, 450));
 }

 void update() {
   //update by using the super class Enemy's update method
   super.update();

   ghost = ghostImg.get(0, 0, w, h);
   tint(255, 255 - int(sin(xPos/sinSpeed) * 500)); //creating transparency depending on x position of ghost
   image(ghost, xPos, yPos);
   tint(255, 255); //reset transparency
 }

 void move() {
   xPos -= speed;
   //moving the ghost on a sinus curve
   yPos = offset + int(sin(xPos/sinSpeed) * scalar); //resource: [book] Make: Getting Started with Processing by Casey Reas & Ben Fry, second edition
 }
}
class HeartPowerUp {
 int w = 60;
 int h = 26;
 int collisionX;
 int collisionY;
 int collisionRadius = h/2;
 int xPos = width + w;
 int yPos = int(random(50, 500));
 int speed = 8;
 int timeSinceLastFlap;
 PImage heartWithWings;
 boolean flap = true;

 HeartPowerUp() {
   timeSinceLastFlap = millis();
 }

 void update() {
   //update position of heart
   move();

   //update collision position
   collisionX = xPos + w/2;
   collisionY = yPos + h/2;

   //update flap
   flap();

   //update heart image
   if (flap) {
     heartWithWings = heartWithWingsImg.get(0, 0, w, h);
   } else {
     heartWithWings = heartWithWingsImg.get(w, 0, w, h);
   }
   image(heartWithWings, xPos, yPos);
 }

 void flap() {
   if (millis() - timeSinceLastFlap > 150) {
     flap = !flap; //switching flap between true and false
     timeSinceLastFlap = millis();
   }
 }

 int getCollisionX() {
   return collisionX;
 }

 int getCollisionY() {
   return collisionY;
 }

 int getCollisionRadius() {
   return collisionRadius;
 }

 void move() {
   xPos -= speed;
 }
}
class Hero {
 ArrayList<Projectile> projectiles = new ArrayList<Projectile>();
 int xPos;
 int yPos;
 int w = 189;
 int h = 144;
 int timeSinceLastShot;
 int hitBoxW = 50;
 int hitBoxH = 144;
 int hitBoxX;
 int hitBoxY;
 PImage hero;
 boolean isShooting;
 boolean heroAlive = true;

 Hero() {
   timeSinceLastShot = millis();
 }

 void update() {
   xPos = constrain(mouseX, w/2, width-550);
   yPos = constrain(mouseY, h/2, height-100);

   if (mousePressed) { //shooting posture
     isShooting = true;
     hero = heroImg.get(0, 0, w, h);
     if ((millis() - timeSinceLastShot) > 300) { //using time fix to delay shootings
       projectiles.add(new Projectile(xPos, yPos));
       timeSinceLastShot = millis(); //reset time since last shot
     }
   } else { //default posture
     isShooting = false;
     hero = heroImg.get(0+w, 0, w, h);
   }

   imageMode(CENTER); //center hero image on mouse cursor
   image(hero, xPos, yPos);
   imageMode(CORNER); //reset to default

   //resource: [online] https://natureofcode.com/book/chapter-4-particle-systems/#chapter04_section3
   //loop through hero's projectiles starting from the end of the array
   for (int i = projectiles.size() - 1; i >= 0; i--) {
     Projectile p = projectiles.get(i);
     p.update();
     if (p.xPos > width + p.w) {
       projectiles.remove(i);
     }
   }
 }

 //collision detection
 //resource: [online] http://www.jeffreythompson.org/collision-detection/circle-rect.php
 boolean isColliding(float enemyCollisionX, float enemyCollisionY, float enemyCollisionRadius) {
   hitBoxX = xPos - hitBoxW/2;
   hitBoxY = yPos - hitBoxH/2;

   //temporary variables to set edges for testing
   float testX = enemyCollisionX;
   float testY = enemyCollisionY;

   //which edge is closest?
   if (enemyCollisionX < hitBoxX) {
     testX = hitBoxX; // test left edge
   } else if (enemyCollisionX > hitBoxX + hitBoxW) {
     testX = hitBoxX + hitBoxW; // right edge
   }
   if (enemyCollisionY < hitBoxY) {
     testY = hitBoxY; // top edge
   } else if (enemyCollisionY > hitBoxY + hitBoxH) {
     testY = hitBoxY + hitBoxH; // bottom edge
   }    //get distance from closest edges
   float distX = enemyCollisionX - testX;
   float distY = enemyCollisionY - testY;
   float distance = sqrt((distX * distX) + (distY * distY));

   //if the distance is less than the radius, then collide
   if (distance <= enemyCollisionRadius) {
     return true;
   }
   return false;
 }

 void hit() {
   hero = heroImg.get(w*2, 0, w, h); //blink red if hero is hit
   imageMode(CENTER); //center hero image on mouse cursor
   image(hero, xPos, yPos);
   imageMode(CORNER); //resetting to default
 }
}
class Landscape {

 //Sky (background gradient)
 color topColor;
 color buttomColor;

 Cloud[] clouds;

 Tree[] trees;

 //Ground
 color groundColor;

 //Moon
 PImage moon;
 int moonX = 853;
 int moonY = 30;
 int moonH = 97;
 int moonW = 97;

 //Gravestone
 PImage gravestone;
 float gravestoneX = width - int(random(width));
 int gravestoneY = height - 140;
 int gravestoneW = 136;
 int gravestoneH = 140;
 float gravestoneSpeed = 5.5;
 float timeSinceGameStarted;

 Landscape() {
   timeSinceGameStarted = millis();

   //Level 1
   if (currentLevel == 1) {
     topColor = color(116, 182, 215); //blue
     buttomColor = color(253, 241, 235); //light beige
     groundColor = color(81, 135, 74); //green
     moon = moonImg.get(0, 0, moonW, moonH);
     clouds = new Cloud[2];
   }

   //Level 2
   if (currentLevel == 2) {
     topColor = color(215, 177, 116); //dark beige
     buttomColor = color(253, 241, 235); //light beige
     groundColor = color(175, 140, 70); //brown
     moon = moonImg.get(moonW, 0, moonW, moonH);
     clouds = new Cloud[3];
   }

   //Level 3
   if (currentLevel == 3) {
     topColor = color(160, 92, 39); //dark brown
     buttomColor = color(223, 182, 141); //light brown
     groundColor = color(175, 140, 70); //brown
     moon = moonImg.get((moonW*2), 0, moonW, moonH);
     clouds = new Cloud[4];
   }

   //Level 4
   if (currentLevel == 4) {
     topColor = color(100); //grey
     buttomColor = color(200); //light grey
     groundColor = color(108, 91, 58); //dark brown
     moon = moonImg.get((moonW*3), 0, moonW, moonH);
     clouds = new Cloud[5];
     gravestone = gravestoneImg.get(0, 0, gravestoneW, gravestoneH);
   }

   //Level 5
   if (currentLevel == 5) {
     topColor = color(50); //dark grey
     buttomColor = color(100); //grey
     groundColor = color(91, 75, 43); //dark brown
     moon = moonImg.get((moonW*4), 0, moonW, moonH);
     gravestone = gravestoneImg.get((0 + gravestoneW), 0, gravestoneW, gravestoneH);
     clouds = new Cloud[5];
   }

   //All levels

   //create clouds
   for (int i = 0; i < clouds.length; i++) {
     int x = (i%3)+1; //use i to get number between 1 to 3
     clouds[i] = new Cloud(x); //use number 1 to 3 to create clouds in three sizes
   }

   //create trees
   trees = new Tree[2];
   trees[0] = new Tree(1);
   trees[1] = new Tree(2);
 }

 void display() {
   //update gradient sky
   drawBackgroundGradient(topColor, buttomColor);

   //update ground
   noStroke();
   fill(groundColor);
   rect(0, 660, 1000, 40);

   //update moon
   image(moon, moonX, moonY);

   //update clouds
   for (int i = 0; i < clouds.length; i++) {
     clouds[i].move(); //clouds still move when the rest of the landscape stand still
     clouds[i].display();
   }

   //update trees
   trees[0].display();
   trees[1].display();

   //update help text
   if (currentLevel == 1) {
     //using time fix to show text only in a limited time
     if ((millis() - timeSinceGameStarted) < 2000) {
       fill(0);
       textFont(bodyText);
       textAlign(CENTER);
       text("Click mouse to shoot", 500, 262);
     }
     if ((millis() - timeSinceGameStarted) > 2000 && (millis() - timeSinceGameStarted) < 4000) {
       fill(0);
       textFont(bodyText);
       textAlign(CENTER);
       text("Avoid enemies", 500, 262);
     }
     if ((millis() - timeSinceGameStarted) > 4000 && (millis() - timeSinceGameStarted) < 6000) {
       fill(0);
       textFont(bodyText);
       textAlign(CENTER);
       text("Good luck!", 500, 262);
     }
   } else {//Countdown
     if ((millis() - timeSinceGameStarted) < 2000) {
       fill(0);
       textFont(bodyText);
       textAlign(CENTER);
       text("Get ready", 500, 262);
     }
     if ((millis() - timeSinceGameStarted) > 2000 && (millis() - timeSinceGameStarted) < 4000) {
       fill(0);
       textFont(bodyText);
       textAlign(CENTER);
       text("Go!", 500, 262);
     }
   }

   //update gravestone
   if (currentLevel == 4 || currentLevel == 5) {
     displayGravestone();
   }
 }

 //gradient for background colour
 void drawBackgroundGradient(color top, color buttom) { //resource: [online] https://processing.org/examples/lineargradient.html
   noFill();
   for (int i = 0; i <= height; i++) { //top to bottom gradient
     float gradientPosition = map(i, 0, height, 0, 0.8);
     color colour = lerpColor(top, buttom, gradientPosition);
     stroke(colour);
     line(0, i, width, i);
   }
 }  void moveGravestone() {
   gravestoneX -= gravestoneSpeed;

   if (gravestoneX + gravestoneW < 0) {
     gravestoneX = width + random(200); //looping the gravestone
   }
 }  void displayGravestone() {
   image(gravestone, gravestoneX, gravestoneY);
 }

 void move() {

   //update clouds
   for (int i = 0; i < clouds.length; i++) {
     clouds[i].move();
   }

   //update trees
   trees[0].move();
   trees[1].move();

   //update gravestone
   if (currentLevel == 4 || currentLevel == 5) {
     moveGravestone();
   }
 }
}
class Level {
 ArrayList<Enemy> enemies = new ArrayList<Enemy>();
 ArrayList<Explosion> particleSystem = new ArrayList<Explosion>();
 Werewolf werewolf;
 Hero hero;
 HeartPowerUp heartPowerUp;
 Landscape landscape;
 PImage heart;
 int heartPosX = 20;
 int heartPosY = 58;
 int heartW = 28;
 int heartH = 26;
 int distBetweenHearts = 10;
 int timeSinceLastBat;
 int timeSinceLastCrow;
 int timeSinceLastGhost;
 int timeSinceLastPumpkin;
 int timeSinceLevelStarted;
 boolean levelCompleted = false;
 boolean heartPowerUpReady = true;

 Level() {
   timeSinceLevelStarted = millis();
   timeSinceLastBat = millis();
   timeSinceLastCrow = millis();
   timeSinceLastGhost = millis();
   timeSinceLastPumpkin = millis();
   this.landscape = new Landscape();
   this.hero = new Hero();
 }

 boolean isLevelCompeled() {
   return levelCompleted;
 }

 void drawHearts() {
   for (int i = 0; i < maxHealth; i++) {
     if (i < health) {
       heart = heartImg.get(0, 0, heartW, heartH); //filled heart
     } else {
       heart = heartImg.get(0 + heartW, 0, heartW, heartH); //empty heart
     }
     image(heart, heartPosX+(i * (heartW + distBetweenHearts)), heartPosY); //drawing hearts
   }
 }

 void drawScore() {
   fill(0);
   textFont(scoreText);
   textAlign(LEFT);
   text("Score: " + score, 20, 43);
 }

 void checkCollision() {    //checking if hero is colliding with an heart for power up
   if (heartPowerUp != null) {
     if (hero.isColliding(heartPowerUp.getCollisionX(), heartPowerUp.getCollisionY(), heartPowerUp.getCollisionRadius())) {
       if (health < maxHealth) { //if hero does not aldreay have max health
         health++;
       }
       heartPowerUp = null; //remove heart power up from level
       heartPowerUpReady = false;
     }
     if (heartPowerUp != null && heartPowerUp.xPos < -heartPowerUp.w) { //if heart exceeds the left side of screen
       heartPowerUp = null; //remove heart power up from level
       heartPowerUpReady = false;
     }
   }

   //reource: [online] https://natureofcode.com/book/chapter-4-particle-systems/#chapter04_section3
   //loop through array of enemies starting from the end of the array
   for (int j = enemies.size() - 1; j >= 0; j--) {
     Enemy e = enemies.get(j);

     //if enemy's position exceeds the left side of screen, then enemy die
     if (e.xPos < -e.w) {
       e.lives = 0;
     }

     //loop through hero's projectiles starting from the end of the array
     for (int i = hero.projectiles.size() - 1; i >= 0; i--) {
       Projectile p = hero.projectiles.get(i);
       //checking if enemy "e" is colliding with a projectile
       if (e.isColliding(p.getCollisionX(), p.getCollisionY(), p.getCollisionRadius())) {
         hero.projectiles.remove(i); //remove projectile from hero's array list of projectiles to optimize performance
         e.hit(); //remove one life from enemy
         if (!e.isAlive()) {
           score += e.rewardPoints;
         }
       }
     }

     //checking if hero is colliding with an enemy
     if (hero.isColliding(e.getCollisionX(), e.getCollisionY(), e.getCollisionRadius())) {
       heroHit();
       e.lives = 0;
       hero.hit();
     }

     //if enemy "e" is dead, remove from array list of enemies
     if (!e.isAlive()) {
       particleSystem.add(new Explosion(e.getCollisionX(), e.getCollisionY()));
       enemies.remove(j); //remove dead enemies to optimize performance
     }
   }
 }

 void createEnemy(Enemy e, int timeSinceLast, int timeMin, int timeMax) {
   int randomValue = int(random(timeMin, timeMax));    if (millis() > randomValue + timeSinceLast) {
     enemies.add(e); //adding a new enemy to array list

     //resource: [online] https://stackoverflow.com/questions/1641619/getting-typeof-in-java
     if (e instanceof Bat) {
       timeSinceLastBat = millis();
     }
     if (e instanceof Crow) {
       timeSinceLastCrow = millis();
     }
     if (e instanceof Ghost) {
       timeSinceLastGhost = millis();
     }
     if (e instanceof Pumpkin) {
       timeSinceLastPumpkin = millis();
     }
   }
 }

 void update() {
   //General for all levels

   //Werewolf
   if (werewolf != null && werewolf.isInPosition()) {
     //stop landscape from moving
   } else {
     landscape.move(); //keep landscape moving
   }

   landscape.display();
   drawHearts();
   drawScore();
   hero.update();
   checkCollision();

   for (Explosion ex : particleSystem) {
     ex.update();
   }

   //resource: [online] https://natureofcode.com/book/chapter-4-particle-systems/#chapter04_section3
   //loop through array of explosions starting from the end of the array
   for (int i = particleSystem.size() - 1; i >= 0; i--) {
     Explosion ex = particleSystem.get(i);
     if (ex.active == false) {
       particleSystem.remove(i); //remove inactive explosions to optimize performance
     }
   }

   //Level 1
   if (currentLevel == 1) {
    //crow
     if (millis() - timeSinceLevelStarted <= 26000) { //stop spawning enemies after 26 seconds
       createEnemy(new Crow(), timeSinceLastCrow, 1000, 2000);
     }

     if (millis() - timeSinceLevelStarted >= 30000) {
       levelCompleted = true;
     }
   }

   //Level 2
   if (currentLevel == 2) {
     if (millis() - timeSinceLevelStarted <= 26000) { //stop spawning enemies after 26 seconds

       //crow
       createEnemy(new Crow(), timeSinceLastCrow, 1000, 1500);

       //pumpkin
       createEnemy(new Pumpkin(), timeSinceLastPumpkin, 3500, 4500);
     }

     if (millis() - timeSinceLevelStarted >= 30000) {
       levelCompleted = true;
     }
   }

   //Level 3
   if (currentLevel == 3) {
     if (millis() - timeSinceLevelStarted <= 26000) { //stop spawning enemies after 26 seconds
       //crow
       createEnemy(new Crow(), timeSinceLastCrow, 1000, 1500);

       //pumpkin
       createEnemy(new Pumpkin(), timeSinceLastPumpkin, 3500, 4500);

       //ghost
       createEnemy(new Ghost(), timeSinceLastGhost, 3500, 4500);
     }
     if (millis() - timeSinceLevelStarted >= 30000) {
       levelCompleted = true;
     }
   }

   //Level 4
   if (currentLevel == 4) {
     if (millis() - timeSinceLevelStarted <= 26000) { //stop spawning enemies after 26 seconds
       //bat
       createEnemy(new Bat(), timeSinceLastBat, 1000, 2500);

       //pumpkin
       createEnemy(new Pumpkin(), timeSinceLastPumpkin, 3500, 5500);

       //ghost
       createEnemy(new Ghost(), timeSinceLastGhost, 3500, 5500);
     }
     if (millis() - timeSinceLevelStarted >= 30000) {
       levelCompleted = true;
     }
   }

   //Level 5
   if (currentLevel == 5) {
     if (millis() - timeSinceLevelStarted <= 25000) { //stop spawning enemies after 25 seconds

       //bat
       createEnemy(new Bat(), timeSinceLastBat, 1000, 2000);

       //pumpkin
       createEnemy(new Pumpkin(), timeSinceLastPumpkin, 3500, 4500);

       //ghost
       createEnemy(new Ghost(), timeSinceLastGhost, 3500, 4500);
     }

     //werewolf
     if (millis() - timeSinceLevelStarted >= 30000) { //spawn werewolf boss after 30 seconds

       //if werewolf does not exist, create one
       if (werewolf == null) {
         werewolf = new Werewolf();
         enemies.add(werewolf);
       }

       //if werewolf does exist
       if (werewolf != null) {

         //throw bone when ready
         if (werewolf.throwBone()) {
           enemies.add(new Bone(werewolf.getThrowX(), werewolf.getThrowY()));
         }

         //level completed when werewolf is dead
         if (!werewolf.isAlive()) {
           levelCompleted = true;
         }
       }
     }
   }

   if (millis() - timeSinceLevelStarted >= 15000 && heartPowerUpReady == true) { //spawn only one power up heart after 15 seconds
     if (heartPowerUp == null) { //if power up heart does not exists, create a new one
       heartPowerUp = new HeartPowerUp();
     } else {
       heartPowerUp.update(); //if a power up heart already exists, update it
     }
   }
   
//for each enemy in the array list enemies, update each enemy
   for (int i = 0; i < enemies.size(); i++) {
     Enemy e = enemies.get(i);
     e.update();
   }
 }
}
class MenuScreen {

 //Background gradient
 color topColor = color(50); //dark grey
 color buttomColor = color(100); //grey

 //Bat
 PImage batOpen;
 PImage batClosed;
 int batW = 69;
 int batH = 50;

 //Trees
 PImage tree, trees;
 int treeW = 268;
 int treeH = 254;
 int treesW = 445;
 int treesH = 311;

 //Gravestone
 PImage gravestone;
 int gravestoneW = 136;
 int gravestoneH = 140;

 //Moon
 PImage moon;
 int moonH = 97;
 int moonW = 97;

 //Start button
 int startButtonX = 455;
 int startButtonY = 340;
 int startButtonW = 89;
 int startButtonH = 33;

 //Highscore button
 int highscoreButtonX = 396;
 int highscoreButtonY = 391;
 int highscoreButtonW = 208;
 int highscoreButtonH = 34;

 //Go to menu button
 int goToMenuButtonX = 412;
 int goToMenuButtonY = 620;
 int goToMenuButtonW = 176;
 int goToMenuButtonH = 35;

 //Highscore text
 int lineHeight = 50;

 MenuScreen() {
 }  //Gradient for background colour
 void drawBackgroundGradient(color top, color buttom) {
//resource: [online] https://processing.org/examples/lineargradient.html
   noFill();
   for (int i = 0; i <= height; i++) { //top to bottom gradient
     float gradientPosition = map(i, 0, height, 0, 0.8);
     color colour = lerpColor(top, buttom, gradientPosition);
     stroke(colour);
     line(0, i, width, i);
   }
 }

 void ground() {
   noStroke();
   fill(91, 75, 43); //dark brown
   rect(0, 660, 1000, 40);
 }

 void moon() {
   moon = moonImg.get((moonW*4), 0, moonW, moonH);
   image(moon, 853, 30);
 }

 void bats() {
   batOpen = batImg.get(0, 0, batW, batH);
   batClosed = batImg.get(batW, 0, batW, batH);
 }

 void drawText(String text) {

   //Header shadow
   fill(0, 0, 0, 125); //transparent black
   textFont(headerText);
   textAlign(CENTER);
   text(text, 501, 301);

   //Header
   fill(156, 2, 8); //dark red
   textFont(headerText);
   textAlign(CENTER);
   text(text, 500, 300);

  //Body text shadow (score text)
   fill(0, 0, 0, 125); //transparent black
   textFont(bodyText);
   textAlign(CENTER);
   text("Total score: " + score, 501, 360);

   fill(156, 2, 8); //dark red
   textFont(bodyText);
   textAlign(CENTER);
   text("Total score: " + score, 500, 359);
 }

 void display() {

   /* COMMON FOR ALL */
   //Gradient Background
   drawBackgroundGradient(topColor, buttomColor);

   /* MAIN MENU */
   if (currentMenu.equals("MAIN MENU")) {

     //Clouds
     image(cloudSmallImg, 235, 71);
     image(cloudMediumImg, 513, 97);
     image(cloudLargeImg, 591, 51);

     //Moon
     moon();

     //Bats
     bats();
     image(batOpen, 444, 113);
     image(batClosed, 656, 210);
     image(batOpen, 842, 113);
     image(batOpen, 842, 231);
     image(batClosed, 784, 292);

     //Ground
     ground();

     //Gravestone
     gravestone = gravestoneImg.get((0 + gravestoneW), 0, gravestoneW, gravestoneH);
     image(gravestone, 213, 560);

     //Trees
     tree = treeImg.get(4*268, 0, treeW, treeH);
     image(tree, 19, 435);
     trees = treesImg.get(4*445, 0, treesW, treesH);
     image(trees, 537, 386);

     //Header shadow
     fill(0, 0, 0, 125); //transparent black
     textFont(headerText);
     textAlign(CENTER);
     text("The Halloween Game", 501, 301);

     //Header
     fill(156, 2, 8); //dark red
     textFont(headerText);
     textAlign(CENTER);
     text("The Halloween Game", 500, 300);

     //Body text shadow
     //start button
     fill(0, 0, 0, 125); //transparent black
     textFont(bodyText);
     textAlign(CENTER);
     text("Start", 501, 360);

     //highscore list button
     fill(0, 0, 0, 125); //transparent black
     textFont(bodyText);
     textAlign(CENTER);
     text("Highscore list", 501, 412);

     //start button
     //if mouse is over button text, highlight text by changing colour
     if ((mouseX > startButtonX && mouseX < startButtonX + startButtonW &&
       mouseY > startButtonY && mouseY < startButtonY + startButtonH)) {
       fill(0); //dark red
       if (mousePressed) {
         currentLevel = 1; //get ready to start a new game
         health = 3;
         score = 0;
         currentMenu = "PLAY";
       }
     } else {
       fill(156, 2, 8); //dark red
     }
     textFont(bodyText);
     textAlign(CENTER);
     text("Start", 500, 359);

     //highscore list button
     //if mouse is over button text, highlight text by changing colour
     if ((mouseX > highscoreButtonX && mouseX < highscoreButtonX + highscoreButtonW &&
       mouseY > highscoreButtonY && mouseY < highscoreButtonY + highscoreButtonH)) {
       fill(0); //dark red
       if (mousePressed) {
         currentMenu = "HIGHSCORE LIST";
       }
     } else {
       fill(156, 2, 8); //dark red
     }

     textFont(bodyText);
     textAlign(CENTER);
     text("Highscore list", 500, 411);
   }

   /* LEVEL COMPLETED SCREEN */
   if (currentMenu.equals("LEVEL COMPLETED")) {

     if (currentLevel >= 5) {
       drawText("Game completed");
     } else {
       drawText("Level " + currentLevel + " completed");
     }
   }

   /* GAME OVER SCREEN */
   if (currentMenu.equals("GAME OVER")) {
     drawText("Game over");
   }

   /* NEW HIGHSCORE SCREEN */
   if (currentMenu.equals("NEW HIGHSCORE")) {

     //if player score is within top 10 scores, present player for name entry screen, before showing highscore list
     //Header shadow
     fill(0, 0, 0, 125); //transparent black
     textFont(headerText);
     textAlign(CENTER);
     text("New highscore", 501, 228);

     //Header
     fill(156, 2, 8); //dark red
     textFont(headerText);
     textAlign(CENTER);
     text("New highscore", 500, 227);

     //Name field shadow
     fill(0, 0, 0, 125); //transparent black
     textFont(bodyText);
     textAlign(CENTER);
     text(playerName, 501, 351);

     //Name field
     fill(0); //black
     textFont(bodyText);
     textAlign(CENTER);
     text(playerName, 501, 350);

     //Body text shadow
     fill(0, 0, 0, 125); //transparent black
     textFont(bodyText);
     textAlign(CENTER);
     text("Type name and press enter", 501, 450);

     //Body text field
     fill(156, 2, 8); //dark red
     textFont(bodyText);
     textAlign(CENTER);
     text("Type name and press enter", 501, 449);
   }

   /* HIGHSCORE LIST SCREEN */
   if (currentMenu.equals("HIGHSCORE LIST")) {

     /* GRAPHICS */

     //Moon
     moon();

     //Bats
     bats();
     image(batOpen, 39, 272);
     image(batClosed, 146, 210);
     image(batOpen, 842, 113);
     image(batOpen, 842, 231);

     //Ground
     ground();

     //Trees
     tree = treeImg.get(4*268, 0, treeW, treeH);
     image(tree, 763, 436);
     trees = treesImg.get(4*445, 0, treesW, treesH);
     image(trees, -105, 386);

     /* TYPOGRAPHY */
     //Header shadow
     fill(0, 0, 0, 125); //transparent black
     textFont(headerText);
     textAlign(CENTER);
     text("Highscore list", 501, 81);

     //Header
     fill(156, 2, 8); //dark red
     textFont(headerText);
     textAlign(CENTER);
     text("Highscore list", 500, 80);

     //Highscore list text shadow
     for (int i = 0; i < 10; i++) {
       fill(0, 0, 0, 125); //transparent black
       textFont(scoreText);
       textAlign(RIGHT);
       text(i+1 + ".", 361, 132 + (i * lineHeight));
       textAlign(LEFT);
       text(highscoreList.getRow(i).getString("name"), 416, 132 + (i * lineHeight));
       textAlign(RIGHT);
       text(highscoreList.getRow(i).getInt("score"), 661, 132 + (i * lineHeight));

       //Highscore list text
       fill(156, 2, 8); //dark red
       textFont(scoreText);
       textAlign(RIGHT);
       text(i+1 + ".", 360, 131 + (i * lineHeight));
       textAlign(LEFT);
       text(highscoreList.getRow(i).getString("name"), 415, 131 + (i * lineHeight));
       textAlign(RIGHT);
       text(highscoreList.getRow(i).getInt("score"), 660, 131 + (i * lineHeight));
     }

     //Go to menu button shadow
     fill(0, 0, 0, 125); //transparent black
     textFont(bodyText);
     textAlign(CENTER);
     text("Go to menu", 501, 656);

     //Go to menu button text
     //if mouse is over button text, highlight text by changing colour
     if ((mouseX > goToMenuButtonX && mouseX < goToMenuButtonX + goToMenuButtonW &&
       mouseY > goToMenuButtonY && mouseY < goToMenuButtonY + goToMenuButtonH)) {
       fill(0); //black colour
       if (mousePressed) {
         currentMenu = "MAIN MENU";
       }
     } else {
       fill(156, 2, 8); //dark red colour
     }

     textFont(bodyText);
     textAlign(CENTER);
     text("Go to menu", 500, 655);
   }
 }
}
class Projectile {
 int xPos = 46; //x position in relation to center of hero
 int yPos = -30; //y position in relation to center of hero
 int w = 15; //width
 int h = 15; //height
 int collisionRadius;
 int collisionX;
 int collisionY;
 float speed = 15;
 PImage projectile;

 Projectile(int heroXPos, int heroYPos) {
   this.xPos += heroXPos;
   this.yPos += heroYPos;
   projectile = projectileImg;

   //setting size for hit box circle
   collisionRadius = h/2;
 }
 
 int getCollisionX() {
   return collisionX;
 }

 int getCollisionY() {
   return collisionY;
 }

 int getCollisionRadius() {
   return collisionRadius;
 }

 void update() {
   xPos += speed;
   collisionX = xPos + collisionRadius;
   collisionY = yPos + collisionRadius;
   image(projectile, xPos, yPos);
 }
}
class Pumpkin extends Enemy {
 int timeSinceLastShot;
 PImage pumpkin;

 Pumpkin() {
   super();
   w = 78;
   h = 80;
   xPos = width+(width/2);
   yPos = height - h-10;
   speed = 4.5;
   rewardPoints = 15;
   setHitBox();
   lives = 1;
   timeSinceLastShot = millis();
 }

 void update() {
   //update by using the super class Enemy's update method
   super.update();
   if ((millis() - timeSinceLastShot) > int(random(2000, 3000))) { //using time fix to delay shootings
     level.enemies.add(new PumpkinShot(xPos, yPos));
     timeSinceLastShot = millis(); //reset time since last shot
   }
   
   pumpkin = pumpkinImg.get(0, 0, w, h);
   image(pumpkin, xPos, yPos);
 }
}
class PumpkinShot extends Enemy {
 PImage pumpkinShot;  PumpkinShot(int pumpkinXPos, int pumpkinYPos) {
   super();
   
   this.xPos += pumpkinXPos;
   this.yPos += pumpkinYPos;
   w = 34; //width
   h = 34; //height
   lives = 1;
   setHitBox();
   speed = int(random(5, 7));
   pumpkinShot = pumpkinShotImg;
   rewardPoints = 15;
 }

 void move() {
   xPos -= speed*1.5;
   yPos -= speed;
 }

 void update() {
   //update by using the super class Enemy's update method
   super.update();
   
   collisionX = xPos + collisionRadius;
   collisionY = yPos + collisionRadius;
   image(pumpkinShot, xPos, yPos);
 }
}
class Spark {
 float xPos;
 float yPos;
 float yPosStart;
 float velocityX = random(-1, 1);
 float velocityY = random(-2, 0);
 int r = int(random(131, 239)); //red
 int g = int(random(49, 97)); //green
 int b = int(random(143, 250)); //blue
 int t; //transparency
 color c = color(r, g, b, t); //random purple colour
 float gravity = 0.1;

 Spark(float x, float y) {
   xPos = x;
   yPos = y;
   yPosStart = y;
 }  void update() {
   t = 100 + ((int(yPosStart) - int(yPos))*2);
   c = color(r, g, b, t);
   
   //display
   stroke(c);
   point(xPos, yPos);
   
   //move
   xPos += velocityX;
   yPos += velocityY;
   velocityY += gravity;
 }
}
class Tree {
 float xPos; //x position
 int yPos; //y position
 int w; //width
 int h; //height
 float speed;
 PImage tree;

 Tree(int x) {

   //One tree
   if (x == 1) {
     w = 268;
     h = 254;
     speed = 5;
     tree = treeImg.get((currentLevel-1)*w, 0, w, h); //sprite sheet with one tree
     xPos = width - int(random(750, 1000));
   }

   //Two trees
   if (x == 2) {
     w = 445;
     h = 311;
     speed = 4.5;
     tree = treesImg.get((currentLevel-1)*w, 0, w, h); //sprite sheet with two trees
     xPos = width - int(random(300, 600));
   }    yPos = height - h;
 }

 void move() {
   xPos -= speed;    if (xPos + w < 0) {
     xPos = width + random(500); //looping the trees
   }
 }
 
   void display () {
   image(tree, xPos, yPos);
 }
}
class Werewolf extends Enemy {
 int xPos; //x position
 int yPos; //y position
 int w; //width
 int h; //height
 int hitBoxX = 69;
 int hitBoxY = 27;
 int hitBoxW = 115;
 int hitBoxH = 222;
 int throwX = 29;
 int throwY = 8;
 int distanceFromScreen;
 int gravity = -1;
 int jumpVelocity = 0;
 int timeSinceLastJump;
 int whenToJumpNext = 2000;
 PImage werewolf;
 float speed;
 float timeSincesThrow;
 boolean isInPosition;
 boolean readyToThrow;
 boolean boneInHand;
 int maxLives = 30;

 Werewolf() {
   super();
   w = 196;
   h = 257;
   xPos = width;
   yPos = height - h;
   rewardPoints = 100;
   distanceFromScreen = 300;
   speed = 4.5;
   setHitBox();
   isInPosition = false;
   lives = maxLives;
 }

 boolean isInPosition() {
   return isInPosition;
 }

 //collision detection
 boolean isColliding(int projectileX, int projectileY, int projectileRadius) {
   if (projectileX >= xPos + hitBoxX && projectileX <= xPos + hitBoxX + hitBoxW) {
     if (projectileY >= yPos + hitBoxY && projectileY <= yPos + hitBoxY + hitBoxH) {
       return true;
     }
   }
   return false;
 }

 boolean throwBone() {
   if (boneInHand && readyToThrow) {
     readyToThrow = false;
     boneInHand = false;
     return true;
   } else {
     return false;
   }
 }

 int getThrowX() {
   return xPos + throwX;
 }

 int getThrowY() {
   return yPos + throwY;
 }

 void update() {
   //update by using the super class Enemy's update method
   super.update();    //update center of explosion
   collisionX = 110 + xPos;
   collisionY = 97 + yPos;    if (xPos > width - distanceFromScreen) { //moving werewolf into the position
     xPos -= speed;
   } else if (!isInPosition) {
     xPos = width - distanceFromScreen; //stopping werewolf at a fixed position
     isInPosition = true;
     timeSincesThrow = millis();
     timeSinceLastJump = millis();
   }

   werewolf = werewolfImg.get(0, 0, w, h);

   if (isInPosition) {
     //update sprit sheet of werewolf
     if (millis() - timeSincesThrow > 400) {
       werewolf = werewolfImg.get(w*4, 0, w, h); //throwing positon
       readyToThrow = true;
     } else if (millis() - timeSincesThrow > 300) {
       werewolf = werewolfImg.get(w*3, 0, w, h); //arm raised
     } else if (millis() - timeSincesThrow > 200) {
       werewolf = werewolfImg.get(w*2, 0, w, h); //arm raised
     } else if (millis() - timeSincesThrow > 100) {
       werewolf = werewolfImg.get(w, 0, w, h); //default position
       readyToThrow = false;
       boneInHand = true;
     }

     //jump
     if (millis() - timeSinceLastJump > whenToJumpNext) {
       jumpVelocity = 27;
       timeSinceLastJump = millis();
       whenToJumpNext = int(random(2000, 4000)); //next jump occur randomly between 2 and 4 seconds
     }

     yPos -= jumpVelocity;
     jumpVelocity += gravity;

     //ensures that werewolf does not exit screen
     if (yPos > height - h) {
       yPos = height - h;
     }
   }

   //delaying time between throwings
   if (millis() - timeSincesThrow > 833) {
     timeSincesThrow = millis(); //reset time since last throw
   }
   
   image(werewolf, xPos, yPos);
   stroke(0, 0, 0, 0);
   fill(203, 0, 0);
   rect(0, 0, ((width/maxLives)*lives), 7);
 }
}
Behind the scenes of the graphics
To create the graphics for the game, I used Sketch. The graphics are based on basic icons found online (the links to the sources can be found in the code above), later edited to fit the needs of the game. For example, to create my own sprite sheets, I edited the icons multiple times by moving the vector points.
Using Sketch to create the graphics and sprite sheets of the game.