2026-04-11 04:47:06 -04:00
// ============================================================
// THE BEACON - Tutorial / Onboarding
// First-time player walkthrough (4 screens + skip option)
// ============================================================
const TUTORIAL _KEY = 'the-beacon-tutorial-done' ;
const TUTORIAL _STEPS = [
{
title : 'THE BEACON' ,
body : 'Build an AI from scratch.\n\nWrite code. Train models. Deploy to the world.\nSave lives.' ,
icon : '🏠' ,
tip : 'A sovereign AI idle game'
} ,
{
title : 'WRITE CODE' ,
body : 'Click WRITE CODE or press SPACE to generate code.\n\nClick fast for combo bonuses:\n 10× combo → bonus ops\n 20× combo → bonus knowledge\n 30× + combo → bonus code' ,
icon : '⌨️' ,
tip : 'This is your primary action'
} ,
{
title : 'BUILD & RESEARCH' ,
body : 'Buy Buildings for passive production.\nThey generate resources automatically.\n\nResearch Projects appear as you progress.\nThey unlock powerful multipliers and new systems.' ,
icon : '🏗️' ,
tip : 'Automation is the goal'
} ,
{
title : 'PHASES & PROGRESS' ,
body : 'The game has 6 phases, from "The First Line" to "The Beacon."\n\nEach phase unlocks new buildings, projects, and challenges.\n\nYour AI grows from a script... to something that matters.' ,
icon : '📊' ,
tip : 'Watch the progress bar at the top'
} ,
{
title : 'YOU\'RE READY' ,
body : 'Buildings produce while you think.\nProjects multiply your output.\nKeep harmony high. Avoid the Drift.\n\nThe Beacon is waiting. Start writing.' ,
icon : '✦' ,
tip : 'Press ? anytime for keyboard shortcuts'
}
] ;
function isTutorialDone ( ) {
try {
return localStorage . getItem ( TUTORIAL _KEY ) === 'done' ;
} catch ( e ) {
return true ; // If localStorage is broken, skip tutorial
}
}
function markTutorialDone ( ) {
try {
localStorage . setItem ( TUTORIAL _KEY , 'done' ) ;
} catch ( e ) {
// silent fail
}
}
function createTutorialStyles ( ) {
if ( document . getElementById ( 'tutorial-styles' ) ) return ;
const style = document . createElement ( 'style' ) ;
style . id = 'tutorial-styles' ;
style . textContent = `
# tutorial - overlay {
position : fixed ;
top : 0 ; left : 0 ; right : 0 ; bottom : 0 ;
background : rgba ( 8 , 8 , 16 , 0.96 ) ;
z - index : 300 ;
display : flex ;
justify - content : center ;
align - items : center ;
animation : tutorial - fade - in 0.4 s ease - out ;
}
@ keyframes tutorial - fade - in {
from { opacity : 0 } to { opacity : 1 }
}
# tutorial - card {
background : # 0e0 e1a ;
border : 1 px solid # 1 a3a5a ;
border - radius : 10 px ;
padding : 32 px 36 px ;
max - width : 420 px ;
width : 90 % ;
text - align : center ;
animation : tutorial - slide - up 0.5 s ease - out ;
position : relative ;
}
@ keyframes tutorial - slide - up {
from { transform : translateY ( 20 px ) ; opacity : 0 }
to { transform : translateY ( 0 ) ; opacity : 1 }
}
# tutorial - card . t - icon {
font - size : 36 px ;
margin - bottom : 12 px ;
display : block ;
}
# tutorial - card . t - title {
color : # 4 a9eff ;
font - size : 16 px ;
font - weight : 700 ;
letter - spacing : 3 px ;
margin - bottom : 12 px ;
font - family : inherit ;
}
# tutorial - card . t - body {
color : # 999 ;
font - size : 11 px ;
line - height : 1.9 ;
margin - bottom : 20 px ;
white - space : pre - line ;
font - family : inherit ;
text - align : left ;
}
# tutorial - card . t - tip {
color : # 555 ;
font - size : 9 px ;
font - style : italic ;
margin - bottom : 20 px ;
letter - spacing : 1 px ;
font - family : inherit ;
}
# tutorial - dots {
display : flex ;
gap : 6 px ;
justify - content : center ;
margin - bottom : 18 px ;
}
# tutorial - dots . t - dot {
width : 6 px ;
height : 6 px ;
border - radius : 50 % ;
background : # 1 a1a2e ;
transition : background 0.3 s ;
}
# tutorial - dots . t - dot . active {
background : # 4 a9eff ;
box - shadow : 0 0 6 px rgba ( 74 , 158 , 255 , 0.4 ) ;
}
# tutorial - btns {
display : flex ;
gap : 8 px ;
justify - content : center ;
}
# tutorial - btns button {
font - family : inherit ;
font - size : 11 px ;
padding : 8 px 20 px ;
border - radius : 4 px ;
cursor : pointer ;
transition : all 0.15 s ;
}
# tutorial - next - btn {
background : # 1 a2a3a ;
border : 1 px solid # 4 a9eff ;
color : # 4 a9eff ;
}
# tutorial - next - btn : hover {
background : # 203040 ;
box - shadow : 0 0 12 px rgba ( 74 , 158 , 255 , 0.2 ) ;
}
# tutorial - skip - btn {
background : transparent ;
border : 1 px solid # 333 ;
color : # 555 ;
}
# tutorial - skip - btn : hover {
border - color : # 555 ;
color : # 888 ;
}
` ;
document . head . appendChild ( style ) ;
}
function renderTutorialStep ( index ) {
const step = TUTORIAL _STEPS [ index ] ;
if ( ! step ) return ;
let overlay = document . getElementById ( 'tutorial-overlay' ) ;
if ( ! overlay ) {
overlay = document . createElement ( 'div' ) ;
overlay . id = 'tutorial-overlay' ;
2026-04-13 04:37:08 -04:00
overlay . setAttribute ( 'role' , 'dialog' ) ;
overlay . setAttribute ( 'aria-modal' , 'true' ) ;
overlay . setAttribute ( 'aria-label' , 'Tutorial' ) ;
2026-04-11 04:47:06 -04:00
document . body . appendChild ( overlay ) ;
}
const isLast = index === TUTORIAL _STEPS . length - 1 ;
// Build dots
let dots = '' ;
for ( let i = 0 ; i < TUTORIAL _STEPS . length ; i ++ ) {
dots += ` <div class="t-dot ${ i === index ? ' active' : '' } "></div> ` ;
}
overlay . innerHTML = `
< div id = "tutorial-card" >
< span class = "t-icon" > $ { step . icon } < / s p a n >
< div class = "t-title" > $ { step . title } < / d i v >
< div class = "t-body" > $ { step . body } < / d i v >
< div class = "t-tip" > $ { step . tip } < / d i v >
< div id = "tutorial-dots" > $ { dots } < / d i v >
< div id = "tutorial-btns" >
2026-04-13 04:37:08 -04:00
< button id = "tutorial-skip-btn" onclick = "closeTutorial()" aria - label = "Skip tutorial" > Skip < / b u t t o n >
< button id = "tutorial-next-btn" onclick = "${isLast ? 'closeTutorial()' : 'nextTutorialStep()'}" aria - label = "${isLast ? 'Start playing' : 'Next tutorial step'}" > $ { isLast ? 'Start Playing' : 'Next →' } < / b u t t o n >
2026-04-11 04:47:06 -04:00
< / d i v >
< / d i v >
` ;
// Focus the next button so Enter works
const nextBtn = document . getElementById ( 'tutorial-next-btn' ) ;
if ( nextBtn ) nextBtn . focus ( ) ;
}
let _tutorialStep = 0 ;
function nextTutorialStep ( ) {
_tutorialStep ++ ;
renderTutorialStep ( _tutorialStep ) ;
}
// Keyboard support: Enter/Right to advance, Escape to close
document . addEventListener ( 'keydown' , function tutorialKeyHandler ( e ) {
if ( ! document . getElementById ( 'tutorial-overlay' ) ) return ;
if ( e . key === 'Enter' || e . key === 'ArrowRight' ) {
e . preventDefault ( ) ;
if ( _tutorialStep >= TUTORIAL _STEPS . length - 1 ) {
closeTutorial ( ) ;
} else {
nextTutorialStep ( ) ;
}
} else if ( e . key === 'Escape' ) {
e . preventDefault ( ) ;
closeTutorial ( ) ;
}
} ) ;
function closeTutorial ( ) {
const overlay = document . getElementById ( 'tutorial-overlay' ) ;
if ( overlay ) {
overlay . style . animation = 'tutorial-fade-in 0.3s ease-in reverse' ;
setTimeout ( ( ) => overlay . remove ( ) , 280 ) ;
}
markTutorialDone ( ) ;
}
function startTutorial ( ) {
if ( isTutorialDone ( ) ) return ;
createTutorialStyles ( ) ;
_tutorialStep = 0 ;
// Small delay so the page renders first
setTimeout ( ( ) => renderTutorialStep ( 0 ) , 300 ) ;
}