feat(geom11 ch1 final): Финал Раздела 1 (5 боссов + ачивка)
This commit is contained in:
@@ -306,7 +306,8 @@ const ACH_LABELS = {
|
||||
p1_done:"Призма освоено!",
|
||||
p2_done:"Цилиндр освоено!",
|
||||
start:"Начало раздела 1!",
|
||||
ch1_done:"Раздел 1 пройден!"
|
||||
ch1_done:"Раздел 1 пройден!",
|
||||
r1_done:"Мастер призмы и цилиндра"
|
||||
};
|
||||
|
||||
function loadProgress(){
|
||||
@@ -392,7 +393,7 @@ function buildParaSelector(){
|
||||
}
|
||||
|
||||
const BUILT=new Set();
|
||||
const BUILDERS = { p1:()=>buildP1(), p2:()=>buildP2(), final1:()=>buildStub('final1') };
|
||||
const BUILDERS = { p1:()=>buildP1(), p2:()=>buildP2(), final1:()=>buildFinal1() };
|
||||
function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } }
|
||||
function goTo(id){
|
||||
STATE.current=id; ensureBuilt(id);
|
||||
@@ -409,7 +410,7 @@ function goTo(id){
|
||||
const SIDEBARS = {
|
||||
p1:{title:"Шпаргалка § 1", rows:[["Тема","Призма"],["Прямая","$S_{бок}=P_{осн}\\\\cdot h$"],["Наклонная","$S_{бок}=P_{пер}\\\\cdot l$"],["Объём","$V=S_{осн}\\\\cdot h$"],["Диагональ пар.","$d=\\\\sqrt{a^2+b^2+c^2}$"]]},
|
||||
p2:{title:"Шпаргалка § 2", rows:[["Тема", "Цилиндр"],["$S_{осн}$","$\\\\pi R^2$"],["$S_{бок}$","$2\\\\pi Rh$"],["$S_{полн}$","$2\\\\pi R(R+h)$"],["$V$","$\\\\pi R^2 h$"],["Развёртка","прямоуг. $2\\\\pi R \\\\times h$"],["Осевое сеч.","прямоуг. $2R \\\\times h$"],["Наклон. сеч.","эллипс, $a=R/\\\\cos\\\\alpha$, $b=R$"]]},
|
||||
final1:{title:"Финал раздела 1", rows:[["§ 1–§ 2","теория раздела 1"],["Награда","+50 XP"]]}
|
||||
final1:{title:"Финал раздела 1", rows:[["§ 1","Призма"],["§ 2","Цилиндр"],["Боссы","5 интегрированных"],["Награда","+50 XP + ачивка"]]}
|
||||
};
|
||||
|
||||
const TIPS=[
|
||||
@@ -1406,6 +1407,213 @@ function buildP2(){
|
||||
wireReadBtn('p2');
|
||||
}
|
||||
|
||||
/* ===== ФИНАЛ РАЗДЕЛА 1 — Шпаргалка + 5 боссов + ачивка ===== */
|
||||
function buildFinal1(){
|
||||
const box = document.getElementById('final1-body');
|
||||
if(!box) return;
|
||||
let html = '';
|
||||
|
||||
/* Часть А — Шпаргалка раздела (2 mini-карточки по числу § в разделе) */
|
||||
html += '<div class="card">'
|
||||
+ '<div class="card-header">'
|
||||
+ '<div class="card-icon theory">' + ICONS.theory + '</div>'
|
||||
+ '<div class="card-title">Шпаргалка раздела 1</div>'
|
||||
+ '<div class="card-num">Итог</div>'
|
||||
+ '</div>'
|
||||
+ '<div class="card-body">'
|
||||
+ '<p>Ключевые формулы обоих параграфов раздела в одном месте — пробеги глазами перед битвой с боссами.</p>'
|
||||
+ '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin-top:10px">'
|
||||
+ '<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">'
|
||||
+ '<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">'
|
||||
+ '<svg viewBox="0 0 24 24" fill="none" stroke="#0891b2" stroke-width="2" style="width:18px;height:18px"><path d="M3 7l9-4 9 4v10l-9 4-9-4z"/><path d="M3 7l9 4 9-4"/><path d="M12 11v10"/></svg>'
|
||||
+ '<div style="font-family:\'Unbounded\',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 1 · Призма</div>'
|
||||
+ '</div>'
|
||||
+ '<div style="font-size:.94rem;line-height:1.55">$V = S_{осн}\\cdot h$ — для любой призмы. Для прямой: $S_{бок}=P_{осн}\\cdot h$. Прямоугольный параллелепипед: $d^2=a^2+b^2+c^2$. Виды: прямая, правильная, наклонная, параллелепипед, куб.</div>'
|
||||
+ '</div>'
|
||||
+ '<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">'
|
||||
+ '<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">'
|
||||
+ '<svg viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2" style="width:18px;height:18px"><ellipse cx="12" cy="5" rx="8" ry="3"/><path d="M4 5v14a8 3 0 0 0 16 0V5"/></svg>'
|
||||
+ '<div style="font-family:\'Unbounded\',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 2 · Цилиндр</div>'
|
||||
+ '</div>'
|
||||
+ '<div style="font-size:.94rem;line-height:1.55">$V=\\pi R^2 h$, $S_{бок}=2\\pi Rh$, $S_{полн}=2\\pi R(R+h)$. Сечения: круг ($\\perp$ оси), прямоугольник (через ось или $\\parallel$ оси), эллипс (наклонная плоскость).</div>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '</div>'
|
||||
+ '</div>';
|
||||
|
||||
/* Часть Б — анонс 5 боссов */
|
||||
html += '<div class="card">'
|
||||
+ '<div class="card-header">'
|
||||
+ '<div class="card-icon rule">' + ICONS.rule + '</div>'
|
||||
+ '<div class="card-title">Боссы раздела 1</div>'
|
||||
+ '<div class="card-num">5</div>'
|
||||
+ '</div>'
|
||||
+ '<div class="card-body">'
|
||||
+ '<p>5 интегрированных задач — каждая комбинирует темы § 1 и § 2. За каждого побеждённого босса: <b>+10 XP, +18% к прогрессу</b>. Победишь всех — ачивка <b>«Мастер призмы и цилиндра»</b> и <b>+50 XP бонус</b>.</p>'
|
||||
+ '<p style="font-size:.88rem;color:var(--muted);margin-top:6px">Для расчётов с $\\pi$ используй $\\pi\\approx 3{,}14$. Допуск ответа — $\\pm 0{,}05$ (для боссов 2 и 5 указан в условии).</p>'
|
||||
+ '</div>'
|
||||
+ '</div>';
|
||||
|
||||
html += '<div id="r1-bosses-container"></div>';
|
||||
|
||||
/* Прогресс-итог + ачивка */
|
||||
html += '<div style="margin-top:18px;padding:18px 20px;background:linear-gradient(135deg,var(--pri-soft),var(--sec-acc-soft));border-radius:14px;border:1.5px solid var(--pri);text-align:center" id="r1-final-summary">'
|
||||
+ '<div style="font-family:\'Unbounded\',sans-serif;font-weight:800;color:var(--pri2);font-size:1.1rem;margin-bottom:6px">Прогресс по боссам</div>'
|
||||
+ '<div id="r1-boss-overall" style="font-size:.95rem;color:var(--text);margin-bottom:10px">0 / 5 боссов побеждено</div>'
|
||||
+ '<div style="height:12px;background:var(--card);border-radius:8px;overflow:hidden;border:1px solid var(--border)">'
|
||||
+ '<div id="r1-boss-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,#0891b2,#7c3aed);transition:width .35s"></div>'
|
||||
+ '</div>'
|
||||
+ '<div id="r1-final-reward" style="margin-top:14px;display:none;padding:14px;background:var(--card);border-radius:11px;border:2px solid #f59e0b">'
|
||||
+ '<div style="font-family:\'Unbounded\',sans-serif;font-weight:800;color:#92400e;font-size:1.05rem;margin-bottom:6px">Мастер призмы и цилиндра</div>'
|
||||
+ '<div style="font-size:.92rem;margin-bottom:10px">Раздел 1 пройден! Все 5 боссов повержены. +50 XP бонус.</div>'
|
||||
+ '<a class="btn primary" href="/textbook/geometry-11-ch2" style="text-decoration:none">Дальше: Раздел 2 <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></a>'
|
||||
+ '</div>'
|
||||
+ '</div>';
|
||||
|
||||
html += secNav('p2', null);
|
||||
|
||||
box.innerHTML = html;
|
||||
renderMath(box);
|
||||
|
||||
/* === Боссы === */
|
||||
const BOSSES = [
|
||||
{
|
||||
n:1, color:'#0891b2',
|
||||
title:'Циклоп Параллелепипеда',
|
||||
tag:'§ 1',
|
||||
q:'В прямоугольном параллелепипеде $a=3$, $b=4$, $c=12$. Найдите длину главной диагонали.',
|
||||
ans:13, tol:0.05,
|
||||
hint:'$d=\\sqrt{a^2+b^2+c^2}=\\sqrt{9+16+144}=\\sqrt{169}=13$.'
|
||||
},
|
||||
{
|
||||
n:2, color:'#10b981',
|
||||
title:'Минотавр Куба',
|
||||
tag:'§ 1',
|
||||
q:'Куб со стороной $4$. Найдите длину диагонали куба (допуск $\\pm 0{,}05$).',
|
||||
ans:6.93, tol:0.05,
|
||||
hint:'Диагональ куба: $d=a\\sqrt{3}=4\\sqrt{3}\\approx 6{,}928$.'
|
||||
},
|
||||
{
|
||||
n:3, color:'#7c3aed',
|
||||
title:'Гарпия Цилиндра',
|
||||
tag:'§ 2',
|
||||
q:'Цилиндр с $R=5$, $h=10$. Найдите объём (используйте $\\pi=3{,}14$).',
|
||||
ans:785, tol:0.05,
|
||||
hint:'$V=\\pi R^2 h = 3{,}14\\cdot 25\\cdot 10 = 785$.'
|
||||
},
|
||||
{
|
||||
n:4, color:'#dc2626',
|
||||
title:'Дракон Сечений',
|
||||
tag:'§ 2 + § 1',
|
||||
q:'Цилиндр с $R=6$ пересечён плоскостью под углом $60°$ к плоскости основания (плоскость пересекает обе окружности оснований). Найдите большую полуось эллипса в сечении.',
|
||||
ans:12, tol:0.05,
|
||||
hint:'При наклоне на угол $\\alpha$ малая полуось эллипса равна $R$, большая — $a=R/\\cos\\alpha = 6/\\cos 60° = 6/0{,}5 = 12$.'
|
||||
},
|
||||
{
|
||||
n:5, color:'#f59e0b',
|
||||
title:'Мастер 3D-форм',
|
||||
tag:'синтез § 1 + § 2',
|
||||
q:'В цилиндр с $R=6$ и $h=8$ вписана правильная шестиугольная призма (вершины основания лежат на окружности). Найдите объём призмы (допуск $\\pm 0{,}1$).',
|
||||
ans:748.25, tol:0.1,
|
||||
hint:'Сторона правильного 6-угольника, вписанного в окружность радиуса $R$, равна $R=6$. Его площадь: $S=\\tfrac{3\\sqrt{3}}{2}\\cdot 36 = 54\\sqrt{3}$. Объём: $V=S\\cdot h=54\\sqrt{3}\\cdot 8 = 432\\sqrt{3}\\approx 748{,}25$.'
|
||||
}
|
||||
];
|
||||
|
||||
const cont = document.getElementById('r1-bosses-container');
|
||||
const STATE_KEY = 'geometry11_ch1_bosses';
|
||||
const BOSS_STATE = (function(){
|
||||
try{ const s = localStorage.getItem(STATE_KEY); if(s){ const p = JSON.parse(s); if(Array.isArray(p) && p.length === BOSSES.length) return p; } }catch(e){}
|
||||
return BOSSES.map(()=>({defeated:false}));
|
||||
})();
|
||||
function saveBosses(){ try{ localStorage.setItem(STATE_KEY, JSON.stringify(BOSS_STATE)); }catch(e){} }
|
||||
|
||||
cont.innerHTML = BOSSES.map(b=>{
|
||||
return '<div class="boss-card" id="boss1-'+b.n+'-card" style="padding:16px;background:var(--card);border-radius:12px;border:2px solid '+b.color+';margin-bottom:14px">'
|
||||
+ '<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap">'
|
||||
+ '<svg viewBox="0 0 24 24" fill="none" stroke="'+b.color+'" stroke-width="2.2" style="width:28px;height:28px;flex-shrink:0"><polygon points="12,2 22,20 2,20"/></svg>'
|
||||
+ '<div style="font-family:\'Unbounded\',sans-serif;font-weight:800;color:'+b.color+';font-size:1.05rem">Босс '+b.n+': '+b.title+'</div>'
|
||||
+ '<div style="margin-left:auto;font-size:.78rem;color:var(--muted);padding:3px 8px;background:var(--sec-acc-soft);border-radius:6px">'+b.tag+'</div>'
|
||||
+ '</div>'
|
||||
+ '<div class="boss-q" id="boss1-'+b.n+'-q" style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:1rem;line-height:1.5;margin-bottom:10px">'+b.q+'</div>'
|
||||
+ '<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">'
|
||||
+ '<span style="font-family:\'JetBrains Mono\',monospace;font-size:.92rem">ответ =</span>'
|
||||
+ '<input type="number" id="boss1-'+b.n+'-ans" class="tinp" style="width:140px;text-align:center" step="0.01" placeholder="число">'
|
||||
+ '<button class="btn primary" id="boss1-'+b.n+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атаковать</button>'
|
||||
+ '<button class="btn" id="boss1-'+b.n+'-hint">Подсказка</button>'
|
||||
+ '</div>'
|
||||
+ '<div class="feedback" id="boss1-'+b.n+'-fb"></div>'
|
||||
+ '</div>';
|
||||
}).join('');
|
||||
renderMath(cont);
|
||||
|
||||
function refreshOverall(){
|
||||
const won = BOSS_STATE.filter(s => s.defeated).length;
|
||||
const txt = document.getElementById('r1-boss-overall');
|
||||
const fill = document.getElementById('r1-boss-overall-fill');
|
||||
if(txt) txt.textContent = won + ' / ' + BOSSES.length + ' боссов побеждено';
|
||||
if(fill) fill.style.width = (won * 100 / BOSSES.length) + '%';
|
||||
if(won >= BOSSES.length){
|
||||
const reward = document.getElementById('r1-final-reward');
|
||||
if(reward && reward.style.display === 'none'){
|
||||
reward.style.display = 'block';
|
||||
if(!STATE.achievements.has('r1_done')){
|
||||
achievement('r1_done','Мастер призмы и цилиндра');
|
||||
addXp(50, 'r1-bonus');
|
||||
bumpProgress('final1', 30);
|
||||
if(window.confetti){ try{ confetti(); }catch(e){} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOSSES.forEach((b, idx)=>{
|
||||
const card = document.getElementById('boss1-'+b.n+'-card');
|
||||
const goBtn = document.getElementById('boss1-'+b.n+'-go');
|
||||
const hintBtn= document.getElementById('boss1-'+b.n+'-hint');
|
||||
const ansInp = document.getElementById('boss1-'+b.n+'-ans');
|
||||
if(BOSS_STATE[idx].defeated){
|
||||
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
|
||||
card.classList.add('glow');
|
||||
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.innerHTML = '✓ Повержен';
|
||||
ansInp.disabled = true;
|
||||
}
|
||||
goBtn.addEventListener('click', ()=>{
|
||||
if(BOSS_STATE[idx].defeated) return;
|
||||
const fb = document.getElementById('boss1-'+b.n+'-fb');
|
||||
const raw = (ansInp.value||'').replace(',', '.').trim();
|
||||
const val = parseFloat(raw);
|
||||
if(!isFinite(val)){ feedback(fb, false, '✗ Введи число.'); return; }
|
||||
if(Math.abs(val - b.ans) <= b.tol){
|
||||
BOSS_STATE[idx].defeated = true; saveBosses();
|
||||
feedback(fb, true, '✓ Босс '+b.n+' повержен! +10 XP. '+b.hint);
|
||||
addXp(10, 'boss-r1-'+b.n);
|
||||
bumpProgress('final1', 18);
|
||||
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.innerHTML = '✓ Повержен';
|
||||
ansInp.disabled = true;
|
||||
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
|
||||
card.classList.add('glow','pulse');
|
||||
setTimeout(()=>card.classList.remove('pulse'), 900);
|
||||
refreshOverall();
|
||||
} else {
|
||||
feedback(fb, false, '✗ Промах. Попробуй ещё. Подсказка доступна.');
|
||||
}
|
||||
});
|
||||
hintBtn.addEventListener('click', ()=>{
|
||||
const fb = document.getElementById('boss1-'+b.n+'-fb');
|
||||
fb.className = 'feedback ok';
|
||||
fb.innerHTML = '<b>Подсказка:</b> '+b.hint;
|
||||
fb.style.display = 'block';
|
||||
fb.style.background = 'var(--warn-bg,#fef3c7)';
|
||||
fb.style.color = '#92400e';
|
||||
fb.style.borderLeftColor = 'var(--warn,#f59e0b)';
|
||||
try{ renderMath(fb); }catch(e){}
|
||||
});
|
||||
ansInp.addEventListener('keydown', e=>{ if(e.key === 'Enter') goBtn.click(); });
|
||||
});
|
||||
|
||||
refreshOverall();
|
||||
}
|
||||
|
||||
/* ===== STUB BUILDER — единый для всех параграфов раздела (Phase 0) ===== */
|
||||
|
||||
function buildStub(id){
|
||||
|
||||
Reference in New Issue
Block a user