MM60

Market Mayhem Companion App
Cash Guy

A Gift from Cash Guy Starts Your Financial Journey! Invest it Well and Good Luck!

Palm Tree Left

Market Dashboard!

Palm Tree Right
Q1 ROLL UP NEXT
Net Worth: $2000

Early Investor Time

Make A Trade Below Or Trigger Next Price Phase

DOGECAR
SPACE

Yearly Chart

Portfolio Icon Portfolio

Dogecar News Bomb Dogecar Die Roll

DOGECAR

$500

Shares: 0

Value: $0

Space News Bomb Space Die Roll

SPACE

$1000

Shares: 0

Value: $0

Cash CASH Cash

$2000

Loan Management

Loan Balance: $0

Total Interest Paid: $0

Paycheck Roll

// JavaScript Code Starts Here // Global Game State const gameState = { cashBalance: 2000, netWorth: 2000, previousNetWorth: 2000, loanBalance: 0, totalInterestPaid: 0, loanCap: 5000, currentQuarter: 1, phase: 'roll', // 'roll' or 'earnings' year: 1, dogecar: { currentPrice: 500, shares: 0, positionValue: 0, tradeAmount: 0, lastIPOPrice: 500, totalProfit: 0, previousPrice: 500, usedNewsBombs: [], usedEarnings: [], lastPriceMovement: '', shortLimit: 5, previousPositionValue: 0, priceHistory: [500], // To track price movements for the chart }, space: { currentPrice: 1000, shares: 0, positionValue: 0, tradeAmount: 0, lastIPOPrice: 1000, totalProfit: 0, previousPrice: 1000, usedNewsBombs: [], usedEarnings: [], lastPriceMovement: '', shortLimit: 3, previousPositionValue: 0, priceHistory: [1000], // To track price movements for the chart }, notifications: [], lastInterestCharged: 0, pricePhases: ['Early Investor'], // To track the x-axis labels for the chart }; // Short Selling Limits function updateShortLimits() { const increment = 5; gameState.dogecar.shortLimit = 5 + increment * (gameState.currentQuarter - 1); gameState.space.shortLimit = 3 + increment * (gameState.currentQuarter - 1); } // Loan Cap function updateLoanCap() { const increment = 5000; gameState.loanCap = 5000 + increment * (gameState.currentQuarter - 1); } // News Bomb Distributions const newsBombs = { dogecar: [400, 300, 200, 100, -100, -200, -300, -400], space: [1000, 400, 300, 200, -200, -300, -400, -1000], }; // Earnings Cards Distributions const earningsCards = { dogecar: [600, 400, 300, 200, -200, -300, -400, -500], space: [700, 400, 300, 200, -200, -300, -400, -600], }; // Utility Functions function updateNetWorth() { const { cashBalance, loanBalance, dogecar, space } = gameState; const totalAssets = dogecar.positionValue + space.positionValue; gameState.netWorth = cashBalance + totalAssets - loanBalance; // Calculate net worth change: positions change minus any loan interest charged let netWorthChange = dogecar.positionValue + space.positionValue - dogecar.previousPositionValue - space.previousPositionValue - gameState.lastInterestCharged; netWorthChange = Math.round(netWorthChange / 100) * 100; // Round to nearest $100 const netWorthChangeElement = document.getElementById('net-worth-change'); if (netWorthChange > 0) { netWorthChangeElement.innerHTML = ` $${netWorthChange}`; } else if (netWorthChange < 0) { netWorthChangeElement.innerHTML = ` $${Math.abs(netWorthChange)}`; } else { netWorthChangeElement.innerHTML = `$0`; } document.getElementById('net-worth').textContent = `Net Worth: $${gameState.netWorth}`; gameState.previousNetWorth = gameState.netWorth; } function showNotification(message, type) { gameState.notifications.push(message); const notification = document.getElementById('notification'); notification.innerHTML = gameState.notifications.join('

