Escape Room Builder - generator de jocuri escape room in 5 stiluri
Builder single-file HTML cu editor + preview live jucabil si export de jocuri standalone. Stiluri: clasic (quiz), terminal retro, arcade pixel, story chat, point-and-click. Fara backend, fara build. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
31
README.md
Normal file
31
README.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Escape Room Builder
|
||||||
|
|
||||||
|
Generator de jocuri escape room intr-un singur fisier HTML, fara backend, fara build. Acelasi set de puzzle-uri poate fi exportat in 5 stiluri de joc diferite.
|
||||||
|
|
||||||
|
## Folosire
|
||||||
|
|
||||||
|
Deschide `escape-builder.html` in browser (dublu-click, merge si de pe `file://`).
|
||||||
|
|
||||||
|
- **Stanga**: editor — titlu, poveste, culoare, **stil joc**, puzzle-uri (raspuns liber / adevarat-fals / variante), indiciu si litera per puzzle.
|
||||||
|
- **Dreapta**: preview live — jocul exact cum va arata, jucabil direct in pagina.
|
||||||
|
- **Exporta jocul HTML**: descarca un joc standalone pe care il trimiti pe telefon/email; merge offline.
|
||||||
|
- **Salveaza / Incarca JSON**: pastreaza proiectul ca fisier ca sa-l reiei mai tarziu.
|
||||||
|
|
||||||
|
Proiectul curent se salveaza automat in `localStorage` la fiecare modificare.
|
||||||
|
|
||||||
|
## Stiluri de joc
|
||||||
|
|
||||||
|
| Stil | Mecanica | Exemplu |
|
||||||
|
|------|----------|---------|
|
||||||
|
| Clasic (quiz) | Carduri secventiale cu progres si litere | `exemplu-clasic.html` |
|
||||||
|
| Terminal retro | Text adventure pe ecran CRT verde; scrii comenzi (INDICIU, LITERE) si raspunsuri | `exemplu-terminal.html` |
|
||||||
|
| Arcade pixel | Te misti cu sagetile/WASD prin camere; usile incuiate pun intrebari, cufarul final e scaparea | `exemplu-arcade.html` |
|
||||||
|
| Story chat | Un personaj blocat iti scrie mesaje (typing...); il ajuti raspunzand din composer | `exemplu-chat.html` |
|
||||||
|
| Point-and-click | Camera ilustrata SVG; dai click pe obiecte (ceas, tablou, seif...), le rezolvi si deschizi usa | `exemplu-point.html` |
|
||||||
|
|
||||||
|
## Mecanici comune
|
||||||
|
|
||||||
|
- Stele: 3 la prima incercare, 2 la a doua, 1 daca folosesti indiciul sau gresesti de mai multe ori.
|
||||||
|
- Fiecare puzzle poate da o litera; literele formeaza cuvantul final, dezvaluit la castig (cu confetti, in functie de stil).
|
||||||
|
- Sunete WebAudio la corect/gresit; raspunsurile se compara fara diacritice si fara majuscule.
|
||||||
|
- Toate motoarele de joc impart aceeasi biblioteca (config, scor, verificare raspuns, modal, ecran final) generata din builder.
|
||||||
1311
escape-builder.html
Normal file
1311
escape-builder.html
Normal file
File diff suppressed because it is too large
Load Diff
238
exemplu-arcade.html
Normal file
238
exemplu-arcade.html
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ro">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Comoara ascunsa</title>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; min-height: 100vh; background: #0d0820; color: #fff; font-family: ui-monospace, "Courier New", monospace; display: flex; flex-direction: column; align-items: center; }
|
||||||
|
h1 { font-size: 17px; margin: 14px 0 6px; letter-spacing: .06em; text-transform: uppercase; }
|
||||||
|
#hud { display: flex; gap: 16px; align-items: center; font-size: 13px; color: #b9aee0; margin-bottom: 8px; flex-wrap: wrap; justify-content: center; padding: 0 10px; }
|
||||||
|
#hudLetters { display: flex; gap: 4px; }
|
||||||
|
#hudLetters span { width: 22px; height: 26px; border-radius: 5px; background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.2); display: flex; align-items: center; justify-content: center; font-weight: 700; color: rgba(255,255,255,.4); font-size: 13px; }
|
||||||
|
#hudLetters span.won { background: var(--accent); color: #fff; border-color: transparent; }
|
||||||
|
canvas { border: 3px solid #36246b; border-radius: 8px; background: #18102e; max-width: calc(100vw - 16px); }
|
||||||
|
.help { font-size: 12px; color: #6f639e; margin: 8px 0 4px; text-align: center; padding: 0 10px; }
|
||||||
|
#dpad { display: flex; gap: 8px; margin: 6px 0 16px; }
|
||||||
|
#dpad button { width: 52px; height: 44px; font-size: 18px; border-radius: 9px; border: 1px solid #4a3590; background: #221643; color: #cdc3f0; cursor: pointer; }
|
||||||
|
#dpad button:active { background: var(--accent); }
|
||||||
|
|
||||||
|
.confetti { position: fixed; top: -12px; width: 9px; height: 14px; z-index: 99; animation: fall linear forwards; }
|
||||||
|
@keyframes fall { to { transform: translateY(105vh) rotate(720deg); } }
|
||||||
|
.shake { animation: shake .4s ease; }
|
||||||
|
@keyframes shake { 20%,60% { transform: translateX(-8px); } 40%,80% { transform: translateX(8px); } }
|
||||||
|
#mOverlay { display: none; position: fixed; inset: 0; background: rgba(8,4,20,.72); z-index: 20; align-items: center; justify-content: center; padding: 16px; }
|
||||||
|
#mCard { width: 100%; max-width: 460px; background: #221440; border: 1px solid rgba(255,255,255,.18); border-radius: 16px; padding: 22px; color: #fff; font-family: system-ui, sans-serif; box-shadow: 0 18px 50px rgba(0,0,0,.5); }
|
||||||
|
#mCard .mtitle { font-size: 12px; text-transform: uppercase; letter-spacing: .08em; color: #c4b5fd; font-weight: 700; }
|
||||||
|
#mCard .mq { font-size: 18px; line-height: 1.45; margin: 8px 0 16px; }
|
||||||
|
#mCard input[type=text] { width: 100%; font: inherit; font-size: 17px; padding: 10px 12px; border-radius: 10px; border: 1px solid rgba(255,255,255,.25); background: rgba(0,0,0,.3); color: #fff; text-align: center; box-sizing: border-box; }
|
||||||
|
#mCard input:focus { outline: 2px solid var(--accent); border-color: transparent; }
|
||||||
|
#mCard button { font: inherit; cursor: pointer; border: none; border-radius: 10px; padding: 11px 16px; font-weight: 700; background: var(--accent); color: #fff; width: 100%; margin-top: 10px; box-sizing: border-box; }
|
||||||
|
#mCard button.opt { background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.2); font-weight: 600; text-align: left; }
|
||||||
|
#mCard button.opt:hover { background: rgba(255,255,255,.2); }
|
||||||
|
#mCard .mfb { min-height: 20px; text-align: center; font-weight: 700; margin-top: 10px; }
|
||||||
|
#mCard .mfb.bad { color: #fda4af; } #mCard .mfb.good { color: #86efac; }
|
||||||
|
#mCard .mhint { background: none !important; color: rgba(255,255,255,.55) !important; font-weight: 600 !important; font-size: 13px; width: auto !important; display: block; margin: 10px auto 0; }
|
||||||
|
#mCard .mhinttext { background: rgba(255,255,255,.1); border-radius: 9px; padding: 9px 11px; font-size: 14px; margin-top: 8px; display: none; white-space: pre-line; }
|
||||||
|
#mCard .mclose { background: none !important; color: rgba(255,255,255,.4) !important; font-size: 12px; width: auto !important; margin: 6px auto 0; display: block; }
|
||||||
|
#fOverlay { display: none; position: fixed; inset: 0; background: rgba(8,4,20,.88); z-index: 30; align-items: center; justify-content: center; padding: 16px; }
|
||||||
|
#fOverlay .fcard { width: 100%; max-width: 480px; text-align: center; background: #221440; border: 1px solid rgba(255,255,255,.18); border-radius: 18px; padding: 28px; color: #fff; font-family: system-ui, sans-serif; }
|
||||||
|
#fOverlay h1 { margin: 0 0 8px; font-size: 26px; }
|
||||||
|
#fOverlay .fstars { font-size: 26px; letter-spacing: 4px; color: #fbbf24; margin: 6px 0; }
|
||||||
|
#fOverlay .fword { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin: 16px 0; }
|
||||||
|
#fOverlay .fword span { width: 44px; height: 52px; border-radius: 10px; background: var(--accent); display: flex; align-items: center; justify-content: center; font-size: 26px; font-weight: 800; animation: flipin .6s ease backwards; }
|
||||||
|
@keyframes flipin { from { transform: rotateX(90deg); } to { transform: rotateX(0); } }
|
||||||
|
#fOverlay p { color: rgba(255,255,255,.8); line-height: 1.5; }
|
||||||
|
#fOverlay button { font: inherit; cursor: pointer; border: none; border-radius: 10px; padding: 12px 18px; font-weight: 700; background: var(--accent); color: #fff; width: 100%; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Comoara ascunsa</h1>
|
||||||
|
<div id="hud"><span id="hudStep"></span><span id="hudStars"></span><div id="hudLetters"></div></div>
|
||||||
|
<canvas id="cv"></canvas>
|
||||||
|
<div class="help">Sageti / WASD (da click pe joc intai). Usile rosii iti pun intrebari; cufarul auriu e scaparea.</div>
|
||||||
|
<div id="dpad"><button data-d="L">◀</button><button data-d="U">▲</button><button data-d="D">▼</button><button data-d="R">▶</button></div>
|
||||||
|
<div id="mOverlay"><div id="mCard">
|
||||||
|
<div class="mtitle" id="mTitle"></div>
|
||||||
|
<div class="mq" id="mQ"></div>
|
||||||
|
<div id="mAnswers"></div>
|
||||||
|
<div class="mfb" id="mFeedback"></div>
|
||||||
|
<button class="mhint" id="mHintBtn">Vreau un indiciu</button>
|
||||||
|
<div class="mhinttext" id="mHintText"></div>
|
||||||
|
<button class="mclose" id="mClose">Pleaca de aici</button>
|
||||||
|
</div></div>
|
||||||
|
<div id="fOverlay"><div class="fcard">
|
||||||
|
<h1>Evadare reusita!</h1>
|
||||||
|
<div class="fstars" id="fStars"></div>
|
||||||
|
<div class="fword" id="fWord"></div>
|
||||||
|
<p id="fMsg"></p>
|
||||||
|
<button id="fAgain">Joaca din nou</button>
|
||||||
|
</div></div>
|
||||||
|
<script>
|
||||||
|
var CFG = {"title":"Comoara ascunsa","player":"","color":"#6d28d9","style":"arcade","charName":"Alex","story":"O comoara a fost ascunsa, iar singurul drum spre ea trece prin cateva incercari. Rezolva fiecare puzzle ca sa aduni literele cuvantului magic.","finalMessage":"Felicitari! Ai gasit comoara!","puzzles":[{"title":"Incalzirea","type":"free","question":"Cat fac 7 x 8?","answer":"56","tfAnswer":"Adevarat","choices":"","hint":"Tabla inmultirii cu 7.","letter":"D"},{"title":"Adevarat sau fals","type":"tf","question":"Romania are iesire la Marea Neagra.","answer":"","tfAnswer":"Adevarat","choices":"","hint":"","letter":"A"},{"title":"Alege raspunsul","type":"choice","question":"Care este capitala Frantei?","answer":"","tfAnswer":"Adevarat","choices":"*Paris\nLyon\nMarsilia","hint":"Turnul Eiffel.","letter":"R"}]};
|
||||||
|
document.documentElement.style.setProperty('--accent', CFG.color || '#6d28d9');
|
||||||
|
var totalStars = 0;
|
||||||
|
function el(id){ return document.getElementById(id); }
|
||||||
|
function norm(s){ return String(s).trim().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/\s+/g, ' ').replace(/,/g, '.'); }
|
||||||
|
function starsFor(att, hint){ return (hint || att >= 2) ? 1 : (att === 1 ? 2 : 3); }
|
||||||
|
function finalWord(){ var w = ''; for (var i = 0; i < CFG.puzzles.length; i++){ var L = (CFG.puzzles[i].letter || '').trim(); if (L) w += L.toUpperCase(); } return w; }
|
||||||
|
function choiceOpts(p){ return (p.choices || '').split('\n').map(function(l){ return l.trim(); }).filter(Boolean).map(function(o){ return o.charAt(0) === '*' ? o.slice(1).trim() : o; }); }
|
||||||
|
function choiceCorrect(p){ var ls = (p.choices || '').split('\n'); for (var i = 0; i < ls.length; i++){ var l = ls[i].trim(); if (l.charAt(0) === '*') return l.slice(1).trim(); } return ''; }
|
||||||
|
function checkAnswer(p, given){ var exp = p.type === 'tf' ? p.tfAnswer : (p.type === 'choice' ? choiceCorrect(p) : p.answer); return norm(given) !== '' && norm(given) === norm(exp); }
|
||||||
|
function beep(ok){ try { var ctx = beep.ctx || (beep.ctx = new (window.AudioContext || window.webkitAudioContext)()); var t = ctx.currentTime; var fs = ok ? [523, 784] : [196]; fs.forEach(function(f, k){ var o = ctx.createOscillator(), g = ctx.createGain(); o.frequency.value = f; o.type = 'triangle'; g.gain.setValueAtTime(0.12, t + k * 0.09); g.gain.exponentialRampToValueAtTime(0.001, t + k * 0.09 + 0.25); o.connect(g); g.connect(ctx.destination); o.start(t + k * 0.09); o.stop(t + k * 0.09 + 0.3); }); } catch (e) {} }
|
||||||
|
function confetti(){ var colors = [CFG.color || '#6d28d9', '#fbbf24', '#34d399', '#60a5fa', '#f472b6']; for (var i = 0; i < 90; i++){ var c = document.createElement('div'); c.className = 'confetti'; c.style.left = (i * 137 % 100) + 'vw'; c.style.background = colors[i % colors.length]; c.style.animationDuration = (2.2 + (i * 53 % 18) / 10) + 's'; c.style.animationDelay = ((i * 31 % 14) / 10) + 's'; document.body.appendChild(c); } }
|
||||||
|
var N = CFG.puzzles.length;
|
||||||
|
var GW = 13, RH = 4, ROOMS = N + 1, GH = ROOMS * RH + 1;
|
||||||
|
var TS = 38, VR = Math.min(GH, 11);
|
||||||
|
var map = [], doorAt = {}, doorPos = [], solvedFlags = [];
|
||||||
|
for (var y = 0; y < GH; y++) {
|
||||||
|
map[y] = [];
|
||||||
|
for (var x = 0; x < GW; x++) {
|
||||||
|
map[y][x] = (x === 0 || x === GW - 1 || y === 0 || y === GH - 1 || y % RH === 0) ? 1 : 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var i = 0; i < N; i++) {
|
||||||
|
var dy = (i + 1) * RH, dx = (i % 2 === 0) ? GW - 3 : 2;
|
||||||
|
map[dy][dx] = 2; doorAt[dy + '_' + dx] = i; doorPos.push({ y: dy, x: dx });
|
||||||
|
}
|
||||||
|
var chest = { y: (ROOMS - 1) * RH + 2, x: Math.floor(GW / 2) };
|
||||||
|
map[chest.y][chest.x] = 4;
|
||||||
|
var hero = { y: 2, x: Math.floor(GW / 2) - 2 };
|
||||||
|
var finished = false;
|
||||||
|
|
||||||
|
var cv = el('cv'); cv.width = GW * TS; cv.height = VR * TS;
|
||||||
|
var ctx = cv.getContext('2d');
|
||||||
|
|
||||||
|
function draw(){
|
||||||
|
var offY = Math.max(0, Math.min(hero.y - Math.floor(VR / 2), GH - VR));
|
||||||
|
ctx.clearRect(0, 0, cv.width, cv.height);
|
||||||
|
for (var vy = 0; vy < VR; vy++) {
|
||||||
|
var y = vy + offY;
|
||||||
|
for (var x = 0; x < GW; x++) {
|
||||||
|
var t = map[y][x], px = x * TS, py = vy * TS;
|
||||||
|
if (t === 1) {
|
||||||
|
ctx.fillStyle = '#33215f'; ctx.fillRect(px, py, TS, TS);
|
||||||
|
ctx.fillStyle = '#241646';
|
||||||
|
ctx.fillRect(px, py + TS / 2 - 1, TS, 2);
|
||||||
|
ctx.fillRect(px + ((y % 2) ? TS / 2 : TS / 4) - 1, py, 2, TS / 2 - 1);
|
||||||
|
} else {
|
||||||
|
ctx.fillStyle = ((x + y) % 2) ? '#191130' : '#1c1336'; ctx.fillRect(px, py, TS, TS);
|
||||||
|
if (t === 2 || t === 3) {
|
||||||
|
ctx.fillStyle = t === 2 ? '#9f1239' : '#166534'; ctx.fillRect(px + 3, py + 2, TS - 6, TS - 4);
|
||||||
|
ctx.fillStyle = t === 2 ? '#e11d48' : '#22c55e'; ctx.fillRect(px + 6, py + 5, TS - 12, TS - 10);
|
||||||
|
ctx.fillStyle = '#fbbf24'; ctx.fillRect(px + TS / 2 - 2, py + TS / 2 - 2, 4, 7);
|
||||||
|
}
|
||||||
|
if (t === 4) {
|
||||||
|
ctx.fillStyle = '#92400e'; ctx.fillRect(px + 5, py + 10, TS - 10, TS - 16);
|
||||||
|
ctx.fillStyle = '#f59e0b'; ctx.fillRect(px + 5, py + 10, TS - 10, 7);
|
||||||
|
ctx.fillStyle = '#fde68a'; ctx.fillRect(px + TS / 2 - 2, py + 13, 4, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var hx = hero.x * TS, hy = (hero.y - offY) * TS;
|
||||||
|
ctx.fillStyle = CFG.color || '#6d28d9'; ctx.fillRect(hx + 7, hy + 5, TS - 14, TS - 10);
|
||||||
|
ctx.fillStyle = '#fff'; ctx.fillRect(hx + 12, hy + 12, 5, 5); ctx.fillRect(hx + TS - 17, hy + 12, 5, 5);
|
||||||
|
ctx.fillStyle = '#0d0820'; ctx.fillRect(hx + 13, hy + 14, 2, 2); ctx.fillRect(hx + TS - 16, hy + 14, 2, 2);
|
||||||
|
ctx.fillStyle = '#fff'; ctx.fillRect(hx + 13, hy + TS - 14, TS - 26, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateHud(){
|
||||||
|
var open = 0; for (var i = 0; i < N; i++) if (solvedFlags[i]) open++;
|
||||||
|
el('hudStep').textContent = 'Usi: ' + open + '/' + N;
|
||||||
|
el('hudStars').textContent = totalStars + ' \u2605';
|
||||||
|
var hb = el('hudLetters'); hb.innerHTML = '';
|
||||||
|
for (var j = 0; j < N; j++) {
|
||||||
|
var L = (CFG.puzzles[j].letter || '').trim();
|
||||||
|
if (!L) continue;
|
||||||
|
var s = document.createElement('span');
|
||||||
|
if (solvedFlags[j]) { s.textContent = L.toUpperCase(); s.className = 'won'; }
|
||||||
|
else s.textContent = '?';
|
||||||
|
hb.appendChild(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function move(dx, dy){
|
||||||
|
if (finished || modalOpen()) return;
|
||||||
|
var nx = hero.x + dx, ny = hero.y + dy;
|
||||||
|
if (ny < 0 || ny >= GH || nx < 0 || nx >= GW) return;
|
||||||
|
var t = map[ny][nx];
|
||||||
|
if (t === 1) return;
|
||||||
|
if (t === 2) { openPuzzle(doorAt[ny + '_' + nx], onDoorSolved); return; }
|
||||||
|
if (t === 4) { finished = true; showFinal(); return; }
|
||||||
|
hero.x = nx; hero.y = ny; draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onDoorSolved(i){
|
||||||
|
solvedFlags[i] = true;
|
||||||
|
map[doorPos[i].y][doorPos[i].x] = 3;
|
||||||
|
updateHud(); draw();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('keydown', function(e){
|
||||||
|
var d = { ArrowUp: [0, -1], ArrowDown: [0, 1], ArrowLeft: [-1, 0], ArrowRight: [1, 0], w: [0, -1], s: [0, 1], a: [-1, 0], d: [1, 0] }[e.key];
|
||||||
|
if (!d) return;
|
||||||
|
e.preventDefault();
|
||||||
|
move(d[0], d[1]);
|
||||||
|
});
|
||||||
|
document.querySelectorAll('#dpad button').forEach(function(b){
|
||||||
|
b.addEventListener('click', function(){
|
||||||
|
var m = { U: [0, -1], D: [0, 1], L: [-1, 0], R: [1, 0] }[b.dataset.d];
|
||||||
|
move(m[0], m[1]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
var mIdx = -1, mAtt = 0, mHint = false, mCb = null;
|
||||||
|
el('mHintBtn').onclick = function(){ mHint = true; el('mHintText').style.display = 'block'; };
|
||||||
|
el('mClose').onclick = function(){ el('mOverlay').style.display = 'none'; };
|
||||||
|
function modalOpen(){ return el('mOverlay').style.display === 'flex'; }
|
||||||
|
function openPuzzle(i, cb){
|
||||||
|
mIdx = i; mAtt = 0; mHint = false; mCb = cb;
|
||||||
|
var p = CFG.puzzles[i];
|
||||||
|
el('mTitle').textContent = p.title || ('Puzzle ' + (i + 1));
|
||||||
|
el('mQ').textContent = p.question;
|
||||||
|
el('mFeedback').textContent = ''; el('mFeedback').className = 'mfb';
|
||||||
|
el('mHintText').style.display = 'none'; el('mHintText').textContent = p.hint || '';
|
||||||
|
el('mHintBtn').style.display = p.hint ? '' : 'none';
|
||||||
|
var box = el('mAnswers'); box.innerHTML = '';
|
||||||
|
if (p.type === 'free') {
|
||||||
|
var inp = document.createElement('input'); inp.type = 'text'; inp.placeholder = 'Scrie raspunsul...'; inp.autocomplete = 'off';
|
||||||
|
var b = document.createElement('button'); b.textContent = 'Verifica';
|
||||||
|
b.onclick = function(){ mCheck(inp.value); };
|
||||||
|
inp.onkeydown = function(e){ e.stopPropagation(); if (e.key === 'Enter') b.click(); };
|
||||||
|
box.appendChild(inp); box.appendChild(b);
|
||||||
|
setTimeout(function(){ inp.focus(); }, 60);
|
||||||
|
} else if (p.type === 'tf') {
|
||||||
|
['Adevarat', 'Fals'].forEach(function(v){ var b = document.createElement('button'); b.className = 'opt'; b.textContent = v; b.onclick = function(){ mCheck(v); }; box.appendChild(b); });
|
||||||
|
} else {
|
||||||
|
choiceOpts(p).forEach(function(o){ var b = document.createElement('button'); b.className = 'opt'; b.textContent = o; b.onclick = function(){ mCheck(o); }; box.appendChild(b); });
|
||||||
|
}
|
||||||
|
el('mOverlay').style.display = 'flex';
|
||||||
|
}
|
||||||
|
function mCheck(given){
|
||||||
|
var p = CFG.puzzles[mIdx];
|
||||||
|
if (checkAnswer(p, given)) {
|
||||||
|
var s = starsFor(mAtt, mHint);
|
||||||
|
totalStars += s; beep(true);
|
||||||
|
el('mFeedback').textContent = 'Corect! +' + s + ' \u2605'; el('mFeedback').className = 'mfb good';
|
||||||
|
setTimeout(function(){ el('mOverlay').style.display = 'none'; var cb = mCb; mCb = null; if (cb) cb(mIdx, s); }, 750);
|
||||||
|
} else {
|
||||||
|
mAtt++; beep(false);
|
||||||
|
el('mFeedback').textContent = 'Nu e bine, mai incearca!'; el('mFeedback').className = 'mfb bad';
|
||||||
|
var c = el('mCard'); c.classList.remove('shake'); void c.offsetWidth; c.classList.add('shake');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function showFinal(){
|
||||||
|
el('fStars').textContent = totalStars + ' / ' + (CFG.puzzles.length * 3) + ' \u2605';
|
||||||
|
var w = finalWord(); var bw = el('fWord'); bw.innerHTML = '';
|
||||||
|
for (var j = 0; j < w.length; j++){ var s = document.createElement('span'); s.textContent = w.charAt(j); s.style.animationDelay = (j * 0.18) + 's'; bw.appendChild(s); }
|
||||||
|
var msg = CFG.finalMessage || '';
|
||||||
|
el('fMsg').textContent = CFG.player ? CFG.player + ', ' + msg.charAt(0).toLowerCase() + msg.slice(1) : msg;
|
||||||
|
el('fOverlay').style.display = 'flex';
|
||||||
|
beep(true); confetti();
|
||||||
|
}
|
||||||
|
el('fAgain').onclick = function(){ location.reload(); };
|
||||||
|
updateHud(); draw();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
171
exemplu-chat.html
Normal file
171
exemplu-chat.html
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ro">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Comoara ascunsa</title>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; background: #0b1220; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; color: #e5e7eb; display: flex; justify-content: center; min-height: 100vh; }
|
||||||
|
#app { width: 100%; max-width: 480px; height: 100vh; height: 100dvh; display: flex; flex-direction: column; background: #0f172a; }
|
||||||
|
header { display: flex; gap: 10px; align-items: center; padding: 10px 14px; background: #1e293b; border-bottom: 1px solid #334155; }
|
||||||
|
.avatar { width: 38px; height: 38px; border-radius: 50%; background: var(--accent); display: flex; align-items: center; justify-content: center; font-weight: 800; font-size: 18px; color: #fff; }
|
||||||
|
.cname { font-weight: 700; }
|
||||||
|
.cstatus { font-size: 12px; color: #34d399; }
|
||||||
|
#msgs { flex: 1; overflow-y: auto; padding: 14px 12px; display: flex; flex-direction: column; gap: 8px; }
|
||||||
|
.row { display: flex; }
|
||||||
|
.row.me { justify-content: flex-end; }
|
||||||
|
.bub { max-width: 78%; padding: 9px 13px; border-radius: 16px; line-height: 1.4; font-size: 15px; white-space: pre-line; animation: bin .25s ease; }
|
||||||
|
@keyframes bin { from { transform: translateY(8px); opacity: 0; } to { transform: none; opacity: 1; } }
|
||||||
|
.row.him .bub { background: #1e293b; border-bottom-left-radius: 5px; }
|
||||||
|
.row.me .bub { background: var(--accent); color: #fff; border-bottom-right-radius: 5px; }
|
||||||
|
.bub.tile { font-size: 24px; font-weight: 800; letter-spacing: 2px; background: #14532d; border: 1px solid #22c55e; }
|
||||||
|
.bub.typing i { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: #94a3b8; margin: 0 2px; animation: tp 1s infinite; }
|
||||||
|
.bub.typing i:nth-child(2) { animation-delay: .15s; } .bub.typing i:nth-child(3) { animation-delay: .3s; }
|
||||||
|
@keyframes tp { 30% { transform: translateY(-5px); } }
|
||||||
|
#composer { padding: 10px 12px; background: #1e293b; border-top: 1px solid #334155; display: flex; flex-wrap: wrap; gap: 8px; min-height: 58px; }
|
||||||
|
#composer input { flex: 1; min-width: 120px; font: inherit; font-size: 15px; padding: 9px 13px; border-radius: 99px; border: 1px solid #475569; background: #0f172a; color: #fff; }
|
||||||
|
#composer input:focus { outline: none; border-color: var(--accent); }
|
||||||
|
#composer button { font: inherit; cursor: pointer; border: none; border-radius: 99px; padding: 9px 16px; font-weight: 600; background: var(--accent); color: #fff; }
|
||||||
|
#composer button.chip { background: #0f172a; border: 1px solid #475569; color: #cbd5e1; }
|
||||||
|
#composer button.chip:hover { border-color: var(--accent); color: #fff; }
|
||||||
|
|
||||||
|
.confetti { position: fixed; top: -12px; width: 9px; height: 14px; z-index: 99; animation: fall linear forwards; }
|
||||||
|
@keyframes fall { to { transform: translateY(105vh) rotate(720deg); } }
|
||||||
|
.shake { animation: shake .4s ease; }
|
||||||
|
@keyframes shake { 20%,60% { transform: translateX(-8px); } 40%,80% { transform: translateX(8px); } }
|
||||||
|
#fOverlay { display: none; position: fixed; inset: 0; background: rgba(8,4,20,.88); z-index: 30; align-items: center; justify-content: center; padding: 16px; }
|
||||||
|
#fOverlay .fcard { width: 100%; max-width: 480px; text-align: center; background: #221440; border: 1px solid rgba(255,255,255,.18); border-radius: 18px; padding: 28px; color: #fff; font-family: system-ui, sans-serif; }
|
||||||
|
#fOverlay h1 { margin: 0 0 8px; font-size: 26px; }
|
||||||
|
#fOverlay .fstars { font-size: 26px; letter-spacing: 4px; color: #fbbf24; margin: 6px 0; }
|
||||||
|
#fOverlay .fword { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin: 16px 0; }
|
||||||
|
#fOverlay .fword span { width: 44px; height: 52px; border-radius: 10px; background: var(--accent); display: flex; align-items: center; justify-content: center; font-size: 26px; font-weight: 800; animation: flipin .6s ease backwards; }
|
||||||
|
@keyframes flipin { from { transform: rotateX(90deg); } to { transform: rotateX(0); } }
|
||||||
|
#fOverlay p { color: rgba(255,255,255,.8); line-height: 1.5; }
|
||||||
|
#fOverlay button { font: inherit; cursor: pointer; border: none; border-radius: 10px; padding: 12px 18px; font-weight: 700; background: var(--accent); color: #fff; width: 100%; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">
|
||||||
|
<header><div class="avatar" id="av"></div><div><div class="cname" id="cn"></div><div class="cstatus" id="cs">online</div></div></header>
|
||||||
|
<div id="msgs"></div>
|
||||||
|
<div id="composer"></div>
|
||||||
|
</div>
|
||||||
|
<div id="fOverlay"><div class="fcard">
|
||||||
|
<h1>Evadare reusita!</h1>
|
||||||
|
<div class="fstars" id="fStars"></div>
|
||||||
|
<div class="fword" id="fWord"></div>
|
||||||
|
<p id="fMsg"></p>
|
||||||
|
<button id="fAgain">Joaca din nou</button>
|
||||||
|
</div></div>
|
||||||
|
<script>
|
||||||
|
var CFG = {"title":"Comoara ascunsa","player":"","color":"#6d28d9","style":"chat","charName":"Alex","story":"O comoara a fost ascunsa, iar singurul drum spre ea trece prin cateva incercari. Rezolva fiecare puzzle ca sa aduni literele cuvantului magic.","finalMessage":"Felicitari! Ai gasit comoara!","puzzles":[{"title":"Incalzirea","type":"free","question":"Cat fac 7 x 8?","answer":"56","tfAnswer":"Adevarat","choices":"","hint":"Tabla inmultirii cu 7.","letter":"D"},{"title":"Adevarat sau fals","type":"tf","question":"Romania are iesire la Marea Neagra.","answer":"","tfAnswer":"Adevarat","choices":"","hint":"","letter":"A"},{"title":"Alege raspunsul","type":"choice","question":"Care este capitala Frantei?","answer":"","tfAnswer":"Adevarat","choices":"*Paris\nLyon\nMarsilia","hint":"Turnul Eiffel.","letter":"R"}]};
|
||||||
|
document.documentElement.style.setProperty('--accent', CFG.color || '#6d28d9');
|
||||||
|
var totalStars = 0;
|
||||||
|
function el(id){ return document.getElementById(id); }
|
||||||
|
function norm(s){ return String(s).trim().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/\s+/g, ' ').replace(/,/g, '.'); }
|
||||||
|
function starsFor(att, hint){ return (hint || att >= 2) ? 1 : (att === 1 ? 2 : 3); }
|
||||||
|
function finalWord(){ var w = ''; for (var i = 0; i < CFG.puzzles.length; i++){ var L = (CFG.puzzles[i].letter || '').trim(); if (L) w += L.toUpperCase(); } return w; }
|
||||||
|
function choiceOpts(p){ return (p.choices || '').split('\n').map(function(l){ return l.trim(); }).filter(Boolean).map(function(o){ return o.charAt(0) === '*' ? o.slice(1).trim() : o; }); }
|
||||||
|
function choiceCorrect(p){ var ls = (p.choices || '').split('\n'); for (var i = 0; i < ls.length; i++){ var l = ls[i].trim(); if (l.charAt(0) === '*') return l.slice(1).trim(); } return ''; }
|
||||||
|
function checkAnswer(p, given){ var exp = p.type === 'tf' ? p.tfAnswer : (p.type === 'choice' ? choiceCorrect(p) : p.answer); return norm(given) !== '' && norm(given) === norm(exp); }
|
||||||
|
function beep(ok){ try { var ctx = beep.ctx || (beep.ctx = new (window.AudioContext || window.webkitAudioContext)()); var t = ctx.currentTime; var fs = ok ? [523, 784] : [196]; fs.forEach(function(f, k){ var o = ctx.createOscillator(), g = ctx.createGain(); o.frequency.value = f; o.type = 'triangle'; g.gain.setValueAtTime(0.12, t + k * 0.09); g.gain.exponentialRampToValueAtTime(0.001, t + k * 0.09 + 0.25); o.connect(g); g.connect(ctx.destination); o.start(t + k * 0.09); o.stop(t + k * 0.09 + 0.3); }); } catch (e) {} }
|
||||||
|
function confetti(){ var colors = [CFG.color || '#6d28d9', '#fbbf24', '#34d399', '#60a5fa', '#f472b6']; for (var i = 0; i < 90; i++){ var c = document.createElement('div'); c.className = 'confetti'; c.style.left = (i * 137 % 100) + 'vw'; c.style.background = colors[i % colors.length]; c.style.animationDuration = (2.2 + (i * 53 % 18) / 10) + 's'; c.style.animationDelay = ((i * 31 % 14) / 10) + 's'; document.body.appendChild(c); } }
|
||||||
|
var who = (CFG.charName || 'Alex').trim() || 'Alex';
|
||||||
|
el('cn').textContent = who; el('av').textContent = who.charAt(0).toUpperCase();
|
||||||
|
var msgs = el('msgs'), composer = el('composer');
|
||||||
|
var idx = -1, attempts = 0, hintUsed = false;
|
||||||
|
var wrongs = ['Nu... nu a mers. Mai incearca!', 'Hmm, nu e asta. Gandeste-te bine!', 'Tot incuiat. Alta idee?'];
|
||||||
|
|
||||||
|
function scrollEnd(){ msgs.scrollTop = msgs.scrollHeight; }
|
||||||
|
function bubble(side, text, cls){
|
||||||
|
var r = document.createElement('div'); r.className = 'row ' + side;
|
||||||
|
var b = document.createElement('div'); b.className = 'bub' + (cls ? ' ' + cls : '');
|
||||||
|
b.textContent = text;
|
||||||
|
r.appendChild(b); msgs.appendChild(r); scrollEnd();
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
function charMsg(text, cb){
|
||||||
|
el('cs').textContent = 'scrie...';
|
||||||
|
var b = bubble('him', '', 'typing');
|
||||||
|
b.innerHTML = '<i></i><i></i><i></i>';
|
||||||
|
var d = Math.min(450 + text.length * 14, 1800);
|
||||||
|
setTimeout(function(){
|
||||||
|
b.className = 'bub'; b.textContent = text;
|
||||||
|
el('cs').textContent = 'online'; scrollEnd();
|
||||||
|
if (cb) setTimeout(cb, 280);
|
||||||
|
}, d);
|
||||||
|
}
|
||||||
|
function seq(texts, cb){ var i = 0; (function n(){ if (i >= texts.length) { if (cb) cb(); return; } charMsg(texts[i++], n); })(); }
|
||||||
|
|
||||||
|
function storyChunks(){
|
||||||
|
var parts = (CFG.story || '').match(/[^.!?]+[.!?]*\s*/g) || [];
|
||||||
|
var out = [], cur = '';
|
||||||
|
for (var i = 0; i < parts.length; i++) {
|
||||||
|
if (cur && (cur + parts[i]).length > 110) { out.push(cur.trim()); cur = ''; }
|
||||||
|
cur += parts[i];
|
||||||
|
}
|
||||||
|
if (cur.trim()) out.push(cur.trim());
|
||||||
|
return out.length ? out : [CFG.story || ''];
|
||||||
|
}
|
||||||
|
|
||||||
|
function setComposer(p){
|
||||||
|
composer.innerHTML = '';
|
||||||
|
function chip(label, fn, cls){ var b = document.createElement('button'); if (cls) b.className = cls; b.textContent = label; b.onclick = fn; composer.appendChild(b); return b; }
|
||||||
|
if (p.type === 'free') {
|
||||||
|
var inp = document.createElement('input'); inp.placeholder = 'Scrie raspunsul...'; inp.autocomplete = 'off';
|
||||||
|
composer.appendChild(inp);
|
||||||
|
var send = chip('Trimite', function(){ if (inp.value.trim()) { var v = inp.value.trim(); inp.value = ''; answer(v); } });
|
||||||
|
inp.onkeydown = function(e){ if (e.key === 'Enter') send.click(); };
|
||||||
|
setTimeout(function(){ inp.focus(); }, 100);
|
||||||
|
} else if (p.type === 'tf') {
|
||||||
|
chip('Adevarat', function(){ answer('Adevarat'); }, 'chip');
|
||||||
|
chip('Fals', function(){ answer('Fals'); }, 'chip');
|
||||||
|
} else {
|
||||||
|
choiceOpts(p).forEach(function(o){ chip(o, function(){ answer(o); }, 'chip'); });
|
||||||
|
}
|
||||||
|
if (p.hint) chip('Cere un indiciu', function(){ hintUsed = true; bubble('me', 'Ai vreun indiciu?'); composer.innerHTML = ''; charMsg(p.hint, function(){ setComposer(p); }); }, 'chip');
|
||||||
|
}
|
||||||
|
|
||||||
|
function answer(given){
|
||||||
|
var p = CFG.puzzles[idx];
|
||||||
|
bubble('me', given);
|
||||||
|
composer.innerHTML = '';
|
||||||
|
if (checkAnswer(p, given)) {
|
||||||
|
var s = starsFor(attempts, hintUsed);
|
||||||
|
totalStars += s; beep(true);
|
||||||
|
var L = (p.letter || '').trim();
|
||||||
|
charMsg('Da! Asta era! (+' + s + ' \u2605, total ' + totalStars + ')', function(){
|
||||||
|
if (L) { bubble('him', L.toUpperCase(), 'tile'); charMsg('Am gasit o litera!', next); }
|
||||||
|
else next();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
attempts++; beep(false);
|
||||||
|
charMsg(wrongs[(attempts - 1) % wrongs.length], function(){ setComposer(p); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function next(){
|
||||||
|
idx++; attempts = 0; hintUsed = false;
|
||||||
|
if (idx >= CFG.puzzles.length) {
|
||||||
|
seq(['AM IESIT! Multumesc' + (CFG.player ? ', ' + CFG.player : '') + '!', CFG.finalMessage || ''], function(){ showFinal(); });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var p = CFG.puzzles[idx];
|
||||||
|
seq([(p.title ? p.title + '. ' : '') + p.question], function(){ setComposer(p); });
|
||||||
|
}
|
||||||
|
|
||||||
|
seq(['Salut' + (CFG.player ? ', ' + CFG.player : '') + '!'].concat(storyChunks()).concat(['Ma ajuti sa ies de aici?']), next);
|
||||||
|
function showFinal(){
|
||||||
|
el('fStars').textContent = totalStars + ' / ' + (CFG.puzzles.length * 3) + ' \u2605';
|
||||||
|
var w = finalWord(); var bw = el('fWord'); bw.innerHTML = '';
|
||||||
|
for (var j = 0; j < w.length; j++){ var s = document.createElement('span'); s.textContent = w.charAt(j); s.style.animationDelay = (j * 0.18) + 's'; bw.appendChild(s); }
|
||||||
|
var msg = CFG.finalMessage || '';
|
||||||
|
el('fMsg').textContent = CFG.player ? CFG.player + ', ' + msg.charAt(0).toLowerCase() + msg.slice(1) : msg;
|
||||||
|
el('fOverlay').style.display = 'flex';
|
||||||
|
beep(true); confetti();
|
||||||
|
}
|
||||||
|
el('fAgain').onclick = function(){ location.reload(); };
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
270
exemplu-clasic.html
Normal file
270
exemplu-clasic.html
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ro">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Comoara ascunsa</title>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body {
|
||||||
|
margin: 0; min-height: 100vh; font-family: system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||||
|
color: #fff; display: flex; align-items: center; justify-content: center; padding: 16px;
|
||||||
|
background: linear-gradient(160deg, #14092e 0%, #2a1257 55%, #14092e 100%);
|
||||||
|
}
|
||||||
|
.card {
|
||||||
|
width: 100%; max-width: 560px; background: rgba(255,255,255,.07);
|
||||||
|
border: 1px solid rgba(255,255,255,.14); border-radius: 18px; padding: 26px;
|
||||||
|
backdrop-filter: blur(6px); box-shadow: 0 18px 50px rgba(0,0,0,.45);
|
||||||
|
}
|
||||||
|
h1 { margin: 0 0 6px; font-size: 26px; text-align: center; }
|
||||||
|
.story { color: rgba(255,255,255,.8); text-align: center; line-height: 1.5; }
|
||||||
|
.screen { display: none; }
|
||||||
|
.screen.on { display: block; animation: pop .35s ease; }
|
||||||
|
@keyframes pop { from { transform: scale(.96); opacity: 0; } to { transform: scale(1); opacity: 1; } }
|
||||||
|
.progress { height: 7px; background: rgba(255,255,255,.15); border-radius: 99px; overflow: hidden; margin: 14px 0 4px; }
|
||||||
|
.progress i { display: block; height: 100%; background: var(--accent); width: 0; transition: width .4s ease; }
|
||||||
|
.meta { display: flex; justify-content: space-between; font-size: 12px; color: rgba(255,255,255,.6); margin-bottom: 14px; }
|
||||||
|
.letters { display: flex; gap: 6px; justify-content: center; flex-wrap: wrap; margin: 14px 0; }
|
||||||
|
.tile {
|
||||||
|
width: 34px; height: 40px; border-radius: 8px; display: flex; align-items: center; justify-content: center;
|
||||||
|
font-weight: 800; font-size: 18px; background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.18);
|
||||||
|
color: rgba(255,255,255,.35);
|
||||||
|
}
|
||||||
|
.tile.won { background: var(--accent); color: #fff; border-color: transparent; animation: flip .5s ease; }
|
||||||
|
@keyframes flip { from { transform: rotateX(90deg); } to { transform: rotateX(0); } }
|
||||||
|
.qtitle { font-size: 13px; text-transform: uppercase; letter-spacing: .08em; color: var(--accent-light); font-weight: 700; }
|
||||||
|
.question { font-size: 19px; line-height: 1.45; margin: 8px 0 18px; }
|
||||||
|
input[type=text] {
|
||||||
|
width: 100%; font: inherit; font-size: 18px; padding: 11px 13px; border-radius: 10px;
|
||||||
|
border: 1px solid rgba(255,255,255,.25); background: rgba(0,0,0,.25); color: #fff; text-align: center;
|
||||||
|
}
|
||||||
|
input:focus { outline: 2px solid var(--accent); border-color: transparent; }
|
||||||
|
button {
|
||||||
|
font: inherit; cursor: pointer; border: none; border-radius: 10px; padding: 12px 18px;
|
||||||
|
font-weight: 700; background: var(--accent); color: #fff; width: 100%; margin-top: 10px;
|
||||||
|
}
|
||||||
|
button:hover { filter: brightness(1.12); }
|
||||||
|
button.opt { background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.2); font-weight: 600; text-align: left; }
|
||||||
|
button.opt:hover { background: rgba(255,255,255,.18); }
|
||||||
|
button.hint { background: none; border: none; color: rgba(255,255,255,.55); font-weight: 600; font-size: 13px; width: auto; display: block; margin: 12px auto 0; }
|
||||||
|
button.hint:hover { color: #fff; }
|
||||||
|
.hinttext { background: rgba(255,255,255,.1); border-radius: 9px; padding: 10px 12px; font-size: 14px; margin-top: 10px; white-space: pre-line; display: none; }
|
||||||
|
.feedback { min-height: 22px; text-align: center; font-weight: 700; margin-top: 10px; }
|
||||||
|
.feedback.bad { color: #fda4af; }
|
||||||
|
.feedback.good { color: #86efac; }
|
||||||
|
.shake { animation: shake .4s ease; }
|
||||||
|
@keyframes shake { 20%,60% { transform: translateX(-8px); } 40%,80% { transform: translateX(8px); } }
|
||||||
|
.stars { text-align: center; font-size: 26px; letter-spacing: 4px; color: #fbbf24; margin: 6px 0; }
|
||||||
|
.bigword { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin: 18px 0; }
|
||||||
|
.bigword span {
|
||||||
|
width: 44px; height: 52px; border-radius: 10px; background: var(--accent); display: flex;
|
||||||
|
align-items: center; justify-content: center; font-size: 26px; font-weight: 800; animation: flip .6s ease backwards;
|
||||||
|
}
|
||||||
|
.confetti { position: fixed; top: -12px; width: 9px; height: 14px; z-index: 5; animation: fall linear forwards; }
|
||||||
|
@keyframes fall { to { transform: translateY(105vh) rotate(720deg); } }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="card">
|
||||||
|
<div id="sStart" class="screen on">
|
||||||
|
<h1 id="gtitle"></h1>
|
||||||
|
<p class="story" id="gstory"></p>
|
||||||
|
<button id="btnStart">Incepe aventura</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="sGame" class="screen">
|
||||||
|
<div class="progress"><i id="bar"></i></div>
|
||||||
|
<div class="meta"><span id="step"></span><span id="score"></span></div>
|
||||||
|
<div class="letters" id="lettersBar"></div>
|
||||||
|
<div id="qbox">
|
||||||
|
<div class="qtitle" id="qtitle"></div>
|
||||||
|
<div class="question" id="qtext"></div>
|
||||||
|
<div id="answers"></div>
|
||||||
|
<div class="feedback" id="feedback"></div>
|
||||||
|
<button class="hint" id="btnHint">Vreau un indiciu</button>
|
||||||
|
<div class="hinttext" id="hinttext"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="sFinal" class="screen">
|
||||||
|
<h1>Evadare reusita!</h1>
|
||||||
|
<div class="stars" id="finalStars"></div>
|
||||||
|
<div class="bigword" id="bigword"></div>
|
||||||
|
<p class="story" id="finalMsg"></p>
|
||||||
|
<button id="btnAgain">Joaca din nou</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
var CFG = {"title":"Comoara ascunsa","player":"","color":"#6d28d9","story":"O comoara a fost ascunsa, iar singurul drum spre ea trece prin cateva incercari. Rezolva fiecare puzzle ca sa aduni literele cuvantului magic.","finalMessage":"Felicitari! Ai gasit comoara!","puzzles":[{"title":"Incalzirea","type":"free","question":"Cat fac 7 x 8?","answer":"56","tfAnswer":"Adevarat","choices":"","hint":"Tabla inmultirii cu 7.","letter":"D"},{"title":"Adevarat sau fals","type":"tf","question":"Romania are iesire la Marea Neagra.","answer":"","tfAnswer":"Adevarat","choices":"","hint":"","letter":"A"},{"title":"Alege raspunsul","type":"choice","question":"Care este capitala Frantei?","answer":"","tfAnswer":"Adevarat","choices":"*Paris\nLyon\nMarsilia","hint":"Turnul Eiffel.","letter":"R"}]};
|
||||||
|
document.documentElement.style.setProperty('--accent', CFG.color || '#6d28d9');
|
||||||
|
document.documentElement.style.setProperty('--accent-light', 'color-mix(in srgb, ' + (CFG.color || '#6d28d9') + ' 40%, white)');
|
||||||
|
|
||||||
|
var idx = 0, totalStars = 0, attempts = 0, hintUsed = false, won = [];
|
||||||
|
|
||||||
|
function el(id) { return document.getElementById(id); }
|
||||||
|
function norm(s) {
|
||||||
|
return String(s).trim().toLowerCase().normalize('NFD')
|
||||||
|
.replace(/[\u0300-\u036f]/g, '').replace(/\s+/g, ' ').replace(/,/g, '.');
|
||||||
|
}
|
||||||
|
function show(id) {
|
||||||
|
var scr = document.querySelectorAll('.screen');
|
||||||
|
for (var i = 0; i < scr.length; i++) scr[i].classList.remove('on');
|
||||||
|
el(id).classList.add('on');
|
||||||
|
}
|
||||||
|
|
||||||
|
el('gtitle').textContent = CFG.title;
|
||||||
|
var hello = CFG.player ? 'Salut, ' + CFG.player + '! ' : '';
|
||||||
|
el('gstory').textContent = hello + CFG.story;
|
||||||
|
|
||||||
|
el('btnStart').onclick = function () { show('sGame'); renderPuzzle(); };
|
||||||
|
el('btnAgain').onclick = function () { location.reload(); };
|
||||||
|
|
||||||
|
function lettersBar() {
|
||||||
|
var bar = el('lettersBar');
|
||||||
|
bar.innerHTML = '';
|
||||||
|
var any = false;
|
||||||
|
for (var i = 0; i < CFG.puzzles.length; i++) {
|
||||||
|
var L = (CFG.puzzles[i].letter || '').trim();
|
||||||
|
if (!L) continue;
|
||||||
|
any = true;
|
||||||
|
var d = document.createElement('div');
|
||||||
|
d.className = 'tile' + (won[i] ? ' won' : '');
|
||||||
|
d.textContent = won[i] ? L.toUpperCase() : '?';
|
||||||
|
bar.appendChild(d);
|
||||||
|
}
|
||||||
|
bar.style.display = any ? '' : 'none';
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderPuzzle() {
|
||||||
|
var p = CFG.puzzles[idx];
|
||||||
|
attempts = 0; hintUsed = false;
|
||||||
|
el('bar').style.width = (idx / CFG.puzzles.length * 100) + '%';
|
||||||
|
el('step').textContent = 'Puzzle ' + (idx + 1) + ' din ' + CFG.puzzles.length;
|
||||||
|
el('score').textContent = totalStars + ' \u2605';
|
||||||
|
el('qtitle').textContent = p.title || 'Puzzle ' + (idx + 1);
|
||||||
|
el('qtext').textContent = p.question;
|
||||||
|
el('feedback').textContent = ''; el('feedback').className = 'feedback';
|
||||||
|
el('hinttext').style.display = 'none';
|
||||||
|
el('hinttext').textContent = p.hint || '';
|
||||||
|
el('btnHint').style.display = p.hint ? '' : 'none';
|
||||||
|
lettersBar();
|
||||||
|
|
||||||
|
var box = el('answers');
|
||||||
|
box.innerHTML = '';
|
||||||
|
if (p.type === 'free') {
|
||||||
|
var inp = document.createElement('input');
|
||||||
|
inp.type = 'text'; inp.autocomplete = 'off'; inp.placeholder = 'Scrie raspunsul...';
|
||||||
|
var btn = document.createElement('button');
|
||||||
|
btn.textContent = 'Verifica';
|
||||||
|
btn.onclick = function () { check(inp.value, p.answer); };
|
||||||
|
inp.onkeydown = function (e) { if (e.key === 'Enter') btn.click(); };
|
||||||
|
box.appendChild(inp); box.appendChild(btn);
|
||||||
|
setTimeout(function () { inp.focus(); }, 50);
|
||||||
|
} else if (p.type === 'tf') {
|
||||||
|
['Adevarat', 'Fals'].forEach(function (v) {
|
||||||
|
var b = document.createElement('button');
|
||||||
|
b.className = 'opt'; b.textContent = v;
|
||||||
|
b.onclick = function () { check(v, p.tfAnswer); };
|
||||||
|
box.appendChild(b);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var correct = '';
|
||||||
|
var opts = (p.choices || '').split('\n').map(function (l) { return l.trim(); }).filter(Boolean);
|
||||||
|
opts.forEach(function (o) { if (o.charAt(0) === '*') correct = o.slice(1).trim(); });
|
||||||
|
opts.map(function (o) { return o.charAt(0) === '*' ? o.slice(1).trim() : o; })
|
||||||
|
.forEach(function (o) {
|
||||||
|
var b = document.createElement('button');
|
||||||
|
b.className = 'opt'; b.textContent = o;
|
||||||
|
b.onclick = function () { check(o, correct); };
|
||||||
|
box.appendChild(b);
|
||||||
|
});
|
||||||
|
if (!opts.length) box.textContent = '(puzzle fara variante - completeaza-le in builder)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
el('btnHint').onclick = function () {
|
||||||
|
hintUsed = true;
|
||||||
|
el('hinttext').style.display = 'block';
|
||||||
|
};
|
||||||
|
|
||||||
|
function check(given, expected) {
|
||||||
|
if (norm(given) === norm(expected) && norm(given) !== '') {
|
||||||
|
var stars = (hintUsed || attempts >= 2) ? 1 : (attempts === 1 ? 2 : 3);
|
||||||
|
totalStars += stars;
|
||||||
|
won[idx] = true;
|
||||||
|
beep(true);
|
||||||
|
var f = el('feedback');
|
||||||
|
f.textContent = 'Corect! +' + stars + ' \u2605';
|
||||||
|
f.className = 'feedback good';
|
||||||
|
lettersBar();
|
||||||
|
el('bar').style.width = ((idx + 1) / CFG.puzzles.length * 100) + '%';
|
||||||
|
setTimeout(next, 900);
|
||||||
|
} else {
|
||||||
|
attempts++;
|
||||||
|
beep(false);
|
||||||
|
var fb = el('feedback');
|
||||||
|
fb.textContent = 'Nu e bine, mai incearca!';
|
||||||
|
fb.className = 'feedback bad';
|
||||||
|
var card = document.querySelector('.card');
|
||||||
|
card.classList.remove('shake');
|
||||||
|
void card.offsetWidth;
|
||||||
|
card.classList.add('shake');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function next() {
|
||||||
|
idx++;
|
||||||
|
if (idx < CFG.puzzles.length) { renderPuzzle(); return; }
|
||||||
|
show('sFinal');
|
||||||
|
var max = CFG.puzzles.length * 3;
|
||||||
|
el('finalStars').textContent = totalStars + ' / ' + max + ' \u2605';
|
||||||
|
var word = '';
|
||||||
|
for (var i = 0; i < CFG.puzzles.length; i++) {
|
||||||
|
var L = (CFG.puzzles[i].letter || '').trim();
|
||||||
|
if (L) word += L.toUpperCase();
|
||||||
|
}
|
||||||
|
var bw = el('bigword');
|
||||||
|
bw.innerHTML = '';
|
||||||
|
for (var j = 0; j < word.length; j++) {
|
||||||
|
var s = document.createElement('span');
|
||||||
|
s.textContent = word.charAt(j);
|
||||||
|
s.style.animationDelay = (j * 0.18) + 's';
|
||||||
|
bw.appendChild(s);
|
||||||
|
}
|
||||||
|
var name = CFG.player ? CFG.player + ', ' : '';
|
||||||
|
el('finalMsg').textContent = name ? name + (CFG.finalMessage || '').charAt(0).toLowerCase() + (CFG.finalMessage || '').slice(1) : (CFG.finalMessage || '');
|
||||||
|
confetti();
|
||||||
|
}
|
||||||
|
|
||||||
|
function confetti() {
|
||||||
|
var colors = [CFG.color || '#6d28d9', '#fbbf24', '#34d399', '#60a5fa', '#f472b6'];
|
||||||
|
for (var i = 0; i < 90; i++) {
|
||||||
|
var c = document.createElement('div');
|
||||||
|
c.className = 'confetti';
|
||||||
|
c.style.left = (i * 137 % 100) + 'vw';
|
||||||
|
c.style.background = colors[i % colors.length];
|
||||||
|
c.style.animationDuration = (2.2 + (i * 53 % 18) / 10) + 's';
|
||||||
|
c.style.animationDelay = ((i * 31 % 14) / 10) + 's';
|
||||||
|
document.body.appendChild(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function beep(ok) {
|
||||||
|
try {
|
||||||
|
var ctx = beep.ctx || (beep.ctx = new (window.AudioContext || window.webkitAudioContext)());
|
||||||
|
var t = ctx.currentTime;
|
||||||
|
var freqs = ok ? [523, 784] : [196];
|
||||||
|
freqs.forEach(function (f, k) {
|
||||||
|
var o = ctx.createOscillator(), g = ctx.createGain();
|
||||||
|
o.frequency.value = f; o.type = 'triangle';
|
||||||
|
g.gain.setValueAtTime(0.12, t + k * 0.09);
|
||||||
|
g.gain.exponentialRampToValueAtTime(0.001, t + k * 0.09 + 0.25);
|
||||||
|
o.connect(g); g.connect(ctx.destination);
|
||||||
|
o.start(t + k * 0.09); o.stop(t + k * 0.09 + 0.3);
|
||||||
|
});
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
207
exemplu-point.html
Normal file
207
exemplu-point.html
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ro">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Comoara ascunsa</title>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; min-height: 100vh; background: #0d0820; color: #fff; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; display: flex; flex-direction: column; align-items: center; }
|
||||||
|
h1 { font-size: 19px; margin: 14px 0 4px; }
|
||||||
|
#hud { display: flex; gap: 16px; align-items: center; font-size: 13px; color: #b9aee0; margin-bottom: 4px; flex-wrap: wrap; justify-content: center; padding: 0 10px; }
|
||||||
|
#hudLetters { display: flex; gap: 4px; }
|
||||||
|
#hudLetters span { width: 22px; height: 26px; border-radius: 5px; background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.2); display: flex; align-items: center; justify-content: center; font-weight: 700; color: rgba(255,255,255,.4); font-size: 13px; }
|
||||||
|
#hudLetters span.won { background: var(--accent); color: #fff; border-color: transparent; }
|
||||||
|
.note { font-size: 13px; color: #8d80bb; margin: 2px 0 10px; text-align: center; padding: 0 12px; min-height: 18px; }
|
||||||
|
#stage { width: 100%; max-width: 860px; padding: 0 10px 20px; }
|
||||||
|
svg { width: 100%; height: auto; border-radius: 12px; box-shadow: 0 14px 40px rgba(0,0,0,.5); display: block; }
|
||||||
|
.hot { cursor: pointer; }
|
||||||
|
.hot:hover { filter: brightness(1.35) drop-shadow(0 0 6px rgba(255,255,255,.35)); }
|
||||||
|
.hot.done { opacity: .6; cursor: default; }
|
||||||
|
.hot.done:hover { filter: none; }
|
||||||
|
#door { cursor: pointer; }
|
||||||
|
#door.open { filter: drop-shadow(0 0 12px #22c55e); }
|
||||||
|
|
||||||
|
.confetti { position: fixed; top: -12px; width: 9px; height: 14px; z-index: 99; animation: fall linear forwards; }
|
||||||
|
@keyframes fall { to { transform: translateY(105vh) rotate(720deg); } }
|
||||||
|
.shake { animation: shake .4s ease; }
|
||||||
|
@keyframes shake { 20%,60% { transform: translateX(-8px); } 40%,80% { transform: translateX(8px); } }
|
||||||
|
#mOverlay { display: none; position: fixed; inset: 0; background: rgba(8,4,20,.72); z-index: 20; align-items: center; justify-content: center; padding: 16px; }
|
||||||
|
#mCard { width: 100%; max-width: 460px; background: #221440; border: 1px solid rgba(255,255,255,.18); border-radius: 16px; padding: 22px; color: #fff; font-family: system-ui, sans-serif; box-shadow: 0 18px 50px rgba(0,0,0,.5); }
|
||||||
|
#mCard .mtitle { font-size: 12px; text-transform: uppercase; letter-spacing: .08em; color: #c4b5fd; font-weight: 700; }
|
||||||
|
#mCard .mq { font-size: 18px; line-height: 1.45; margin: 8px 0 16px; }
|
||||||
|
#mCard input[type=text] { width: 100%; font: inherit; font-size: 17px; padding: 10px 12px; border-radius: 10px; border: 1px solid rgba(255,255,255,.25); background: rgba(0,0,0,.3); color: #fff; text-align: center; box-sizing: border-box; }
|
||||||
|
#mCard input:focus { outline: 2px solid var(--accent); border-color: transparent; }
|
||||||
|
#mCard button { font: inherit; cursor: pointer; border: none; border-radius: 10px; padding: 11px 16px; font-weight: 700; background: var(--accent); color: #fff; width: 100%; margin-top: 10px; box-sizing: border-box; }
|
||||||
|
#mCard button.opt { background: rgba(255,255,255,.1); border: 1px solid rgba(255,255,255,.2); font-weight: 600; text-align: left; }
|
||||||
|
#mCard button.opt:hover { background: rgba(255,255,255,.2); }
|
||||||
|
#mCard .mfb { min-height: 20px; text-align: center; font-weight: 700; margin-top: 10px; }
|
||||||
|
#mCard .mfb.bad { color: #fda4af; } #mCard .mfb.good { color: #86efac; }
|
||||||
|
#mCard .mhint { background: none !important; color: rgba(255,255,255,.55) !important; font-weight: 600 !important; font-size: 13px; width: auto !important; display: block; margin: 10px auto 0; }
|
||||||
|
#mCard .mhinttext { background: rgba(255,255,255,.1); border-radius: 9px; padding: 9px 11px; font-size: 14px; margin-top: 8px; display: none; white-space: pre-line; }
|
||||||
|
#mCard .mclose { background: none !important; color: rgba(255,255,255,.4) !important; font-size: 12px; width: auto !important; margin: 6px auto 0; display: block; }
|
||||||
|
#fOverlay { display: none; position: fixed; inset: 0; background: rgba(8,4,20,.88); z-index: 30; align-items: center; justify-content: center; padding: 16px; }
|
||||||
|
#fOverlay .fcard { width: 100%; max-width: 480px; text-align: center; background: #221440; border: 1px solid rgba(255,255,255,.18); border-radius: 18px; padding: 28px; color: #fff; font-family: system-ui, sans-serif; }
|
||||||
|
#fOverlay h1 { margin: 0 0 8px; font-size: 26px; }
|
||||||
|
#fOverlay .fstars { font-size: 26px; letter-spacing: 4px; color: #fbbf24; margin: 6px 0; }
|
||||||
|
#fOverlay .fword { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin: 16px 0; }
|
||||||
|
#fOverlay .fword span { width: 44px; height: 52px; border-radius: 10px; background: var(--accent); display: flex; align-items: center; justify-content: center; font-size: 26px; font-weight: 800; animation: flipin .6s ease backwards; }
|
||||||
|
@keyframes flipin { from { transform: rotateX(90deg); } to { transform: rotateX(0); } }
|
||||||
|
#fOverlay p { color: rgba(255,255,255,.8); line-height: 1.5; }
|
||||||
|
#fOverlay button { font: inherit; cursor: pointer; border: none; border-radius: 10px; padding: 12px 18px; font-weight: 700; background: var(--accent); color: #fff; width: 100%; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Comoara ascunsa</h1>
|
||||||
|
<div id="hud"><span id="hudStep"></span><span id="hudStars"></span><div id="hudLetters"></div></div>
|
||||||
|
<div class="note" id="note">Cerceteaza camera: da click pe obiecte si rezolva-le ca sa deschizi usa.</div>
|
||||||
|
<div id="stage"><svg id="scene" viewBox="0 0 800 500" xmlns="http://www.w3.org/2000/svg"></svg></div>
|
||||||
|
<div id="mOverlay"><div id="mCard">
|
||||||
|
<div class="mtitle" id="mTitle"></div>
|
||||||
|
<div class="mq" id="mQ"></div>
|
||||||
|
<div id="mAnswers"></div>
|
||||||
|
<div class="mfb" id="mFeedback"></div>
|
||||||
|
<button class="mhint" id="mHintBtn">Vreau un indiciu</button>
|
||||||
|
<div class="mhinttext" id="mHintText"></div>
|
||||||
|
<button class="mclose" id="mClose">Pleaca de aici</button>
|
||||||
|
</div></div>
|
||||||
|
<div id="fOverlay"><div class="fcard">
|
||||||
|
<h1>Evadare reusita!</h1>
|
||||||
|
<div class="fstars" id="fStars"></div>
|
||||||
|
<div class="fword" id="fWord"></div>
|
||||||
|
<p id="fMsg"></p>
|
||||||
|
<button id="fAgain">Joaca din nou</button>
|
||||||
|
</div></div>
|
||||||
|
<script>
|
||||||
|
var CFG = {"title":"Comoara ascunsa","player":"","color":"#6d28d9","style":"point","charName":"Alex","story":"O comoara a fost ascunsa, iar singurul drum spre ea trece prin cateva incercari. Rezolva fiecare puzzle ca sa aduni literele cuvantului magic.","finalMessage":"Felicitari! Ai gasit comoara!","puzzles":[{"title":"Incalzirea","type":"free","question":"Cat fac 7 x 8?","answer":"56","tfAnswer":"Adevarat","choices":"","hint":"Tabla inmultirii cu 7.","letter":"D"},{"title":"Adevarat sau fals","type":"tf","question":"Romania are iesire la Marea Neagra.","answer":"","tfAnswer":"Adevarat","choices":"","hint":"","letter":"A"},{"title":"Alege raspunsul","type":"choice","question":"Care este capitala Frantei?","answer":"","tfAnswer":"Adevarat","choices":"*Paris\nLyon\nMarsilia","hint":"Turnul Eiffel.","letter":"R"}]};
|
||||||
|
document.documentElement.style.setProperty('--accent', CFG.color || '#6d28d9');
|
||||||
|
var totalStars = 0;
|
||||||
|
function el(id){ return document.getElementById(id); }
|
||||||
|
function norm(s){ return String(s).trim().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/\s+/g, ' ').replace(/,/g, '.'); }
|
||||||
|
function starsFor(att, hint){ return (hint || att >= 2) ? 1 : (att === 1 ? 2 : 3); }
|
||||||
|
function finalWord(){ var w = ''; for (var i = 0; i < CFG.puzzles.length; i++){ var L = (CFG.puzzles[i].letter || '').trim(); if (L) w += L.toUpperCase(); } return w; }
|
||||||
|
function choiceOpts(p){ return (p.choices || '').split('\n').map(function(l){ return l.trim(); }).filter(Boolean).map(function(o){ return o.charAt(0) === '*' ? o.slice(1).trim() : o; }); }
|
||||||
|
function choiceCorrect(p){ var ls = (p.choices || '').split('\n'); for (var i = 0; i < ls.length; i++){ var l = ls[i].trim(); if (l.charAt(0) === '*') return l.slice(1).trim(); } return ''; }
|
||||||
|
function checkAnswer(p, given){ var exp = p.type === 'tf' ? p.tfAnswer : (p.type === 'choice' ? choiceCorrect(p) : p.answer); return norm(given) !== '' && norm(given) === norm(exp); }
|
||||||
|
function beep(ok){ try { var ctx = beep.ctx || (beep.ctx = new (window.AudioContext || window.webkitAudioContext)()); var t = ctx.currentTime; var fs = ok ? [523, 784] : [196]; fs.forEach(function(f, k){ var o = ctx.createOscillator(), g = ctx.createGain(); o.frequency.value = f; o.type = 'triangle'; g.gain.setValueAtTime(0.12, t + k * 0.09); g.gain.exponentialRampToValueAtTime(0.001, t + k * 0.09 + 0.25); o.connect(g); g.connect(ctx.destination); o.start(t + k * 0.09); o.stop(t + k * 0.09 + 0.3); }); } catch (e) {} }
|
||||||
|
function confetti(){ var colors = [CFG.color || '#6d28d9', '#fbbf24', '#34d399', '#60a5fa', '#f472b6']; for (var i = 0; i < 90; i++){ var c = document.createElement('div'); c.className = 'confetti'; c.style.left = (i * 137 % 100) + 'vw'; c.style.background = colors[i % colors.length]; c.style.animationDuration = (2.2 + (i * 53 % 18) / 10) + 's'; c.style.animationDelay = ((i * 31 % 14) / 10) + 's'; document.body.appendChild(c); } }
|
||||||
|
var N = CFG.puzzles.length, solvedFlags = [], solvedCount = 0;
|
||||||
|
|
||||||
|
var POOL = [
|
||||||
|
{ name: 'Ceasul', x: 308, y: 62, svg: '<circle cx="32" cy="32" r="30" fill="#caa856"/><circle cx="32" cy="32" r="24" fill="#f4ecd8"/><line x1="32" y1="32" x2="32" y2="16" stroke="#222" stroke-width="3"/><line x1="32" y1="32" x2="43" y2="36" stroke="#222" stroke-width="3"/><circle cx="32" cy="32" r="2.5" fill="#222"/>' },
|
||||||
|
{ name: 'Tabloul', x: 108, y: 84, svg: '<rect width="84" height="64" fill="#8a5a2b"/><rect x="6" y="6" width="72" height="52" fill="#b7d3e8"/><polygon points="10,52 34,24 50,44 60,32 74,52" fill="#4f7a4f"/><circle cx="62" cy="16" r="6" fill="#f7d774"/>' },
|
||||||
|
{ name: 'Sertarul biroului', x: 64, y: 300, svg: '<rect width="150" height="14" fill="#7a4a22"/><rect x="6" y="14" width="138" height="46" fill="#925a2c"/><rect x="20" y="22" width="110" height="28" fill="#7a4a22"/><circle cx="75" cy="36" r="4" fill="#f3cf6d"/><rect x="10" y="60" width="10" height="34" fill="#7a4a22"/><rect x="130" y="60" width="10" height="34" fill="#7a4a22"/>' },
|
||||||
|
{ name: 'Dulapul', x: 232, y: 228, svg: '<rect width="92" height="142" fill="#8a5a2b"/><rect x="5" y="5" width="38" height="132" fill="#a06a35"/><rect x="49" y="5" width="38" height="132" fill="#a06a35"/><circle cx="38" cy="72" r="3.5" fill="#f3cf6d"/><circle cx="54" cy="72" r="3.5" fill="#f3cf6d"/>' },
|
||||||
|
{ name: 'Fereastra', x: 424, y: 62, svg: '<rect width="96" height="120" fill="#6b7f9e"/><rect x="6" y="6" width="84" height="108" fill="#101d3a"/><line x1="48" y1="6" x2="48" y2="114" stroke="#6b7f9e" stroke-width="5"/><line x1="6" y1="60" x2="90" y2="60" stroke="#6b7f9e" stroke-width="5"/><circle cx="68" cy="32" r="11" fill="#f4f1de"/>' },
|
||||||
|
{ name: 'Raftul cu carti', x: 558, y: 78, svg: '<rect y="34" width="120" height="8" fill="#7a4a22"/><rect x="6" y="6" width="13" height="28" fill="#b54a4a"/><rect x="21" y="2" width="13" height="32" fill="#4a7ab5"/><rect x="36" y="8" width="13" height="26" fill="#54a05e"/><rect x="51" y="4" width="13" height="30" fill="#c2a23e"/><rect x="66" y="9" width="13" height="25" fill="#9a5ab5"/><rect x="81" y="5" width="13" height="29" fill="#b5764a"/><rect x="96" y="8" width="13" height="26" fill="#5aa0b5"/>' },
|
||||||
|
{ name: 'Cutia', x: 404, y: 330, svg: '<rect width="66" height="50" fill="#925a2c"/><line x1="0" y1="0" x2="66" y2="50" stroke="#7a4a22" stroke-width="5"/><line x1="66" y1="0" x2="0" y2="50" stroke="#7a4a22" stroke-width="5"/><rect width="66" height="50" fill="none" stroke="#7a4a22" stroke-width="6"/>' },
|
||||||
|
{ name: 'Lampa', x: 516, y: 252, svg: '<polygon points="14,0 50,0 60,34 4,34" fill="#d9a23e"/><rect x="29" y="34" width="6" height="78" fill="#555"/><rect x="12" y="112" width="40" height="8" rx="3" fill="#555"/><circle cx="32" cy="17" r="9" fill="#ffe9a8" opacity=".8"/>' },
|
||||||
|
{ name: 'Seiful', x: 596, y: 300, svg: '<rect width="74" height="74" rx="6" fill="#5b6470"/><rect x="7" y="7" width="60" height="60" rx="4" fill="#434b55"/><circle cx="37" cy="37" r="14" fill="#5b6470"/><circle cx="37" cy="37" r="9" fill="#2c3138"/><line x1="37" y1="28" x2="37" y2="37" stroke="#d4d4d4" stroke-width="2.5"/>' },
|
||||||
|
{ name: 'Covorul', x: 250, y: 432, svg: '<ellipse cx="110" cy="26" rx="110" ry="26" fill="#7a3b56"/><ellipse cx="110" cy="26" rx="80" ry="17" fill="#94506c"/><ellipse cx="110" cy="26" rx="46" ry="9" fill="#7a3b56"/>' }
|
||||||
|
];
|
||||||
|
function crate(i){ return { name: 'Lada ' + (i + 1), x: 50 + ((i - 10) % 8) * 86, y: 408, svg: POOL[6].svg }; }
|
||||||
|
|
||||||
|
var base = '<rect width="800" height="380" fill="#3b2a63"/><rect y="380" width="800" height="120" fill="#241a3f"/><rect y="372" width="800" height="8" fill="#1c1336"/>'
|
||||||
|
+ '<g id="door"><rect x="694" y="148" width="86" height="232" fill="#6b4226"/><rect x="702" y="156" width="70" height="216" fill="#8a5a2b"/><circle cx="712" cy="266" r="5" fill="#f3cf6d"/><g id="lock"><rect x="730" y="250" width="26" height="22" rx="4" fill="#caa856"/><path d="M735 250 v-7 a8 8 0 0 1 16 0 v7" fill="none" stroke="#caa856" stroke-width="4"/></g></g>';
|
||||||
|
var objs = '';
|
||||||
|
for (var i = 0; i < N; i++) {
|
||||||
|
var o = POOL[i] || crate(i);
|
||||||
|
objs += '<g class="hot" data-i="' + i + '" transform="translate(' + o.x + ',' + o.y + ')">' + o.svg + '<title>' + o.name + '</title></g>';
|
||||||
|
}
|
||||||
|
el('scene').innerHTML = base + objs;
|
||||||
|
|
||||||
|
el('scene').addEventListener('click', function(e){
|
||||||
|
var t = e.target;
|
||||||
|
var g = t.closest ? t.closest('g.hot') : null;
|
||||||
|
if (g) {
|
||||||
|
if (!g.classList.contains('done')) openPuzzle(+g.getAttribute('data-i'), onSolved);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var door = t.closest ? t.closest('#door') : null;
|
||||||
|
if (door) {
|
||||||
|
if (solvedCount >= N) { showFinal(); }
|
||||||
|
else { beep(false); el('note').textContent = 'Usa e incuiata! Mai ai ' + (N - solvedCount) + ' obiecte de cercetat.'; }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onSolved(i){
|
||||||
|
solvedFlags[i] = true; solvedCount++;
|
||||||
|
var g = document.querySelector('g.hot[data-i="' + i + '"]');
|
||||||
|
g.classList.add('done');
|
||||||
|
var L = (CFG.puzzles[i].letter || '').trim();
|
||||||
|
g.innerHTML += '<circle cx="0" cy="0" r="13" fill="#16a34a"/><text x="0" y="5" text-anchor="middle" font-size="14" font-weight="700" fill="#fff">' + (L ? L.toUpperCase() : '\u2713') + '</text>';
|
||||||
|
updateHud();
|
||||||
|
if (solvedCount >= N) {
|
||||||
|
el('door').classList.add('open');
|
||||||
|
el('note').textContent = 'Toate obiectele rezolvate! Da click pe usa ca sa evadezi.';
|
||||||
|
beep(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateHud(){
|
||||||
|
el('hudStep').textContent = 'Obiecte: ' + solvedCount + '/' + N;
|
||||||
|
el('hudStars').textContent = totalStars + ' \u2605';
|
||||||
|
var hb = el('hudLetters'); hb.innerHTML = '';
|
||||||
|
for (var j = 0; j < N; j++) {
|
||||||
|
var L = (CFG.puzzles[j].letter || '').trim();
|
||||||
|
if (!L) continue;
|
||||||
|
var s = document.createElement('span');
|
||||||
|
if (solvedFlags[j]) { s.textContent = L.toUpperCase(); s.className = 'won'; }
|
||||||
|
else s.textContent = '?';
|
||||||
|
hb.appendChild(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var mIdx = -1, mAtt = 0, mHint = false, mCb = null;
|
||||||
|
el('mHintBtn').onclick = function(){ mHint = true; el('mHintText').style.display = 'block'; };
|
||||||
|
el('mClose').onclick = function(){ el('mOverlay').style.display = 'none'; };
|
||||||
|
function modalOpen(){ return el('mOverlay').style.display === 'flex'; }
|
||||||
|
function openPuzzle(i, cb){
|
||||||
|
mIdx = i; mAtt = 0; mHint = false; mCb = cb;
|
||||||
|
var p = CFG.puzzles[i];
|
||||||
|
el('mTitle').textContent = p.title || ('Puzzle ' + (i + 1));
|
||||||
|
el('mQ').textContent = p.question;
|
||||||
|
el('mFeedback').textContent = ''; el('mFeedback').className = 'mfb';
|
||||||
|
el('mHintText').style.display = 'none'; el('mHintText').textContent = p.hint || '';
|
||||||
|
el('mHintBtn').style.display = p.hint ? '' : 'none';
|
||||||
|
var box = el('mAnswers'); box.innerHTML = '';
|
||||||
|
if (p.type === 'free') {
|
||||||
|
var inp = document.createElement('input'); inp.type = 'text'; inp.placeholder = 'Scrie raspunsul...'; inp.autocomplete = 'off';
|
||||||
|
var b = document.createElement('button'); b.textContent = 'Verifica';
|
||||||
|
b.onclick = function(){ mCheck(inp.value); };
|
||||||
|
inp.onkeydown = function(e){ e.stopPropagation(); if (e.key === 'Enter') b.click(); };
|
||||||
|
box.appendChild(inp); box.appendChild(b);
|
||||||
|
setTimeout(function(){ inp.focus(); }, 60);
|
||||||
|
} else if (p.type === 'tf') {
|
||||||
|
['Adevarat', 'Fals'].forEach(function(v){ var b = document.createElement('button'); b.className = 'opt'; b.textContent = v; b.onclick = function(){ mCheck(v); }; box.appendChild(b); });
|
||||||
|
} else {
|
||||||
|
choiceOpts(p).forEach(function(o){ var b = document.createElement('button'); b.className = 'opt'; b.textContent = o; b.onclick = function(){ mCheck(o); }; box.appendChild(b); });
|
||||||
|
}
|
||||||
|
el('mOverlay').style.display = 'flex';
|
||||||
|
}
|
||||||
|
function mCheck(given){
|
||||||
|
var p = CFG.puzzles[mIdx];
|
||||||
|
if (checkAnswer(p, given)) {
|
||||||
|
var s = starsFor(mAtt, mHint);
|
||||||
|
totalStars += s; beep(true);
|
||||||
|
el('mFeedback').textContent = 'Corect! +' + s + ' \u2605'; el('mFeedback').className = 'mfb good';
|
||||||
|
setTimeout(function(){ el('mOverlay').style.display = 'none'; var cb = mCb; mCb = null; if (cb) cb(mIdx, s); }, 750);
|
||||||
|
} else {
|
||||||
|
mAtt++; beep(false);
|
||||||
|
el('mFeedback').textContent = 'Nu e bine, mai incearca!'; el('mFeedback').className = 'mfb bad';
|
||||||
|
var c = el('mCard'); c.classList.remove('shake'); void c.offsetWidth; c.classList.add('shake');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function showFinal(){
|
||||||
|
el('fStars').textContent = totalStars + ' / ' + (CFG.puzzles.length * 3) + ' \u2605';
|
||||||
|
var w = finalWord(); var bw = el('fWord'); bw.innerHTML = '';
|
||||||
|
for (var j = 0; j < w.length; j++){ var s = document.createElement('span'); s.textContent = w.charAt(j); s.style.animationDelay = (j * 0.18) + 's'; bw.appendChild(s); }
|
||||||
|
var msg = CFG.finalMessage || '';
|
||||||
|
el('fMsg').textContent = CFG.player ? CFG.player + ', ' + msg.charAt(0).toLowerCase() + msg.slice(1) : msg;
|
||||||
|
el('fOverlay').style.display = 'flex';
|
||||||
|
beep(true); confetti();
|
||||||
|
}
|
||||||
|
el('fAgain').onclick = function(){ location.reload(); };
|
||||||
|
updateHud();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
129
exemplu-terminal.html
Normal file
129
exemplu-terminal.html
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ro">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Comoara ascunsa</title>
|
||||||
|
<style>
|
||||||
|
* { box-sizing: border-box; }
|
||||||
|
body { margin: 0; min-height: 100vh; background: #04130a; color: #39ff6e; font-family: "Courier New", ui-monospace, monospace; }
|
||||||
|
#crt { max-width: 760px; margin: 0 auto; padding: 20px 16px 80px; }
|
||||||
|
.line { white-space: pre-wrap; word-break: break-word; line-height: 1.45; font-size: 15px; text-shadow: 0 0 7px rgba(57,255,110,.5); }
|
||||||
|
.line.dim { color: #1f9c4a; }
|
||||||
|
.line.warn { color: #ffd24a; text-shadow: 0 0 7px rgba(255,210,74,.45); }
|
||||||
|
.line.bad { color: #ff6b6b; text-shadow: 0 0 7px rgba(255,107,107,.45); }
|
||||||
|
.line.ok { color: #9dffc0; }
|
||||||
|
#inline { display: flex; gap: 8px; align-items: baseline; font-size: 15px; text-shadow: 0 0 7px rgba(57,255,110,.5); }
|
||||||
|
#cmd { flex: 1; background: none; border: none; outline: none; color: inherit; font: inherit; text-shadow: inherit; caret-color: #39ff6e; }
|
||||||
|
.scan { position: fixed; inset: 0; pointer-events: none; background: repeating-linear-gradient(0deg, rgba(0,0,0,.28) 0 1px, transparent 1px 3px); }
|
||||||
|
.vign { position: fixed; inset: 0; pointer-events: none; background: radial-gradient(ellipse at center, transparent 55%, rgba(0,0,0,.6)); }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="scan"></div><div class="vign"></div>
|
||||||
|
<div id="crt"><div id="out"></div>
|
||||||
|
<div id="inline"><span>></span><input id="cmd" autocomplete="off" autofocus spellcheck="false"></div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
var CFG = {"title":"Comoara ascunsa","player":"","color":"#6d28d9","style":"terminal","charName":"Alex","story":"O comoara a fost ascunsa, iar singurul drum spre ea trece prin cateva incercari. Rezolva fiecare puzzle ca sa aduni literele cuvantului magic.","finalMessage":"Felicitari! Ai gasit comoara!","puzzles":[{"title":"Incalzirea","type":"free","question":"Cat fac 7 x 8?","answer":"56","tfAnswer":"Adevarat","choices":"","hint":"Tabla inmultirii cu 7.","letter":"D"},{"title":"Adevarat sau fals","type":"tf","question":"Romania are iesire la Marea Neagra.","answer":"","tfAnswer":"Adevarat","choices":"","hint":"","letter":"A"},{"title":"Alege raspunsul","type":"choice","question":"Care este capitala Frantei?","answer":"","tfAnswer":"Adevarat","choices":"*Paris\nLyon\nMarsilia","hint":"Turnul Eiffel.","letter":"R"}]};
|
||||||
|
document.documentElement.style.setProperty('--accent', CFG.color || '#6d28d9');
|
||||||
|
var totalStars = 0;
|
||||||
|
function el(id){ return document.getElementById(id); }
|
||||||
|
function norm(s){ return String(s).trim().toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/\s+/g, ' ').replace(/,/g, '.'); }
|
||||||
|
function starsFor(att, hint){ return (hint || att >= 2) ? 1 : (att === 1 ? 2 : 3); }
|
||||||
|
function finalWord(){ var w = ''; for (var i = 0; i < CFG.puzzles.length; i++){ var L = (CFG.puzzles[i].letter || '').trim(); if (L) w += L.toUpperCase(); } return w; }
|
||||||
|
function choiceOpts(p){ return (p.choices || '').split('\n').map(function(l){ return l.trim(); }).filter(Boolean).map(function(o){ return o.charAt(0) === '*' ? o.slice(1).trim() : o; }); }
|
||||||
|
function choiceCorrect(p){ var ls = (p.choices || '').split('\n'); for (var i = 0; i < ls.length; i++){ var l = ls[i].trim(); if (l.charAt(0) === '*') return l.slice(1).trim(); } return ''; }
|
||||||
|
function checkAnswer(p, given){ var exp = p.type === 'tf' ? p.tfAnswer : (p.type === 'choice' ? choiceCorrect(p) : p.answer); return norm(given) !== '' && norm(given) === norm(exp); }
|
||||||
|
function beep(ok){ try { var ctx = beep.ctx || (beep.ctx = new (window.AudioContext || window.webkitAudioContext)()); var t = ctx.currentTime; var fs = ok ? [523, 784] : [196]; fs.forEach(function(f, k){ var o = ctx.createOscillator(), g = ctx.createGain(); o.frequency.value = f; o.type = 'triangle'; g.gain.setValueAtTime(0.12, t + k * 0.09); g.gain.exponentialRampToValueAtTime(0.001, t + k * 0.09 + 0.25); o.connect(g); g.connect(ctx.destination); o.start(t + k * 0.09); o.stop(t + k * 0.09 + 0.3); }); } catch (e) {} }
|
||||||
|
function confetti(){ var colors = [CFG.color || '#6d28d9', '#fbbf24', '#34d399', '#60a5fa', '#f472b6']; for (var i = 0; i < 90; i++){ var c = document.createElement('div'); c.className = 'confetti'; c.style.left = (i * 137 % 100) + 'vw'; c.style.background = colors[i % colors.length]; c.style.animationDuration = (2.2 + (i * 53 % 18) / 10) + 's'; c.style.animationDelay = ((i * 31 % 14) / 10) + 's'; document.body.appendChild(c); } }
|
||||||
|
var idx = -1, attempts = 0, hintUsed = false, done = false;
|
||||||
|
var solved = [];
|
||||||
|
var out = el('out'), cmd = el('cmd');
|
||||||
|
document.body.addEventListener('click', function(){ cmd.focus(); });
|
||||||
|
|
||||||
|
var queue = [], typing = false;
|
||||||
|
function say(lines, cls, cb){ queue.push({ lines: lines.slice(), cls: cls || '', cb: cb }); pump(); }
|
||||||
|
function pump(){
|
||||||
|
if (typing) return;
|
||||||
|
var job = queue[0];
|
||||||
|
if (!job) return;
|
||||||
|
if (!job.lines.length) { queue.shift(); if (job.cb) job.cb(); pump(); return; }
|
||||||
|
var text = job.lines.shift();
|
||||||
|
typing = true;
|
||||||
|
var d = document.createElement('div');
|
||||||
|
d.className = 'line ' + job.cls;
|
||||||
|
out.appendChild(d);
|
||||||
|
var i = 0;
|
||||||
|
(function tick(){
|
||||||
|
d.textContent = text.slice(0, i);
|
||||||
|
i += 3;
|
||||||
|
window.scrollTo(0, document.body.scrollHeight);
|
||||||
|
if (i <= text.length + 2) setTimeout(tick, 11);
|
||||||
|
else { d.textContent = text; typing = false; pump(); }
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
function echo(text, cls){ var d = document.createElement('div'); d.className = 'line ' + (cls || ''); d.textContent = text; out.appendChild(d); window.scrollTo(0, document.body.scrollHeight); }
|
||||||
|
|
||||||
|
function collected(){ var w = ''; for (var i = 0; i < CFG.puzzles.length; i++){ var L = (CFG.puzzles[i].letter || '').trim(); if (L) w += solved[i] ? L.toUpperCase() + ' ' : '_ '; } return w.trim() || '(niciuna)'; }
|
||||||
|
|
||||||
|
var bar = '==============================================';
|
||||||
|
say([bar, ' ' + CFG.title.toUpperCase(), bar, ' ', (CFG.player ? CFG.player + ', ' : '') + CFG.story, ' ', 'Comenzi: INDICIU, LITERE, AJUTOR. In rest, scrie raspunsul si apasa Enter.'], '', nextPuzzle);
|
||||||
|
|
||||||
|
function nextPuzzle(){
|
||||||
|
idx++; attempts = 0; hintUsed = false;
|
||||||
|
if (idx >= CFG.puzzles.length) return finale();
|
||||||
|
var p = CFG.puzzles[idx];
|
||||||
|
var lines = [' ', '----------------------------------------------', '[' + (idx + 1) + '/' + CFG.puzzles.length + '] ' + (p.title || 'OBSTACOL').toUpperCase(), p.question];
|
||||||
|
if (p.type === 'tf') lines.push('(raspunde: ADEVARAT sau FALS)');
|
||||||
|
if (p.type === 'choice') { var o = choiceOpts(p); for (var i = 0; i < o.length; i++) lines.push(' ' + (i + 1) + ') ' + o[i]); }
|
||||||
|
say(lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
function finale(){
|
||||||
|
done = true;
|
||||||
|
var w = finalWord().split('').join(' ');
|
||||||
|
var lines = [' ', bar, ' E V A D A R E R E U S I T A', bar, 'Stele: ' + totalStars + ' / ' + (CFG.puzzles.length * 3)];
|
||||||
|
if (w) lines.push('Cuvantul magic: ' + w);
|
||||||
|
lines.push((CFG.player ? CFG.player + ', ' : '') + CFG.finalMessage);
|
||||||
|
lines.push(' ');
|
||||||
|
lines.push('Scrie RESTART pentru a juca din nou.');
|
||||||
|
say(lines, 'ok');
|
||||||
|
beep(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.addEventListener('keydown', function(e){
|
||||||
|
if (e.key !== 'Enter') return;
|
||||||
|
var v = cmd.value.trim();
|
||||||
|
cmd.value = '';
|
||||||
|
if (!v) return;
|
||||||
|
echo('> ' + v, 'dim');
|
||||||
|
var n = norm(v);
|
||||||
|
if (done) { if (n === 'restart') location.reload(); else echo('Scrie RESTART pentru a juca din nou.', 'dim'); return; }
|
||||||
|
if (n === 'ajutor' || n === 'help') { say(['INDICIU = primesti un ajutor (dar pierzi stele)', 'LITERE = literele adunate pana acum', 'Orice altceva e tratat ca raspuns.']); return; }
|
||||||
|
if (n === 'litere') { say(['Litere adunate: ' + collected()]); return; }
|
||||||
|
var p = CFG.puzzles[idx];
|
||||||
|
if (!p) return;
|
||||||
|
if (n === 'indiciu' || n === 'hint') {
|
||||||
|
if (p.hint) { hintUsed = true; say(['INDICIU: ' + p.hint], 'warn'); }
|
||||||
|
else say(['Nu exista niciun indiciu aici.'], 'warn');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var given = v;
|
||||||
|
if (p.type === 'choice') { var num = parseInt(v, 10); var o = choiceOpts(p); if (num >= 1 && o[num - 1]) given = o[num - 1]; }
|
||||||
|
if (p.type === 'tf') { if (n === 'a' || n === 'adevarat') given = 'Adevarat'; if (n === 'f' || n === 'fals') given = 'Fals'; }
|
||||||
|
if (checkAnswer(p, given)) {
|
||||||
|
var s = starsFor(attempts, hintUsed);
|
||||||
|
totalStars += s; solved[idx] = true; beep(true);
|
||||||
|
var ls = ['>> ACCES PERMIS. +' + s + ' stele (total ' + totalStars + ')'];
|
||||||
|
var L = (p.letter || '').trim();
|
||||||
|
if (L) ls.push('>> AI GASIT LITERA: ' + L.toUpperCase() + ' [' + collected() + ']');
|
||||||
|
say(ls, 'ok', nextPuzzle);
|
||||||
|
} else {
|
||||||
|
attempts++; beep(false);
|
||||||
|
say(['>> ACCES RESPINS. Mai incearca.'], 'bad');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
30
index.html
Normal file
30
index.html
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="ro">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Escape Room Builder - demo</title>
|
||||||
|
<style>
|
||||||
|
body { margin: 0; min-height: 100vh; background: #14092e; color: #fff; font-family: system-ui, sans-serif; display: flex; align-items: center; justify-content: center; padding: 20px; box-sizing: border-box; }
|
||||||
|
.card { max-width: 520px; width: 100%; }
|
||||||
|
h1 { font-size: 24px; margin: 0 0 4px; }
|
||||||
|
p { color: rgba(255,255,255,.65); margin: 0 0 20px; }
|
||||||
|
a { display: block; background: rgba(255,255,255,.07); border: 1px solid rgba(255,255,255,.15); border-radius: 12px; padding: 14px 18px; margin-bottom: 10px; color: #fff; text-decoration: none; font-weight: 600; }
|
||||||
|
a:hover { border-color: #8b5cf6; background: rgba(139,92,246,.15); }
|
||||||
|
a span { display: block; font-weight: 400; font-size: 13px; color: rgba(255,255,255,.55); margin-top: 2px; }
|
||||||
|
a.builder { background: #6d28d9; border-color: #6d28d9; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="card">
|
||||||
|
<h1>Escape Room Builder</h1>
|
||||||
|
<p>Builder-ul + cate un joc demo exportat in fiecare stil.</p>
|
||||||
|
<a class="builder" href="escape-builder.html">Builder <span>editor + preview live; schimba "Stil joc" si vezi transformarea pe loc</span></a>
|
||||||
|
<a href="exemplu-clasic.html">Clasic (quiz) <span>carduri secventiale cu progres si litere</span></a>
|
||||||
|
<a href="exemplu-terminal.html">Terminal retro <span>text adventure CRT; scrie raspunsul, INDICIU, LITERE</span></a>
|
||||||
|
<a href="exemplu-arcade.html">Arcade pixel <span>sageti / WASD; usi incuiate, cufar final</span></a>
|
||||||
|
<a href="exemplu-chat.html">Story chat <span>personajul iti scrie; raspunzi din composer</span></a>
|
||||||
|
<a href="exemplu-point.html">Point-and-click <span>camera ilustrata; click pe obiecte, apoi pe usa</span></a>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Reference in New Issue
Block a user