Skip to content

Instantly share code, notes, and snippets.

@rebane2001
Created February 24, 2024 19:51
Show Gist options
  • Save rebane2001/5201f7cfa0b8ee45cf465df2f94429fe to your computer and use it in GitHub Desktop.
Save rebane2001/5201f7cfa0b8ee45cf465df2f94429fe to your computer and use it in GitHub Desktop.
Pikem versioon Maalehe mängust
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="https://r.muu.ee/39109/73149/index.hyperesources/homoteerull_1.png">
<meta content="Maalehe Mäng Lõputu" property="og:title" />
<meta content="Pikem versioon Maalehe mängust" property="og:description" />
<meta content="https://lyra.horse/misc/maalehe_mäng_lõputu.html" property="og:url" />
<meta content="https://r.muu.ee/39109/73149/index.hyperesources/homoteerull_1.png" property="og:image" />
<meta content="#47A95D" data-react-helmet="true" name="theme-color" />
<title>Maalehe Mäng Lõputu</title>
<style type="text/css">
html, body {
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
font-family: sans-serif;
}
a {
color: green;
}
#kaart {
width: 100%;
height: 100%;
user-select: none;
}
.pawn {
position: absolute;
max-width: 15%;
max-height: 15%;
width: auto;
height: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
opacity: 0;
transition: transform linear 6s, opacity linear 1s;
cursor: pointer;
user-select: none;
}
#dialog {
position: absolute;
z-index: 1;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
width: 300px;
height: fit-content;
text-align: center;
background: #EEEE;
border-radius: 40px;
padding: 32px;
font-weight: bold;
}
#header {
position: absolute;
z-index: 1;
top: 20px;
left: 0;
right: 0;
margin: 0 auto;
width: 500px;
height: fit-content;
color: #50A748;
text-align: center;
font-weight: bold;
transition: opacity linear 1s;
}
#header h1 {
font-size: 48px;
color: #377834;
line-height: 0;
}
#dialog button {
background: green;
font-size: 30px;
color: white;
border: none;
border-radius: 10px;
cursor: pointer;
}
#dialog p {
margin-top: 0;
}
</style>
</head>
<body>
<div id="dialog">
<p id="dialogText">Kliki erinevatel elementidel, et need oma koduõuelt eemal hoida. Iga kaitsmine annab sekundi aega juurde, aga iga element mis koduõuele jõuab võtab 30 sekundit aega vähemaks.<br>(<a href="https://maaleht.delfi.ee/artikkel/92649377/suur-vabariigi-aastapaeva-mang-mitte-minu-aia-taha">originaalne versioon</a>)</p>
<button id="dialogBtn" onclick="start()">Alusta</button>
</div>
<div id="header">
<h2>Maalehe mäng (lõputu versioon)</h2>
<h1>Mitte minu aia taha!</h1>
</div>
<canvas id="kaart"></canvas>
<script type="text/javascript">
const assetsBaseURI = "https://r.muu.ee/39109/73149/index.hyperesources";
const assets = {
"5g": "5g_1.png",
"arst": "arst_1.png",
"eesti_kaart": "eesti_kaart_1.png",
"harvester": "harvester_1.png",
"homoteerull": "homoteerull_1.png",
"hooldekodu": "hooldekodu_1.png",
"joala": "joala_1.png",
"joala_nope": "joala_nope_1.png",
"kopp": "kopp_1.png",
"korsten": "korsten_1.png",
"maja": "maja_1.png",
"tiivikud": "tiivikud_1.png",
"ukrainlane": "ukrainlane_1.png",
"rong": "rong_1.png",
"lipp_1": "lipp_1.png",
"lipp_2": "lipp_2.png",
"nope_1": "nope_1.png",
"nope_2": "nope_2_2x.png",
"tutt_1": "tutt_1.png",
"tutt_2": "tutt_2.png",
"tutt_3": "tutt_3.png",
};
function getAsset(assetName) {
return `${assetsBaseURI}/${assets[assetName]}`;
}
const audioContext = new AudioContext();
function beep(t, f, l, v) {
const vol = audioContext.createGain();
const oscillator = audioContext.createOscillator();
oscillator.type = t;
oscillator.frequency.value = f;
oscillator.connect(vol);
vol.connect(audioContext.destination);
vol.gain.value = v;
oscillator.start();
setTimeout(() => oscillator.stop(), l);
}
const kaart = document.getElementById("kaart");
function createImg(assetName, classes) {
const img = document.createElement("img");
img.src = getAsset(assetName);
classes.forEach(c => img.classList.add(c));
return img;
}
const loadedAssets = Object.keys(assets).reduce((o, key) => {
const img = new Image();
img.src = getAsset(key);
return { ...o, [key]: img };
}, {});
function drawImgC(ctx, img, x, y, w) {
const aspect = img.height / img.width;
const h = w*aspect;
ctx.drawImage(img, x - (w/2), y - (h/2), w, h);
}
function isGameRunning() {
return Date.now() < gameEnd && !gameFinished;
}
function takeDamage(pawn) {
if (!isGameRunning()) return;
if (Date.now() - gameStart < 5000) return;
if (pawn.src.includes("homoteerull")) {
if (homo > 2) {
setTimeout(()=>beep("sawtooth", 52, 200, 0.4),16);
}
homo = 0;
}
if (homo > 1 && !pawn.src.includes("homoteerull")) return;
beep("sawtooth", 112, 100, 0.4);
gameEnd -= 30*1000;
}
function pawnDestroyed(pawn) {
if (isGameRunning()) {
score += 1;
gameEnd += (homo > 10 ? 10 : 1)*1000;
if (pawn.src.includes("homoteerull")) {
homo += 1;
} else {
homo = 0;
}
if (homo > 1) {
score += 9;
beep("sine", 295 + Math.random()*25, 150, 0.3);
} else {
beep("square", 100 + Math.random()*100, 50, 0.2);
}
pawn.src = getAsset("nope_2");
}
pawn.style.transform = getComputedStyle(pawn).transform;
pawn.style.opacity = "0";
pawn.style.pointerEvents = "none";
setTimeout(() => {
pawn.remove();
}, 1000);
}
const possiblePawns = [
"5g",
"arst",
"harvester",
"homoteerull",
"hooldekodu",
"joala",
"kopp",
"korsten",
"tiivikud",
"ukrainlane",
"rong",
];
function spawnPawn() {
const pawn = document.createElement("img");
pawn.classList.add("pawn");
pawn.src = getAsset(possiblePawns[Math.floor(Math.random()*possiblePawns.length)]);
pawn.draggable = false;
const offsetA = `${100*Math.random() - 50}`;
const offsetB = `${Math.random() > 0.5 ? 50 : -50}`;
pawn.style.transform = Math.random() > 0.5 ? `translate(${offsetA}vw, ${offsetB}vh)` : `translate(${offsetB}vw, ${offsetA}vh)`;
let pawnExists = true;
pawn.onmousedown = () => {
if (isGameRunning() && pawnExists) {
pawnExists = false;
pawnDestroyed(pawn);
}
}
pawn.ontouchstart = () => {
if (isGameRunning() && pawnExists) {
pawnExists = false;
pawnDestroyed(pawn);
}
}
setTimeout(() => {
if (!pawnExists) return;
takeDamage(pawn);
pawn.remove();
}, 5000);
document.body.appendChild(pawn);
setTimeout(() => {
pawn.style.transform = "translate(0, 0)";
pawn.style.opacity = "1";
}, 32);
}
let gameStart = 0;
let gameEnd = 0;
let score = 0;
let homo = 0;
let lastPawnSpawn = 0;
let gameFinished = true;
function start() {
gameStart = Date.now();
gameEnd = Date.now() + 60*1000;
score = 0;
homo = 0;
gameFinished = false;
document.getElementById("dialog").style.display = "none";
document.getElementById("header").style.opacity = 0;
setTimeout(() => {
document.getElementById("header").style.display = "none";
}, 1000);
gameLoop();
}
function end() {
gameFinished = true;
beep("square", 550, 100, 0.3);
document.getElementById("dialogText").innerHTML = `Said koduõue vapra kaitsmise eest<br><span style="font-size: 40px; color: green">${score}</span><br>punkti!`;
document.getElementById("dialogBtn").innerText = "Proovi uuesti!";
document.getElementById("dialog").style.display = "block";
document.querySelectorAll(".pawn").forEach(pawn => pawnDestroyed(pawn));
}
function gameLoop() {
if (!isGameRunning()) {
if (!gameFinished) end();
return;
}
if (homo >= 50) {
gameFinished = true;
end();
document.getElementById("dialogText").innerHTML = `Saavutasid Eestis abieluvõrdsuse, palju õnne!`;
return;
}
if (Date.now() > lastPawnSpawn + 500 - (1 - Math.pow(Math.max(0, 1 - score/1000), 1.8))*270) {
spawnPawn();
lastPawnSpawn = Date.now();
}
}
function homoLipp(ctx) {
if (homo < 3) return;
ctx.globalAlpha = ((homo-2)/100);
["#E40303", "#FF8C00", "#FFED00", "#008026", "#24408E", "#732982"].forEach((c,i) => {
ctx.fillStyle = c;
ctx.fillRect(0, window.innerHeight/6*i, window.innerWidth, window.innerHeight/6);
});
ctx.fillStyle = "black";
ctx.globalAlpha = 1;
}
function draw() {
const ctx = kaart.getContext("2d");
ctx.canvas.width = window.innerWidth;
ctx.canvas.height = window.innerHeight;
const aspect = window.innerWidth / window.innerHeight;
const desiredAspect = 20 / 12;
const actualWidth = aspect > desiredAspect ? window.innerHeight*desiredAspect : window.innerWidth;
const actualHeight = aspect < desiredAspect ? window.innerWidth/desiredAspect : window.innerHeight;
const offsetX = (window.innerWidth - actualWidth) / 2;
const offsetY = (window.innerHeight - actualHeight) / 2;
homoLipp(ctx);
ctx.drawImage(loadedAssets["eesti_kaart"], offsetX, offsetY, actualWidth, actualHeight);
homoLipp(ctx);
const majaPos = [offsetX + actualWidth/2, offsetY + actualHeight/2];
drawImgC(ctx, loadedAssets["maja"], ...majaPos, actualWidth*0.32);
const lipp_o = Math.sin(Date.now()/400);
ctx.globalAlpha = Math.min(1, Math.max(0, (lipp_o + 0.25)*2));
drawImgC(ctx, loadedAssets[`lipp_1`], ...majaPos.map((e, i) => e-[actualWidth*0.0282, actualWidth*0.072][i]), actualWidth*0.32/8.89);
ctx.globalAlpha = Math.min(1, Math.max(0, (-lipp_o + 0.25)*2));
drawImgC(ctx, loadedAssets[`lipp_2`], ...majaPos.map((e, i) => e-[actualWidth*0.027, actualWidth*0.072][i]), actualWidth*0.32/8.89);
for (const tutt of [1,2,3]) {
const prog = ((Date.now()/1500) + tutt*0.2) % 1;
ctx.globalAlpha = 1 - Math.max(0, Math.abs(prog - 0.5) - 0.25)*4;
drawImgC(ctx, loadedAssets[`tutt_${tutt}`], ...majaPos.map((e, i) => e-[-actualWidth*0.026 - prog*actualWidth*0.01, actualWidth*0.058 + prog*actualWidth*0.06][i]), actualWidth*0.32/12.35);
}
ctx.globalAlpha = 1;
ctx.font = "48px sans-serif";
ctx.fillText(`Aeg: ${Math.max(0,Math.floor((gameEnd - Date.now())/1000))}`, 10, 50);
ctx.fillText(`Skoor: ${score}`, 10, 100);
gameLoop();
requestAnimationFrame(draw);
}
document.body.onload = () => {
draw();
};
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment