feat(plants): Plant purchase system in Dave's Shop & pre-battle deck selection screen

This commit is contained in:
2026-09-17 10:30:57 +00:00
parent 4e450d758b
commit 84c28addfe
7 changed files with 889 additions and 66 deletions
+270 -4
View File
@@ -422,7 +422,14 @@ window.GameEngine = class GameEngine {
} else if (this.currentMode === 'roguelike') {
startLvl = this.roguelike ? this.roguelike.currentFloor : 1;
}
this.startLevel(startLvl, this.currentMode);
// Fixed-conveyor mini-games launch immediately, standard modes open deck builder
if (this.currentMode === 'bowling' || this.currentMode === 'izombie' || this.currentMode === 'boss' || this.currentMode === 'bossrush') {
this.startLevel(startLvl, this.currentMode);
} else {
this.openSeedSelectionModal(startLvl, this.currentMode);
}
if (!this.loopStarted) {
this.loopStarted = true;
requestAnimationFrame(this.loop.bind(this));
@@ -1102,10 +1109,32 @@ window.GameEngine = class GameEngine {
}
// Shop Button
// Inventory / Deck Builder Buttons
const btnInvStart = document.getElementById('btn-inventory-start');
const btnDrawerInv = document.getElementById('btn-drawer-inventory');
if (btnInvStart) {
btnInvStart.addEventListener('click', () => {
this.openSeedSelectionModal(this.currentLevel || 1, this.currentMode || 'adventure');
});
}
if (btnDrawerInv) {
btnDrawerInv.addEventListener('click', () => {
const drawer = document.getElementById('mobile-drawer');
const backdrop = document.getElementById('drawer-backdrop');
if (drawer) drawer.classList.add('hidden');
if (backdrop) backdrop.classList.add('hidden');
this.openSeedSelectionModal(this.currentLevel || 1, this.currentMode || 'adventure');
});
}
// Shop Button & Tabs
const btnShop = document.getElementById('btn-shop');
const shopModal = document.getElementById('shop-modal');
const btnCloseShop = document.getElementById('btn-close-shop');
const tabShopPlants = document.getElementById('tab-shop-plants');
const tabShopUpgrades = document.getElementById('tab-shop-upgrades');
const panelShopPlants = document.getElementById('shop-plants-panel');
const panelShopUpgrades = document.getElementById('shop-upgrades-panel');
if (btnShop && shopModal) {
btnShop.addEventListener('click', () => {
@@ -1120,6 +1149,73 @@ window.GameEngine = class GameEngine {
});
}
if (tabShopPlants && tabShopUpgrades && panelShopPlants && panelShopUpgrades) {
tabShopPlants.addEventListener('click', () => {
tabShopPlants.classList.add('active');
tabShopUpgrades.classList.remove('active');
panelShopPlants.classList.remove('hidden');
panelShopUpgrades.classList.add('hidden');
});
tabShopUpgrades.addEventListener('click', () => {
tabShopUpgrades.classList.add('active');
tabShopPlants.classList.remove('active');
panelShopUpgrades.classList.remove('hidden');
panelShopPlants.classList.add('hidden');
});
}
// Pre-Battle Seed Selection & Inventory Modal Handlers
const seedSelectModal = document.getElementById('seed-select-modal');
const btnSeedStart = document.getElementById('btn-seed-start-battle');
const btnSeedAuto = document.getElementById('btn-seed-auto-fill');
const btnSeedShop = document.getElementById('btn-seed-open-shop');
const btnSeedCancel = document.getElementById('btn-seed-cancel');
if (btnSeedStart) {
btnSeedStart.addEventListener('click', () => {
if (!this.economy.selectedDeck || this.economy.selectedDeck.length === 0) {
this.economy.autoFillDeck();
}
if (seedSelectModal) seedSelectModal.classList.add('hidden');
const startModal = document.getElementById('start-modal');
if (startModal) startModal.classList.add('hidden');
if (window.PvZAudio) window.PvZAudio.init();
const lvl = this.pendingLevel || this.currentLevel || 1;
const mode = this.pendingMode || this.currentMode || 'adventure';
this.startLevel(lvl, mode);
if (!this.loopStarted) {
this.loopStarted = true;
requestAnimationFrame(this.loop.bind(this));
}
});
}
if (btnSeedAuto) {
btnSeedAuto.addEventListener('click', () => {
this.economy.autoFillDeck();
this.renderSeedSelectionUI();
if (window.PvZAudio) window.PvZAudio.playButtonClick();
});
}
if (btnSeedShop) {
btnSeedShop.addEventListener('click', () => {
if (seedSelectModal) seedSelectModal.classList.add('hidden');
this.updateShopUI();
if (shopModal) shopModal.classList.remove('hidden');
});
}
if (btnSeedCancel) {
btnSeedCancel.addEventListener('click', () => {
if (seedSelectModal) seedSelectModal.classList.add('hidden');
const startModal = document.getElementById('start-modal');
if (startModal) startModal.classList.remove('hidden');
});
}
// Shop Item Purchases
const btnBuySlot = document.getElementById('btn-buy-slot');
if (btnBuySlot) {
@@ -1128,6 +1224,7 @@ window.GameEngine = class GameEngine {
if (window.PvZAudio) window.PvZAudio.playWin();
this.saveUserData();
this.updateShopUI();
this.renderSeedSelectionUI();
}
});
}
@@ -1544,7 +1641,11 @@ window.GameEngine = class GameEngine {
if (startModal) startModal.classList.add('hidden');
if (window.PvZAudio) window.PvZAudio.init();
this.startLevel(targetLevel, 'adventure');
if (targetLevel === 25) {
this.startLevel(targetLevel, 'adventure');
} else {
this.openSeedSelectionModal(targetLevel, 'adventure');
}
if (!this.loopStarted) {
this.loopStarted = true;
requestAnimationFrame(this.loop.bind(this));
@@ -1566,9 +1667,126 @@ window.GameEngine = class GameEngine {
});
}
openSeedSelectionModal(targetLevel = null, targetMode = null) {
this.pendingLevel = targetLevel !== null ? targetLevel : (this.currentLevel || 1);
this.pendingMode = targetMode !== null ? targetMode : (this.currentMode || 'adventure');
this.renderSeedSelectionUI();
const modal = document.getElementById('seed-select-modal');
if (modal) modal.classList.remove('hidden');
const startModal = document.getElementById('start-modal');
if (startModal) startModal.classList.add('hidden');
const campaignModal = document.getElementById('campaign-modal');
if (campaignModal) campaignModal.classList.add('hidden');
}
renderSeedSelectionUI() {
const activeDeckContainer = document.getElementById('seed-active-deck');
const inventoryContainer = document.getElementById('seed-inventory-grid');
const deckCountEl = document.getElementById('seed-deck-count');
const deckMaxEl = document.getElementById('seed-deck-max');
const maxSlots = this.economy.getMaxSlots();
const selectedDeck = this.economy.selectedDeck || [];
if (deckCountEl) deckCountEl.textContent = selectedDeck.length;
if (deckMaxEl) deckMaxEl.textContent = maxSlots;
// 1. Render Active Deck Slots
if (activeDeckContainer) {
activeDeckContainer.innerHTML = '';
selectedDeck.forEach(plantId => {
const p = window.PLANT_CATALOGUE.find(item => item.id === plantId);
if (!p) return;
const slot = document.createElement('div');
slot.className = 'seed-slot-card';
slot.title = `Нажмите, чтобы убрать ${p.name} из колоды`;
slot.innerHTML = `
<div class="slot-remove-badge">✕</div>
<div class="slot-icon">${p.icon}</div>
<div class="slot-cost">☀️ ${p.cost}</div>
`;
slot.addEventListener('click', () => {
this.economy.removeFromDeck(plantId);
this.renderSeedSelectionUI();
if (window.PvZAudio) window.PvZAudio.playButtonClick();
});
activeDeckContainer.appendChild(slot);
});
// Render Empty Slots up to maxSlots
for (let i = selectedDeck.length; i < maxSlots; i++) {
const emptySlot = document.createElement('div');
emptySlot.className = 'seed-slot-empty';
emptySlot.textContent = `+ Слот ${i + 1}`;
activeDeckContainer.appendChild(emptySlot);
}
}
// 2. Render Inventory Grid
if (inventoryContainer && window.PLANT_CATALOGUE) {
inventoryContainer.innerHTML = '';
window.PLANT_CATALOGUE.forEach(p => {
const isUnlocked = this.economy.isPlantUnlocked(p.id);
const isInDeck = selectedDeck.includes(p.id);
const canAfford = this.economy.totalCoins >= (p.price || 0);
const item = document.createElement('div');
item.className = `seed-inv-item ${isUnlocked ? (isInDeck ? 'in-deck' : 'unlocked') : 'locked'}`;
let statusBadge = '';
if (isInDeck) {
statusBadge = '<span class="seed-inv-status status-deck">✓ В колоде</span>';
} else if (isUnlocked) {
statusBadge = '<span class="seed-inv-status status-ready">+ Выбрать</span>';
} else {
statusBadge = `<button type="button" class="btn btn-sm btn-shop" style="font-size: 10px; padding: 2px 6px; margin-top: 3px;" ${canAfford ? '' : 'disabled'}>🔒 ${p.price} 💰</button>`;
}
item.innerHTML = `
<div class="seed-inv-icon">${p.icon}</div>
<div class="seed-inv-name" title="${p.name}">${p.name}</div>
<div class="seed-inv-sun">☀️ ${p.cost}</div>
${statusBadge}
`;
if (isUnlocked) {
item.addEventListener('click', () => {
if (isInDeck) {
this.economy.removeFromDeck(p.id);
} else {
this.economy.addToDeck(p.id);
}
this.renderSeedSelectionUI();
if (window.PvZAudio) window.PvZAudio.playButtonClick();
});
} else {
const buyBtn = item.querySelector('button');
if (buyBtn) {
buyBtn.addEventListener('click', (e) => {
e.stopPropagation();
const res = this.economy.buyPlant(p.id);
if (res.success) {
if (window.PvZAudio) window.PvZAudio.playCoin();
this.saveUserData();
this.updateShopUI();
this.renderSeedSelectionUI();
}
});
}
}
inventoryContainer.appendChild(item);
});
}
}
updateShopUI() {
const elCoins = document.getElementById('shop-coins');
if (elCoins) elCoins.textContent = `💰 Монеты: ${this.economy.totalCoins}`;
if (elCoins) elCoins.textContent = `💰 Монеты: ${this.economy.totalCoins.toLocaleString('ru-RU')}`;
const btnBuySlot = document.getElementById('btn-buy-slot');
if (btnBuySlot) {
@@ -1581,6 +1799,54 @@ window.GameEngine = class GameEngine {
btnBuyRake.textContent = this.economy.upgrades.hasRake ? 'Куплено ✔️' : 'Купить (150 💰)';
btnBuyRake.disabled = this.economy.upgrades.hasRake || this.economy.totalCoins < 150;
}
// Populate Shop Plants Grid
const shopPlantsGrid = document.getElementById('shop-plants-grid');
if (shopPlantsGrid && window.PLANT_CATALOGUE) {
shopPlantsGrid.innerHTML = '';
window.PLANT_CATALOGUE.forEach(p => {
const isBasic = this.economy.defaultUnlocked.includes(p.id);
const isUnlocked = this.economy.isPlantUnlocked(p.id);
const canAfford = this.economy.totalCoins >= (p.price || 0);
const card = document.createElement('div');
card.className = 'shop-plant-card';
let actionHtml = '';
if (isBasic) {
actionHtml = '<span class="badge-purchased" style="background: rgba(33, 150, 243, 0.25); border-color: #64b5f6; color: #90caf9;">Базовое ✅</span>';
} else if (isUnlocked) {
actionHtml = '<span class="badge-purchased">Куплено ✅</span>';
} else {
actionHtml = `<button type="button" class="btn-buy" data-plant-id="${p.id}" ${canAfford ? '' : 'disabled'}>Купить (${p.price} 💰)</button>`;
}
card.innerHTML = `
<div class="shop-plant-icon">${p.icon}</div>
<div class="shop-plant-info">
<h4>${p.name}</h4>
<div class="shop-plant-cost">☀️ ${p.cost} солнца</div>
<p>${p.desc}</p>
</div>
<div>${actionHtml}</div>
`;
const buyBtn = card.querySelector('button.btn-buy');
if (buyBtn) {
buyBtn.addEventListener('click', () => {
const res = this.economy.buyPlant(p.id);
if (res.success) {
if (window.PvZAudio) window.PvZAudio.playCoin();
this.saveUserData();
this.updateShopUI();
this.renderSeedSelectionUI();
}
});
}
shopPlantsGrid.appendChild(card);
});
}
}
updateWorkshopUI() {