financefalconai / script.js
Ram2005's picture
User
9679277 verified
// --- GAME DATA & CONFIGURATION ---
const PERSONAS = {
farmer: {
id: 'farmer',
name: 'Ramesh',
age: 38,
role: 'Cotton Farmer',
location: 'Vidarbha, Maharashtra',
description: 'You own 3 acres. Rain is your boss. One bad season can destroy 10 years of work.',
img: 'http://static.photos/agriculture/640x360/12',
stats: { income: 4000, stability: 30, safety: 20, debt: 20, scamRisk: 40, wealth: 10 },
startMoney: 15000,
difficulty: 'Hard',
special: 'monsoon_risk'
},
woman: {
id: 'woman',
name: 'Sunita',
age: 32,
role: 'Tailoring & Home Manager',
location: 'Ranchi, Jharkhand',
description: 'You run a sewing business and manage a home of 5. Money mixes too easily. Can you keep them separate?',
img: 'http://static.photos/people/640x360/45',
stats: { income: 12000, stability: 50, safety: 40, debt: 10, scamRisk: 30, wealth: 20 },
startMoney: 8000,
businessStartCash: 5000,
difficulty: 'Medium',
special: 'separation_wallets' // Enables dual wallet UI
},
student: {
id: 'student',
name: 'Aman',
age: 19,
role: 'Engineering Student',
location: 'Kota, Rajasthan',
description: 'Your parents send money. Your friends spend money. FOMO is your enemy. Today habits build your future.',
img: 'http://static.photos/education/640x360/88',
stats: { income: 5000, stability: 80, safety: 10, debt: 0, scamRisk: 60, wealth: 5 },
startMoney: 2000,
difficulty: 'Easy',
special: 'impulse_control'
},
young_adult: {
id: 'young_adult',
name: 'Rohit',
age: 24,
role: 'IT Support',
location: 'Bangalore, Karnataka',
description: 'You got a credit card. You got a salary. You have no idea how tax works. Welcome to the golden handcuffs.',
img: 'http://static.photos/technology/640x360/33',
stats: { income: 35000, stability: 60, safety: 30, debt: 30, scamRisk: 50, wealth: 15 },
startMoney: 5000,
difficulty: 'Medium',
special: 'lifestyle_inflation'
}
};
const SCENARIOS = {
common: [
{
id: 'c1',
text: "You get a message on WhatsApp: 'Your SBI account is suspended. Click here to verify.'",
type: 'scam',
choices: [
{ text: "Click link immediately (Panic)", effect: { money: -5000, safety: -20, scamRisk: +30 }, result: "SCAM! Your bank account is drained. You lose β‚Ή5,000." },
{ text: "Ignore and block", effect: { safety: +5, scamRisk: -5 }, result: "Smart move. You check the real app. Everything is fine." },
{ text: "Call customer care", effect: { safety: +10, stability: +5 }, result: "It takes 20 minutes, but you confirm it was fake." }
]
},
{
id: 'c2',
text: "A wedding invitation arrives from a distant relative. Gift expectations are high.",
type: 'social',
choices: [
{ text: "Take a small loan for a good gift", effect: { money: +2000, debt: +15, stability: -10 }, result: "You look respectable at the wedding, but the loan EMI starts next month." },
{ text: "Give what you can afford", effect: { money: -1000, stability: +5 }, result: "You give a modest gift. Nobody cares, but you are debt-free." },
{ text: "Make an excuse and don't go", effect: { stability: -5, money: 0 }, result: "You save money but your mother is upset with you." }
]
},
{
id: 'c3',
text: "A sudden fever hits the house. You need medicines fast.",
type: 'shock',
choices: [
{ text: "Buy generic medicines from pharmacy", effect: { money: -500, safety: +10 }, result: "Effective and cheap. Health improves." },
{ text: "Go to private hospital immediately", effect: { money: -3000, safety: +15, stability: -5 }, result: "Great care, but expensive. Your wallet hurts." },
{ text: "Wait and see if it passes", effect: { safety: -20, stability: -5 }, result: "Condition worsens. You end up paying double later." }
]
}
],
farmer: [
{
id: 'f1',
text: "The monsoon is delayed. You have enough water for only 2 more weeks.",
type: 'crisis',
choices: [
{ text: "Buy a diesel pump set on loan (β‚Ή15k)", effect: { money: +15000, debt: +30, safety: +10 }, result: "The pump saves the crop, but now you owe the money lender heavily." },
{ text: "Pray and wait for rain", effect: { safety: -20, stability: -10 }, result: "Rain comes late. Crop yield is reduced by 40%.", shockResult: { money: -10000 } },
{ text: "Sell the calf early to buy water", effect: { money: -500, wealth: -10, safety: +5 }, result: "You save the crop, but lost a future asset." }
]
},
{
id: 'f_insurance',
text: "An agent offers 'Pradhan Mantri Fasal Bima Yojana' for β‚Ή2,000. It covers crop failure.",
type: 'decision',
choices: [
{ text: "Buy Insurance (β‚Ή2,000)", effect: { money: -2000, tags: ['insured_crop'] }, result: "You feel lighter. If disaster strikes, you won't lose everything." },
{ text: "Save the money (Keep β‚Ή2,000)", effect: { money: 0, tags: ['uninsured_crop'] }, result: "You keep the cash today. But you are exposed to nature's fury.", karmaConsequence: 'drought_check' }
]
},
{
id: 'f_drought',
text: "A severe drought hits the region. Crops are failing everywhere.",
type: 'crisis',
requiredTags: ['uninsured_crop'], // Only if they didn't buy insurance
choices: [
{ text: "Accept the loss", effect: { money: -40000, stability: -40, safety: -20 }, result: "Years of work gone. You have to take a massive loan to survive next season." }
]
},
{
id: 'f_drought_safe',
text: "A severe drought hits the region. Crops are failing everywhere.",
type: 'crisis',
requiredTags: ['insured_crop'], // Only if insured
choices: [
{ text: "Claim Insurance", effect: { money: +30000, stability: +10 }, result: "Insurance payout arrives! You survive the drought without falling into debt." }
]
}
],
woman: [
{
id: 'w1',
text: "A customer orders 10 blouses for a festival, asks to pay 'later'.",
type: 'business',
choices: [
{ text: "Accept (Need the work)", effect: { tags: ['mixed_wallets'] }, result: "She doesn't pay for 3 months. Your business cash flow stops because you used home money for groceries.", shockResult: { businessCash: -5000 } },
{ text: "Demand 50% advance", effect: { businessCash: +1000, stability: +10 }, result: "She agrees. You have money to buy materials immediately." },
{ text: "Refuse politely", effect: { money: 0, stability: -5 }, result: "You lose the work, but keep your dignity and safety." }
]
},
{
id: 'w2',
text: "Groceries are running low. You have β‚Ή2,000 in business cash and β‚Ή500 in home cash.",
type: 'routine',
choices: [
{ text: "Pay from Business Cash (Easy)", effect: { businessCash: -1500, tags: ['mixed_wallets'], stability: -5 }, result: "Groceries bought. But now you can't buy fabric for the next order." },
{ text: "Pay from Home Cash (Tight)", effect: { money: -500, wealth: +5, tags: ['separated_wallets'] }, result: "You skip a meal or two, but your business capital is safe." }
]
},
{
id: 'w3',
text: "Big order! 50 school uniforms. Need β‚Ή5,000 fabric immediately.",
type: 'business',
choices: [
{ text: "Use business savings", effect: { businessCash: -5000, money: +10000, tags: ['separated_wallets'] }, result: "You complete the order, make profit, and business grows." },
{ text: "Borrow from local lender", effect: { money: +5000, debt: +20 }, result: "You pay high interest. Profit is eaten up by the loan.", shockResult: { stability: -10 } }
]
}
],
student: [
{
id: 's1',
text: "Friends are going to Goa for the weekend. You don't have the money.",
type: 'social',
choices: [
{ text: "Borrow from Parents (Lie about fees)", effect: { money: 5000, debt: +20, stability: -10, scamRisk: +10, tags: ['habitual_liar'] }, result: "Fun trip. But you start a habit of lying for money." },
{ text: "Use 'Buy Now Pay Later' app", effect: { money: 5000, debt: +30, safety: -10, tags: ['credit_addict'] }, result: "Instant approval. Now you owe the app + high interest." },
{ text: "Stay back and study", effect: { wealth: +10, stability: +5, tags: ['focused'] }, result: "FOMO hits hard, but your grades improve." }
]
},
{
id: 's2',
text: "You see a YouTube video: 'Turn β‚Ή500 into β‚Ή50,000 with Crypto'.",
type: 'scam',
choices: [
{ text: "Invest immediately", effect: { money: -500, wealth: -10, tags: ['crypto_gambler'] }, result: "It's a rug pull. Your money is gone in seconds.", karmaConsequence: 'future_loan_rejection' },
{ text: "Watch full video, then research", effect: { scamRisk: -10 }, result: "You find comments calling it a scam. You dodged a bullet." }
]
},
{
id: 's3',
text: "Flash sale on sneakers! 70% OFF for 1 hour.",
type: 'social',
choices: [
{ text: "Buy Now!", effect: { money: -2000, tags: ['impulse_buyer'] }, result: "They look cool. But you eat instant noodles for two weeks." },
{ text: "Ignore", effect: { wealth: +5 }, result: "Resisting feels good. Your savings stay intact." }
]
}
],
young_adult: [
{
id: 'y1',
text: "Salary credited! Bank offers a pre-approved personal loan for 'Travel'.",
type: 'credit',
choices: [
{ text: "Take the loan (Trip to Europe)", effect: { money: 100000, debt: +40, stability: -20 }, result: "Instagram photos look great. The EMI is 40% of your salary." },
{ text: "Start a SIP (Mutual Fund) instead", effect: { money: -10000, wealth: +20, stability: +10, tags: ['investor'] }, result: "Not exciting today, but your future self will thank you." },
{ text: "Buy a new iPhone on EMI", effect: { money: -10000, debt: +20, wealth: -5 }, result: "You look cool. Asset value drops immediately." }
]
},
{
id: 'y2',
text: "Company announces layoffs. You have 6 months of expenses saved.",
type: 'shock',
choices: [
{ text: "Panic and look for job now", effect: { stability: -10 }, result: "You survive the cut, but the stress was unnecessary." },
{ text: "Relax, you have a fund", effect: { stability: +20, safety: +20 }, result: "You are laid off, but you survive 4 months comfortably without panic." },
{ text: "Invest savings in stock market", effect: { wealth: -20, safety: -30 }, result: "Market crashes the next week. You lose your safety net.", shockResult: { money: -20000, stability: -20 } }
]
},
{
id: 'y_loan_reject',
text: "You apply for a home loan to buy a flat.",
type: 'finance',
requiredTags: ['credit_addict', 'crypto_gambler', 'habitual_liar'],
choices: [
{ text: "Submit Application", effect: { stability: -20 }, result: "REJECTED. Your credit score is ruined by past impulsive decisions and 'Buy Now Pay Later' apps." }
]
}
]
};
// --- STATE MANAGEMENT ---
class GameState {
constructor() {
this.reset();
}
reset() {
this.personaId = null;
this.month = 1;
this.year = 1;
this.money = 0;
this.businessCash = 0; // For Sunita
this.stats = { stability: 50, safety: 50, debt: 0, scamRisk: 0, wealth: 20 };
this.karmaTags = []; // "Invisible Future Meter" tags
this.history = [];
this.currentScenario = null;
this.gameOver = false;
}
loadPersona(id) {
const p = PERSONAS[id];
this.personaId = id;
this.money = p.startMoney;
this.businessCash = p.businessStartCash || 0;
this.stats = { ...p.stats };
this.month = 1;
this.year = 1;
this.gameOver = false;
this.karmaTags = [];
}
nextTurn() {
this.month++;
if (this.month > 12) {
this.month = 1;
this.year++;
}
// Passive Income/Expense
const persona = PERSONAS[this.personaId];
// Dual Wallet Income Logic
if (persona.special === 'separation_wallets') {
this.businessCash += persona.income;
} else {
this.money += persona.income;
}
// Debt Interest Effect (Compounding)
if (this.stats.debt > 50) {
this.money -= 2000;
this.stats.stability -= 2;
}
// Game Over Condition (10 Years or Bankruptcy)
if (this.year > 10 || (this.money < -20000 && this.businessCash < -20000)) {
this.gameOver = true;
return 'end';
}
return 'continue';
}
applyEffect(effect) {
if (!effect) return;
// Financials
if (effect.money) this.money += effect.money;
if (effect.businessCash) this.businessCash += effect.businessCash;
// Stats
if (effect.stability) this.updateStat('stability', effect.stability);
if (effect.safety) this.updateStat('safety', effect.safety);
if (effect.debt) this.updateStat('debt', effect.debt);
if (effect.scamRisk) this.updateStat('scamRisk', effect.scamRisk);
if (effect.wealth) this.updateStat('wealth', effect.wealth);
// Karma Tags (The Invisible Hand)
if (effect.tags) {
effect.tags.forEach(tag => {
if (!this.karmaTags.includes(tag)) {
this.karmaTags.push(tag);
}
});
}
// Clamp stats 0-100
for (let key in this.stats) {
this.stats[key] = Math.max(0, Math.min(100, this.stats[key]));
}
}
updateStat(key, value) {
this.stats[key] += value;
}
getScenario() {
// 1. Check for Karma-triggered specific events first
// If user has tags ['uninsured_crop'], prioritize drought event
if (this.karmaTags.includes('uninsured_crop') && Math.random() > 0.6) {
return SCENARIOS.farmer.find(s => s.id === 'f_drought');
}
if (this.karmaTags.includes('insured_crop') && Math.random() > 0.6) {
return SCENARIOS.farmer.find(s => s.id === 'f_drought_safe');
}
if (this.personaId === 'young_adult' && (this.karmaTags.includes('credit_addict') || this.karmaTags.includes('crypto_gambler')) && Math.random() > 0.7) {
return SCENARIOS.young_adult.find(s => s.id === 'y_loan_reject');
}
// 2. Standard Logic: 30% Common, 70% Persona Specific
const pool = Math.random() > 0.3 ? SCENARIOS[this.personaId] : SCENARIOS.common;
const available = pool.filter(s => {
if (!s.requiredTags) return true;
// If scenario requires tags, check if player has ANY of them
return s.requiredTags.some(tag => this.karmaTags.includes(tag));
});
return available.length > 0 ? available[Math.floor(Math.random() * available.length)] : pool[Math.floor(Math.random() * pool.length)];
}
}
const state = new GameState();
// --- GAME LOGIC ---
const mainContent = document.getElementById('main-content');
function initGame() {
renderHome();
}
function renderHome() {
mainContent.innerHTML = `
<div class="animate-enter text-center mb-10">
<h1 class="text-4xl md:text-6xl font-extrabold text-white mb-2 tracking-tight">PAISA YUDDH</h1>
<p class="text-xl text-primary font-bold uppercase">The Financial Survival Game of Bharat</p>
<p class="text-slate-400 mt-2 max-w-lg mx-auto">You don't learn money. You survive with it. <br>No reset button. No right answers. Only consequences.</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 animate-enter" style="animation-delay: 0.1s;">
<persona-card id="farmer"></persona-card>
<persona-card id="woman"></persona-card>
<persona-card id="student"></persona-card>
<persona-card id="young_adult"></persona-card>
</div>
`;
}
window.selectPersona = (id) => {
state.loadPersona(id);
document.dispatchEvent(new Event('state-update'));
renderGameInterface();
startTurn();
};
function renderGameInterface() {
mainContent.innerHTML = `
<div class="grid grid-cols-1 md:grid-cols-2 gap-6 animate-enter">
<!-- Left: Story & Interaction -->
<div class="flex flex-col gap-6">
<div id="scenario-container" class="glass-panel p-6 rounded-xl border-l-4 border-primary min-h-[300px] flex flex-col justify-center relative overflow-hidden">
<div id="scenario-bg" class="absolute inset-0 opacity-10 pointer-events-none bg-cover bg-center" style="background-image: url('http://static.photos/abstract/640x360');"></div>
<div class="relative z-10">
<!-- Content loads here -->
</div>
</div>
</div>
<!-- Right: Invisible Future Meter -->
<div class="glass-panel p-6 rounded-xl flex flex-col">
<h3 class="text-white font-bold mb-4 border-b border-slate-700 pb-2 flex items-center gap-2">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="#f97316" stroke-width="2"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="16" x2="12" y2="12"></line><line x1="12" y1="8" x2="12.01" y2="8"></line></svg>
Invisible Future Meter
</h3>
<p class="text-xs text-slate-400 mb-6">You don't see the numbers, you feel the effects. These hidden stats determine your fate.</p>
<stat-meter label="Stability"></stat-meter>
<stat-meter label="Safety"></stat-meter>
<stat-meter label="Debt Load"></stat-meter>
<stat-meter label="Scam Risk"></stat-meter>
<stat-meter label="Wealth Path"></stat-meter>
</div>
</div>
`;
}
function startTurn() {
const turnResult = state.nextTurn();
if (turnResult === 'end') {
renderEndScreen();
return;
}
const scenario = state.getScenario();
state.currentScenario = scenario;
renderScenario(scenario);
}
function renderScenario(scenario) {
const container = document.getElementById('scenario-container');
const containerContent = container.querySelector('.relative.z-10');
// Update background based on type
const bgType = scenario.type === 'scam' ? 'technology' : scenario.type === 'crisis' ? 'nature' : 'abstract';
document.getElementById('scenario-bg').style.backgroundImage = `url('http://static.photos/${bgType}/640x360/${Math.floor(Math.random()*100)}')`;
let iconColor = 'text-primary';
if(scenario.type === 'scam') iconColor = 'text-purple-500';
if(scenario.type === 'crisis') iconColor = 'text-red-500';
containerContent.innerHTML = `
<div class="animate-enter">
<div class="text-xs font-bold uppercase tracking-widest mb-3 ${iconColor} flex items-center gap-2">
<span class="w-2 h-2 rounded-full bg-current"></span> Life Event: ${scenario.type}
</div>
<h2 class="text-xl md:text-2xl text-white font-bold mb-6 leading-relaxed">
${scenario.text}
</h2>
<div class="flex flex-col gap-3 mt-4">
${scenario.choices.map((choice, idx) => `
<button onclick="handleChoice(${idx})"
class="text-left p-4 bg-slate-800/80 hover:bg-slate-700 border border-slate-600 rounded-lg transition-all hover:border-primary group focus:outline-none focus:ring-2 focus:ring-primary">
<div class="text-white font-medium group-hover:text-primary flex justify-between items-center">
<span>${choice.text}</span>
<i data-feather="chevron-right" class="opacity-0 group-hover:opacity-100 transition-opacity" width="16"></i>
</div>
</button>
`).join('')}
</div>
</div>
`;
feather.replace();
}
window.handleChoice = (idx) => {
const choice = state.currentScenario.choices[idx];
state.applyEffect(choice.effect);
let resultText = choice.result;
// Handle Shock Results (Random consequence)
if (choice.shockResult && Math.random() > 0.5) {
state.applyEffect(choice.shockResult);
const shockMsg = choice.shockResult.text || "Unexpected financial hit due to previous risks!";
resultText += `<br><br><div class="bg-red-900/30 border border-red-500/30 p-3 rounded text-red-200 text-sm font-bold mt-2">⚠️ SHOCK: ${shockMsg}</div>`;
}
// Check for Karma Consequences (The "Aha!" moment)
if (choice.karmaConsequence === 'drought_check' && Math.random() > 0.4) {
// Triggered if they didn't buy insurance previously and bad luck strikes
resultText += `<br><br><div class="bg-red-900/30 border border-red-500/30 p-3 rounded text-red-200 text-sm font-bold mt-2">πŸ’€ KARMA: The drought you feared has arrived next season!</div>`;
}
renderResult(resultText);
// Visual Feedback: Karma Positive
const container = document.getElementById('scenario-container');
if (choice.effect && choice.effect.tags && choice.effect.tags.includes('separated_wallets')) {
container.classList.add('karma-positive');
setTimeout(() => container.classList.remove('karma-positive'), 2000);
}
document.dispatchEvent(new Event('state-update'));
};
function renderResult(text) {
const container = document.getElementById('scenario-container');
const containerContent = container.querySelector('.relative z-10');
containerContent.innerHTML = `
<div class="animate-enter text-center py-8">
<div class="text-6xl mb-6">🎲</div>
<h3 class="text-2xl text-white font-bold mb-4">The Outcome</h3>
<div class="text-slate-300 text-lg leading-relaxed mb-8">${text}</div>
<button onclick="startTurn()" class="bg-primary hover:bg-orange-600 text-white font-bold py-3 px-8 rounded-full shadow-lg shadow-orange-500/30 transition-all transform hover:scale-105 active:scale-95">
Next Month <i data-feather="arrow-right" class="inline ml-2 align-middle" style="width:16px;"></i>
</button>
</div>
`;
feather.replace();
}
function renderEndScreen() {
const title = state.money < 0 ? "FINANCIAL RUIN" :
state.stats.wealth > 60 ? "FINANCIAL WARRIOR" : "SURVIVED";
const color = state.money < 0 ? "text-red-500" : "text-green-500";
// Analyze Karma
const badHabits = state.karmaTags.filter(t => ['crypto_gambler', 'credit_addict', 'habitual_liar', 'mixed_wallets'].includes(t));
const goodHabits = state.karmaTags.filter(t => ['investor', 'separated_wallets', 'focused', 'insured_crop'].includes(t));
mainContent.innerHTML = `
<div class="animate-enter glass-panel p-8 rounded-xl text-center max-w-2xl mx-auto mt-10">
<div class="text-5xl mb-4">${state.money < 0 ? 'πŸ“‰' : 'πŸ†'}</div>
<h1 class="text-4xl font-bold ${color} mb-2">${title}</h1>
<p class="text-slate-400 mb-8">10 Years have passed in the life of ${PERSONAS[state.personaId].name}</p>
<div class="grid grid-cols-2 gap-4 mb-8">
<div class="bg-slate-800 p-4 rounded border border-slate-700">
<div class="text-sm text-slate-400">Final Net Worth</div>
<div class="text-2xl font-bold text-white">β‚Ή${(state.money + (state.businessCash || 0)).toLocaleString()}</div>
</div>
<div class="bg-slate-800 p-4 rounded border border-slate-700">
<div class="text-sm text-slate-400">Wealth Score</div>
<div class="text-2xl font-bold text-white">${state.stats.wealth}/100</div>
</div>
</div>
<div class="bg-slate-800 p-6 rounded text-left mb-8 border border-slate-700">
<h4 class="font-bold text-white mb-4 text-lg flex items-center gap-2">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a10 10 0 1 0 10 10H12V2z"></path></svg>
Behavioral Analysis
</h4>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<div class="text-xs uppercase text-slate-500 font-bold mb-2">Strengths</div>
${goodHabits.length > 0 ?
goodHabits.map(h => `<div class="text-green-400 text-sm flex items-center gap-2"><span>βœ“</span> ${h.replace('_', ' ')}</div>`).join('') :
<div class="text-slate-500 text-sm italic">No significant financial habits formed.</div>
}
</div>
<div>
<div class="text-xs uppercase text-slate-500 font-bold mb-2">Risks Detected</div>
${badHabits.length > 0 ?
badHabits.map(h => `<div class="text-red-400 text-sm flex items-center gap-2"><span>⚠</span> ${h.replace('_', ' ')}</div>`).join('') :
<div class="text-slate-500 text-sm italic">Clean financial record.</div>
}
</div>
</div>
</div>
<button onclick="location.reload()" class="bg-slate-700 hover:bg-slate-600 text-white font-bold py-3 px-8 rounded-full border border-slate-500 transition-colors">
Play Again
</button>
</div>
`;
}
// Init
window.onload = () => {
feather.replace();
initGame();
};