Téléverser les fichiers vers "Sources"
This commit is contained in:
commit
236b5b8d67
902
Sources/game.js
Normal file
902
Sources/game.js
Normal file
@ -0,0 +1,902 @@
|
||||
class Game {
|
||||
constructor() {
|
||||
this.menuScreen = document.getElementById('menuScreen');
|
||||
this.gameScreen = document.getElementById('gameScreen');
|
||||
this.canvas = document.getElementById('gameCanvas');
|
||||
|
||||
// Stats initialization
|
||||
this.stats = {
|
||||
beersServed: 0,
|
||||
totalMoney: 0,
|
||||
perfectPours: 0,
|
||||
maxStreak: 0
|
||||
};
|
||||
|
||||
if (!this.canvas) {
|
||||
console.error('Game canvas not found!');
|
||||
return;
|
||||
}
|
||||
|
||||
this.ctx = this.canvas.getContext('2d');
|
||||
|
||||
// Set canvas size
|
||||
this.canvas.width = 600;
|
||||
this.canvas.height = 600;
|
||||
|
||||
// Colors
|
||||
this.backgroundColor = '#1a1a1a';
|
||||
this.foregroundColor = '#e0e0e0';
|
||||
|
||||
// Game state
|
||||
this.money = 0;
|
||||
this.timer = 60;
|
||||
this.currentLevel = 0;
|
||||
this.targetLevel = 0;
|
||||
this.isPouring = false;
|
||||
this.gameActive = false;
|
||||
this.lastTime = 0;
|
||||
this.feedbackMessage = '';
|
||||
this.feedbackTimer = 0;
|
||||
this.perfectStreak = 0;
|
||||
|
||||
// Keg system
|
||||
this.kegCapacity = 20;
|
||||
this.currentKeg = this.kegCapacity;
|
||||
this.kegPrice = 10;
|
||||
this.kegsInStock = 0; // Add this line
|
||||
|
||||
// Level system
|
||||
this.level = 1;
|
||||
this.levelTarget = 10;
|
||||
this.currentLevelMoney = 0;
|
||||
this.upgrades = {
|
||||
pourSpeed: { level: 1, cost: 5, name: "Pour Speed" },
|
||||
accuracy: { level: 1, cost: 8, name: "Accuracy" },
|
||||
timer: { level: 1, cost: 10, name: "Extra Time" }
|
||||
};
|
||||
this.pourSpeed = 0.005;
|
||||
|
||||
// Background canvas setup
|
||||
this.bgCanvas = document.getElementById('backgroundCanvas');
|
||||
if (this.bgCanvas) {
|
||||
this.bgCtx = this.bgCanvas.getContext('2d');
|
||||
this.bgCanvas.width = window.innerWidth;
|
||||
this.bgCanvas.height = window.innerHeight;
|
||||
|
||||
// Bubbles setup
|
||||
this.bubbles = Array.from({length: 50}, () => this.createBubble());
|
||||
this.backgroundAnimation();
|
||||
}
|
||||
|
||||
// Initialize instructions
|
||||
const instructionsCanvas = document.getElementById('instructionsCanvas');
|
||||
if (instructionsCanvas) {
|
||||
instructionsCanvas.width = 100;
|
||||
instructionsCanvas.height = 80;
|
||||
const ictx = instructionsCanvas.getContext('2d');
|
||||
this.drawMouseIcon(ictx, 50, 40);
|
||||
}
|
||||
|
||||
this.setupEventListeners();
|
||||
}
|
||||
|
||||
startGame() {
|
||||
if (!this.menuScreen || !this.gameScreen) return;
|
||||
if (!this.canvas || !this.ctx) return;
|
||||
|
||||
this.menuScreen.classList.add('hidden');
|
||||
this.gameScreen.classList.remove('hidden');
|
||||
this.canvas.style.display = 'block';
|
||||
this.gameActive = true;
|
||||
this.timer = 60 + (this.upgrades.timer.level - 1) * 10;
|
||||
this.money = 0;
|
||||
this.currentLevel = 0;
|
||||
this.currentLevelMoney = 0;
|
||||
this.level = 1;
|
||||
this.levelTarget = 10;
|
||||
this.lastTime = Date.now();
|
||||
this.resetLevel();
|
||||
|
||||
this.draw();
|
||||
this.gameLoop();
|
||||
}
|
||||
|
||||
draw() {
|
||||
this.ctx.fillStyle = this.backgroundColor;
|
||||
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
|
||||
// Dessiner le contour arrondi
|
||||
this.ctx.strokeStyle = this.foregroundColor;
|
||||
this.ctx.lineWidth = 4;
|
||||
const radius = 15;
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(radius, 0);
|
||||
this.ctx.lineTo(this.canvas.width - radius, 0);
|
||||
this.ctx.quadraticCurveTo(this.canvas.width, 0, this.canvas.width, radius);
|
||||
this.ctx.lineTo(this.canvas.width, this.canvas.height - radius);
|
||||
this.ctx.quadraticCurveTo(this.canvas.width, this.canvas.height, this.canvas.width - radius, this.canvas.height);
|
||||
this.ctx.lineTo(radius, this.canvas.height);
|
||||
this.ctx.quadraticCurveTo(0, this.canvas.height, 0, this.canvas.height - radius);
|
||||
this.ctx.lineTo(0, radius);
|
||||
this.ctx.quadraticCurveTo(0, 0, radius, 0);
|
||||
this.ctx.stroke();
|
||||
|
||||
// Draw game elements
|
||||
this.drawTap();
|
||||
this.drawGlass();
|
||||
this.drawMoneyBag();
|
||||
this.drawTimer();
|
||||
this.drawPrice();
|
||||
this.drawKegMeter(); // Add this line
|
||||
this.drawKegStock(); // Ajout de l'indicateur de fûts en stock
|
||||
|
||||
// Draw level info
|
||||
this.ctx.font = '12px "Press Start 2P"';
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
this.ctx.fillText(`Level ${this.level}`, 10, 30);
|
||||
this.ctx.fillText(`Target: $${this.levelTarget}`, 10, 50);
|
||||
this.ctx.fillText(`Progress: $${this.currentLevelMoney}`, 10, 70);
|
||||
|
||||
if (this.isPouring) {
|
||||
this.drawPourStream();
|
||||
}
|
||||
|
||||
if (this.feedbackTimer > 0) {
|
||||
this.drawFeedback();
|
||||
}
|
||||
}
|
||||
|
||||
drawFeedback() {
|
||||
const animationProgress = 1 - (this.feedbackTimer / 1.5);
|
||||
|
||||
this.ctx.font = '16px "Press Start 2P"';
|
||||
const centerX = this.canvas.width / 2;
|
||||
const textWidth = this.ctx.measureText(this.feedbackMessage).width;
|
||||
const feedbackY = 450;
|
||||
|
||||
let shakeOffset = 0;
|
||||
let verticalBounce = 0;
|
||||
|
||||
if (this.feedbackMessage.includes('PERFECT!')) {
|
||||
const shakeSpeed = Math.max(10, 30 - this.perfectStreak * 2);
|
||||
const shakeAmount = 2 + (this.perfectStreak * 0.8);
|
||||
verticalBounce = Math.sin(Date.now() / 200) * (this.perfectStreak * 2);
|
||||
shakeOffset = Math.sin(Date.now() / shakeSpeed) * shakeAmount;
|
||||
|
||||
// Ajout d'une rotation avec le streak
|
||||
this.ctx.save();
|
||||
this.ctx.translate(centerX, feedbackY);
|
||||
this.ctx.rotate(Math.sin(Date.now() / 200) * (this.perfectStreak * 0.02));
|
||||
this.ctx.translate(-centerX, -feedbackY);
|
||||
}
|
||||
|
||||
if (this.feedbackMessage.includes('PERFECT!')) {
|
||||
const goldIntensity = Math.min(255, 218 + (this.perfectStreak * 5));
|
||||
this.ctx.fillStyle = `rgb(${goldIntensity}, ${Math.floor(goldIntensity * 0.84)}, 0)`;
|
||||
} else {
|
||||
this.ctx.fillStyle = `rgba(224, 224, 224, ${1 - animationProgress * 0.7})`;
|
||||
}
|
||||
|
||||
this.ctx.fillText(this.feedbackMessage, centerX - textWidth/2 + shakeOffset, feedbackY + verticalBounce);
|
||||
|
||||
if (this.feedbackMessage.includes('PERFECT!')) {
|
||||
this.ctx.restore();
|
||||
}
|
||||
}
|
||||
|
||||
drawTap() {
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
this.ctx.strokeStyle = this.foregroundColor;
|
||||
this.ctx.lineWidth = 2;
|
||||
|
||||
// Corps principal du robinet
|
||||
this.ctx.beginPath();
|
||||
this.ctx.rect(285, 25, 30, 25); // Ajusté pour le nouveau centre
|
||||
this.ctx.fill();
|
||||
|
||||
// Bec verseur
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(295, 50); // Ajusté
|
||||
this.ctx.lineTo(305, 50); // Ajusté
|
||||
this.ctx.lineTo(300, 60); // Ajusté
|
||||
this.ctx.closePath();
|
||||
this.ctx.fill();
|
||||
|
||||
// Base décorative
|
||||
this.ctx.beginPath();
|
||||
this.ctx.rect(290, 20, 20, 5); // Ajusté
|
||||
this.ctx.fill();
|
||||
|
||||
// Poignée latérale avec style rétro
|
||||
const handleRotation = this.isPouring ? Math.PI / 6 : 0;
|
||||
this.ctx.save();
|
||||
this.ctx.translate(320, 35); // Ajusté pour aligner avec le nouveau corps du robinet
|
||||
this.ctx.rotate(handleRotation);
|
||||
|
||||
// Tige de la poignée
|
||||
this.ctx.beginPath();
|
||||
this.ctx.rect(0, -3, 15, 6);
|
||||
this.ctx.fill();
|
||||
|
||||
// Embout de la poignée
|
||||
this.ctx.beginPath();
|
||||
this.ctx.rect(15, -5, 5, 10);
|
||||
this.ctx.fill();
|
||||
|
||||
this.ctx.restore();
|
||||
|
||||
// Détails décoratifs
|
||||
this.ctx.beginPath();
|
||||
this.ctx.rect(285, 32, 30, 2); // Ajusté pour aligner avec le nouveau corps
|
||||
this.ctx.rect(285, 40, 30, 2); // Ajusté pour aligner avec le nouveau corps
|
||||
this.ctx.fill();
|
||||
}
|
||||
|
||||
checkResult() {
|
||||
const accuracy = Math.abs(this.currentLevel - this.targetLevel);
|
||||
const accuracyBonus = this.upgrades.accuracy.level * 0.005;
|
||||
|
||||
this.stats.beersServed++;
|
||||
|
||||
if (accuracy < (0.02 + accuracyBonus)) {
|
||||
const streakBonus = this.perfectStreak; // Get current streak before incrementing
|
||||
this.money += 2 + streakBonus; // Add streak bonus to money
|
||||
this.currentLevelMoney += 2 + streakBonus;
|
||||
this.perfectStreak++;
|
||||
this.stats.perfectPours++;
|
||||
this.stats.maxStreak = Math.max(this.stats.maxStreak, this.perfectStreak);
|
||||
this.stats.totalMoney += 2 + streakBonus;
|
||||
this.feedbackMessage = this.perfectStreak > 1 ?
|
||||
`PERFECT! x${this.perfectStreak} +$${2 + streakBonus}` :
|
||||
'PERFECT! +$2';
|
||||
} else {
|
||||
this.perfectStreak = 0;
|
||||
if (accuracy < (0.05 + accuracyBonus)) {
|
||||
this.money += 1;
|
||||
this.currentLevelMoney += 1;
|
||||
this.feedbackMessage = 'GOOD! +$1';
|
||||
if (this.currentKeg <= 0) {
|
||||
this.showKegEmptyMessage();
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentKeg--;
|
||||
} else if (this.currentLevel > this.targetLevel) {
|
||||
this.money = Math.max(0, this.money - 1);
|
||||
this.feedbackMessage = 'TOO MUCH! -$1';
|
||||
} else {
|
||||
this.feedbackMessage = 'NOT ENOUGH!';
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
this.feedbackTimer = 1.5;
|
||||
setTimeout(() => {
|
||||
if (this.currentLevelMoney >= this.levelTarget) {
|
||||
this.showShop();
|
||||
} else {
|
||||
this.resetLevel();
|
||||
}
|
||||
}, 1000);
|
||||
}
|
||||
showKegEmptyMessage() {
|
||||
this.gameActive = false;
|
||||
const kegScreen = document.createElement('div');
|
||||
kegScreen.style.position = 'absolute';
|
||||
kegScreen.style.top = '50%';
|
||||
kegScreen.style.left = '50%';
|
||||
kegScreen.style.transform = 'translate(-50%, -50%)';
|
||||
kegScreen.style.backgroundColor = this.backgroundColor;
|
||||
kegScreen.style.border = `2px solid ${this.foregroundColor}`;
|
||||
kegScreen.style.padding = '20px';
|
||||
kegScreen.style.textAlign = 'center';
|
||||
kegScreen.style.color = this.foregroundColor;
|
||||
kegScreen.style.fontFamily = '"Press Start 2P", monospace';
|
||||
|
||||
kegScreen.innerHTML = `
|
||||
<h2>KEG EMPTY!</h2>
|
||||
<p>You need a new keg to continue.</p>
|
||||
<p>Cost: $${this.kegPrice}</p>
|
||||
${this.kegsInStock > 0 ? `
|
||||
<button class="button" onclick="game.useStoredKeg()">
|
||||
Use Keg from Stock
|
||||
</button>
|
||||
` : `
|
||||
<button class="button" onclick="game.buyNewKeg()"
|
||||
${this.money < this.kegPrice ? 'disabled' : ''}>
|
||||
Buy New Keg
|
||||
</button>
|
||||
`}
|
||||
`;
|
||||
|
||||
this.gameScreen.appendChild(kegScreen);
|
||||
window.game = this;
|
||||
}
|
||||
buyNewKeg() {
|
||||
if (this.money >= this.kegPrice) {
|
||||
this.money -= this.kegPrice;
|
||||
this.currentKeg = this.kegCapacity;
|
||||
this.gameScreen.querySelector('div[style*="position: absolute"]')?.remove();
|
||||
this.gameActive = true;
|
||||
this.lastTime = Date.now();
|
||||
this.gameLoop();
|
||||
}
|
||||
}
|
||||
resetLevel() {
|
||||
this.currentLevel = 0;
|
||||
|
||||
// Cible plus aléatoire avec des valeurs plus extrêmes
|
||||
this.targetLevel = Math.random() * 0.8 + 0.1;
|
||||
|
||||
// Calcul du bonus de vitesse basé sur le niveau
|
||||
const levelSpeedBonus = Math.floor((this.level - 1) / 2) * 0.1; // Adjusted to reduce speed increase
|
||||
|
||||
// Vitesses réduites pour les deux modes
|
||||
const baseSpeed = Math.random() > 0.5 ?
|
||||
(Math.random() * 0.004 + 0.003) : // Vitesse lente (était 0.008 + 0.005)
|
||||
(Math.random() * 0.01 + 0.008); // Vitesse rapide (était 0.02 + 0.015)
|
||||
|
||||
this.pourSpeed = baseSpeed *
|
||||
(1 + levelSpeedBonus) *
|
||||
(1 + (this.upgrades.pourSpeed.level - 1) * 0.1); // Adjusted to reduce speed increase
|
||||
}
|
||||
|
||||
showShop() {
|
||||
this.gameActive = false;
|
||||
|
||||
// Remove any existing shop screen first
|
||||
const existingShop = this.gameScreen.querySelector('div[style*="position: absolute"]');
|
||||
if (existingShop) {
|
||||
existingShop.remove();
|
||||
}
|
||||
|
||||
const shopScreen = document.createElement('div');
|
||||
shopScreen.style.position = 'absolute';
|
||||
shopScreen.style.top = '50%';
|
||||
shopScreen.style.left = '50%';
|
||||
shopScreen.style.transform = 'translate(-50%, -50%)';
|
||||
shopScreen.style.backgroundColor = this.backgroundColor;
|
||||
shopScreen.style.border = `2px solid ${this.foregroundColor}`;
|
||||
shopScreen.style.padding = '20px';
|
||||
shopScreen.style.textAlign = 'center';
|
||||
shopScreen.style.color = this.foregroundColor;
|
||||
shopScreen.style.fontFamily = '"Press Start 2P", monospace';
|
||||
|
||||
shopScreen.innerHTML = `
|
||||
<h2>LEVEL ${this.level} COMPLETE!</h2>
|
||||
<p>Money: $${this.money}</p>
|
||||
<div style="margin: 20px 0;">
|
||||
${Object.entries(this.upgrades).map(([key, upgrade]) => `
|
||||
<button class="button" style="margin: 5px"
|
||||
onclick="game.buyUpgrade('${key}')"
|
||||
${this.money < upgrade.cost ? 'disabled' : ''}>
|
||||
${upgrade.name} (Lvl ${upgrade.level}) - $${upgrade.cost}
|
||||
</button>
|
||||
`).join('')}
|
||||
<button class="button" style="margin: 5px"
|
||||
onclick="game.buyExtraKeg()"
|
||||
${this.money < this.kegPrice ? 'disabled' : ''}>
|
||||
Buy Extra Keg - $${this.kegPrice}
|
||||
</button>
|
||||
</div>
|
||||
<button class="button" onclick="game.nextLevel()">NEXT LEVEL</button>
|
||||
`;
|
||||
|
||||
this.gameScreen.appendChild(shopScreen);
|
||||
window.game = this;
|
||||
}
|
||||
|
||||
// Corriger la méthode buyUpgrade en retirant la méthode buyExtraKeg imbriquée
|
||||
buyUpgrade(type) {
|
||||
const upgrade = this.upgrades[type];
|
||||
if (this.money >= upgrade.cost) {
|
||||
this.money -= upgrade.cost;
|
||||
upgrade.level++;
|
||||
upgrade.cost = Math.floor(upgrade.cost * 1.5);
|
||||
|
||||
// Mettre à jour l'affichage de la boutique
|
||||
this.showShop(); // Refresh the shop display after purchase
|
||||
}
|
||||
}
|
||||
|
||||
// Ajouter buyExtraKeg comme méthode séparée de la classe
|
||||
buyExtraKeg() {
|
||||
if (this.money >= this.kegPrice) {
|
||||
this.money -= this.kegPrice;
|
||||
this.kegsInStock++;
|
||||
|
||||
// Update shop display
|
||||
const shopScreen = this.gameScreen.querySelector('div[style*="position: absolute"]');
|
||||
if (shopScreen) {
|
||||
shopScreen.querySelector('p').textContent = `Money: $${this.money}`;
|
||||
const buttons = shopScreen.querySelectorAll('button');
|
||||
buttons.forEach(button => {
|
||||
if (button.textContent.includes('Buy Extra Keg')) {
|
||||
button.disabled = this.money < this.kegPrice;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ajouter useStoredKeg comme méthode séparée de la classe
|
||||
useStoredKeg() {
|
||||
if (this.kegsInStock > 0) {
|
||||
this.kegsInStock--;
|
||||
this.currentKeg = this.kegCapacity;
|
||||
this.gameScreen.querySelector('div[style*="position: absolute"]')?.remove();
|
||||
this.gameActive = true;
|
||||
this.lastTime = Date.now();
|
||||
this.gameLoop();
|
||||
}
|
||||
}
|
||||
|
||||
nextLevel() {
|
||||
// Prevent multiple executions
|
||||
if (!this.gameActive) {
|
||||
const shopScreen = this.gameScreen.querySelector('div[style*="position: absolute"]');
|
||||
if (shopScreen) {
|
||||
shopScreen.remove();
|
||||
}
|
||||
this.level++;
|
||||
this.levelTarget = Math.floor(this.levelTarget * 1.5);
|
||||
this.currentLevelMoney = 0;
|
||||
this.timer = 60 + (this.upgrades.timer.level - 1) * 10;
|
||||
this.gameActive = true;
|
||||
this.resetLevel();
|
||||
this.lastTime = Date.now();
|
||||
this.gameLoop();
|
||||
}
|
||||
}
|
||||
|
||||
backgroundAnimation() {
|
||||
this.updateBubbles();
|
||||
this.drawBackground();
|
||||
requestAnimationFrame(() => this.backgroundAnimation());
|
||||
}
|
||||
|
||||
createBubble() {
|
||||
return {
|
||||
x: Math.random() * this.bgCanvas.width,
|
||||
y: this.bgCanvas.height + Math.random() * 20,
|
||||
size: Math.random() * 8 + 4,
|
||||
speed: Math.random() * 3 + 1.5, // Vitesse augmentée (était 1.5 + 0.5)
|
||||
opacity: Math.random() * 0.5 + 0.1
|
||||
};
|
||||
}
|
||||
|
||||
updateBubbles() {
|
||||
this.bubbles.forEach(bubble => {
|
||||
bubble.y -= bubble.speed;
|
||||
if (bubble.y < -20) {
|
||||
Object.assign(bubble, this.createBubble());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
drawBackground() {
|
||||
this.bgCtx.clearRect(0, 0, this.bgCanvas.width, this.bgCanvas.height);
|
||||
|
||||
// Draw bubbles
|
||||
this.bubbles.forEach(bubble => {
|
||||
this.bgCtx.beginPath();
|
||||
this.bgCtx.strokeStyle = `rgba(224, 224, 224, ${bubble.opacity})`;
|
||||
this.bgCtx.lineWidth = 2;
|
||||
this.bgCtx.arc(bubble.x, bubble.y, bubble.size, 0, Math.PI * 2);
|
||||
this.bgCtx.stroke();
|
||||
});
|
||||
}
|
||||
|
||||
update() {
|
||||
if (!this.gameActive) return;
|
||||
|
||||
const currentTime = Date.now();
|
||||
const deltaTime = (currentTime - this.lastTime) / 1000;
|
||||
this.lastTime = currentTime;
|
||||
|
||||
this.timer -= deltaTime;
|
||||
if (this.timer <= 0) {
|
||||
this.timer = 0;
|
||||
this.gameActive = false;
|
||||
this.showGameOver();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.isPouring) {
|
||||
this.currentLevel += this.pourSpeed;
|
||||
if (this.currentLevel >= 1) {
|
||||
this.currentLevel = 1;
|
||||
this.isPouring = false;
|
||||
this.checkResult();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.feedbackTimer > 0) {
|
||||
this.feedbackTimer -= deltaTime;
|
||||
}
|
||||
}
|
||||
|
||||
showGameOver() {
|
||||
const gameOverScreen = document.createElement('div');
|
||||
gameOverScreen.style.position = 'absolute';
|
||||
gameOverScreen.style.top = '50%';
|
||||
gameOverScreen.style.left = '50%';
|
||||
gameOverScreen.style.transform = 'translate(-50%, -50%)';
|
||||
gameOverScreen.style.backgroundColor = this.backgroundColor;
|
||||
gameOverScreen.style.border = `2px solid ${this.foregroundColor}`;
|
||||
gameOverScreen.style.padding = '20px';
|
||||
gameOverScreen.style.textAlign = 'center';
|
||||
gameOverScreen.style.color = this.foregroundColor;
|
||||
gameOverScreen.style.fontFamily = '"Press Start 2P", monospace';
|
||||
|
||||
gameOverScreen.innerHTML = `
|
||||
<h2>GAME OVER</h2>
|
||||
<p>Money earned: $${this.money}</p>
|
||||
<div style="margin: 20px 0;">
|
||||
<h3>STATS</h3>
|
||||
<p>Beers Served: ${this.stats.beersServed}</p>
|
||||
<p>Total Money: $${this.stats.totalMoney}</p>
|
||||
<p>Perfect Pours: ${this.stats.perfectPours}</p>
|
||||
<p>Best Streak: ${this.stats.maxStreak}</p>
|
||||
</div>
|
||||
<button class="button" onclick="location.reload()">PLAY AGAIN</button>
|
||||
`;
|
||||
|
||||
this.gameScreen.appendChild(gameOverScreen);
|
||||
}
|
||||
|
||||
gameLoop() {
|
||||
if (this.gameActive) {
|
||||
this.update();
|
||||
this.draw();
|
||||
requestAnimationFrame(() => this.gameLoop());
|
||||
}
|
||||
}
|
||||
|
||||
setupEventListeners() {
|
||||
const startButton = document.getElementById('startButton');
|
||||
const patchNotesButton = document.getElementById('patchNotesButton');
|
||||
|
||||
if (startButton) {
|
||||
startButton.addEventListener('click', () => this.startGame());
|
||||
}
|
||||
|
||||
if (patchNotesButton) {
|
||||
patchNotesButton.addEventListener('click', () => {
|
||||
document.getElementById('patchNotesOverlay').classList.remove('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
if (!this.canvas) return;
|
||||
|
||||
this.canvas.addEventListener('mousedown', () => this.startPouring());
|
||||
this.canvas.addEventListener('mouseup', () => this.stopPouring());
|
||||
this.canvas.addEventListener('touchstart', (e) => {
|
||||
e.preventDefault();
|
||||
this.startPouring();
|
||||
});
|
||||
this.canvas.addEventListener('touchend', (e) => {
|
||||
e.preventDefault();
|
||||
this.stopPouring();
|
||||
});
|
||||
}
|
||||
|
||||
startPouring() {
|
||||
if (this.gameActive && this.currentLevel < 1) {
|
||||
this.isPouring = true;
|
||||
}
|
||||
}
|
||||
|
||||
stopPouring() {
|
||||
if (this.isPouring && this.gameActive) {
|
||||
this.isPouring = false;
|
||||
this.checkResult();
|
||||
}
|
||||
}
|
||||
drawMouseIcon(ctx, x, y) {
|
||||
ctx.strokeStyle = this.foregroundColor;
|
||||
ctx.fillStyle = this.foregroundColor;
|
||||
ctx.lineWidth = 2;
|
||||
|
||||
// Draw mouse outline
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - 12, y - 15);
|
||||
ctx.lineTo(x - 12, y + 10);
|
||||
ctx.quadraticCurveTo(x - 12, y + 20, x, y + 20);
|
||||
ctx.quadraticCurveTo(x + 12, y + 20, x + 12, y + 10);
|
||||
ctx.lineTo(x + 12, y - 15);
|
||||
ctx.quadraticCurveTo(x + 12, y - 20, x, y - 20);
|
||||
ctx.quadraticCurveTo(x - 12, y - 20, x - 12, y - 15);
|
||||
ctx.stroke();
|
||||
|
||||
// Draw scroll wheel
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y - 20);
|
||||
ctx.lineTo(x, y - 5);
|
||||
ctx.stroke();
|
||||
|
||||
// Draw left button
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x - 12, y - 15);
|
||||
ctx.lineTo(x, y - 15);
|
||||
ctx.lineTo(x, y - 5);
|
||||
ctx.lineTo(x - 12, y - 5);
|
||||
ctx.closePath();
|
||||
ctx.fill();
|
||||
|
||||
// Draw scroll indicator
|
||||
ctx.beginPath();
|
||||
ctx.rect(x - 2, y - 12, 4, 4);
|
||||
ctx.stroke();
|
||||
|
||||
// Draw click animation
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y - 20);
|
||||
ctx.quadraticCurveTo(x, y - 35, x - 5, y - 35);
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
drawGlass() {
|
||||
const centerX = this.canvas.width / 2;
|
||||
const glassTop = 200;
|
||||
const glassBottom = 400; // Réduit pour un verre plus harmonieux
|
||||
const glassWidth = 150;
|
||||
|
||||
// Draw glass outline
|
||||
this.ctx.strokeStyle = this.foregroundColor;
|
||||
this.ctx.lineWidth = 2;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(centerX - glassWidth/2, glassTop);
|
||||
this.ctx.lineTo(centerX - glassWidth/2, glassBottom);
|
||||
this.ctx.lineTo(centerX + glassWidth/2, glassBottom);
|
||||
this.ctx.lineTo(centerX + glassWidth/2, glassTop);
|
||||
this.ctx.stroke();
|
||||
|
||||
// Draw enlarged glass handle
|
||||
const handleWidth = 60;
|
||||
const handleHeight = 150;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(centerX + glassWidth/2 + handleWidth/4, glassTop + handleHeight/2, handleWidth/2, Math.PI / 2, -Math.PI / 2, true);
|
||||
this.ctx.stroke();
|
||||
|
||||
// Draw target line
|
||||
this.ctx.setLineDash([4, 4]);
|
||||
this.ctx.beginPath();
|
||||
const targetY = glassBottom - ((glassBottom - glassTop) * this.targetLevel);
|
||||
this.ctx.moveTo(centerX - glassWidth/2, targetY);
|
||||
this.ctx.lineTo(centerX + glassWidth/2, targetY);
|
||||
this.ctx.stroke();
|
||||
this.ctx.setLineDash([]);
|
||||
|
||||
// Draw liquid
|
||||
if (this.currentLevel > 0) {
|
||||
const liquidHeight = (glassBottom - glassTop) * this.currentLevel;
|
||||
this.drawBeerPattern(
|
||||
centerX - glassWidth/2,
|
||||
glassBottom - liquidHeight,
|
||||
glassWidth,
|
||||
liquidHeight
|
||||
);
|
||||
|
||||
// Adjust foam height based on pour speed
|
||||
const baseFoamHeight = 10;
|
||||
const maxFoamHeight = 25;
|
||||
const foamHeight = baseFoamHeight + (maxFoamHeight - baseFoamHeight) * (this.pourSpeed / 0.01);
|
||||
const foamWidth = glassWidth;
|
||||
const foamY = glassBottom - liquidHeight - foamHeight;
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
|
||||
// Draw foam pattern with adjusted alignment
|
||||
const startX = Math.floor(centerX - foamWidth/2) + 2;
|
||||
for (let y = Math.floor(foamY); y < foamY + foamHeight; y += 4) {
|
||||
for (let x = startX; x < centerX + foamWidth/2 - 2; x += 4) {
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(x, y, 2, 0, Math.PI * 2);
|
||||
this.ctx.fill();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawBeerPattern(x, y, width, height) {
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
const dotSize = 2;
|
||||
const spacing = 4;
|
||||
|
||||
for (let i = 0; i < width; i += spacing) {
|
||||
for (let j = 0; j < height; j += spacing) {
|
||||
this.ctx.fillRect(x + i, y + j, dotSize, dotSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawMoneyBag() {
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
this.ctx.font = '24px "Press Start 2P"'; // Agrandi
|
||||
this.ctx.fillText(`$${this.money}`, 50, 300); // Ajusté
|
||||
}
|
||||
|
||||
drawTimer() {
|
||||
const centerX = 500; // Ajusté
|
||||
const centerY = 300; // Ajusté
|
||||
const radius = 35; // Agrandi
|
||||
|
||||
this.ctx.strokeStyle = this.foregroundColor;
|
||||
this.ctx.lineWidth = 2;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
|
||||
this.ctx.stroke();
|
||||
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
this.ctx.font = '16px "Press Start 2P"';
|
||||
const timerText = Math.ceil(this.timer).toString();
|
||||
const textWidth = this.ctx.measureText(timerText).width;
|
||||
this.ctx.fillText(timerText, centerX - textWidth/2, centerY + 6);
|
||||
}
|
||||
|
||||
drawPrice() {
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
this.ctx.font = '16px "Press Start 2P"'; // Agrandi
|
||||
this.ctx.fillText('$1', this.canvas.width / 2 - 15, 180); // Ajusté
|
||||
}
|
||||
|
||||
drawPourStream() {
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
const centerX = this.canvas.width / 2;
|
||||
const glassTop = 200;
|
||||
const liquidTop = glassTop + ((400 - glassTop) * (1 - this.currentLevel)); // Ajusté pour la nouvelle hauteur
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(centerX, 35);
|
||||
this.ctx.lineTo(centerX + 4, liquidTop);
|
||||
this.ctx.lineTo(centerX - 4, liquidTop);
|
||||
this.ctx.closePath();
|
||||
this.ctx.fill();
|
||||
}
|
||||
drawKegMeter() {
|
||||
const y = 480;
|
||||
const x = this.canvas.width / 2 - 100;
|
||||
const width = 200;
|
||||
const height = 30;
|
||||
|
||||
// Draw keg outline and fill
|
||||
this.ctx.strokeStyle = this.foregroundColor;
|
||||
this.ctx.lineWidth = 2;
|
||||
this.ctx.strokeRect(x, y, width, height);
|
||||
|
||||
const fillWidth = (this.currentKeg / this.kegCapacity) * width;
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
this.ctx.fillRect(x, y, fillWidth, height);
|
||||
|
||||
// Draw keg count above
|
||||
this.ctx.font = '12px "Press Start 2P"';
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
const countText = `${this.currentKeg}/${this.kegCapacity}`;
|
||||
const countWidth = this.ctx.measureText(countText).width;
|
||||
this.ctx.fillText(countText, x + (width/2) - (countWidth/2), y - 5);
|
||||
|
||||
// Add "KEG METER" text below
|
||||
const meterText = "KEG METER";
|
||||
const meterWidth = this.ctx.measureText(meterText).width;
|
||||
this.ctx.fillText(meterText, x + (width/2) - (meterWidth/2), y + height + 20);
|
||||
}
|
||||
|
||||
// Ajouter cette nouvelle méthode
|
||||
drawKegStock() {
|
||||
if (this.kegsInStock > 0) {
|
||||
const x = 500; // Garde l'alignement vertical avec le timer (centerX)
|
||||
const y = 480; // Même hauteur que la jauge de fût
|
||||
const kegWidth = 20;
|
||||
const kegHeight = 30;
|
||||
const lineSpacing = 8;
|
||||
|
||||
// Dessiner l'icône du fût (rectangle simple)
|
||||
this.ctx.strokeStyle = this.foregroundColor;
|
||||
this.ctx.lineWidth = 2;
|
||||
|
||||
// Rectangle principal
|
||||
this.ctx.strokeRect(x - kegWidth/2, y, kegWidth, kegHeight);
|
||||
|
||||
// Lignes horizontales intérieures
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(x - kegWidth/2, y + lineSpacing);
|
||||
this.ctx.lineTo(x + kegWidth/2, y + lineSpacing);
|
||||
this.ctx.moveTo(x - kegWidth/2, y + kegHeight - lineSpacing);
|
||||
this.ctx.lineTo(x + kegWidth/2, y + kegHeight - lineSpacing);
|
||||
this.ctx.stroke();
|
||||
|
||||
// Afficher le nombre
|
||||
this.ctx.font = '12px "Press Start 2P"';
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
this.ctx.fillText(`x${this.kegsInStock}`, x + 15, y + 20);
|
||||
}
|
||||
}
|
||||
|
||||
drawBeerPattern(x, y, width, height) {
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
const dotSize = 2;
|
||||
const spacing = 4;
|
||||
|
||||
for (let i = 0; i < width; i += spacing) {
|
||||
for (let j = 0; j < height; j += spacing) {
|
||||
this.ctx.fillRect(x + i, y + j, dotSize, dotSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawMoneyBag() {
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
this.ctx.font = '24px "Press Start 2P"'; // Agrandi
|
||||
this.ctx.fillText(`$${this.money}`, 50, 300); // Ajusté
|
||||
}
|
||||
|
||||
drawTimer() {
|
||||
const centerX = 500; // Ajusté
|
||||
const centerY = 300; // Ajusté
|
||||
const radius = 35; // Agrandi
|
||||
|
||||
this.ctx.strokeStyle = this.foregroundColor;
|
||||
this.ctx.lineWidth = 2;
|
||||
this.ctx.beginPath();
|
||||
this.ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
|
||||
this.ctx.stroke();
|
||||
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
this.ctx.font = '16px "Press Start 2P"';
|
||||
const timerText = Math.ceil(this.timer).toString();
|
||||
const textWidth = this.ctx.measureText(timerText).width;
|
||||
this.ctx.fillText(timerText, centerX - textWidth/2, centerY + 6);
|
||||
}
|
||||
|
||||
drawPrice() {
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
this.ctx.font = '16px "Press Start 2P"'; // Agrandi
|
||||
this.ctx.fillText('$1', this.canvas.width / 2 - 15, 180); // Ajusté
|
||||
}
|
||||
|
||||
drawPourStream() {
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
const centerX = this.canvas.width / 2;
|
||||
const glassTop = 200;
|
||||
const liquidTop = glassTop + ((400 - glassTop) * (1 - this.currentLevel)); // Ajusté pour la nouvelle hauteur
|
||||
|
||||
this.ctx.beginPath();
|
||||
this.ctx.moveTo(centerX, 35);
|
||||
this.ctx.lineTo(centerX + 4, liquidTop);
|
||||
this.ctx.lineTo(centerX - 4, liquidTop);
|
||||
this.ctx.closePath();
|
||||
this.ctx.fill();
|
||||
}
|
||||
drawKegMeter() {
|
||||
const y = 480;
|
||||
const x = this.canvas.width / 2 - 100;
|
||||
const width = 200;
|
||||
const height = 30;
|
||||
|
||||
// Draw keg outline and fill
|
||||
this.ctx.strokeStyle = this.foregroundColor;
|
||||
this.ctx.lineWidth = 2;
|
||||
this.ctx.strokeRect(x, y, width, height);
|
||||
|
||||
const fillWidth = (this.currentKeg / this.kegCapacity) * width;
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
this.ctx.fillRect(x, y, fillWidth, height);
|
||||
|
||||
// Draw keg count above
|
||||
this.ctx.font = '12px "Press Start 2P"';
|
||||
this.ctx.fillStyle = this.foregroundColor;
|
||||
const countText = `${this.currentKeg}/${this.kegCapacity}`;
|
||||
const countWidth = this.ctx.measureText(countText).width;
|
||||
this.ctx.fillText(countText, x + (width/2) - (countWidth/2), y - 5);
|
||||
|
||||
// Add "KEG METER" text below
|
||||
const meterText = "KEG METER";
|
||||
const meterWidth = this.ctx.measureText(meterText).width;
|
||||
this.ctx.fillText(meterText, x + (width/2) - (meterWidth/2), y + height + 20);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('load', () => {
|
||||
new Game();
|
||||
});
|
47
Sources/index.html
Normal file
47
Sources/index.html
Normal file
@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Perfect Pour</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="style.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="menuScreen">
|
||||
<canvas id="backgroundCanvas"></canvas>
|
||||
<div class="menu-content">
|
||||
<h1 class="game-title floating">PERFECT<br>POUR</h1>
|
||||
<p class="game-instructions">Fill the glass to<br>the dotted line</p>
|
||||
<canvas id="instructionsCanvas"></canvas>
|
||||
<button id="startButton" class="button">START GAME</button>
|
||||
<div style="height: 10px"></div>
|
||||
<button id="patchNotesButton" class="button">PATCH NOTES</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="gameScreen" class="hidden">
|
||||
<canvas id="gameCanvas"></canvas>
|
||||
</div>
|
||||
<div id="patchNotesOverlay" class="hidden">
|
||||
<div class="patch-notes-content">
|
||||
<h2>PATCH NOTES</h2>
|
||||
<div class="patch-notes-list">
|
||||
<h3>Version 1.0.2</h3>
|
||||
<ul>
|
||||
<li>🎮 Initial game release</li>
|
||||
<li>🍺 Basic beer pouring mechanics</li>
|
||||
<li>💰 Money and upgrade system</li>
|
||||
<li>🛢️ Keg management system</li>
|
||||
<li>⏱️ Timer and scoring system</li>
|
||||
</ul>
|
||||
</div>
|
||||
<button class="button" onclick="this.parentElement.parentElement.classList.add('hidden')">CLOSE</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="patchNotesScreen" class="hidden"></div>
|
||||
<script src="game.js"></script>
|
||||
<footer class="footer">
|
||||
© <a href="https://www.baptisteleforestier.com" target="_blank">Baptiste Leforestier</a> 2025 - Made with HTML,CSS et JS - V.1.0.2
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
223
Sources/style.css
Normal file
223
Sources/style.css
Normal file
@ -0,0 +1,223 @@
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: #1a1a1a;
|
||||
font-family: 'Press Start 2P', monospace;
|
||||
color: #e0e0e0;
|
||||
}
|
||||
|
||||
#menuScreen, #gameScreen {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.menu-content {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.game-title {
|
||||
font-size: clamp(2rem, 8vw, 4rem);
|
||||
line-height: 1.4;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.game-instructions {
|
||||
font-size: clamp(0.8rem, 3vw, 1.2rem);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#gameCanvas {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
#backgroundCanvas {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
#instructionsCanvas {
|
||||
margin: 1rem auto;
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.button {
|
||||
font-family: 'Press Start 2P', monospace;
|
||||
background-color: transparent;
|
||||
border: 2px solid #e0e0e0;
|
||||
color: #e0e0e0;
|
||||
padding: clamp(0.5rem, 2vw, 1rem) clamp(1rem, 4vw, 2rem);
|
||||
cursor: pointer;
|
||||
font-size: clamp(0.8rem, 2.5vw, 1rem);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #e0e0e0;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-align: center;
|
||||
padding: 1rem;
|
||||
font-size: clamp(0.4rem, 1.5vw, 0.6rem);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #e0e0e0;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.menu-content {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 80%;
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
.floating {
|
||||
animation: float 6s cubic-bezier(0.37, 0, 0.63, 1) infinite;
|
||||
filter: drop-shadow(0 0 15px rgba(224, 224, 224, 0.2));
|
||||
transform-origin: center;
|
||||
perspective: 1000px;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% {
|
||||
transform: translateY(0) rotateX(0deg) scale(1);
|
||||
filter: drop-shadow(0 0 15px rgba(224, 224, 224, 0.2));
|
||||
}
|
||||
25% {
|
||||
transform: translateY(-10px) rotateX(2deg) scale(1.02);
|
||||
filter: drop-shadow(0 0 20px rgba(224, 224, 224, 0.3));
|
||||
}
|
||||
50% {
|
||||
transform: translateY(0) rotateX(0deg) scale(1);
|
||||
filter: drop-shadow(0 0 15px rgba(224, 224, 224, 0.2));
|
||||
}
|
||||
75% {
|
||||
transform: translateY(5px) rotateX(-1deg) scale(0.98);
|
||||
filter: drop-shadow(0 0 10px rgba(224, 224, 224, 0.1));
|
||||
}
|
||||
100% {
|
||||
transform: translateY(0) rotateX(0deg) scale(1);
|
||||
filter: drop-shadow(0 0 15px rgba(224, 224, 224, 0.2));
|
||||
}
|
||||
}
|
||||
#patchNotesOverlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
#patchNotesOverlay.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.patch-notes-content {
|
||||
background-color: #1a1a1a;
|
||||
border: 2px solid #e0e0e0;
|
||||
padding: 20px;
|
||||
max-width: 600px;
|
||||
width: 90%;
|
||||
max-height: 80vh;
|
||||
overflow-y: auto;
|
||||
color: #e0e0e0;
|
||||
font-family: 'Press Start 2P', monospace;
|
||||
}
|
||||
|
||||
.patch-notes-content h2 {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.patch-notes-list {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.patch-notes-list ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.patch-notes-list li {
|
||||
margin: 10px 0;
|
||||
}
|
||||
|
||||
.patch-notes-content .button {
|
||||
display: block;
|
||||
margin: 20px auto 0;
|
||||
}
|
||||
.button {
|
||||
font-family: 'Press Start 2P', monospace;
|
||||
background-color: #1a1a1a;
|
||||
border: 2px solid #e0e0e0;
|
||||
color: #e0e0e0;
|
||||
padding: 10px 20px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
width: 250px;
|
||||
transition: all 0.3s ease;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background-color: #e0e0e0;
|
||||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user