'); // Apply specific class based on type notification.className = ''; if (type === 'bond-purchase') { notification.classList.add('notification-bond-purchase'); } else if (type === 'paycheck') { notification.classList.add('notification-paycheck'); } notification.style.display = 'block'; const duration = message.includes('
') ? 4000 : 2000; // Shorter duration setTimeout(() => { notification.style.display = 'none'; gameState.notifications = []; }, duration); // Notifications stay on screen shorter } // Initialize Trade Inputs function initTradeInputs() { const tradeAmounts = []; for (let i = 0; i <= 10; i++) { tradeAmounts.push(i); } for (let i = 10; i <= 100; i += 10) { tradeAmounts.push(i); } ['dogecar', 'space'].forEach((asset) => { const select = document.getElementById(`${asset}-trade`); select.innerHTML = ''; // Clear existing options tradeAmounts.forEach((amount) => { const option = document.createElement('option'); option.value = amount; option.textContent = amount; select.appendChild(option); }); select.value = 0; }); } // Update Asset Information function updateAssetInfo(asset) { document.getElementById(`${asset}-current-price`).textContent = `$${gameState[asset].currentPrice}`; document.getElementById(`${asset}-shares`).textContent = gameState[asset].shares; document.getElementById(`${asset}-position`).textContent = `$${gameState[asset].positionValue}`; document.getElementById(`${asset}-price-change`).innerHTML = gameState[asset].priceChangeText || ''; } // Updating Price Change Indicators function updatePriceChangeIndicators(asset) { const assetData = gameState[asset]; const previousPrice = assetData.previousPrice !== undefined ? assetData.previousPrice : assetData.currentPrice; const priceChange = assetData.currentPrice - previousPrice; // Update previousPrice for next calculation assetData.previousPrice = assetData.currentPrice; // Determine direction and color let arrow = ''; let color = ''; if (priceChange > 0) { arrow = '▲'; color = 'green'; } else if (priceChange < 0) { arrow = '▼'; color = 'red'; } else { arrow = ''; color = 'yellow'; } // Handle same price if (priceChange === 0) { assetData.priceChangeText = `0`; } else { assetData.priceChangeText = `${arrow} $${Math.abs(priceChange)}`; } // Store last price movement for price update area if (priceChange !== 0) { assetData.lastPriceMovement = `${arrow} ${Math.abs(priceChange)}`; } else { assetData.lastPriceMovement = ''; } // Update price history for chart assetData.priceHistory.push(assetData.currentPrice); updateAssetInfo(asset); } // Update Quarter Display function updateQuarterDisplay() { const quarterDisplay = document.getElementById('quarter-display'); const rollEarningsArea = document.getElementById('roll-earnings-area'); if (gameState.phase === 'roll') { quarterDisplay.innerHTML = `Q${gameState.currentQuarter} Roll Up Next`; rollEarningsArea.className = 'roll-town'; rollEarningsArea.innerHTML = `

Roll Town!

It's the Middle of the Quarter

DOGECAR
SPACE
`; } else { quarterDisplay.innerHTML = `Q${gameState.currentQuarter} Earnings Up Next`; rollEarningsArea.className = 'earnings-time'; rollEarningsArea.innerHTML = `

Quarterly Results Out

Companies Report Earnings Results

