feat(qol & backup): add wave progress bar, hover collection, area explosion preview, hotkeys, auto-sync and json save export/import
This commit is contained in:
1 parent
7cd0792799
commit
23918dab1f
7 files changed
+710
-38
No files matched your search
+96
-1
@@ -677,12 +677,20 @@ window.GameEngine = class GameEngine {
|
||||
}
|
||||
|
||||
checkTouchCollect(pos) {
|
||||
if (!this.isRunning) return;
|
||||
for (let i = this.economy.suns.length - 1; i >= 0; i--) {
|
||||
const s = this.economy.suns[i];
|
||||
if (!s.collecting && Math.hypot(pos.x - s.x, pos.y - s.y) <= s.radius + 28) {
|
||||
s.collecting = true;
|
||||
this.triggerHaptic(12);
|
||||
if (window.PvZAudio) window.PvZAudio.playSun();
|
||||
if (this.economy.workshop) {
|
||||
this.economy.sunCount = (this.economy.sunCount || 0) + 1;
|
||||
if (this.economy.sunCount >= 8) {
|
||||
this.economy.sunCount = 0;
|
||||
this.economy.workshop.addMaterial('sunEssence', 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (let i = this.economy.coins.length - 1; i >= 0; i--) {
|
||||
@@ -699,6 +707,9 @@ window.GameEngine = class GameEngine {
|
||||
this.canvas.addEventListener('mousemove', (e) => {
|
||||
const pos = this.getEventPos(e);
|
||||
this.hoverCell = this.grid.getCellFromCoords(pos.x, pos.y);
|
||||
if (this.isRunning) {
|
||||
this.checkTouchCollect(pos);
|
||||
}
|
||||
});
|
||||
|
||||
this.canvas.addEventListener('mouseleave', () => {
|
||||
@@ -920,10 +931,11 @@ window.GameEngine = class GameEngine {
|
||||
document.addEventListener('fullscreenchange', updateFullscreenBtn);
|
||||
document.addEventListener('webkitfullscreenchange', updateFullscreenBtn);
|
||||
|
||||
// Global Hotkeys (Space, KeyP for Pause; KeyF for Fullscreen)
|
||||
// Global Hotkeys (Space/KeyP: Pause; KeyF: Fullscreen; Digit1-Digit6: Deck slots 1-6; KeyS: Shovel)
|
||||
window.addEventListener('keydown', (e) => {
|
||||
// Do not intercept hotkeys if user is typing in an input or textarea
|
||||
if (['INPUT', 'TEXTAREA', 'SELECT'].includes(document.activeElement?.tagName)) return;
|
||||
|
||||
if (e.code === 'Space' || e.code === 'KeyP') {
|
||||
e.preventDefault();
|
||||
if (this.isRunning) {
|
||||
@@ -932,6 +944,29 @@ window.GameEngine = class GameEngine {
|
||||
} else if (e.code === 'KeyF') {
|
||||
e.preventDefault();
|
||||
toggleFullscreen();
|
||||
} else if (e.code === 'KeyS' || e.key === 's' || e.key === 'S' || e.key === 'ы' || e.key === 'Ы') {
|
||||
e.preventDefault();
|
||||
if (this.isRunning && !this.isPaused) {
|
||||
this.economy.selectedCard = (this.economy.selectedCard === 'shovel') ? null : 'shovel';
|
||||
if (window.PvZAudio) window.PvZAudio.playButtonClick();
|
||||
this.triggerHaptic(15);
|
||||
}
|
||||
} else if (['Digit1', 'Digit2', 'Digit3', 'Digit4', 'Digit5', 'Digit6', 'Numpad1', 'Numpad2', 'Numpad3', 'Numpad4', 'Numpad5', 'Numpad6'].includes(e.code) || (e.key >= '1' && e.key <= '6')) {
|
||||
const num = parseInt(e.key, 10) || parseInt(e.code.replace(/\D/g, ''), 10);
|
||||
const slotIdx = num - 1;
|
||||
if (this.isRunning && !this.isPaused && this.economy.cards && slotIdx >= 0 && slotIdx < this.economy.cards.length) {
|
||||
e.preventDefault();
|
||||
const card = this.economy.cards[slotIdx];
|
||||
if (card) {
|
||||
if (this.economy.selectedCard === card.id) {
|
||||
this.economy.selectedCard = null;
|
||||
} else if (this.economy.canAfford(card.cost) && card.timer <= 0) {
|
||||
this.economy.selectedCard = card.id;
|
||||
if (window.PvZAudio) window.PvZAudio.playButtonClick();
|
||||
this.triggerHaptic(15);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1182,6 +1217,66 @@ window.GameEngine = class GameEngine {
|
||||
});
|
||||
}
|
||||
|
||||
// Export / Import Save File Handlers
|
||||
const btnExportSave = document.getElementById('btn-export-save');
|
||||
const btnExportSaveProfile = document.getElementById('btn-export-save-profile');
|
||||
const btnImportSave = document.getElementById('btn-import-save');
|
||||
const btnImportSaveProfile = document.getElementById('btn-import-save-profile');
|
||||
const inputImportSave = document.getElementById('input-import-save');
|
||||
|
||||
const handleExportSave = () => {
|
||||
this.saveUserData();
|
||||
if (window.PvZAuth && window.PvZAuth.exportSaveFile) {
|
||||
window.PvZAuth.exportSaveFile();
|
||||
}
|
||||
};
|
||||
|
||||
if (btnExportSave) btnExportSave.addEventListener('click', handleExportSave);
|
||||
if (btnExportSaveProfile) btnExportSaveProfile.addEventListener('click', handleExportSave);
|
||||
|
||||
const handleTriggerImport = () => {
|
||||
if (inputImportSave) {
|
||||
inputImportSave.value = '';
|
||||
inputImportSave.click();
|
||||
}
|
||||
};
|
||||
|
||||
if (btnImportSave) btnImportSave.addEventListener('click', handleTriggerImport);
|
||||
if (btnImportSaveProfile) btnImportSaveProfile.addEventListener('click', handleTriggerImport);
|
||||
|
||||
if (inputImportSave) {
|
||||
inputImportSave.addEventListener('change', async (e) => {
|
||||
const file = e.target.files && e.target.files[0];
|
||||
if (!file) return;
|
||||
if (window.PvZAuth && window.PvZAuth.importSaveFile) {
|
||||
const res = await window.PvZAuth.importSaveFile(file);
|
||||
if (res && res.success) {
|
||||
this.loadUserData();
|
||||
this.updateProfileUI();
|
||||
this.updateRecordDisplay();
|
||||
this.updateShopUI();
|
||||
}
|
||||
}
|
||||
inputImportSave.value = '';
|
||||
});
|
||||
}
|
||||
|
||||
// Auto-save on page hide / tab switch / page unload
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
this.saveUserData();
|
||||
if (window.PvZAuth && window.PvZAuth.flushSave) {
|
||||
window.PvZAuth.flushSave();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
window.addEventListener('beforeunload', () => {
|
||||
this.saveUserData();
|
||||
if (window.PvZAuth && window.PvZAuth.flushSave) {
|
||||
window.PvZAuth.flushSave(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Inventory / Deck Builder Buttons
|
||||
const btnInvStart = document.getElementById('btn-inventory-start');
|
||||
|
||||
Reference in new issue
Block a user