feat(gameplay): add crazy dave casino and bank, and zombie mutation lab
This commit is contained in:
@@ -0,0 +1,261 @@
|
||||
// Crazy Dave's Casino & Bank System (Slot Machine & Bank Deposits/Loans)
|
||||
window.CasinoSystem = class CasinoSystem {
|
||||
constructor(economy) {
|
||||
this.economy = economy;
|
||||
|
||||
// Slot machine symbols
|
||||
this.symbols = [
|
||||
{ id: 'sunflower', icon: '🌻', name: 'Подсолнух', weight: 30, mult: 4 },
|
||||
{ id: 'pea', icon: '🟢', name: 'Горох', weight: 25, mult: 8 },
|
||||
{ id: 'wallnut', icon: '🌰', name: 'Орех', weight: 20, mult: 12 },
|
||||
{ id: 'cherry', icon: '🍒', name: 'Вишня', weight: 12, mult: 25 },
|
||||
{ id: 'diamond', icon: '💎', name: 'Алмаз', weight: 6, mult: 60, crystals: 2 },
|
||||
{ id: 'jackpot7', icon: '7️⃣', name: 'Семёрка', weight: 3, mult: 200, crystals: 5 },
|
||||
{ id: 'brains', icon: '🧠', name: 'Мозги', weight: 4, mult: 0, isZombie: true }
|
||||
];
|
||||
|
||||
// Bank State
|
||||
this.deposit = 0;
|
||||
this.depositStartTime = 0;
|
||||
this.interestRatePerHour = 0.05; // 5% per hour (up to 48 hours max)
|
||||
this.loan = 0;
|
||||
this.loanLimit = 2500;
|
||||
this.loanInterest = 0.10; // 10% fee
|
||||
|
||||
// Statistics
|
||||
this.spinsTotal = 0;
|
||||
this.totalWon = 0;
|
||||
this.jackpotsWon = 0;
|
||||
|
||||
this.isSpinning = false;
|
||||
this.currentBet = 50;
|
||||
this.lastReels = ['🌻', '🟢', '🌰'];
|
||||
|
||||
this.loadFromAuth();
|
||||
}
|
||||
|
||||
loadFromAuth() {
|
||||
if (window.PvZAuth) {
|
||||
const uData = window.PvZAuth.getUserData();
|
||||
if (uData.casino) {
|
||||
const c = uData.casino;
|
||||
this.deposit = Number(c.deposit) || 0;
|
||||
this.depositStartTime = Number(c.depositStartTime) || 0;
|
||||
this.loan = Number(c.loan) || 0;
|
||||
this.spinsTotal = Number(c.spinsTotal) || 0;
|
||||
this.totalWon = Number(c.totalWon) || 0;
|
||||
this.jackpotsWon = Number(c.jackpotsWon) || 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
saveToAuth() {
|
||||
if (window.PvZAuth && !window.PvZAuth.isGuest()) {
|
||||
window.PvZAuth.saveUserData({
|
||||
casino: {
|
||||
deposit: this.deposit,
|
||||
depositStartTime: this.depositStartTime,
|
||||
loan: this.loan,
|
||||
spinsTotal: this.spinsTotal,
|
||||
totalWon: this.totalWon,
|
||||
jackpotsWon: this.jackpotsWon
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Get random symbol based on weights
|
||||
getRandomSymbol() {
|
||||
const totalWeight = this.symbols.reduce((sum, s) => sum + s.weight, 0);
|
||||
let rand = Math.random() * totalWeight;
|
||||
for (const s of this.symbols) {
|
||||
if (rand < s.weight) return s;
|
||||
rand -= s.weight;
|
||||
}
|
||||
return this.symbols[0];
|
||||
}
|
||||
|
||||
// Spin the reels
|
||||
spin(bet = this.currentBet) {
|
||||
if (this.isSpinning) return { error: 'Уже крутится!' };
|
||||
if (!this.economy || this.economy.totalCoins < bet) {
|
||||
return { error: 'Недостаточно монет для ставки!' };
|
||||
}
|
||||
|
||||
// Deduct bet
|
||||
this.economy.totalCoins -= bet;
|
||||
this.economy.saveProfileData();
|
||||
this.spinsTotal++;
|
||||
this.isSpinning = true;
|
||||
|
||||
// Pick 3 symbols
|
||||
const s1 = this.getRandomSymbol();
|
||||
const s2 = this.getRandomSymbol();
|
||||
const s3 = this.getRandomSymbol();
|
||||
this.lastReels = [s1.icon, s2.icon, s3.icon];
|
||||
|
||||
let payout = 0;
|
||||
let crystalsAwarded = 0;
|
||||
let message = 'Повезет в следующий раз!';
|
||||
let isJackpot = false;
|
||||
|
||||
// Check matches
|
||||
if (s1.id === s2.id && s2.id === s3.id) {
|
||||
// 3 of a kind
|
||||
if (s1.id === 'jackpot7') {
|
||||
payout = bet * s1.mult;
|
||||
crystalsAwarded = s1.crystals || 5;
|
||||
isJackpot = true;
|
||||
this.jackpotsWon++;
|
||||
message = `🔥 МЕГА ДЖЕКПОТ! +${payout} монет и +${crystalsAwarded} алмазов!`;
|
||||
} else if (s1.id === 'brains') {
|
||||
payout = 0;
|
||||
message = '🧟 АТАКА ЗОМБИ! Все барабаны съедены!';
|
||||
} else {
|
||||
payout = bet * s1.mult;
|
||||
if (s1.crystals) crystalsAwarded = s1.crystals;
|
||||
message = `🎉 СУПЕР ВЫИГРЫШ: 3x ${s1.name}! +${payout} монет!`;
|
||||
}
|
||||
} else if (s1.id === s2.id || s2.id === s3.id || s1.id === s3.id) {
|
||||
// 2 of a kind
|
||||
const matched = (s1.id === s2.id) ? s1 : (s2.id === s3.id ? s2 : s1);
|
||||
if (!matched.isZombie) {
|
||||
payout = Math.round(bet * Math.max(1.5, matched.mult * 0.35));
|
||||
message = `✨ Выигрыш: пара ${matched.icon}! +${payout} монет`;
|
||||
}
|
||||
}
|
||||
|
||||
if (payout > 0) {
|
||||
this.economy.totalCoins += payout;
|
||||
this.totalWon += payout;
|
||||
this.economy.saveProfileData();
|
||||
if (crystalsAwarded > 0 && this.economy.engine && this.economy.engine.workshop) {
|
||||
this.economy.engine.workshop.addMaterial('crystals', crystalsAwarded);
|
||||
}
|
||||
if (window.PvZAudio) {
|
||||
if (isJackpot) window.PvZAudio.playWin();
|
||||
else window.PvZAudio.playCoin();
|
||||
}
|
||||
}
|
||||
|
||||
this.saveToAuth();
|
||||
|
||||
return {
|
||||
reels: [s1, s2, s3],
|
||||
icons: [s1.icon, s2.icon, s3.icon],
|
||||
payout,
|
||||
crystals: crystalsAwarded,
|
||||
isJackpot,
|
||||
message,
|
||||
coinsLeft: this.economy.totalCoins
|
||||
};
|
||||
}
|
||||
|
||||
// Calculate accrued interest on deposit
|
||||
getAccruedInterest() {
|
||||
if (this.deposit <= 0 || !this.depositStartTime) return 0;
|
||||
const elapsedMs = Date.now() - this.depositStartTime;
|
||||
const hours = Math.min(elapsedMs / (1000 * 3600), 48); // max 48h cap
|
||||
return Math.floor(this.deposit * (this.interestRatePerHour * hours));
|
||||
}
|
||||
|
||||
// Deposit coins into bank
|
||||
depositCoins(amount) {
|
||||
amount = Math.floor(amount);
|
||||
if (amount <= 0) return { success: false, message: 'Некорректная сумма' };
|
||||
if (!this.economy || this.economy.totalCoins < amount) {
|
||||
return { success: false, message: 'Недостаточно монет на руках' };
|
||||
}
|
||||
|
||||
// Collect any prior accrued interest
|
||||
const interest = this.getAccruedInterest();
|
||||
const priorDeposit = this.deposit;
|
||||
|
||||
this.economy.totalCoins -= amount;
|
||||
this.deposit = priorDeposit + interest + amount;
|
||||
this.depositStartTime = Date.now();
|
||||
this.economy.saveProfileData();
|
||||
this.saveToAuth();
|
||||
|
||||
if (window.PvZAudio) window.PvZAudio.playCoin();
|
||||
return {
|
||||
success: true,
|
||||
deposit: this.deposit,
|
||||
addedInterest: interest,
|
||||
coinsLeft: this.economy.totalCoins,
|
||||
message: `Успешно внесено ${amount} монет! (Начислено %: +${interest})`
|
||||
};
|
||||
}
|
||||
|
||||
// Withdraw full deposit + interest
|
||||
withdrawDeposit() {
|
||||
if (this.deposit <= 0) return { success: false, message: 'В банке нет вклада' };
|
||||
|
||||
const interest = this.getAccruedInterest();
|
||||
const totalPayout = this.deposit + interest;
|
||||
|
||||
this.economy.totalCoins += totalPayout;
|
||||
this.deposit = 0;
|
||||
this.depositStartTime = 0;
|
||||
this.economy.saveProfileData();
|
||||
this.saveToAuth();
|
||||
|
||||
if (window.PvZAudio) window.PvZAudio.playWin();
|
||||
return {
|
||||
success: true,
|
||||
payout: totalPayout,
|
||||
interest,
|
||||
coinsLeft: this.economy.totalCoins,
|
||||
message: `Снято из банка: ${totalPayout} монет (включая % доход: +${interest})`
|
||||
};
|
||||
}
|
||||
|
||||
// Take a loan from Crazy Dave
|
||||
takeLoan(amount) {
|
||||
amount = Math.floor(amount);
|
||||
if (this.loan > 0) {
|
||||
return { success: false, message: `У вас уже есть непогашенный кредит: ${this.loan} монет!` };
|
||||
}
|
||||
if (amount <= 0 || amount > this.loanLimit) {
|
||||
return { success: false, message: `Лимит кредита: от 100 до ${this.loanLimit} монет` };
|
||||
}
|
||||
|
||||
const totalDebt = Math.round(amount * (1 + this.loanInterest));
|
||||
this.loan = totalDebt;
|
||||
this.economy.totalCoins += amount;
|
||||
this.economy.saveProfileData();
|
||||
this.saveToAuth();
|
||||
|
||||
if (window.PvZAudio) window.PvZAudio.playCoin();
|
||||
return {
|
||||
success: true,
|
||||
amount,
|
||||
debt: totalDebt,
|
||||
coinsLeft: this.economy.totalCoins,
|
||||
message: `Кредит выдан! Получено +${amount} монет. К возврату: ${totalDebt} монет.`
|
||||
};
|
||||
}
|
||||
|
||||
// Repay part or all loan
|
||||
repayLoan(amount) {
|
||||
if (this.loan <= 0) return { success: false, message: 'У вас нет долгов!' };
|
||||
amount = Math.min(this.loan, Math.floor(amount));
|
||||
if (amount <= 0 || this.economy.totalCoins < amount) {
|
||||
return { success: false, message: 'Недостаточно монет для погашения' };
|
||||
}
|
||||
|
||||
this.economy.totalCoins -= amount;
|
||||
this.loan -= amount;
|
||||
this.economy.saveProfileData();
|
||||
this.saveToAuth();
|
||||
|
||||
if (window.PvZAudio) window.PvZAudio.playCoin();
|
||||
return {
|
||||
success: true,
|
||||
paid: amount,
|
||||
debtLeft: this.loan,
|
||||
coinsLeft: this.economy.totalCoins,
|
||||
message: this.loan === 0 ? '🎉 КРЕДИТ ПОЛНОСТЬЮ ПОГАШЕН!' : `Погашено ${amount} монет. Остаток долга: ${this.loan}`
|
||||
};
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user