all kinds of updates

This commit is contained in:
Dan 2026-01-20 14:31:02 +00:00
parent 25815aa4d2
commit 5f2bbca38f
10 changed files with 792 additions and 216 deletions

View file

@ -6,6 +6,7 @@ class GameEngine {
this.adapter = null; this.adapter = null;
this.state = null; this.state = null;
this.input = null; this.input = null;
this.sound = null;
this.scenes = null; this.scenes = null;
this.isRunning = false; this.isRunning = false;
@ -53,7 +54,13 @@ class GameEngine {
this.adapter = new TerminalAdapter(this.terminal); this.adapter = new TerminalAdapter(this.terminal);
this.state = new StateManager(this.definition.id); this.state = new StateManager(this.definition.id);
this.input = new InputManager(this.adapter); this.input = new InputManager(this.adapter);
this.scenes = new SceneManager(this.adapter, this.state, this.input);
// Initialize sound manager if SoundManager is available
if (window.SoundManager) {
this.sound = new SoundManager(this.adapter);
}
this.scenes = new SceneManager(this.adapter, this.state, this.input, this.sound);
// Initialize state // Initialize state
this.state.init(this.definition.initialState || {}); this.state.init(this.definition.initialState || {});
@ -173,6 +180,11 @@ class GameEngine {
this.isRunning = false; this.isRunning = false;
// Cleanup sound manager
if (this.sound) {
this.sound.stopAll();
}
// Cleanup input manager // Cleanup input manager
if (this.input) { if (this.input) {
this.input.destroy(); this.input.destroy();

View file

@ -18,7 +18,8 @@ class InputManager {
// Check if terminal input has text - if so, let user submit commands like "quit" // Check if terminal input has text - if so, let user submit commands like "quit"
const terminalInput = document.getElementById("input"); const terminalInput = document.getElementById("input");
const hasInputText = terminalInput && terminalInput.value.trim().length > 0; const hasInputText =
terminalInput && terminalInput.value.trim().length > 0;
if (e.key === "ArrowUp") { if (e.key === "ArrowUp") {
e.preventDefault(); e.preventDefault();
@ -85,6 +86,7 @@ class InputManager {
if (prompt) { if (prompt) {
this.adapter.print(""); this.adapter.print("");
this.adapter.printInfo("------------------");
this.adapter.printInfo(prompt); this.adapter.printInfo(prompt);
} }
this.adapter.print(""); this.adapter.print("");

View file

@ -1,12 +1,14 @@
// Scene Manager - Handles scene definitions, rendering, and transitions // Scene Manager - Handles scene definitions, rendering, and transitions
class SceneManager { class SceneManager {
constructor(adapter, stateManager, inputManager) { constructor(adapter, stateManager, inputManager, soundManager = null) {
this.adapter = adapter; this.adapter = adapter;
this.state = stateManager; this.state = stateManager;
this.input = inputManager; this.input = inputManager;
this.sound = soundManager;
this.scenes = {}; this.scenes = {};
this.currentScene = null; this.currentScene = null;
this.sceneHistory = []; this.sceneHistory = [];
this.activeSounds = new Map(); // Track sounds started in current scene
} }
// Register scenes from game definition // Register scenes from game definition
@ -34,8 +36,16 @@ class SceneManager {
this.sceneHistory.push(this.currentScene.id); this.sceneHistory.push(this.currentScene.id);
} }
// Stop scene-specific sounds from previous scene
await this._cleanupSceneSounds();
this.currentScene = scene; this.currentScene = scene;
// Preload sounds for this scene
if (this.sound && scene.sounds) {
await this._preloadSceneSounds(scene.sounds);
}
// Execute onEnter actions // Execute onEnter actions
if (scene.onEnter) { if (scene.onEnter) {
await this._executeActions(scene.onEnter); await this._executeActions(scene.onEnter);
@ -145,7 +155,11 @@ class SceneManager {
} }
if (block.type === "typewriter") { if (block.type === "typewriter") {
await this._typewriter(block.text, block.speed || 50); await this._typewriter(block.text, block.speed || 50, {
bold: block.bold,
italic: block.italic,
className: block.className,
});
continue; continue;
} }
@ -154,12 +168,20 @@ class SceneManager {
continue; continue;
} }
if (block.type === "sound") {
await this._handleSound(block);
continue;
}
// Text with optional className (supports html: true for HTML content) // Text with optional className (supports html: true for HTML content)
if (block.text !== undefined) { if (block.text !== undefined) {
if (block.html) { if (block.html) {
this._printHTML(block.text, block.className || ""); this._printHTML(block.text, block.className || "");
} else { } else {
this._printText(block.text, block.className || ""); this._printText(block.text, block.className || "", {
bold: block.bold,
italic: block.italic,
});
} }
continue; continue;
} }
@ -167,15 +189,22 @@ class SceneManager {
} }
// Print text with variable interpolation // Print text with variable interpolation
_printText(text, className = "") { _printText(text, className = "", options = {}) {
// Support ${path} interpolation // Support ${path} interpolation
const interpolated = text.replace(/\$\{([^}]+)\}/g, (match, path) => { const interpolated = text.replace(/\$\{([^}]+)\}/g, (match, path) => {
const value = this.state.get(path); const value = this.state.get(path);
return value !== undefined ? String(value) : match; return value !== undefined ? String(value) : match;
}); });
if (className) { // Build style classes based on options
this.adapter.print(interpolated, className); let styleClasses = className;
if (options.bold)
styleClasses += (styleClasses ? " " : "") + "typewriter-bold";
if (options.italic)
styleClasses += (styleClasses ? " " : "") + "typewriter-italic";
if (styleClasses) {
this.adapter.print(interpolated, styleClasses);
} else { } else {
this.adapter.print(interpolated); this.adapter.print(interpolated);
} }
@ -190,7 +219,9 @@ class SceneManager {
}); });
if (className) { if (className) {
this.adapter.printHTML(`<span class="${className}">${interpolated}</span>`); this.adapter.printHTML(
`<span class="${className}">${interpolated}</span>`,
);
} else { } else {
this.adapter.printHTML(interpolated); this.adapter.printHTML(interpolated);
} }
@ -350,10 +381,16 @@ class SceneManager {
} }
// Typewriter effect // Typewriter effect
async _typewriter(text, speed) { async _typewriter(text, speed, options = {}) {
const interpolated = this._interpolateText(text); const interpolated = this._interpolateText(text);
let output = ""; let output = "";
// Build style classes based on options
let styleClasses = "typewriter-line";
if (options.bold) styleClasses += " typewriter-bold";
if (options.italic) styleClasses += " typewriter-italic";
if (options.className) styleClasses += " " + options.className;
for (const char of interpolated) { for (const char of interpolated) {
output += char; output += char;
// Create a single updating line for typewriter // Create a single updating line for typewriter
@ -364,7 +401,7 @@ class SceneManager {
typewriterSpan.textContent = output; typewriterSpan.textContent = output;
} else { } else {
this.adapter.printHTML( this.adapter.printHTML(
`<span class="typewriter-line">${output}</span>`, `<span class="${styleClasses}">${output}</span>`,
); );
} }
@ -451,4 +488,127 @@ class SceneManager {
resetHistory() { resetHistory() {
this.sceneHistory = []; this.sceneHistory = [];
} }
// Preload sounds for a scene
async _preloadSceneSounds(sounds) {
if (!this.sound) return;
const soundList = Array.isArray(sounds) ? sounds : [sounds];
let hasShownLoading = false;
for (const soundDef of soundList) {
const soundId = soundDef.id;
const url = soundDef.url || soundDef.src;
if (!soundId || !url) {
console.warn("Invalid sound definition:", soundDef);
continue;
}
// Skip if already loaded
if (this.sound.isLoaded(soundId)) {
continue;
}
// Show loading indicator if not shown yet
if (!hasShownLoading) {
this.adapter.printHTML(
'<span class="sound-loading info">Loading audio...</span>',
);
hasShownLoading = true;
}
try {
await this.sound.preload(soundId, url);
} catch (error) {
console.error(`Failed to preload sound ${soundId}:`, error);
// Continue loading other sounds even if one fails
}
}
// Remove loading indicator
if (hasShownLoading) {
const indicator =
this.adapter.terminal.output.querySelector(".sound-loading");
if (indicator) {
indicator.remove();
}
}
}
// Handle sound playback in content blocks
async _handleSound(block) {
if (!this.sound) {
console.warn("Sound manager not available");
return;
}
const action = block.action || "play"; // play, stop, stopAll
const soundId = block.id || block.sound;
try {
if (action === "play") {
const options = {
loop: block.loop || false,
volume: block.volume !== undefined ? block.volume : 1.0,
fade: block.fade || false,
fadeDuration: block.fadeDuration || 1000,
};
const controller = await this.sound.play(soundId, options);
// Store reference for cleanup unless it's a one-shot sound
if (block.loop || block.persist) {
this.activeSounds.set(soundId, controller);
}
// Auto-stop after duration if specified
if (block.duration) {
setTimeout(() => {
if (block.fadeOut !== false) {
controller.fadeOut(block.fadeDuration || 1000);
} else {
controller.stop();
}
}, block.duration);
}
} else if (action === "stop") {
const controller = this.activeSounds.get(soundId);
if (controller) {
if (block.fadeOut !== false) {
await controller.fadeOut(block.fadeDuration || 1000);
} else {
controller.stop();
}
this.activeSounds.delete(soundId);
}
} else if (action === "stopAll") {
await this._cleanupSceneSounds();
}
} catch (error) {
console.error(`Sound error (${action} ${soundId}):`, error);
// Don't show error to user, just log it
}
}
// Clean up sounds when leaving a scene
async _cleanupSceneSounds() {
if (!this.sound) return;
const fadePromises = [];
for (const [, controller] of this.activeSounds) {
if (controller.fadeOut) {
fadePromises.push(
controller.fadeOut(500).catch((e) => console.error("Fade error:", e)),
);
} else {
controller.stop();
}
}
// Wait for all fades to complete
await Promise.all(fadePromises);
this.activeSounds.clear();
}
} }

View file

@ -0,0 +1,247 @@
// Sound Manager - Handles audio loading, caching, and playback for games
class SoundManager {
constructor(adapter) {
this.adapter = adapter;
this.sounds = new Map(); // soundId -> { audio, loaded, loading, error }
this.currentlyPlaying = new Set(); // Track currently playing sounds
this.globalVolume = 1.0;
}
// Preload a sound file
async preload(soundId, url) {
// If already loaded or loading, return existing promise
if (this.sounds.has(soundId)) {
const sound = this.sounds.get(soundId);
if (sound.loaded) {
return sound.audio;
}
if (sound.loading) {
return sound.loadingPromise;
}
if (sound.error) {
throw new Error(`Sound ${soundId} failed to load: ${sound.error}`);
}
}
// Create new audio element
const audio = new Audio();
const soundEntry = {
audio,
loaded: false,
loading: true,
error: null,
url,
};
// Create promise for loading
const loadingPromise = new Promise((resolve, reject) => {
const onLoad = () => {
soundEntry.loaded = true;
soundEntry.loading = false;
audio.removeEventListener("canplaythrough", onLoad);
audio.removeEventListener("error", onError);
resolve(audio);
};
const onError = (e) => {
soundEntry.loading = false;
soundEntry.error = e.message || "Failed to load audio";
audio.removeEventListener("canplaythrough", onLoad);
audio.removeEventListener("error", onError);
reject(new Error(`Failed to load sound ${soundId}: ${soundEntry.error}`));
};
audio.addEventListener("canplaythrough", onLoad, { once: true });
audio.addEventListener("error", onError, { once: true });
audio.preload = "auto";
audio.src = url;
audio.load();
});
soundEntry.loadingPromise = loadingPromise;
this.sounds.set(soundId, soundEntry);
return loadingPromise;
}
// Play a sound (will load if not already loaded)
async play(soundId, options = {}) {
const {
loop = false,
volume = 1.0,
onEnd = null,
fade = false,
fadeDuration = 1000,
} = options;
let soundEntry = this.sounds.get(soundId);
if (!soundEntry) {
throw new Error(`Sound ${soundId} not preloaded. Use preload() first.`);
}
// Wait for loading if still loading
if (soundEntry.loading) {
await soundEntry.loadingPromise;
}
if (soundEntry.error) {
throw new Error(`Sound ${soundId} failed to load: ${soundEntry.error}`);
}
const audio = soundEntry.audio;
// Clone the audio element for concurrent playback
const playInstance = audio.cloneNode();
playInstance.loop = loop;
playInstance.volume = fade ? 0 : volume * this.globalVolume;
// Track this instance
const trackingId = `${soundId}_${Date.now()}`;
this.currentlyPlaying.add(trackingId);
// Handle end event
const cleanup = () => {
this.currentlyPlaying.delete(trackingId);
playInstance.removeEventListener("ended", cleanup);
if (onEnd) onEnd();
};
playInstance.addEventListener("ended", cleanup);
// Play the sound
try {
await playInstance.play();
// Fade in if requested
if (fade) {
this._fadeIn(playInstance, volume * this.globalVolume, fadeDuration);
}
return {
instance: playInstance,
stop: () => {
playInstance.pause();
playInstance.currentTime = 0;
cleanup();
},
fadeOut: (duration = fadeDuration) => {
return this._fadeOut(playInstance, duration).then(() => {
playInstance.pause();
cleanup();
});
},
};
} catch (error) {
cleanup();
throw new Error(`Failed to play sound ${soundId}: ${error.message}`);
}
}
// Fade in audio
_fadeIn(audio, targetVolume, duration) {
const steps = 20;
const stepDuration = duration / steps;
const volumeStep = targetVolume / steps;
let currentStep = 0;
const interval = setInterval(() => {
currentStep++;
audio.volume = Math.min(volumeStep * currentStep, targetVolume);
if (currentStep >= steps) {
clearInterval(interval);
audio.volume = targetVolume;
}
}, stepDuration);
}
// Fade out audio
_fadeOut(audio, duration) {
return new Promise((resolve) => {
const steps = 20;
const stepDuration = duration / steps;
const startVolume = audio.volume;
const volumeStep = startVolume / steps;
let currentStep = 0;
const interval = setInterval(() => {
currentStep++;
audio.volume = Math.max(startVolume - volumeStep * currentStep, 0);
if (currentStep >= steps) {
clearInterval(interval);
audio.volume = 0;
resolve();
}
}, stepDuration);
});
}
// Stop all currently playing sounds
stopAll() {
for (const soundId of Array.from(this.currentlyPlaying)) {
const [id] = soundId.split("_");
const soundEntry = this.sounds.get(id);
if (soundEntry && soundEntry.audio) {
soundEntry.audio.pause();
soundEntry.audio.currentTime = 0;
}
}
this.currentlyPlaying.clear();
}
// Set global volume (0.0 to 1.0)
setGlobalVolume(volume) {
this.globalVolume = Math.max(0, Math.min(1, volume));
}
// Check if a sound is loaded
isLoaded(soundId) {
const sound = this.sounds.get(soundId);
return sound && sound.loaded;
}
// Check if a sound is currently loading
isLoading(soundId) {
const sound = this.sounds.get(soundId);
return sound && sound.loading;
}
// Get loading progress for all sounds
getLoadingStatus() {
const status = {
total: this.sounds.size,
loaded: 0,
loading: 0,
failed: 0,
};
for (const [, sound] of this.sounds) {
if (sound.loaded) status.loaded++;
else if (sound.loading) status.loading++;
else if (sound.error) status.failed++;
}
return status;
}
// Clear all sounds (useful for cleanup)
clear() {
this.stopAll();
this.sounds.clear();
}
// Remove a specific sound from cache
unload(soundId) {
const sound = this.sounds.get(soundId);
if (sound && sound.audio) {
sound.audio.pause();
sound.audio.src = "";
}
this.sounds.delete(soundId);
}
}
// Make available globally
window.SoundManager = SoundManager;

View file

@ -46,24 +46,15 @@ const DARK_TOWER_HEADER = `
`; `;
const LIGHTHOUSE_HEADER = ` const LIGHTHOUSE_HEADER = `
\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[48;5;0m \x1b[38;5;17;48;5;0m\x1b[38;5;4;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;233;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;178;48;5;0m\x1b[38;5;178;48;5;233m\x1b[38;5;220;48;5;0m\x1b[38;5;233;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;208m\x1b[38;5;208;48;5;130m\x1b[38;5;0;48;5;160m\x1b[38;5;166;48;5;52m\x1b[38;5;130;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;178;48;5;0m\x1b[38;5;3;48;5;0m\x1b[38;5;236;48;5;232m\x1b[38;5;238;48;5;230m\x1b[38;5;0;48;5;0m\x1b[38;5;4;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;23m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;184m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;4;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;233;48;5;0m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;18;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;232m\x1b[38;5;233;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;23;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[m .
\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;232;48;5;0m\x1b[38;5;94;48;5;0m\x1b[38;5;100;48;5;220m\x1b[38;5;178;48;5;0m\x1b[38;5;136;48;5;0m\x1b[38;5;3;48;5;0m\x1b[38;5;220;48;5;0m\x1b[38;5;136;48;5;0m\x1b[38;5;202;48;5;232m\x1b[38;5;220;48;5;58m\x1b[38;5;0;48;5;172m\x1b[38;5;178;48;5;11m\x1b[38;5;214;48;5;232m\x1b[38;5;11;48;5;94m\x1b[38;5;143;48;5;172m\x1b[38;5;11;48;5;236m\x1b[38;5;229;48;5;234m\x1b[38;5;220;48;5;220m\x1b[38;5;11;48;5;94m\x1b[38;5;220;48;5;232m\x1b[38;5;208;48;5;11m\x1b[38;5;0;48;5;178m\x1b[38;5;136;48;5;52m\x1b[38;5;11;48;5;220m\x1b[38;5;11;48;5;0m\x1b[38;5;94;48;5;18m\x1b[38;5;220;48;5;0m\x1b[38;5;11;48;5;0m\x1b[38;5;184;48;5;0m\x1b[38;5;58;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;3;48;5;238m\x1b[38;5;233;48;5;0m\x1b[38;5;233;48;5;232m\x1b[38;5;0;48;5;58m\x1b[38;5;232;48;5;220m\x1b[38;5;0;48;5;184m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;234m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;4m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;74m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;234;48;5;0m\x1b[38;5;73;48;5;0m\x1b[38;5;235;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;24m\x1b[38;5;232;48;5;4m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;4m\x1b[38;5;25;48;5;17m\x1b[38;5;23;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;24m\x1b[38;5;0;48;5;23m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;73m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[m /|\\
\x1b[38;5;60;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;100;48;5;242m\x1b[38;5;94;48;5;220m\x1b[38;5;233;48;5;220m\x1b[38;5;232;48;5;214m\x1b[38;5;58;48;5;149m\x1b[38;5;58;48;5;11m\x1b[38;5;184;48;5;185m\x1b[38;5;94;48;5;101m\x1b[38;5;220;48;5;88m\x1b[38;5;94;48;5;11m\x1b[38;5;178;48;5;227m\x1b[38;5;11;48;5;228m\x1b[38;5;11;48;5;230m\x1b[38;5;101;48;5;15m\x1b[38;5;11;48;5;229m\x1b[38;5;11;48;5;15m\x1b[38;5;229;48;5;15m\x1b[38;5;11;48;5;15m\x1b[38;5;184;48;5;15m\x1b[38;5;11;48;5;15m\x1b[38;5;11;48;5;230m\x1b[38;5;11;48;5;228m\x1b[38;5;214;48;5;228m\x1b[38;5;178;48;5;11m\x1b[38;5;232;48;5;11m\x1b[38;5;184;48;5;11m\x1b[38;5;11;48;5;11m\x1b[38;5;184;48;5;227m\x1b[38;5;142;48;5;11m\x1b[38;5;3;48;5;220m\x1b[38;5;58;48;5;220m\x1b[38;5;94;48;5;220m\x1b[38;5;3;48;5;184m\x1b[38;5;100;48;5;221m\x1b[38;5;136;48;5;234m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;4;48;5;17m\x1b[38;5;236;48;5;17m\x1b[38;5;234;48;5;0m\x1b[38;5;236;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;18;48;5;0m\x1b[38;5;20;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;23;48;5;0m\x1b[38;5;0;48;5;25m\x1b[38;5;0;48;5;17m\x1b[38;5;232;48;5;17m\x1b[38;5;232;48;5;0m\x1b[38;5;4;48;5;24m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[m / | \\
\x1b[38;5;220;48;5;237m\x1b[38;5;11;48;5;0m\x1b[38;5;11;48;5;234m\x1b[38;5;232;48;5;100m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;235m\x1b[38;5;0;48;5;94m\x1b[38;5;0;48;5;184m\x1b[38;5;0;48;5;220m\x1b[38;5;0;48;5;214m\x1b[38;5;232;48;5;58m\x1b[38;5;240;48;5;0m\x1b[38;5;136;48;5;0m\x1b[38;5;94;48;5;233m\x1b[38;5;220;48;5;0m\x1b[38;5;52;48;5;184m\x1b[38;5;94;48;5;178m\x1b[38;5;17;48;5;178m\x1b[38;5;3;48;5;172m\x1b[38;5;220;48;5;220m\x1b[38;5;227;48;5;220m\x1b[38;5;178;48;5;136m\x1b[38;5;0;48;5;220m\x1b[38;5;232;48;5;214m\x1b[38;5;130;48;5;166m\x1b[38;5;214;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;232m\x1b[38;5;232;48;5;136m\x1b[38;5;0;48;5;11m\x1b[38;5;0;48;5;220m\x1b[38;5;0;48;5;11m\x1b[38;5;0;48;5;142m\x1b[38;5;0;48;5;178m\x1b[38;5;0;48;5;136m\x1b[38;5;232;48;5;58m\x1b[38;5;178;48;5;3m\x1b[38;5;227;48;5;94m\x1b[38;5;229;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;234;48;5;0m\x1b[38;5;60;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;24m\x1b[38;5;232;48;5;0m\x1b[38;5;17;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;18;48;5;0m\x1b[38;5;26;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;20;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;19;48;5;0m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;4;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;233;48;5;232m\x1b[38;5;17;48;5;4m\x1b[38;5;25;48;5;0m\x1b[38;5;24;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[m / | \\
\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;184m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;172m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;58m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;107m\x1b[38;5;239;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;1m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;94;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;234;48;5;0m\x1b[38;5;0;48;5;252m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;4;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;18;48;5;0m\x1b[38;5;19;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;18;48;5;0m\x1b[38;5;0;48;5;4m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;67m\x1b[38;5;0;48;5;232m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;24m\x1b[38;5;17;48;5;24m\x1b[38;5;0;48;5;4m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[m / | \\
\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;232;48;5;0m\x1b[38;5;232;48;5;67m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;24m\x1b[38;5;24;48;5;236m\x1b[38;5;227;48;5;24m\x1b[38;5;43;48;5;122m\x1b[38;5;0;48;5;0m\x1b[38;5;235;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;17m\x1b[38;5;4;48;5;17m\x1b[38;5;240;48;5;0m\x1b[38;5;15;48;5;232m\x1b[38;5;15;48;5;0m\x1b[38;5;15;48;5;17m\x1b[38;5;15;48;5;0m\x1b[38;5;233;48;5;0m\x1b[38;5;230;48;5;0m\x1b[38;5;17;48;5;17m\x1b[38;5;17;48;5;232m\x1b[38;5;15;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;15;48;5;0m\x1b[38;5;230;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;232m\x1b[38;5;0;48;5;17m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;25m\x1b[38;5;0;48;5;19m\x1b[38;5;17;48;5;17m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;17;48;5;17m\x1b[38;5;18;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;233m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;25m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;233;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[m / | \\
\x1b[38;5;0;48;5;0m\x1b[38;5;233;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;24;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;85;48;5;0m\x1b[38;5;0;48;5;234m\x1b[38;5;0;48;5;0m\x1b[38;5;18;48;5;26m\x1b[48;5;0m \x1b[38;5;0;48;5;233m\x1b[38;5;65;48;5;101m\x1b[38;5;192;48;5;230m\x1b[38;5;115;48;5;31m\x1b[38;5;236;48;5;0m\x1b[38;5;0;48;5;234m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;17m\x1b[38;5;17;48;5;19m\x1b[38;5;18;48;5;25m\x1b[38;5;19;48;5;20m\x1b[38;5;17;48;5;17m\x1b[38;5;232;48;5;19m\x1b[38;5;4;48;5;0m\x1b[38;5;62;48;5;17m\x1b[38;5;27;48;5;62m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;17;48;5;4m\x1b[38;5;232;48;5;18m\x1b[38;5;232;48;5;19m\x1b[38;5;17;48;5;19m\x1b[38;5;18;48;5;178m\x1b[38;5;236;48;5;24m\x1b[38;5;227;48;5;15m\x1b[38;5;0;48;5;17m\x1b[38;5;11;48;5;230m\x1b[38;5;11;48;5;18m\x1b[38;5;11;48;5;19m\x1b[38;5;227;48;5;15m\x1b[38;5;232;48;5;4m\x1b[38;5;11;48;5;15m\x1b[38;5;11;48;5;232m\x1b[38;5;227;48;5;0m\x1b[38;5;0;48;5;232m\x1b[48;5;0m \x1b[38;5;19;48;5;17m\x1b[38;5;19;48;5;19m\x1b[38;5;25;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;17m\x1b[38;5;4;48;5;18m\x1b[38;5;17;48;5;17m\x1b[38;5;19;48;5;0m\x1b[38;5;18;48;5;0m\x1b[38;5;17;48;5;232m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;17;48;5;0m\x1b[38;5;17;48;5;24m\x1b[38;5;4;48;5;17m\x1b[38;5;233;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[m / .--+--. \\
\x1b[38;5;0;48;5;0m\x1b[38;5;24;48;5;0m\x1b[38;5;23;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;3;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;58m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;23;48;5;74m\x1b[38;5;0;48;5;227m\x1b[38;5;229;48;5;229m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;25m\x1b[38;5;172;48;5;0m\x1b[38;5;0;48;5;24m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;179;48;5;0m\x1b[38;5;1;48;5;234m\x1b[38;5;215;48;5;4m\x1b[38;5;17;48;5;0m\x1b[38;5;17;48;5;17m\x1b[38;5;235;48;5;234m\x1b[38;5;11;48;5;142m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;184;48;5;100m\x1b[38;5;17;48;5;17m\x1b[38;5;17;48;5;4m\x1b[38;5;227;48;5;178m\x1b[38;5;237;48;5;0m\x1b[38;5;227;48;5;184m\x1b[38;5;220;48;5;0m\x1b[38;5;11;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;242;48;5;0m\x1b[38;5;234;48;5;17m\x1b[38;5;214;48;5;227m\x1b[38;5;0;48;5;0m\x1b[38;5;19;48;5;232m\x1b[38;5;19;48;5;0m\x1b[38;5;4;48;5;17m\x1b[38;5;17;48;5;0m\x1b[38;5;4;48;5;232m\x1b[38;5;4;48;5;0m\x1b[38;5;0;48;5;229m\x1b[38;5;17;48;5;25m\x1b[38;5;17;48;5;17m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;94;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;17;48;5;11m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[m /__/ | \\__\\
\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;24m\x1b[38;5;0;48;5;232m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;108m\x1b[38;5;6;48;5;229m\x1b[38;5;6;48;5;79m\x1b[38;5;23;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;15;48;5;255m\x1b[38;5;230;48;5;255m\x1b[38;5;17;48;5;17m\x1b[38;5;0;48;5;17m\x1b[38;5;232;48;5;233m\x1b[38;5;230;48;5;230m\x1b[38;5;230;48;5;15m\x1b[38;5;0;48;5;233m\x1b[38;5;15;48;5;108m\x1b[38;5;230;48;5;15m\x1b[38;5;230;48;5;230m\x1b[38;5;15;48;5;230m\x1b[38;5;230;48;5;230m\x1b[38;5;235;48;5;178m\x1b[38;5;15;48;5;230m\x1b[38;5;230;48;5;229m\x1b[38;5;233;48;5;232m\x1b[38;5;0;48;5;17m\x1b[38;5;15;48;5;230m\x1b[38;5;24;48;5;237m\x1b[38;5;230;48;5;15m\x1b[38;5;228;48;5;15m\x1b[38;5;230;48;5;15m\x1b[38;5;230;48;5;255m\x1b[38;5;230;48;5;229m\x1b[38;5;232;48;5;94m\x1b[38;5;15;48;5;230m\x1b[38;5;230;48;5;186m\x1b[38;5;17;48;5;17m\x1b[38;5;17;48;5;0m\x1b[38;5;230;48;5;60m\x1b[38;5;0;48;5;94m\x1b[38;5;230;48;5;94m\x1b[38;5;230;48;5;229m\x1b[38;5;230;48;5;230m\x1b[38;5;15;48;5;230m\x1b[38;5;230;48;5;143m\x1b[38;5;101;48;5;94m\x1b[38;5;230;48;5;61m\x1b[38;5;229;48;5;60m\x1b[38;5;233;48;5;0m\x1b[38;5;233;48;5;17m\x1b[38;5;229;48;5;238m\x1b[38;5;230;48;5;237m\x1b[38;5;15;48;5;0m\x1b[38;5;229;48;5;60m\x1b[38;5;230;48;5;67m\x1b[38;5;230;48;5;187m\x1b[38;5;230;48;5;223m\x1b[38;5;232;48;5;0m\x1b[38;5;228;48;5;228m\x1b[38;5;230;48;5;222m\x1b[38;5;230;48;5;7m\x1b[38;5;230;48;5;188m\x1b[38;5;15;48;5;229m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;18;48;5;0m\x1b[38;5;0;48;5;0m\x1b[m [__] | [__]
\x1b[38;5;0;48;5;4m\x1b[38;5;17;48;5;24m\x1b[38;5;0;48;5;24m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;31m\x1b[38;5;25;48;5;17m\x1b[38;5;232;48;5;0m\x1b[38;5;25;48;5;17m\x1b[38;5;0;48;5;17m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;18m\x1b[38;5;0;48;5;17m\x1b[38;5;4;48;5;0m\x1b[38;5;25;48;5;25m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;58m\x1b[38;5;25;48;5;17m\x1b[38;5;24;48;5;235m\x1b[38;5;31;48;5;232m\x1b[38;5;230;48;5;44m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;18m\x1b[38;5;0;48;5;17m\x1b[38;5;229;48;5;15m\x1b[38;5;228;48;5;15m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;17m\x1b[38;5;0;48;5;232m\x1b[38;5;229;48;5;15m\x1b[38;5;15;48;5;15m\x1b[38;5;0;48;5;0m\x1b[38;5;230;48;5;15m\x1b[38;5;229;48;5;15m\x1b[38;5;0;48;5;4m\x1b[38;5;229;48;5;18m\x1b[38;5;11;48;5;19m\x1b[38;5;0;48;5;166m\x1b[38;5;230;48;5;15m\x1b[38;5;230;48;5;230m\x1b[38;5;227;48;5;19m\x1b[38;5;228;48;5;0m\x1b[38;5;230;48;5;15m\x1b[38;5;233;48;5;61m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;18m\x1b[38;5;255;48;5;15m\x1b[38;5;228;48;5;230m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;18m\x1b[38;5;228;48;5;15m\x1b[38;5;228;48;5;230m\x1b[38;5;227;48;5;17m\x1b[38;5;255;48;5;17m\x1b[38;5;230;48;5;230m\x1b[38;5;0;48;5;18m\x1b[38;5;192;48;5;230m\x1b[38;5;255;48;5;230m\x1b[38;5;232;48;5;18m\x1b[38;5;0;48;5;18m\x1b[38;5;15;48;5;15m\x1b[38;5;229;48;5;230m\x1b[38;5;228;48;5;15m\x1b[38;5;227;48;5;229m\x1b[38;5;0;48;5;240m\x1b[38;5;0;48;5;17m\x1b[38;5;227;48;5;229m\x1b[38;5;230;48;5;15m\x1b[38;5;229;48;5;15m\x1b[38;5;228;48;5;230m\x1b[38;5;228;48;5;232m\x1b[38;5;228;48;5;4m\x1b[38;5;228;48;5;17m\x1b[38;5;0;48;5;232m\x1b[38;5;227;48;5;229m\x1b[38;5;227;48;5;230m\x1b[38;5;229;48;5;17m\x1b[38;5;227;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;19;48;5;232m\x1b[38;5;19;48;5;0m\x1b[38;5;24;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[m |
\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;31;48;5;0m\x1b[38;5;38;48;5;232m\x1b[38;5;31;48;5;0m\x1b[38;5;0;48;5;4m\x1b[38;5;0;48;5;232m\x1b[38;5;17;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;17m\x1b[38;5;4;48;5;19m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;25m\x1b[38;5;26;48;5;31m\x1b[38;5;25;48;5;31m\x1b[38;5;194;48;5;15m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;26m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;158;48;5;194m\x1b[38;5;195;48;5;230m\x1b[38;5;80;48;5;0m\x1b[38;5;122;48;5;0m\x1b[38;5;66;48;5;0m\x1b[38;5;195;48;5;255m\x1b[38;5;159;48;5;255m\x1b[38;5;0;48;5;0m\x1b[38;5;152;48;5;254m\x1b[38;5;122;48;5;188m\x1b[38;5;79;48;5;0m\x1b[38;5;158;48;5;65m\x1b[38;5;158;48;5;187m\x1b[38;5;0;48;5;233m\x1b[38;5;159;48;5;253m\x1b[38;5;158;48;5;187m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;255;48;5;151m\x1b[38;5;233;48;5;232m\x1b[38;5;17;48;5;0m\x1b[48;5;0m \x1b[38;5;194;48;5;254m\x1b[38;5;151;48;5;194m\x1b[38;5;0;48;5;0m\x1b[38;5;255;48;5;230m\x1b[38;5;159;48;5;187m\x1b[38;5;0;48;5;232m\x1b[38;5;158;48;5;194m\x1b[38;5;0;48;5;0m\x1b[38;5;122;48;5;193m\x1b[38;5;80;48;5;187m\x1b[38;5;158;48;5;0m\x1b[38;5;159;48;5;0m\x1b[38;5;194;48;5;255m\x1b[38;5;194;48;5;229m\x1b[38;5;158;48;5;151m\x1b[38;5;116;48;5;180m\x1b[38;5;195;48;5;0m\x1b[38;5;158;48;5;232m\x1b[38;5;116;48;5;186m\x1b[38;5;80;48;5;253m\x1b[38;5;116;48;5;0m\x1b[38;5;152;48;5;0m\x1b[38;5;152;48;5;237m\x1b[38;5;86;48;5;187m\x1b[38;5;0;48;5;0m\x1b[38;5;79;48;5;150m\x1b[38;5;80;48;5;151m\x1b[38;5;80;48;5;0m\x1b[38;5;158;48;5;232m\x1b[38;5;158;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[m ~~~~~~~|~~~~~~~
\x1b[38;5;0;48;5;0m\x1b[38;5;233;48;5;0m\x1b[38;5;31;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;24;48;5;0m\x1b[38;5;24;48;5;31m\x1b[38;5;24;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;37;48;5;32m\x1b[38;5;44;48;5;25m\x1b[38;5;31;48;5;37m\x1b[38;5;23;48;5;158m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;159m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;66m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;255m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;233m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;195m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;255m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;159m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;195m\x1b[38;5;232;48;5;15m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;159m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;195m\x1b[38;5;18;48;5;15m\x1b[38;5;0;48;5;15m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;184m\x1b[38;5;0;48;5;0m\x1b[38;5;19;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[m
\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;26;48;5;0m\x1b[38;5;0;48;5;233m\x1b[38;5;0;48;5;23m\x1b[38;5;24;48;5;23m\x1b[38;5;6;48;5;23m\x1b[38;5;37;48;5;31m\x1b[38;5;31;48;5;24m\x1b[38;5;31;48;5;37m\x1b[38;5;24;48;5;17m\x1b[38;5;30;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;25;48;5;0m\x1b[38;5;37;48;5;0m\x1b[38;5;25;48;5;25m\x1b[38;5;37;48;5;6m\x1b[38;5;121;48;5;230m\x1b[38;5;31;48;5;25m\x1b[38;5;232;48;5;0m\x1b[38;5;24;48;5;0m\x1b[38;5;25;48;5;0m\x1b[38;5;4;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;25;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;18;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;25;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;4;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;18;48;5;0m\x1b[38;5;19;48;5;0m\x1b[38;5;19;48;5;4m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;4m\x1b[38;5;17;48;5;232m\x1b[38;5;18;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;32;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;25;48;5;0m\x1b[38;5;19;48;5;19m\x1b[38;5;0;48;5;18m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;4m\x1b[m
\x1b[38;5;0;48;5;25m\x1b[38;5;0;48;5;232m\x1b[38;5;6;48;5;0m\x1b[38;5;31;48;5;24m\x1b[38;5;24;48;5;235m\x1b[38;5;17;48;5;31m\x1b[38;5;37;48;5;17m\x1b[38;5;44;48;5;232m\x1b[38;5;44;48;5;30m\x1b[38;5;19;48;5;19m\x1b[38;5;0;48;5;19m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;25;48;5;0m\x1b[38;5;24;48;5;0m\x1b[38;5;4;48;5;0m\x1b[38;5;227;48;5;24m\x1b[38;5;67;48;5;31m\x1b[38;5;6;48;5;24m\x1b[38;5;4;48;5;31m\x1b[38;5;192;48;5;31m\x1b[38;5;193;48;5;26m\x1b[38;5;115;48;5;25m\x1b[38;5;25;48;5;23m\x1b[38;5;25;48;5;17m\x1b[38;5;17;48;5;0m\x1b[38;5;116;48;5;17m\x1b[38;5;79;48;5;0m\x1b[38;5;17;48;5;31m\x1b[38;5;24;48;5;17m\x1b[38;5;31;48;5;0m\x1b[38;5;24;48;5;37m\x1b[38;5;4;48;5;31m\x1b[38;5;17;48;5;0m\x1b[38;5;24;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;37;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;233;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;18;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;19m\x1b[38;5;0;48;5;4m\x1b[38;5;0;48;5;18m\x1b[38;5;19;48;5;0m\x1b[38;5;26;48;5;0m\x1b[38;5;4;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;18;48;5;0m\x1b[38;5;24;48;5;0m\x1b[38;5;25;48;5;0m\x1b[38;5;31;48;5;0m\x1b[38;5;24;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;25;48;5;0m\x1b[38;5;19;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;232m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[m
\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;17m\x1b[38;5;233;48;5;0m\x1b[38;5;17;48;5;31m\x1b[38;5;233;48;5;31m\x1b[38;5;17;48;5;23m\x1b[38;5;23;48;5;80m\x1b[38;5;0;48;5;17m\x1b[38;5;17;48;5;18m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[48;5;0m \x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;233m\x1b[38;5;0;48;5;0m\x1b[38;5;137;48;5;24m\x1b[38;5;17;48;5;235m\x1b[38;5;4;48;5;193m\x1b[38;5;157;48;5;72m\x1b[38;5;114;48;5;115m\x1b[38;5;71;48;5;31m\x1b[38;5;24;48;5;31m\x1b[38;5;24;48;5;115m\x1b[38;5;24;48;5;79m\x1b[38;5;24;48;5;24m\x1b[38;5;24;48;5;17m\x1b[38;5;17;48;5;79m\x1b[38;5;17;48;5;17m\x1b[38;5;6;48;5;24m\x1b[38;5;24;48;5;37m\x1b[38;5;17;48;5;24m\x1b[38;5;30;48;5;24m\x1b[38;5;24;48;5;24m\x1b[38;5;4;48;5;23m\x1b[38;5;17;48;5;31m\x1b[38;5;23;48;5;234m\x1b[38;5;24;48;5;17m\x1b[38;5;0;48;5;232m\x1b[38;5;24;48;5;17m\x1b[38;5;18;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;32;48;5;232m\x1b[38;5;0;48;5;25m\x1b[38;5;0;48;5;20m\x1b[38;5;26;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;232;48;5;0m\x1b[38;5;26;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;24;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;18m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;184m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;25m\x1b[38;5;0;48;5;232m\x1b[38;5;232;48;5;17m\x1b[38;5;0;48;5;24m\x1b[38;5;0;48;5;25m\x1b[38;5;17;48;5;17m\x1b[38;5;0;48;5;4m\x1b[38;5;232;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[m
\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;31m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;18;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;18;48;5;0m\x1b[38;5;0;48;5;24m\x1b[38;5;25;48;5;25m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;31m\x1b[38;5;23;48;5;0m\x1b[38;5;6;48;5;232m\x1b[38;5;242;48;5;232m\x1b[38;5;17;48;5;31m\x1b[38;5;0;48;5;26m\x1b[38;5;32;48;5;151m\x1b[38;5;229;48;5;37m\x1b[38;5;229;48;5;36m\x1b[38;5;228;48;5;79m\x1b[38;5;78;48;5;6m\x1b[38;5;155;48;5;24m\x1b[38;5;229;48;5;30m\x1b[38;5;77;48;5;24m\x1b[38;5;193;48;5;24m\x1b[38;5;157;48;5;24m\x1b[38;5;122;48;5;79m\x1b[38;5;121;48;5;25m\x1b[38;5;86;48;5;24m\x1b[38;5;44;48;5;18m\x1b[38;5;44;48;5;17m\x1b[38;5;31;48;5;25m\x1b[38;5;38;48;5;17m\x1b[38;5;24;48;5;17m\x1b[38;5;45;48;5;17m\x1b[38;5;32;48;5;19m\x1b[38;5;32;48;5;17m\x1b[38;5;38;48;5;17m\x1b[38;5;24;48;5;0m\x1b[38;5;32;48;5;19m\x1b[38;5;17;48;5;18m\x1b[38;5;4;48;5;0m\x1b[38;5;26;48;5;0m\x1b[38;5;27;48;5;0m\x1b[38;5;19;48;5;0m\x1b[38;5;18;48;5;0m\x1b[38;5;26;48;5;25m\x1b[38;5;26;48;5;18m\x1b[38;5;27;48;5;17m\x1b[38;5;27;48;5;0m\x1b[38;5;20;48;5;0m\x1b[38;5;26;48;5;0m\x1b[38;5;27;48;5;0m\x1b[38;5;27;48;5;19m\x1b[38;5;27;48;5;232m\x1b[38;5;19;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;18;48;5;0m\x1b[38;5;26;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[m
\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;31;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;19;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;31;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;23;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;30;48;5;25m\x1b[38;5;17;48;5;23m\x1b[38;5;152;48;5;18m\x1b[38;5;24;48;5;25m\x1b[38;5;4;48;5;25m\x1b[38;5;31;48;5;17m\x1b[38;5;17;48;5;17m\x1b[38;5;17;48;5;18m\x1b[38;5;17;48;5;0m\x1b[38;5;24;48;5;24m\x1b[38;5;24;48;5;25m\x1b[38;5;67;48;5;25m\x1b[38;5;17;48;5;24m\x1b[38;5;18;48;5;17m\x1b[38;5;233;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;31;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;24;48;5;17m\x1b[38;5;31;48;5;0m\x1b[38;5;25;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;17;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;4m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;4m\x1b[38;5;0;48;5;17m\x1b[38;5;17;48;5;0m\x1b[38;5;4;48;5;17m\x1b[38;5;24;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;232;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[m
\x1b[38;5;0;48;5;0m\x1b[38;5;24;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;4m\x1b[38;5;0;48;5;19m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;25m\x1b[38;5;17;48;5;19m\x1b[38;5;25;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;235;48;5;0m\x1b[38;5;67;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;24;48;5;0m\x1b[38;5;23;48;5;25m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;233m\x1b[38;5;24;48;5;24m\x1b[38;5;31;48;5;38m\x1b[38;5;31;48;5;0m\x1b[38;5;25;48;5;236m\x1b[38;5;4;48;5;193m\x1b[38;5;0;48;5;23m\x1b[38;5;229;48;5;24m\x1b[38;5;17;48;5;17m\x1b[38;5;187;48;5;24m\x1b[38;5;65;48;5;17m\x1b[38;5;194;48;5;0m\x1b[38;5;6;48;5;24m\x1b[38;5;229;48;5;73m\x1b[38;5;72;48;5;30m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;4m\x1b[38;5;232;48;5;4m\x1b[38;5;232;48;5;17m\x1b[38;5;0;48;5;116m\x1b[38;5;0;48;5;6m\x1b[38;5;0;48;5;81m\x1b[38;5;0;48;5;17m\x1b[38;5;0;48;5;123m\x1b[38;5;0;48;5;0m\x1b[38;5;234;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;26;48;5;25m\x1b[38;5;24;48;5;32m\x1b[38;5;233;48;5;0m\x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;37m\x1b[38;5;0;48;5;0m\x1b[38;5;233;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;38m\x1b[38;5;0;48;5;24m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;25;48;5;0m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;17;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;233;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;233;48;5;0m\x1b[38;5;234;48;5;0m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[m
\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;23m\x1b[38;5;32;48;5;0m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;4;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;232;48;5;24m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;73m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;80m\x1b[38;5;238;48;5;0m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;6m\x1b[38;5;0;48;5;25m\x1b[38;5;0;48;5;17m\x1b[38;5;17;48;5;232m\x1b[38;5;232;48;5;232m\x1b[38;5;24;48;5;0m\x1b[38;5;79;48;5;0m\x1b[38;5;0;48;5;24m\x1b[38;5;32;48;5;0m\x1b[38;5;241;48;5;0m\x1b[38;5;66;48;5;0m\x1b[38;5;232;48;5;17m\x1b[38;5;0;48;5;0m\x1b[38;5;66;48;5;0m\x1b[38;5;0;48;5;238m\x1b[38;5;0;48;5;116m\x1b[38;5;0;48;5;238m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;236m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;23;48;5;232m\x1b[38;5;80;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;23;48;5;0m\x1b[38;5;81;48;5;234m\x1b[38;5;23;48;5;236m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;18m\x1b[38;5;0;48;5;24m\x1b[38;5;0;48;5;232m\x1b[38;5;0;48;5;0m\x1b[38;5;0;48;5;24m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[38;5;232;48;5;0m\x1b[38;5;0;48;5;0m\x1b[48;5;0m \x1b[m
`; `;

View file

@ -1,22 +1,6 @@
// Boxing Day - Day 1: December 26, 1999 // Boxing Day - Day 1: December 26, 1999
// A BBS-themed mystery game // A BBS-themed mystery game
// ASCII Art Constants
// const LIGHTHOUSE_HEADER = `
// .
// /|\\
// / | \\
// / | \\
// / | \\
// / | \\
// / .--+--. \\
// /__/ | \\__\\
// [__] | [__]
// |
// ~~~~~~~~~|~~~~~~~~~
// `;
const GLITCH_ART = ` const GLITCH_ART = `
E̸̢R̷̨R̵̢O̸̧R̷̨ E̸̢R̷̨R̵̢O̸̧R̷̨
D̶̨A̷̧T̸̢Ą̵ C̷̢Ǫ̸Ŗ̵R̷̨U̸̢P̵̧T̷̨ D̶̨A̷̧T̸̢Ą̵ C̷̢Ǫ̸Ŗ̵R̷̨U̸̢P̵̧T̷̨
@ -51,7 +35,7 @@ const boxingDayGame = {
found_number: false, found_number: false,
dialed_lighthouse: false, dialed_lighthouse: false,
// rm -rf deletion flags (persist across sessions) // deletion flags (this needs to persist across game sessions somehow... TBD)
archives_deleted: false, archives_deleted: false,
corrupted_file_deleted: false, corrupted_file_deleted: false,
@ -62,19 +46,23 @@ const boxingDayGame = {
intro: [ intro: [
{ type: "ansi", art: BOXING_DAY_TITLE, className: "game-ansi-art center" }, { type: "ansi", art: BOXING_DAY_TITLE, className: "game-ansi-art center" },
"", "",
{ text: "December 26, 1999", className: "info" }, { text: "December 26, 1999 - 10:47 PM", className: "info" },
{ text: "11:47 PM", className: "info" },
"", "",
{ type: "delay", ms: 600 }, { type: "delay", ms: 600 },
"Five days until the millennium.", "Five days until the millennium.",
{ type: "delay", ms: 400 }, { type: "delay", ms: 1500 },
"Five days until everything might change.", "Five days until everything might change.",
"", "",
{ type: "delay", ms: 600 }, { type: "delay", ms: 1000 },
"Your 56k modem hums quietly in the dark.", "Your 56k modem hums quietly in the dark.",
"The house is silent. Everyone else is asleep.", "The house is silent. Everyone else is asleep.",
"", "",
{ type: "delay", ms: 400 }, { type: "delay", ms: 400 },
{
text: "<strong>This game occasionally plays sounds, mute your tab now if that offends you.</strong>",
html: true,
className: "warning",
},
{ text: 'Type "quit" at any time to save and exit.', className: "info" }, { text: 'Type "quit" at any time to save and exit.', className: "info" },
], ],
@ -87,39 +75,48 @@ const boxingDayGame = {
connect_prompt: { connect_prompt: {
content: [ content: [
"Your terminal awaits a command.", {
"", text: "<em>Your terminal awaits a command.</em>",
{ text: "The familiar glow illuminates your face.", className: "info" }, html: true,
className: "info",
},
{
text: "<em>The familiar glow illuminates your face.</em>",
html: true,
className: "info",
},
], ],
options: [{ text: "Connect to Dark Tower BBS", next: "modem_connect" }], options: [{ text: "Connect to Dark Tower BBS", next: "modem_connect" }],
}, },
modem_connect: { modem_connect: {
clear: true, clear: true,
// Preload sounds for this scene
sounds: [{ id: "modem_connect", url: "/audio/modem-connect.mp3" }],
content: [ content: [
{ type: "typewriter", text: "ATDT 555-0199", speed: 80 }, { type: "typewriter", text: "ATDT 555-0199", speed: 80 },
{ type: "delay", ms: 800 },
"", "",
// Play modem dial sound
{ type: "sound", id: "modem_connect", volume: 0.6 },
{ type: "delay", ms: 400 },
{ text: "DIALING...", className: "info" }, { text: "DIALING...", className: "info" },
{ type: "delay", ms: 600 }, { type: "delay", ms: 3000 },
"",
{ text: "RING", className: "warning" },
{ type: "delay", ms: 800 },
{ text: "RING", className: "warning" },
{ type: "delay", ms: 600 },
"", "",
{ {
type: "typewriter", type: "typewriter",
text: "~~ eEe ~~ EEE ~~ eee ~~ CONNECT 56000", text: "~~ eEe ~~ EEE ~~ eee ~~",
speed: 35, speed: 35,
italic: true,
}, },
{ type: "delay", ms: 400 }, { type: "delay", ms: 3000 },
"CONNECT 56000",
"", "",
{ text: "Carrier detected.", className: "success" }, { text: "Carrier detected.", className: "success" },
{ type: "delay", ms: 300 }, { type: "delay", ms: 4500 },
{ text: "Negotiating protocol...", className: "info" }, "Negotiating protocol...",
{ type: "delay", ms: 500 }, { type: "delay", ms: 4500 },
{ text: "Connection established.", className: "success" }, { text: "Connection established.", className: "success" },
{ type: "delay", ms: 2000 },
], ],
next: "dark_tower_main", next: "dark_tower_main",
delay: 1200, delay: 1200,
@ -139,16 +136,16 @@ const boxingDayGame = {
}, },
"", "",
{ {
text: "---=[ B B S - E S T. 1 9 9 5 ]=---", text: "---=[ D A R K T O W E R B B S - E S T. 1 9 9 5 ]=---",
className: "info center", className: "info center",
}, },
{ {
text: "[ Users Online - 3 ] - [ SysOp - NightWatchman ]", text: "[ Users Connected - 3 ] - [ SysOp - NightWatchman ]",
className: "info center", className: "info center",
}, },
{ text: "[ Local Time: 11:52 PM ]", className: "info center" }, { text: "[ Local Time: 10:52 PM ]", className: "info center" },
"", "",
// New message notification (only if not read) // New message notification if not read
{ {
condition: { not: "read_new_message" }, condition: { not: "read_new_message" },
content: [ content: [
@ -159,15 +156,6 @@ const boxingDayGame = {
"", "",
], ],
}, },
// Return visitor message
{
condition: "visited.dark_tower_main",
content: {
text: "<em>Returning to main menu...</em>",
html: true,
className: "info",
},
},
], ],
onAfterRender: [{ set: "visited.dark_tower_main", value: true }], onAfterRender: [{ set: "visited.dark_tower_main", value: true }],
prompt: "Select:", prompt: "Select:",
@ -199,7 +187,6 @@ const boxingDayGame = {
// ========================================== // ==========================================
read_messages: { read_messages: {
title: "Private Messages",
content: [ content: [
...TableHelper.table({ ...TableHelper.table({
title: "Private Messages for 0BSERVER0", title: "Private Messages for 0BSERVER0",
@ -209,7 +196,7 @@ const boxingDayGame = {
"23", "23",
"[UNKNOWN]", "[UNKNOWN]",
"0BSERVER0", "0BSERVER0",
"25/12", "24/12",
{ text: "NEW", className: "warning" }, { text: "NEW", className: "warning" },
], ],
["22", "NIGHTWATCHER", "0BSERVER0", "12/12", "READ"], ["22", "NIGHTWATCHER", "0BSERVER0", "12/12", "READ"],
@ -227,7 +214,6 @@ const boxingDayGame = {
}, },
new_message: { new_message: {
title: "Reading Message...",
content: [ content: [
{ type: "delay", ms: 300 }, { type: "delay", ms: 300 },
"─── BEGIN MESSAGE ───", "─── BEGIN MESSAGE ───",
@ -276,6 +262,11 @@ const boxingDayGame = {
{ type: "delay", ms: 800 }, { type: "delay", ms: 800 },
"", "",
], ],
/**
* Update variables for read messages and found number
*
* The option the user takes here determines the path taken for this chapter
*/
onEnter: [ onEnter: [
{ set: "read_new_message", value: true }, { set: "read_new_message", value: true },
{ set: "found_number", value: true }, { set: "found_number", value: true },
@ -301,35 +292,28 @@ const boxingDayGame = {
}, },
message_archive: { message_archive: {
title: "Message Archive",
content: [ content: [
{ {
type: "table", type: "table",
title: "Private Messages for ${username}", // Supports interpolation title: "Private Messages for 0BSERVER0",
headers: ["#", "FROM", "TO", "DATE", "STATUS"], headers: ["#", "FROM", "TO", "DATE", "STATUS"],
rows: [ rows: [
// Simple array row (always shown) // Conditionally display watcher message as read or not
["22", "NIGHTWAT.", "0BSERVER0", "12/12", "READ"],
// Conditional row - only shown if condition is true
{ {
condition: { not: "read_new_message" }, condition: { not: "read_new_message" },
cells: ["23", "[UNKNOWN]", "0BSERVER0", "25/12", "NEW"], cells: ["23", "[UNKNOWN]", "0BSERVER0", "24/12", "NEW"],
className: "warning", // Applied to all cells in row className: "warning",
}, },
// Row with per-cell styling
{ {
cells: [ condition: "read_new_message",
"21", cells: ["23", "[UNKNOWN]", "0BSERVER0", "24/12", "READ"],
"0BSERVER0",
{ text: "DELETED", className: "error" },
"11/12",
"SENT",
],
}, },
// Conditional with complex logic ["22", "NIGHTWATCHER", "0BSERVER0", "12/12", "READ"],
["21", "0BSERVER0", "NIGHTWATCHER", "11/12", "SENT"],
["22", "NIGHTWATCHER", "0BSERVER0", "10/12", "READ"],
// Testing the advanced condition stuff...
{ {
condition: { and: ["has_secret", { not: "revealed_secret" }] }, condition: { and: ["has_secret", { not: "revealed_secret" }] },
cells: ["99", "???", "???", "??/??", "HIDDEN"], cells: ["99", "???", "???", "??/??", "HIDDEN"],
@ -338,19 +322,20 @@ const boxingDayGame = {
], ],
widths: [4, 12, 12, 8, 8], widths: [4, 12, 12, 8, 8],
align: ["right", "left", "left", "left", "left"], align: ["right", "left", "left", "left", "left"],
style: "single", // "single", "double", or "ascii" style: "single",
}, },
"═══ ARCHIVED MESSAGES ═══", { text: "<em>No new messages.</em>", html: true, className: "info" },
"",
{ {
condition: "read_new_message", condition: "read_new_message",
content: [ content: [
" 1. [SENDER UNKNOWN] - For your eyes only", {
{ text: " (The number: 555-0237)", className: "info" }, text: "<em>Just the number... 555-0237...</em>",
html: true,
className: "warning",
},
], ],
}, },
"", "",
{ text: "No other messages.", className: "info" },
], ],
options: [{ text: "Back", next: "dark_tower_main" }], options: [{ text: "Back", next: "dark_tower_main" }],
}, },
@ -362,18 +347,49 @@ const boxingDayGame = {
choice_immediate: { choice_immediate: {
clear: true, clear: true,
content: [ content: [
"Your fingers move before doubt can settle.", {
type: "text",
text: "<em>Your fingers move before doubt can settle.</em>",
html: true,
className: "info",
},
"", "",
{ type: "typewriter", text: "ATH0", speed: 100 }, { type: "typewriter", text: "ATH0", speed: 100 },
{ type: "delay", ms: 400 }, { type: "delay", ms: 400 },
{ text: "NO CARRIER", className: "warning" }, { text: "NO CARRIER", className: "warning" },
{ type: "delay", ms: 600 }, { type: "delay", ms: 600 },
"", "",
"You disconnect from Dark Tower.", {
"The silence of your room feels heavier now.", text: "<em>You disconnect from Dark Tower.</em>",
html: true,
className: "info",
},
{
text: "<em>The silence of your room feels heavier now.</em>",
html: true,
className: "info",
},
"",
"",
"", "",
{ type: "delay", ms: 500 }, { type: "delay", ms: 500 },
{ text: "Something compels you forward.", className: "info" }, //{ text: "Something compels you forward.", className: "info" },
{
type: "typewriter",
text: "Something compels you forward...",
italic: true,
speed: 100,
className: "info",
},
{ type: "delay", ms: 1500 },
{
type: "typewriter",
text: "...555-0237",
italic: true,
speed: 100,
className: "info",
},
{ type: "delay", ms: 2000 },
], ],
next: "dial_lighthouse", next: "dial_lighthouse",
delay: 1000, delay: 1000,
@ -395,28 +411,28 @@ const boxingDayGame = {
"The number fades from memory.", "The number fades from memory.",
"Just another piece of BBS spam, you tell yourself.", "Just another piece of BBS spam, you tell yourself.",
"", "",
{ type: "delay", ms: 800 }, { type: "delay", ms: 2000 },
"You browse Dark Tower for another hour.", "You browse Dark Tower for another hour.",
"Download some wallpapers. Chat about nothing.", "Download some wallpapers.",
"", "",
{ type: "delay", ms: 800 }, { type: "delay", ms: 2000 },
"At 11:47 PM, you disconnect.", "At 11:57 PM, you disconnect.",
"", "",
{ type: "delay", ms: 800 }, { type: "delay", ms: 2000 },
"Five days later, the millennium arrives.", "Five days later, the millennium arrives.",
"Fireworks. Champagne. Relief.", "Fireworks. Champagne. Relief.",
"", "",
{ type: "delay", ms: 600 }, { type: "delay", ms: 600 },
"Nothing happens.", "Nothing happens.",
"", "",
{ type: "delay", ms: 1000 }, { type: "delay", ms: 2000 },
{ text: "Or does it?", className: "warning" }, { text: "Or does it?", className: "warning" },
"", "",
{ type: "delay", ms: 800 }, { type: "delay", ms: 2000 },
"You never find out what cascade.exe would have done.", "You never find out what cascade.exe would have done.",
"The lighthouse keeper's message was never meant for you.", "The lighthouse keeper's message was never meant for you.",
"", "",
{ type: "delay", ms: 600 }, { type: "delay", ms: 1000 },
{ text: "Perhaps that's for the best.", className: "info" }, { text: "Perhaps that's for the best.", className: "info" },
"", "",
{ type: "delay", ms: 1000 }, { type: "delay", ms: 1000 },
@ -448,53 +464,90 @@ const boxingDayGame = {
// ========================================== // ==========================================
browse_boards: { browse_boards: {
title: "Message Boards",
content: [ content: [
"═══ DARK TOWER MESSAGE BOARDS ═══", {
"", type: "table",
" [1] General Discussion (47 new)", title: "DARK TOWER / MESSAGE BOARDS",
" [2] Tech Support (12 new)", headers: ["#", "NAME", "NEW MSG", "LAST"],
// Archives conditionally shown rows: [
["1", "General Discussion", "8", "24/12"],
["2", "Tech Support", "1", "25/12"],
["3", "File Updates", "3", "23/12"],
// Display the archives or have them deleted
// depending on progress.
// Not sure if people will be able to go back from lighthouse to tower at this stage
// Leaving it in just incase I want to do this later...
{ {
condition: { not: "archives_deleted" }, condition: { not: "archives_deleted" },
content: " [3] The Archives (3 new)", cells: ["4", "ARCHIVED", "-", "-"],
}, },
{ {
condition: "archives_deleted", condition: "archives_deleted",
content: { text: " [3] <BOARD REMOVED>", className: "error" }, cells: ["4", "<BOARD REMOVED>", "-", "-"],
className: "error",
},
],
widths: [4, 20, 10, 8],
align: ["right", "left", "left", "left"],
style: "single",
},
{
condition: "archives_deleted",
content: {
type: "typewriter",
italic: true,
text: "The archived messages are just... gone...",
speed: 80,
className: "info",
},
}, },
" [4] File Announcements (8 new)",
"",
], ],
prompt: "Select board:", prompt: "Select board:",
options: [ options: [
{ text: "General Discussion", next: "board_general" }, { text: "General Discussion", next: "board_general" },
{ text: "Tech Support", next: "board_tech" }, { text: "Tech Support", next: "board_tech" },
{ text: "File Updates", next: "board_files" },
{ {
text: "The Archives", text: "ARCHIVED",
next: "board_archives", next: "board_archives",
condition: { not: "archives_deleted" }, condition: { not: "archives_deleted" },
}, },
{ text: "File Announcements", next: "board_files" },
{ text: "Back to main menu", next: "dark_tower_main" }, { text: "Back to main menu", next: "dark_tower_main" },
], ],
}, },
board_general: { board_general: {
title: "General Discussion",
content: [ content: [
"═══ GENERAL DISCUSSION ═══",
"",
" [HOT] Y2K Preparation Thread - 234 replies",
" [NEW] Anyone else getting weird messages? - 12 replies",
" [NEW] Happy Boxing Day everyone! - 8 replies",
" Best BBS games? - 45 replies",
" New user intro thread - 67 replies",
"",
{ {
text: "The usual chatter. Nothing about lighthouses.", type: "table",
title: "GENERAL DISCUSSION",
headers: ["#", "SUBJECT", "MSG", "LAST"],
rows: [
["1", "2K Preparation Thread", "243", "25/12"],
["2", "Anyone else getting weird messages?", "3", "25/12"],
["3", "Happy Boxing Day everyone!", "5", "25/12"],
["4", "Best BBS games?", "43", "23/12"],
["5", "New user intro thread", "67", "20/12"],
],
widths: [4, 40, 6, 8],
align: ["right", "left", "right", "left"],
style: "single",
},
{
text: "The usual chatter.",
italic: true,
className: "info", className: "info",
}, },
{
condition: "found_number",
content: {
type: "text",
italic: true,
text: "Nothing about lighthouses...",
className: "info",
},
},
], ],
options: [ options: [
{ text: "Read 'weird messages' thread", next: "thread_weird" }, { text: "Read 'weird messages' thread", next: "thread_weird" },
@ -503,11 +556,17 @@ const boxingDayGame = {
}, },
thread_weird: { thread_weird: {
title: "Thread: Anyone else getting weird messages?", // title: "Thread: Anyone else getting weird messages?",
content: [ content: [
"═══ Anyone else getting weird messages? ═══", {
"", type: "table",
{ text: "Posted by: Static_User", className: "info" }, title: "Anyone else getting weird messages?",
headers: ["FROM", "TO", "DATE"],
rows: [["Static_User", "All", "25/12/99"]],
widths: [20, 20, 10],
align: ["left", "left", "left"],
style: "single",
},
" Got a strange PM last night. No sender listed.", " Got a strange PM last night. No sender listed.",
" Just a phone number and something about a 'cascade'.", " Just a phone number and something about a 'cascade'.",
" Probably spam, but creepy timing with Y2K coming up.", " Probably spam, but creepy timing with Y2K coming up.",
@ -519,14 +578,15 @@ const boxingDayGame = {
"", "",
"---", "---",
{ text: "Reply from: [DELETED USER]", className: "error" }, { text: "Reply from: [DELETED USER]", className: "error" },
"[This post has been removed]", "[This post cannot be accessed]",
"", "",
{ type: "delay", ms: 300 }, { type: "delay", ms: 1000 },
{ {
condition: "found_number", condition: "found_number",
content: { content: {
text: "You notice your message was similar...", text: "<br /><br /><em>You notice your message was similar...</em>",
className: "warning", html: true,
className: "info",
}, },
}, },
], ],
@ -534,17 +594,26 @@ const boxingDayGame = {
}, },
board_tech: { board_tech: {
title: "Tech Support", // title: "Tech Support",
content: [ content: [
"═══ TECH SUPPORT ═══", {
"", type: "table",
" [STICKY] READ FIRST: Y2K Compliance Guide", title: "TECH SUPPORT",
" [NEW] Modem dropping connection at midnight?", headers: ["#", "SUBJECT", "MSG", "LAST"],
" Best virus scanner for 1999?", rows: [
" How to increase download speeds", ["1", "READ FIRST: Y2K Compliance Guide", "152", "25/12"],
"", ["2", "Modem dropping connection at midnight?", "3", "25/12"],
["3", "How to increase download speeds", "98", "25/12"],
["4", "We are migrating to TELNET/IP on 01/04/00", "429", "11/12"],
["5", "Inputs not registering", "2", "29/11"],
],
widths: [4, 45, 6, 8],
align: ["right", "left", "right", "left"],
style: "single",
},
{ {
text: "Standard tech questions. Nothing unusual.", text: "Standard tech questions. Nothing unusual.",
italic: true,
className: "info", className: "info",
}, },
], ],
@ -668,46 +737,79 @@ const boxingDayGame = {
}, },
board_files: { board_files: {
title: "File Announcements", // title: "File Announcements",
content: [ content: [
"═══ FILE ANNOUNCEMENTS ═══", {
"", type: "table",
" [NEW] Y2K_FIX.ZIP - Y2K compliance patches", title: "FILE ANNOUNCEMENTS",
" [NEW] DOOM_WAD.ZIP - New Doom levels", headers: ["#", "SUBJECT", "MSG", "LAST"],
" FONTS99.ZIP - Cool fonts collection", rows: [
"", ["1", "1001FONTS.ZIP - Font Collection", "1", "25/12"],
{ text: "Nothing about cascades here.", className: "info" }, ["2", "Y2K_FIX.ZIP - Y2K compliance patches", "4", "23/12"],
["3", "DOOM_WAD.ZIP - New Doom Levels", "3", "11/12"],
["4", "BRUCE.JPEG - Just my dog :-)", "15", "20/11"],
["5", "CATS.GIF - All your base are belong to us", "1", "01/11"],
],
widths: [4, 45, 6, 8],
align: ["right", "left", "right", "left"],
style: "single",
},
{
text: "<em>New fonts... At last...</em>",
html: true,
className: "info",
},
{
text: "<em>Can't get distracted just yet.</em>",
html: true,
className: "info",
},
], ],
options: [{ text: "Back to boards", next: "browse_boards" }], options: [{ text: "Back to boards", next: "browse_boards" }],
}, },
dark_tower_files: { dark_tower_files: {
title: "File Library", //title: "File Library",
content: [ content: [
"═══ DARK TOWER FILE LIBRARY ═══", {
"", type: "table",
" /games - 234 files", title: "FILE LIBRARY",
" /utils - 156 files", headers: ["#", "DIR", "QTY", "UPDATED"],
" /images - 89 files", rows: [
" /music - 67 files", ["1", "/IMAGES", "234", "25/12"],
"", ["2", "/GAMES", "67", "12/12"],
{ text: "Standard BBS fare. Nothing unusual.", className: "info" }, ["3", "/MUSIC", "89", "30/11"],
["4", "/UTILS", "156", "23/11"],
["5", "/MISC", "13", "09/10"],
],
widths: [4, 25, 6, 8],
align: ["right", "left", "right", "left"],
style: "single",
},
{
text: "<em>Standard BBS fare. Nothing unusual.</em>",
html: true,
className: "info",
},
], ],
options: [{ text: "Back to main menu", next: "dark_tower_main" }], options: [{ text: "Back to main menu", next: "dark_tower_main" }],
}, },
whos_online: { whos_online: {
title: "Who's Online", //title: "Who's Online",
content: [ content: [
"═══ USERS ONLINE ═══",
"",
" 0BSERVER0 (you) - Main Menu",
" NightWatchman - SysOp Console",
" Static_User - Message Boards",
"",
{ {
text: "Last login: Signal_Lost - March 23, 1999", type: "table",
className: "warning", title: "CONNECTED USERS",
headers: ["#", "USER", "LOC", "UPDATED"],
rows: [
["1", "0BSERVER0", "Main Menu", "10:54 PM"],
["2", "Static_User", "Message Boards", "10:39 PM"],
["3", "NightWatchman", "SysOp Console", "10:12 PM"],
],
widths: [4, 15, 15, 8],
align: ["right", "left", "right", "left"],
style: "single",
}, },
], ],
options: [{ text: "Back", next: "dark_tower_main" }], options: [{ text: "Back", next: "dark_tower_main" }],
@ -875,20 +977,27 @@ const boxingDayGame = {
clear: true, clear: true,
content: [ content: [
{ {
type: "ansi", type: "ascii",
art: LIGHTHOUSE_HEADER, art: LIGHTHOUSE_HEADER,
className: "game-ansi-art center", className: "game-ascii",
}, },
"", "",
{ text: "T H E L I G H T H O U S E", className: "game-scene-title" }, { text: "T H E L I G H T H O U S E", className: "center" },
{ text: "Last updated: 12/24/1999 23:59:59", className: "info" }, { text: "Last updated: 24/12/1999 23:59:59", className: "center" },
{ text: "Users online: 1 (you)", className: "warning" },
"", "",
{ {
condition: { not: "visited.lighthouse_main" }, condition: { not: "visited.lighthouse_main" },
content: [ content: [
{ text: "Something feels wrong here.", className: "warning" }, {
{ text: "The BBS feels... frozen. Abandoned.", className: "info" }, text: "Something feels wrong here.",
italic: true,
className: "info",
},
{
text: "The BBS feels... frozen. Abandoned.",
italic: true,
className: "info",
},
"", "",
], ],
}, },
@ -903,7 +1012,7 @@ const boxingDayGame = {
], ],
}, },
], ],
onEnter: [{ set: "visited.lighthouse_main", value: true }], onAfterRender: [{ set: "visited.lighthouse_main", value: true }],
prompt: "Navigate:", prompt: "Navigate:",
options: [ options: [
{ text: "The Keeper's Log", next: "lighthouse_log" }, { text: "The Keeper's Log", next: "lighthouse_log" },
@ -921,30 +1030,42 @@ const boxingDayGame = {
}, },
lighthouse_log: { lighthouse_log: {
title: "The Keeper's Log", //title: "The Keeper's Log",
content: [ content: [
"═══ THE KEEPER'S LOG ═══", "═══ THE KEEPER'S LOG ═══",
"", "",
{ text: "Entry 1 - November 3, 1998", className: "info" }, {
text: "Entry 1 - November 3, 1998<br /><br />",
html: true,
className: "info",
},
" I've found something. In the static between radio stations.", " I've found something. In the static between radio stations.",
" Patterns. Structures. A language, maybe.", " Patterns. Structures. A language, maybe.",
"", {
{ text: "Entry 7 - December 12, 1998", className: "info" }, text: "<br />Entry 7 - December 12, 1998<br /><br />",
"The patterns are getting clearer.", html: true,
"They want to be understood.", className: "info",
},
" The patterns are getting clearer. They want to be understood.",
" They want to SPREAD.", " They want to SPREAD.",
"", {
{ text: "Entry 15 - March 19, 1999", className: "info" }, text: "<br />Entry 15 - March 19, 1999<br /><br />",
"CASCADE.EXE is complete.", html: true,
"A translator. A carrier. A key.", className: "info",
},
" CASCADE.EXE is complete. A translator. A carrier. A key.",
" When run at the right moment, it will open the door.", " When run at the right moment, it will open the door.",
"", {
{ text: "Entry 23 - December 24, 1999", className: "warning" }, text: "<br />Entry 23 - December 24, 1999<br /><br />",
html: true,
className: "info",
},
" The alignment approaches.", " The alignment approaches.",
" Seven days until the millennium.", " Seven days until the millennium.",
" I can hear them now. Always.", " I can hear them now. Always.",
"", "",
{ type: "typewriter", text: "They are beautiful.", speed: 60 }, { type: "typewriter", text: " They are beautiful...", speed: 100 },
{ type: "delay", ms: 2000 },
], ],
options: [{ text: "Back", next: "lighthouse_main" }], options: [{ text: "Back", next: "lighthouse_main" }],
}, },
@ -1113,7 +1234,7 @@ const boxingDayGame = {
], ],
onEnter: [ onEnter: [
{ set: "downloaded_cascade", value: true }, { set: "downloaded_cascade", value: true },
{ set: "archives_deleted", value: true }, // rm -rf effect { set: "archives_deleted", value: true }, // Archives are removed
], ],
next: "post_download", next: "post_download",
delay: 1500, delay: 1500,
@ -1172,6 +1293,10 @@ const boxingDayGame = {
choice_corrupted: { choice_corrupted: {
title: "Accessing SHADOW.DAT...", title: "Accessing SHADOW.DAT...",
clear: true, clear: true,
// sounds: [
// { id: "static", url: "/assets/audio/static.mp3" },
// { id: "glitch", url: "/assets/audio/glitch.mp3" },
// ],
content: [ content: [
{ type: "typewriter", text: "ATTEMPTING TO READ FILE...", speed: 50 }, { type: "typewriter", text: "ATTEMPTING TO READ FILE...", speed: 50 },
{ type: "delay", ms: 1000 }, { type: "delay", ms: 1000 },
@ -1183,6 +1308,8 @@ const boxingDayGame = {
{ text: "ERROR: ????????????????????", className: "error" }, { text: "ERROR: ????????????????????", className: "error" },
{ type: "delay", ms: 800 }, { type: "delay", ms: 800 },
"", "",
// Play glitch sound effect
//{ type: "sound", id: "glitch", volume: 0.5 },
{ type: "ascii", art: GLITCH_ART, className: "error" }, { type: "ascii", art: GLITCH_ART, className: "error" },
{ type: "delay", ms: 1000 }, { type: "delay", ms: 1000 },
"", "",
@ -1190,6 +1317,8 @@ const boxingDayGame = {
"", "",
{ type: "delay", ms: 600 }, { type: "delay", ms: 600 },
{ text: "A sound from your speakers.", className: "warning" }, { text: "A sound from your speakers.", className: "warning" },
// Play eerie static with voice
//{ type: "sound", id: "static", volume: 0.4, duration: 3000, fade: true },
{ {
text: "A voice, maybe. Or static shaped like words:", text: "A voice, maybe. Or static shaped like words:",
className: "info", className: "info",

View file

@ -161,3 +161,11 @@
.typewriter-line { .typewriter-line {
display: inline; display: inline;
} }
.typewriter-bold {
font-weight: bold;
}
.typewriter-italic {
font-style: italic;
}

View file

@ -0,0 +1,27 @@
---
title: "Week 5 - Back to Some Gamedev"
date: 2026-01-25
tags:
- weeknote
- weekly update
draft: false
---
- Emojii
- Memeojii
## Links I Found Interesting
- [AI Company Logos That Look Like Buttholes](https://velvetshark.com/ai-company-logos-that-look-like-buttholes) - Pretty self explanatory if you ask me.
## Music
If you like
{{< youtube MxekyGtqcNE >}}
## Next Week
Not necessary
Until next week!

BIN
content/buttons/222-2.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.