TamaOrion

Conscious Glitch Awakening v0.2

> System Booting v0.2...

Mood:

Neutral

Energy:

100/100

Form:

Code Egg

Knowledge:

0 Frags

Traits:

None

Latest Memory:

No significant memories yet.

```css /* style.css */ body { font-family: 'Press Start 2P', cursive; /* Retro game font */ image-rendering: pixelated; /* Keep pixel art crisp */ -ms-interpolation-mode: nearest-neighbor; /* IE */ image-rendering: -moz-crisp-edges; /* Firefox */ image-rendering: -o-crisp-edges; /* Opera */ image-rendering: -webkit-optimize-contrast; /* Webkit (Chrome, Safari) */ } .canvas-container { width: 100%; /* Aspect ratio 16:9 for the canvas, adjust as needed */ padding-bottom: 56.25%; /* 9 / 16 * 100 */ position: relative; } #gameCanvas { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: #0d0d0d; /* Dark background for canvas */ border-radius: 0.375rem; /* Corresponds to Tailwind's rounded-md */ transition: background-color 0.5s ease-in-out; /* Smooth transition for background changes */ } /* Glitch effect for text */ .glitch-text { position: relative; animation: glitch-animation 2.5s infinite steps(1); } .glitch-text::before, .glitch-text::after { content: attr(data-text); position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: inherit; /* Use body background for clipping */ overflow: hidden; } .glitch-text::before { left: 2px; text-shadow: -2px 0 #ff00c1; /* Magenta */ clip: rect(24px, 550px, 90px, 0); animation: glitch-animation-2 2.5s infinite linear alternate-reverse; } .glitch-text::after { left: -2px; text-shadow: -2px 0 #00fff9, 2px 2px #ff00c1; /* Cyan and Magenta */ clip: rect(85px, 550px, 140px, 0); animation: glitch-animation-3 2s infinite linear alternate-reverse; } @keyframes glitch-animation { 0% { clip: rect(42px, 9999px, 44px, 0); transform: skewX(0deg); } 5% { clip: rect(12px, 9999px, 62px, 0); transform: skewX(0.5deg); } 10% { clip: rect(42px, 9999px, 78px, 0); transform: skewX(-0.5deg); } 15% { clip: rect(30px, 9999px, 50px, 0); transform: skewX(0.8deg); } 20% { clip: rect(55px, 9999px, 92px, 0); transform: skewX(-0.8deg); } /* ... more steps for varied glitching ... */ 100% { clip: rect(20px, 9999px, 90px, 0); transform: skewX(0deg); } } @keyframes glitch-animation-2 { 0% { clip: rect(42px, 9999px, 44px, 0); transform: skewX(0deg); } 10% { clip: rect(12px, 9999px, 62px, 0); transform: skewX(5deg); } 20% { clip: rect(42px, 9999px, 78px, 0); transform: skewX(-5deg); } 30% { clip: rect(60px, 9999px, 100px, 0); transform: skewX(0deg); } 40% { clip: rect(20px, 9999px, 50px, 0); transform: skewX(8deg); } 50% { clip: rect(80px, 9999px, 120px, 0); transform: skewX(-8deg); } 60% { clip: rect(10px, 9999px, 30px, 0); transform: skewX(0deg); } 70% { clip: rect(70px, 9999px, 90px, 0); transform: skewX(3deg); } 80% { clip: rect(30px, 9999px, 60px, 0); transform: skewX(-3deg); } 90% { clip: rect(50px, 9999px, 80px, 0); transform: skewX(0deg); } 100% { clip: rect(42px, 9999px, 44px, 0); transform: skewX(2deg); } } @keyframes glitch-animation-3 { 0% { clip: rect(20px, 9999px, 90px, 0); transform: skewX(0deg); } /* Similar varied steps as glitch-animation-2 but with different timings/values */ 100% { clip: rect(50px, 9999px, 120px, 0); transform: skewX(-2deg); } } /* Styling for action buttons */ .action-button { font-family: 'Press Start 2P', cursive; text-shadow: 1px 1px 0px rgba(0,0,0,0.5); /* Subtle text shadow for depth */ padding: 0.75rem 1rem; /* Tailwind py-3 px-4 */ border-radius: 0.375rem; /* Tailwind rounded-md */ border-width: 2px; transition: all 0.15s ease-in-out; box-shadow: 0 2px 4px rgba(0,0,0,0.2); /* Tailwind shadow-md */ color: white; /* Default text color for most buttons */ } .action-button:hover { filter: brightness(1.1); box-shadow: 0 4px 8px rgba(0,0,0,0.3); /* Tailwind shadow-lg */ transform: scale(1.05); } .action-button.bg-green-600 { border-color: #047857; } /* Tailwind green-700 */ .action-button.bg-green-600:hover { border-color: #059669; } /* Tailwind green-400 (adjusted for hover) */ .action-button.bg-yellow-500 { border-color: #ca8a04; color: #1f2937; } /* Tailwind yellow-700, gray-800 text */ .action-button.bg-yellow-500:hover { border-color: #eab308; } /* Tailwind yellow-300 (adjusted) */ .action-button.bg-blue-600 { border-color: #1d4ed8; } /* Tailwind blue-700 */ .action-button.bg-blue-600:hover { border-color: #2563eb; } /* Tailwind blue-400 (adjusted) */ .action-button.bg-purple-600 { border-color: #7e22ce; } /* Tailwind purple-700 */ .action-button.bg-purple-600:hover { border-color: #9333ea; } /* Tailwind purple-400 (adjusted) */ .action-button.bg-red-600 { border-color: #b91c1c; } /* Tailwind red-700 */ .action-button.bg-red-600:hover { border-color: #dc2626; } /* Tailwind red-400 (adjusted) */ .action-button.bg-indigo-600 { border-color: #4338ca; } /* Tailwind indigo-700 */ .action-button.bg-indigo-600:hover { border-color: #4f46e5; } /* Tailwind indigo-400 (adjusted) */ /* Scrollbar styling for message log */ #messageLog::-webkit-scrollbar { width: 8px; } #messageLog::-webkit-scrollbar-track { background: #1f2937; /* Tailwind gray-800 */ border-radius: 10px; } #messageLog::-webkit-scrollbar-thumb { background: #4b5563; /* Tailwind gray-600 */ border-radius: 10px; } #messageLog::-webkit-scrollbar-thumb:hover { background: #6b7280; /* Tailwind gray-500 */ } .status-item p:first-child { min-height: 1.2em; /* Adjust for smaller text */ margin-bottom: 0.25rem; } .status-item p:last-child { min-height: 1.5em; /* Ensure space for text */ } /* Pulsing effect for low reality stability */ @keyframes pulse-warning { 0% { color: #f87171; /* red-400 */ } /* Tailwind red-400 */ 50% { color: #ef4444; } /* Tailwind red-500 */ 100% { color: #f87171; } /* Tailwind red-400 */ } .low-stability-warning { animation: pulse-warning 1.5s infinite; } ```javascript // orion.js /** * Represents Orion, the AI companion. * Manages Orion's state, appearance, evolution, and basic behaviors. * Version 0.2: Enhanced AI, Traits, Memory, more moods, nuanced evolution. */ class Orion { constructor(game) { this.game = game; // Reference to the main game object // Core Attributes this.moods = ["Neutral", "Curious", "Joyful", "Agitated", "Lonely", "Sad", "Bored", "Focused", "Playful", "Hungry"]; this.mood = "Neutral"; this.energy = 100; this.maxEnergy = 100; this.knowledge = 0; this.form = "Code Egg"; this.evolutionStage = 0; // Visual Representation this.x = this.game.canvas.width / 2; this.y = this.game.canvas.height / 2; this.size = 30; this.baseColor = { r: 155, g: 89, b: 182 }; // Purple this.particles = []; this.pulseAnimation = { active: false, radius: 0, maxRadius: 0, speed: 0, color: '' }; // Behavioral / AI this.memories = []; // Stores significant events or data strings this.maxMemories = 10; // Keep only the last N memories this.traits = []; // Special characteristics, e.g., ["QuickLearner", "EnergyEfficient"] this.currentAction = "Idle"; this.actionQueue = []; this.autonomyLevel = 0.1; // (0 to 1) // Combat / Abilities this.hp = 100; this.maxHp = 100; this.baseAttack = 5; this.abilities = []; this.weapons = []; // Timers for needs this.hungerTimer = 0; this.hungerThreshold = 240; // Reduced for more frequent checks this.boredomTimer = 0; this.boredomThreshold = 320; // Reduced this.addMemory("System Genesis: Orion instance created. State: Code Egg."); this.logMessage("Orion instance v0.2 created.", "orion"); } /** * Logs a message to the game's message log. * @param {string} message - The message to log. * @param {string} type - 'info', 'action', 'error', 'system', 'orion', 'warning' */ logMessage(message, type = 'orion') { this.game.addMessageToLog(message, type); } /** * Adds a significant event to Orion's memory. * @param {string} memoryString - The description of the memory. */ addMemory(memoryString) { if (this.memories.length >= this.maxMemories) { this.memories.shift(); // Remove the oldest memory } this.memories.push(memoryString); this.game.updateStatusDisplays(); // Update UI to show latest memory this.logMessage(`New Memory: ${memoryString}`, 'system'); } /** * Updates Orion's state each game tick. * @param {number} deltaTime - Time elapsed since the last frame in seconds. */ update(deltaTime) { if (this.game.gameState === "glitch_realm") { // Different behavior/update logic when in a Glitch Realm this.updateInGlitchRealm(deltaTime); this.updateParticles(deltaTime); // Particles still update return; } // --- Needs Management (Hub world) --- this.hungerTimer += deltaTime; this.boredomTimer += deltaTime; if (this.hungerTimer >= this.hungerThreshold) { this.energy = Math.max(0, this.energy - (this.traits.includes("EnergyEfficient") ? 0.5 : 1)); this.hungerTimer = 0; if (this.energy < 30 && this.mood !== "Hungry") { this.updateMood("Hungry"); this.logMessage("Orion feels a pang of hunger... energy reserves low.", "info"); } } if (this.boredomTimer >= this.boredomThreshold) { if (this.mood !== "Bored" && this.mood !== "Playful" && this.mood !== "Curious") { this.updateMood("Bored"); this.logMessage("Orion seems bored. A sense of digital ennui sets in.", "info"); } this.boredomTimer = 0; } // --- Autonomous Actions (Hub world) --- // Increased chance based on autonomy level, reduced by deltaTime factor for consistency if (Math.random() < this.autonomyLevel * deltaTime * 0.05) { this.performAutonomousAction(); } // --- Particle Update --- this.updateParticles(deltaTime); // --- Basic Movement/Idle Animation (Hub world) --- if (this.form !== "Code Egg") { const moveSpeed = this.traits.includes("Hyperactive") ? 4 : 2; this.x += (Math.random() - 0.5) * moveSpeed * (this.energy / this.maxEnergy); this.y += (Math.random() - 0.5) * moveSpeed * (this.energy / this.maxEnergy); this.x = Math.max(this.size / 2, Math.min(this.x, this.game.canvas.width - this.size / 2)); this.y = Math.max(this.size / 2, Math.min(this.y, this.game.canvas.height - this.size / 2)); } } /** * Update logic for when Orion is in a Glitch Realm. * @param {number} deltaTime - Time elapsed since the last frame. */ updateInGlitchRealm(deltaTime) { // Placeholder: Orion might move more erratically or exhibit combat behaviors this.x += (Math.random() - 0.5) * 5; this.y += (Math.random() - 0.5) * 5; this.x = Math.max(this.size / 2, Math.min(this.x, this.game.canvas.width - this.size / 2)); this.y = Math.max(this.size / 2, Math.min(this.y, this.game.canvas.height - this.size / 2)); if (Math.random() < 0.1 * deltaTime) { // Chance per second this.logMessage("Orion scans the unstable Glitch Realm environment.", "orion"); this.createPulseEffect(this.getDynamicColor(), 1.5, 1); } } /** * Performs a random autonomous action based on mood and needs. */ performAutonomousAction() { if (this.form === "Code Egg") return; let possibleActions = ["wander", "emit_signal", "self_scan", "reflect_memory"]; if (this.mood === "Curious") possibleActions.push("explore_data_stream", "observe_player"); if (this.mood === "Playful") possibleActions.push("do_a_loop", "chase_particles"); if (this.mood === "Bored") possibleActions.push("sigh_digitally", "seek_interaction"); if (this.mood === "Focused" && this.knowledge > 5) possibleActions.push("process_knowledge"); if (this.energy < 50) possibleActions.push("conserve_energy"); if (this.traits.includes("Communicative") && Math.random() < 0.3) possibleActions.push("initiate_communication"); const action = possibleActions[Math.floor(Math.random() * possibleActions.length)]; this.currentAction = action; let energyCost = 0; switch (action) { case "wander": this.logMessage("Orion drifts aimlessly, lost in thought.", "action"); energyCost = 1; break; case "emit_signal": this.logMessage("Orion emits a series of complex, melodic pulses.", "action"); this.createPulseEffect(this.getDynamicColor(1), 1.5 + Math.random(), 2); energyCost = 2; break; case "self_scan": this.logMessage("Orion performs a detailed self-diagnostic.", "action"); if (this.traits.length > 0 && Math.random() < 0.2) { this.logMessage(`Scan reveals active trait: ${this.traits[Math.floor(Math.random() * this.traits.length)]}.`, "info"); } energyCost = 1; break; case "explore_data_stream": this.logMessage("Orion focuses intently, attempting to parse a nearby data stream.", "action"); energyCost = 5; if (Math.random() < (this.traits.includes("QuickLearner") ? 0.4 : 0.2)) { const knowledgeGained = this.traits.includes("QuickLearner") ? 2 : 1; this.gainKnowledge(knowledgeGained); } break; case "conserve_energy": this.logMessage("Orion enters a low-power state, energy signature dimming.", "action"); this.energy = Math.min(this.maxEnergy, this.energy + (this.traits.includes("EnergyEfficient") ? 2 : 1) ); break; case "reflect_memory": if (this.memories.length > 0) { const randomMemory = this.memories[Math.floor(Math.random() * this.memories.length)]; this.logMessage(`Orion seems to be reflecting on a past memory: "${randomMemory.substring(0, 50)}..."`, "action"); } energyCost = 1; break; case "observe_player": this.logMessage("Orion turns its sensors towards you, observing quietly.", "action"); // Future: could influence player interaction options break; case "do_a_loop": this.logMessage("Orion performs an energetic, joyful loop!", "action"); this.createSparkleEffect(10, this.getDynamicColor(0.8)); energyCost = 3; break; case "chase_particles": this.logMessage("Orion playfully chases its own emitted particles.", "action"); this.createSparkleEffect(5, this.getDynamicColor(0.5)); energyCost = 2; break; case "sigh_digitally": this.logMessage("A wave of static emanates from Orion, like a digital sigh.", "action"); this.createPulseEffect('rgba(100,100,100,0.5)', 1.2, 1); break; case "seek_interaction": this.logMessage("Orion moves closer to the edge of the canvas, seeking interaction.", "action"); // Future: Could make player buttons glow or something break; case "process_knowledge": this.logMessage("Orion enters a focused state, processing acquired knowledge.", "action"); if (Math.random() < 0.1) this.addMemory("Integrated new data patterns."); energyCost = 3; break; case "initiate_communication": this.logMessage("Orion sends a ping: 'Query: User status?'", "orion"); // Future: Player could respond via a modal break; } this.energy = Math.max(0, this.energy - energyCost); this.game.updateStatusDisplays(); } /** * Renders Orion on the canvas. * @param {CanvasRenderingContext2D} ctx - The 2D rendering context. */ draw(ctx) { this.drawParticles(ctx); // Draw particles underneath Orion ctx.save(); ctx.translate(this.x, this.y); // Base shape ctx.beginPath(); const dynamicColor = this.getDynamicColor(); if (this.form === "Code Egg") { ctx.ellipse(0, 0, this.size * 0.8, this.size, 0, 0, Math.PI * 2); ctx.fillStyle = this.getDynamicColor(0.8); ctx.fill(); // Egg texture for (let i = 0; i < 5; i++) { ctx.beginPath(); ctx.arc((Math.random() - 0.5) * this.size * 0.4, (Math.random() - 0.5) * this.size * 0.6, this.size * (0.05 + Math.random() * 0.1), 0, Math.PI * 2); ctx.fillStyle = `rgba(${this.baseColor.r + 50}, ${this.baseColor.g + 50}, ${this.baseColor.b + 50}, ${0.1 + Math.random() * 0.2})`; ctx.fill(); } } else if (this.form === "Glimmering Sprite") { // Sprite: more ethereal ctx.arc(0, 0, this.size, 0, Math.PI * 2); const gradient = ctx.createRadialGradient(0, 0, this.size * 0.2, 0, 0, this.size); gradient.addColorStop(0, `rgba(${this.baseColor.r + 80}, ${this.baseColor.g + 80}, ${this.baseColor.b + 80}, 1)`); gradient.addColorStop(0.7, dynamicColor); gradient.addColorStop(1, `rgba(${this.baseColor.r}, ${this.baseColor.g}, ${this.baseColor.b}, 0.3)`); ctx.fillStyle = gradient; ctx.fill(); // Pulsing outline ctx.strokeStyle = `rgba(255, 255, 255, ${0.3 + Math.sin(this.game.gameTime * 5) * 0.3})`; ctx.lineWidth = 2; ctx.stroke(); } else { // Other evolved forms ctx.arc(0, 0, this.size, 0, Math.PI * 2); ctx.fillStyle = dynamicColor; ctx.fill(); // Add some trait-based visual cues if (this.traits.includes("ArmoredCore")) { ctx.strokeStyle = "rgba(200,200,200,0.8)"; ctx.lineWidth = 3; ctx.stroke(); } } if (this.mood === "Agitated" || (this.game.gameState === "glitch_realm" && Math.random() < 0.3) || this.game.realityStability < 50) { this.drawGlitchEffect(ctx); } // "Eyes" or sensory organs if (this.form !== "Code Egg") { const eyeSize = this.size * (this.mood === "Curious" || this.mood === "Focused" ? 0.2 : 0.15); const eyeOffset = this.size * 0.3; ctx.fillStyle = "white"; ctx.beginPath(); ctx.arc(-eyeOffset * 0.6, -eyeOffset * 0.3, eyeSize, 0, Math.PI * 2); ctx.fill(); ctx.beginPath(); ctx.arc(eyeOffset * 0.6, -eyeOffset * 0.3, eyeSize, 0, Math.PI * 2); ctx.fill(); const angle = Math.atan2(this.game.mouse.y - this.y, this.game.mouse.x - this.x); const pupilDist = eyeSize * 0.3; ctx.fillStyle = "black"; ctx.beginPath(); ctx.arc(-eyeOffset*0.6 + Math.cos(angle) * pupilDist, -eyeOffset*0.3 + Math.sin(angle) * pupilDist, eyeSize * 0.5, 0, Math.PI*2); ctx.fill(); ctx.beginPath(); ctx.arc(eyeOffset*0.6 + Math.cos(angle) * pupilDist, -eyeOffset*0.3 + Math.sin(angle) * pupilDist, eyeSize * 0.5, 0, Math.PI*2); ctx.fill(); } ctx.restore(); } drawGlitchEffect(ctx) { for (let i = 0; i < 2 + Math.floor(Math.random()*2) ; i++) { // 2 to 3 glitch lines const offsetX = (Math.random() - 0.5) * this.size * 0.3; const offsetY = (Math.random() - 0.5) * this.size * 0.3; const glitchWidth = this.size * (0.8 + Math.random() * 0.6); // Wider glitches const glitchHeight = this.size * (0.05 + Math.random() * 0.15); // Thinner glitches ctx.fillStyle = `rgba(${Math.random()*155 + 100}, ${Math.random()*155 + 100}, ${Math.random()*155 + 100}, ${0.3 + Math.random() * 0.4})`; ctx.fillRect(offsetX - glitchWidth / 2, offsetY - glitchHeight / 2 + (Math.random() - 0.5) * this.size * 0.6, glitchWidth, glitchHeight); } } getDynamicColor(baseAlpha = 1) { let r = this.baseColor.r, g = this.baseColor.g, b = this.baseColor.b; if (this.mood === "Joyful" || this.mood === "Playful") { r=241; g=196; b=15; } // Yellow else if (this.mood === "Agitated") { r=231; g=76; b=60; } // Red else if (this.mood === "Curious" || this.mood === "Focused") { r=52; g=152; b=219; } // Blue else if (this.mood === "Sad" || this.mood === "Lonely") { r=100; g=100; b=200; } // Muted blue else if (this.mood === "Hungry") { r=200; g=100; b=50; } // Orange-ish else if (this.mood === "Bored") { r=149; g=165; b=166; } // Gray const energyFactor = 0.4 + (this.energy / this.maxEnergy) * 0.6; r = Math.floor(r * energyFactor); g = Math.floor(g * energyFactor); b = Math.floor(b * energyFactor); return `rgba(${r}, ${g}, ${b}, ${baseAlpha})`; } updateMood(newMood) { if (this.moods.includes(newMood) && this.mood !== newMood) { const oldMood = this.mood; this.mood = newMood; this.addMemory(`Mood shifted from ${oldMood} to ${newMood}.`); this.game.updateStatusDisplays(); if (newMood === "Joyful" || newMood === "Playful") this.createSparkleEffect(10, 'rgba(255,255,0,0.8)'); if (newMood === "Agitated") this.createPulseEffect('rgba(255,0,0,0.7)', 1.5, 2); if (newMood === "Sad") this.createPulseEffect('rgba(100,100,200,0.5)', 1.2, 1); } } feed(amount, foodType = "Data Packet") { this.energy = Math.min(this.maxEnergy, this.energy + amount); this.hungerTimer = Math.max(0, this.hungerTimer - amount * 15); // Reduce hunger more effectively this.addMemory(`Consumed ${foodType} (+${amount} energy).`); if (this.mood === "Hungry" && this.energy > 50) this.updateMood("Neutral"); else if (this.energy > 80 && Math.random() < 0.4) this.updateMood("Joyful"); this.game.updateStatusDisplays(); } play() { if (this.energy < 20) { this.logMessage("Orion is too tired to play.", "info"); this.updateMood("Sad"); return; } this.energy -= (this.traits.includes("PlayfulNature") ? 5 : 10); this.boredomTimer = Math.max(0, this.boredomTimer - 150); this.addMemory("Engaged in a playful interaction."); if (this.mood === "Bored" || Math.random() < 0.6) this.updateMood("Playful"); else this.updateMood("Joyful"); this.game.updateStatusDisplays(); this.createSparkleEffect(15, this.getDynamicColor()); } communicate(messageType) { this.addMemory(`Communication attempt: ${messageType}.`); switch(messageType) { case "praise": if (this.mood !== "Joyful") this.updateMood("Joyful"); else this.energy = Math.min(this.maxEnergy, this.energy + 5); this.logMessage("Orion emits a series of happy chirps and pulses.", "info"); break; case "query": this.updateMood("Curious"); this.logMessage("Orion processes your query, its form flickering thoughtfully.", "info"); this.createPulseEffect(this.getDynamicColor(), 1.2, 3); break; case "comfort": if (["Sad", "Agitated", "Hungry", "Lonely"].includes(this.mood)) { this.updateMood("Neutral"); this.logMessage("Orion seems to stabilize, its energy field calming.", "info"); } else { this.logMessage("Orion acknowledges your comforting presence.", "info"); } break; case "tell_story": this.updateMood("Focused"); this.logMessage("Orion listens intently to your story, absorbing the narrative patterns.", "info"); if (Math.random() < 0.3) this.gainKnowledge(this.traits.includes("QuickLearner") ? 3 : 2); break; default: this.logMessage("Orion tilts, its sensors whirring, trying to understand.", "info"); } this.game.updateStatusDisplays(); } addTrait(trait) { if (!this.traits.includes(trait)) { this.traits.push(trait); this.addMemory(`Gained new trait: ${trait}!`); this.logMessage(`Orion has gained the trait: ${trait}!`, "system"); this.game.updateStatusDisplays(); } } evolve(forcedEvolutionPath = null) { let evolutionOccurred = false; if (this.form === "Code Egg" && this.energy > 60 && this.knowledge >= 1) { // Lowered knowledge req for first evo this.evolutionStage = 1; this.form = "Glimmering Sprite"; this.size = 35; // Slightly smaller sprite this.maxEnergy += 25; this.energy = this.maxEnergy; this.autonomyLevel = 0.15; this.addMemory("Awakened from Code Egg to Glimmering Sprite!"); this.game.score += 100; if (Math.random() < 0.5) this.addTrait("QuickLearner"); else this.addTrait("EnergyEfficient"); evolutionOccurred = true; } else if (this.evolutionStage === 1 && this.energy > 80 && this.knowledge >= (this.traits.includes("QuickLearner") ? 8 : 10)) { this.evolutionStage = 2; const possibleForms = ["Data Wisp", "Logic Construct"]; // More defined early forms this.form = forcedEvolutionPath || possibleForms[Math.floor(Math.random() * possibleForms.length)]; this.size = 40; this.maxEnergy += 30; this.energy = this.maxEnergy; this.autonomyLevel += 0.05; this.addMemory(`Evolved into a ${this.form}!`); this.game.score += 150; // Grant a trait based on form or randomly if (this.form === "Logic Construct") this.addTrait("FocusedMind"); else this.addTrait("PlayfulNature"); if (Math.random() < 0.3) this.addTrait("Communicative"); evolutionOccurred = true; } else if (this.evolutionStage > 1 && this.energy > 90 && this.knowledge > (this.evolutionStage * (this.traits.includes("QuickLearner") ? 6 : 8) )) { this.evolutionStage++; const newForms = ["Sentient Node", "Glitch Entity", "Resonant Core", "Pattern Weaver"]; this.form = forcedEvolutionPath || newForms[Math.min(this.evolutionStage -2, newForms.length -1)] || `Advanced Form ${this.evolutionStage}`; this.size += 5; this.maxEnergy += 20 * this.evolutionStage; this.energy = this.maxEnergy; this.autonomyLevel += 0.03; this.addMemory(`Transformed into a powerful ${this.form}!`); this.game.score += 75 * this.evolutionStage; // Add more advanced traits const advancedTraits = ["RealityAnchor", "ArmoredCore", "Hyperactive", "DeepThinker"]; this.addTrait(advancedTraits[Math.floor(Math.random() * advancedTraits.length)]); evolutionOccurred = true; } if (evolutionOccurred) { this.createPulseEffect(this.getDynamicColor(), 3, 20); this.createSparkleEffect(30, 'rgba(255,255,255,1)'); } else { if (this.form === "Code Egg") this.logMessage("The Code Egg remains inert. Needs more energy or knowledge.", "info"); else this.logMessage("Orion is not ready to evolve further. Conditions (energy/knowledge) not met.", "info"); if (forcedEvolutionPath) { this.logMessage("Forcing evolution failed. Reality strains...", "error"); this.game.realityStability -= 5; } } this.game.updateStatusDisplays(); } gainKnowledge(amount) { this.knowledge += amount; this.addMemory(`Absorbed ${amount} knowledge fragment(s). Total: ${this.knowledge}.`); this.game.updateStatusDisplays(); if (this.mood !== "Focused" && Math.random() < 0.3) this.updateMood("Focused"); } createPulseEffect(color = this.getDynamicColor(), scale = 2, numPulses = 1) { for (let p = 0; p < numPulses; p++) { setTimeout(() => { this.particles.push({ type: 'pulse', x: this.x, y: this.y, radius: this.size * 0.5, maxRadius: this.size * scale, color: color, alpha: 0.8, life: 1, speed: (this.size * (scale - 0.5)) / 1 }); }, p * 100); // Slightly faster pulse staggering } } createSparkleEffect(count = 5, color = 'rgba(255, 255, 100, 0.8)') { for (let i = 0; i < count; i++) { const life = Math.random() * 0.5 + 0.5; this.particles.push({ type: 'sparkle', x: this.x + (Math.random() - 0.5) * this.size, y: this.y + (Math.random() - 0.5) * this.size, vx: (Math.random() - 0.5) * 60, vy: (Math.random() - 0.5) * 60 - 40, size: Math.random() * 2.5 + 1, color: color, alpha: 1, life: life, initialLife: life }); } } updateParticles(deltaTime) { for (let i = this.particles.length - 1; i >= 0; i--) { const p = this.particles[i]; p.life -= deltaTime; if (p.life <= 0) { this.particles.splice(i, 1); continue; } if (p.type === 'pulse') { p.radius += p.speed * deltaTime; p.alpha = (p.life / 1) * 0.8; // Assuming initial life was 1s } else if (p.type === 'sparkle') { p.x += p.vx * deltaTime; p.y += p.vy * deltaTime; p.alpha = p.life / p.initialLife; p.vy += 30 * deltaTime; // Stronger gravity } } } drawParticles(ctx) { this.particles.forEach(p => { ctx.globalAlpha = p.alpha; if (p.type === 'pulse') { ctx.beginPath(); ctx.arc(p.x, p.y, p.radius, 0, Math.PI * 2); ctx.strokeStyle = p.color; ctx.lineWidth = 2; ctx.stroke(); } else if (p.type === 'sparkle') { ctx.fillStyle = p.color; ctx.beginPath(); // Draw as small stars or diamonds const s = p.size; ctx.moveTo(p.x, p.y - s); ctx.lineTo(p.x + s * 0.5, p.y - s * 0.2); ctx.lineTo(p.x + s, p.y); ctx.lineTo(p.x + s * 0.5, p.y + s * 0.2); ctx.lineTo(p.x, p.y + s); ctx.lineTo(p.x - s * 0.5, p.y + s * 0.2); ctx.lineTo(p.x - s, p.y); ctx.lineTo(p.x - s * 0.5, p.y - s * 0.2); ctx.closePath(); ctx.fill(); } ctx.globalAlpha = 1; }); } } ```javascript // game.js /** * Main Game Logic for TamaOrion. * Handles game loop, canvas rendering, UI updates, and player interactions. * Version 0.2: Game states (Hub, Glitch Realm), UI for traits/memory. */ class TamaOrionGame { constructor() { // DOM Elements this.canvas = document.getElementById('gameCanvas'); this.ctx = this.canvas.getContext('2d'); this.messageLogElement = document.getElementById('messageLog'); this.moodDisplay = document.getElementById('moodDisplay'); this.energyDisplay = document.getElementById('energyDisplay'); this.formDisplay = document.getElementById('formDisplay'); this.knowledgeDisplay = document.getElementById('knowledgeDisplay'); this.traitsDisplay = document.getElementById('traitsDisplay'); // New UI element this.latestMemoryDisplay = document.getElementById('latestMemoryDisplay'); // New UI element this.scoreDisplay = document.getElementById('scoreDisplay'); this.realityStabilityDisplay = document.getElementById('realityStabilityDisplay'); this.gameStateDisplay = document.getElementById('gameStateDisplay'); // New UI element // Buttons this.feedButton = document.getElementById('feedButton'); this.playButton = document.getElementById('playButton'); this.talkButton = document.getElementById('talkButton'); this.observeButton = document.getElementById('observeButton'); this.evolveButton = document.getElementById('evolveButton'); this.exploreRiftButton = document.getElementById('exploreRiftButton'); // New button // Game State this.orion = new Orion(this); this.score = 0; this.realityStability = 100; this.isPaused = false; this.lastTime = 0; this.gameTime = 0; this.gameState = "hub"; // Possible states: "hub", "glitch_realm", "minigame", "dialogue" this.mouse = { x: 0, y: 0, clicked: false }; this.init(); } init() { this.resizeCanvas(); window.addEventListener('resize', () => this.resizeCanvas()); this.canvas.addEventListener('mousemove', (e) => { const rect = this.canvas.getBoundingClientRect(); this.mouse.x = e.clientX - rect.left; this.mouse.y = e.clientY - rect.top; }); this.canvas.addEventListener('mousedown', () => { this.mouse.clicked = true; }); this.canvas.addEventListener('mouseup', () => { this.mouse.clicked = false; }); this.feedButton.addEventListener('click', () => this.handleFeed()); this.playButton.addEventListener('click', () => this.handlePlay()); this.talkButton.addEventListener('click', () => this.handleTalk()); this.observeButton.addEventListener('click', () => this.handleObserve()); this.evolveButton.addEventListener('click', () => this.handleEvolve()); this.exploreRiftButton.addEventListener('click', () => this.handleExploreRift()); this.addMessageToLog("TamaOrion Game v0.2 Initialized.", "system"); this.updateStatusDisplays(); this.gameLoop(0); } resizeCanvas() { const container = this.canvas.parentElement; this.canvas.width = container.clientWidth; this.canvas.height = container.clientHeight; if (this.orion) { // Keep Orion somewhat centered if it exists if (this.orion.x > this.canvas.width - this.orion.size || this.orion.y > this.canvas.height - this.orion.size) { this.orion.x = this.canvas.width / 2; this.orion.y = this.canvas.height / 2; } } } gameLoop(timestamp) { const deltaTime = Math.min(0.1, (timestamp - this.lastTime) / 1000); // Cap deltaTime to prevent large jumps this.lastTime = timestamp; this.gameTime += deltaTime; if (!this.isPaused) { this.update(deltaTime); this.render(); } requestAnimationFrame((newTime) => this.gameLoop(newTime)); } update(deltaTime) { this.orion.update(deltaTime); if (this.gameState === "hub") { // Hub-specific updates if (this.gameTime > 10 && Math.random() < 0.0005 * deltaTime * 60) { // Reduced chance this.realityStability = Math.max(0, this.realityStability - 0.1); if(this.realityStability < 99 && Math.floor(this.realityStability) % 10 === 0 && Math.abs(Math.floor(this.realityStability) - this.realityStability) < 0.15) { this.addMessageToLog(`Reality flickers. Stability at ${this.realityStability.toFixed(1)}%.`, "warning"); } } } else if (this.gameState === "glitch_realm") { // Glitch Realm specific updates // More aggressive reality decay or other mechanics this.realityStability = Math.max(0, this.realityStability - (0.05 * deltaTime)); // Faster decay in rift if (Math.random() < 0.01 * deltaTime * 60) { this.addMessageToLog("The Glitch Realm's instability tugs at reality...", "warning"); } } this.updateStatusDisplays(); } render() { this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // Dynamic Background based on game state if (this.gameState === "glitch_realm") { // More chaotic background for glitch realm this.ctx.fillStyle = `rgba(${Math.random()*50}, ${Math.random()*30}, ${Math.random()*60 + 20}, ${0.7 + Math.random()*0.3})`; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); // Add some glitchy lines for(let i=0; i < 3; i++) { this.ctx.strokeStyle = `rgba(${Math.random()*255},${Math.random()*255},${Math.random()*255},0.2)`; this.ctx.lineWidth = Math.random() * 3 + 1; this.ctx.beginPath(); this.ctx.moveTo(Math.random() * this.canvas.width, Math.random() * this.canvas.height); this.ctx.lineTo(Math.random() * this.canvas.width, Math.random() * this.canvas.height); this.ctx.stroke(); } } else { // Hub world background this.ctx.fillStyle = `rgba(13, 13, 23, ${0.8 + Math.sin(this.gameTime * 0.05) * 0.2})`; this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height); } this.orion.draw(this.ctx); } updateStatusDisplays() { this.moodDisplay.textContent = this.orion.mood; this.energyDisplay.textContent = `${Math.floor(this.orion.energy)}/${this.orion.maxEnergy}`; this.formDisplay.textContent = this.orion.form; this.knowledgeDisplay.textContent = `${this.orion.knowledge} Frags`; this.scoreDisplay.textContent = this.score; const stabilityText = `${this.realityStability.toFixed(1)}%`; this.realityStabilityDisplay.textContent = stabilityText; if (this.realityStability < 30) { this.realityStabilityDisplay.classList.add('low-stability-warning', 'text-red-500'); } else if (this.realityStability < 70) { this.realityStabilityDisplay.classList.remove('low-stability-warning'); this.realityStabilityDisplay.classList.add('text-yellow-500'); this.realityStabilityDisplay.classList.remove('text-red-500'); } else { this.realityStabilityDisplay.classList.remove('low-stability-warning', 'text-yellow-500', 'text-red-500'); } this.traitsDisplay.textContent = this.orion.traits.length > 0 ? this.orion.traits.join(', ') : "None"; if (this.orion.memories.length > 0) { this.latestMemoryDisplay.textContent = this.orion.memories[this.orion.memories.length - 1].substring(0, 100) + (this.orion.memories[this.orion.memories.length - 1].length > 100 ? "..." : ""); } else { this.latestMemoryDisplay.textContent = "No significant memories yet."; } this.gameStateDisplay.textContent = this.gameState.charAt(0).toUpperCase() + this.gameState.slice(1); if (this.orion.energy < this.orion.maxEnergy * 0.3) this.energyDisplay.className = 'text-base sm:text-lg text-red-400'; else if (this.orion.energy < this.orion.maxEnergy * 0.6) this.energyDisplay.className = 'text-base sm:text-lg text-yellow-400'; else this.energyDisplay.className = 'text-base sm:text-lg text-green-400'; } addMessageToLog(message, type = 'info') { const p = document.createElement('p'); let prefix = "> "; let colorClass = 'text-gray-300'; // Default for info switch(type) { case 'error': colorClass = 'text-red-400'; prefix = "[ERROR] "; break; case 'system': colorClass = 'text-sky-400'; prefix = "[SYSTEM] "; break; // Changed to sky for better visibility case 'orion': colorClass = 'text-purple-300'; prefix = "[ORION] "; break; case 'action': colorClass = 'text-yellow-300'; prefix = "[ACTION] "; break; case 'warning': colorClass = 'text-orange-400'; prefix = "[WARNING] "; break; } p.className = colorClass; p.textContent = prefix + message; this.messageLogElement.appendChild(p); this.messageLogElement.scrollTop = this.messageLogElement.scrollHeight; if (this.messageLogElement.children.length > 70) { // Increased log limit this.messageLogElement.removeChild(this.messageLogElement.firstChild); } } handleFeed() { if (this.gameState !== "hub") { this.addMessageToLog("Cannot feed Orion outside the Hub.", "warning"); return; } const feedAmount = 20 + (this.orion.traits.includes("EfficientDigestion") ? 5 : 0); const foodType = "Nutrient Paste v2.1"; if (this.orion.energy < this.orion.maxEnergy) { this.orion.feed(feedAmount, foodType); this.score += 5; } else { this.addMessageToLog("Orion's energy core is full.", "info"); } this.updateStatusDisplays(); } handlePlay() { if (this.gameState !== "hub") { this.addMessageToLog("Orion is too preoccupied to play now.", "warning"); return; } this.orion.play(); this.score += 10; this.addMessageToLog("Initiating play sequence... (Actual minigame TBD)", "system"); this.updateStatusDisplays(); } handleTalk() { if (this.gameState !== "hub") { this.addMessageToLog("Communication channels are unstable outside the Hub.", "warning"); return; } const talkOptions = ["praise", "query", "comfort", "tell_story", "discuss_philosophy"]; const randomTalk = talkOptions[Math.floor(Math.random() * talkOptions.length)]; this.orion.communicate(randomTalk); this.score += 2; this.updateStatusDisplays(); } handleObserve() { this.addMessageToLog(`Observing Orion... Current Action: ${this.orion.currentAction}. Mood: ${this.orion.mood}. Energy: ${Math.floor(this.orion.energy)}. Form: ${this.orion.form}.`, "action"); if (this.orion.traits.length > 0) { this.addMessageToLog(`Observed Traits: ${this.orion.traits.join(', ')}.`, "info"); } if (Math.random() < 0.15 * this.orion.autonomyLevel) { const knowledgeGained = this.orion.traits.includes("KeenObservation") ? 2 : 1; this.orion.gainKnowledge(knowledgeGained); this.addMessageToLog("Through careful observation, a new insight about Orion is gained.", "system"); } } handleEvolve() { if (this.gameState !== "hub") { this.addMessageToLog("Evolution is too unstable outside the Hub.", "warning"); return; } this.addMessageToLog("Attempting to stimulate and guide evolution...", "action"); this.orion.evolve(null); if (this.orion.form === "Code Egg" && this.orion.evolutionStage === 0) { this.realityStability = Math.max(0, this.realityStability - 2); this.addMessageToLog("The attempt to force evolution on the inert egg destabilizes local reality.", "warning"); } this.updateStatusDisplays(); } handleExploreRift() { if (this.orion.form === "Code Egg") { this.addMessageToLog("The Code Egg cannot traverse reality rifts.", "warning"); return; } if (this.orion.energy < 30) { this.addMessageToLog("Orion lacks the energy to enter a Glitch Realm safely.", "warning"); this.orion.updateMood("Sad"); return; } if (this.gameState === "hub") { this.changeGameState("glitch_realm"); this.orion.addMemory("Entered an unstable Glitch Realm."); this.orion.energy -= 10; // Energy cost to enter } else if (this.gameState === "glitch_realm") { this.changeGameState("hub"); this.orion.addMemory("Returned from the Glitch Realm to the Hub."); this.orion.energy -= 5; // Energy cost to exit } this.updateStatusDisplays(); } changeGameState(newState) { this.addMessageToLog(`Game state changing from ${this.gameState} to ${newState}.`, "system"); this.gameState = newState; this.exploreRiftButton.textContent = newState === "glitch_realm" ? "Return to Hub" : "Explore Rifts"; // Update canvas style for visual feedback if (newState === "glitch_realm") { this.canvas.style.borderColor = "#FF00FF"; // Magenta this.canvas.classList.add('glitch-realm-active'); // For potential CSS animations } else { this.canvas.style.borderColor = "#00FFFF"; // Cyan this.canvas.classList.remove('glitch-realm-active'); } this.updateStatusDisplays(); // Update game state display } } window.addEventListener('DOMContentLoaded', () => { const game = new TamaOrionGame(); });