Start repository
This commit is contained in:
183
party/index.ts
Normal file
183
party/index.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
interface GameState {
|
||||
totalClicks: number;
|
||||
users: Record<string, { name: string; clicks: number; lastSeen: number }>;
|
||||
upgrades: Record<string, { owned: number; cost: number }>;
|
||||
milestones: Record<string, boolean>;
|
||||
clickMultiplier: number;
|
||||
autoClickRate: number;
|
||||
currentBackground: string;
|
||||
currentClickImage: string;
|
||||
}
|
||||
|
||||
interface ClickMessage {
|
||||
type: 'click';
|
||||
userId: string;
|
||||
userName: string;
|
||||
}
|
||||
|
||||
interface PurchaseUpgradeMessage {
|
||||
type: 'purchase-upgrade';
|
||||
userId: string;
|
||||
upgradeId: string;
|
||||
}
|
||||
|
||||
interface UserJoinMessage {
|
||||
type: 'user-join';
|
||||
userId: string;
|
||||
userName: string;
|
||||
}
|
||||
|
||||
type Message = ClickMessage | PurchaseUpgradeMessage | UserJoinMessage;
|
||||
|
||||
const UPGRADES = {
|
||||
clickMultiplier: { baseCost: 10, multiplier: 1.5, clickBonus: 1 },
|
||||
autoClicker: { baseCost: 50, multiplier: 2, autoClickRate: 1 },
|
||||
megaBonus: { baseCost: 200, multiplier: 2.5, clickBonus: 5 },
|
||||
hyperClicker: { baseCost: 1000, multiplier: 3, autoClickRate: 10 },
|
||||
quantumClicker: { baseCost: 5000, multiplier: 4, clickBonus: 50 }
|
||||
};
|
||||
|
||||
const MILESTONES = [
|
||||
{ threshold: 100, id: 'first-hundred', background: 'rainbow', image: 'https://media1.tenor.com/m/x8v1oNUOmg4AAAAd/spinning-rat-rat.gif' },
|
||||
{ threshold: 500, id: 'five-hundred', background: 'matrix', image: 'https://media1.tenor.com/m/pV74fmh_NLgAAAAd/louie-rat-spinning-rat.gif' },
|
||||
{ threshold: 1000, id: 'one-thousand', background: 'cyberpunk', image: 'https://media1.tenor.com/m/YsWlbVbRWFQAAAAd/rat-spinning.gif' },
|
||||
{ threshold: 2500, id: 'epic-milestone', background: 'space', image: 'https://media1.tenor.com/m/x8v1oNUOmg4AAAAd/spinning-rat-rat.gif' },
|
||||
{ threshold: 5000, id: 'legendary', background: 'glitch', image: 'https://media1.tenor.com/m/pV74fmh_NLgAAAAd/louie-rat-spinning-rat.gif' },
|
||||
{ threshold: 10000, id: 'ultimate', background: 'ultimate', image: 'https://media1.tenor.com/m/YsWlbVbRWFQAAAAd/rat-spinning.gif' }
|
||||
];
|
||||
|
||||
export default class GameServer implements Party.Server {
|
||||
constructor(readonly party: Party.Party) {}
|
||||
|
||||
gameState: GameState = {
|
||||
totalClicks: 0,
|
||||
users: {},
|
||||
upgrades: Object.keys(UPGRADES).reduce((acc, key) => {
|
||||
acc[key] = { owned: 0, cost: UPGRADES[key as keyof typeof UPGRADES].baseCost };
|
||||
return acc;
|
||||
}, {} as Record<string, { owned: number; cost: number }>),
|
||||
milestones: {},
|
||||
clickMultiplier: 1,
|
||||
autoClickRate: 0,
|
||||
currentBackground: 'default',
|
||||
currentClickImage: 'https://media1.tenor.com/m/pV74fmh_NLgAAAAd/louie-rat-spinning-rat.gif'
|
||||
};
|
||||
|
||||
autoClickInterval?: NodeJS.Timeout;
|
||||
|
||||
onConnect(conn: Party.Connection, ctx: Party.ConnectionContext) {
|
||||
conn.send(JSON.stringify({ type: 'game-state', state: this.gameState }));
|
||||
}
|
||||
|
||||
onMessage(message: string, sender: Party.Connection) {
|
||||
const data = JSON.parse(message) as Message;
|
||||
|
||||
switch (data.type) {
|
||||
case 'user-join':
|
||||
this.handleUserJoin(data);
|
||||
break;
|
||||
case 'click':
|
||||
this.handleClick(data);
|
||||
break;
|
||||
case 'purchase-upgrade':
|
||||
this.handlePurchaseUpgrade(data);
|
||||
break;
|
||||
}
|
||||
|
||||
this.broadcast();
|
||||
}
|
||||
|
||||
handleUserJoin(data: UserJoinMessage) {
|
||||
if (!this.gameState.users[data.userId]) {
|
||||
this.gameState.users[data.userId] = {
|
||||
name: data.userName,
|
||||
clicks: 0,
|
||||
lastSeen: Date.now()
|
||||
};
|
||||
}
|
||||
this.gameState.users[data.userId].lastSeen = Date.now();
|
||||
}
|
||||
|
||||
handleClick(data: ClickMessage) {
|
||||
const clickValue = this.gameState.clickMultiplier;
|
||||
|
||||
this.gameState.totalClicks += clickValue;
|
||||
|
||||
if (!this.gameState.users[data.userId]) {
|
||||
this.gameState.users[data.userId] = {
|
||||
name: data.userName,
|
||||
clicks: 0,
|
||||
lastSeen: Date.now()
|
||||
};
|
||||
}
|
||||
|
||||
this.gameState.users[data.userId].clicks += clickValue;
|
||||
this.gameState.users[data.userId].lastSeen = Date.now();
|
||||
|
||||
this.checkMilestones();
|
||||
}
|
||||
|
||||
handlePurchaseUpgrade(data: PurchaseUpgradeMessage) {
|
||||
const upgrade = UPGRADES[data.upgradeId as keyof typeof UPGRADES];
|
||||
const currentUpgrade = this.gameState.upgrades[data.upgradeId];
|
||||
|
||||
if (!upgrade || !currentUpgrade) return;
|
||||
|
||||
const userClicks = this.gameState.users[data.userId]?.clicks || 0;
|
||||
|
||||
if (userClicks >= currentUpgrade.cost) {
|
||||
this.gameState.users[data.userId].clicks -= currentUpgrade.cost;
|
||||
currentUpgrade.owned += 1;
|
||||
currentUpgrade.cost = Math.floor(upgrade.baseCost * Math.pow(upgrade.multiplier, currentUpgrade.owned));
|
||||
|
||||
this.updateGameMultipliers();
|
||||
}
|
||||
}
|
||||
|
||||
updateGameMultipliers() {
|
||||
this.gameState.clickMultiplier = 1;
|
||||
this.gameState.autoClickRate = 0;
|
||||
|
||||
Object.entries(this.gameState.upgrades).forEach(([upgradeId, upgrade]) => {
|
||||
const config = UPGRADES[upgradeId as keyof typeof UPGRADES];
|
||||
if (config.clickBonus) {
|
||||
this.gameState.clickMultiplier += config.clickBonus * upgrade.owned;
|
||||
}
|
||||
if (config.autoClickRate) {
|
||||
this.gameState.autoClickRate += config.autoClickRate * upgrade.owned;
|
||||
}
|
||||
});
|
||||
|
||||
this.setupAutoClicker();
|
||||
}
|
||||
|
||||
setupAutoClicker() {
|
||||
if (this.autoClickInterval) {
|
||||
clearInterval(this.autoClickInterval);
|
||||
}
|
||||
|
||||
if (this.gameState.autoClickRate > 0) {
|
||||
this.autoClickInterval = setInterval(() => {
|
||||
this.gameState.totalClicks += this.gameState.autoClickRate;
|
||||
this.checkMilestones();
|
||||
this.broadcast();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
checkMilestones() {
|
||||
MILESTONES.forEach(milestone => {
|
||||
if (this.gameState.totalClicks >= milestone.threshold && !this.gameState.milestones[milestone.id]) {
|
||||
this.gameState.milestones[milestone.id] = true;
|
||||
this.gameState.currentBackground = milestone.background;
|
||||
this.gameState.currentClickImage = milestone.image;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
broadcast() {
|
||||
this.party.broadcast(JSON.stringify({ type: 'game-state', state: this.gameState }));
|
||||
}
|
||||
}
|
||||
|
||||
GameServer satisfies Party.Worker;
|
||||
Reference in New Issue
Block a user