User prompt
Update with: function animateTerminalScroll() { // More comprehensive safety checks if (!gameState || !gameState.terminalScroll || !gameState.terminalContentContainer || !gameState.terminalContentContainer.parent) { return; // Exit if any required element is missing } var scroll = gameState.terminalScroll; scroll.isAnimating = true; // Calculate new position with easing var scrollDiff = scroll.targetScrollPosition - scroll.scrollPosition; var scrollStep = scrollDiff * SCROLL_ANIMATION_SPEED; scroll.scrollPosition += scrollStep; // Apply new position (with additional safety check) if (gameState.terminalContentContainer && gameState.terminalContentContainer.parent) { gameState.terminalContentContainer.y = gameState.terminalBaseY + scroll.scrollPosition; } // Continue animation if not close enough to target if (Math.abs(scrollDiff) > 1) { // Only schedule next animation frame if container still exists if (gameState.terminalContentContainer && gameState.terminalContentContainer.parent) { LK.setTimeout(animateTerminalScroll, 16); } else { scroll.isAnimating = false; // Stop animation if container is gone } } else { // Snap to exact position and end animation scroll.scrollPosition = scroll.targetScrollPosition; if (gameState.terminalContentContainer && gameState.terminalContentContainer.parent) { gameState.terminalContentContainer.y = gameState.terminalBaseY + scroll.scrollPosition; } scroll.isAnimating = false; } }
User prompt
Update with: function initGameWithIntro() { // Create a unique ID for this transition to avoid conflicts var transitionId = Date.now(); // Create first fade overlay with the unique ID var fadeInOverlay = LK.getAsset('overlayBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0, name: 'fadeOverlay' + transitionId + '_in' }); game.addChild(fadeInOverlay); // Fade in with the first overlay tween(fadeInOverlay, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { // Safely remove all children while (game.children.length > 0) { game.removeChild(game.children[0]); } // Initialize new game state initGame(); // IMPORTANT: Add a delay to ensure the terminal is fully initialized // before attempting to add and animate the fadeOutOverlay LK.setTimeout(function() { // Create a NEW overlay for fading out var fadeOutOverlay = LK.getAsset('overlayBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 1, name: 'fadeOverlay' + transitionId + '_out' }); // Make sure the fade overlay is the top-most element game.addChild(fadeOutOverlay); // Wait another frame to ensure WebGL has processed the new object LK.setTimeout(function() { // Now it's safe to tween the overlay tween(fadeOutOverlay, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { game.removeChild(fadeOutOverlay); } }); }, 16); // Wait one frame (approx 16ms at 60fps) }, 100); // Wait 100ms to ensure terminal is initialized } }); }
Code edit (2 edits merged)
Please save this source code
User prompt
Please fix the bug: 'TypeError: setTimeout is not a function. (In 'setTimeout(function () { // Fade out with the new overlay __$(268); tween(fadeOutOverlay, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { __$(269); game.removeChild(fadeOutOverlay); } }); }, 50)', 'setTimeout' is undefined)' in or related to this line: 'setTimeout(function () {' Line Number: 711
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'LK.setTimeout(function () {' Line Number: 711
User prompt
Update with: function animateTerminalScroll() { if (!gameState.terminalScroll || !gameState.terminalContentContainer) { return; // Guard against missing properties } var scroll = gameState.terminalScroll; scroll.isAnimating = true; // Calculate new position with easing var scrollDiff = scroll.targetScrollPosition - scroll.scrollPosition; var scrollStep = scrollDiff * SCROLL_ANIMATION_SPEED; scroll.scrollPosition += scrollStep; // Apply new position if (gameState.terminalContentContainer) { gameState.terminalContentContainer.y = gameState.terminalBaseY + scroll.scrollPosition; } // Continue animation if not close enough to target if (Math.abs(scrollDiff) > 1) { LK.setTimeout(animateTerminalScroll, 16); // ~60fps } else { // Snap to exact position and end animation scroll.scrollPosition = scroll.targetScrollPosition; if (gameState.terminalContentContainer) { gameState.terminalContentContainer.y = gameState.terminalBaseY + scroll.scrollPosition; } scroll.isAnimating = false; } }
User prompt
Update with: function showTitleScreen() { // Clear screen safely while (game.children.length > 0) { game.removeChild(game.children[0]); } // Set state gameState.state = STATES.TITLE; // Create background var background = LK.getAsset('overlayBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(background); // Create title var title = new Text2("VIBE CODER", { size: 120 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); title.anchor.set(0.5, 0.5); title.x = 2048 / 2; title.y = 800; game.addChild(title); // Create subtitle var subtitle = new Text2("The AI Coding Simulator", { size: 60 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); subtitle.anchor.set(0.5, 0.5); subtitle.x = 2048 / 2; subtitle.y = 1000; game.addChild(subtitle); // Create start button - twice as large var startButton = createCommandButton(">START", function() { initGameWithIntro(); }); startButton.scale.set(2); // Make the button twice as large startButton.x = 2048 / 2; startButton.y = 1400; game.addChild(startButton); }
User prompt
Replace with: function initGameWithIntro() { // Create a unique ID for this transition to avoid conflicts var transitionId = Date.now(); // Create first fade overlay with the unique ID var fadeInOverlay = LK.getAsset('overlayBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0, name: 'fadeOverlay' + transitionId + '_in' }); game.addChild(fadeInOverlay); // Fade in with the first overlay tween(fadeInOverlay, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { // Safely remove all children while (game.children.length > 0) { game.removeChild(game.children[0]); } // Initialize new game state initGame(); // Create a NEW overlay for fading out - never reuse the old one var fadeOutOverlay = LK.getAsset('overlayBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 1, name: 'fadeOverlay' + transitionId + '_out' }); // Make sure the fade overlay is the top-most element game.addChild(fadeOutOverlay); // Small delay to ensure everything is initialized LK.setTimeout(function() { // Fade out with the new overlay tween(fadeOutOverlay, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { game.removeChild(fadeOutOverlay); } }); }, 50); } }); } āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update as needed with: function initGame() { // Reset game state gameState = { state: STATES.PLAYING, day: 1, maxDays: 10, vibePoints: 100, codeCoherence: 100, bugs: 0, commandsUsed: 0, maxCommandsPerDay: 3, terminal: { log: [], statusLine: "", currentTask: "", featureTags: [] }, conceptCards: { platform: null, visual: null, genre: null, mechanic: null, feature: null }, cursorTracking: { lineCount: 0 }, // Add these new properties needed by our scrolling system terminalScroll: { scrollPosition: 0, targetScrollPosition: 0, isAnimating: false }, terminalContentContainer: null, // Will be set in createTerminal() terminalBaseY: 2732 * 0.15, terminalLineHeight: 30 * TEXT_SIZE_MULTIPLIER, terminalMaxVisibleHeight: MAX_VISIBLE_LINES * 30 * TEXT_SIZE_MULTIPLIER }; createTerminal(); // Sequence the initial text LK.setTimeout(function () { addToTerminalLogWithEffect("VIBE CODER OS v1.0", function () { LK.setTimeout(function () { addToTerminalLogWithEffect("INITIALIZING NEW PROJECT...", function () { LK.setTimeout(function () { addToTerminalLogWithEffect("SELECT PLATFORM TO BEGIN", function () { LK.setTimeout(function () { showConceptSelection('platform'); }, 500); }); }, 500); }); }, 500); }); }, 500); }
User prompt
Update with: function initGameWithIntro() { // Clear screen with fade effect var fadeOverlay = LK.getAsset('overlayBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0 }); game.addChild(fadeOverlay); // Fade to black tween(fadeOverlay, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { // Save reference to fadeOverlay var tempOverlay = fadeOverlay; // Remove all EXCEPT fadeOverlay for (var i = game.children.length - 1; i >= 0; i--) { if (game.children[i] !== tempOverlay) { game.removeChild(game.children[i]); } } // Initialize the game initGame(); // Now fade out the overlay that we kept tween(tempOverlay, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { game.removeChild(tempOverlay); } }); } }); } āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
Please fix the bug: 'Timeout.tick error: tween.to is not a function. (In 'tween.to(overlay, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { __$(228); game.removeChild(overlay); __$(229); game.removeChild(bootText); __$(230); showTitleScreen(); } })', 'tween.to' is undefined)' in or related to this line: 'tween.to(overlay, {' Line Number: 609
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'tween(fadeOverlay, {' Line Number: 692
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'tween.to(fadeOverlay, {' Line Number: 679
User prompt
Update with: tween.to(fadeOverlay, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { // Clear screen and init game state while (game.children.length > 0) { game.removeChild(game.children[0]); } initGame(); // Fade from black fadeOverlay.alpha = 1; game.addChild(fadeOverlay); tween.to(fadeOverlay, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { game.removeChild(fadeOverlay); } }); } }); āŖš” Consider importing and using the following plugins: @upit/tween.v1
User prompt
Replace with: function addToTerminalLogWithEffect(text, callback) { var typingState = { text: text, position: 0, currentText: "" }; // Add new entry to log var logIndex = gameState.terminal.log.length; gameState.terminal.log.push(""); function type() { if (typingState.position < typingState.text.length) { // Get current character var currentChar = typingState.text[typingState.position]; // Add character to current text typingState.currentText += currentChar; // Update log entry gameState.terminal.log[logIndex] = typingState.currentText; // Update terminal display updateTerminal(); // Continue typing typingState.position++; LK.setTimeout(type, 50); } else if (callback) { callback(); } } type(); }
User prompt
Replace with: function updateCursorPosition() { if (!gameState.cursor) return; // Get the last log entry var lastLogEntry = gameState.terminal.log[gameState.terminal.log.length - 1] || ""; // Get the last line of text var lastLine = lastLogEntry.split("\n").pop() || ""; // Set X position based on the last line's character count gameState.cursor.x = lastLine.length * 14 * TEXT_SIZE_MULTIPLIER; // Set Y position at the bottom of the rendered text gameState.cursor.y = gameState.logText.height; }
User prompt
Replace with: function updateTerminal() { // Update status line (unchanged) var conceptString = ""; for (var category in gameState.conceptCards) { if (gameState.conceptCards[category]) { conceptString += "#" + gameState.conceptCards[category].replace(/\s+/g, '') + " "; } } var statusText = "DAY: " + gameState.day + "/" + gameState.maxDays + " | VIBES: " + gameState.vibePoints + "%" + " | COHERENCE: " + gameState.codeCoherence + "%" + " | BUGS: " + gameState.bugs + " | " + conceptString; gameState.statusLineText.setText(statusText); // Update main terminal log text gameState.logText.setText(gameState.terminal.log.join('\n\n')); // Get actual rendered text height var contentHeight = gameState.logText.height; // Check if content exceeds visible area if (contentHeight > gameState.terminalMaxVisibleHeight) { // Calculate target scroll position to show the latest text at the bottom var targetScrollY = -(contentHeight - gameState.terminalMaxVisibleHeight); // Set target scroll position gameState.terminalScroll.targetScrollPosition = targetScrollY; // Start animation if not already running if (!gameState.terminalScroll.isAnimating) { animateTerminalScroll(); } } else { // Reset scroll position if content fits gameState.terminalScroll.scrollPosition = 0; gameState.terminalScroll.targetScrollPosition = 0; gameState.terminalContentContainer.y = gameState.terminalBaseY; } // Update cursor position updateCursorPosition(); }
User prompt
Replace with: function createTerminal() { // Create main terminal container var terminal = new Container(); game.addChild(terminal); gameState.terminalContainer = terminal; // Create full screen background var bg = LK.getAsset('terminalBg', { width: 2048, height: 2732, x: 0, y: 2732 * 0.05 }); terminal.addChild(bg); // Create scrollable content container var contentContainer = new Container(); contentContainer.x = 50; contentContainer.y = 2732 * 0.15; terminal.addChild(contentContainer); gameState.terminalContentContainer = contentContainer; // Create status line (outside scroll container) var statusLine = new Text2("", { size: 30 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); statusLine.x = 50; statusLine.y = 2732 * 0.10; terminal.addChild(statusLine); gameState.statusLineText = statusLine; // Create main terminal output (inside scroll container) var logText = new Text2("", { size: 24 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00, align: 'left', wordWrap: true, wordWrapWidth: 1900 }); logText.x = 0; // Relative to content container logText.y = 0; // Relative to content container contentContainer.addChild(logText); gameState.logText = logText; // Create cursor (inside scroll container) var cursor = new Text2("_", { size: 24 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); cursor.x = 0; cursor.y = 0; contentContainer.addChild(cursor); gameState.cursor = cursor; // Store reference values gameState.terminalBaseY = 2732 * 0.15; gameState.terminalLineHeight = 30 * TEXT_SIZE_MULTIPLIER; gameState.terminalMaxVisibleHeight = MAX_VISIBLE_LINES * gameState.terminalLineHeight; // Initialize scroll state gameState.terminalScroll = { scrollPosition: 0, targetScrollPosition: 0, isAnimating: false }; // Make cursor blink LK.setInterval(function() { if (gameState.cursor) { gameState.cursor.visible = !gameState.cursor.visible; } }, 500); // Create mask for terminal viewing area var maskHeight = gameState.terminalMaxVisibleHeight; var mask = LK.getAsset('terminalBg', { width: 2048, height: maskHeight, x: 0, y: 0, alpha: 1 }); mask.x = 0; mask.y = 0; terminal.addChild(mask); contentContainer.mask = mask; }
Code edit (1 edits merged)
Please save this source code
User prompt
Update as needed with: // DIRECT FIX FOR CURSOR DRIFT ISSUE // First, create a position tracker for cursor if (!gameState.cursorTracking) { gameState.cursorTracking = { lineCount: 0 // Track the exact number of lines }; } // Modify addToTerminalLogWithEffect to EXPLICITLY track lines function addToTerminalLogWithEffect(text, callback) { var typingState = { text: text, position: 0, currentText: "" }; // Add new entry to log var logIndex = gameState.terminal.log.length; gameState.terminal.log.push(""); // Track any new lines being added var initialLinesAdded = 0; // If this is not the first entry, count the entry separation if (logIndex > 0) { initialLinesAdded = 1; // One blank line between entries } // Update line count gameState.cursorTracking.lineCount += initialLinesAdded; function type() { if (typingState.position < typingState.text.length) { // Get previous character to check for newline var prevChar = typingState.currentText.slice(-1); // Get current character var currentChar = typingState.text[typingState.position]; // Add character to current text typingState.currentText += currentChar; // Check if we're adding a newline if (currentChar === '\n') { // Increment line counter gameState.cursorTracking.lineCount++; } // Update log entry gameState.terminal.log[logIndex] = typingState.currentText; // Update terminal display updateTerminal(); // Continue typing typingState.position++; LK.setTimeout(type, 50); } else if (callback) { callback(); } } type(); } // Extremely simple cursor position function using the tracked line count function updateCursorPosition() { if (!gameState.cursor) return; // Initialize cursor tracking if it doesn't exist if (!gameState.cursorTracking) { gameState.cursorTracking = { lineCount: 0 }; } // Get the last log entry for X position var lastLogEntry = gameState.terminal.log[gameState.terminal.log.length - 1] || ""; var lastLine = lastLogEntry.split("\n").pop() || ""; // Set X position gameState.cursor.x = 50 + (lastLine.length * 14 * TEXT_SIZE_MULTIPLIER); // Set Y position using our explicitly tracked line count var baseY = 2732 * 0.15; // Terminal base Y position var lineHeight = 30 * TEXT_SIZE_MULTIPLIER; // Calculate Y position gameState.cursor.y = baseY + (gameState.cursorTracking.lineCount * lineHeight); } // Reset line tracking when starting a new game function initGame() { // Reset game state gameState = { state: STATES.PLAYING, day: 1, // ... other state variables ... // Reset cursor tracking cursorTracking: { lineCount: 0 } }; // ... rest of initGame function ... }
User prompt
Update with: function updateCursorPosition() { if (!gameState.cursor) return; // Fixed constants var baseY = gameState.terminalBaseY; var lineHeight = gameState.terminalLineHeight; // Get the last log entry to calculate X position var lastLogEntry = gameState.terminal.log[gameState.terminal.log.length - 1] || ""; var lastLine = lastLogEntry.split("\n").pop() || ""; gameState.cursor.x = 50 + (lastLine.length * 14 * TEXT_SIZE_MULTIPLIER); // SIMPLE APPROACH: Always position cursor at the correct number of lines from the base // Calculate ONLY the lines in current log entries, ignoring the display formatting var lineCount = 0; // Count single newlines within each entry for (var i = 0; i < gameState.terminal.log.length; i++) { var entryLines = gameState.terminal.log[i].split("\n").length; lineCount += entryLines; } // Add space for one blank line between entries (count is one less than entries) lineCount += Math.max(0, gameState.terminal.log.length - 1); // Position cursor at exact last line (subtract 1) lineCount -= 1; // Set final cursor Y position gameState.cursor.y = baseY + (lineCount * lineHeight); } // Modify how we update the terminal display to be consistent function updateTerminal() { // Update status line (unchanged) var conceptString = ""; for (var category in gameState.conceptCards) { if (gameState.conceptCards[category]) { conceptString += "#" + gameState.conceptCards[category].replace(/\s+/g, '') + " "; } } var statusText = "DAY: " + gameState.day + "/" + gameState.maxDays + " | VIBES: " + gameState.vibePoints + "%" + " | COHERENCE: " + gameState.codeCoherence + "%" + " | BUGS: " + gameState.bugs + " | " + conceptString; gameState.statusLineText.setText(statusText); // Update main terminal log - ONLY ONE NEWLINE between entries for consistency gameState.logText.setText(gameState.terminal.log.join('\n\n')); // Update cursor position updateCursorPosition(); }
User prompt
Update with: function createTerminal() { // Create main terminal container var terminal = new Container(); game.addChild(terminal); gameState.terminalContainer = terminal; // Create full screen background var bg = LK.getAsset('terminalBg', { width: 2048, height: 2732, x: 0, y: 2732 * 0.05 }); terminal.addChild(bg); // Create status line var statusLine = new Text2("", { size: 30 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); statusLine.x = 50; statusLine.y = 2732 * 0.10; terminal.addChild(statusLine); gameState.statusLineText = statusLine; // Create main terminal output var logText = new Text2("", { size: 24 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00, align: 'left', wordWrap: true, wordWrapWidth: 1900 }); logText.x = 50; logText.y = 2732 * 0.15; terminal.addChild(logText); gameState.logText = logText; // Create cursor - BUT DON'T UPDATE ITS POSITION YET var cursor = new Text2("_", { size: 24 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); cursor.x = 50; // Initial X position cursor.y = 2732 * 0.15; // Initial Y position at first line terminal.addChild(cursor); gameState.cursor = cursor; // Store initial Y position and line height for future reference gameState.terminalBaseY = 2732 * 0.15; gameState.terminalLineHeight = 30 * TEXT_SIZE_MULTIPLIER; // Make cursor blink LK.setInterval(function() { if (gameState.cursor) { gameState.cursor.visible = !gameState.cursor.visible; } }, 500); }
User prompt
Update with: function createTerminal() { // Create main terminal container var terminal = new Container(); game.addChild(terminal); gameState.terminalContainer = terminal; // Create full screen background var bg = LK.getAsset('terminalBg', { width: 2048, height: 2732, x: 0, y: 2732 * 0.05 }); terminal.addChild(bg); // Create status line var statusLine = new Text2("", { size: 30 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); statusLine.x = 50; statusLine.y = 2732 * 0.10; terminal.addChild(statusLine); gameState.statusLineText = statusLine; // Create main terminal output var logText = new Text2("", { size: 24 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00, align: 'left', wordWrap: true, wordWrapWidth: 1900 }); logText.x = 50; logText.y = 2732 * 0.15; terminal.addChild(logText); gameState.logText = logText; // Create cursor - BUT DON'T UPDATE ITS POSITION YET var cursor = new Text2("_", { size: 24 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); cursor.x = 50; // Initial X position cursor.y = 2732 * 0.15; // Initial Y position at first line terminal.addChild(cursor); gameState.cursor = cursor; // Store initial Y position and line height for future reference gameState.terminalBaseY = 2732 * 0.15; gameState.terminalLineHeight = 30 * TEXT_SIZE_MULTIPLIER; // Make cursor blink LK.setInterval(function() { if (gameState.cursor) { gameState.cursor.visible = !gameState.cursor.visible; } }, 500); }
User prompt
Update with: function updateCursorPosition() { if (!gameState.logText || !gameState.cursor) { return; } // Get the last log entry var lastLogEntry = gameState.terminal.log[gameState.terminal.log.length - 1] || ""; var lastLine = lastLogEntry.split("\n").pop(); // Set X position based on the width of the last line gameState.cursor.x = 50 + (lastLine.length * 14 * TEXT_SIZE_MULTIPLIER); // Base position and line height var baseY = 2732 * 0.15; // Terminal text Y position var lineHeight = 30 * TEXT_SIZE_MULTIPLIER; // CRITICAL FIX: Calculate EXACT line count including double newlines var exactLineCount = 0; // For each log entry for (var i = 0; i < gameState.terminal.log.length; i++) { // Count lines in this entry var entry = gameState.terminal.log[i]; var linesInEntry = entry.split("\n").length; exactLineCount += linesInEntry; // Add TWO lines of spacing after each entry except the last one if (i < gameState.terminal.log.length - 1) { exactLineCount += 2; // This is crucial - we're adding TWO newlines in the setText call } } // Subtract one to position cursor on the last line, not after it exactLineCount -= 1; // Set final cursor Y position gameState.cursor.y = baseY + (exactLineCount * lineHeight); }
User prompt
Update with: function updateCursorPosition() { if (!gameState.logText || !gameState.cursor) { return; } // Get the last log entry var lastLogEntry = gameState.terminal.log[gameState.terminal.log.length - 1] || ""; var lastLine = lastLogEntry.split("\n").pop(); // Set X position based on the width of the last line gameState.cursor.x = 50 + (lastLine.length * 14 * TEXT_SIZE_MULTIPLIER); // Base position from terminal output var baseY = 2732 * 0.15; var lineHeight = 30 * TEXT_SIZE_MULTIPLIER; // Fixed approach to prevent drift: // Use the current terminal log to calculate position // Get the text that's currently displayed in the terminal var displayedText = gameState.terminal.log.join('\n\n'); // This matches how text is rendered if (!displayedText) { // If no text yet, position at the baseline gameState.cursor.y = baseY; } else { // Count all visible lines including the double newlines between entries var textLines = displayedText.split('\n').length; // Calculate position, accounting for the last line gameState.cursor.y = baseY + ((textLines - 1) * lineHeight); } }
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Initialize Game ****/ // Core game setup with improved text sizes /**** * Core Game Systems ****/ // Initialize game object var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Text size multiplier for readability var TEXT_SIZE_MULTIPLIER = 2; // Game states var STATES = { TITLE: 'title', PLAYING: 'playing', GAME_OVER: 'gameOver' }; var MAX_VISIBLE_LINES = 20; // Maximum number of visible lines in terminal window var SCROLL_ANIMATION_SPEED = 0.2; // Easing factor for smooth scrolling // Game state object var gameState = { state: STATES.TITLE, day: 1, maxDays: 10, vibePoints: 100, codeCoherence: 100, bugs: 0, commandsUsed: 0, maxCommandsPerDay: 3, terminal: { log: [], statusLine: "", currentTask: "", featureTags: [] }, conceptCards: { platform: null, visual: null, genre: null, mechanic: null, feature: null }, cursorTracking: { lineCount: 0 // Track the exact number of lines } }; // Concept options var conceptOptions = { platform: ["Mobile", "VR", "Console", "PC", "Smart Fridge", "Smart Watch", "Metaverse"], visual: ["Pixel Art", "ASCII", "Hand-drawn", "Corporate PowerPoint", "Low-poly", "Claymation"], genre: ["Horror", "Dating Sim", "RPG", "Educational", "Battle Royale", "Idle Clicker"], mechanic: ["Gacha", "Physics-based", "Deckbuilding", "Match-3", "Auto-battler"], feature: ["Cloud Save", "Microtransactions", "AI Companions", "Procedural Generation", "NFT Integration"] }; // Available commands based on development stage var commandSets = { initial: [ // Basic commands { text: "Make it run faster", response: "Attempting to accelerate undefined systems...", vibePoints: 10, coherenceImpact: 15, bugChance: 0.3 }, { text: "Optimize code flow", response: "Restructuring algorithmic pathways...", vibePoints: 15, coherenceImpact: 10, bugChance: 0.2 }, { text: "Debug system", response: "Scanning for anomalies...", vibePoints: 5, coherenceImpact: 5, bugChance: 0.1 }, { text: "Implement AI assistance", response: "WARNING: AI recursion detected", vibePoints: -5, coherenceImpact: 20, bugChance: 0.4 }, { text: "Refactor codebase", response: "Restructuring core systems...", vibePoints: 20, coherenceImpact: 25, bugChance: 0.5 }, { text: "Apply design patterns", response: "Implementing architectural frameworks...", vibePoints: 15, coherenceImpact: 15, bugChance: 0.3 }, { text: "Enable quantum computing", response: "ERROR: Quantum decoherence detected", vibePoints: -10, coherenceImpact: 30, bugChance: 0.6 }], // Platform-specific commands platform_vr: [{ text: "Reduce motion sickness", response: "Implementing anti-nausea protocols...", vibePoints: 15, coherenceImpact: 10, bugChance: 0.2 }, { text: "Enhance hand tracking", response: "Calibrating spatial recognition...", vibePoints: 20, coherenceImpact: 15, bugChance: 0.3 }, { text: "Add haptic feedback", response: "Integrating tactile response systems...", vibePoints: 10, coherenceImpact: 20, bugChance: 0.4 }, { text: "Optimize frame rate", response: "Adjusting refresh cycle timing...", vibePoints: 15, coherenceImpact: 15, bugChance: 0.3 }, { text: "Enable room scaling", response: "Calculating spatial dimensions...", vibePoints: 10, coherenceImpact: 25, bugChance: 0.4 }, { text: "Implement gesture controls", response: "Mapping kinetic inputs...", vibePoints: 15, coherenceImpact: 20, bugChance: 0.4 }, { text: "Add virtual mirrors", response: "WARNING: Reality recursion detected", vibePoints: -5, coherenceImpact: 30, bugChance: 0.6 }] }; var combinationCommands = { "VR_ASCII": [{ text: "Implement 3D text rendering", response: "Converting ASCII to volumetric data...", vibePoints: 15, coherenceImpact: 20, bugChance: 0.4 }, { text: "Add depth perception", response: "Calculating character z-index...", vibePoints: 10, coherenceImpact: 15, bugChance: 0.3 } // Add more specific combinations ] }; /**** * Terminal Interface ****/ function createTerminal() { // Create main terminal container var terminal = new Container(); game.addChild(terminal); gameState.terminalContainer = terminal; // Create full screen background var bg = LK.getAsset('terminalBg', { width: 2048, height: 2732, x: 0, y: 2732 * 0.05 }); terminal.addChild(bg); // Create scrollable content container var contentContainer = new Container(); contentContainer.x = 50; contentContainer.y = 2732 * 0.15; terminal.addChild(contentContainer); gameState.terminalContentContainer = contentContainer; // Create status line (outside scroll container) var statusLine = new Text2("", { size: 30 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); statusLine.x = 50; statusLine.y = 2732 * 0.10; terminal.addChild(statusLine); gameState.statusLineText = statusLine; // Create main terminal output (inside scroll container) var logText = new Text2("", { size: 24 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00, align: 'left', wordWrap: true, wordWrapWidth: 1900 }); logText.x = 0; // Relative to content container logText.y = 0; // Relative to content container contentContainer.addChild(logText); gameState.logText = logText; // Create cursor (inside scroll container) var cursor = new Text2("_", { size: 24 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); cursor.x = 0; cursor.y = 0; contentContainer.addChild(cursor); gameState.cursor = cursor; // Store reference values gameState.terminalBaseY = 2732 * 0.15; gameState.terminalLineHeight = 30 * TEXT_SIZE_MULTIPLIER; gameState.terminalMaxVisibleHeight = MAX_VISIBLE_LINES * gameState.terminalLineHeight; // Initialize scroll state gameState.terminalScroll = { scrollPosition: 0, targetScrollPosition: 0, isAnimating: false }; // Make cursor blink LK.setInterval(function () { if (gameState.cursor) { gameState.cursor.visible = !gameState.cursor.visible; } }, 500); // Create mask for terminal viewing area var maskHeight = gameState.terminalMaxVisibleHeight; var mask = LK.getAsset('terminalBg', { width: 2048, height: maskHeight, x: 0, y: 0, alpha: 1 }); mask.x = 0; mask.y = 0; terminal.addChild(mask); contentContainer.mask = mask; } // Update command prompts with dynamic sizing and adjusted positioning function createCommandPrompts() { if (gameState.promptContainer) { game.removeChild(gameState.promptContainer); } var promptContainer = new Container(); promptContainer.x = 2048 / 2; promptContainer.y = 2732 * 0.75; // Moved to 75% from top game.addChild(promptContainer); gameState.promptContainer = promptContainer; // Get current command set and select 5 random commands var commands = getCurrentCommands(); var availableCommands = shuffleArray(commands).slice(0, 5); // Create END DAY button with dynamic width var endDayText = "END DAY"; var endDayWidth = calculateButtonWidth(endDayText); var endDayButton = createCommandButton(endDayText, function () { endDay(); }, endDayWidth); endDayButton.x = 600; endDayButton.y = 50; promptContainer.addChild(endDayButton); // Create command buttons with dynamic widths availableCommands.forEach(function (command, index) { var buttonText = ">" + command.text; var buttonWidth = calculateButtonWidth(buttonText); var button = createCommandButton(buttonText, function () { if (gameState.commandsUsed < 3) { executeCommand(command); } else { addToTerminalLogWithEffect("ERROR: Maximum commands (3) already used for today"); } }, buttonWidth); button.x = -600; button.y = 50 + index * 120; promptContainer.addChild(button); }); } function getCurrentCommands() { var availableCommands = []; // Add base commands availableCommands = availableCommands.concat(commandSets.initial); // Add platform-specific commands if (gameState.conceptCards.platform) { var platformCommands = commandSets['platform_' + gameState.conceptCards.platform.toLowerCase()]; if (platformCommands) { availableCommands = availableCommands.concat(platformCommands); } } // Add combination-specific commands if (gameState.conceptCards.platform && gameState.conceptCards.visual) { var combinationKey = gameState.conceptCards.platform + "_" + gameState.conceptCards.visual; var combinationSet = combinationCommands[combinationKey]; if (combinationSet) { availableCommands = availableCommands.concat(combinationSet); } } // Add day-specific commands if (gameState.day > 5) { availableCommands = availableCommands.concat(getLateGameCommands()); } return availableCommands; } function executeCommand(command) { if (gameState.commandsUsed >= gameState.maxCommandsPerDay) { addToTerminalLogWithEffect("ERROR: Daily command limit reached"); return; } // Apply command effects gameState.vibePoints = Math.max(0, gameState.vibePoints + command.vibePoints); gameState.codeCoherence = Math.max(0, gameState.codeCoherence - command.coherenceImpact); gameState.commandsUsed++; // Add to terminal log addToTerminalLogWithEffect(">" + command.text); // Show response LK.setTimeout(function () { addToTerminalLogWithEffect(command.response); checkForBugs(command); updateTerminal(); checkGameState(); }, 500); } function animateTerminalScroll() { // More comprehensive safety checks if (!gameState || !gameState.terminalScroll || !gameState.terminalContentContainer || !gameState.terminalContentContainer.parent) { return; // Exit if any required element is missing } var scroll = gameState.terminalScroll; scroll.isAnimating = true; // Calculate new position with easing var scrollDiff = scroll.targetScrollPosition - scroll.scrollPosition; var scrollStep = scrollDiff * SCROLL_ANIMATION_SPEED; scroll.scrollPosition += scrollStep; // Apply new position (with additional safety check) if (gameState.terminalContentContainer && gameState.terminalContentContainer.parent) { gameState.terminalContentContainer.y = gameState.terminalBaseY + scroll.scrollPosition; } // Continue animation if not close enough to target if (Math.abs(scrollDiff) > 1) { // Only schedule next animation frame if container still exists if (gameState.terminalContentContainer && gameState.terminalContentContainer.parent) { LK.setTimeout(animateTerminalScroll, 16); } else { scroll.isAnimating = false; // Stop animation if container is gone } } else { // Snap to exact position and end animation scroll.scrollPosition = scroll.targetScrollPosition; if (gameState.terminalContentContainer && gameState.terminalContentContainer.parent) { gameState.terminalContentContainer.y = gameState.terminalBaseY + scroll.scrollPosition; } scroll.isAnimating = false; } } function addToTerminalLogWithEffect(text, callback) { var typingState = { text: text, position: 0, currentText: "" }; // Add new entry to log var logIndex = gameState.terminal.log.length; gameState.terminal.log.push(""); function type() { if (typingState.position < typingState.text.length) { // Get current character var currentChar = typingState.text[typingState.position]; // Add character to current text typingState.currentText += currentChar; // Update log entry gameState.terminal.log[logIndex] = typingState.currentText; // Update terminal display updateTerminal(); // Continue typing typingState.position++; LK.setTimeout(type, 50); } else if (callback) { callback(); } } type(); } function updateTerminal() { // Update status line var conceptString = ""; for (var category in gameState.conceptCards) { if (gameState.conceptCards[category]) { conceptString += "#" + gameState.conceptCards[category].replace(/\s+/g, '') + " "; } } var statusText = "DAY: " + gameState.day + "/" + gameState.maxDays + " | VIBES: " + gameState.vibePoints + "%" + " | COHERENCE: " + gameState.codeCoherence + "%" + " | BUGS: " + gameState.bugs + " | " + conceptString; gameState.statusLineText.setText(statusText); // Update main terminal log text gameState.logText.setText(gameState.terminal.log.join('\n\n')); // Get actual rendered text height var contentHeight = gameState.logText.height; // Check if content exceeds visible area if (contentHeight > gameState.terminalMaxVisibleHeight) { // Calculate target scroll position to show the latest text at the bottom var targetScrollY = -(contentHeight - gameState.terminalMaxVisibleHeight); // Set target scroll position gameState.terminalScroll.targetScrollPosition = targetScrollY; // Start animation if not already running if (!gameState.terminalScroll.isAnimating) { animateTerminalScroll(); } } else { // Reset scroll position if content fits gameState.terminalScroll.scrollPosition = 0; gameState.terminalScroll.targetScrollPosition = 0; gameState.terminalContentContainer.y = gameState.terminalBaseY; } // Update cursor position updateCursorPosition(); } function updateCursorPosition() { if (!gameState.cursor) { return; } // Get the last log entry var lastLogEntry = gameState.terminal.log[gameState.terminal.log.length - 1] || ""; // Get the last line of text var lastLine = lastLogEntry.split("\n").pop() || ""; // Set X position based on the last line's character count gameState.cursor.x = lastLine.length * 14 * TEXT_SIZE_MULTIPLIER; // Set Y position at the bottom of the rendered text gameState.cursor.y = gameState.logText.height; } /**** * Game Flow ****/ function showConceptSelection(category) { // Create selection container var selectionContainer = new Container(); selectionContainer.x = 2048 / 2; selectionContainer.y = 2732 / 2; game.addChild(selectionContainer); // Create background var bg = LK.getAsset('terminalBg', { width: 1800, height: 1200, anchorX: 0.5, anchorY: 0.5 }); selectionContainer.addChild(bg); // Create title var title = new Text2("SELECT " + category.toUpperCase(), { size: 40 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); title.anchor.set(0.5, 0); title.y = -500; selectionContainer.addChild(title); // Get and display options var options = conceptOptions[category].slice(); // Use slice to avoid modifying the original array var selectedOptions = []; while (selectedOptions.length < 3 && options.length > 0) { var index = Math.floor(Math.random() * options.length); selectedOptions.push(options[index]); options.splice(index, 1); // Remove selected option from the copy } selectedOptions.forEach(function (option, index) { var button = createCommandButton(">" + option, function () { // Changed createButton to createCommandButton gameState.conceptCards[category] = option; game.removeChild(selectionContainer); // Sequence the post-selection events addToTerminalLogWithEffect("SELECTED " + category.toUpperCase() + ": " + option, function () { LK.setTimeout(function () { addToTerminalLogWithEffect("INITIALIZING COMMAND INTERFACE...", function () { LK.setTimeout(function () { createCommandPrompts(); // Create commands only after initialization text updateTerminal(); // Update terminal *after* prompts are created }, 500); }); }, 500); }); }); button.x = 0; // Keep x centered relative to container button.y = -200 + index * 150; // Adjusted spacing for button height selectionContainer.addChild(button); }); } // Updated button creation with more precise width handling function createCommandButton(text, callback, width) { var button = new Container(); // Create a temporary text object to measure actual text width var measureText = new Text2(text, { size: 30 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); var actualTextWidth = measureText.width + 100; // Add padding var finalWidth = Math.max(width, actualTextWidth); // Add button background with calculated width var bg = LK.getAsset('buttonBg', { width: finalWidth, height: 100, color: 0x333333 }); bg.anchor.set(0.5, 0.5); button.addChild(bg); // Add text var textObj = new Text2(text, { size: 30 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); textObj.anchor.set(0.5, 0.5); button.addChild(textObj); // Make interactive button.interactive = true; button.down = function () { button.scale.set(0.95); }; button.up = function () { button.scale.set(1); if (callback) { callback(); } }; return button; } // Launch sequence functions function showLaunchScreen() { // Black overlay var overlay = LK.getAsset('overlayBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(overlay); // "Booting up" text - store initial text in a variable var bootMessage = "BOOTING VIBE CODER OS v1.0..."; var bootText = new Text2(bootMessage, { size: 40 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); bootText.anchor.set(0.5, 0.5); bootText.x = 2048 / 2; bootText.y = 2732 / 2; game.addChild(bootText); // Simulate boot sequence - concatenate to our variable instead LK.setTimeout(function () { bootMessage += "\nINITIALIZING TERMINAL..."; bootText.setText(bootMessage); LK.setTimeout(function () { bootMessage += "\nLOADING VIBE DATABASE..."; bootText.setText(bootMessage); LK.setTimeout(function () { bootMessage += "\nCONNECTING TO AI SUBSYSTEMS..."; bootText.setText(bootMessage); LK.setTimeout(function () { bootMessage += "\nREADY!"; bootText.setText(bootMessage); LK.setTimeout(function () { // Fade out tween(overlay, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { game.removeChild(overlay); game.removeChild(bootText); showTitleScreen(); } }); }, 800); }, 800); }, 800); }, 800); }, 800); } function showTitleScreen() { // Clear screen safely while (game.children.length > 0) { game.removeChild(game.children[0]); } // Set state gameState.state = STATES.TITLE; // Create background var background = LK.getAsset('overlayBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(background); // Create title var title = new Text2("VIBE CODER", { size: 120 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); title.anchor.set(0.5, 0.5); title.x = 2048 / 2; title.y = 800; game.addChild(title); // Create subtitle var subtitle = new Text2("The AI Coding Simulator", { size: 60 * TEXT_SIZE_MULTIPLIER, fill: 0x00ff00 }); subtitle.anchor.set(0.5, 0.5); subtitle.x = 2048 / 2; subtitle.y = 1000; game.addChild(subtitle); // Create start button - twice as large var startButton = createCommandButton(">START", function () { initGameWithIntro(); }); startButton.scale.set(2); // Make the button twice as large startButton.x = 2048 / 2; startButton.y = 1400; game.addChild(startButton); } function initGameWithIntro() { // Create a unique ID for this transition to avoid conflicts var transitionId = Date.now(); // Create first fade overlay with the unique ID var fadeInOverlay = LK.getAsset('overlayBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 0, name: 'fadeOverlay' + transitionId + '_in' }); game.addChild(fadeInOverlay); // Fade in with the first overlay tween(fadeInOverlay, { alpha: 1 }, { duration: 500, onFinish: function onFinish() { // Safely remove all children while (game.children.length > 0) { game.removeChild(game.children[0]); } // Initialize new game state initGame(); // IMPORTANT: Add a delay to ensure the terminal is fully initialized // before attempting to add and animate the fadeOutOverlay LK.setTimeout(function () { // Create a NEW overlay for fading out var fadeOutOverlay = LK.getAsset('overlayBg', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2, alpha: 1, name: 'fadeOverlay' + transitionId + '_out' }); // Make sure the fade overlay is the top-most element game.addChild(fadeOutOverlay); // Wait another frame to ensure WebGL has processed the new object LK.setTimeout(function () { // Now it's safe to tween the overlay tween(fadeOutOverlay, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { game.removeChild(fadeOutOverlay); } }); }, 16); // Wait one frame (approx 16ms at 60fps) }, 100); // Wait 100ms to ensure terminal is initialized } }); } // Helper function to calculate button width based on text length function calculateButtonWidth(text) { var minWidth = 400; // Minimum button width var charWidth = 15 * TEXT_SIZE_MULTIPLIER; // Reduced width per character var calculatedWidth = text.length * charWidth; return Math.max(minWidth, calculatedWidth + 80); // Add padding } function shuffleArray(array) { for (var i = array.length - 1; i > 0; i--) { var j = Math.floor(Math.random() * (i + 1)); var _ref = [array[j], array[i]]; array[i] = _ref[0]; array[j] = _ref[1]; } return array; } function endDay() { gameState.day++; if (gameState.day > gameState.maxDays) { evaluateProject(); return; } gameState.commandsUsed = 0; gameState.codeCoherence = Math.min(100, gameState.codeCoherence + 10); addToTerminalLogWithEffect("DAY " + gameState.day + " INITIALIZED"); updateTerminal(); // Check if it's a concept selection day var conceptDays = { 1: 'platform', 3: 'visual', 5: 'genre', 7: 'mechanic', 9: 'feature' }; if (conceptDays[gameState.day]) { LK.setTimeout(function () { showConceptSelection(conceptDays[gameState.day]); }, 500); } } function checkForBugs(command) { var bugChance = command.bugChance * (1 + (100 - gameState.codeCoherence) / 100); if (Math.random() < bugChance) { gameState.bugs++; addToTerminalLogWithEffect("WARNING: Bug detected in system"); } } function checkGameState() { if (gameState.bugs >= 10) { gameOver("CRITICAL FAILURE: TOO MANY BUGS"); return; } if (gameState.codeCoherence <= 0) { gameOver("FATAL ERROR: CODE COHERENCE LOST"); return; } if (gameState.commandsUsed >= gameState.maxCommandsPerDay) { addToTerminalLogWithEffect("NOTICE: Daily command limit reached. END DAY to continue."); } } function evaluateProject() { var score = gameState.vibePoints * 0.4 + Object.values(gameState.conceptCards).filter(Boolean).length * 10 + (10 - gameState.bugs) * 5 + gameState.codeCoherence * 0.2; score = Math.round(Math.min(100, score)); gameOver("PROJECT COMPLETE!\nFINAL SCORE: " + score + "/100\n\n" + generateReview(score)); } function generateReview(score) { // [Previous review generation code remains the same] } function gameOver(message) { gameState.state = STATES.GAME_OVER; addToTerminalLogWithEffect(message); addToTerminalLogWithEffect("\n>PRESS ANY KEY TO RESTART"); // Simple click anywhere to restart var overlay = new Container(); overlay.width = 2048; overlay.height = 2732; overlay.interactive = true; overlay.down = initGame; game.addChild(overlay); } function initGame() { // Reset game state gameState = { state: STATES.PLAYING, day: 1, maxDays: 10, vibePoints: 100, codeCoherence: 100, bugs: 0, commandsUsed: 0, maxCommandsPerDay: 3, terminal: { log: [], statusLine: "", currentTask: "", featureTags: [] }, conceptCards: { platform: null, visual: null, genre: null, mechanic: null, feature: null }, cursorTracking: { lineCount: 0 // Track the exact number of lines }, // Add these new properties needed by our scrolling system terminalScroll: { scrollPosition: 0, targetScrollPosition: 0, isAnimating: false }, terminalContentContainer: null, // Will be set in createTerminal() terminalBaseY: 2732 * 0.15, terminalLineHeight: 30 * TEXT_SIZE_MULTIPLIER, terminalMaxVisibleHeight: MAX_VISIBLE_LINES * 30 * TEXT_SIZE_MULTIPLIER }; createTerminal(); // Sequence the initial text LK.setTimeout(function () { addToTerminalLogWithEffect("VIBE CODER OS v1.0", function () { LK.setTimeout(function () { addToTerminalLogWithEffect("INITIALIZING NEW PROJECT...", function () { LK.setTimeout(function () { addToTerminalLogWithEffect("SELECT PLATFORM TO BEGIN", function () { LK.setTimeout(function () { showConceptSelection('platform'); }, 500); }); }, 500); }); }, 500); }); }, 500); } // Start with launch screen showLaunchScreen();
===================================================================
--- original.js
+++ change.js
@@ -337,28 +337,34 @@
checkGameState();
}, 500);
}
function animateTerminalScroll() {
- if (!gameState.terminalScroll || !gameState.terminalContentContainer) {
- return; // Guard against missing properties
+ // More comprehensive safety checks
+ if (!gameState || !gameState.terminalScroll || !gameState.terminalContentContainer || !gameState.terminalContentContainer.parent) {
+ return; // Exit if any required element is missing
}
var scroll = gameState.terminalScroll;
scroll.isAnimating = true;
// Calculate new position with easing
var scrollDiff = scroll.targetScrollPosition - scroll.scrollPosition;
var scrollStep = scrollDiff * SCROLL_ANIMATION_SPEED;
scroll.scrollPosition += scrollStep;
- // Apply new position
- if (gameState.terminalContentContainer) {
+ // Apply new position (with additional safety check)
+ if (gameState.terminalContentContainer && gameState.terminalContentContainer.parent) {
gameState.terminalContentContainer.y = gameState.terminalBaseY + scroll.scrollPosition;
}
// Continue animation if not close enough to target
if (Math.abs(scrollDiff) > 1) {
- LK.setTimeout(animateTerminalScroll, 16); // ~60fps
+ // Only schedule next animation frame if container still exists
+ if (gameState.terminalContentContainer && gameState.terminalContentContainer.parent) {
+ LK.setTimeout(animateTerminalScroll, 16);
+ } else {
+ scroll.isAnimating = false; // Stop animation if container is gone
+ }
} else {
// Snap to exact position and end animation
scroll.scrollPosition = scroll.targetScrollPosition;
- if (gameState.terminalContentContainer) {
+ if (gameState.terminalContentContainer && gameState.terminalContentContainer.parent) {
gameState.terminalContentContainer.y = gameState.terminalBaseY + scroll.scrollPosition;
}
scroll.isAnimating = false;
}
vibebeat1
Music
vibebeat2
Music
vibebeat3
Music
vibebeat4
Music
vibebeat5
Music
vibebeat6
Music
buttonsound
Sound effect
endday
Sound effect
startsound
Sound effect
bugsquish
Sound effect
conceptbutton
Sound effect
wheelstart
Sound effect
wheelspin
Sound effect
wheelstop
Sound effect
keytype
Sound effect