-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
455 lines (403 loc) Β· 18 KB
/
Copy pathscript.js
File metadata and controls
455 lines (403 loc) Β· 18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
const bgm = document.getElementById("bgm");
bgm.volume = 0.2; // subtle cinematic volume
function startBGM() {
bgm.play().catch(() => { });
document.removeEventListener("click", startBGM);
document.removeEventListener("keydown", startBGM);
}
// Start on first interaction (invisible)
document.addEventListener("click", startBGM);
document.addEventListener("keydown", startBGM);
function toggleAudio() {
bgm.muted = !bgm.muted;
const volumePath = document.getElementById("volume-path");
const speakerPath = document.getElementById("speaker-path");
if (bgm.muted) {
// Muted state: remove volume waves and add an 'X' or cross
volumePath.style.display = "none";
// Optionally update speaker path or add a line for 'muted'
speakerPath.setAttribute("d", "M11 5L6 9H2v6h4l5 4V5z M23 9l-6 6 M17 9l6 6");
} else {
// Unmuted state: restore waves and basic speaker
volumePath.style.display = "block";
speakerPath.setAttribute("d", "M11 5L6 9H2v6h4l5 4V5z");
}
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// STATE & PERSISTENCE
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
const STATE_KEY = 'thanos_ctf_state';
function loadState() {
try {
const s = localStorage.getItem(STATE_KEY);
return s ? JSON.parse(s) : { stage: 0, videosPlayed: {}, stonesUnlocked: [] };
} catch (e) { return { stage: 0, videosPlayed: {}, stonesUnlocked: [] }; }
}
function saveState(state) {
localStorage.setItem(STATE_KEY, JSON.stringify(state));
}
let G = loadState();
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// PARTICLES
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
(function spawnParticles() {
const c = document.getElementById('particles');
const colors = ['#c9a84c', '#8b6914', '#e8630a', '#7c1ac4', '#1a6bc4'];
for (let i = 0; i < 60; i++) {
const p = document.createElement('div');
p.className = 'particle';
const size = Math.random() * 4 + 1;
p.style.cssText = `
width:${size}px;height:${size}px;
left:${Math.random() * 100}%;
background:${colors[Math.floor(Math.random() * colors.length)]};
animation-duration:${8 + Math.random() * 12}s;
animation-delay:${Math.random() * 10}s;
`;
c.appendChild(p);
}
})();
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// TYPEWRITER
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function typewriterText(text, callback) {
const activeStage = document.querySelector('.stage.visible');
if (!activeStage) {
if (callback) callback();
return;
}
const el = activeStage.querySelector('.clue-text');
if (!el) {
if (callback) callback();
return;
}
// If empty text, clear and return
if (!text) {
el.style.opacity = '0';
setTimeout(() => {
el.innerHTML = '';
el.style.opacity = '1';
if (callback) callback();
}, 500);
return;
}
el.innerHTML = '';
el.style.opacity = '1';
let i = 0;
const cursor = document.createElement('span');
cursor.className = 'thanos-cursor';
el.appendChild(cursor);
function type() {
if (i < text.length) {
cursor.before(document.createTextNode(text[i]));
i++;
setTimeout(type, 45 + Math.random() * 30);
} else {
cursor.remove();
el.style.textShadow = "0 0 15px #c9a84c";
if (callback) callback();
}
}
type();
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// VIDEO PLAYER
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function playVideo(src, callback, forcePlay) {
// If already played, skip (unless forced)
if (!forcePlay && G.videosPlayed[src]) {
if (callback) callback();
return;
}
document.body.classList.add('video-playing');
const vc = document.getElementById('video-container');
const v = document.getElementById('main-video');
v.src = src;
vc.classList.add('active');
v.classList.remove('clear');
v.play().catch(() => {
// autoplay blocked β try muted
v.muted = true;
v.play();
});
v.addEventListener('play', () => {
setTimeout(() => v.classList.add('clear'), 100);
}, { once: true });
v.addEventListener('ended', () => {
document.body.classList.remove('video-playing');
vc.classList.remove('active');
v.src = '';
v.classList.remove('clear');
G.videosPlayed[src] = true;
saveState(G);
if (callback) callback();
}, { once: true });
}
function preloadVideos(urls) {
if (!urls) return;
urls.forEach(url => {
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = url;
link.as = 'video';
document.head.appendChild(link);
});
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// LEVEL TITLE SCREEN
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function showLevelTitle(num, name, callback) {
const ls = document.getElementById('level-title-screen');
document.getElementById('level-num').textContent = `STONE ${num} OF 4`;
document.getElementById('level-name').textContent = name;
// reset animation
const ln = document.getElementById('level-name');
const ld = document.getElementById('level-divider');
ln.style.animation = 'none'; void ln.offsetHeight;
ln.style.animation = 'level-appear 1s ease forwards';
ld.style.animation = 'none'; void ld.offsetHeight;
ld.style.animation = 'expand-line 1.5s ease forwards .5s';
ls.classList.add('active');
setTimeout(() => {
ls.classList.remove('active');
if (callback) callback();
}, 2500);
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// STONE UNLOCK
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
const stoneOrder = ['space', 'mind', 'reality', 'power'];
function unlockStone(n) {
const stone = stoneOrder[n];
if (!G.stonesUnlocked.includes(stone)) {
G.stonesUnlocked.push(stone);
saveState(G);
}
const el = document.querySelector(`.stone[data-stone="${stone}"]`);
if (el) {
el.classList.remove('locked');
el.classList.add('unlocked');
el.classList.add('active-anim');
setTimeout(() => el.classList.remove('active-anim'), 700);
}
}
function restoreStones() {
G.stonesUnlocked.forEach(s => {
const el = document.querySelector(`.stone[data-stone="${s}"]`);
if (el) { el.classList.remove('locked'); el.classList.add('unlocked'); }
});
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// SHOW/HIDE STAGES
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function showStage(n) {
// Clear all clues instantly to prevent race conditions
document.querySelectorAll('.clue-text').forEach(el => {
el.innerHTML = '';
el.style.opacity = '1';
});
document.querySelectorAll('.stage').forEach(s => s.classList.remove('visible'));
if (n > 0) document.getElementById(`stage${n}`).classList.add('visible');
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// STAGE 1
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function startStage1() {
G.stage = 1;
saveState(G);
showStage(0);
showLevelTitle(1, 'Illusion of Sight', () => {
playVideo('assets/videos/intro1.mp4', () => {
showStage(1);
typewriterText(
"Mortals believe what is visible.They trust the surface.They obey what appears locked.But reality does not answer to appearances.It answers to those who dare to look beneath."
);
// Preload victory 1 and intro 2 while user plays
preloadVideos(['assets/videos/vic1.mp4', 'assets/videos/intro2.mp4']);
});
});
}
// Stage 1: btn is disabled β user must remove 'disabled' via DevTools
document.getElementById('btn-stage1').addEventListener('click', () => {
solveStage1();
});
function solveStage1() {
unlockStone(0);
showStage(0);
playVideo('assets/videos/vic1.mp4', () => {
showLevelTitle(2, 'In Search of Identity', () => {
playVideo('assets/videos/intro2.mp4', startStage2);
});
});
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// STAGE 2
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function startStage2() {
G.stage = 2; saveState(G);
showStage(2);
typewriterText(
"Reality bends to those with power.Become more than you are.Names are given.Roles are assigned.Power is inherited.But identity⦠Identity can be rewritten."
);
// Preload victory 2 and intro 3
preloadVideos(['assets/videos/vic2.mp4', 'assets/videos/intro3.mp4']);
}
function loginStage2() {
const payload = { username: "player", role: "user" };
const token = btoa(JSON.stringify(payload));
localStorage.setItem('jwt', token);
const td = document.getElementById('token-display');
td.style.display = 'block';
td.textContent = 'Token: ' + token;
}
function checkAdmin() {
const token = localStorage.getItem('jwt');
const res = document.getElementById('admin-result');
if (!token) { res.textContent = 'No token found. Generate one first.'; res.className = 'result-box error'; return; }
try {
const decoded = JSON.parse(atob(token));
if (decoded.role === 'Thanos') {
res.textContent = '⬑ Admin Access Granted! Welcome, Thanos.';
res.className = 'result-box success';
document.getElementById('admin-panel').style.display = 'block';
setTimeout(solveStage2, 1500);
} else {
res.textContent = `Access Denied. Role "${decoded.role}" is insufficient. Only Thanos may pass.`;
res.className = 'result-box error';
}
} catch (e) {
res.textContent = 'Token decode failed. Ensure base64 JSON is valid.';
res.className = 'result-box error';
}
}
function solveStage2() {
unlockStone(1);
showStage(0);
playVideo('assets/videos/vic2.mp4', () => {
showLevelTitle(3, 'The Weight of Authority', () => {
playVideo('assets/videos/intro3.mp4', startStage3);
});
});
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// STAGE 3
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function startStage3() {
G.stage = 3; saveState(G);
showStage(3);
typewriterText(
"Power without understanding is meaningless.Seek the hidden endpoint.The universe keeps its state.Change the state⦠and you change destiny"
);
// Preload victory 3 and intro 4
preloadVideos(['assets/videos/vic3.mp4', 'assets/videos/intro4.mp4']);
}
function callRealityAPI() {
const res = document.getElementById('api-result');
if (localStorage.getItem('reality_level') === '3000') {
res.textContent = '⬑ Reality Shift Complete! The stone acknowledges you.';
res.className = 'result-box success';
setTimeout(solveStage3, 1500);
} else {
res.textContent = 'Reality state incomplete.';
res.className = 'result-box error';
}
}
function solveStage3() {
unlockStone(2);
showStage(0);
playVideo('assets/videos/vic3.mp4', () => {
showLevelTitle(4, 'The Illusion of Truth', () => {
playVideo('assets/videos/intro4.mp4', startStage4);
});
});
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// STAGE 4
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function startStage4() {
G.stage = 4; saveState(G);
showStage(4);
typewriterText(
"You have altered what you see, and who you are.Now alter what is true.The final stone awaits those who see through the last illusion.Nothing is fixed.Everything is subject to the will.The universe is a canvas.And you are the brush."
);
// Preload final victory
preloadVideos(['assets/videos/final_vic4.mp4']);
}
function triggerFinalVictory() {
showStage(0);
playVideo('assets/videos/final_vic4.mp4', () => {
unlockStone(3);
// Final end screen with flag β NO gauntlet flag shown in DOM before this
G.stage = 5; saveState(G);
showEndScreen();
});
}
function showEndScreen() {
const es = document.getElementById('end-screen');
es.classList.add('active');
// Dynamically create flag element (Obfuscated from initial DOM)
// Base64 of GAUNTLET{reality_rewritten} is R0FVTlRMRVR7cmVhbGl0eV9yZXdyaXR0ZW59
const flag = document.createElement('div');
flag.id = 'end-flag';
flag.textContent = atob('R0FVTlRMRVR7cmVhbGl0eV9yZXdyaXR0ZW59');
flag.style.opacity = '0';
flag.style.transform = 'scale(.8)';
flag.style.transition = 'all 1.5s ease 1s';
// Insert before the "PROVEN WORTHY" text
es.insertBefore(flag, es.lastElementChild);
setTimeout(() => {
flag.style.opacity = '1';
flag.style.transform = 'scale(1)';
}, 500);
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// RESUME FROM SAVED STATE
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
function resumeGame() {
restoreStones();
const overlay = document.getElementById('black-overlay');
switch (G.stage) {
case 0:
default:
const header = document.getElementById('game-header');
header.classList.add('intro-center'); // Center header on first load
setTimeout(() => {
overlay.classList.add('fade-out');
setTimeout(() => {
overlay.classList.add('hidden');
// Move header back to top before game starts
header.classList.remove('intro-center');
startStage1();
}, 1000);
}, 800);
break;
case 1:
overlay.classList.add('hidden');
showStage(1);
typewriterText("Mortals believe what is visible.They trust the surface.They obey what appears locked.But reality does not answer to appearances.It answers to those who dare to look beneath.");
break;
case 2:
overlay.classList.add('hidden');
showStage(2);
typewriterText("Reality bends to those with power.Become more than you are.Names are given.Roles are assigned.Power is inherited.But identity⦠Identity can be rewritten.");
break;
case 3:
overlay.classList.add('hidden');
showStage(3);
typewriterText("Power without understanding is meaningless.Seek the hidden endpoint.The universe keeps its state.Change the state⦠and you change destiny");
break;
case 4:
overlay.classList.add('hidden');
showStage(4);
typewriterText("You have altered what you see, and who you are.Now alter what is true.The final stone awaits those who see through the last illusion.Nothing is fixed.Everything is subject to the will.The universe is a canvas.And you are the brush.");
break;
case 5:
overlay.classList.add('hidden');
showEndScreen();
break;
}
}
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
// BOOT
// βββββββββββββββββββββββββββββββββββββββββββββββββββββββ
window.addEventListener('DOMContentLoaded', resumeGame);