xxxxxxxxxx
900
let game;
let titleFont;
let gameEffectController;
let ghosts;
let coverImage;
let pacmanImage;
let blinkyImage;
let clydeImage;
let inkyImage;
let pinkyImage;
let eyesImage;
let vulImage;
let fireImageSet;
let woodImageSet;
let islandImageSet;
let endImage;
let allScenes;
let woodTheme;
let fireTheme;
let waterTheme;
let coverTheme;
let defeatTheme;
let defeatGhostSound;
let bPelletSound;
let getCoinSound;
let deathSound;
let player;
let tempMap;
let mapData=[];
let pelletArr;
let leveltable; // Variable to store the table data
let scatterTime = []; // Array to store the first column
let chaseTime = []; // Array to store the second column
let mapNumber = []; // Array to store the first column
let ghostSpeed = []; // Array to store the second column
function preload() {
soundFormats('mp3', 'wav');
woodTheme=loadSound("Sounds/WoodTheme.mp3");
fireTheme=loadSound("Sounds/FireTheme.mp3");
waterTheme=loadSound("Sounds/WaterTheme.mp3");
coverTheme=loadSound("Sounds/CoverTheme.mp3");
defeatTheme=loadSound("Sounds/DefeatTheme.mp3");
defeatGhostSound=loadSound("Sounds/DefeatGhostSound.wav");
bPelletSound=loadSound("Sounds/BPelletSound.wav");
getCoinSound=loadSound("Sounds/GetCoinSound.wav");
deathSound=loadSound("Sounds/death.mp3");
// Load the text file
tempMap = loadStrings('map1.txt', parseData);
tempMap = loadStrings('map2.txt', parseData);
tempMap = loadStrings('map3.txt', parseData);
tempMap = loadStrings('map4.txt', parseData);
leveltable = loadTable('levelSpecs.csv', 'csv');
titleFont=loadFont("BungeeSpice-Regular.ttf");
}
function processCSVData(data) {
// Loop through the table and populate the arrays
for (let i = 0; i < data.getRowCount(); i++) {
let row = data.getRow(i);
// Make sure there are at least 2 columns in each row
scatterTime.push(row.get(0));
chaseTime.push(row.get(1));
mapNumber.push(row.get(2));
ghostSpeed.push(row.get(3));
}
}
function parseData(data) {
// Split the data into rows
tempMap = data.map(row => row.split(',').map(Number));
mapData.push(tempMap);
}
function setup() {
createCanvas(1120, 1240);
if (!coverTheme.isPlaying()) {
coverTheme.play();
}
game = new Game();
allScenes=[];
fireImageSet = new ImageSet(["FireTextures/1.png","FireTextures/2.png","FireTextures/3.png","FireTextures/4.png","FireTextures/5.png","FireTextures/6.png"]);
woodImageSet = new ImageSet(["WoodTextures/1.png","WoodTextures/2.png"]);
islandImageSet = new ImageSet(["IslandTextures/1.png","IslandTextures/2.png"]);
allScenes.push(fireImageSet);
allScenes.push(woodImageSet);
allScenes.push(islandImageSet);
coverImage=loadImage("cover.png");
pacmanImage=loadImage("pacman.png");
blinkyImage=loadImage("Ghosts/blinky.png");
clydeImage=loadImage("Ghosts/clyde.png");
inkyImage=loadImage("Ghosts/inky.png");
pinkyImage=loadImage("Ghosts/pinky.png");
eyesImage=loadImage("Ghosts/eyes.png");
vulImage=loadImage("Ghosts/vulnerable.png");
endImage=loadImage("endScreen.png");
processCSVData(leveltable);
pelletArr=[];
let numRows = mapData[0].length;
let numCols = mapData[0][0].length;
// Create a new grid with expanded rows and columns
let newGrid = Array(numRows + 2).fill(1).map(() => Array(numCols + 2).fill(1));
// Copy the original grid into the center of the new grid
for (let i = 1; i <= numRows; i++) {
for (let j = 1; j <= numCols; j++) {
newGrid[i][j] = mapData[0][i - 1][j - 1];
}
}
mapData[0]=newGrid;
numRows = mapData[1].length;
numCols = mapData[1][0].length;
// Create a new grid with expanded rows and columns
newGrid = Array(numRows + 2).fill(1).map(() => Array(numCols + 2).fill(1));
// Copy the original grid into the center of the new grid
for (let i = 1; i <= numRows; i++) {
for (let j = 1; j <= numCols; j++) {
newGrid[i][j] = mapData[1][i - 1][j - 1];
}
}
mapData[1]=newGrid;
numRows = mapData[2].length;
numCols = mapData[2][0].length;
// Create a new grid with expanded rows and columns
newGrid = Array(numRows + 2).fill(1).map(() => Array(numCols + 2).fill(1));
// Copy the original grid into the center of the new grid
for (let i = 1; i <= numRows; i++) {
for (let j = 1; j <= numCols; j++) {
newGrid[i][j] = mapData[2][i - 1][j - 1];
}
}
mapData[2]=newGrid;
numRows = mapData[3].length;
numCols = mapData[3][0].length;
// Create a new grid with expanded rows and columns
newGrid = Array(numRows + 2).fill(1).map(() => Array(numCols + 2).fill(1));
// Copy the original grid into the center of the new grid
for (let i = 1; i <= numRows; i++) {
for (let j = 1; j <= numCols; j++) {
newGrid[i][j] = mapData[3][i - 1][j - 1];
}
}
mapData[3]=newGrid;
game.initGame();
}
function draw() {
// frameRate(5);
stroke(255);
for (let x = 0; x < 31; x++) {
for (let y = 0; y < 28; y++) {
if(mapData[game.mapNum][x][y]==0){
image(allScenes[game.gameLevel%3].images[0],y*40,x*40,40,40);
}
else if(mapData[game.mapNum][x][y]==1){
image(allScenes[game.gameLevel%3].images[1],y*40,x*40,40,40);
}
else if(mapData[game.mapNum][x][y]==2){
image(fireImageSet.images[4],y*40,x*40,40,40);
fill(0);
}
else if(mapData[game.mapNum][x][y]==4){
if(game.stageFinished){
image(fireImageSet.images[2],y*40,x*40,40,40);
}
else{
image(fireImageSet.images[3],y*40,x*40,40,40);
}
}
else{
image(fireImageSet.images[5],y*40,x*40,40,40);
}
}
}
if (game.gameStage==0) {
// Display "Press Anywhere to Start" message
fill(0);
background(coverImage);
if (!coverTheme.isPlaying()) {
coverTheme.play();
}
textAlign(CENTER, CENTER);
textFont(titleFont);
textSize(220);
text("Pac-Man",width / 2,height/4);
textSize(44);
text("Remake",3 * width / 4,height/2.8);
text("Press Anywhere to Start", width / 2, 2 * height / 3);
} else if(game.gameStage==1) {
// Game logic goes here
coverTheme.stop();
game.inGame();
game.levelTimer+=deltaTime/1000;
if(game.frightened){
game.frightenedTimer+=deltaTime/1000;
}
else{
game.cycleTimer+=deltaTime/1000;
}
textAlign(CENTER, CENTER);
text("Time of Run: "+ nfc(game.levelTimer,2)+" seconds", width / 2, height / 2-80);
text("Current Points: "+ game.gameScore +" pts", width / 2, height / 2-60);
text("Current Stage: "+ game.gameLevel, width / 2, height / 2-40);
text("Health Left: "+ player.health, width / 2, height / 2-20);
// ghost.display();
}
else if(game.gameStage==2){
woodTheme.stop();
fireTheme.stop();
waterTheme.stop();
if (!defeatTheme.isPlaying()) {
defeatTheme.play();
}
background(endImage);
fill(255,0,0,70);
rect(0,0,width,height);
fill(0);
textSize(170);
text("Game Over!",width / 2,height/4);
textSize(24);
textAlign(CENTER, CENTER);
text("Time of Run: "+ nfc(game.levelTimer,2)+" seconds", width / 2, height / 2-90);
text("Current Points: "+ game.gameScore +" pts", width / 2, height / 2-60);
text("Current Stage: "+ game.gameLevel, width / 2, height / 2-30);
textSize(44);
text("Press V to restart", width / 2, height / 2+200);
}
}
function keyPressed(){
if(keyCode==32){
if (game.gameStage==1) {
game.gameStage = 2;
}
}
else if(keyCode==86){
if (game.gameStage==2) {
game.initGame();
game.gameStage = 1;
}
}
}
function mousePressed() {
if (game.gameStage==0) {
game.gameStage = 1;
}
}
class ImageSet {
constructor(imagePaths) {
this.images = [];
this.style=0;
for (let path of imagePaths) {
let img = loadImage(path);
this.images.push(img);
}
}
}
class Game{
constructor(){
this.gameStage=0;
this.gameLevel=0;
this.levelTimer=0;
this.cycleTimer=0;
this.frightenedTimer=0;
this.frightened=false;
this.ghostState=0;
this.gameScore=0;
this.mapNum=0
this.stageFinished=false;
}
generatePellets(){
this.cycleTimer=0;
this.frightenedTimer=0;
let numRows = mapData[game.mapNum].length;
let numCols = mapData[game.mapNum][0].length;
for (let i = 0; i < numRows; i++) {
for (let j = 0; j < numCols; j++) {
if(mapData[game.mapNum][i][j]==0){
pelletArr.push(new pellet(j,i,random(2)<0.03));
}
}
}
}
initGame(){
if(mapNumber[this.gameLevel]==6){
this.mapNum=int(random(3))+1;
}
else{
this.mapNum=0;
}
this.generatePellets();
player=new Player();
ghosts=[];
ghosts.push(new Blinky());
ghosts.push(new Pinky());
ghosts.push(new Inky());
ghosts.push(new Clyde());
this.gameLevel=0;
this.levelTimer=0;
this.gameScore=0;
gameEffectController=new GameEffectController();
}
inGame(){
if(this.gameLevel%3==0){
if (!fireTheme.isPlaying()) {
woodTheme.stop();
waterTheme.stop();
fireTheme.play();
}
}
else if(this.gameLevel%3==1){
if (!woodTheme.isPlaying()) {
woodTheme.play();
waterTheme.stop();
fireTheme.stop();
}
}
else if(this.gameLevel%3==2){
if (!waterTheme.isPlaying()) {
woodTheme.stop();
waterTheme.play();
fireTheme.stop();
}
}
if(pelletArr.length==0){
this.stageFinished=true;
}
else{
this.stageFinished=false;
}
player.display();
for (let i = pelletArr.length - 1; i >= 0; i--) {
pelletArr[i].display();
if (gameEffectController.checkCollision(pelletArr[i],player)) {
if(pelletArr[i].power){
this.gameScore+=10;
bPelletSound.play();
game.frightened=true;
}
else{
this.gameScore++;
getCoinSound.play();
}
pelletArr.splice(i, 1); // Remove the element at index i
}
}
checkKeyDown();
player.updateColl();
ghosts.forEach(element => {
element.display();
element.checkMoved();
element.moveToNext();
element.checkState();
if(gameEffectController.checkCollision(element,player)){
if(element.vulnerable==false){
deathSound.play();
player.health-=1;
if(player.health>0){
game.halfReset()
}
else{
this.gameStage=2;
}
}
else{
element.defeatedMode();
if(!element.addPointFlag){
defeatGhostSound.play();
game.gameScore+=100;
element.addPointFlag=true;
}
}
}
});
}
halfReset(){
if(mapNumber[this.gameLevel]==6){
this.mapNum=int(random(3))+1;
}
else{
this.mapNum=0;
}
player.position=[13.5,23];
ghosts=[];
ghosts.push(new Blinky());
ghosts.push(new Pinky());
ghosts.push(new Inky());
ghosts.push(new Clyde());
this.cycleTimer=0;
this.frightenedTimer=0;
this.stagedFinished=false;
}
}
class GameEffectController{
constructor(){
}
checkCollision(elem1,elem2){
return (elem1.absPos[0]==elem2.absPos[0]) && (elem1.absPos[1]==elem2.absPos[1]);
}
}
class Ghosts{
constructor(){
this.position=[1,1];
this.img=blinkyImage;
this.vulnerable=false;
this.addPointFlag=false;
this.absPos=[round(this.position[0]),round(this.position[1])];
this.prevAbsPos=this.absPos;
this.state=0;//prepare state
this.dir=[1,0];//no direction
this.speed=ghostSpeed[game.gameLevel];
this.dest=[0,0];
this.next=[0,0];
this.sur=[[this.absPos[0],this.absPos[1]-1],
[this.absPos[0]+1,this.absPos[1]],
[this.absPos[0],this.absPos[1]+1],
[this.absPos[0]-1,this.absPos[1]]];
this.choices=[];
}
checkState(){ // Check ghost's state to determin action
if(this.state==0&&this.absPos[1]>11){
this.resetMode();
}
else if(this.state==9){
if(this.absPos[0]==13&&this.absPos[1]==15){
this.state=0;
this.addPointFlag=false;
}
}
else{
if(game.frightened && game.frightenedTimer<=7){
this.frightenedMode();
}
else{
this.vulnerable=false;
game.frightened=false;
game.frightenedTimer=0;
if(game.cycleTimer/(int(chaseTime[game.gameLevel])+int(scatterTime[game.gameLevel]))<=5 && game.cycleTimer%(int(chaseTime[game.gameLevel])+int(scatterTime[game.gameLevel]))<scatterTime[game.gameLevel]){
this.scatterMode();
}
else{
this.chaseMode();
}
}
}
}
defeatedMode(){
this.state=9;
this.img=eyesImage;
this.dest=[13,15];
}
resetMode(){
this.dest=[13,11];
}
checkMoved(){ // check if the ghost moved a whole block, if moved calculate new destination
this.absPos=[round(this.position[0]),round(this.position[1])];
this.sur=[[this.absPos[0],this.absPos[1]-1],
[this.absPos[0]+1,this.absPos[1]],
[this.absPos[0],this.absPos[1]+1],
[this.absPos[0]-1,this.absPos[1]]];
if(this.absPos[0]!=this.prevAbsPos[0] || this.absPos[1]!=this.prevAbsPos[1]){
this.calcMovement();
this.prevAbsPos=this.absPos;
}
}
calcMovement(){ // calculate new destination and how to get there
this.choices=[];
this.sur.forEach(element => {
if((element[0]!=this.prevAbsPos[0] || element[1]!=this.prevAbsPos[1]) && mapData[game.mapNum][element[1]][element[0]]!=1){
if((this.state != 0 && this.state != 9)&& mapData[game.mapNum][element[1]][element[0]]==3){
}
else{
this.choices.push(element);
}
}
});
if(this.choices.length==0){
if(this.absPos[0]==1 && this.absPos[1]==14){
this.position=[26,14];
this.absPos=[round(this.position[0]),round(this.position[1])];
this.choices.push([25,14]);
}
else if(this.absPos[0]==26 && this.absPos[1]==14){
this.position=[1,14];
this.absPos=[round(this.position[0]),round(this.position[1])];
this.choices.push([2,14]);
}
}
let closest = Infinity;
this.choices.forEach(element => {
let difference = sq(element[0]-this.dest[0])+sq(element[1]-this.dest[1]);
// Check if the current element is closer than the previous closest element
if (difference < closest) {
closest = difference;
this.next = element;
}
});
this.dir=[this.next[0]-this.absPos[0],this.next[1]-this.absPos[1]];
}
moveToNext(){ // move
if(this.dir[0]!=0){
this.position[0]+=this.dir[0]*this.speed*deltaTime/1000;
this.position[1]=this.absPos[1];
}
else{
this.position[1]+=this.dir[1]*this.speed*deltaTime/1000;
this.position[0]=this.absPos[0];
}
}
frightenedMode(){
this.vulnerable=true;
this.img=vulImage;
if(this.choices.length>1){
this.dest=this.choices[floor(random(this.choices.length))];
}
}
}
class Blinky extends Ghosts{
constructor(state,dest,position,img){
super(state,dest,position,img);
this.position=[13.5,11]
this.img=blinkyImage;
}
scatterMode(){ // Scatter mode determin destination
if(this.state!=2){
this.dir[0]=-this.dir[0];
this.dir[1]=-this.dir[1];
this.state=2;
}
this.dest=[27,0];
}
chaseMode(){ // Chase mode determine destination
if(this.state!=1){
this.dir[0]=-this.dir[0];
this.dir[1]=-this.dir[1];
this.state=1;
}
this.dest=player.position;
}
display(){
if(this.state!=9 && this.vulnerable==false){
this.img=blinkyImage;
}
fill("red");
// rect(this.dest[0]*40,this.dest[1]*40,40,40);
image(this.img,this.position[0]*40,this.position[1]*40,40,40);
fill(0); // Set the text fill color to black
}
}
class Pinky extends Ghosts{
constructor(state,dest,position,img){
super(state,dest,position,img);
this.position=[13.5,14];
this.img=pinkyImage;
}
scatterMode(){
if(this.state!=2){
this.dir[0]=-this.dir[0];
this.dir[1]=-this.dir[1];
this.state=2;
}
this.dest=[0,0];
}
chaseMode(){
if(this.state!=1){
this.dir[0]=-this.dir[0];
this.dir[1]=-this.dir[1];
this.state=1;
}
this.dest[0]=player.position[0]+4*player.dir[0];
this.dest[1]=player.position[1]+4*player.dir[1];
}
display(){
if(this.state!=9&&this.vulnerable==false){
this.img=pinkyImage;
}
// fill("pink");
// rect(this.dest[0]*40,this.dest[1]*40,40,40);
image(this.img,this.position[0]*40,this.position[1]*40,40,40);
fill(0); // Set the text fill color to black
}
}
class Clyde extends Ghosts{
constructor(state,dest,position,img){
super(state,dest,position,img);
this.position=[15.5,14];
this.img=clydeImage;
}
scatterMode(){
if(this.state!=2){
this.dir[0]=-this.dir[0];
this.dir[1]=-this.dir[1];
this.state=2;
}
this.dest=[0,30];
}
chaseMode(){
if(this.state!=1){
this.dir[0]=-this.dir[0];
this.dir[1]=-this.dir[1];
this.state=1;
}
let distance = sq(this.position[0]-player.position[0])+sq(this.position[1]-player.position[1]);
if(distance>=64){
this.dest=player.position;
}
else{
this.dest=[0,30];
}
}
display(){
if(this.state!=9&&this.vulnerable==false){
this.img=clydeImage;
}
// fill("yellow");
// rect(this.dest[0]*40,this.dest[1]*40,40,40);
image(this.img,this.position[0]*40,this.position[1]*40,40,40);
fill(0); // Set the text fill color to black
}
}
class Inky extends Ghosts{
constructor(state,dest,position,img){
super(state,dest,position,img);
this.position=[11.5,14];
this.intermediate=[0,0];
this.img=inkyImage;
}
scatterMode(){
if(this.state!=2){
this.dir[0]=-this.dir[0];
this.dir[1]=-this.dir[1];
this.state=2;
}
this.dest=[27,30];
}
chaseMode(){
if(this.state!=1){
this.dir[0]=-this.dir[0];
this.dir[1]=-this.dir[1];
this.state=1;
}
this.intermediate[0]=player.position[0]+2*player.dir[0];
this.intermediate[1]=player.position[1]+2*player.dir[1];
this.dest[0]=2*this.intermediate[0]-ghosts[0].position[0];
this.dest[1]=2*this.intermediate[1]-ghosts[0].position[1];
}
display(){
if(this.state!=9&&this.vulnerable==false){
this.img=inkyImage;
}
// fill("blue");
// rect(this.intermediate[0]*40,this.intermediate[1]*40,40,40);
// rect(this.dest[0]*40,this.dest[1]*40,40,40);
image(this.img,this.position[0]*40,this.position[1]*40,40,40);
fill(0); // Set the text fill color to black
}
}
class Player{
constructor(){
this.health=3;
this.position=[13.5,23];
this.absPos=[round(this.position[0]),round(this.position[1])];
this.state=0;//prepare state
this.dir=[0,0];//no direction
this.rotation=0;
this.speed=10;
}
display(){
push();
imageMode(CENTER);
translate(this.position[0]*40+20,this.position[1]*40+20);
rotate(radians(this.rotation));
image(pacmanImage,0,0,40,40);
pop();
textSize(16); // Set the text size
text("X COORDS: "+this.absPos[0], 10, 400);
text("Y COORDS: "+this.absPos[1], 10, 420);
}
updateColl(){
this.absPos=[round(this.position[0]),round(this.position[1])];
}
}
class pellet{
constructor(x,y,power){
this.absPos=[x,y];
this.power=power;
}
display(){
fill("yellow");
stroke("yellow");
if(this.power){
circle(this.absPos[0]*40+20,this.absPos[1]*40+20,15);
}
else{
circle(this.absPos[0]*40+20,this.absPos[1]*40+20,5);
}
}
}
function checkKeyDown(){
if(keyIsDown(87)){
player.dir=[0,-1];
if(checkLegal(round(player.position[0]),floor(player.position[1]))){
player.position[1]+=player.dir[1]*player.speed*deltaTime/1000;
player.rotation=270;
}
}
if(keyIsDown(83)){
player.dir=[0,1];
if(checkLegal(round(player.position[0]),ceil(player.position[1]))){
player.position[1]+=player.dir[1]*player.speed*deltaTime/1000;
player.rotation=90;
}
}
if(keyIsDown(65)){
player.dir=[-1,0];
if(checkLegal(floor(player.position[0]),round(player.position[1]))){
player.position[0]+=player.dir[0]*player.speed*deltaTime/1000;
player.rotation=180;
}
}
if(keyIsDown(68)){
player.dir=[1,0];
if(checkLegal(ceil(player.position[0]),round(player.position[1]))){
player.position[0]+=player.dir[0]*player.speed*deltaTime/1000;
player.rotation=0;
}
}
if(player.absPos[0]==1 && player.absPos[1]==14 && player.dir[0]==-1){
if(game.stageFinished){
game.gameLevel++;
game.generatePellets();
game.halfReset();
}
else{
player.position=[26,14];
player.absPos=[round(player.position[0]),round(player.position[1])];
}
}
else if(player.absPos[0]==26 && player.absPos[1]==14 && player.dir[0]==1){
if(game.stageFinished){
game.gameLevel++;
game.generatePellets();
game.halfReset();
}
else{
player.position=[1,14];
player.absPos=[round(player.position[0]),round(player.position[1])];
}
}
}
function checkLegal(x,y){
if(mapData[game.mapNum][y][x]==0 || mapData[game.mapNum][y][x]==4){
return true;
}
else{
return false;
}
}