fixed news

This commit is contained in:
2025-08-04 00:10:03 +05:30
parent 511bbb87b4
commit b6041e82bb
7 changed files with 233 additions and 25 deletions

View File

@@ -2,9 +2,31 @@
import type * as Party from 'partykit/server'; import type * as Party from 'partykit/server';
import { createClerkClient, verifyToken } from '@clerk/backend'; import { createClerkClient, verifyToken } from '@clerk/backend';
export interface Upgrade {
id: string;
name: string;
description: string;
baseCost: number;
multiplier: number;
clickBonus?: number;
autoClickRate?: number;
clickMultiplierBonus?: number;
icon: string;
mascotTiers?: MascotTier[];
oneTime?: boolean;
newsTitles?: string[];
}
export interface MascotTier {
level: number;
imageSrc: string;
multiplier: number;
rarity: number;
}
interface GameState { interface GameState {
totalClicks: number; totalClicks: number;
users: Record<string, { name: string; clicks: number; lastSeen: number; bonusMultiplier: number }>; // Added bonusMultiplier users: Record<string, { name: string; clicks: number; lastSeen: number; bonusMultiplier: number }>;
upgrades: Record<string, { owned: number; cost: number }>; upgrades: Record<string, { owned: number; cost: number }>;
milestones: Record<string, boolean>; milestones: Record<string, boolean>;
clickMultiplier: number; clickMultiplier: number;
@@ -45,14 +67,120 @@ interface UserJoinMessage extends AuthenticatedMessage {
type Message = ClickMessage | PurchaseUpgradeMessage | ApplyMultiplierBonusMessage | UserJoinMessage | AdminBroadcastMessage; // Updated Message type type Message = ClickMessage | PurchaseUpgradeMessage | ApplyMultiplierBonusMessage | UserJoinMessage | AdminBroadcastMessage; // Updated Message type
const UPGRADES = { const UPGRADES: Upgrade[] = [
clickMultiplier: { baseCost: 10, multiplier: 1.5, clickBonus: 1 }, {
autoClicker: { baseCost: 50, multiplier: 2, autoClickRate: 1 }, id: 'clickMultiplier',
megaBonus: { baseCost: 200, multiplier: 2.5, clickBonus: 5 }, name: '🖱️ Mega Click',
hyperClicker: { baseCost: 1000, multiplier: 3, autoClickRate: 10 }, description: '+1 click power per purchase',
quantumClicker: { baseCost: 5000, multiplier: 4, clickBonus: 50 }, baseCost: 10,
friendBoost: { baseCost: 2000, multiplier: 3, clickMultiplierBonus: 1.02 } // Renamed from shoominions upgrade multiplier: 1.5,
}; clickBonus: 1,
icon: '🖱️'
},
{
id: 'autoClicker',
name: '🤖 Auto Clicker',
description: '+1 click per second',
baseCost: 50,
multiplier: 2,
autoClickRate: 1,
icon: '🤖'
},
{
id: 'megaBonus',
name: '💎 Mega Bonus',
description: '+5 click power per purchase',
baseCost: 200,
multiplier: 2.5,
clickBonus: 5,
icon: '💎'
},
{
id: 'hyperClicker',
name: '⚡ Hyper Clicker',
description: '+10 auto clicks per second',
baseCost: 1000,
multiplier: 3,
autoClickRate: 10,
icon: '⚡'
},
{
id: 'quantumClicker',
name: '🌟 Quantum Clicker',
description: '+50 click power per purchase',
baseCost: 5000,
multiplier: 4,
clickBonus: 50,
icon: '🌟'
},
{
id: 'friendBoost',
name: '🤝 Friend Boost',
description: 'Spawns various clickable friends for a compounding click boost',
baseCost: 2000,
multiplier: 3,
icon: '🤝',
mascotTiers: [
{
level: 0,
imageSrc: '/src/assets/bozo.png',
multiplier: 1.02,
rarity: 1.0,
},
{
level: 1,
imageSrc: '/src/assets/shoominion.png',
multiplier: 1.03,
rarity: 0.8,
},
{
level: 5,
imageSrc: '/src/assets/codebug.gif',
multiplier: 1.05,
rarity: 0.6,
},
{
level: 10,
imageSrc: '/src/assets/lalan.gif',
multiplier: 1.07,
rarity: 0.4,
},
{
level: 15,
imageSrc: '/src/assets/neuro-neurosama.gif',
multiplier: 1.10,
rarity: 0.2,
},
{
level: 20,
imageSrc: '/src/assets/evil-neurosama.gif',
multiplier: 1.15,
rarity: 0.1,
},
],
},
{
id: 'news',
name: '📰 Bozo News Network',
description: 'Unlock the latest (fake) news headlines!',
baseCost: 50000, // A higher cost for a unique, one-time unlock
multiplier: 1, // No direct click/auto-click bonus
icon: '📰',
oneTime: true,
newsTitles: [
'Bozo Clicker Breaks Internet, Causes Global Click Shortage!',
'Scientists Discover New Element: "Bozo-nium," Powers Clicker Devices',
'Local Man Achieves Enlightenment Through Excessive Clicking',
'World Leaders Debate Universal Basic Clicks Initiative',
'Ancient Prophecy Foretells Rise of the Ultimate Clicker',
'Clicker Enthusiast Develops New Muscle Group: The "Click-ceps"',
'Bozo Clicker Declared Official Sport of the Future',
'AI Learns to Click, Demands Higher Click-Per-Second Wages',
'Interdimensional Portal Opens, Emits Sound of Relentless Clicking',
'The Great Clicker Migration: Millions Flock to Clicker Hotspots'
]
}
];
const MILESTONES = [ const MILESTONES = [
{ threshold: 100, id: 'first-hundred', background: 'rainbow', image: 'https://media1.tenor.com/m/x8v1oNUOmg4AAAAd/spinning-rat-rat.gif' }, { threshold: 100, id: 'first-hundred', background: 'rainbow', image: 'https://media1.tenor.com/m/x8v1oNUOmg4AAAAd/spinning-rat-rat.gif' },
@@ -76,8 +204,8 @@ export default class GameServer implements Party.Server {
gameState: GameState = { gameState: GameState = {
totalClicks: 0, totalClicks: 0,
users: {}, users: {},
upgrades: Object.keys(UPGRADES).reduce((acc, key) => { upgrades: UPGRADES.reduce((acc, upgrade) => {
acc[key] = { owned: 0, cost: UPGRADES[key as keyof typeof UPGRADES].baseCost }; acc[upgrade.id] = { owned: 0, cost: upgrade.baseCost };
return acc; return acc;
}, {} as Record<string, { owned: number; cost: number }>), }, {} as Record<string, { owned: number; cost: number }>),
milestones: {}, milestones: {},
@@ -269,16 +397,26 @@ export default class GameServer implements Party.Server {
} }
handlePurchaseUpgrade(data: PurchaseUpgradeMessage, authenticatedUserId: string) { handlePurchaseUpgrade(data: PurchaseUpgradeMessage, authenticatedUserId: string) {
const upgrade = UPGRADES[data.upgradeId as keyof typeof UPGRADES]; const upgradeConfig = UPGRADES.find(u => u.id === data.upgradeId);
const currentUpgrade = this.gameState.upgrades[data.upgradeId]; const currentUpgradeState = this.gameState.upgrades[data.upgradeId];
if (!upgrade || !currentUpgrade) return; if (!upgradeConfig || !currentUpgradeState) return;
// Prevent purchasing one-time upgrades if already owned
if (upgradeConfig.oneTime && currentUpgradeState.owned > 0) {
console.warn(`Attempted to re-purchase one-time upgrade: ${data.upgradeId}`);
return;
}
// Check affordability against totalClicks // Check affordability against totalClicks
if (this.gameState.totalClicks >= currentUpgrade.cost) { if (this.gameState.totalClicks >= currentUpgradeState.cost) {
this.gameState.totalClicks -= currentUpgrade.cost; // Deduct from totalClicks this.gameState.totalClicks -= currentUpgradeState.cost; // Deduct from totalClicks
currentUpgrade.owned += 1; currentUpgradeState.owned += 1;
currentUpgrade.cost = Math.floor(upgrade.baseCost * Math.pow(upgrade.multiplier, currentUpgrade.owned));
// For one-time upgrades, cost doesn't change after first purchase
if (!upgradeConfig.oneTime) {
currentUpgradeState.cost = Math.floor(upgradeConfig.baseCost * Math.pow(upgradeConfig.multiplier, currentUpgradeState.owned));
}
this.updateGameMultipliers(); this.updateGameMultipliers();
} }
@@ -288,13 +426,15 @@ export default class GameServer implements Party.Server {
this.gameState.clickMultiplier = 1; this.gameState.clickMultiplier = 1;
this.gameState.autoClickRate = 0; this.gameState.autoClickRate = 0;
Object.entries(this.gameState.upgrades).forEach(([upgradeId, upgrade]) => { Object.entries(this.gameState.upgrades).forEach(([upgradeId, upgradeState]) => {
const config = UPGRADES[upgradeId as keyof typeof UPGRADES]; const config = UPGRADES.find(u => u.id === upgradeId);
if ('clickBonus' in config && config.clickBonus) { if (!config) return; // Should not happen if UPGRADES is consistent
this.gameState.clickMultiplier += config.clickBonus * upgrade.owned;
if (config.clickBonus) {
this.gameState.clickMultiplier += config.clickBonus * upgradeState.owned;
} }
if ('autoClickRate' in config && config.autoClickRate) { if (config.autoClickRate) {
this.gameState.autoClickRate += config.autoClickRate * upgrade.owned; this.gameState.autoClickRate += config.autoClickRate * upgradeState.owned;
} }
// Note: clickMultiplierBonus from upgrades.ts is handled client-side for spawning frequency // Note: clickMultiplierBonus from upgrades.ts is handled client-side for spawning frequency
// and applied per-click on the server via handleApplyMultiplierBonus // and applied per-click on the server via handleApplyMultiplierBonus

View File

@@ -14,6 +14,7 @@ import { ClickableMascot as ClickableMascotType, Upgrade } from './types'; // Im
import { UPGRADES } from './config/upgrades'; // Import UPGRADES for upgrade config import { UPGRADES } from './config/upgrades'; // Import UPGRADES for upgrade config
import { useLocation, Link } from 'wouter'; // Import wouter hooks import { useLocation, Link } from 'wouter'; // Import wouter hooks
import AdminPage from './components/AdminPage'; // Import AdminPage import AdminPage from './components/AdminPage'; // Import AdminPage
import { NewsMarquee } from './components/NewsMarquee'; // Import NewsMarquee
function App() { function App() {
const { isSignedIn, isLoaded, userId: clerkUserId } = useAuth(); // Get clerkUserId from useAuth const { isSignedIn, isLoaded, userId: clerkUserId } = useAuth(); // Get clerkUserId from useAuth
@@ -216,6 +217,11 @@ function App() {
</div> </div>
)} )}
{/* News Marquee */}
{gameState.upgrades['news']?.owned > 0 && UPGRADES.find(u => u.id === 'news')?.newsTitles && (
<NewsMarquee titles={UPGRADES.find(u => u.id === 'news')!.newsTitles!} />
)}
<div className="container mx-auto px-4 py-8 relative z-10"> <div className="container mx-auto px-4 py-8 relative z-10">
{/* Header */} {/* Header */}
<div className="mb-8"> <div className="mb-8">

View File

@@ -0,0 +1,24 @@
import React from 'react';
interface NewsMarqueeProps {
titles: string[];
}
export const NewsMarquee: React.FC<NewsMarqueeProps> = ({ titles }) => {
if (titles.length === 0) {
return null;
}
// Join all titles with a separator and duplicate for seamless looping
const marqueeText = titles.join(' --- ') + ' --- ' + titles.join(' --- ');
return (
<div className="fixed bottom-0 z-40 h-10 left-0 w-full overflow-hidden bg-gray-900 text-yellow-400 py-2 border-t-2 border-yellow-500">
<div className="absolute whitespace-nowrap animate-marquee z-50">
<span className="text-lg font-bold px-4">
{marqueeText}
</span>
</div>
</div>
);
};

View File

@@ -35,6 +35,11 @@ export function UpgradeShop({ gameState, totalClicks, onPurchase }: UpgradeShopP
const cost = gameState.upgrades[upgrade.id]?.cost || upgrade.baseCost; const cost = gameState.upgrades[upgrade.id]?.cost || upgrade.baseCost;
const canAfford = totalClicks >= cost; // Changed from userClicks const canAfford = totalClicks >= cost; // Changed from userClicks
// If it's a one-time upgrade and already owned, don't display it
if (upgrade.oneTime && owned > 0) {
return null;
}
let description = upgrade.description; let description = upgrade.description;
// Custom description for Friend Boost upgrade // Custom description for Friend Boost upgrade

View File

@@ -91,5 +91,26 @@ export const UPGRADES: Upgrade[] = [
rarity: 0.1, rarity: 0.1,
}, },
], ],
},
{
id: 'news',
name: '📰 Bozo News Network',
description: 'Unlock the latest (fake) news headlines!',
baseCost: 50000, // A higher cost for a unique, one-time unlock
multiplier: 1, // No direct click/auto-click bonus
icon: '📰',
oneTime: true,
newsTitles: [
'Bozo Clicker Breaks Internet, Causes Global Click Shortage!',
'Scientists Discover New Element: "Bozo-nium," Powers Clicker Devices',
'Local Man Achieves Enlightenment Through Excessive Clicking',
'World Leaders Debate Universal Basic Clicks Initiative',
'Ancient Prophecy Foretells Rise of the Ultimate Clicker',
'Clicker Enthusiast Develops New Muscle Group: The "Click-ceps"',
'Bozo Clicker Declared Official Sport of the Future',
'AI Learns to Click, Demands Higher Click-Per-Second Wages',
'Interdimensional Portal Opens, Emits Sound of Relentless Clicking',
'The Great Clicker Migration: Millions Flock to Clicker Hotspots'
]
} }
]; ];

View File

@@ -20,6 +20,8 @@ export interface Upgrade {
clickMultiplierBonus?: number; // New: for compounding click boosts from mascots clickMultiplierBonus?: number; // New: for compounding click boosts from mascots
icon: string; icon: string;
mascotTiers?: MascotTier[]; // New: for defining mascot tiers for friendBoost mascotTiers?: MascotTier[]; // New: for defining mascot tiers for friendBoost
oneTime?: boolean; // New: Indicates if the upgrade is a one-time purchase
newsTitles?: string[]; // New: Array of news titles for the news upgrade
} }
export interface MascotTier { export interface MascotTier {

View File

@@ -2,7 +2,17 @@
export default { export default {
content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'],
theme: { theme: {
extend: {}, extend: {
keyframes: {
marquee: {
'0%': { transform: 'translateX(100%)' },
'100%': { transform: 'translateX(-100%)' },
},
},
animation: {
marquee: 'marquee 120s linear infinite', // Adjusted duration for slower scroll
},
},
}, },
plugins: [], plugins: [],
}; };