From 14240e140f8944248b09e547baae175ed8fadac5 Mon Sep 17 00:00:00 2001 From: Jack Armstrong Date: Thu, 12 Apr 2018 14:16:46 -0700 Subject: [PATCH] Added scorekeeping --- PacmanGame/Blinky.pde | 437 ++++++++++++------------ PacmanGame/Clyde.pde | 453 ++++++++++++------------- PacmanGame/Inky.pde | 465 ++++++++++++------------- PacmanGame/Node.pde | 0 PacmanGame/Pacman.pde | 267 ++++++++------- PacmanGame/PacmanGame.pde | 542 +++++++++++++++--------------- PacmanGame/Path.pde | 0 PacmanGame/Pinky.pde | 465 ++++++++++++------------- PacmanGame/Tile.pde | 0 PacmanGame/data/Monospaced-15.vlw | Bin 0 -> 33312 bytes PacmanGame/data/map.jpg | Bin 11 files changed, 1319 insertions(+), 1310 deletions(-) mode change 100644 => 100755 PacmanGame/Node.pde mode change 100644 => 100755 PacmanGame/Path.pde mode change 100644 => 100755 PacmanGame/Tile.pde create mode 100644 PacmanGame/data/Monospaced-15.vlw mode change 100644 => 100755 PacmanGame/data/map.jpg diff --git a/PacmanGame/Blinky.pde b/PacmanGame/Blinky.pde index 91ebca6..435c5b1 100644 --- a/PacmanGame/Blinky.pde +++ b/PacmanGame/Blinky.pde @@ -1,219 +1,220 @@ -class Blinky { - PVector pos = new PVector(13*16 +8, 11*16+8);//starting position - PVector vel = new PVector(1, 0); - Path bestPath; // the variable stores the path the ghost will be following - ArrayList ghostNodes = new ArrayList();//the nodes making up the path including the ghosts position and the target position - Node start;//the ghosts position as a node - Node end; //the ghosts target position as a node - color colour = color(255, 0, 0);//red - - boolean chase = true;//true if the ghost is in chase mode false if in scatter mode - boolean frightened = false;//true if the ghost is in frightened mode - int flashCount = 0;//in order to make the ghost flash when frightened this is a counter - int chaseCount = 0;//counter for the switch between chase and scatter - boolean returnHome = false;//if eaten return home - boolean deadForABit = false;//after the ghost returns home it disappears for a bit - int deadCount = 0; - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //constructor - Blinky() { - setPath(); - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - - void show() { - //increments counts - chaseCount ++; - if (chase) { - if (chaseCount > 2000) { - chase = false; - chaseCount = 0; - } - } else { - if (chaseCount > 700) { - chase = true; - chaseCount = 0; - } - } - - - - if (deadForABit) { - deadCount ++; - if (deadCount > 300) { - deadForABit = false; - } - } else {//if not deadforabit then show the ghost - if (!frightened) { - if (returnHome) {//have the ghost be transparent if on its way home - stroke(colour, 100); - fill(colour, 100); - } else {// colour the ghost - stroke(colour); - fill(colour); - } - bestPath.show();//show the path the ghost is following - } else {//if frightened - flashCount ++; - if (flashCount > 800) {//after 8 seconds the ghosts are no longer frightened - frightened = false; - flashCount = 0; - } - - if (floor(flashCount / 30) %2 ==0) {//make it flash white and blue every 30 frames - stroke(255); - fill(255); - } else {//flash blue - stroke(0, 0, 200); - fill(0, 0, 200); - } - } - ellipse(pos.x, pos.y, 20, 20);//draw the ghost as a circle - } - } - - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //moves the ghost along the path - void move() { - if (!deadForABit) {//dont move if dead - pos.add(vel); - checkDirection();//check if need to change direction next move - } - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - - //calculates a path from the first node in ghost nodes to the last node in ghostNodes and sets it as best path - void setPath() { - ghostNodes.clear(); - setNodes(); - start = ghostNodes.get(0); - end = ghostNodes.get(ghostNodes.size()-1); - Path temp = AStar(start, end, vel); - if (temp!= null) {//if not path is found then dont change bestPath - bestPath = temp.clone(); - } - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //sets all the nodes and connects them with adjacent nodes - //also sets the target node - void setNodes() { - - ghostNodes.add(new Node((pos.x-8)/16, (pos.y-8)/16));//add the current position as a node - for (int i = 1; i< 27; i++) {//check every position - for (int j = 1; j< 30; j++) { - //if there is a space up or below and a space left or right then this space is a node - if (!tiles[j][i].wall) { - if (!tiles[j-1][i].wall || !tiles[j+1][i].wall) { //check up for space - if (!tiles[j][i-1].wall || !tiles[j][i+1].wall) {//check left and right for space - - ghostNodes.add(new Node(i, j));//add the nodes - } - } - } - } - } - if (returnHome) {//if returning home then the target is just above the ghost room thing - ghostNodes.add(new Node(13, 11)); - } else { - if (chase) { - ghostNodes.add(new Node((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16));//target pacman - } else { - ghostNodes.add(new Node(1, 1));//scatter to corner - } - } - - for (int i = 0; i< ghostNodes.size(); i++) {//connect all the nodes together - ghostNodes.get(i).addEdges(ghostNodes); - } - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //check if the ghost needs to change direction as well as other stuff - void checkDirection() { - if (pacman.hitPacman(pos)) {//if hit pacman - if (frightened) {//eaten by pacman - returnHome = true; - frightened = false; - } else if (!returnHome) {//killPacman - pacman.kill(); - } - } - - - // check if reached home yet - if (returnHome) { - if (dist((pos.x-8)/16, (pos.y - 8)/16, 13, 11) < 1) { - //set the ghost as dead for a bit - returnHome = false; - deadForABit = true; - deadCount = 0; - } - } - - if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position - - PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position - - if (frightened) {//no path needs to generated by the ghost if frightened - boolean isNode = false; - for (int j = 0; j < ghostNodes.size(); j++) { - if (matrixPosition.x == ghostNodes.get(j).x && matrixPosition.y == ghostNodes.get(j).y) { - isNode = true; - } - } - if (!isNode) {//if not on a node then no need to do anything - return; - } else {//if on a node - //set a random direction - PVector newVel = new PVector(); - int rand = floor(random(4)); - switch(rand) { - case 0: - newVel = new PVector(1, 0); - break; - case 1: - newVel = new PVector(0, 1); - break; - case 2: - newVel = new PVector(-1, 0); - break; - case 3: - newVel = new PVector(0, -1); - break; - } - //if the random velocity is into a wall or in the opposite direction then choose another one - while (tiles[floor(matrixPosition.y + newVel.y)][floor(matrixPosition.x + newVel.x)].wall || (newVel.x +2*vel.x ==0 && newVel.y + 2*vel.y ==0)) { - rand = floor(random(4)); - switch(rand) { - case 0: - newVel = new PVector(1, 0); - break; - case 1: - newVel = new PVector(0, 1); - break; - case 2: - newVel = new PVector(-1, 0); - break; - case 3: - newVel = new PVector(0, -1); - break; - } - } - vel = new PVector(newVel.x/2, newVel.y/2);//halve the speed - } - } else {//not frightened - - setPath(); - - for (int i =0; i< bestPath.path.size(); i++) {//if currently on a node turn towards the direction of the next node in the path - if (matrixPosition.x == bestPath.path.get(i).x && matrixPosition.y == bestPath.path.get(i).y) { - - vel = new PVector(bestPath.path.get(i+1).x - matrixPosition.x, bestPath.path.get(i+1).y - matrixPosition.y); - vel.limit(1); - - return; - } - } - } - } - } +class Blinky { + PVector pos = new PVector(13*16 +8, 11*16+8);//starting position + PVector vel = new PVector(1, 0); + Path bestPath; // the variable stores the path the ghost will be following + ArrayList ghostNodes = new ArrayList();//the nodes making up the path including the ghosts position and the target position + Node start;//the ghosts position as a node + Node end; //the ghosts target position as a node + color colour = color(255, 0, 0);//red + + boolean chase = true;//true if the ghost is in chase mode false if in scatter mode + boolean frightened = false;//true if the ghost is in frightened mode + int flashCount = 0;//in order to make the ghost flash when frightened this is a counter + int chaseCount = 0;//counter for the switch between chase and scatter + boolean returnHome = false;//if eaten return home + boolean deadForABit = false;//after the ghost returns home it disappears for a bit + int deadCount = 0; + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //constructor + Blinky() { + setPath(); + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + + void show() { + //increments counts + chaseCount ++; + if (chase) { + if (chaseCount > 2000) { + chase = false; + chaseCount = 0; + } + } else { + if (chaseCount > 700) { + chase = true; + chaseCount = 0; + } + } + + + + if (deadForABit) { + deadCount ++; + if (deadCount > 300) { + deadForABit = false; + } + } else {//if not deadforabit then show the ghost + if (!frightened) { + if (returnHome) {//have the ghost be transparent if on its way home + stroke(colour, 100); + fill(colour, 100); + } else {// colour the ghost + stroke(colour); + fill(colour); + } + bestPath.show();//show the path the ghost is following + } else {//if frightened + flashCount ++; + if (flashCount > 800) {//after 8 seconds the ghosts are no longer frightened + frightened = false; + flashCount = 0; + } + + if (floor(flashCount / 30) %2 ==0) {//make it flash white and blue every 30 frames + stroke(255); + fill(255); + } else {//flash blue + stroke(0, 0, 200); + fill(0, 0, 200); + } + } + ellipse(pos.x, pos.y, 20, 20);//draw the ghost as a circle + } + } + + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //moves the ghost along the path + void move() { + if (!deadForABit) {//dont move if dead + pos.add(vel); + checkDirection();//check if need to change direction next move + } + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + + //calculates a path from the first node in ghost nodes to the last node in ghostNodes and sets it as best path + void setPath() { + ghostNodes.clear(); + setNodes(); + start = ghostNodes.get(0); + end = ghostNodes.get(ghostNodes.size()-1); + Path temp = AStar(start, end, vel); + if (temp!= null) {//if not path is found then dont change bestPath + bestPath = temp.clone(); + } + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //sets all the nodes and connects them with adjacent nodes + //also sets the target node + void setNodes() { + + ghostNodes.add(new Node((pos.x-8)/16, (pos.y-8)/16));//add the current position as a node + for (int i = 1; i< 27; i++) {//check every position + for (int j = 1; j< 30; j++) { + //if there is a space up or below and a space left or right then this space is a node + if (!tiles[j][i].wall) { + if (!tiles[j-1][i].wall || !tiles[j+1][i].wall) { //check up for space + if (!tiles[j][i-1].wall || !tiles[j][i+1].wall) {//check left and right for space + + ghostNodes.add(new Node(i, j));//add the nodes + } + } + } + } + } + if (returnHome) {//if returning home then the target is just above the ghost room thing + ghostNodes.add(new Node(13, 11)); + } else { + if (chase) { + ghostNodes.add(new Node((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16));//target pacman + } else { + ghostNodes.add(new Node(1, 1));//scatter to corner + } + } + + for (int i = 0; i< ghostNodes.size(); i++) {//connect all the nodes together + ghostNodes.get(i).addEdges(ghostNodes); + } + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //check if the ghost needs to change direction as well as other stuff + void checkDirection() { + if (pacman.hitPacman(pos)) {//if hit pacman + if (frightened) {//eaten by pacman + returnHome = true; + frightened = false; + score+=ghostScore; + } else if (!returnHome) {//killPacman + pacman.kill(); + } + } + + + // check if reached home yet + if (returnHome) { + if (dist((pos.x-8)/16, (pos.y - 8)/16, 13, 11) < 1) { + //set the ghost as dead for a bit + returnHome = false; + deadForABit = true; + deadCount = 0; + } + } + + if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position + + PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position + + if (frightened) {//no path needs to generated by the ghost if frightened + boolean isNode = false; + for (int j = 0; j < ghostNodes.size(); j++) { + if (matrixPosition.x == ghostNodes.get(j).x && matrixPosition.y == ghostNodes.get(j).y) { + isNode = true; + } + } + if (!isNode) {//if not on a node then no need to do anything + return; + } else {//if on a node + //set a random direction + PVector newVel = new PVector(); + int rand = floor(random(4)); + switch(rand) { + case 0: + newVel = new PVector(1, 0); + break; + case 1: + newVel = new PVector(0, 1); + break; + case 2: + newVel = new PVector(-1, 0); + break; + case 3: + newVel = new PVector(0, -1); + break; + } + //if the random velocity is into a wall or in the opposite direction then choose another one + while (tiles[floor(matrixPosition.y + newVel.y)][floor(matrixPosition.x + newVel.x)].wall || (newVel.x +2*vel.x ==0 && newVel.y + 2*vel.y ==0)) { + rand = floor(random(4)); + switch(rand) { + case 0: + newVel = new PVector(1, 0); + break; + case 1: + newVel = new PVector(0, 1); + break; + case 2: + newVel = new PVector(-1, 0); + break; + case 3: + newVel = new PVector(0, -1); + break; + } + } + vel = new PVector(newVel.x/2, newVel.y/2);//halve the speed + } + } else {//not frightened + + setPath(); + + for (int i =0; i< bestPath.path.size(); i++) {//if currently on a node turn towards the direction of the next node in the path + if (matrixPosition.x == bestPath.path.get(i).x && matrixPosition.y == bestPath.path.get(i).y) { + + vel = new PVector(bestPath.path.get(i+1).x - matrixPosition.x, bestPath.path.get(i+1).y - matrixPosition.y); + vel.limit(1); + + return; + } + } + } + } + } } \ No newline at end of file diff --git a/PacmanGame/Clyde.pde b/PacmanGame/Clyde.pde index 2563963..58fa154 100644 --- a/PacmanGame/Clyde.pde +++ b/PacmanGame/Clyde.pde @@ -1,227 +1,228 @@ -class Clyde { - PVector pos = new PVector(1*16 +8, 29*16+8); - PVector vel = new PVector(1, 0); - Path bestPath; // the variable stores the path the ghost will be following - ArrayList ghostNodes = new ArrayList();//the nodes making up the path including the ghosts position and the target position - Node start;//the ghosts position as a node - Node end; //the ghosts target position as a node - color colour = color(255, 100, 0);//orange - - boolean chase = true;//true if the ghost is in chase mode false if in scatter mode - boolean frightened = false;//true if the ghost is in frightened mode - int flashCount = 0;//in order to make the ghost flash when frightened this is a counter - int chaseCount = 0;//counter for the switch between chase and scatter - boolean returnHome = false;//if eaten return home - boolean deadForABit = false;//after the ghost returns home it disappears for a bit - int deadCount = 0; - - - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //constructor - Clyde() { - setPath(); - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - - void show() { - //increments counts - chaseCount ++; - if (chase) { - if (chaseCount > 2000) { - chase = false; - chaseCount = 0; - } - } else { - if (chaseCount > 700) { - chase = true; - chaseCount = 0; - } - } - - - - if (deadForABit) { - deadCount ++; - if (deadCount > 300) { - deadForABit = false; - } - } else {//if not deadforabit then show the ghost - if (!frightened) { - if (returnHome) {//have the ghost be transparent if on its way home - stroke(colour, 100); - fill(colour, 100); - } else {// colour the ghost - stroke(colour); - fill(colour); - } - bestPath.show();//show the path the ghost is following - } else {//if frightened - flashCount ++; - if (flashCount > 800) {//after 8 seconds the ghosts are no longer frightened - frightened = false; - flashCount = 0; - } - - if (floor(flashCount / 30) %2 ==0) {//make it flash white and blue every 30 frames - stroke(255); - fill(255); - } else {//flash blue - stroke(0, 0, 200); - fill(0, 0, 200); - } - } - ellipse(pos.x, pos.y, 20, 20);//draw the ghost as a circle - } - } - - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //moves the ghost along the path - void move() { - if (!deadForABit) {//dont move if dead - pos.add(vel); - checkDirection();//check if need to change direction next move - } - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - - //calculates a path from the first node in ghost nodes to the last node in ghostNodes and sets it as best path - void setPath() { - ghostNodes.clear(); - setNodes(); - start = ghostNodes.get(0); - end = ghostNodes.get(ghostNodes.size()-1); - Path temp = AStar(start, end, vel); - if (temp!= null) {//if not path is found then dont change bestPath - bestPath = temp.clone(); - } - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //sets all the nodes and connects them with adjacent nodes - //also sets the target node - void setNodes() { - - ghostNodes.add(new Node((pos.x-8)/16, (pos.y-8)/16));//add the current position as a node - for (int i = 1; i< 27; i++) {//check every position - for (int j = 1; j< 30; j++) { - //if there is a space up or below and a space left or right then this space is a node - if (!tiles[j][i].wall) { - if (!tiles[j-1][i].wall || !tiles[j+1][i].wall) { //check up for space - if (!tiles[j][i-1].wall || !tiles[j][i+1].wall) {//check left and right for space - - ghostNodes.add(new Node(i, j));//add the nodes - } - } - } - } - } - if (returnHome) {//if returning home then the target is just above the ghost room thing - ghostNodes.add(new Node(13, 11)); - } else { - if (chase) { - if (dist((pos.x-8)/16, (pos.y-8)/16, (pacman.pos.x-8) / 16, (pacman.pos.y-8)/16) > 8) { - - ghostNodes.add(new Node((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16)); - } else { - - ghostNodes.add(new Node(1, 29)); - } - } else {//scatter - ghostNodes.add(new Node(1, 29)); - } - } - - for (int i = 0; i< ghostNodes.size(); i++) {//connect all the nodes together - ghostNodes.get(i).addEdges(ghostNodes); - } - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //check if the ghost needs to change direction as well as other stuff - void checkDirection() { - if (pacman.hitPacman(pos)) {//if hit pacman - if (frightened) {//eaten by pacman - returnHome = true; - frightened = false; - } else if (!returnHome) {//killPacman - pacman.kill(); - } - } - - - // check if reached home yet - if (returnHome) { - if (dist((pos.x-8)/16, (pos.y - 8)/16, 13, 11) < 1) { - //set the ghost as dead for a bit - returnHome = false; - deadForABit = true; - deadCount = 0; - } - } - - if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position - - PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position - - if (frightened) {//no path needs to generated by the ghost if frightened - boolean isNode = false; - for (int j = 0; j < ghostNodes.size(); j++) { - if (matrixPosition.x == ghostNodes.get(j).x && matrixPosition.y == ghostNodes.get(j).y) { - isNode = true; - } - } - if (!isNode) {//if not on a node then no need to do anything - return; - } else {//if on a node - //set a random direction - PVector newVel = new PVector(); - int rand = floor(random(4)); - switch(rand) { - case 0: - newVel = new PVector(1, 0); - break; - case 1: - newVel = new PVector(0, 1); - break; - case 2: - newVel = new PVector(-1, 0); - break; - case 3: - newVel = new PVector(0, -1); - break; - } - //if the random velocity is into a wall or in the opposite direction then choose another one - while (tiles[floor(matrixPosition.y + newVel.y)][floor(matrixPosition.x + newVel.x)].wall || (newVel.x +2*vel.x ==0 && newVel.y + 2*vel.y ==0)) { - rand = floor(random(4)); - switch(rand) { - case 0: - newVel = new PVector(1, 0); - break; - case 1: - newVel = new PVector(0, 1); - break; - case 2: - newVel = new PVector(-1, 0); - break; - case 3: - newVel = new PVector(0, -1); - break; - } - } - vel = new PVector(newVel.x/2, newVel.y/2);//halve the speed - } - } else {//not frightened - - setPath(); - - for (int i =0; i< bestPath.path.size(); i++) {//if currently on a node turn towards the direction of the next node in the path - if (matrixPosition.x == bestPath.path.get(i).x && matrixPosition.y == bestPath.path.get(i).y) { - - vel = new PVector(bestPath.path.get(i+1).x - matrixPosition.x, bestPath.path.get(i+1).y - matrixPosition.y); - vel.limit(1); - - return; - } - } - } - } - } +class Clyde { + PVector pos = new PVector(1*16 +8, 29*16+8); + PVector vel = new PVector(1, 0); + Path bestPath; // the variable stores the path the ghost will be following + ArrayList ghostNodes = new ArrayList();//the nodes making up the path including the ghosts position and the target position + Node start;//the ghosts position as a node + Node end; //the ghosts target position as a node + color colour = color(255, 100, 0);//orange + + boolean chase = true;//true if the ghost is in chase mode false if in scatter mode + boolean frightened = false;//true if the ghost is in frightened mode + int flashCount = 0;//in order to make the ghost flash when frightened this is a counter + int chaseCount = 0;//counter for the switch between chase and scatter + boolean returnHome = false;//if eaten return home + boolean deadForABit = false;//after the ghost returns home it disappears for a bit + int deadCount = 0; + + + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //constructor + Clyde() { + setPath(); + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + + void show() { + //increments counts + chaseCount ++; + if (chase) { + if (chaseCount > 2000) { + chase = false; + chaseCount = 0; + } + } else { + if (chaseCount > 700) { + chase = true; + chaseCount = 0; + } + } + + + + if (deadForABit) { + deadCount ++; + if (deadCount > 300) { + deadForABit = false; + } + } else {//if not deadforabit then show the ghost + if (!frightened) { + if (returnHome) {//have the ghost be transparent if on its way home + stroke(colour, 100); + fill(colour, 100); + } else {// colour the ghost + stroke(colour); + fill(colour); + } + bestPath.show();//show the path the ghost is following + } else {//if frightened + flashCount ++; + if (flashCount > 800) {//after 8 seconds the ghosts are no longer frightened + frightened = false; + flashCount = 0; + } + + if (floor(flashCount / 30) %2 ==0) {//make it flash white and blue every 30 frames + stroke(255); + fill(255); + } else {//flash blue + stroke(0, 0, 200); + fill(0, 0, 200); + } + } + ellipse(pos.x, pos.y, 20, 20);//draw the ghost as a circle + } + } + + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //moves the ghost along the path + void move() { + if (!deadForABit) {//dont move if dead + pos.add(vel); + checkDirection();//check if need to change direction next move + } + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + + //calculates a path from the first node in ghost nodes to the last node in ghostNodes and sets it as best path + void setPath() { + ghostNodes.clear(); + setNodes(); + start = ghostNodes.get(0); + end = ghostNodes.get(ghostNodes.size()-1); + Path temp = AStar(start, end, vel); + if (temp!= null) {//if not path is found then dont change bestPath + bestPath = temp.clone(); + } + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //sets all the nodes and connects them with adjacent nodes + //also sets the target node + void setNodes() { + + ghostNodes.add(new Node((pos.x-8)/16, (pos.y-8)/16));//add the current position as a node + for (int i = 1; i< 27; i++) {//check every position + for (int j = 1; j< 30; j++) { + //if there is a space up or below and a space left or right then this space is a node + if (!tiles[j][i].wall) { + if (!tiles[j-1][i].wall || !tiles[j+1][i].wall) { //check up for space + if (!tiles[j][i-1].wall || !tiles[j][i+1].wall) {//check left and right for space + + ghostNodes.add(new Node(i, j));//add the nodes + } + } + } + } + } + if (returnHome) {//if returning home then the target is just above the ghost room thing + ghostNodes.add(new Node(13, 11)); + } else { + if (chase) { + if (dist((pos.x-8)/16, (pos.y-8)/16, (pacman.pos.x-8) / 16, (pacman.pos.y-8)/16) > 8) { + + ghostNodes.add(new Node((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16)); + } else { + + ghostNodes.add(new Node(1, 29)); + } + } else {//scatter + ghostNodes.add(new Node(1, 29)); + } + } + + for (int i = 0; i< ghostNodes.size(); i++) {//connect all the nodes together + ghostNodes.get(i).addEdges(ghostNodes); + } + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //check if the ghost needs to change direction as well as other stuff + void checkDirection() { + if (pacman.hitPacman(pos)) {//if hit pacman + if (frightened) {//eaten by pacman + returnHome = true; + frightened = false; + score+=ghostScore; + } else if (!returnHome) {//killPacman + pacman.kill(); + } + } + + + // check if reached home yet + if (returnHome) { + if (dist((pos.x-8)/16, (pos.y - 8)/16, 13, 11) < 1) { + //set the ghost as dead for a bit + returnHome = false; + deadForABit = true; + deadCount = 0; + } + } + + if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position + + PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position + + if (frightened) {//no path needs to generated by the ghost if frightened + boolean isNode = false; + for (int j = 0; j < ghostNodes.size(); j++) { + if (matrixPosition.x == ghostNodes.get(j).x && matrixPosition.y == ghostNodes.get(j).y) { + isNode = true; + } + } + if (!isNode) {//if not on a node then no need to do anything + return; + } else {//if on a node + //set a random direction + PVector newVel = new PVector(); + int rand = floor(random(4)); + switch(rand) { + case 0: + newVel = new PVector(1, 0); + break; + case 1: + newVel = new PVector(0, 1); + break; + case 2: + newVel = new PVector(-1, 0); + break; + case 3: + newVel = new PVector(0, -1); + break; + } + //if the random velocity is into a wall or in the opposite direction then choose another one + while (tiles[floor(matrixPosition.y + newVel.y)][floor(matrixPosition.x + newVel.x)].wall || (newVel.x +2*vel.x ==0 && newVel.y + 2*vel.y ==0)) { + rand = floor(random(4)); + switch(rand) { + case 0: + newVel = new PVector(1, 0); + break; + case 1: + newVel = new PVector(0, 1); + break; + case 2: + newVel = new PVector(-1, 0); + break; + case 3: + newVel = new PVector(0, -1); + break; + } + } + vel = new PVector(newVel.x/2, newVel.y/2);//halve the speed + } + } else {//not frightened + + setPath(); + + for (int i =0; i< bestPath.path.size(); i++) {//if currently on a node turn towards the direction of the next node in the path + if (matrixPosition.x == bestPath.path.get(i).x && matrixPosition.y == bestPath.path.get(i).y) { + + vel = new PVector(bestPath.path.get(i+1).x - matrixPosition.x, bestPath.path.get(i+1).y - matrixPosition.y); + vel.limit(1); + + return; + } + } + } + } + } } \ No newline at end of file diff --git a/PacmanGame/Inky.pde b/PacmanGame/Inky.pde index 7000181..b78aa90 100644 --- a/PacmanGame/Inky.pde +++ b/PacmanGame/Inky.pde @@ -1,233 +1,234 @@ -class Inky { - PVector pos = new PVector(8*16 +8, 1*16+8);//starting position - PVector vel = new PVector(1, 0); - Path bestPath; // the variable stores the path the ghost will be following - ArrayList ghostNodes = new ArrayList();//the nodes making up the path including the ghosts position and the target position - Node start;//the ghosts position as a node - Node end; //the ghosts target position as a node - color colour = color(135, 206, 250);//blueish - - - boolean chase = true;//true if the ghost is in chase mode false if in scatter mode - boolean frightened = false;//true if the ghost is in frightened mode - int flashCount = 0;//in order to make the ghost flash when frightened this is a counter - int chaseCount = 0;//counter for the switch between chase and scatter - boolean returnHome = false;//if eaten return home - boolean deadForABit = false;//after the ghost returns home it disappears for a bit - int deadCount = 0; - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //constructor - Inky() { - setPath(); - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - - void show() { - //increments counts - chaseCount ++; - if (chase) { - if (chaseCount > 2000) { - chase = false; - chaseCount = 0; - } - } else { - if (chaseCount > 700) { - chase = true; - chaseCount = 0; - } - } - - - - if (deadForABit) { - deadCount ++; - if (deadCount > 300) { - deadForABit = false; - } - } else {//if not deadforabit then show the ghost - if (!frightened) { - if (returnHome) {//have the ghost be transparent if on its way home - stroke(colour, 100); - fill(colour, 100); - } else {// colour the ghost - stroke(colour); - fill(colour); - } - bestPath.show();//show the path the ghost is following - } else {//if frightened - flashCount ++; - if (flashCount > 800) {//after 8 seconds the ghosts are no longer frightened - frightened = false; - flashCount = 0; - } - - if (floor(flashCount / 30) %2 ==0) {//make it flash white and blue every 30 frames - stroke(255); - fill(255); - } else {//flash blue - stroke(0, 0, 200); - fill(0, 0, 200); - } - } - ellipse(pos.x, pos.y, 20, 20);//draw the ghost as a circle - } - } - - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //moves the ghost along the path - void move() { - if (!deadForABit) {//dont move if dead - pos.add(vel); - checkDirection();//check if need to change direction next move - } - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - - //calculates a path from the first node in ghost nodes to the last node in ghostNodes and sets it as best path - void setPath() { - ghostNodes.clear(); - setNodes(); - start = ghostNodes.get(0); - end = ghostNodes.get(ghostNodes.size()-1); - Path temp = AStar(start, end, vel); - if (temp!= null) {//if not path is found then dont change bestPath - bestPath = temp.clone(); - } - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //sets all the nodes and connects them with adjacent nodes - //also sets the target node - void setNodes() { - - ghostNodes.add(new Node((pos.x-8)/16, (pos.y-8)/16));//add the current position as a node - for (int i = 1; i< 27; i++) {//check every position - for (int j = 1; j< 30; j++) { - //if there is a space up or below and a space left or right then this space is a node - if (!tiles[j][i].wall) { - if (!tiles[j-1][i].wall || !tiles[j+1][i].wall) { //check up for space - if (!tiles[j][i-1].wall || !tiles[j][i+1].wall) {//check left and right for space - - ghostNodes.add(new Node(i, j));//add the nodes - } - } - } - } - } - if (returnHome) {//if returning home then the target is just above the ghost room thing - ghostNodes.add(new Node(13, 11)); - } else { - if (chase) { - PVector pacmanPosition = new PVector((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16); - PVector blinkyPosition = new PVector((blinky.pos.x-8)/16, (blinky.pos.y - 8)/16); - PVector blinkyToPacman = new PVector(pacmanPosition.x - blinkyPosition.x, pacmanPosition.y - blinkyPosition.y); - - PVector target = new PVector (pacmanPosition.x + blinkyToPacman.x, pacmanPosition.y + blinkyToPacman.y); - PVector nearestTile = getNearestNonWallTile(target); - - if (dist((pos.x-8)/16, (pos.y-8)/16, nearestTile.x, nearestTile.y)<1) { - ghostNodes.add(new Node((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16)); - } else { - - - ghostNodes.add(new Node(nearestTile.x, nearestTile.y)); - } - } else {//scatter - ghostNodes.add(new Node(26, 29)); - } - } - - for (int i = 0; i< ghostNodes.size(); i++) {//connect all the nodes together - ghostNodes.get(i).addEdges(ghostNodes); - } - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //check if the ghost needs to change direction as well as other stuff - void checkDirection() { - if (pacman.hitPacman(pos)) {//if hit pacman - if (frightened) {//eaten by pacman - returnHome = true; - frightened = false; - } else if (!returnHome) {//killPacman - pacman.kill(); - } - } - - - // check if reached home yet - if (returnHome) { - if (dist((pos.x-8)/16, (pos.y - 8)/16, 13, 11) < 1) { - //set the ghost as dead for a bit - returnHome = false; - deadForABit = true; - deadCount = 0; - } - } - - if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position - - PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position - - if (frightened) {//no path needs to generated by the ghost if frightened - boolean isNode = false; - for (int j = 0; j < ghostNodes.size(); j++) { - if (matrixPosition.x == ghostNodes.get(j).x && matrixPosition.y == ghostNodes.get(j).y) { - isNode = true; - } - } - if (!isNode) {//if not on a node then no need to do anything - return; - } else {//if on a node - //set a random direction - PVector newVel = new PVector(); - int rand = floor(random(4)); - switch(rand) { - case 0: - newVel = new PVector(1, 0); - break; - case 1: - newVel = new PVector(0, 1); - break; - case 2: - newVel = new PVector(-1, 0); - break; - case 3: - newVel = new PVector(0, -1); - break; - } - //if the random velocity is into a wall or in the opposite direction then choose another one - while (tiles[floor(matrixPosition.y + newVel.y)][floor(matrixPosition.x + newVel.x)].wall || (newVel.x +2*vel.x ==0 && newVel.y + 2*vel.y ==0)) { - rand = floor(random(4)); - switch(rand) { - case 0: - newVel = new PVector(1, 0); - break; - case 1: - newVel = new PVector(0, 1); - break; - case 2: - newVel = new PVector(-1, 0); - break; - case 3: - newVel = new PVector(0, -1); - break; - } - } - vel = new PVector(newVel.x/2, newVel.y/2);//halve the speed - } - } else {//not frightened - - setPath(); - - for (int i =0; i< bestPath.path.size(); i++) {//if currently on a node turn towards the direction of the next node in the path - if (matrixPosition.x == bestPath.path.get(i).x && matrixPosition.y == bestPath.path.get(i).y) { - - vel = new PVector(bestPath.path.get(i+1).x - matrixPosition.x, bestPath.path.get(i+1).y - matrixPosition.y); - vel.limit(1); - - return; - } - } - } - } - } +class Inky { + PVector pos = new PVector(8*16 +8, 1*16+8);//starting position + PVector vel = new PVector(1, 0); + Path bestPath; // the variable stores the path the ghost will be following + ArrayList ghostNodes = new ArrayList();//the nodes making up the path including the ghosts position and the target position + Node start;//the ghosts position as a node + Node end; //the ghosts target position as a node + color colour = color(135, 206, 250);//blueish + + + boolean chase = true;//true if the ghost is in chase mode false if in scatter mode + boolean frightened = false;//true if the ghost is in frightened mode + int flashCount = 0;//in order to make the ghost flash when frightened this is a counter + int chaseCount = 0;//counter for the switch between chase and scatter + boolean returnHome = false;//if eaten return home + boolean deadForABit = false;//after the ghost returns home it disappears for a bit + int deadCount = 0; + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //constructor + Inky() { + setPath(); + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + + void show() { + //increments counts + chaseCount ++; + if (chase) { + if (chaseCount > 2000) { + chase = false; + chaseCount = 0; + } + } else { + if (chaseCount > 700) { + chase = true; + chaseCount = 0; + } + } + + + + if (deadForABit) { + deadCount ++; + if (deadCount > 300) { + deadForABit = false; + } + } else {//if not deadforabit then show the ghost + if (!frightened) { + if (returnHome) {//have the ghost be transparent if on its way home + stroke(colour, 100); + fill(colour, 100); + } else {// colour the ghost + stroke(colour); + fill(colour); + } + bestPath.show();//show the path the ghost is following + } else {//if frightened + flashCount ++; + if (flashCount > 800) {//after 8 seconds the ghosts are no longer frightened + frightened = false; + flashCount = 0; + } + + if (floor(flashCount / 30) %2 ==0) {//make it flash white and blue every 30 frames + stroke(255); + fill(255); + } else {//flash blue + stroke(0, 0, 200); + fill(0, 0, 200); + } + } + ellipse(pos.x, pos.y, 20, 20);//draw the ghost as a circle + } + } + + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //moves the ghost along the path + void move() { + if (!deadForABit) {//dont move if dead + pos.add(vel); + checkDirection();//check if need to change direction next move + } + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + + //calculates a path from the first node in ghost nodes to the last node in ghostNodes and sets it as best path + void setPath() { + ghostNodes.clear(); + setNodes(); + start = ghostNodes.get(0); + end = ghostNodes.get(ghostNodes.size()-1); + Path temp = AStar(start, end, vel); + if (temp!= null) {//if not path is found then dont change bestPath + bestPath = temp.clone(); + } + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //sets all the nodes and connects them with adjacent nodes + //also sets the target node + void setNodes() { + + ghostNodes.add(new Node((pos.x-8)/16, (pos.y-8)/16));//add the current position as a node + for (int i = 1; i< 27; i++) {//check every position + for (int j = 1; j< 30; j++) { + //if there is a space up or below and a space left or right then this space is a node + if (!tiles[j][i].wall) { + if (!tiles[j-1][i].wall || !tiles[j+1][i].wall) { //check up for space + if (!tiles[j][i-1].wall || !tiles[j][i+1].wall) {//check left and right for space + + ghostNodes.add(new Node(i, j));//add the nodes + } + } + } + } + } + if (returnHome) {//if returning home then the target is just above the ghost room thing + ghostNodes.add(new Node(13, 11)); + } else { + if (chase) { + PVector pacmanPosition = new PVector((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16); + PVector blinkyPosition = new PVector((blinky.pos.x-8)/16, (blinky.pos.y - 8)/16); + PVector blinkyToPacman = new PVector(pacmanPosition.x - blinkyPosition.x, pacmanPosition.y - blinkyPosition.y); + + PVector target = new PVector (pacmanPosition.x + blinkyToPacman.x, pacmanPosition.y + blinkyToPacman.y); + PVector nearestTile = getNearestNonWallTile(target); + + if (dist((pos.x-8)/16, (pos.y-8)/16, nearestTile.x, nearestTile.y)<1) { + ghostNodes.add(new Node((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16)); + } else { + + + ghostNodes.add(new Node(nearestTile.x, nearestTile.y)); + } + } else {//scatter + ghostNodes.add(new Node(26, 29)); + } + } + + for (int i = 0; i< ghostNodes.size(); i++) {//connect all the nodes together + ghostNodes.get(i).addEdges(ghostNodes); + } + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //check if the ghost needs to change direction as well as other stuff + void checkDirection() { + if (pacman.hitPacman(pos)) {//if hit pacman + if (frightened) {//eaten by pacman + returnHome = true; + frightened = false; + score+=ghostScore; + } else if (!returnHome) {//killPacman + pacman.kill(); + } + } + + + // check if reached home yet + if (returnHome) { + if (dist((pos.x-8)/16, (pos.y - 8)/16, 13, 11) < 1) { + //set the ghost as dead for a bit + returnHome = false; + deadForABit = true; + deadCount = 0; + } + } + + if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position + + PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position + + if (frightened) {//no path needs to generated by the ghost if frightened + boolean isNode = false; + for (int j = 0; j < ghostNodes.size(); j++) { + if (matrixPosition.x == ghostNodes.get(j).x && matrixPosition.y == ghostNodes.get(j).y) { + isNode = true; + } + } + if (!isNode) {//if not on a node then no need to do anything + return; + } else {//if on a node + //set a random direction + PVector newVel = new PVector(); + int rand = floor(random(4)); + switch(rand) { + case 0: + newVel = new PVector(1, 0); + break; + case 1: + newVel = new PVector(0, 1); + break; + case 2: + newVel = new PVector(-1, 0); + break; + case 3: + newVel = new PVector(0, -1); + break; + } + //if the random velocity is into a wall or in the opposite direction then choose another one + while (tiles[floor(matrixPosition.y + newVel.y)][floor(matrixPosition.x + newVel.x)].wall || (newVel.x +2*vel.x ==0 && newVel.y + 2*vel.y ==0)) { + rand = floor(random(4)); + switch(rand) { + case 0: + newVel = new PVector(1, 0); + break; + case 1: + newVel = new PVector(0, 1); + break; + case 2: + newVel = new PVector(-1, 0); + break; + case 3: + newVel = new PVector(0, -1); + break; + } + } + vel = new PVector(newVel.x/2, newVel.y/2);//halve the speed + } + } else {//not frightened + + setPath(); + + for (int i =0; i< bestPath.path.size(); i++) {//if currently on a node turn towards the direction of the next node in the path + if (matrixPosition.x == bestPath.path.get(i).x && matrixPosition.y == bestPath.path.get(i).y) { + + vel = new PVector(bestPath.path.get(i+1).x - matrixPosition.x, bestPath.path.get(i+1).y - matrixPosition.y); + vel.limit(1); + + return; + } + } + } + } + } } \ No newline at end of file diff --git a/PacmanGame/Node.pde b/PacmanGame/Node.pde old mode 100644 new mode 100755 diff --git a/PacmanGame/Pacman.pde b/PacmanGame/Pacman.pde index 0d706b6..6ec5816 100644 --- a/PacmanGame/Pacman.pde +++ b/PacmanGame/Pacman.pde @@ -1,135 +1,134 @@ -class Pacman { - PVector pos; - PVector vel = new PVector(-1, 0); - - //when pacman reaches a node its velocity changes to the value stored in turnto - PVector turnTo = new PVector(-1, 0); - boolean turn = false; - int score = 0; - int lives = 2; - boolean gameOver = false; - //--------------------------------------------------------------------------------------------------------------------------------------------------------- - //constructor - Pacman() { - pos = new PVector(13*16+8, 23*16 +8); - } - - //--------------------------------------------------------------------------------------------------------------------------------------------------------- - - //draws pacman - void show() { - fill(255, 255, 0); - stroke(255, 255, 0); - ellipse(pos.x, pos.y, 20, 20); - } - - //--------------------------------------------------------------------------------------------------------------------------------------------------------- - //move pacman if not facing wall - void move() { - if (checkPosition()) { - pos.add(vel); - } - } - - //--------------------------------------------------------------------------------------------------------------------------------------------------------- - - //returns whether the input vector hits pacman - boolean hitPacman(PVector GhostPos) { - if (dist(GhostPos.x, GhostPos.y, pos.x, pos.y) < 10) { - return true; - } - return false; - } - - - //--------------------------------------------------------------------------------------------------------------------------------------------------------- - //called when a ghost hits pacman - void kill() { - lives -=1; - if (lives < 0) {//game over if no lives left - gameOver = true; - } else { - pos = new PVector(13*16+8, 23*16 +8); //reset positions - - blinky = new Blinky(); - clyde = new Clyde(); - pinky = new Pinky(); - inky = new Inky(); - vel = new PVector(-1, 0); - turnTo = new PVector(-1, 0); - } - } - - //------------------------------------------------------------------------------------------------------------------------------------------------- - //returns whether pacman can move i.e. there is no wall in the direction of vel - boolean checkPosition() { - - if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position - - PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position - - //reset all the paths for all the ghosts - blinky.setPath(); - pinky.setPath(); - clyde.setPath(); - inky.setPath(); - - //check if the position has been eaten or not, note the blank spaces are initialised as already eaten - if (!tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].eaten) { - tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].eaten =true; - score +=1;//add a point - if (tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].bigDot) {//if big dot eaten - //set all ghosts to frightened - blinky.frightened = true; - blinky.flashCount = 0; - clyde.frightened = true; - clyde.flashCount = 0; - pinky.frightened = true; - pinky.flashCount = 0; - inky.frightened = true; - inky.flashCount = 0; - } - } - - - PVector positionToCheck= new PVector(matrixPosition.x + turnTo.x, matrixPosition.y+ turnTo.y); // the position in the tiles double array that the player is turning towards - - if (tiles[floor(positionToCheck.y)][floor(positionToCheck.x)].wall) {//check if there is a free space in the direction that it is going to turn - if (tiles[floor(matrixPosition.y + vel.y)][floor(matrixPosition.x + vel.x)].wall) {//if not check if the path ahead is free - return false;//if neither are free then dont move - } else {//forward is free - return true; - } - } else {//free to turn - vel = new PVector(turnTo.x, turnTo.y); - return true; - } - } else { - if ((pos.x+10*vel.x-8)%16 == 0 && (pos.y + 10*vel.y - 8)% 16 ==0) {//if 10 places off a critical position in the direction that pacman is moving - PVector matrixPosition = new PVector((pos.x+10*vel.x-8)/16, (pos.y+10*vel.y-8)/16);//convert that position to an array position - if (!tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].eaten ) {//if that tile has not been eaten - tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].eaten =true;//eat it - score +=1; - println("Score:", score); - if (tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].bigDot) {//big dot eaten - //set all ghosts as frightened - blinky.frightened = true; - blinky.flashCount = 0; - clyde.frightened = true; - clyde.flashCount = 0; - pinky.frightened = true; - pinky.flashCount = 0; - inky.frightened = true; - inky.flashCount = 0; - } - } - } - if (turnTo.x + vel.x == 0 && vel.y + turnTo.y ==0) {//if turning chenging directions entirely i.e. 180 degree turn - vel = new PVector(turnTo.x, turnTo.y);//turn - return true; - } - return true;//if not on a critical postion then continue forward - } - - } +class Pacman { + PVector pos; + PVector vel = new PVector(-1, 0); + + //when pacman reaches a node its velocity changes to the value stored in turnto + PVector turnTo = new PVector(-1, 0); + boolean turn = false; + int lives = 2; + boolean gameOver = false; + //--------------------------------------------------------------------------------------------------------------------------------------------------------- + //constructor + Pacman() { + pos = new PVector(13*16+8, 23*16 +8); + } + + //--------------------------------------------------------------------------------------------------------------------------------------------------------- + + //draws pacman + void show() { + fill(255, 255, 0); + stroke(255, 255, 0); + ellipse(pos.x, pos.y, 20, 20); + } + + //--------------------------------------------------------------------------------------------------------------------------------------------------------- + //move pacman if not facing wall + void move() { + if (checkPosition()) { + pos.add(vel); + } + } + + //--------------------------------------------------------------------------------------------------------------------------------------------------------- + + //returns whether the input vector hits pacman + boolean hitPacman(PVector GhostPos) { + if (dist(GhostPos.x, GhostPos.y, pos.x, pos.y) < 10) { + return true; + } + return false; + } + + + //--------------------------------------------------------------------------------------------------------------------------------------------------------- + //called when a ghost hits pacman + void kill() { + lives -=1; + if (lives < 0) {//game over if no lives left + gameOver = true; + } else { + pos = new PVector(13*16+8, 23*16 +8); //reset positions + + blinky = new Blinky(); + clyde = new Clyde(); + pinky = new Pinky(); + inky = new Inky(); + vel = new PVector(-1, 0); + turnTo = new PVector(-1, 0); + } + } + + //------------------------------------------------------------------------------------------------------------------------------------------------- + //returns whether pacman can move i.e. there is no wall in the direction of vel + boolean checkPosition() { + + if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position + + PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position + + //reset all the paths for all the ghosts + blinky.setPath(); + pinky.setPath(); + clyde.setPath(); + inky.setPath(); + + //check if the position has been eaten or not, note the blank spaces are initialised as already eaten + if (!tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].eaten) { + tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].eaten =true; + score +=1;//add a point + if (tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].bigDot) {//if big dot eaten + //set all ghosts to frightened + blinky.frightened = true; + blinky.flashCount = 0; + clyde.frightened = true; + clyde.flashCount = 0; + pinky.frightened = true; + pinky.flashCount = 0; + inky.frightened = true; + inky.flashCount = 0; + } + } + + + PVector positionToCheck= new PVector(matrixPosition.x + turnTo.x, matrixPosition.y+ turnTo.y); // the position in the tiles double array that the player is turning towards + + if (tiles[floor(positionToCheck.y)][floor(positionToCheck.x)].wall) {//check if there is a free space in the direction that it is going to turn + if (tiles[floor(matrixPosition.y + vel.y)][floor(matrixPosition.x + vel.x)].wall) {//if not check if the path ahead is free + return false;//if neither are free then dont move + } else {//forward is free + return true; + } + } else {//free to turn + vel = new PVector(turnTo.x, turnTo.y); + return true; + } + } else { + if ((pos.x+10*vel.x-8)%16 == 0 && (pos.y + 10*vel.y - 8)% 16 ==0) {//if 10 places off a critical position in the direction that pacman is moving + PVector matrixPosition = new PVector((pos.x+10*vel.x-8)/16, (pos.y+10*vel.y-8)/16);//convert that position to an array position + if (!tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].eaten ) {//if that tile has not been eaten + tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].eaten =true;//eat it + score +=dotScore; + println("Score:", score); + if (tiles[floor(matrixPosition.y)][floor(matrixPosition.x)].bigDot) {//big dot eaten + //set all ghosts as frightened + blinky.frightened = true; + blinky.flashCount = 0; + clyde.frightened = true; + clyde.flashCount = 0; + pinky.frightened = true; + pinky.flashCount = 0; + inky.frightened = true; + inky.flashCount = 0; + } + } + } + if (turnTo.x + vel.x == 0 && vel.y + turnTo.y ==0) {//if turning chenging directions entirely i.e. 180 degree turn + vel = new PVector(turnTo.x, turnTo.y);//turn + return true; + } + return true;//if not on a critical postion then continue forward + } + + } } \ No newline at end of file diff --git a/PacmanGame/PacmanGame.pde b/PacmanGame/PacmanGame.pde index df7af83..0c63ac2 100644 --- a/PacmanGame/PacmanGame.pde +++ b/PacmanGame/PacmanGame.pde @@ -1,269 +1,275 @@ -//import stuff for pathfinding -import java.util.Deque; -import java.util.Iterator; -import java.util.LinkedList; - -Pacman pacman; -PImage img;//background image - -Pinky pinky; -Blinky blinky; -Clyde clyde; -Inky inky; -Tile[][] tiles = new Tile[31][28]; //note it goes y then x because of how I inserted the data -int[][] tilesRepresentation = { - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - {1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1}, - {1, 8, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 8, 1}, - {1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - {1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1}, - {1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1}, - {1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1}, - {1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 6, 1, 1, 6, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 6, 1, 1, 6, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 0, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 0, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, - {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - {1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1}, - {1, 8, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 8, 1}, - {1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1}, - {1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1}, - {1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1}, - {1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1}, - {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, - {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, - {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, - {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};//its not sexy but it does the job -//-------------------------------------------------------------------------------------------------------------------------------------------------- - -void setup() { - frameRate(100); - size(448, 496); - img = loadImage("map.jpg"); - //initiate tiles - for (int i = 0; i< 28; i++) { - for (int j = 0; j< 31; j++) { - tiles[j][i] = new Tile(16*i +8, 16*j+8); - switch(tilesRepresentation[j][i]) { - case 1: //1 is a wall - tiles[j][i].wall = true; - break; - case 0: // 0 is a dot - tiles[j][i].dot = true; - break; - case 8: // 8 is a big dot - tiles[j][i].bigDot = true; - break; - case 6://6 is a blank space - tiles[j][i].eaten = true; - break; - } - } - } - - pacman = new Pacman(); - pinky = new Pinky(); - blinky = new Blinky(); - clyde = new Clyde(); - inky = new Inky(); -} -//-------------------------------------------------------------------------------------------------------------------------------------------------- - -void draw() { - image(img, 0, 0); - if (!pacman.gameOver) { - stroke(255); - - for (int i = 0; i< 28; i++) { - for (int j = 0; j< 31; j++) { - tiles[j][i].show(); - } - } - pacman.move(); - - //move and show the ghosts - inky.show(); - inky.move(); - - clyde.show(); - clyde.move(); - - pinky.show(); - pinky.move(); - - blinky.show(); - blinky.move(); - - //show pacman last so he appears over the path lines - pacman.show(); - } -} -//-------------------------------------------------------------------------------------------------------------------------------------------------- - -void keyPressed() {//controls for pacman - switch(key) { - case CODED: - switch(keyCode) { - case UP: - pacman.turnTo = new PVector(0, -1); - pacman.turn = true; - break; - case DOWN: - pacman.turnTo = new PVector(0, 1); - pacman.turn = true; - break; - case LEFT: - pacman.turnTo = new PVector(-1, 0); - pacman.turn = true; - break; - case RIGHT: - pacman.turnTo = new PVector(1, 0); - pacman.turn = true; - break; - } - } -} -//-------------------------------------------------------------------------------------------------------------------------------------------------- - - -//returns the nearest non wall tile to the input vector -//input is in tile coordinates -PVector getNearestNonWallTile(PVector target) { - float min = 1000; - int minIndexj = 0; - int minIndexi = 0; - for (int i = 0; i< 28; i++) {//for each tile - for (int j = 0; j< 31; j++) { - if (!tiles[j][i].wall) {//if its not a wall - if (dist(i, j, target.x, target.y) big = new LinkedList();//stores all paths - Path extend = new Path(); //a temp Path which is to be extended by adding another node - Path winningPath = new Path(); //the final path - Path extended = new Path(); //the extended path - LinkedList sorting = new LinkedList();///used for sorting paths by their distance to the target - - //startin off with big storing a path with only the starting node - extend.addToTail(start, finish); - extend.velAtLast = new PVector(vel.x, vel.y);//used to prevent ghosts from doing a u turn - big.add(extend); - - - boolean winner = false;//has a path from start to finish been found - - while (true) //repeat the process until ideal path is found or there is not path found - { - extend = big.pop();//grab the front path form the big to be extended - if (extend.path.getLast().equals(finish)) //if goal found - { - if (!winner) //if first goal found, set winning path - { - winner = true; - winningPath = extend.clone(); - } else { //if current path found the goal in a shorter distance than the previous winner - if (winningPath.distance > extend.distance) - { - winningPath = extend.clone();//set this path as the winning path - } - } - if (big.isEmpty()) //if this extend is the last path then return the winning path - { - return winningPath.clone(); - } else {//if not the current extend is useless to us as it cannot be extended since its finished - extend = big.pop();//so get the next path - } - } - - - //if the final node in the path has already been checked and the distance to it was shorter than this path has taken to get there than this path is no good - if (!extend.path.getLast().checked || extend.distance < extend.path.getLast().smallestDistToPoint) - { - if (!winner || extend.distance + dist(extend.path.getLast().x, extend.path.getLast().y, finish.x, finish.y) < winningPath.distance) //dont look at paths that are longer than a path which has already reached the goal - { - - //if this is the first path to reach this node or the shortest path to reach this node then set the smallest distance to this point to the distance of this path - extend.path.getLast().smallestDistToPoint = extend.distance; - - //move all paths to sorting form big then add the new paths (in the for loop)and sort them back into big. - sorting = (LinkedList)big.clone(); - Node tempN = new Node(0, 0);//reset temp node - if (extend.path.size() >1) { - tempN = extend.path.get(extend.path.size() -2);//set the temp node to be the second last node in the path - } - - for (int i =0; i< extend.path.getLast().edges.size(); i++) //for each node incident (connected) to the final node of the path to be extended - { - if (tempN != extend.path.getLast().edges.get(i))//if not going backwards i.e. the new node is not the previous node behind it - { - - //if the direction to the new node is in the opposite to the way the path was heading then dont count this path - PVector directionToNode = new PVector( extend.path.getLast().edges.get(i).x -extend.path.getLast().x, extend.path.getLast().edges.get(i).y - extend.path.getLast().y ); - directionToNode.limit(vel.mag()); - if (directionToNode.x == -1* extend.velAtLast.x && directionToNode.y == -1* extend.velAtLast.y ) { - } else {//if not turning around - extended = extend.clone(); - extended.addToTail(extend.path.getLast().edges.get(i), finish); - extended.velAtLast = new PVector(directionToNode.x, directionToNode.y); - sorting.add(extended.clone());//add this extended list to the list of paths to be sorted - } - } - } - - - //sorting now contains all the paths form big plus the new paths which where extended - //adding the path which has the higest distance to big first so that its at the back of big. - //using selection sort i.e. the easiest and worst sorting algorithm - big.clear(); - while (!sorting.isEmpty()) - { - float max = -1; - int iMax = 0; - for (int i = 0; i < sorting.size(); i++) - { - if (max < sorting.get(i).distance + sorting.get(i).distToFinish)//A* uses the distance from the goal plus the paths length to determine the sorting order - { - iMax = i; - max = sorting.get(i).distance + sorting.get(i).distToFinish; - } - } - big.addFirst(sorting.remove(iMax).clone());//add it to the front so that the ones with the greatest distance end up at the back - //and the closest ones end up at the front - } - } - extend.path.getLast().checked = true; - } - //if no more paths avaliable - if (big.isEmpty()) { - if (winner ==false) //there is not path from start to finish - { - print("FUCK!!!!!!!!!!");//error message - return null; - } else {//if winner is found then the shortest winner is stored in winning path so return that - return winningPath.clone(); - } - } - } +//import stuff for pathfinding +import java.util.Deque; +import java.util.Iterator; +import java.util.LinkedList; +int score=-1; +int dotScore=10; +int ghostScore=100; +Pacman pacman; +PImage img;//background image + +Pinky pinky; +Blinky blinky; +Clyde clyde; +Inky inky; +Tile[][] tiles = new Tile[31][28]; //note it goes y then x because of how I inserted the data +int[][] tilesRepresentation = { + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1}, + {1, 8, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 8, 1}, + {1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1}, + {1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1}, + {1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 6, 1, 1, 6, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 6, 1, 1, 6, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 0, 6, 6, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 0, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, + {1, 1, 1, 1, 1, 1, 0, 1, 1, 6, 1, 1, 1, 1, 1, 1, 1, 1, 6, 1, 1, 0, 1, 1, 1, 1, 1, 1}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1}, + {1, 8, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 8, 1}, + {1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1}, + {1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1}, + {1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1}, + {1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1}, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, + {1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1}, + {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, + {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};//its not sexy but it does the job +//-------------------------------------------------------------------------------------------------------------------------------------------------- + +void setup() { + frameRate(100); + size(448, 496); + img = loadImage("map.jpg"); + textFont(loadFont("Monospaced-15.vlw")); + //initiate tiles + for (int i = 0; i< 28; i++) { + for (int j = 0; j< 31; j++) { + tiles[j][i] = new Tile(16*i +8, 16*j+8); + switch(tilesRepresentation[j][i]) { + case 1: //1 is a wall + tiles[j][i].wall = true; + break; + case 0: // 0 is a dot + tiles[j][i].dot = true; + break; + case 8: // 8 is a big dot + tiles[j][i].bigDot = true; + break; + case 6://6 is a blank space + tiles[j][i].eaten = true; + break; + } + } + } + + pacman = new Pacman(); + pinky = new Pinky(); + blinky = new Blinky(); + clyde = new Clyde(); + inky = new Inky(); +} +//-------------------------------------------------------------------------------------------------------------------------------------------------- + +void draw() { + image(img, 0, 0); + stroke(255); + + for (int i = 0; i< 28; i++) { + for (int j = 0; j< 31; j++) { + tiles[j][i].show(); + } + } + if (!pacman.gameOver) { + pacman.move(); + } + //move and show the ghosts + inky.show(); + inky.move(); + + clyde.show(); + clyde.move(); + + pinky.show(); + pinky.move(); + + blinky.show(); + blinky.move(); + + //show pacman last so he appears over the path lines + pacman.show(); + fill(255); + stroke(255); + textAlign(LEFT,TOP); + text("Score: "+score,0,0); + //} +} +//-------------------------------------------------------------------------------------------------------------------------------------------------- + +void keyPressed() {//controls for pacman + switch(key) { + case CODED: + switch(keyCode) { + case UP: + pacman.turnTo = new PVector(0, -1); + pacman.turn = true; + break; + case DOWN: + pacman.turnTo = new PVector(0, 1); + pacman.turn = true; + break; + case LEFT: + pacman.turnTo = new PVector(-1, 0); + pacman.turn = true; + break; + case RIGHT: + pacman.turnTo = new PVector(1, 0); + pacman.turn = true; + break; + } + } +} +//-------------------------------------------------------------------------------------------------------------------------------------------------- + + +//returns the nearest non wall tile to the input vector +//input is in tile coordinates +PVector getNearestNonWallTile(PVector target) { + float min = 1000; + int minIndexj = 0; + int minIndexi = 0; + for (int i = 0; i< 28; i++) {//for each tile + for (int j = 0; j< 31; j++) { + if (!tiles[j][i].wall) {//if its not a wall + if (dist(i, j, target.x, target.y) big = new LinkedList();//stores all paths + Path extend = new Path(); //a temp Path which is to be extended by adding another node + Path winningPath = new Path(); //the final path + Path extended = new Path(); //the extended path + LinkedList sorting = new LinkedList();///used for sorting paths by their distance to the target + + //startin off with big storing a path with only the starting node + extend.addToTail(start, finish); + extend.velAtLast = new PVector(vel.x, vel.y);//used to prevent ghosts from doing a u turn + big.add(extend); + + + boolean winner = false;//has a path from start to finish been found + + while (true) //repeat the process until ideal path is found or there is not path found + { + extend = big.pop();//grab the front path form the big to be extended + if (extend.path.getLast().equals(finish)) //if goal found + { + if (!winner) //if first goal found, set winning path + { + winner = true; + winningPath = extend.clone(); + } else { //if current path found the goal in a shorter distance than the previous winner + if (winningPath.distance > extend.distance) + { + winningPath = extend.clone();//set this path as the winning path + } + } + if (big.isEmpty()) //if this extend is the last path then return the winning path + { + return winningPath.clone(); + } else {//if not the current extend is useless to us as it cannot be extended since its finished + extend = big.pop();//so get the next path + } + } + + + //if the final node in the path has already been checked and the distance to it was shorter than this path has taken to get there than this path is no good + if (!extend.path.getLast().checked || extend.distance < extend.path.getLast().smallestDistToPoint) + { + if (!winner || extend.distance + dist(extend.path.getLast().x, extend.path.getLast().y, finish.x, finish.y) < winningPath.distance) //dont look at paths that are longer than a path which has already reached the goal + { + + //if this is the first path to reach this node or the shortest path to reach this node then set the smallest distance to this point to the distance of this path + extend.path.getLast().smallestDistToPoint = extend.distance; + + //move all paths to sorting form big then add the new paths (in the for loop)and sort them back into big. + sorting = (LinkedList)big.clone(); + Node tempN = new Node(0, 0);//reset temp node + if (extend.path.size() >1) { + tempN = extend.path.get(extend.path.size() -2);//set the temp node to be the second last node in the path + } + + for (int i =0; i< extend.path.getLast().edges.size(); i++) //for each node incident (connected) to the final node of the path to be extended + { + if (tempN != extend.path.getLast().edges.get(i))//if not going backwards i.e. the new node is not the previous node behind it + { + + //if the direction to the new node is in the opposite to the way the path was heading then dont count this path + PVector directionToNode = new PVector( extend.path.getLast().edges.get(i).x -extend.path.getLast().x, extend.path.getLast().edges.get(i).y - extend.path.getLast().y ); + directionToNode.limit(vel.mag()); + if (directionToNode.x == -1* extend.velAtLast.x && directionToNode.y == -1* extend.velAtLast.y ) { + } else {//if not turning around + extended = extend.clone(); + extended.addToTail(extend.path.getLast().edges.get(i), finish); + extended.velAtLast = new PVector(directionToNode.x, directionToNode.y); + sorting.add(extended.clone());//add this extended list to the list of paths to be sorted + } + } + } + + + //sorting now contains all the paths form big plus the new paths which where extended + //adding the path which has the higest distance to big first so that its at the back of big. + //using selection sort i.e. the easiest and worst sorting algorithm + big.clear(); + while (!sorting.isEmpty()) + { + float max = -1; + int iMax = 0; + for (int i = 0; i < sorting.size(); i++) + { + if (max < sorting.get(i).distance + sorting.get(i).distToFinish)//A* uses the distance from the goal plus the paths length to determine the sorting order + { + iMax = i; + max = sorting.get(i).distance + sorting.get(i).distToFinish; + } + } + big.addFirst(sorting.remove(iMax).clone());//add it to the front so that the ones with the greatest distance end up at the back + //and the closest ones end up at the front + } + } + extend.path.getLast().checked = true; + } + //if no more paths avaliable + if (big.isEmpty()) { + if (winner ==false) //there is not path from start to finish + { + return null; + } else {//if winner is found then the shortest winner is stored in winning path so return that + return winningPath.clone(); + } + } + } } \ No newline at end of file diff --git a/PacmanGame/Path.pde b/PacmanGame/Path.pde old mode 100644 new mode 100755 diff --git a/PacmanGame/Pinky.pde b/PacmanGame/Pinky.pde index 5472bba..e8cec78 100644 --- a/PacmanGame/Pinky.pde +++ b/PacmanGame/Pinky.pde @@ -1,233 +1,234 @@ -class Pinky { - PVector pos = new PVector(23*16 +8, 26*16+8); - PVector vel = new PVector(1, 0); - Path bestPath; // the variable stores the path the ghost will be following - ArrayList ghostNodes = new ArrayList();//the nodes making up the path including the ghosts position and the target position - Node start;//the ghosts position as a node - Node end; //the ghosts target position as a node - color colour = color(255, 0, 255);//pink - - - boolean chase = true;//true if the ghost is in chase mode false if in scatter mode - boolean frightened = false;//true if the ghost is in frightened mode - int flashCount = 0;//in order to make the ghost flash when frightened this is a counter - int chaseCount = 0;//counter for the switch between chase and scatter - boolean returnHome = false;//if eaten return home - boolean deadForABit = false;//after the ghost returns home it disappears for a bit - int deadCount = 0; - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //constructor - Pinky() { - setPath(); - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - - void show() { - //increments counts - chaseCount ++; - if (chase) { - if (chaseCount > 2000) { - chase = false; - chaseCount = 0; - } - } else { - if (chaseCount > 700) { - chase = true; - chaseCount = 0; - } - } - - - - if (deadForABit) { - deadCount ++; - if (deadCount > 300) { - deadForABit = false; - } - } else {//if not deadforabit then show the ghost - if (!frightened) { - if (returnHome) {//have the ghost be transparent if on its way home - stroke(colour, 100); - fill(colour, 100); - } else {// colour the ghost - stroke(colour); - fill(colour); - } - bestPath.show();//show the path the ghost is following - } else {//if frightened - flashCount ++; - if (flashCount > 800) {//after 8 seconds the ghosts are no longer frightened - frightened = false; - flashCount = 0; - } - - if (floor(flashCount / 30) %2 ==0) {//make it flash white and blue every 30 frames - stroke(255); - fill(255); - } else {//flash blue - stroke(0, 0, 200); - fill(0, 0, 200); - } - } - ellipse(pos.x, pos.y, 20, 20);//draw the ghost as a circle - } - } - - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //moves the ghost along the path - void move() { - if (!deadForABit) {//dont move if dead - pos.add(vel); - checkDirection();//check if need to change direction next move - } - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - - //calculates a path from the first node in ghost nodes to the last node in ghostNodes and sets it as best path - void setPath() { - ghostNodes.clear(); - setNodes(); - start = ghostNodes.get(0); - end = ghostNodes.get(ghostNodes.size()-1); - Path temp = AStar(start, end, vel); - if (temp!= null) {//if not path is found then dont change bestPath - bestPath = temp.clone(); - } - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //sets all the nodes and connects them with adjacent nodes - //also sets the target node - void setNodes() { - - ghostNodes.add(new Node((pos.x-8)/16, (pos.y-8)/16));//add the current position as a node - for (int i = 1; i< 27; i++) {//check every position - for (int j = 1; j< 30; j++) { - //if there is a space up or below and a space left or right then this space is a node - if (!tiles[j][i].wall) { - if (!tiles[j-1][i].wall || !tiles[j+1][i].wall) { //check up for space - if (!tiles[j][i-1].wall || !tiles[j][i+1].wall) {//check left and right for space - - ghostNodes.add(new Node(i, j));//add the nodes - } - } - } - } - } - if (returnHome) {//if returning home then the target is just above the ghost room thing - ghostNodes.add(new Node(13, 11)); - } else { - if (chase) { - //pinky moves towards 4 positions ahead of pacman - int lookAhead = 4; - PVector pacmanMatrixPos = new PVector((pacman.pos.x-8) / 16 + (pacman.vel.x *lookAhead), (pacman.pos.y-8 )/16 +(pacman.vel.y *lookAhead)); - while (!(pacmanMatrixPos.x > 0 && pacmanMatrixPos.y >0 && pacmanMatrixPos.x <28 - && pacmanMatrixPos.y < 31 && !tiles[(int)pacmanMatrixPos.y][(int)pacmanMatrixPos.x].wall)) { - lookAhead -=1; - pacmanMatrixPos = new PVector((pacman.pos.x-8) / 16 + (pacman.vel.x *lookAhead), (pacman.pos.y-8 )/16 +(pacman.vel.y *lookAhead)); - } - if (dist((pos.x-8)/16, (pos.y-8)/16, pacmanMatrixPos.x, pacmanMatrixPos.y)<1) { - ghostNodes.add(new Node((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16)); - } else { - - ghostNodes.add(new Node(pacmanMatrixPos.x, pacmanMatrixPos.y));//(pacman.pos.x-8 + (pacman.vel.x *4)) / 16, (pacman.pos.y-8 +(pacman.vel.y *4))/16)); - } - } else {//scatter - ghostNodes.add(new Node(26, 1)); - } - } - - for (int i = 0; i< ghostNodes.size(); i++) {//connect all the nodes together - ghostNodes.get(i).addEdges(ghostNodes); - } - } - //-------------------------------------------------------------------------------------------------------------------------------------------------- - //check if the ghost needs to change direction as well as other stuff - void checkDirection() { - if (pacman.hitPacman(pos)) {//if hit pacman - if (frightened) {//eaten by pacman - returnHome = true; - frightened = false; - } else if (!returnHome) {//killPacman - pacman.kill(); - } - } - - - // check if reached home yet - if (returnHome) { - if (dist((pos.x-8)/16, (pos.y - 8)/16, 13, 11) < 1) { - //set the ghost as dead for a bit - returnHome = false; - deadForABit = true; - deadCount = 0; - } - } - - if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position - - PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position - - if (frightened) {//no path needs to generated by the ghost if frightened - boolean isNode = false; - for (int j = 0; j < ghostNodes.size(); j++) { - if (matrixPosition.x == ghostNodes.get(j).x && matrixPosition.y == ghostNodes.get(j).y) { - isNode = true; - } - } - if (!isNode) {//if not on a node then no need to do anything - return; - } else {//if on a node - //set a random direction - PVector newVel = new PVector(); - int rand = floor(random(4)); - switch(rand) { - case 0: - newVel = new PVector(1, 0); - break; - case 1: - newVel = new PVector(0, 1); - break; - case 2: - newVel = new PVector(-1, 0); - break; - case 3: - newVel = new PVector(0, -1); - break; - } - //if the random velocity is into a wall or in the opposite direction then choose another one - while (tiles[floor(matrixPosition.y + newVel.y)][floor(matrixPosition.x + newVel.x)].wall || (newVel.x +2*vel.x ==0 && newVel.y + 2*vel.y ==0)) { - rand = floor(random(4)); - switch(rand) { - case 0: - newVel = new PVector(1, 0); - break; - case 1: - newVel = new PVector(0, 1); - break; - case 2: - newVel = new PVector(-1, 0); - break; - case 3: - newVel = new PVector(0, -1); - break; - } - } - vel = new PVector(newVel.x/2, newVel.y/2);//halve the speed - } - } else {//not frightened - - setPath(); - - for (int i =0; i< bestPath.path.size(); i++) {//if currently on a node turn towards the direction of the next node in the path - if (matrixPosition.x == bestPath.path.get(i).x && matrixPosition.y == bestPath.path.get(i).y) { - - vel = new PVector(bestPath.path.get(i+1).x - matrixPosition.x, bestPath.path.get(i+1).y - matrixPosition.y); - vel.limit(1); - - return; - } - } - } - } - } +class Pinky { + PVector pos = new PVector(23*16 +8, 26*16+8); + PVector vel = new PVector(1, 0); + Path bestPath; // the variable stores the path the ghost will be following + ArrayList ghostNodes = new ArrayList();//the nodes making up the path including the ghosts position and the target position + Node start;//the ghosts position as a node + Node end; //the ghosts target position as a node + color colour = color(255, 0, 255);//pink + + + boolean chase = true;//true if the ghost is in chase mode false if in scatter mode + boolean frightened = false;//true if the ghost is in frightened mode + int flashCount = 0;//in order to make the ghost flash when frightened this is a counter + int chaseCount = 0;//counter for the switch between chase and scatter + boolean returnHome = false;//if eaten return home + boolean deadForABit = false;//after the ghost returns home it disappears for a bit + int deadCount = 0; + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //constructor + Pinky() { + setPath(); + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + + void show() { + //increments counts + chaseCount ++; + if (chase) { + if (chaseCount > 2000) { + chase = false; + chaseCount = 0; + } + } else { + if (chaseCount > 700) { + chase = true; + chaseCount = 0; + } + } + + + + if (deadForABit) { + deadCount ++; + if (deadCount > 300) { + deadForABit = false; + } + } else {//if not deadforabit then show the ghost + if (!frightened) { + if (returnHome) {//have the ghost be transparent if on its way home + stroke(colour, 100); + fill(colour, 100); + } else {// colour the ghost + stroke(colour); + fill(colour); + } + bestPath.show();//show the path the ghost is following + } else {//if frightened + flashCount ++; + if (flashCount > 800) {//after 8 seconds the ghosts are no longer frightened + frightened = false; + flashCount = 0; + } + + if (floor(flashCount / 30) %2 ==0) {//make it flash white and blue every 30 frames + stroke(255); + fill(255); + } else {//flash blue + stroke(0, 0, 200); + fill(0, 0, 200); + } + } + ellipse(pos.x, pos.y, 20, 20);//draw the ghost as a circle + } + } + + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //moves the ghost along the path + void move() { + if (!deadForABit) {//dont move if dead + pos.add(vel); + checkDirection();//check if need to change direction next move + } + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + + //calculates a path from the first node in ghost nodes to the last node in ghostNodes and sets it as best path + void setPath() { + ghostNodes.clear(); + setNodes(); + start = ghostNodes.get(0); + end = ghostNodes.get(ghostNodes.size()-1); + Path temp = AStar(start, end, vel); + if (temp!= null) {//if not path is found then dont change bestPath + bestPath = temp.clone(); + } + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //sets all the nodes and connects them with adjacent nodes + //also sets the target node + void setNodes() { + + ghostNodes.add(new Node((pos.x-8)/16, (pos.y-8)/16));//add the current position as a node + for (int i = 1; i< 27; i++) {//check every position + for (int j = 1; j< 30; j++) { + //if there is a space up or below and a space left or right then this space is a node + if (!tiles[j][i].wall) { + if (!tiles[j-1][i].wall || !tiles[j+1][i].wall) { //check up for space + if (!tiles[j][i-1].wall || !tiles[j][i+1].wall) {//check left and right for space + + ghostNodes.add(new Node(i, j));//add the nodes + } + } + } + } + } + if (returnHome) {//if returning home then the target is just above the ghost room thing + ghostNodes.add(new Node(13, 11)); + } else { + if (chase) { + //pinky moves towards 4 positions ahead of pacman + int lookAhead = 4; + PVector pacmanMatrixPos = new PVector((pacman.pos.x-8) / 16 + (pacman.vel.x *lookAhead), (pacman.pos.y-8 )/16 +(pacman.vel.y *lookAhead)); + while (!(pacmanMatrixPos.x > 0 && pacmanMatrixPos.y >0 && pacmanMatrixPos.x <28 + && pacmanMatrixPos.y < 31 && !tiles[(int)pacmanMatrixPos.y][(int)pacmanMatrixPos.x].wall)) { + lookAhead -=1; + pacmanMatrixPos = new PVector((pacman.pos.x-8) / 16 + (pacman.vel.x *lookAhead), (pacman.pos.y-8 )/16 +(pacman.vel.y *lookAhead)); + } + if (dist((pos.x-8)/16, (pos.y-8)/16, pacmanMatrixPos.x, pacmanMatrixPos.y)<1) { + ghostNodes.add(new Node((pacman.pos.x-8) / 16, (pacman.pos.y-8)/16)); + } else { + + ghostNodes.add(new Node(pacmanMatrixPos.x, pacmanMatrixPos.y));//(pacman.pos.x-8 + (pacman.vel.x *4)) / 16, (pacman.pos.y-8 +(pacman.vel.y *4))/16)); + } + } else {//scatter + ghostNodes.add(new Node(26, 1)); + } + } + + for (int i = 0; i< ghostNodes.size(); i++) {//connect all the nodes together + ghostNodes.get(i).addEdges(ghostNodes); + } + } + //-------------------------------------------------------------------------------------------------------------------------------------------------- + //check if the ghost needs to change direction as well as other stuff + void checkDirection() { + if (pacman.hitPacman(pos)) {//if hit pacman + if (frightened) {//eaten by pacman + returnHome = true; + frightened = false; + score+=ghostScore; + } else if (!returnHome) {//killPacman + pacman.kill(); + } + } + + + // check if reached home yet + if (returnHome) { + if (dist((pos.x-8)/16, (pos.y - 8)/16, 13, 11) < 1) { + //set the ghost as dead for a bit + returnHome = false; + deadForABit = true; + deadCount = 0; + } + } + + if ((pos.x-8)%16 == 0 && (pos.y - 8)% 16 ==0) {//if on a critical position + + PVector matrixPosition = new PVector((pos.x-8)/16, (pos.y - 8)/16);//convert position to an array position + + if (frightened) {//no path needs to generated by the ghost if frightened + boolean isNode = false; + for (int j = 0; j < ghostNodes.size(); j++) { + if (matrixPosition.x == ghostNodes.get(j).x && matrixPosition.y == ghostNodes.get(j).y) { + isNode = true; + } + } + if (!isNode) {//if not on a node then no need to do anything + return; + } else {//if on a node + //set a random direction + PVector newVel = new PVector(); + int rand = floor(random(4)); + switch(rand) { + case 0: + newVel = new PVector(1, 0); + break; + case 1: + newVel = new PVector(0, 1); + break; + case 2: + newVel = new PVector(-1, 0); + break; + case 3: + newVel = new PVector(0, -1); + break; + } + //if the random velocity is into a wall or in the opposite direction then choose another one + while (tiles[floor(matrixPosition.y + newVel.y)][floor(matrixPosition.x + newVel.x)].wall || (newVel.x +2*vel.x ==0 && newVel.y + 2*vel.y ==0)) { + rand = floor(random(4)); + switch(rand) { + case 0: + newVel = new PVector(1, 0); + break; + case 1: + newVel = new PVector(0, 1); + break; + case 2: + newVel = new PVector(-1, 0); + break; + case 3: + newVel = new PVector(0, -1); + break; + } + } + vel = new PVector(newVel.x/2, newVel.y/2);//halve the speed + } + } else {//not frightened + + setPath(); + + for (int i =0; i< bestPath.path.size(); i++) {//if currently on a node turn towards the direction of the next node in the path + if (matrixPosition.x == bestPath.path.get(i).x && matrixPosition.y == bestPath.path.get(i).y) { + + vel = new PVector(bestPath.path.get(i+1).x - matrixPosition.x, bestPath.path.get(i+1).y - matrixPosition.y); + vel.limit(1); + + return; + } + } + } + } + } } \ No newline at end of file diff --git a/PacmanGame/Tile.pde b/PacmanGame/Tile.pde old mode 100644 new mode 100755 diff --git a/PacmanGame/data/Monospaced-15.vlw b/PacmanGame/data/Monospaced-15.vlw new file mode 100644 index 0000000000000000000000000000000000000000..2493b480bb678f6a96bb28de7b34e54cf7f227cb GIT binary patch literal 33312 zcmc&+2S8Lu^Pf99h$4t6SO9y$-h080y@f%j(K&hG5)?CxwoB25h&yg43AJYR<^1~(4J?q7#14mVo^9>=Fb z2@sqXHJ!|3nxP~fj*s~)lVxb}FqFc>JkIOaywY$})6YDnRVV`@GhSKnSSPAXPKO$w zhn{A6{IEY2%7Nf8YBMId3GS>X3p|__rd3{L z&@3|}UKO}!Rz_99GnC2xRGHO4XO_0l&WM}2&1*>X6wzI4)u%P0?>!TVZg zYPc>&!g(9vbv454W`x&W&r|bYSU-D!_qE;Sw#e`u9@e#CICf{~iRbHZYP;P!GYtI!7o+X&Sbu>Mq;eL=IGSL-K-SM}Ksv|&08%c#GRa04<32PX+W zcpLUB9G?mUK^T^;VSL|%XP6elaD$A58?5K4x;B*g19*0LRGk{~h8PJq)Cg}Fc$_xF z2cHs#3Rg*Subij*`HbtzM#L(yUH7>=Vc`GNASLmPc8RR;AK|sqrv04rKZKO zo{cdQZY+3)`DR*$pFm{B8)t+!9z4#wVOV@hn1DCCXO#DeaOZZU@>qtdv!6k8xvTAk z%Ux}olR#${ZZde8^^H@(Q_G0;#xhly27=?_hr{!umcw)dcTOwUMHPMl!D;4)(^Lq} z`U5b-3_R@4`N+xbodGn%Ogx!|n`MMI+X(MhBfL3Acyo>Lelx0>7~usN;jJ{nTV;f|+6Zrr5#Cw@ z9@_-Nwz3Z2Y{R$@WS&Zcr1f}s{G!?k=5gC$*no%Iv1$jIR(Tus?kbPvs=Q60Sq{6Y z@qv`E8E=j+qqJ<%^VGB$#tFQ}1W@*_Co?$pniwb){upBi`!*uS2yP7wSk7*SS zfM8v+{bnAA<8Ow8cvPFLrd2JYLtpVY%_+@^G0|VjPU*fkJGKD(@>um z;cnQca$eQ^T>{N{Q0pMmDlf_vjdY+m;L*7#(yk|yu&yDb27~#DHFEgD*fM;myIG-v+ zg5Y$kc`z)aS8&g)j9wezy)nXjYlIgCo?%)H_4&>SFWLw%#t4tcy_v-q3tndB9%qCX zZ-n>J2rt11FA+S|mZ^0VP7;#vHnfq3{n#h)I1g&s8tOCI2rtD5FVzSy4LqJZsA=T* zRN#3;COi%I-BXYj?>7Gnii(jw6Hu5r{=*xXByU*Vc8nyFSn6!`M@)bkHe|) zhT#m;=>i_7Qw?XB&Wd_>wVtsbHNKjlGm}}%2(K=9oNiTThI!yMV~$_tF|Eee5H$N$ z^=b2Y4y$Pd_pigji9ypCFHWbLKc@Ad@dRO5woI$>H3OYlxaQz-e5zgy)7cL0T=p5o z*Bj9d-RlOMMlh-2+!x`$VH+Y8eaUNBfeZC537++uT4D)9wvmbcuSJjuH zKKtu=Y90)E1HKB!GW4MF0by8vOzS~290a#rHSgTU)jW&<&2m(pp`x6h z5_D$y8wVcyRrANR9yH@Y@VA;jLzxrcZYa|*EfbA|<8?5@GBVWlBqQN?9W1kOydJ`O zQuA&gQ_FJ%?jzXVa~_$euY=l)pflrz8R6a2%iyr8Or`NHJo8OdHl&d?w8c~^fFDh!7Y>aZgLvDOxnAv19&V$<+E%(xaI+o8ISu8 zPNSL!PAAg<*Zg>8#&ZUb<50sfR>Kw0hs&V+TnifE6#|cCs^M6kzRtV4f;Oz5tRt1@ zW`ySs9?MkAf%UD|wQF(EYWmq-pMFr}S^=+2%E7fJc-)3~Ji_4&W!B1or|O0K6|O&O z7|xF#Tz7!rZ?$c5Iq1QS^I#a4fn9Rvys%$YX9jw4=X~?GS|<#{v2Dqujk&YE%8bY5 zo=G0ux%||0a2`1?dT=iTg5|3DGq64G&Ed{zRrO*Rt`FSR^fRAnJ-D+?R>P=R<@sdJ zD|Q(?PKz4Gpd6BT+?q*zDO}%J*J>UN$|yxGhrjb;Mw9qi|DQIVVusM^HV^`+7Qds0 zI8ddNhU&ClBTVVYTf_H|9VRq!HM~{XOf}cZ01E@?kHygO;lnW@hhuLZb}*Z?NK9Lr zHhB`V&@qg9T$kP!tRnv(N-DT5cfPVT?FMaQ%~clP?_*4p--z8jbR)}?dz0Cw zgFoh_3AC{-cQ@g zmCDQ9c!l&kGn#S?9k1=Mvw!K|U#~&S^xXe+f+JD;JLkLPU-D+NO@oR=)7F+hGJ`_~ zSD>~>nD}*>+wLJuIIn)Wkn0=eRr1z9B<5omd=~yxmn|Xf{PAEaWkF<{<`t!1>@8((8h+JZ(teWzJC_&UCB|3lJsB_Isnk9r=Q>}X<<<;Pq z{s>sG2-gV5!5&q_2<5-Xj+St7K}B!$%9>1W`a-*}P+fNHhjYk6n4eEWQNKhVd`3*g z>l7ExlE3h(lv)_?qqFg9^p^8b$2Lb^qT;czwCgtFAW>Qsq8I3P2$(urF&NYgu9L=54= zseN7CYlf6>3U86x`rcmmrb}$$*>biwOOU~tSi!A{p}5*a-L|w7MBxjm`0KT=h!UpK zpCiVnYBgySJKZ)NB`oO8AI}@m^baVsPg{^(l;9#Aiv8gqXl`k9?0?pAx6>z1#49b; zw75)TmF3R@;XdN&tg9!19W-A6exCE&g96)LcHuxgKOL7 z9xnQ`RL$!(jB=;>GFtkItw6Iho^6zRKND}KC)=P(>&xf3nr{EtvsJ5BHJ(YKXNc^1 zBr^G}&_(`0Rf?GB>>&ohflQ-+!`tp>luf>flo+%{n?Zh&hj?g0xjxQfMBCBq&~rb% zxXa76{z@?>I8BJ8`6gsrvINHjDDyU0Vv|ePp*z6XKUVSRjY>SXmxT6$4@tuJvQ`oW z;~VG9x#3|>(*{beMY~*e0SBMgl?AMLW(gc>R18CoiQxqRnu(9tohC+a+5VF?O}xJO ziMV5pTB7MACNjh1^&v)d_7cAxQ`ZN07VLOJS2F}Lz4?wPU%A4v#=&qpNut(`RNb2j zM5U8|uVz{dMrre(91?JET&K-{)Bq8GIAO!f6i?EmrMZ%M=y@WUWB+)fyF0)iuQQ`R z-ay*E7qAdwo^!vrd2k*WFT^fx;WwyUC_ho1QQ#eZJA4|=Xhe!s z|E+!eQAM<8wCoMHM636|^T&6h93wzZ`L>#L{o~E4XaIkFEK4Ompq6aC493|#M4e%t z%BA$gSBJzGpV({^IUP1X^g;=ujiLCuxsx<+xeEO9!Ia+b1+n#`mbNDLw(lOd3s(CO zl=deNM#6dbL~a82EQvGW5l4F5TE z>+9IT+uaox<`GEwq_Yvmz)aq(9qX_ma1J%6g$`c0l)#TRx!3f=<;}Dp0545e1qlEW>yEmkM+(F+zbd z?N{i_s=^I%x+5AlpvIbnXX81SXv`z-Cbne4am@XA2yC$B(*xgJROO!Ta7#`^n?=Q$ z%b;PsJFHDexj)0gwWqyFW2Mv2l{f+JCi|XqM&`vtRB(NYxnn~lf9t!wF`ftVZoK!M6K-`Ou3GUcbit3Kk9%Q!?|t!zQ0r^YI11o z`M|C29J_NXpYJ*UlVaFz0d57~lh{qv6D64%agRdAGI7F>=3M}`OKC}-Y3O;o>AHAz zOzVq^nwFfLoVJI|-j4>~_oJ1ks0gBr@LC)ta~u(HEGFjVmj2ds>8Mj7&xk#AX+J#d z3Z+YD%GaKpG=eU%@flBa>9|YrArAu-e&+-W>92~YEAc2%_0q-0t>;9BTEKtG)Aeo; zLfU-j0}3BZNcptfA!>Is(|ld$y_enarGw0-2x+V7{WAG=XQxC5WizMlGNEJ|`lO+K z{k7z9Fhz*%@L*NefudtxURZ<%C*~BcwKA>=&oF z9|@7TXzCaeYuZhSx|f55h`TZu)^VmZJx^#sG+Q5MS`Z~uL<`{~kNW+_{sV{pIHA$IU-lQx_{-&a0vK6u?Bq}Sqv40Hf1?t^vozBr&psQ{%M>uMe z*bsLVjv>4wCn2Y() z(JTW#7lxt#uf8WsB~8y&po8dK0R2H7(EtJdYgD4u7%{F@$=Nb?80!Ws4(cgTV2)rp zG~qv^sYy1-J_S6ke7R}f@1oWex&moeTwzf>(A&t2_jucdGu}U`E7zi730|m$lg7q1 zL@@D4+%;#M_Hzp7O)RDbNhO$1wl7a>WUcFhQHoUD(P$@$izRLGiBWo}p+fe8^v`h@ zu66l`Dh1%ZEutsRh`fw2Jsy4<*$)?H$Jf6sOks0DtX(vr+P!W})CyfPO%j@?Nu?!0 zKOZH9Kqib67b3dA&$I~b!-NnG(eJ|K;h&Gt7c+5aXt18Je%D-ap-Bw9-BE9wKAwS5qkbAiwa5z(9$#I|cw5gkdo!HAN={`SqT z|2#9r)y(9h4>B`}X0%6U<{VL5nVBAlU~GaAOQpulDu-N9>J;TWSC3xA#5_L@BM`UP z$z!4zNpZiz_rp^X=ScjW;x^UoapY|!ibDl@;R1h)a@P&MY9jfi)A}g>cihv;aJ587 ziRsc66iq%@PqLc-F-3$lq%BV>m|E4VM6c(<5wQ75Rf+2*h~P<(OYSpniTBkNx2J0* zrs+{~|BM2n>C~_iE(7B?(UMpsWDiV`nOj+IA}bX$8ne;9K!G-*TY&=Ho7WJYihwL3 z9?gQVDbJ!70{t3m484_JK0Nn>&$+L!ZU71h8UL}pee1ZXmK#tlwm>Dh%GzNxfsq^L zD^Vj$iB2HAVB8=70b_eep3X5+iPF55HjO@KcV|=Z7>hO3Y zFy(poD8`8i8Z3rOto9Mh-A%Bgd$TjA;G4wrp!lKTvG#?*-i4EW+d8Rt+DAIrM+~yu4bzTD^KXb4Pef znY84Fg56S^E5xSL3NEKifG|(rtvJ-vl~jno^xAIwiQ5&5nU@UPOWl&YP`gjPsYPN}e6>$#qmed; zG@1ZC_^;Q)scSXis>PUk&^=R{l&P#^5yOErIrq8~0#49l}Fd+0y z*bzuPdnOJxJZH+8%@+&XE=@dB`Jygm-O4j)1w1OR-5M`||Jkqch;S|d|G1#G6ViOh zU+0F>xcYMw%;kDwvd-OfH9YX=#h$kfW?p z5@_QM*aOkwb)WIDhcCwCyQ^+m>+`YeCf8~6^_T;^9A-uJlkV&!Z7r9VH%>@n0CKEKV!y> z^};QeIJ=6Wuoi=R1+H$=6YW!fD{kXC4_`hpLYKe|lr(O|G2LPC7|<4RCbVjZRCi|4$1TAQOc^5P5=t?j(TJ5{Zl(&J#a*(bfl}Lz>{rCf% z%}bR*+H17|N#ys}m#F>q=p`n8uR;c|5}o~cM)h);-z#|8yG#1qy3ZJbzp728JX(5A z!48;_U>WVq87_5d*!zUuUF93aW_j|gx|Ao6RYS#~aLLqdh1ZG`c;$Aaqd`GSsh#%; z{*-RUUwhY^nAM2xvO&Zx%i&PS2CJh!vQASIQ3w(+PcXomg*$Bcdr3qwcAxjAH1pSr z=F;E02izK)hY_vP`TWXYffQR8PtFT&FPqNQoO$Lbs_i+t{)uI5vn%EHPKi#^dTD+{ySq7=Pp z_umksR{p#4_X>`?+8%aDx3c9jT_Y^g`xw6sO0aI`9I&0hRRK70Hl#t^X=+x)EijskSh=9XXz2m{k6SsQR3 zxPx#ExCPuxXAzKq@?7wY0>onLYSGgu;QYQcvB!o2PLVU4BVY+CwwE>vPMxlYPaQw6 zcG`-L$6}5K2E#tZAAR&I7{`t#aGEzCeKi$m-_gHdiZtOzeLgid86l3EtrN*tcBB0{ zm7ujifG?4gOVc2S^=}_G>Lzxs=|6jLH-S+Nn^-7`5e7nxV2m)m$dZ`QTw zxT#=koebbA7M0=W6UHh3WAqo z`zC+Tl4;z&w!*idU*bN~<05U{wtcXoJP33Ns*JZskQ;@viTmX-(ey_k?#_j)C{HCt zReXg_qlr&|>6Z|?%KoUFXeO#LLHC}7OH4DwJP=1Z$}g`Ihm}PCeroDvGOKDvXJncpUJ-ejHM`8WXwF>$U0@kj7L}9D9VrZF!y|^S_^c(1D+KC!ux^p}v z{;;0#y!`9Xq{|-1Ij>2zgehX z!Cj=g%?>cQ;wOdZn3A6fP0^d%rNd@%HJ>CL@<>EYt};w6)T2j_L7%vC)8;Tv_<+Q6 zc$ZE19&hVA+wi8<4|C$(N$2hE?(X=Dw7zl-?NqY_+()NI3vr!|#$j zn$OqhKm_7EAWq0?T69;uGxJKwS*7Ze5(l#*J@uDpLDHFSOw{UAw{PX!BQpORS=*&f zVfNYPkhJSR)pWmE@-F@Tl?~eh>x{ET< zq^B{vccaqLh-gJfWuDe(Ph(FJx-SWnSMcP8Wx-pRX;;jOw(L4|QdI} z0*We9GJaQysb6Rg`^X#PR!mf=BCS&52RGMb0hp4K`h+3)>-5o|XWrzGBADGXiSE+#j}Y6S%3;{?fFy_@WhsOS-!4 zrc)-*FxGfo6b;syS|PkPFZ3B=wzEY0t@HjVGX$?--}Gv;A=&YLj-{m7w5B zN)?c&W8l|{`+yPWPaSSZrlmT~yh^{H96l(O-jt-CsT6qRx2vgS-F;}je)Q`afO)H~ z04V`|J^ty}Hq>w_Y5X3!w1{o54G42-AKQlVCi&2@Lr7GU58dP@EL zwYrpZpeOdT8Cq6bA>n;La2Ue=MmXD;-%`NCOut=?cA1finyg#;T`sAjZmaSHc68fC zr>NW3@<-dVim2PQV`~)^bz5O^tEyB{tLfyqTCXC~WTql>4+Sg=-BF+jEC9-F|D8K! zHhNK$Q&Z;K&Yj`7;r%)^6Il@CaMX#@$TI_1yz|to*+z7zSu<~UrE!RU)&&bz5w!{y zvR4JW_j_eGSi*yy)eTilEaTlsBx`pH$4=C`0mevs2V zw!LHMN`q>t*>RD&C%b9`LQ!p7)1;eu%Y8rs`r1>~dK{3s_gwXAWZP66t;wH=EgBSq{hr|9! zFwr;4?A}=8$oig>$)GY%xd#HMk!Sn19^#sMZ?=rH?P@@l2bZ$!2U{9VM&6yamj1~@M!uDq5 zUD=gT$hC*;K<=V};4YMW?^6gmJED`=&ixBGx4NC%LvH87TyK-xxqHd%!)Olh(W;5u zDJ&yer*G$C%SWzpLehZCgM@nl3~vK}>5KzBXeH706{7k_?EFVY)Tc>DsIwUDTbRym z^7@tioSxK@@yY12F}}X{HgP;vt~;*fxs3_zrCB!SuU8L7H<>>y_9h>HtcKX{UBZGd~Q5u?W znv9G(=zqR9{SPSgd+ggj`>&uU#GiO4sK)g^7qw5w6H-@5Zbjfx`*kTh~lHgu@BDuqm&5 zj>8Gl$o|11k;_qxp^ZcP3)jM8aGP@)98RzklOC7g!wK4?kLXqC?-~M5$4l}&MmYC{e`Q+bk)8FBiU}rOB1yG|0r#b7rUKGTeq2CQGBuZ#e zS)%ORXE#5yOpZ!ueSm`V_rbgXkd2YHN>gT91s*lL)r{k+Q-z%6qCxgWTE&|hwB)$v zMbeH7Or%4$MVgVt${!RI)DZo=cMs8#Q(*R=P|iHgqHmr&`*>MS{vzGqP1V1anSqV^ zw=z9?QcYCy@tEa5_iwk>)iO%&R%E}zI|5m{Vu&}r!Hq$5N4FgJhIH1(IW(ZlK91a8S5s4TUQorE&`lHwYBsNU%Z_O&tb;Z`p-df)KHnXIaYJ z;MKf=LbYmp^;BCqziI9je8TISWHK8!kf`BSC0_v(9b~R*pAAKxvtvRN!e#)Gj2xYX`L8uD2O6Z^~EwODa z#QVQrE&TFIp*-bnGO>a@W#-G0)jwjfa)s4`4;x^x-Ck@HaMcbtB62h)XjxRQR!j$Gwt_$mUKv ziAnwvBz==@H;j+v;U@ih(%O|gO3Bk5b^YJY(8=Xup`IILhFU%0yFCnMJW%--8T<8T zT8)0*^emq?MPnM0GYfJ=-j_<=Z2)mapLv|{Ww|D1%J1=f(_Ohw0^<@dFq$B_eZ)vhR8;#OE;MIEFmn=$qY_2YfpgB>IGJwdcJY;lO2YSL){OrHFk65aP zxpQQ>?($B~Mq!Ogf%{jHm_t#8a4a#&^8#6LziiD&mW9j4n5Csd_GKk+ zuY4R!-PDHZk^S2{a)0YkG}0JlTOKm%Nd9deFy3m(m)f;=pB!3%wa-+ZNd!GnF10UL zF0~Ixlq&lY^;aDAsGstsN28)RfXlH9c-y5N=aoFIVmOKSTja45dFVvuNa+~4Ta+v3 z`gLI z$>ixlu3Pks#qU|Ej($QD#ijNT@nj(al)Db)A4BeUrr33~BCS7esJ>wAS^EzTI;j0$b@9%BdVzyTWvy9xRPcP_*qTT&k1xXtk?4-Gc`CC)%caQJR$~oe?zYW3(%uTYmKXAVFzIt~HRXde)&6By*?%G-2tjK|@ zg6GNU)_Rw)6IC*X|+lTJC|#B&&CYwYe6HIn_^UZkPX;OQY>Q$2A| zZnxmq&+g0gQV!gLk}!XZg3Coo8%mnLhAIv|!7|m9=Z#$3!}+--3Zq^Gas@C8>83dR zbWFk2TFxBxADVpJfm!?-pY#dSfHl|ao8)PW`_RppU1@`uIDB|zYH~CYH(m>o^^#a; zXGk6KNL)PE8XlSmCE?Zy0qh@_)8pFN)LaO8i&BMdIlEYub);?2wzq!uUGvwq>4up{ z;K3tn>oc*rKq>$Fw8OO6T>M5MCtmiXj@Pb*VImG_kkUrt;9qbdv9raFZFD^Rz}+(e zWs?fyxauU|v$**zYAs%mp9gwl(&&z9(b#^ZJ@EMBd=BNLH+Dsa@|camKYq1Bn7 z``--ho@yQ{%Go)eW*=gw+@EmCER}Wq{ZllU2i!xRpNH4X>$q$xe*&kxC`(h~+O-8l z&&6+BD)2(@lefIJn7d24djnrH)N#>kGu{zU%nzk zIc5MV^O7XP!3hes{a~vQT@Nal0tevoG&*_94|%D7d?>~)=b+gfZwt}dAKA+$AJcbe zB8X>)=MBc>6uo?^BUDaQk#`tyDQ!fxZ!c2i{+7|`7|@}3>0MwDf}=f*!$rpUWBxWoLsjh982k! zZcE`pHA7b6N^E1v&+nkjVavezN=5V-H?}BFQ#A$f3(-%udJ4?3*9nw6)zUnlyrqD*Tu8>$Qq!K?`uRs zmaSL6O+Hwz^v?K{Kl@>D;eC8y9?Ac^Kz7!PgdLThV0dD^6zW7Bm;pfjmEuUJejde+)>i~ByA6YB*bZ972|MIWPRD_%#cZ0^jtR{< zhYO0X`u#PS9iaaZ#cZJEC=Pgc;w}JCKaSlj`}{C${>YO2TMP_Qwt4Xj%zzR2nLi2j z@zXI97LZl6Lbaf=l6YxJmVZPB2EDAs@{0CrQl=|my-*9bo