website/EasterEggs/pacman.js
2025-10-07 21:27:09 +02:00

859 lines
19 KiB
JavaScript

let width = 800;
let height = 625;
let gridSize = 32;
let offset = parseInt(gridSize / 2);
let config = {
type: Phaser.CANVAS,
width: width,
height: height,
canvas: document.getElementById("mycanvas"),
physics: {
default: "arcade",
arcade: {
debug: false,
gravity: {
x: 0,
y: 0
}
}
},
scene: {
preload: preload,
create: create,
update: update
}
};
let game = new Phaser.Game(config);
let cursors;
let player;
let ghosts = [];
let pills;
let pillsCount = 0;
let pillsAte = 0;
let map;
let layer1;
let layer2;
let graphics;
let scoreText;
let livesImage = [];
let tiles = "pacman-tiles";
let spritesheet = "pacman-spritesheet";
let customPacman = "customPacman";
let ensr = "ENSR";
let ensl = "ENSL";
let ensu = "ENSU";
let enss = "ENSS";
let spritesheetPath = "https://raw.githubusercontent.com/kudchikarsk/phaser-pacman/master/assets/images/pacmansprites.png";
let tilesPath = "https://raw.githubusercontent.com/kudchikarsk/phaser-pacman/master/assets/images/background.png";
let mapPath =
"https://raw.githubusercontent.com/kudchikarsk/phaser-pacman/master/assets/levels/codepen-level.json";
let Animation = {
Player: {
Eat: "player-eat",
Stay: "player-stay",
Die: "player-die"
},
Ghost: {
Blue: {
Move: "ghost-blue-move"
},
Orange: {
Move: "ghost-orange-move"
},
White: {
Move: "ghost-white-move"
},
Pink: {
Move: "ghost-pink-move"
},
Red: {
Move: "ghost-red-move"
}
}
};
function preload() {
this.load.spritesheet(spritesheet, spritesheetPath, {
frameWidth: gridSize,
frameHeight: gridSize
});
this.load.tilemapTiledJSON("map", mapPath);
this.load.image(tiles, tilesPath);
this.load.image("pill", "https://raw.githubusercontent.com/kudchikarsk/phaser-pacman/master/assets/images/pac%20man%20pill/spr_pill_0.png");
this.load.image("lifecounter", "https://raw.githubusercontent.com/kudchikarsk/phaser-pacman/master/assets/images/pac%20man%20life%20counter/spr_lifecounter_0.png");
this.load.spritesheet(customPacman, "images/pp.jpg", {
frameWidth: gridSize,
frameHeight: gridSize
});
this.load.image("0", "./images/0.png");
this.load.image("1", "./images/1.png");
this.load.spritesheet(ensr, "images/ENSR.png", {
frameWidth: gridSize,
frameHeight: gridSize
});
this.load.spritesheet(ensl, "images/ENSL.png", {
frameWidth: gridSize,
frameHeight: gridSize
});
this.load.spritesheet(ensu, "images/ENSU.png", {
frameWidth: gridSize,
frameHeight: gridSize
});
this.load.spritesheet(enss, "images/ENSS.jpeg", {
frameWidth: gridSize,
frameHeight: gridSize
});
}
function create() {
this.anims.create({
key: Animation.Player.Stay,
frames: this.anims.generateFrameNumbers(customPacman, {frame: 0}),
frameRate: 20
});
this.anims.create({
key: Animation.Ghost.Blue.Move,
frames: this.anims.generateFrameNumbers(ensl, { start: 0, end: 0 }),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: Animation.Ghost.Orange.Move,
frames: this.anims.generateFrameNumbers(enss, { start: 0, end: 0 }),
frameRate: 10,
repeat: -1
});
//this.anims.create({
// key: Animation.Ghost.White.Move,
// frames: this.anims.generateFrameNumbers(ensl, { start: 0, end: 0 }),
// frameRate: 10,
// repeat: -1
//});
this.anims.create({
key: Animation.Ghost.Pink.Move,
frames: this.anims.generateFrameNumbers(ensu, {
start: 0,
end: 0
}),
frameRate: 10,
repeat: -1
});
this.anims.create({
key: Animation.Ghost.Red.Move,
frames: this.anims.generateFrameNumbers(ensr, {
start: 0,
end: 0
}),
frameRate: 10,
repeat: -1
});
map = this.make.tilemap({
key: "map",
tileWidth: gridSize,
tileHeight: gridSize
});
const tileset = map.addTilesetImage(tiles);
layer1 = map.createStaticLayer("Layer 1", tileset, 0, 0);
layer1.setCollisionByProperty({ collides: true });
layer2 = map.createStaticLayer("Layer 2", tileset, 0, 0);
layer2.setCollisionByProperty({ collides: true });
let spawnPoint = map.findObject("Objects", obj => obj.name === "Player");
let position = new Phaser.Geom.Point(
spawnPoint.x + offset,
spawnPoint.y - offset
);
player = new Player(this, position, Animation.Player, function() {
if (player.life <= 0) {
newGame();
} else {
respawn();
}
});
let scene = this;
pills = this.physics.add.group();
map.filterObjects("Objects", function(value, index, array) {
if (value.name == "Pill") {
let pill;
let randomValue = Phaser.Math.Between(0,1);
if (randomValue > 0.5) {
pill = scene.physics.add.sprite(
value.x + offset,
value.y - offset,
"0"
);
pill.setScale(0.07);
} else {
pill = scene.physics.add.sprite(
value.x + offset,
value.y - offset,
"1"
);
pill.setScale(0.1);
}
pills.add(pill);
pillsCount++;
}
});
let ghostsGroup = this.physics.add.group();
let i = 0;
let skins = [
Animation.Ghost.Blue,
Animation.Ghost.Red,
Animation.Ghost.Orange,
Animation.Ghost.Pink
];
map.filterObjects("Objects", function(value, index, array) {
if (value.name == "Ghost") {
let position = new Phaser.Geom.Point(value.x + offset, value.y - offset);
let ghost = new Ghost(scene, position, skins[i]);
ghosts.push(ghost);
ghostsGroup.add(ghost.sprite);
i++;
}
});
this.physics.add.collider(player.sprite, layer1);
this.physics.add.collider(player.sprite, layer2);
this.physics.add.collider(ghostsGroup, layer1);
this.physics.add.overlap(
player.sprite,
pills,
function(sprite, pill) {
pill.disableBody(true, true);
pillsAte++;
player.score += 10;
if (pillsCount == pillsAte) {
reset();
}
},
null,
this
);
this.physics.add.overlap(
player.sprite,
ghostsGroup,
function(sprite, ghostSprite) {
if (player.active) {
player.die();
for (let ghost of ghosts) {
ghost.freeze();
}
}
},
null,
this
);
cursors = this.input.keyboard.createCursorKeys();
graphics = this.add.graphics();
scoreText = this.add
.text(25, 595, "Score: " + player.score)
.setFontFamily("Arial")
.setFontSize(18)
.setColor("#ffffff");
this.add
.text(630, 595, "Lives:")
.setFontFamily("Arial")
.setFontSize(18)
.setColor("#ffffff");
for (let i = 0; i < player.life; i++) {
livesImage.push(this.add.image(700 + i * 25, 605, "lifecounter"));
}
}
function respawn() {
player.respawn();
for (let ghost of ghosts) {
ghost.respawn();
}
}
function reset() {
respawn();
for (let child of pills.getChildren()) {
child.enableBody(false, child.x, child.y, true, true);
}
pillsAte = 0;
}
function newGame() {
reset();
player.life = 3;
player.score = 0;
for (let i = 0; i < player.life; i++) {
let image = livesImage[i];
if (image) {
image.alpha = 1;
}
}
}
function update() {
player.setDirections(getDirection(map, layer1, player.sprite));
if (!player.playing) {
for (let ghost of ghosts) {
ghost.freeze();
}
}
for (let ghost of ghosts) {
ghost.setDirections(getDirection(map, layer1, ghost.sprite));
}
player.setTurningPoint(getTurningPoint(map, player.sprite));
for (let ghost of ghosts) {
ghost.setTurningPoint(getTurningPoint(map, ghost.sprite));
}
if (cursors.left.isDown) {
player.setTurn(Phaser.LEFT);
} else if (cursors.right.isDown) {
player.setTurn(Phaser.RIGHT);
} else if (cursors.up.isDown) {
player.setTurn(Phaser.UP);
} else if (cursors.down.isDown) {
player.setTurn(Phaser.DOWN);
} else {
player.setTurn(Phaser.NONE);
}
player.update();
for (let ghost of ghosts) {
ghost.update();
}
scoreText.setText("Score: " + player.score);
for (let i = player.life; i < 3; i++) {
let image = livesImage[i];
if (image) {
image.alpha = 0;
}
}
if (player.active) {
if (player.sprite.x < 0 - offset) {
player.sprite.setPosition(width + offset, player.sprite.y);
} else if (player.sprite.x > width + offset) {
player.sprite.setPosition(0 - offset, player.sprite.y);
}
}
//drawDebug();
}
function drawDebug() {
graphics.clear();
player.drawDebug(graphics);
for (let ghost of ghosts) {
ghost.drawDebug(graphics);
}
}
function getDirection(map, layer, sprite) {
let directions = [];
let sx = Phaser.Math.FloorTo(sprite.x);
let sy = Phaser.Math.FloorTo(sprite.y);
let currentTile = map.getTileAtWorldXY(sx, sy, true);
if (currentTile) {
var x = currentTile.x;
var y = currentTile.y;
directions[Phaser.LEFT] = map.getTileAt(x - 1, y, true, layer);
directions[Phaser.RIGHT] = map.getTileAt(x + 1, y, true, layer);
directions[Phaser.UP] = map.getTileAt(x, y - 1, true, layer);
directions[Phaser.DOWN] = map.getTileAt(x, y + 1, true, layer);
}
return directions;
}
function getTurningPoint(map, sprite) {
let turningPoint = new Phaser.Geom.Point();
let sx = Phaser.Math.FloorTo(sprite.x);
let sy = Phaser.Math.FloorTo(sprite.y);
let currentTile = map.getTileAtWorldXY(sx, sy, true);
if (currentTile) {
turningPoint.x = currentTile.pixelX + offset;
turningPoint.y = currentTile.pixelY + offset;
}
return turningPoint;
}
class Ghost {
constructor(scene, position, anim) {
this.sprite = scene.physics.add
.sprite(position.x, position.y, "ghost")
.setScale(0.85)
.setOrigin(0.5);
this.spawnPoint = position;
this.anim = anim;
this.speed = 100;
this.moveTo = new Phaser.Geom.Point();
this.safetile = [-1, 19];
this.directions = [];
this.opposites = [
null,
null,
null,
null,
null,
Phaser.DOWN,
Phaser.UP,
Phaser.RIGHT,
Phaser.LEFT
];
this.turning = Phaser.NONE;
this.current = Phaser.NONE;
this.turningPoint = new Phaser.Geom.Point();
this.threshold = 5;
this.rnd = new Phaser.Math.RandomDataGenerator();
this.sprite.anims.play(anim.Move, true);
this.turnCount = 0;
this.turnAtTime = [4, 8, 16, 32, 64];
this.turnAt = this.rnd.pick(this.turnAtTime);
}
freeze() {
this.moveTo = new Phaser.Geom.Point();
this.current = Phaser.NONE;
}
move() {
this.move(this.rnd.pick([Phaser.UP, Phaser.DOWN]));
}
respawn() {
this.sprite.setPosition(this.spawnPoint.x, this.spawnPoint.y);
this.move(this.rnd.pick([Phaser.UP, Phaser.DOWN]));
this.sprite.flipX = false;
}
moveLeft() {
this.moveTo.x = -1;
this.moveTo.y = 0;
this.sprite.flipX = false;
this.sprite.angle = 0;
}
moveRight() {
this.moveTo.x = 1;
this.moveTo.y = 0;
this.sprite.flipX = false;
this.sprite.angle = 0;
}
moveUp() {
this.moveTo.x = 0;
this.moveTo.y = -1;
this.sprite.angle = 0;
}
moveDown() {
this.moveTo.x = 0;
this.moveTo.y = 1;
this.sprite.angle = 0;
}
update() {
this.sprite.setVelocity(
this.moveTo.x * this.speed,
this.moveTo.y * this.speed
);
this.turn();
if (
this.directions[this.current] &&
!this.isSafe(this.directions[this.current].index)
) {
this.sprite.anims.play("faceRight", true);
this.takeRandomTurn();
}
}
setDirections(directions) {
this.directions = directions;
}
setTurningPoint(turningPoint) {
this.turningPoint = turningPoint;
}
setTurn(turnTo) {
if (
!this.directions[turnTo] ||
this.turning === turnTo ||
this.current === turnTo ||
!this.isSafe(this.directions[turnTo].index)
) {
return false;
}
//console.log("turning:"+this.turning+" current:"+this.current+" turnTo:"+turnTo);
if (this.opposites[turnTo] && this.opposites[turnTo] === this.current) {
this.move(turnTo);
this.turning = Phaser.NONE;
this.turningPoint = new Phaser.Geom.Point();
} else {
this.turning = turnTo;
}
}
takeRandomTurn() {
let turns = [];
for (let i = 0; i < this.directions.length; i++) {
let direction = this.directions[i];
if (direction) {
if (this.isSafe(direction.index)) {
turns.push(i);
}
}
}
if (turns.length >= 2) {
let index = turns.indexOf(this.opposites[this.current]);
if (index > -1) {
turns.splice(index, 1);
}
}
let turn = this.rnd.pick(turns);
this.setTurn(turn);
this.turnCount = 0;
this.turnAt = this.rnd.pick(this.turnAtTime);
}
turn() {
if (this.turnCount === this.turnAt) {
this.takeRandomTurn();
}
this.turnCount++;
if (this.turning === Phaser.NONE) {
return false;
}
// This needs a threshold, because at high speeds you can't turn because the coordinates skip past
if (
!Phaser.Math.Within(this.sprite.x, this.turningPoint.x, this.threshold) ||
!Phaser.Math.Within(this.sprite.y, this.turningPoint.y, this.threshold)
) {
return false;
}
this.sprite.setPosition(this.turningPoint.x, this.turningPoint.y);
this.move(this.turning);
this.turning = Phaser.NONE;
this.turningPoint = new Phaser.Geom.Point();
return true;
}
move(direction) {
this.current = direction;
switch (direction) {
case Phaser.LEFT:
this.moveLeft();
break;
case Phaser.RIGHT:
this.moveRight();
break;
case Phaser.UP:
this.moveUp();
break;
case Phaser.DOWN:
this.moveDown();
break;
}
}
isSafe(index) {
for (let i of this.safetile) {
if (i === index) return true;
}
return false;
}
drawDebug(graphics) {
let thickness = 4;
let alpha = 1;
let color = 0x00ff00;
for (var t = 0; t < 9; t++) {
if (this.directions[t] === null || this.directions[t] === undefined) {
continue;
}
if (!this.isSafe(this.directions[t].index)) {
color = 0xff0000;
} else {
color = 0x00ff00;
}
graphics.lineStyle(thickness, color, alpha);
graphics.strokeRect(
this.directions[t].pixelX,
this.directions[t].pixelY,
32,
32
);
}
color = 0x00ff00;
graphics.lineStyle(thickness, color, alpha);
graphics.strokeRect(this.turningPoint.x, this.turningPoint.y, 1, 1);
}
}
class Player {
constructor(scene, position, anim, dieCallback) {
this.sprite = scene.physics.add
.sprite(position.x, position.y, "pacman")
.setScale(0.9)
.setOrigin(0.5);
this.spawnPoint = position;
this.anim = anim;
this.dieCallback = dieCallback;
this.speed = 95;
this.moveTo = new Phaser.Geom.Point();
this.sprite.angle = 0;
this.safetile = [-1, 18];
this.directions = [];
this.opposites = [
null,
null,
null,
null,
null,
Phaser.DOWN,
Phaser.UP,
Phaser.RIGHT,
Phaser.LEFT
];
this.turning = Phaser.NONE;
this.current = Phaser.NONE;
this.turningPoint = new Phaser.Geom.Point();
this.threshold = 5;
this.life = 3;
this.score = 0;
this.active = true;
this.sprite.anims.play(this.anim.Stay, true);
let ref = this;
this.sprite.on(
"animationcomplete",
function(animation, frame) {
ref.animComplete(animation, frame);
},
scene
);
this.playing = false;
}
die() {
this.active = false;
this.playing = false;
this.life--;
this.moveTo = new Phaser.Geom.Point();
setTimeout(() => {
this.dieCallback();
}, 500)
}
animComplete(animation, frame) {
if (animation.key == this.anim.Die) {
this.dieCallback();
}
}
respawn() {
this.active = true;
this.playing = false;
this.sprite.setPosition(this.spawnPoint.x, this.spawnPoint.y);
this.moveTo = new Phaser.Geom.Point();
this.sprite.anims.play(this.anim.Stay, true);
this.sprite.angle = 0;
this.turning = Phaser.NONE;
this.current = Phaser.NONE;
}
moveLeft() {
this.moveTo.x = -1;
this.moveTo.y = 0;
this.sprite.anims.play(this.anim.Eat, true);
this.sprite.angle = 0;
}
moveRight() {
this.moveTo.x = 1;
this.moveTo.y = 0;
this.sprite.anims.play(this.anim.Eat, true);
this.sprite.angle = 0;
}
moveUp() {
this.moveTo.x = 0;
this.moveTo.y = -1;
this.sprite.anims.play(this.anim.Eat, true);
this.sprite.angle = 0;
}
moveDown() {
this.moveTo.x = 0;
this.moveTo.y = 1;
this.sprite.anims.play(this.anim.Eat, true);
this.sprite.angle = 0;
}
update() {
this.sprite.setVelocity(
this.moveTo.x * this.speed,
this.moveTo.y * this.speed
);
this.turn();
if (
this.directions[this.current] &&
!this.isSafe(this.directions[this.current].index)
) {
this.sprite.anims.play("faceRight", true);
}
}
setDirections(directions) {
this.directions = directions;
}
setTurningPoint(turningPoint) {
this.turningPoint = turningPoint;
}
setTurn(turnTo) {
if (
!this.active ||
!this.directions[turnTo] ||
this.turning === turnTo ||
this.current === turnTo ||
!this.isSafe(this.directions[turnTo].index)
) {
return false;
}
if (this.opposites[turnTo] && this.opposites[turnTo] === this.current) {
this.move(turnTo);
this.turning = Phaser.NONE;
this.turningPoint = new Phaser.Geom.Point();
} else {
this.turning = turnTo;
}
}
turn() {
if (this.turning === Phaser.NONE) {
return false;
}
// This needs a threshold, because at high speeds you can't turn because the coordinates skip past
if (
!Phaser.Math.Within(this.sprite.x, this.turningPoint.x, this.threshold) ||
!Phaser.Math.Within(this.sprite.y, this.turningPoint.y, this.threshold)
) {
return false;
}
this.sprite.setPosition(this.turningPoint.x, this.turningPoint.y);
this.move(this.turning);
this.turning = Phaser.NONE;
this.turningPoint = new Phaser.Geom.Point();
return true;
}
move(direction) {
this.playing = true;
this.current = direction;
switch (direction) {
case Phaser.LEFT:
this.moveLeft();
break;
case Phaser.RIGHT:
this.moveRight();
break;
case Phaser.UP:
this.moveUp();
break;
case Phaser.DOWN:
this.moveDown();
break;
}
}
isSafe(index) {
for (let i of this.safetile) {
if (i === index) return true;
}
return false;
}
drawDebug(graphics) {
let thickness = 4;
let alpha = 1;
let color = 0x00ff00;
for (var t = 0; t < 9; t++) {
if (this.directions[t] === null || this.directions[t] === undefined) {
continue;
}
if (this.directions[t].index !== -1) {
color = 0xff0000;
} else {
color = 0x00ff00;
}
graphics.lineStyle(thickness, color, alpha);
graphics.strokeRect(
this.directions[t].pixelX,
this.directions[t].pixelY,
32,
32
);
}
color = 0x00ff00;
graphics.lineStyle(thickness, color, alpha);
graphics.strokeRect(this.turningPoint.x, this.turningPoint.y, 1, 1);
}
}