⚗️
⚗️ SNZK DEBUG v20260623-serial
Фазы
Персонажи
Теги
Статистика
Debug
var idx=PHASES.indexOf(cp); if(idx>0) allowed.push(PHASES[idx-1].id); if(idx=0;}); }}, {id:'condition_check',name:'Проверка условий', filter:function(state,candidates){ return candidates.filter(function(e){return e.conditions?e.conditions(state):true;}); }}, {id:'tag_diversity',name:'Разнообразие тегов', filter:function(state,candidates){ var recentTags={}; var recent=state.log.slice(-3); for(var i=0;i0?fresh:candidates; }}, {id:'metric_mood',name:'Метрики и настроение', filter:function(state,candidates){ var mood=getMood(state); var scored=candidates.map(function(e){ var score=e.weight||5; if(e.tags){ if(mood.indexOf('mania_high')>=0&&e.tags.indexOf('mania')>=0) score+=3; if(mood.indexOf('tumpik_high')>=0&&e.tags.indexOf('tumpik')>=0) score+=3; if(mood.indexOf('energy_critical')>=0&&e.tags.indexOf('rest')>=0) score+=3; if(mood.indexOf('energy_low')>=0&&e.tags.indexOf('rest')>=0) score+=2; } return{event:e,score:score}; }); scored.sort(function(a,b){return b.score-a.score;}); var half=Math.max(1,Math.floor(scored.length/2)); return scored.slice(0,half).map(function(s){return s.event;}); }}, {id:'character_affinity',name:'Сроднение персонажей', filter:function(state,candidates){ var affinity={}; for(var i=0;i=80) m.push('mania_high'); if(state.mania>=50&&state.mania<80) m.push('mania_mid'); if(state.tumpik>=60) m.push('tumpik_high'); if(state.tumpik>=30&&state.tumpik<60) m.push('tumpik_mid'); if(state.energy<=20) m.push('energy_critical'); if(state.energy>20&&state.energy<50) m.push('energy_low'); return m; } function findEventById(id){ for(var i=0;ig.max_mania) return false; if(g.min_tumpik!==undefined&&state.tumpikg.max_tumpik) return false; if(g.min_energy!==undefined&&state.energyg.max_energy) return false; } if(c.mood_gate){ var found=false; for(var i=0;i100?'...':''),choice:choiceText||'', energy:metrics.energy,mania:metrics.mania,tumpik:metrics.tumpik}); } function showCliffhangerScreen(cliffEvent,onDone){ var app=$('#app'); if(!app){if(onDone)onDone();return;} var div=document.createElement('div'); div.className='cliffhanger-overlay'; div.id='cliffhanger-overlay'; var html=''; html+='
'; html+='
'; html+='
СЦЕНА
'; html+='
'+cliffEvent.title+'
'; html+='
'+cliffEvent.hook+'
'; html+=''; html+='
'; div.innerHTML=html; app.appendChild(div); setTimeout(function(){div.classList.add('active');},50); var btn=$('#cliffhanger-btn'); if(btn){ btn.addEventListener('click',function(){ div.classList.remove('active'); setTimeout(function(){ var co=$('#cliffhanger-overlay'); if(co) co.remove(); if(onDone) onDone(); },800); }); } } function showPauseScreen(pauseEvent,onDone){ state.showingPause=true; var app=$('#app'); if(!app){if(onDone)onDone();return;} var div=document.createElement('div'); div.className='pause-screen fade-in'; div.innerHTML='
'+pauseEvent.text+'
Продолжить ▶
'; app.appendChild(div); setTimeout(function(){ var btn=$('#pause-continue-btn'); if(btn){ btn.classList.add('visible'); btn.addEventListener('click',function(){ state.showingPause=false; var ps=$('.pause-screen'); if(ps) ps.remove(); if(onDone) onDone(); }); } },500); setTimeout(function(){ if(state.showingPause){ state.showingPause=false; var ps=$('.pause-screen'); if(ps) ps.remove(); if(onDone) onDone(); } },pauseEvent.duration+2000); } function showCheckpointScreen(episodeId,episodeTitle,onDone){ var app=$('#app'); if(!app){if(onDone)onDone();return;} var div=document.createElement('div'); div.className='checkpoint-screen'; div.id='checkpoint-overlay'; // Determine next episode info var _nextEpId=null,_nextEpTitle=null; var _epNum=parseInt(episodeId.replace('ep','')); if(!isNaN(_epNum)){ var _nextNum=_epNum+1; var _nextId='ep'+_nextNum; // Find an event with this episode to get title for(var _ei=0;_ei'; html+='
'+episodeTitle+'
'; html+='
'; html+='
'+state.energy+'
Энергия
'; html+='
'+state.mania+'
Мания
'; html+='
'+state.tumpik+'
Тупик
'; html+='
'; html+='
ПРОГРЕСС ПРОХОЖДЕНИЯ
'; if(_nextEpId&&_nextEpTitle){ html+=''; } else { html+=''; } html+=''; div.innerHTML=html; app.appendChild(div); setTimeout(function(){div.classList.add('active');},80); var btn=$('#checkpoint-cta'); if(btn){ btn.addEventListener('click',function(){ // Glitch out var _glitch=$('#glitch-overlay'); if(_glitch){_glitch.classList.add('active');} div.classList.remove('active'); setTimeout(function(){ var co=$('#checkpoint-overlay'); if(co) co.remove(); if(_glitch) _glitch.classList.remove('active'); if(onDone) onDone(); },600); }); } } // ═══ RENDER ═══ function $(s){return document.querySelector(s);} function generateSceneBackdrop(phaseId,visualStyle,visualMood){ var canvas=document.getElementById('scene-backdrop-canvas'); if(!canvas) return; var ctx=canvas.getContext('2d'); if(!ctx) return; var W=canvas.width=600,H=canvas.height=400; // Phase-specific cinematic palettes var phasePalettes={ iskra:{bg:['#060612','#0e0e20','#12122a'],accent:'#00ff88',fog:'rgba(0,255,136,0.03)'}, ekspansia:{bg:['#040e04','#0c1e0c','#102a10'],accent:'#33ccff',fog:'rgba(51,204,255,0.03)'}, soprotiv:{bg:['#0e0e04','#1e1e0c','#2a2a10'],accent:'#ffcc00',fog:'rgba(255,204,0,0.03)'}, krah:{bg:['#100404','#200c0c','#2a1010'],accent:'#ff6633',fog:'rgba(255,102,51,0.04)'}, ten:{bg:['#080412','#100c20','#18102a'],accent:'#b026ff',fog:'rgba(176,38,255,0.04)'} }; var pal=phasePalettes[phaseId]||phasePalettes.iskra; // Deep gradient background var grad=ctx.createLinearGradient(0,0,0,H); grad.addColorStop(0,pal.bg[0]); grad.addColorStop(0.4,pal.bg[1]); grad.addColorStop(1,pal.bg[2]); ctx.fillStyle=grad; ctx.fillRect(0,0,W,H); // Atmospheric fog layer ctx.fillStyle=pal.fog; ctx.fillRect(0,0,W,H); // Procedural environment elements based on phase var seed=0; for(var i=0;i60){var _mg=ctx.createRadialGradient(W*0.3,H*0.3,0,W*0.3,H*0.3,W*0.5);_mg.addColorStop(0,'rgba(176,38,255,0.06)');_mg.addColorStop(1,'rgba(176,38,255,0)');ctx.fillStyle=_mg;ctx.fillRect(0,0,W,H);} if(state.tumpik>60){var _tg=ctx.createRadialGradient(W*0.7,H*0.7,0,W*0.7,H*0.7,W*0.5);_tg.addColorStop(0,'rgba(255,51,102,0.06)');_tg.addColorStop(1,'rgba(255,51,102,0)');ctx.fillStyle=_tg;ctx.fillRect(0,0,W,H);} if(state.energy<30){ctx.fillStyle='rgba(0,0,0,0.35)';ctx.fillRect(0,0,W,H);} // Visual style effects if(visualStyle==='cyberpunk-harsh'){ // Scanlines ctx.strokeStyle='rgba(0,0,0,0.15)';ctx.lineWidth=1; for(var y=0;y';return;} debugState.runStats[state.eventId]=(debugState.runStats[state.eventId]||0)+1; // Apply effects if(event.effects){for(var k in event.effects){if(state[k]!==undefined) state[k]=Math.max(0,Math.min(100,state[k]+event.effects[k]));}} if(event.sets_flag) state.flags[event.sets_flag]=true; if(event.type==='ending'&&event.endingId&&state.endings.indexOf(event.endingId)===-1) state.endings.push(event.endingId); var progress=getProgress(state); var phase=getPhase(progress); var availableChoices=getAvailableChoices(event,state); var advisor=getAdvisorText(state,event); var intruder=getIntruderText(state,event); var speakerName=getSpeakerName(event); var speakerColor=getSpeakerColor(event); var visualStyle=event.visual?event.visual.style:'cyberpunk-soft'; var visualMood=event.visual?event.visual.mood:'neutral'; var vignetteClass='v-soft'; if(visualStyle==='cyberpunk-harsh') vignetteClass='v-harsh'; else if(visualStyle==='ethereal') vignetteClass='v-ethereal'; if(visualMood==='void'||visualMood==='blind') vignetteClass='v-void'; // ═══ BUILD CINEMATIC LAYOUT ═══ var html=''; // Header (minimal, semi-transparent) html+='
⚗️ SNZKШаг '+state.choices+' | Концовок: '+state.endings.length+'v20260609
'; // Phase bar (minimal) html+='
'; for(var pi=0;pi
'; } html+=''+phase.name+''; html+=''; var _ep=event.episode;if(_ep&&_ep.id) html+=''+_ep.id.toUpperCase()+''; html+=''+Math.round(progress)+'%'; html+=''; // Metrics (minimal) html+='
'; var mkeys=['energy','mania','tumpik']; for(var mi=0;mi=80)?';text-shadow:0 0 8px #b026ff':''; warn+=(mk==='tumpik'&&mv>=60)?';text-shadow:0 0 8px #ff3366':''; html+='
'+mv+'
'+METRIC_LABELS[mk]+'
'; } html+='
'; // ═══ SCENE VIEWPORT (full remaining space) ═══ html+='
'; // Canvas backdrop html+='
'; // Character silhouettes html+='
'; // Speaker if(event.slots&&event.slots.speaker){ var _spCh=CHARACTERS[event.slots.speaker]; if(_spCh&&_spCh.visual){ html+='
'+_spCh.visual.emoji+'
'+_spCh.name+'
'; } } // Advisor if(advisor){ var _adCh=null; for(var _ck in CHARACTERS){if(CHARACTERS[_ck].name===advisor.name){_adCh=CHARACTERS[_ck];break;}} if(_adCh&&_adCh.visual){ html+='
'+_adCh.visual.emoji+'
'+_adCh.name+'
'; } } // Intruder if(intruder){ var _inCh=null; for(var _ck2 in CHARACTERS){if(CHARACTERS[_ck2].name===intruder.name){_inCh=CHARACTERS[_ck2];break;}} if(_inCh&&_inCh.visual){ html+='
'+_inCh.visual.emoji+'
'+_inCh.name+'
'; } } html+='
'; // ═══ STORY OVERLAY (bottom of scene) ═══ html+='
'; html+='
'; // Speaker name with avatar if(speakerName){ var _spAv='🧪'; var _spCol=speakerColor; if(event.slots&&event.slots.speaker){ var _spCh2=CHARACTERS[event.slots.speaker]; if(_spCh2&&_spCh2.visual){_spAv=_spCh2.visual.emoji;_spCol=_spCh2.visual.glow;} } html+='
'+_spAv+''+speakerName+'
'; } // Narrative text html+='
'; // Intruder box if(intruder) html+='
⚡ '+intruder.name+' вмешивается
Ты чувствуешь чужое присутствие.
'; // Advisor box if(advisor) html+='
💬 '+advisor.name+'
'+advisor.text+'
'; // Ending if(event.type==='ending'){ html+='
🏆 КОНЦОВКА: '+(event.endingId||'???').toUpperCase()+'
'; html+='
'; for(var ei=0;ei'; html+='
'; } else if(availableChoices.length>0){ html+='
'; for(var ci=0;ci=60||c.gate.min_tumpik>=50||c.gate.max_energy<=30)){ if(c.gate.min_mania>=60) styleClass='mania'; else if(c.gate.min_tumpik>=50) styleClass='tumpik'; else if(c.gate.max_energy<=30) styleClass='tired'; } if(c.mood_gate&&c.mood_gate.indexOf('mania')>=0) styleClass='mania'; if(c.mood_gate&&c.mood_gate.indexOf('tumpik')>=0) styleClass='tumpik'; if(c.mood_gate&&c.mood_gate.indexOf('energy')>=0) styleClass='tired'; html+=''; } html+='
'; } html+='
'; // story-content html+='
'; // story-overlay html+='
'; // scene-viewport // Log toggle + panel (collapsible at bottom) html+='
📜 Лог ('+state.log.length+')
'; html+='
'; for(var li=0;li'+le.step+'. '+le.speaker+': '+le.text.substring(0,50)+'... E:'+le.energy+' M:'+le.mania+' T:'+le.tumpik+'
'; } html+=''; html+=''; app.innerHTML=html; // ═══ POST-RENDER: Canvas + Animations ═══ setTimeout(function(){generateSceneBackdrop(phase.id,visualStyle,visualMood);},0); // Animate character silhouettes setTimeout(function(){ var _chars=document.querySelectorAll('.char-silhouette'); for(var _ci=0;_ci<_chars.length;_ci++){(function(el,delay){setTimeout(function(){el.classList.add('active');},delay);})(_chars[_ci],_ci*200);} },100); // Typewriter effect var narr=$('#narr-text'); if(narr){ var idx=0; var text=event.text; var speed=visualMood==='intense'||visualMood==='climax'?12:18; var iv=setInterval(function(){ idx++; if(idx<=text.length){narr.textContent=text.slice(0,idx)+(idx'+ph.name+' ('+ph.min+'–'+ph.max+'%) — '+events.length+' событий'; for(var ei=0;ei'; html+=''+ev.id+''; html+=''+ph.id+''; html+=''+(ev.text||'').substring(0,60).replace(/\n/g,' ')+'…'; if(ev.type==='ending') html+='🏆'; html+=''; } html+=''; } html+=''; return html; } function debugRenderCharacters(){ var html='
Персонажи
'; for(var key in CHARACTERS){ var ch=CHARACTERS[key]; html+='
'; html+=''+ch.name+''; html+=''+ch.role+''; html+=''+(ch.canAppear?'canAppear':'always')+''; html+='
'; } html+='
'; html+='
Персонажи в событиях
'; var charEventCount={}; for(var i=0;i'+ch.name+''+charEventCount[cid]+' событий
'; } } html+=''; return html; } function debugRenderTags(){ var tagCount={}; for(var i=0;i'; for(var s=0;s×'+sortedTags[s].count+''; } html+=''; html+='
События по тегам
'; for(var s2=0;s2=0;}); html+='
'; html+='
'+tag+' ('+events.length+')
'; for(var e2=0;e2'; html+=''+events[e2].id+''; html+=''+(events[e2].text||'').substring(0,50).replace(/\n/g,' ')+'…'; html+='
'; } if(events.length>5) html+='
…и ещё '+(events.length-5)+'
'; html+='
'; } html+=''; return html; } function debugRenderStats(){ var html='
Текущее состояние
'; html+='
Событие'+state.eventId+'
'; html+='
Шаг'+state.choices+'
'; html+='
Энергия'+state.energy+'
'; html+='
Мания'+state.mania+'
'; html+='
Тупик'+state.tumpik+'
'; var progress=getProgress(state); var phase=getPhase(progress); html+='
Прогресс'+Math.round(progress)+'%
'; html+='
Фаза'+phase.name+'
'; html+='
Концовки'+state.endings.length+'
'; html+='
Флаги'+JSON.stringify(state.flags)+'
'; html+='
'; html+='
Статистика появления событий за забег
'; var stats=debugState.runStats; var sortedStats=[]; for(var eid in stats) sortedStats.push({id:eid,count:stats[eid]}); sortedStats.sort(function(a,b){return b.count-a.count;}); if(sortedStats.length===0){ html+='
Пока ни одно событие не было показано
'; } else { for(var si=0;si×'+sortedStats[si].count+'
'; } } html+=''; html+='
Общая статистика пула
'; html+='
Всего событий'+EVENT_POOL.length+'
'; html+='
Концовок'+EVENT_POOL.filter(function(e){return e.type==='ending';}).length+'
'; html+='
Персонажей'+Object.keys(CHARACTERS).length+'
'; html+='
Архетипов'+ARCHETYPES.length+'
'; html+='
'; return html; } function debugRenderDebug(){ var html='
Управление фазой
'; html+='
Принудительно установить фазу (переопределяет расчёт по прогрессу)
'; html+='
'; html+=''; for(var pi=0;pi'+ph.name+''; } html+='
'; html+='
Метрики
'; var metrics=['energy','mania','tumpik']; for(var mi=0;mi'; html+=''; html+=''+state[mk]+''; html+='
'; } html+=''; html+='
Принудительный персонаж
'; html+='
Заставить персонажа появиться как advisor в следующем событии
'; html+='
'; html+=''; for(var key in CHARACTERS){ var ch=CHARACTERS[key]; var active2=debugState.forceCharacter===ch.id?' active':''; html+=''; } html+='
'; html+='
'; html+='
Принудительное событие
'; html+='
'; html+=''; html+=''; html+='
'; html+='
Введите ID события и нажмите → для немедленного перехода
'; html+='
'; html+='
Сброс
'; html+=''; html+=''; html+='
'; return html; } function debugBindTabEvents(tab){ if(tab==='phases'||tab==='tags'){ var items=debugBody.querySelectorAll('[data-event-id]'); for(var i=0;i10) msg+='…и ещё '+(events.length-10); alert(msg); }); })(charItems[ci]); } } if(tab==='debug'){ var phaseBtns=debugBody.querySelectorAll('[data-force-phase]'); for(var pi=0;pi=3){ debugTapCount=0; debugShowPanel(); } }); } // Also: keyboard `~` key document.addEventListener('keydown',function(e){ if(e.key==='`'||e.key==='~'){ if(debugPanel.classList.contains('open')) debugHidePanel(); else debugShowPanel(); } if(e.key==='Escape'&&debugPanel.classList.contains('open')) debugHidePanel(); }); // Tab clicks var tabEls=document.querySelectorAll('.debug-tab'); for(var ti=0;ti'; console.error('[SNZK] Boot failed:',e); } })();