DOGECAR
SPACE
`; } // Move stock movement calculation to the roll and earnings area ['dogecar', 'space'].forEach((asset) => { const movement = gameState[asset].lastPriceMovement; document.getElementById(`${asset}-price-movement`).innerHTML = movement || ''; }); } // Handle Next Price Phase Button document.getElementById('next-price-phase-btn').addEventListener('click', function () { if (gameState.phase === 'roll') { performRollPhase(); } else if (gameState.phase === 'earnings') { performEarningsPhase(); } }); // Perform Roll Phase function performRollPhase() { clearRollEarningsArea(); document.getElementById('next-price-phase-btn').disabled = true; rollForAsset('dogecar', () => { setTimeout(() => { rollForAsset('space', () => { document.getElementById('next-price-phase-btn').disabled = false; advanceGamePhase(); updateQuarterDisplay(); updatePriceChart(); }); }, 1000); }); } // Perform Earnings Phase function performEarningsPhase() { clearRollEarningsArea(); document.getElementById('next-price-phase-btn').disabled = true; revealEarnings('dogecar', () => { setTimeout(() => { revealEarnings('space', () => { document.getElementById('next-price-phase-btn').disabled = false; advanceGamePhase(); updateQuarterDisplay(); applyLoanInterest(0.10); // 10% interest updateNetWorth(); updatePriceChart(); // Show Paycheck button after Q1 Earnings and after Q2 and Q3 Earnings if (gameState.currentQuarter === 1 || gameState.currentQuarter === 2 || gameState.currentQuarter === 3) { if (gameState.phase === 'earnings') { document.getElementById('paycheck-section').style.display = 'block'; } } else { document.getElementById('paycheck-section').style.display = 'none'; } }); }, 2000); }); } // Roll for a Specific Asset function rollForAsset(asset, callback) { const dieOutcomes = asset === 'dogecar' ? ['News Bomb', 'News Bomb', '▲$200', '▲$100', '▼$100', '▼$200'] : ['News Bomb', 'News Bomb', '▲$300', '▲$200', '▼$200', '▼$300']; const randomIndex = Math.floor(Math.random() * dieOutcomes.length); const outcomeText = dieOutcomes[randomIndex]; if (outcomeText === 'News Bomb') { displayNewsBombDie(asset, () => { setTimeout(() => { triggerNewsBomb(asset, callback); }, 1000); // Display dice for 1 second }); } else { const priceChange = parseInt(outcomeText.replace('▲$', '').replace('▼$', '')) * (outcomeText.includes('▼') ? -1 : 1); gameState[asset].currentPrice += priceChange; if (gameState[asset].currentPrice < 0) gameState[asset].currentPrice = 0; gameState[asset].previousPositionValue = gameState[asset].positionValue; gameState[asset].positionValue = gameState[asset].currentPrice * gameState[asset].shares; updateAssetInfo(asset); updatePriceChangeIndicators(asset); displayRollResult(asset, outcomeText); if (gameState[asset].currentPrice === 0) { handleStockZero(asset); } handleStockSplit(asset); updateNetWorth(); callback(); } } // Display News Bomb Die function displayNewsBombDie(asset, callback) { const dieImg = document.getElementById(`${asset}-die`); dieImg.src = `https://cdn.shopify.com/s/files/1/0845/5110/6858/files/${capitalize(asset)}_Die_News_Bomb.png?v=1730005575`; dieImg.style.display = 'block'; dieImg.style.filter = 'drop-shadow(0 0 10px #fff)'; const newsBombImg = document.getElementById(`${asset}-newsbomb`); newsBombImg.style.display = 'none'; // Hide news bomb image during die display setTimeout(() => { dieImg.src = ''; dieImg.style.display = 'none'; callback(); }, 1000); // Display die for 1 second } // Trigger News Bomb function triggerNewsBomb(asset, callback) { const newsValue = getRandomNewsValue(asset); const upDown = newsValue > 0 ? 'Up' : 'Down'; const absValue = Math.abs(newsValue); const newsBombImgUrl = `https://cdn.shopify.com/s/files/1/0845/5110/6858/files/${capitalize(asset)}_News_${upDown}_${absValue}.png?v=1729904195`; displayNewsBombResult(asset, newsBombImgUrl); gameState[asset].currentPrice += newsValue; if (gameState[asset].currentPrice < 0) gameState[asset].currentPrice = 0; gameState[asset].previousPositionValue = gameState[asset].positionValue; gameState[asset].positionValue = gameState[asset].currentPrice * gameState[asset].shares; updateAssetInfo(asset); updatePriceChangeIndicators(asset); if (gameState[asset].currentPrice === 0) { handleStockZero(asset); } handleStockSplit(asset); updateNetWorth(); callback(); } // Get Random News Value function getRandomNewsValue(asset) { const availableNewsBombs = newsBombs[asset].filter(value => !gameState[asset].usedNewsBombs.includes(value)); if (availableNewsBombs.length === 0) { gameState[asset].usedNewsBombs = []; return getRandomNewsValue(asset); } const randomIndex = Math.floor(Math.random() * availableNewsBombs.length); const newsValue = availableNewsBombs[randomIndex]; gameState[asset].usedNewsBombs.push(newsValue); return newsValue; } // Display News Bomb Result function displayNewsBombResult(asset, newsBombImgUrl) { const newsBombImg = document.getElementById(`${asset}-newsbomb`); newsBombImg.src = newsBombImgUrl; newsBombImg.style.display = 'block'; const dieImg = document.getElementById(`${asset}-die`); dieImg.src = ''; dieImg.style.display = 'none'; newsBombImg.style.filter = 'drop-shadow(0 0 10px #fff)'; } // Display Roll Result function displayRollResult(asset, outcome) { const newsBombImg = document.getElementById(`${asset}-newsbomb`); newsBombImg.src = ''; newsBombImg.style.display = 'none'; const dieImg = document.getElementById(`${asset}-die`); const [symbol, value] = outcome.split('$'); const upDown = symbol === '▲' ? 'Up' : 'Down'; const imgUrl = `https://cdn.shopify.com/s/files/1/0845/5110/6858/files/${capitalize(asset)}_${upDown}_${value}.png?v=1730005575`; dieImg.src = imgUrl; dieImg.style.display = 'block'; dieImg.style.filter = 'drop-shadow(0 0 10px #fff)'; setTimeout(() => { dieImg.src = ''; dieImg.style.display = 'none'; }, 1000); // Hide die after 1 second } // Capitalize Word function capitalize(word) { return word.charAt(0).toUpperCase() + word.slice(1); } // Handle Stock Going to $0 function handleStockZero(asset) { const lostShares = gameState[asset].shares; const hadShares = lostShares !== 0; // Player loses all shares and short shares gameState[asset].shares = 0; gameState[asset].positionValue = 0; // Decrease IPO price by $100 gameState[asset].lastIPOPrice -= 100; if (gameState[asset].lastIPOPrice < 100) { gameState[asset].lastIPOPrice = 100; // Minimum IPO price } gameState[asset].currentPrice = gameState[asset].lastIPOPrice; gameState[asset].previousPrice = gameState[asset].currentPrice; updateAssetInfo(asset); updatePriceChangeIndicators(asset); if (hadShares) { // Notification with Jazzy Dimecat showNotification(`Jazzy Dimecat
You lost ${lostShares} shares of ${capitalize(asset)}! Jazzy Dimecat has re-IPO'd ${capitalize(asset)} at $${gameState[asset].currentPrice}.`, 'stock-zero'); } else { // Notification without losing shares showNotification(`${capitalize(asset)} has been re-IPO'd at $${gameState[asset].currentPrice}.`, 'stock-zero'); } } // Handle Stock Splits function handleStockSplit(asset) { const splitPrice = asset === 'dogecar' ? 1000 : asset === 'space' ? 2000 : Infinity; if (gameState[asset].currentPrice >= splitPrice) { let dividendPerShare = 0; const preSplitPrice = gameState[asset].currentPrice; // Check if price ends with odd $100 const hundredsPlace = Math.floor(preSplitPrice / 100) % 10; if (hundredsPlace % 2 === 1) { dividendPerShare = 100; } // Pay dividend if (dividendPerShare > 0) { const totalDividend = dividendPerShare * gameState[asset].shares; const totalShortDividend = dividendPerShare * Math.abs(gameState[asset].shares < 0 ? gameState[asset].shares : 0); gameState.cashBalance += totalDividend; gameState.cashBalance -= totalShortDividend; // Notification for dividend showNotification(`Dividend
You received a dividend of $${totalDividend} from ${capitalize(asset)}.`, 'dividend'); if (totalShortDividend > 0) { showNotification(`Dividend
You paid a dividend of $${totalShortDividend} on your short position of ${capitalize(asset)}.`, 'dividend'); } } // Double shares gameState[asset].shares *= 2; // Adjust price: divide by 2 and round down to nearest $100 let newPrice = Math.floor(preSplitPrice / 2 / 100) * 100; gameState[asset].currentPrice = newPrice; gameState[asset].positionValue = gameState[asset].shares * gameState[asset].currentPrice; updateAssetInfo(asset); updatePriceChangeIndicators(asset); // Notification with image showNotification(`Elron Husk
${capitalize(asset)} has split! Your shares have doubled and price halved.`, 'stock-split'); } } // Reveal Earnings for a Specific Asset function revealEarnings(asset, callback) { const earningsValue = getRandomEarningsValue(asset); const upDown = earningsValue > 0 ? 'Up' : 'Down'; const absValue = Math.abs(earningsValue); const earningsImgUrl = `https://cdn.shopify.com/s/files/1/0845/5110/6858/files/${capitalize(asset)}_Earnings_${upDown}_${absValue}.png?v=1730005575`; displayEarningsResult(asset, earningsImgUrl); // Adjust price gameState[asset].currentPrice += earningsValue; if (gameState[asset].currentPrice < 0) gameState[asset].currentPrice = 0; // Update position value gameState[asset].previousPositionValue = gameState[asset].positionValue; gameState[asset].positionValue = gameState[asset].currentPrice * gameState[asset].shares; updateAssetInfo(asset); updatePriceChangeIndicators(asset); // Handle stock going to $0 if (gameState[asset].currentPrice === 0) { handleStockZero(asset); } // Handle stock splits handleStockSplit(asset); updateNetWorth(); callback(); } // Get Random Earnings Value function getRandomEarningsValue(asset) { const availableEarnings = earningsCards[asset].filter(value => !gameState[asset].usedEarnings.includes(value)); if (availableEarnings.length === 0) { gameState[asset].usedEarnings = []; return getRandomEarningsValue(asset); } const randomIndex = Math.floor(Math.random() * availableEarnings.length); const earningsValue = availableEarnings[randomIndex]; gameState[asset].usedEarnings.push(earningsValue); return earningsValue; } // Display Earnings Result function displayEarningsResult(asset, earningsImgUrl) { const newsBombImg = document.getElementById(`${asset}-newsbomb`); newsBombImg.src = earningsImgUrl; newsBombImg.style.display = 'block'; const dieImg = document.getElementById(`${asset}-die`); dieImg.src = ''; dieImg.style.display = 'none'; newsBombImg.style.filter = 'drop-shadow(0 0 10px #fff)'; } // Clear Roll and Earnings Area function clearRollEarningsArea() { ['dogecar', 'space'].forEach(asset => { const newsBombImg = document.getElementById(`${asset}-newsbomb`); newsBombImg.src = ''; newsBombImg.style.display = 'none'; const dieImg = document.getElementById(`${asset}-die`); dieImg.src = ''; dieImg.style.display = 'none'; }); } // Advance Game Phase function advanceGamePhase() { if (gameState.phase === 'roll') { gameState.phase = 'earnings'; // Add phase label for the chart gameState.pricePhases.push(`Q${gameState.currentQuarter} Roll`); } else { gameState.phase = 'roll'; gameState.currentQuarter += 1; updateShortLimits(); updateLoanCap(); // Add phase label for the chart gameState.pricePhases.push(`Q${gameState.currentQuarter - 1} Earnings`); } // Show Paycheck button after Q1 Earnings and after Q2 and Q3 Earnings if (gameState.currentQuarter === 1 || gameState.currentQuarter === 2 || gameState.currentQuarter === 3) { if (gameState.phase === 'earnings') { document.getElementById('paycheck-section').style.display = 'block'; } } else { document.getElementById('paycheck-section').style.display = 'none'; } } // Handle Trade Buttons document.querySelectorAll('.buy-button').forEach((button) => { button.addEventListener('click', function () { const asset = this.dataset.asset; const tradeAmount = parseInt(document.getElementById(`${asset}-trade`).value); executeTrade(asset, tradeAmount); }); }); document.querySelectorAll('.sell-button').forEach((button) => { button.addEventListener('click', function () { const asset = this.dataset.asset; const tradeAmount = parseInt(document.getElementById(`${asset}-trade`).value); executeTrade(asset, -tradeAmount); }); }); // All In and Cash Out Buttons document.querySelectorAll('.all-in-button').forEach(button => { button.addEventListener('click', function () { const asset = this.dataset.asset; // Buy maximum shares of the selected asset buyMaxShares(asset); }); }); document.querySelectorAll('.cash-out-button').forEach(button => { button.addEventListener('click', function () { const asset = this.dataset.asset; sellAllShares(asset); }); }); // Max Buy and Max Sell Buttons document.querySelectorAll('.max-buy-button').forEach(button => { button.addEventListener('click', function () { const asset = this.dataset.asset; buyMaxShares(asset); }); }); document.querySelectorAll('.max-sell-button').forEach(button => { button.addEventListener('click', function () { const asset = this.dataset.asset; sellMaxShares(asset); }); }); // Sell Max Shares Function function sellMaxShares(asset) { const assetData = gameState[asset]; const currentShares = gameState[asset].shares; const maxShort = gameState[asset].shortLimit; let sellAmount = 0; if (currentShares > 0) { sellAmount = currentShares; } else if (currentShares < 0) { sellAmount = Math.abs(currentShares); } const proceeds = sellAmount * gameState[asset].currentPrice; gameState.cashBalance += proceeds; gameState[asset].shares += sellAmount > 0 ? -sellAmount : sellAmount; gameState[asset].positionValue = gameState[asset].shares * gameState[asset].currentPrice; updateAssetInfo(asset); document.getElementById('cash-balance').textContent = `$${gameState.cashBalance}`; updateNetWorth(); } // Sell All Shares Function function sellAllShares(asset) { const assetData = gameState[asset]; const sellAmount = assetData.shares; if (sellAmount === 0) { showNotification(`No shares to sell for ${capitalize(asset)}.`, 'trade-error'); return; } const proceeds = sellAmount * gameState[asset].currentPrice; gameState.cashBalance += proceeds; gameState[asset].shares = 0; gameState[asset].positionValue = 0; updateAssetInfo(asset); document.getElementById('cash-balance').textContent = `$${gameState.cashBalance}`; updateNetWorth(); } // Buy Max Shares Function function buyMaxShares(asset) { const assetData = gameState[asset]; const maxShares = Math.floor(gameState.cashBalance / assetData.currentPrice); if (maxShares > 0) { gameState[asset].shares += maxShares; gameState.cashBalance -= maxShares * assetData.currentPrice; gameState[asset].positionValue = gameState[asset].shares * gameState[asset].currentPrice; updateAssetInfo(asset); document.getElementById('cash-balance').textContent = `$${gameState.cashBalance}`; updateNetWorth(); } else { showNotification('Insufficient cash to buy any shares.', 'trade-error'); } } // Execute Trade Function with Short Selling function executeTrade(asset, amount) { const assetData = gameState[asset]; const cost = assetData.currentPrice * amount; // Check if the player has enough cash (only if buying) if (amount > 0 && gameState.cashBalance - cost < -gameState.loanBalance) { // Allow negative cash only due to loan interest showNotification('Insufficient cash to make this purchase.', 'trade-error'); return; } // Check short selling limits const potentialShares = assetData.shares + amount; if (potentialShares < -gameState[asset].shortLimit) { showNotification(`You cannot short more than ${gameState[asset].shortLimit} shares of ${capitalize(asset)}.`, 'trade-error'); return; } // Update shares and cash balance gameState[asset].shares += amount; gameState.cashBalance -= cost; // Update position value gameState[asset].positionValue = gameState[asset].shares * gameState[asset].currentPrice; // Update UI updateAssetInfo(asset); document.getElementById('cash-balance').textContent = `$${gameState.cashBalance}`; updateNetWorth(); } // Loan Management function applyLoanInterest(rate) { if (gameState.loanBalance > 0) { const interest = Math.round(gameState.loanBalance * rate); gameState.totalInterestPaid += interest; gameState.loanBalance += interest; gameState.cashBalance -= interest; // Deduct directly from cash gameState.lastInterestCharged = interest; document.getElementById('loan-balance').textContent = gameState.loanBalance; document.getElementById('cash-balance').textContent = `$${gameState.cashBalance}`; document.getElementById('total-interest-paid').textContent = `Total Interest Paid: $${gameState.totalInterestPaid}`; updateNetWorth(); updateCashBalance(); } else { gameState.lastInterestCharged = 0; } } document.getElementById('take-loan').addEventListener('click', function () { const loanAmount = parseInt(document.getElementById('loan-amount').value); if (isNaN(loanAmount) || loanAmount < 1000 || loanAmount > 20000) { showNotification(`Please enter a valid loan amount between $1000 and $20000.`, 'loan-error'); return; } if (gameState.loanBalance + loanAmount > gameState.loanCap) { showNotification(`Loan Dog
You cannot take out more than your loan cap of $${gameState.loanCap}.`, 'loan-error'); return; } gameState.loanBalance += loanAmount; gameState.cashBalance += loanAmount; document.getElementById('loan-balance').textContent = gameState.loanBalance; document.getElementById('cash-balance').textContent = `$${gameState.cashBalance}`; updateNetWorth(); showNotification(`You have taken a loan of $${loanAmount}.`, 'loan'); }); document.getElementById('repay-loan').addEventListener('click', function () { const repayAmount = parseInt(document.getElementById('loan-amount').value); if (isNaN(repayAmount) || repayAmount < 1000 || repayAmount > 20000) { showNotification(`Please enter a valid repayment amount between $1000 and $20000.`, 'loan-error'); return; } if (repayAmount > gameState.loanBalance) { showNotification(`You cannot repay more than your loan balance.`, 'loan-error'); return; } if (repayAmount > gameState.cashBalance) { showNotification(`Insufficient cash to repay the loan.`, 'loan-error'); return; } gameState.loanBalance -= repayAmount; gameState.cashBalance -= repayAmount; document.getElementById('loan-balance').textContent = gameState.loanBalance; document.getElementById('cash-balance').textContent = `$${gameState.cashBalance}`; updateNetWorth(); showNotification(`You have repaid $${repayAmount} of your loan.`, 'loan'); }); // Max Loan and Max Pay Buttons document.getElementById('max-loan').addEventListener('click', function () { const availableLoan = gameState.loanCap - gameState.loanBalance; if (availableLoan <= 0) { showNotification(`Loan Dog
You have reached your loan cap of $${gameState.loanCap}.`, 'loan-error'); return; } gameState.loanBalance += availableLoan; gameState.cashBalance += availableLoan; document.getElementById('loan-balance').textContent = gameState.loanBalance; document.getElementById('cash-balance').textContent = `$${gameState.cashBalance}`; updateNetWorth(); showNotification(`You have taken a loan of $${availableLoan}.`, 'loan'); }); document.getElementById('max-pay').addEventListener('click', function () { const repayAmount = Math.min(gameState.loanBalance, gameState.cashBalance); if (repayAmount <= 0) { showNotification(`No loan to repay or insufficient cash.`, 'loan-error'); return; } gameState.loanBalance -= repayAmount; gameState.cashBalance -= repayAmount; document.getElementById('loan-balance').textContent = gameState.loanBalance; document.getElementById('cash-balance').textContent = `$${gameState.cashBalance}`; updateNetWorth(); showNotification(`You have repaid $${repayAmount} of your loan.`, 'loan'); }); // Paycheck Roll Section document.getElementById('roll-paycheck-btn').addEventListener('click', function () { let multiplier = 1; let rollOutcome = rollPaycheckDie(); while (rollOutcome === '2X') { multiplier *= 2; showNotification(`You rolled 2X! Your multiplier is now ${multiplier}X. Rolling again...`, 'paycheck'); rollOutcome = rollPaycheckDie(); } let paycheckAmount = parseInt(rollOutcome) * multiplier; gameState.cashBalance += paycheckAmount; document.getElementById('cash-balance').textContent = `$${gameState.cashBalance}`; showNotification(`Paycheck
You received a paycheck of $${paycheckAmount}!`, 'paycheck'); updateNetWorth(); // Disable the button after use this.disabled = true; }); function rollPaycheckDie() { const outcomes = ['400', '500', '600', '700', '800', '900', '1000', '2X']; const randomIndex = Math.floor(Math.random() * outcomes.length); return outcomes[randomIndex]; } // Event Listeners for Initial Screens document.getElementById('start-game-btn').addEventListener('click', function () { document.getElementById('splash-screen').style.opacity = '0'; setTimeout(function () { document.getElementById('splash-screen').style.display = 'none'; showWelcomeScreen(); }, 500); }); function showWelcomeScreen() { document.getElementById('welcome-screen').style.display = 'flex'; setTimeout(function () { document.getElementById('speech-bubble').style.transform = 'scale(1)'; }, 1000); } document.getElementById('collect-button').addEventListener('click', function () { document.getElementById('welcome-screen').style.opacity = '0'; setTimeout(function () { document.getElementById('welcome-screen').style.display = 'none'; showGameDashboard(); }, 500); }); function showGameDashboard() { document.getElementById('game-dashboard').style.display = 'flex'; initGame(); } // Initialize the game function initGame() { initTradeInputs(); updateShortLimits(); updateLoanCap(); ['dogecar', 'space'].forEach((asset) => { gameState[asset].previousPositionValue = gameState[asset].positionValue; updateAssetInfo(asset); updatePriceChangeIndicators(asset); }); document.getElementById('cash-balance').textContent = `$${gameState.cashBalance}`; updateQuarterDisplay(); document.getElementById('loan-balance').textContent = gameState.loanBalance; document.getElementById('total-interest-paid').textContent = `Total Interest Paid: $${gameState.totalInterestPaid}`; // Hide paycheck section initially; it will be shown after Q1 Earnings document.getElementById('paycheck-section').style.display = 'none'; // Initialize Price Chart initPriceChart(); } // Initialize Price Chart let priceChart; function initPriceChart() { const ctx = document.getElementById('price-chart').getContext('2d'); priceChart = new Chart(ctx, { type: 'line', data: { labels: gameState.pricePhases, datasets: [ { label: 'Dogecar', data: gameState.dogecar.priceHistory, borderColor: '#ffa9c9', backgroundColor: 'rgba(255, 169, 201, 0.2)', borderWidth: 3, pointRadius: 5, pointBackgroundColor: '#ffa9c9', tension: 0, // Straight lines }, { label: 'Space', data: gameState.space.priceHistory, borderColor: '#bb66d0', backgroundColor: 'rgba(187, 102, 208, 0.2)', borderWidth: 3, pointRadius: 5, pointBackgroundColor: '#bb66d0', tension: 0, // Straight lines }, ], }, options: { responsive: true, maintainAspectRatio: false, scales: { y: { suggestedMin: 0, suggestedMax: 2000, ticks: { stepSize: 200, color: '#fff', }, grid: { color: '#444', }, }, x: { ticks: { color: '#fff', }, grid: { color: '#444', }, }, }, plugins: { legend: { labels: { color: '#fff', }, }, }, }, }); } // Update Price Chart function updatePriceChart() { priceChart.data.labels = gameState.pricePhases; priceChart.data.datasets[0].data = gameState.dogecar.priceHistory; priceChart.data.datasets[1].data = gameState.space.priceHistory; priceChart.update(); } // Include Chart.js library const script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/chart.js'; document.head.appendChild(script); script.onload = function () { initPriceChart(); }; // Ensure the paycheck button appears only after Q1 and Q2 Earnings function checkPaycheckAvailability() { if (gameState.currentQuarter === 1 || gameState.currentQuarter === 2 || gameState.currentQuarter === 3) { document.getElementById('paycheck-section').style.display = 'block'; } else { document.getElementById('paycheck-section').style.display = 'none'; } } // Ensure that loan interest is deducted correctly and cash can go negative only due to loan interest function updateCashBalance() { // This function can be used to ensure cash balance doesn't go negative except for loan interest if (gameState.cashBalance < 0 && gameState.loanBalance <= 0) { gameState.cashBalance = 0; showNotification('Your cash balance cannot go negative.', 'trade-error'); } } // Handle Cash Balance Corrections function handleCashBalance() { if (gameState.cashBalance < 0) { // Only allow negative cash if it's due to loan interest if (gameState.loanBalance > 0) { // Already handled in applyLoanInterest } else { gameState.cashBalance = 0; showNotification('Your cash balance cannot go negative.', 'trade-error'); } } } // Final Cash Balance Correction after net worth update function updateNetWorth() { const { cashBalance, loanBalance, dogecar, space } = gameState; const totalAssets = dogecar.positionValue + space.positionValue; gameState.netWorth = cashBalance + totalAssets - loanBalance; let netWorthChange = dogecar.positionValue + space.positionValue - dogecar.previousPositionValue - space.previousPositionValue - gameState.lastInterestCharged; netWorthChange = Math.round(netWorthChange / 100) * 100; // Round to nearest $100 const netWorthChangeElement = document.getElementById('net-worth-change'); if (netWorthChange > 0) { netWorthChangeElement.innerHTML = ` $${netWorthChange}`; } else if (netWorthChange < 0) { netWorthChangeElement.innerHTML = ` $${Math.abs(netWorthChange)}`; } else { netWorthChangeElement.innerHTML = `$0`; } document.getElementById('net-worth').textContent = `Net Worth: $${gameState.netWorth}`; gameState.previousNetWorth = gameState.netWorth; handleCashBalance(); }