Skip to content

Instantly share code, notes, and snippets.

@micmmakarov
Created August 5, 2020 22:48
Show Gist options
  • Save micmmakarov/ed915724459ac0ffb7550ca5857ccb65 to your computer and use it in GitHub Desktop.
Save micmmakarov/ed915724459ac0ffb7550ca5857ccb65 to your computer and use it in GitHub Desktop.
Comedy game with flying
// Declaration of constants
levels = [
{
title: "1: You make your coworkers laugh",
score: 0,
max: 30,
emoji: {
bomb: 9,
drink: 30,
nobombing: 1000
}
},
{
title: "2: You can do an open mic in Ohio",
score: 20,
max: 40,
emoji: {
bomb: 9,
drink: 30,
nobombing: 2000
}
},
{
title: "3: An open mic with real audience",
score: 80,
max: 50,
emoji: {
bomb: 8,
drink: 30,
nobombing: 100
}
},
{
title: "4: Killed it at an amature show in a bar",
score: 160,
emoji: {
bomb: 7,
drink: 30,
nobombing: 700
}
},
{
title: "5: A set in The Setup San Francisco",
score: 260,
emoji: {
bomb: 6,
drink: 25,
nobombing: 1000
}
},
{
title: "6: Destroyed it at the Punchline SF",
score: 380,
emoji: {
bomb: 5,
drink: 40,
nobombing: 1000
}
},
{
title: "7: Murdered again at the Punchline SF",
score: 420,
emoji: {
bomb: 4,
drink: 30,
nobombing: 100
}
},
{
title: "8: Sleep at the Comedy Store LA",
score: 600,
emoji: {
bomb: 3,
drink: 30,
nobombing: 2000
}
},
{
title: "9: Comedy Cellar NYC might call you",
score: 800,
emoji: {
bomb: 2,
drink: 35,
nobombing: 100
}
},
{
name: "10: Scheduled a Netflix Special",
score: 1200,
emoji: {
bomb: 1,
drink: 35,
nobombing: 700
}
},
{
name: "11: You're the comedy god'",
score: 1500,
emoji: {
bomb: 0,
drink: 30,
nobombing: 400
}
}
]
const laughFiles = [
'laugh1.m4a',
'laugh2.m4a',
'laugh3.mp3',
'laugh4.mp3',
]
const booSound = new Audio('/static/sounds/boo.m4a');
const drinkSound = new Audio('/static/sounds/drink.mp3');
drinkSound.volume = 0.5;
const laugh = () => {
const laughFile = laughFiles[rand(laughFiles.length)];
const audio = new Audio('/static/sounds/' + laughFile);
audio.volume = 0.1 * (rand(4) + 1);
audio.play();
}
const emoji = {
bomb: "💣",
drink: "🍺",
nobombing: "🍹",
laugh: "😂",
rolling: "🤣"
}
// Class of the single emoji
class Emoji {
constructor(x, y, house, type = null) {
house.total++;
this.x = x;
this.y_ = y;
this.createdTime = new Date();
this.lastTime = new Date();
this.size_ = 20 + rand(80);
this.speed = rand(30)
this.rotate_ = 0;
this.house = house;
this.score = 1;
if (!type) {
if (this.condition(0, 10)) type = 'rolling';
if (this.condition(100, levels[this.house.levelIndex].emoji.nobombing, !this.house.noBombing)) type = 'nobombing';
if (this.condition(50, levels[this.house.levelIndex].emoji.drink, !this.house.noBombing)) type = 'drink';
if (this.condition(20, levels[this.house.levelIndex].emoji.bomb, !this.house.noBombing)) type = 'bomb';
}
if (!type) type = 'laugh';
this.type_ = type;
this.emoji = emoji[type];
}
condition(min, chance, condition) {
if (this.house.total > min) {
if (rand(chance) === 0 && condition) {
return true;
}
}
return false;
}
get type() {
return {
[this.type_]: true
}
}
get fontSize() {
if (this.exploded_time_) {
const mil = new Date - this.exploded_time_;
if (this.disappear_) {
const unit = (mil / 100) < 1 ? 1 : mil / 100
const fontSize = Math.round((this.size_ / (unit)));
return fontSize + 'px serif';
} else {
return this.size_ + Math.round(mil / this.speed * 10) + 'px serif';
}
}
return this.size_ + 'px serif';
}
get y() {
const mil = new Date - this.lastTime;
if (!this.house.gameOver) this.y_ = this.exploded_time_ ? this.exploded_y_ : (this.y_ - Math.round(mil / this.speed));
this.lastTime = new Date;
return this.y_;
//return this.y_ - Math.round(mil / this.speed);
}
get alpha() {
if (this.type.bomb && !this.disappear_) {
return 1
}
if (this.exploded_time_) {
const mil = new Date - this.exploded_time_;
const a = 1 - mil / 1000;
return a < 0 ? 0 : a;
}
return 1;
}
render(ctx) {
const { x, y } = this;
ctx.save();
ctx.globalAlpha = this.alpha;
ctx.textBaseline = 'middle';
ctx.textAlign = "center";
ctx.font = this.fontSize;
ctx.fillText((this.house.gameOver && !this.type.bomb) ? "👿" : this.emoji, this.x, this.y);
ctx.restore();
if (y < -this.size_) {
this.destroy();
this.house.missed++;
}
}
disappear() {
this.exploded_y_ = this.y;
this.exploded_time_ = new Date();
this.disappear_ = true;
setTimeout(() => this.destroy(), 2000);
}
explode() {
if (this.type.bomb) {
this.house.gameOver = true;
booSound.play();
}
this.exploded_y_ = this.y;
this.exploded_time_ = new Date();
if (this.type.bomb) {
setTimeout(() => {
this.house.updateScore();
this.destroy()
this.house.gameOver = false;
}, 3000);
this.house.score -= 10;
this.house.loseLife();
return;
}
if (this.type.drink) {
this.house.setMode('drink');
drinkSound.play();
setTimeout(() => this.destroy(), 1000);
return;
}
if (this.type.nobombing) {
this.house.setMode('nobombing');
setTimeout(() => this.destroy(), 1000);
return;
}
this.emoji = "💥";
laugh();
setTimeout(() => this.destroy(), 1000);
this.house.score += this.score;
}
checkCollision(mx, my) {
if (this.exploded_time_) return;
const { x, y } = this;
const half = this.size_ / 2 + 20;
if ((mx > x - half) && (mx < x + half) && (my > y - half) && (my < y + half)) {
this.explode();
}
}
destroy() {
const index = this.house.emojiList.indexOf(this);
this.house.emojiList.splice(index, 1);
}
}
// Class of the game
class Game {
constructor(elPath, scorePath, embedded = false) {
window.game = this;
this.embedded = embedded;
this.canvas = document.querySelector(elPath);
this.scoreboard = document.querySelector(scorePath);
}
reset() {
this.gameOver = false;
this.endGame = false;
}
end() {
this.gameContainer.classList.remove('game-show');
}
start() {
this.gameContainer = document.querySelector(".game-container");
this.gameContainer.classList.add('game-show');
const game = document.querySelector("#game");
game.width = window.innerWidth;
game.height = window.innerHeight;
this.gameOver = false;
this.total = 0;
this.score_ = 0;
this.missed_ = 0;
this.scoreList = [];
this.interval = 2000;
this.levelIndex = 0;
this.totalLifes = 4;
this.lifes_ = this.totalLifes;
this.gameOverElement = document.querySelector("#gameover");
this.currentLevelEl = document.querySelector(".your-level");
this.mx = 0;
this.my = 0;
const section = this.embedded ? this.canvas.parentElement : this.canvas;
this.ctx = this.canvas.getContext('2d');
this.canvas.setAttribute('width', this.canvas.clientWidth);
this.canvas.setAttribute('height', this.canvas.clientHeight);
this.emojiList = [];
this.objectList = [];
this.isTouchDevice = isTouchDevice();
if (this.isTouchDevice) {
setTimeout(() => {
const rect = section.getBoundingClientRect()
section.addEventListener('touchstart', (e) => {
this.touchStarted = true;
this.mx = e.touches[0].clientX;
this.my = e.touches[0].clientY;
this.emojiList.forEach(e => e.checkCollision(this.mx, this.my));
})
section.addEventListener('touchmove', (e) => {
this.mx = e.touches[0].clientX;
this.my = e.touches[0].clientY;
this.emojiList.forEach(e => e.checkCollision(this.mx, this.my));
})
section.addEventListener('touchend', (e) => {
this.touchStarted = false;
this.mx = -1000;
this.my = -1000;
})
section.addEventListener('touchcancel', (e) => {
this.touchStarted = false;
this.mx = -1000;
this.my = -1000;
})
}, 1000);
} else {
section.addEventListener('mousemove', (e) => {
this.mx = e.clientX;
this.my = e.clientY;
})
}
this.level = levels[this.levelIndex].title;
this.createdTime = new Date();
requestAnimationFrame(this.draw.bind(this));
this.speed = 1000;
this.tick();
this.updateScore();
}
get speed() {
return this.speed_;
}
set speed(value) {
this.speed_ = value;
}
setMode(mode) {
switch (mode) {
case 'nobombing':
if(this.noBombing) clearTimeout(this.noBombing);
this.noBombing = setTimeout(() => {
this.noBombing = null;
}, 10000);
this.emojiList.forEach(e => {
if (e.type.bomb) e.disappear();
});
this.sendMessage("🚫💣");
break;
case 'drink':
this.emojiList.forEach(e => {
if (e.type.bomb) e.disappear();
});
break;
default:
}
}
sendMessage(message) {
}
get livesLeft() {
if (this.lifes_ < 0 || this.totalLifes - this.lifes_ < 0) return;
const life = "❤️"
const bomb = "🖤"
return life.repeat(this.lifes_) + bomb.repeat(this.totalLifes - this.lifes_);
}
announceGameOver() {
this.gameOverElement.classList.add('gameover-show')
}
loseLife() {
this.lifes_--;
if (this.lifes_ === 0) {
// Game over;
this.gameOver = true;
this.endGame = true;
this.announceGameOver();
}
}
tick() {
if (this.timeout) clearTimeout(this.timeout);
this.getScoreLastSeconds(5);
const interval = this.noBombing ? 50 : this.scoreLastSeconds > 3 ? this.speed / (this.scoreLastSeconds / 3) : this.speed;
if (this.speed !== 0) {
this.timeout = setTimeout(() => {
if (!this.gameOver) this.addEmoji();
this.tick();
}, interval / 2 + rand(interval))
}
}
addEmoji() {
if (this.emojiList.length > (levels[this.levelIndex].max || 50)) return;
if (this.gameOver) return;
if (this.endGame) return;
const e = new Emoji(Math.floor(Math.random() * this.canvas.width), this.canvas.height, this)
this.emojiList.push(e)
}
get score() {
return this.score_;
}
set score(v) {
if (levels[this.levelIndex + 1].score <= v) {
this.currentLevelEl.classList.remove("blink");
setTimeout(() => this.currentLevelEl.classList.add("blink"), 50);
this.levelIndex++;
this.level = levels[this.levelIndex].title;
}
this.scoreList.push({
date: Date.now(),
value: v - this.score_
})
this.score_ = v;
this.updateScore();
}
get missed() {
return this.missed_;
}
getScoreLastSeconds(seconds) {
const cut = Date.now() - seconds * 1000;
let nobread = true;
for (let i = 0; i < this.scoreList.length; i++) {
if (this.scoreList[i].date > cut) {
nobread = false;
if (i > 0) this.scoreList.splice(0, i - 1);
break;
}
}
if (nobread) this.scoreList = [];
this.scoreLastSeconds = this.scoreList.length
return this.scoreLastSeconds;
}
set missed(v) {
this.missed_ = v;
this.updateScore();
}
updateScore() {
if (!this.scoreboard) return;
if (!this.scoreboardElements) {
this.scoreboardElements = {};
['score', 'missed', 'livesLeft', 'level'].forEach(param => {
this.scoreboardElements[param] = document.querySelector(`#score-${param}`)
});
}
['score', 'missed', 'livesLeft', 'level'].forEach(param => {
this.scoreboardElements[param].innerText = this[param];
});
//this.scoreboardElements.level.className = 'level-' + this.levelIndex;
}
// Main game loop
draw() {
const time = Date.now();
if (!this.lastTime) this.lastTime = time;
if ((this.lastTime + this.interval) < time) {
this.lastTime = time;
}
if (!this.isTouchDevice) this.emojiList.forEach(e => e.checkCollision(this.mx, this.my));
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this.emojiList.forEach(e => e.render(this.ctx));
this.objectList.forEach(e => e.render(this.ctx));
if (!this.isTouchDevice || this.touchStarted) {
this.ctx.textBaseline = 'middle';
this.ctx.textAlign = "center";
this.ctx.font = '70px serif';;
this.ctx.fillText(" 🎤", this.mx, this.my);
}
requestAnimationFrame(this.draw.bind(this));
}
}
// Game init
const theGame = new Game('#game', "#score");
theGame.start();
// Helper functions
function rand(i) {
return Math.floor(Math.random() * i);
}
function isTouchDevice() {
try {
document.createEvent("TouchEvent");
return true;
} catch (e) {
return false;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment