rat poiuson
This commit is contained in:
301
party/index.ts
301
party/index.ts
@@ -25,6 +25,7 @@ export interface MascotTier {
|
||||
rarity: number;
|
||||
}
|
||||
|
||||
|
||||
interface UserState {
|
||||
name: string;
|
||||
clicks: number;
|
||||
@@ -34,6 +35,11 @@ interface UserState {
|
||||
upgrades: Record<string, { owned: number; cost: number }>;
|
||||
clickMultiplier: number;
|
||||
autoClickRate: number;
|
||||
ratPoisonChallenge?: {
|
||||
challengeString: string;
|
||||
expiresAt: number;
|
||||
};
|
||||
ratPoisonImmunityUntil?: number;
|
||||
}
|
||||
|
||||
interface GameState {
|
||||
@@ -81,144 +87,22 @@ interface EditUserMessage extends AuthenticatedMessage {
|
||||
upgrades: Record<string, { owned: number; cost: number }>;
|
||||
}
|
||||
|
||||
type Message = ClickMessage | PurchaseUpgradeMessage | ApplyMultiplierBonusMessage | UserJoinMessage | AdminBroadcastMessage | EditUserMessage; // Updated Message type
|
||||
interface ThrowRatPoisonMessage extends AuthenticatedMessage {
|
||||
type: 'throw-rat-poison';
|
||||
targetUserId: string;
|
||||
}
|
||||
|
||||
const UPGRADES: Upgrade[] = [
|
||||
{
|
||||
id: 'clickMultiplier',
|
||||
name: '🖱️ Mega Click',
|
||||
description: '+1 click power per purchase',
|
||||
baseCost: 10,
|
||||
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: '/assets/bozo.png',
|
||||
multiplier: 1.002,
|
||||
rarity: 1.0,
|
||||
},
|
||||
{
|
||||
level: 1,
|
||||
imageSrc: '/assets/shoominion.png',
|
||||
multiplier: 1.003,
|
||||
rarity: 0.8,
|
||||
},
|
||||
{
|
||||
level: 5,
|
||||
imageSrc: '/assets/codebug.gif',
|
||||
multiplier: 1.005,
|
||||
rarity: 0.6,
|
||||
},
|
||||
{
|
||||
level: 10,
|
||||
imageSrc: '/assets/lalan.gif',
|
||||
multiplier: 1.007,
|
||||
rarity: 0.4,
|
||||
},
|
||||
{
|
||||
level: 15,
|
||||
imageSrc: '/assets/neuro-neurosama.gif',
|
||||
multiplier: 1.010,
|
||||
rarity: 0.2,
|
||||
},
|
||||
{
|
||||
level: 20,
|
||||
imageSrc: '/assets/evil-neurosama.gif',
|
||||
multiplier: 1.015,
|
||||
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: [
|
||||
`Its Bo's birthday!`,
|
||||
`Haru Urara looses another race, forced to eat pickle filled burger.`,
|
||||
`Cupid crashes out once again, completely expected.`,
|
||||
`Bo has been spotted in the wild, please do not approach.`,
|
||||
`Bozo Clicker is now the number game in the world!`,
|
||||
`Bo states that he did win the hidden gem vtuber award, it's just that he just didn't feel like it so Reya was really kind to step up.`,
|
||||
`Reya has been spotted in the wild, please do not approach.`,
|
||||
`What? Stop reading? I wrote this when you were playing roblox with cupid`,
|
||||
`It's 1pm in the night and my mom is calling me to sleep, I don't want to sleep I'm making this stupid game`,
|
||||
`FU BO`,
|
||||
`When are you watching paint dry again?`,
|
||||
`Insert Girls Kissing`,
|
||||
`We luub bo`,
|
||||
`MOOD DOWN`,
|
||||
`All this clicking in this game wont give you your money back from those horse races`,
|
||||
`UMAMUSUME PRETTY DERBY UPDATE : NEW SSSR IS ANNOUNCED - ITS [REDACTED]`,
|
||||
`LEts' get maried bo`,
|
||||
`My AI autocomplete on my code editor is shipping you with Reya for some reason. It literally completed Reya in this sentence`,
|
||||
`DUDE`,
|
||||
`There's a pipebomb in your DMs`,
|
||||
`om`,
|
||||
`You are the Glue to my life like how Tokai Teio is glue(stick) to a child's art project`,
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'secretVideo',
|
||||
name: '🎬 Secret Video',
|
||||
description: 'Unlock a secret video. Plays automatically!',
|
||||
baseCost: 1000000, // Very expensive
|
||||
multiplier: 1,
|
||||
icon: '🎬',
|
||||
oneTime: true,
|
||||
youtubeId: 'ONzntmMFXGE' // The YouTube video ID
|
||||
}
|
||||
];
|
||||
interface SolveRatPoisonMessage extends AuthenticatedMessage {
|
||||
type: 'solve-rat-poison';
|
||||
challengeString: string;
|
||||
}
|
||||
|
||||
type Message = ClickMessage | PurchaseUpgradeMessage | ApplyMultiplierBonusMessage | UserJoinMessage | AdminBroadcastMessage | EditUserMessage | ThrowRatPoisonMessage | SolveRatPoisonMessage; // Updated Message type
|
||||
|
||||
import { UPGRADES as ALL_UPGRADES } from '../src/config/upgrades';
|
||||
|
||||
const UPGRADES = ALL_UPGRADES.filter(upgrade => !upgrade.oneTime);
|
||||
const ONE_TIME_UPGRADES = ALL_UPGRADES.filter(upgrade => upgrade.oneTime);
|
||||
|
||||
const MILESTONES = [
|
||||
{ threshold: 1000, id: 'first-thousand', background: 'rainbow', image: 'https://media1.tenor.com/m/x8v1oNUOmg4AAAAd/spinning-rat-rat.gif' },
|
||||
@@ -325,12 +209,14 @@ export default class GameServer implements Party.Server {
|
||||
clicks: 0,
|
||||
lastSeen: Date.now(),
|
||||
bonusMultiplier: 1, // Initialize bonus multiplier
|
||||
upgrades: UPGRADES.reduce((acc, upgrade) => {
|
||||
upgrades: ALL_UPGRADES.reduce((acc, upgrade) => {
|
||||
acc[upgrade.id] = { owned: 0, cost: upgrade.baseCost };
|
||||
return acc;
|
||||
}, {} as Record<string, { owned: number; cost: number }>),
|
||||
clickMultiplier: 1,
|
||||
autoClickRate: 0,
|
||||
ratPoisonChallenge: undefined,
|
||||
ratPoisonImmunityUntil: undefined,
|
||||
};
|
||||
}
|
||||
this.gameState.users[currentUserId].lastSeen = Date.now();
|
||||
@@ -383,6 +269,20 @@ export default class GameServer implements Party.Server {
|
||||
console.warn(`Unauthorized edit user attempt from ${currentUserId}.`);
|
||||
}
|
||||
break;
|
||||
case 'throw-rat-poison':
|
||||
if (isAuthenticated) {
|
||||
this.handleThrowRatPoison(data, currentUserId);
|
||||
} else {
|
||||
console.warn(`Unauthenticated throw-rat-poison from ${currentUserId} ignored.`);
|
||||
}
|
||||
break;
|
||||
case 'solve-rat-poison':
|
||||
if (isAuthenticated) {
|
||||
this.handleSolveRatPoison(data, currentUserId);
|
||||
} else {
|
||||
console.warn(`Unauthenticated solve-rat-poison from ${currentUserId} ignored.`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
this.broadcast();
|
||||
@@ -399,6 +299,127 @@ export default class GameServer implements Party.Server {
|
||||
}
|
||||
}
|
||||
|
||||
handleThrowRatPoison(data: ThrowRatPoisonMessage, authenticatedUserId: string) {
|
||||
const { targetUserId } = data;
|
||||
const targetUserState = this.gameState.users[targetUserId];
|
||||
const attackerUserState = this.gameState.users[authenticatedUserId];
|
||||
|
||||
if (!targetUserState || !attackerUserState || targetUserId === authenticatedUserId) {
|
||||
return; // Cannot poison self or non-existent user
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
if (targetUserState.ratPoisonImmunityUntil && targetUserState.ratPoisonImmunityUntil > now) {
|
||||
// Target is immune, notify attacker
|
||||
const attackerConn = this.userConnections.get(authenticatedUserId);
|
||||
if (attackerConn) {
|
||||
attackerConn.send(JSON.stringify({
|
||||
type: 'rat-poison-feedback',
|
||||
message: `${targetUserState.name} is currently immune to rat poison!`
|
||||
}));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Get news headlines for challenge string
|
||||
const newsUpgrade = ALL_UPGRADES.find(u => u.id === 'news');
|
||||
const newsTitles = newsUpgrade?.newsTitles || [];
|
||||
if (newsTitles.length === 0) {
|
||||
console.warn('No news titles found for rat poison challenge.');
|
||||
return;
|
||||
}
|
||||
|
||||
const challengeString = newsTitles[Math.floor(Math.random() * newsTitles.length)];
|
||||
const challengeDuration = 10 * 1000; // 10 seconds to solve
|
||||
const expiresAt = now + challengeDuration;
|
||||
|
||||
targetUserState.ratPoisonChallenge = {
|
||||
challengeString,
|
||||
expiresAt,
|
||||
};
|
||||
|
||||
// Notify target user to start challenge
|
||||
const targetConn = this.userConnections.get(targetUserId);
|
||||
if (targetConn) {
|
||||
targetConn.send(JSON.stringify({
|
||||
type: 'rat-poison-challenge',
|
||||
challenge: challengeString,
|
||||
expiresAt,
|
||||
}));
|
||||
}
|
||||
|
||||
// Set a timeout for the challenge to expire if not solved
|
||||
setTimeout(() => {
|
||||
if (targetUserState.ratPoisonChallenge && targetUserState.ratPoisonChallenge.expiresAt === expiresAt) {
|
||||
// Challenge not solved in time
|
||||
this.applyRatPoisonPenalty(targetUserId);
|
||||
this.broadcast();
|
||||
}
|
||||
}, challengeDuration + 500); // Give a small buffer
|
||||
|
||||
this.broadcast();
|
||||
}
|
||||
|
||||
handleSolveRatPoison(data: SolveRatPoisonMessage, authenticatedUserId: string) {
|
||||
const userState = this.gameState.users[authenticatedUserId];
|
||||
if (!userState || !userState.ratPoisonChallenge) {
|
||||
return; // No active challenge
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
if (now > userState.ratPoisonChallenge.expiresAt) {
|
||||
// Challenge expired
|
||||
this.applyRatPoisonPenalty(authenticatedUserId);
|
||||
} else if (data.challengeString === userState.ratPoisonChallenge.challengeString) {
|
||||
// Challenge solved successfully
|
||||
userState.ratPoisonImmunityUntil = now + (2 * 60 * 1000); // 2 minutes immunity
|
||||
const userConn = this.userConnections.get(authenticatedUserId);
|
||||
if (userConn) {
|
||||
userConn.send(JSON.stringify({
|
||||
type: 'rat-poison-result',
|
||||
success: true,
|
||||
message: 'Challenge solved! You are immune for 2 minutes.'
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
// Challenge failed (wrong string)
|
||||
this.applyRatPoisonPenalty(authenticatedUserId);
|
||||
const userConn = this.userConnections.get(authenticatedUserId);
|
||||
if (userConn) {
|
||||
userConn.send(JSON.stringify({
|
||||
type: 'rat-poison-result',
|
||||
success: false,
|
||||
message: 'Incorrect string! You lost clicks and are immune for 20 seconds.'
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
userState.ratPoisonChallenge = undefined; // Clear the challenge
|
||||
this.broadcast();
|
||||
}
|
||||
|
||||
applyRatPoisonPenalty(userId: string) {
|
||||
const userState = this.gameState.users[userId];
|
||||
if (!userState) return;
|
||||
|
||||
const clicksLost = Math.floor(userState.clicks * 0.30);
|
||||
userState.clicks -= clicksLost;
|
||||
userState.ratPoisonImmunityUntil = Date.now() + (20 * 1000); // 20 seconds immunity
|
||||
|
||||
const userConn = this.userConnections.get(userId);
|
||||
if (userConn) {
|
||||
userConn.send(JSON.stringify({
|
||||
type: 'rat-poison-result',
|
||||
success: false,
|
||||
message: `You failed the challenge and lost ${clicksLost} clicks! You are immune for 20 seconds.`
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Define a minimum interval between clicks (e.g., 100ms for 10 clicks/second)
|
||||
// This helps prevent simple auto-clicker scripts by rate-limiting server-side.
|
||||
private readonly MIN_CLICK_INTERVAL = 50; // milliseconds
|
||||
|
||||
// handleUserJoin is now fully integrated into onMessage and can be removed or simplified.
|
||||
// Removing it as its logic is now directly in onMessage.
|
||||
// handleUserJoin(data: UserJoinMessage) {
|
||||
@@ -407,10 +428,6 @@ export default class GameServer implements Party.Server {
|
||||
// // No explicit action needed here beyond what onMessage does.
|
||||
// }
|
||||
|
||||
// Define a minimum interval between clicks (e.g., 100ms for 10 clicks/second)
|
||||
// This helps prevent simple auto-clicker scripts by rate-limiting server-side.
|
||||
private readonly MIN_CLICK_INTERVAL = 50; // milliseconds
|
||||
|
||||
handleClick(data: ClickMessage, authenticatedUserId: string) {
|
||||
const now = Date.now();
|
||||
const userState = this.gameState.users[authenticatedUserId];
|
||||
|
||||
Reference in New Issue
Block a user