<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>Emoji Pac-Man with Matrix Ending</title>
<style>
body, html {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
font-family: Arial, sans-serif;
color: #fff;
}
#rainCanvas {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
#game-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
text-align: center;
}
#maze {
display: inline-grid;
grid-template-columns: repeat(10, 30px);
gap: 1px;
background-color: rgba(17, 17, 17, 0.7);
padding: 10px;
border-radius: 10px;
}
.cell {
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
font-size: 20px;
}
#score {
margin-top: 10px;
font-size: 20px;
}
#fade-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: black;
opacity: 0;
transition: opacity 2s;
pointer-events: none;
z-index: 1000;
}
#youtube-player {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1001;
display: none;
width: 61vw;
height: 34.3125vw; /* 16:9 aspect ratio */
}
#youtube-player iframe {
width: 100%;
height: 100%;
}
#controls {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
gap: 10px;
}
.control-btn {
width: 60px;
height: 60px;
font-size: 24px;
background-color: rgba(255, 255, 255, 0.2);
border: none;
border-radius: 50%;
color: white;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
}
</style>
</head>
<body>
<div id="fade-overlay"></div>
<canvas id="rainCanvas"></canvas>
<div id="game-container">
<h1>Emoji Pac-Man</h1>
<div id="maze"></div>
<div id="score">Score: 0</div>
</div>
<div id="controls">
<button class="control-btn" id="up">โ</button>
<button class="control-btn" id="left">โ</button>
<button class="control-btn" id="down">โ</button>
<button class="control-btn" id="right">โ</button>
</div>
<div id="youtube-player"></div>
<script>
// EmojiRain class
class EmojiRain {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.resizeCanvas();
this.initializeDrops();
this.densityChangeInterval = 5000;
this.transitionDuration = 2000;
this.minDensity = 0.5;
this.maxDensity = 2;
this.currentDensity = 1;
this.targetDensity = 1;
this.lastUpdateTime = Date.now();
window.addEventListener('resize', () => this.resizeCanvas());
this.startDensityFluctuation();
}
resizeCanvas() {
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
this.initializeDrops();
}
initializeDrops() {
this.drops = [];
this.baseDropCount = Math.floor(this.canvas.width * this.canvas.height / 10000);
this.adjustDropCount(this.currentDensity);
}
adjustDropCount(densityFactor) {
const newDropCount = Math.floor(this.baseDropCount * densityFactor);
if (newDropCount > this.drops.length) {
for (let i = this.drops.length; i < newDropCount; i++) {
this.drops.push(this.createDrop());
}
} else if (newDropCount < this.drops.length) {
this.drops = this.drops.slice(0, newDropCount);
}
}
startDensityFluctuation() {
setInterval(() => {
this.targetDensity = Math.random() * (this.maxDensity - this.minDensity) + this.minDensity;
}, this.densityChangeInterval);
}
updateDensity() {
const now = Date.now();
const deltaTime = now - this.lastUpdateTime;
this.lastUpdateTime = now;
const t = Math.min(deltaTime / this.transitionDuration, 1);
this.currentDensity += (this.targetDensity - this.currentDensity) * t;
this.adjustDropCount(this.currentDensity);
}
createDrop() {
return {
x: Math.random() * this.canvas.width,
y: Math.random() * this.canvas.height,
speed: Math.random() * 2 + 1,
size: Math.random() * 20 + 10,
emoji: this.getRandomEmoji()
};
}
getRandomEmoji() {
const emojis = ['๐', '๐', '๐คฃ', 'โค๏ธ', '๐', '๐', '๐ญ', '๐', '๐', '๐ฅฐ', '๐', '๐', '๐ฅ', '๐', '๐คฆ', '๐ฅบ', '๐
', 'โฅ๏ธ', '๐ค', 'โบ๏ธ', '๐', '๐', '๐คท', '๐', '๐', '๐', '๐', '๐', '๐ค', '๐', '๐', '๐ณ', '๐', '๐ฅณ', '๐ช', 'โจ', '๐', '๐', '๐', '๐ข', '๐', '๐ฉ', '๐', '๐', '๐ฏ', '๐น', '๐', '๐', '๐', '๐', '๐ก', '๐คค', '๐', '๐', '๐', '๐ค', '๐', 'โฃ๏ธ', '๐', '๐คช', '๐', '๐คฉ', '๐', '๐ฌ', '๐', '๐', '๐', '', '๐', '๐', '๐', '๐ฉ', '๐', 'โ
', '๐', 'โน๏ธ', '๐ฅ', '๐', '๐', '๐ฅด', '๐ฑ', '๐คญ', '๐ด', '๐', '๐', '๐', '๐', '๐ธ', '๐', '๐ถ', '๐', 'โ๏ธ', '๐ค', '', '๐ฐ', '๐', '๐', 'โ๏ธ', '๐ฅต', '๐', '๐', '๐', '๐ฎ', '๐', '๐', '๐ท', '๐', '๐', '๐คค', '๐ฆ', '๐ฅ', '๐ญ', '๐', '๐
', '๐', '๐', '๐ฆด', '๐ฐ', '๐ฝ', '๐', '๐', '๐', '๐ญ', '๐', '๐', '๐', '๐ฉ', '๐ฃ', '๐ต'];
return emojis[Math.floor(Math.random() * emojis.length)];
}
draw() {
this.updateDensity();
this.ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.drops.forEach(drop => {
this.ctx.font = `${drop.size}px Arial`;
this.ctx.fillStyle = 'rgba(255, 255, 255, 0.8)';
this.ctx.fillText(drop.emoji, drop.x, drop.y);
drop.y += drop.speed;
if (drop.y > this.canvas.height) {
Object.assign(drop, this.createDrop(), {y: -drop.size});
}
});
}
animate() {
this.draw();
requestAnimationFrame(() => this.animate());
}
}
// MatrixRain class
class MatrixRain {
constructor(canvas) {
this.canvas = canvas;
this.ctx = canvas.getContext('2d');
this.characters = 'ใขใคใฆใจใชใซใญใฏใฑใณใตใทในใปใฝใฟใใใใใใใใใใใใใใใใใ ใกใขใคใฆใจใฉใชใซใฌใญใฏใฒใณ';
this.drops = [];
this.initializeDrops();
}
initializeDrops() {
const columns = this.canvas.width / 20;
for (let i = 0; i < columns; i++) {
this.drops[i] = Math.random() * -100;
}
}
draw() {
this.ctx.fillStyle = 'rgba(0, 0, 0, 0.05)';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
this.ctx.fillStyle = '#0F0';
this.ctx.font = '15px monospace';
for (let i = 0; i < this.drops.length; i++) {
const text = this.characters[Math.floor(Math.random() * this.characters.length)];
this.ctx.fillText(text, i * 20, this.drops[i] * 20);
if (this.drops[i] * 20 > this.canvas.height && Math.random() > 0.975) {
this.drops[i] = 0;
}
this.drops[i]++;
}
}
animate() {
this.draw();
requestAnimationFrame(() => this.animate());
}
}
const maze = document.getElementById('maze');
const scoreElement = document.getElementById('score');
const fadeOverlay = document.getElementById('fade-overlay');
const canvas = document.getElementById('rainCanvas');
let score = 0;
let pacmanPosition = { x: 0, y: 0 };
const emojis = ['๐', '๐', '๐', '๐', '๐', '๐', '๐', '๐', '๐', '๐ฅ'];
const grid = [];
let emojisLeft = 0;
let pillsLeft = 0;
let emojiRain;
let matrixRain;
let isMatrixRainActive = false;
let gameState = 'playing'; // 'playing', 'matrix', 'resetting'
let animationFrameId;
function initializeGame() {
if (animationFrameId) {
cancelAnimationFrame(animationFrameId);
}
gameState = 'playing';
score = 0;
emojisLeft = 0;
pillsLeft = 0;
isMatrixRainActive = false;
// Clear the entire grid
for (let y = 0; y < 10; y++) {
grid[y] = [];
for (let x = 0; x < 10; x++) {
grid[y][x] = emojis[Math.floor(Math.random() * emojis.length)];
emojisLeft++;
}
}
grid[0][0] = '๐ฎ';
emojisLeft--;
pacmanPosition = { x: 0, y: 0 };
renderMaze();
updateScore();
// Ensure we're using EmojiRain
canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
emojiRain = new EmojiRain(canvas);
animateRain();
}
function animateRain() {
if (isMatrixRainActive) {
matrixRain.draw();
} else {
emojiRain.draw();
}
animationFrameId = requestAnimationFrame(animateRain);
}
function renderMaze() {
maze.innerHTML = '';
for (let y = 0; y < 10; y++) {
for (let x = 0; x < 10; x++) {
const cell = document.createElement('div');
cell.className = 'cell';
cell.textContent = grid[y][x];
maze.appendChild(cell);
}
}
}
function movePacman(dx, dy) {
if (gameState === 'resetting') return;
const newX = pacmanPosition.x + dx;
const newY = pacmanPosition.y + dy;
if (newX >= 0 && newX < 10 && newY >= 0 && newY < 10) {
if (grid[newY][newX] === '๐') {
eatPill();
return;
}
if (grid[newY][newX] !== '๐ฎ' && grid[newY][newX] !== ' ') {
score += 10;
emojisLeft--;
updateScore();
checkWinCondition();
}
grid[pacmanPosition.y][pacmanPosition.x] = ' ';
pacmanPosition = { x: newX, y: newY };
grid[newY][newX] = '๐ฎ';
renderMaze();
}
}
function updateScore() {
scoreElement.textContent = `Score: ${score}`;
}
function checkWinCondition() {
if (emojisLeft === 0 && gameState === 'playing') {
gameState = 'matrix';
showMatrixRain();
showPills();
}
}
function showMatrixRain() {
isMatrixRainActive = true;
canvas.getContext('2d').clearRect(0, 0, canvas.width, canvas.height);
matrixRain = new MatrixRain(canvas);
}
function showPills() {
const emptyPositions = [];
for (let y = 0; y < 10; y++) {
for (let x = 0; x < 10; x++) {
if (grid[y][x] === ' ' && (x !== pacmanPosition.x || y !== pacmanPosition.y)) {
emptyPositions.push({x, y});
}
}
}
if (emptyPositions.length >= 2) {
const [pos1, pos2] = emptyPositions.sort(() => 0.5 - Math.random()).slice(0, 2);
grid[pos1.y][pos1.x] = '๐';
grid[pos2.y][pos2.x] = '๐';
pillsLeft = 2;
renderMaze();
}
}
function eatPill() {
pillsLeft--;
gameState = 'resetting';
fadeToBlack(() => {
loadYoutubeVideo();
});
}
function fadeToBlack(callback) {
fadeOverlay.style.opacity = '1';
setTimeout(callback, 2000);
}
function fadeFromBlack() {
fadeOverlay.style.opacity = '0';
gameState = 'playing';
}
let player;
function loadYoutubeVideo() {
const youtubePlayer = document.getElementById('youtube-player');
youtubePlayer.style.display = 'block';
if (typeof YT === 'undefined' || typeof YT.Player === 'undefined') {
console.error('YouTube API not loaded');
return;
}
player = new YT.Player('youtube-player', {
height: '34.3125vw',
width: '61vw',
videoId: 'GMgsFZ4rkEI',
playerVars: {
'autoplay': 1,
'controls': 0
},
events: {
'onReady': onPlayerReady,
'onStateChange': onPlayerStateChange,
'onError': onPlayerError
}
});
}
function onPlayerReady(event) {
console.log('Player ready');
event.target.playVideo();
}
function onPlayerStateChange(event) {
console.log('Player state changed:', event.data);
if (event.data == YT.PlayerState.ENDED) {
console.log('Video ended, reloading page');
location.reload();
}
}
function onPlayerError(event) {
console.error('Player error:', event.data);
}
function addTouchControls() {
const controls = ['up', 'left', 'down', 'right'];
controls.forEach(direction => {
const button = document.getElementById(direction);
button.addEventListener('touchstart', (e) => {
e.preventDefault();
handleMove(direction);
});
button.addEventListener('mousedown', (e) => {
e.preventDefault();
handleMove(direction);
});
});
}
function handleMove(direction) {
switch(direction) {
case 'up': movePacman(0, -1); break;
case 'down': movePacman(0, 1); break;
case 'left': movePacman(-1, 0); break;
case 'right': movePacman(1, 0); break;
}
}
document.addEventListener('keydown', (e) => {
switch(e.key) {
case 'ArrowUp': handleMove('up'); break;
case 'ArrowDown': handleMove('down'); break;
case 'ArrowLeft': handleMove('left'); break;
case 'ArrowRight': handleMove('right'); break;
}
});
// Initialize the game and start the emoji rain
window.onload = () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
emojiRain = new EmojiRain(canvas);
initializeGame();
addTouchControls();
};
// Load YouTube API
var tag = document.createElement('script');
tag.src = "https://www.youtube.com/iframe_api";
var firstScriptTag = document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
// This function will be called by the YouTube IFrame API when it's ready
function onYouTubeIframeAPIReady() {
console.log('YouTube API ready');
}
</script>
</body>
</html>