Кнопка сброса прогресса с диалогом подтверждения

- 🗑️ в HUD: диалог «Сбросить прогресс?» (Отмена / Сбросить)
- сбрасывает монеты, снасти, коллекцию и статистику; настройка звука сохраняется
- возврат в главное меню, перезапись localStorage
- автотест: 4 новые проверки (30/30)
This commit is contained in:
2026-08-27 06:50:11 +00:00
parent 1f224822a9
commit f6d89b7a09
11 changed files with 71 additions and 1 deletions
+1
View File
@@ -24,6 +24,7 @@
- Приманки: частота клёва, дальность броска, шанс редких и крупных
- Дневник рыбака — коллекция, максимальный улов по каждому виду, статистика
- Прогресс и коллекция сохраняются в `localStorage` (старые сейвы мигрируются автоматически)
- Сброс всего прогресса кнопкой 🗑️ в HUD — через диалог подтверждения (настройка звука при сбросе сохраняется)
- Звуки на WebAudio, вибрация на телефоне
- Вся графика нарисована на Canvas — внешних ресурсов нет
+42
View File
@@ -81,6 +81,11 @@ button{font-family:inherit;cursor:pointer}
@keyframes recpulse{0%,100%{transform:scale(1)}50%{transform:scale(1.09)}}
#card{animation:pop .3s ease}
@keyframes pop{from{transform:scale(.75);opacity:0}to{transform:scale(1);opacity:1}}
.twobtns{display:flex;gap:10px;margin-top:16px}
.twobtns .big{margin-top:0;width:auto;flex:1}
.big.danger{background:linear-gradient(#ff8a7a,#e74c3c);box-shadow:0 4px 0 #96281b;color:#fff}
.big.danger:active{box-shadow:0 2px 0 #96281b}
</style>
</head>
<body>
@@ -90,6 +95,7 @@ button{font-family:inherit;cursor:pointer}
<div id="coins">🪙 0</div>
<div class="hudbtns">
<button id="btnShop">🛒</button>
<button id="btnReset" title="Сбросить прогресс">🗑️</button>
<button id="btnMute">🔊</button>
</div>
</div>
@@ -126,6 +132,18 @@ button{font-family:inherit;cursor:pointer}
</div>
</div>
<!-- Подтверждение сброса -->
<div id="resetDlg" class="overlay hidden">
<div class="panel">
<div class="panelhead">🗑️ Сбросить прогресс?</div>
<p style="margin-top:8px">Монеты, снасти и коллекция будут стёрты.<br>Это действие <b>нельзя отменить</b>.</p>
<div class="twobtns">
<button id="btnResetNo" class="big">Отмена</button>
<button id="btnResetYes" class="big danger">Сбросить</button>
</div>
</div>
</div>
<!-- Карточка улова -->
<div id="card" class="overlay hidden">
<div class="panel">
@@ -900,6 +918,30 @@ $('btnMute').addEventListener('click',()=>{
save.muted=!save.muted; persist(); updateHUD(); sfx('click');
});
$('btnRecall').addEventListener('click',()=>{ recall(); });
$('btnReset').addEventListener('click',()=>{
sfx('click');
uiOpen=true; // пауза, пока идёт подтверждение
$('resetDlg').classList.remove('hidden');
});
$('btnResetNo').addEventListener('click',()=>{
sfx('click');
$('resetDlg').classList.add('hidden');
uiOpen=(state==='MENU');
});
$('btnResetYes').addEventListener('click',()=>{
const muted=save.muted; // настройка звука — не игровые данные, оставляем
save={coins:0,rod:0,lure:0,rods:[0],lures:[0],caught:{},muted:muted,casts:0,earned:0};
persist();
reel=null; biteFish=null;
$('resetDlg').classList.add('hidden');
$('shop').classList.add('hidden');
$('card').classList.add('hidden');
state='MENU'; uiOpen=true;
$('menu').classList.remove('hidden');
updateHUD(); renderShop();
sfx('no'); vib(50);
toast('Прогресс сброшен 🔄');
});
$('btnCardOk').addEventListener('click',()=>{
sfx('click');
$('card').classList.add('hidden');
Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 156 KiB

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 262 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 234 KiB

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

+28 -1
View File
@@ -180,7 +180,34 @@ const sleep = ms => new Promise(r => setTimeout(r, ms));
const saved = await page.evaluate(() => ({ coins: save.coins, rod: save.rod, lure: save.lure, casts: save.casts }));
ok('сохранение после reload', saved.coins === 9600 && saved.rod === 1 && saved.lure === 1 && saved.casts === 5, JSON.stringify(saved));
// --- 9. Ошибки консоли ---
// --- 9. Сброс данных (играть сначала нужно зайти в игру) ---
await page.click('#btnPlay');
await sleep(150);
await page.click('#btnReset');
await sleep(150);
ok('диалог сброса открыт', await page.$eval('#resetDlg', el => !el.classList.contains('hidden')));
await page.screenshot({ path: 'shot-reset.png' });
await page.click('#btnResetNo');
ok('отмена: диалог закрыт, данные не тронуты',
await page.$eval('#resetDlg', el => el.classList.contains('hidden'))
&& (await page.evaluate(() => save.coins)) === 9600);
await page.click('#btnReset');
await sleep(100);
await page.click('#btnResetYes');
await sleep(150);
const afterReset = await page.evaluate(() => ({
coins: save.coins, rod: save.rod, lure: save.lure, casts: save.casts,
caught: JSON.stringify(save.caught), state: state,
menu: !document.getElementById('menu').classList.contains('hidden'),
ls: localStorage.getItem('fishing-save-v1'),
}));
ok('сброс: данные обнулены, возврат в меню',
afterReset.coins === 0 && afterReset.rod === 0 && afterReset.lure === 0
&& afterReset.casts === 0 && afterReset.caught === '{}' && afterReset.state === 'MENU'
&& afterReset.menu, JSON.stringify(afterReset));
ok('сброс: localStorage перезаписан', afterReset.ls === null || JSON.parse(afterReset.ls).coins === 0);
// --- 10. Ошибки консоли ---
ok('нет ошибок JS (pageerror)', pageErrors.length === 0, pageErrors.join(' | '));
ok('нет ошибок консоли', consoleErrors.length === 0, consoleErrors.join(' | '));