From 157e4768f363d84dbf230dc76fc5fd4e2b09c30e Mon Sep 17 00:00:00 2001 From: MasterGordon Date: Tue, 15 Oct 2024 21:06:36 +0200 Subject: [PATCH] added cases --- backend/controller/userController.ts | 36 ++++- bun.lockb | Bin 228807 -> 244961 bytes package.json | 4 + shared/events.ts | 8 ++ shared/lootboxes.ts | 16 ++- shared/utils.ts | 16 +-- src/Shell.tsx | 2 +- src/atoms.ts | 5 + src/components/Board.tsx | 11 ++ src/components/Dialog.tsx | 37 ++--- src/components/Feed/Feed.tsx | 25 +++- src/components/Feed/FeedItem.tsx | 26 +++- src/components/LeaderboardButton.tsx | 26 ++-- src/components/Rarity.tsx | 22 +++ src/index.css | 4 + src/views/collection/Collection.tsx | 61 +++++---- src/views/store/Store.tsx | 196 ++++++++++++++++++++++++++- src/wsClient.ts | 4 +- vite.config.ts | 3 + 19 files changed, 419 insertions(+), 83 deletions(-) create mode 100644 src/components/Rarity.tsx diff --git a/backend/controller/userController.ts b/backend/controller/userController.ts index 6bee4bd..470fc89 100644 --- a/backend/controller/userController.ts +++ b/backend/controller/userController.ts @@ -11,11 +11,14 @@ import crypto from "crypto"; import { resetSessionUser, setSessionUser } from "../router"; import { userSettings } from "../../shared/user-settings"; import { UnauthorizedError } from "../errors/UnauthorizedError"; -import { getGems } from "../repositories/gemsRepository"; +import { getGems, removeGems } from "../repositories/gemsRepository"; import { getCollection, upsertCollection, } from "../repositories/collectionRepository"; +import { getWeight, lootboxes } from "../../shared/lootboxes"; +import { weightedPickRandom } from "../../shared/utils"; +import { emit } from "../events"; const secret = process.env.SECRET!; @@ -133,4 +136,35 @@ export const userController = createController({ await upsertCollection(db, user, collection); }, ), + openLootbox: createEndpoint( + z.object({ + id: z.string(), + }), + async ({ id }, { db, user }) => { + if (!user) throw new UnauthorizedError("Unauthorized"); + const collection = await getCollection(db, user); + const lootbox = lootboxes.find((l) => l.id === id); + if (!lootbox) { + throw new Error("Lootbox not found"); + } + removeGems(db, user, lootbox.price); + const result = weightedPickRandom(lootbox.items, (i) => + getWeight(i.rarity), + ); + console.log(result); + collection.entries.push({ + id: result.id, + aquired: Date.now(), + selected: false, + }); + await upsertCollection(db, user, collection); + emit({ + type: "lootboxPurchased", + user, + lootbox: lootbox.id, + reward: result.id, + rarity: result.rarity, + }); + }, + ), }); diff --git a/bun.lockb b/bun.lockb index 352fdc6dfe7d3e395691227cb2287db0886deb6e..d0091f2fc444b6b3ba254d1b386a53d2f72aa3dc 100755 GIT binary patch delta 52936 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 delta 42566 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 diff --git a/package.json b/package.json index 65d4d20..fd22354 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,10 @@ "@radix-ui/react-switch": "^1.1.1", "@tanstack/react-query": "^5.59.11", "@tanstack/react-query-devtools": "^5.59.11", + "@tsparticles/engine": "^3.5.0", + "@tsparticles/preset-sea-anemone": "^3.1.0", + "@tsparticles/react": "^3.0.0", + "@tsparticles/slim": "^3.5.0", "@uidotdev/usehooks": "^2.4.1", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", diff --git a/shared/events.ts b/shared/events.ts index fe7dda5..68361ad 100644 --- a/shared/events.ts +++ b/shared/events.ts @@ -1,3 +1,4 @@ +import type { Rarity } from "../shared/lootboxes"; export type EventType = "new" | "finished" | "updateGame" | "updateStage"; export type Events = @@ -25,4 +26,11 @@ export type Events = type: "gemsRewarded"; stage: number; gems: number; + } + | { + type: "lootboxPurchased"; + lootbox: string; + reward: string; + user: string; + rarity: Rarity; }; diff --git a/shared/lootboxes.ts b/shared/lootboxes.ts index cddfe4a..f24278f 100644 --- a/shared/lootboxes.ts +++ b/shared/lootboxes.ts @@ -1,4 +1,5 @@ import type { themes } from "../src/themes"; +import lootbox1 from "../src/assets/illustrations/lootbox1.png?w=360&inline"; export const rarities = [ { @@ -23,11 +24,18 @@ export const rarities = [ }, ] as const; -type Rarity = (typeof rarities)[number]["id"]; +export const getWeight = (rarity: Rarity) => + rarities.find((r) => r.id === rarity)?.weight ?? 0; + +export type Rarity = (typeof rarities)[number]["id"]; type ThemeId = (typeof themes)[number]["id"]; interface Lootbox { name: string; + id: string; + price: number; + priceText: string; + image: string; items: { id: ThemeId; rarity: Rarity; @@ -36,6 +44,10 @@ interface Lootbox { export const series1: Lootbox = { name: "Series 1", + id: "series1", + price: 5000, + priceText: "5.000", + image: lootbox1, items: [ { id: "basic", @@ -195,3 +207,5 @@ export const series1: Lootbox = { }, ], }; + +export const lootboxes = [series1]; diff --git a/shared/utils.ts b/shared/utils.ts index 794778b..ac48d73 100644 --- a/shared/utils.ts +++ b/shared/utils.ts @@ -3,11 +3,6 @@ export const pickRandom = (arr: T[]) => { return arr[index]; }; -interface WeightedEntry { - weight: number; - value: T; -} - export const hashStr = (str: string) => { return [...str].reduce( (hash, c) => (Math.imul(31, hash) + c.charCodeAt(0)) | 0, @@ -16,18 +11,19 @@ export const hashStr = (str: string) => { }; export const weightedPickRandom = ( - arr: WeightedEntry[], + arr: T[], + getWeight: (item: T) => number = () => 1, getRandom: (tw: number) => number = (totalWeight) => Math.random() * totalWeight, ): T => { - const totalWeight = arr.reduce((acc, cur) => acc + cur.weight, 0); + const totalWeight = arr.reduce((acc, cur) => acc + getWeight(cur), 0); const random = getRandom(totalWeight); let currentWeight = 0; for (const entry of arr) { - currentWeight += entry.weight; + currentWeight += getWeight(entry); if (random < currentWeight) { - return entry.value; + return entry; } } - return arr[arr.length - 1].value; + return arr[arr.length - 1]; }; diff --git a/src/Shell.tsx b/src/Shell.tsx index f7e10a2..dead12d 100644 --- a/src/Shell.tsx +++ b/src/Shell.tsx @@ -90,7 +90,7 @@ const Shell: React.FC = ({ children }) => {
-
+ {/*
*/}
diff --git a/src/atoms.ts b/src/atoms.ts index fcc2526..6331c7f 100644 --- a/src/atoms.ts +++ b/src/atoms.ts @@ -10,3 +10,8 @@ export const loginTokenAtom = atomWithStorage( export const cursorXAtom = atom(0); export const cursorYAtom = atom(0); export const feedItemsAtom = atom([]); +interface LootboxResult { + result: string; + lootbox: string; +} +export const lootboxResultAtom = atom(); diff --git a/src/components/Board.tsx b/src/components/Board.tsx index 6b9274f..37dcd96 100644 --- a/src/components/Board.tsx +++ b/src/components/Board.tsx @@ -136,6 +136,17 @@ const Board: React.FC = (props) => { }); } }, [ref]); + useEffect(() => { + const listener = (e: KeyboardEvent) => { + if (e.key === "Escape") { + setZenMode(false); + } + }; + document.addEventListener("keydown", listener); + return () => { + document.removeEventListener("keydown", listener); + }; + }, []); return (
diff --git a/src/components/Dialog.tsx b/src/components/Dialog.tsx index 8aa2f6c..97f6b15 100644 --- a/src/components/Dialog.tsx +++ b/src/components/Dialog.tsx @@ -28,24 +28,27 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; const DialogContent = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( + React.ComponentPropsWithoutRef & { + overlayClassName?: string; + } +>(({ className, children, overlayClassName, ...props }, ref) => ( - - - {children} - - - Close - - + + + {children} + + + Close + + + )); DialogContent.displayName = DialogPrimitive.Content.displayName; diff --git a/src/components/Feed/Feed.tsx b/src/components/Feed/Feed.tsx index f151c08..5837c01 100644 --- a/src/components/Feed/Feed.tsx +++ b/src/components/Feed/Feed.tsx @@ -1,6 +1,6 @@ import { AnimatePresence, motion } from "framer-motion"; import { useAtom } from "jotai"; -import { feedItemsAtom } from "../../atoms"; +import { feedItemsAtom, lootboxResultAtom } from "../../atoms"; import FeedItemElement from "./FeedItem"; import { useEffect } from "react"; import { addMessageListener, removeMessageListener } from "../../wsClient"; @@ -9,6 +9,7 @@ import { useWSQuery } from "../../hooks"; const Feed: React.FC = () => { const [items, setItems] = useAtom(feedItemsAtom); + const [, setLootboxResult] = useAtom(lootboxResultAtom); const { data: user } = useWSQuery("user.getSelf", null); useEffect(() => { @@ -19,7 +20,7 @@ const Feed: React.FC = () => { }, [setItems]); useEffect(() => { - const listener = (event: MessageEvent) => { + const listener = async (event: MessageEvent) => { const data = JSON.parse(event.data) as Events; const newItems = [...items]; if (data.type === "new" && data.user !== user) { @@ -49,11 +50,29 @@ const Feed: React.FC = () => { gems: data.gems, }); } + if (data.type === "lootboxPurchased" && data.user !== user) { + newItems.push({ + type: "lootboxPurchased", + id: crypto.randomUUID(), + decay: Date.now() + 20_000, + lootbox: data.lootbox, + user: data.user, + reward: data.reward, + rarity: data.rarity, + }); + await new Promise((res) => setTimeout(res, 2_000)); + } + if (data.type === "lootboxPurchased" && data.user === user) { + setLootboxResult({ + lootbox: data.lootbox, + result: data.reward, + }); + } setItems(newItems); }; addMessageListener(listener); return () => removeMessageListener(listener); - }, [items, setItems, user]); + }, [items, setItems, setLootboxResult, user]); return (
diff --git a/src/components/Feed/FeedItem.tsx b/src/components/Feed/FeedItem.tsx index a97a996..aa72434 100644 --- a/src/components/Feed/FeedItem.tsx +++ b/src/components/Feed/FeedItem.tsx @@ -2,6 +2,9 @@ import { motion } from "framer-motion"; import { PropsWithChildren } from "react"; import { formatTimeSpan } from "../../../shared/time"; import GemsIcon from "../GemIcon"; +import { Rarity as RarityType } from "../../../shared/lootboxes"; +import { Rarity } from "../Rarity"; +import { themes } from "../../themes"; interface BaseFeedItem { decay: number; @@ -26,7 +29,19 @@ interface GemsEarnedItem extends BaseFeedItem { stage: number; } -export type FeedItem = GameStartedItem | GameFinishedItem | GemsEarnedItem; +interface LootboxPurchasedItem extends BaseFeedItem { + type: "lootboxPurchased"; + lootbox: string; + user: string; + reward: string; + rarity: RarityType; +} + +export type FeedItem = + | GameStartedItem + | GameFinishedItem + | GemsEarnedItem + | LootboxPurchasedItem; const FeedItemWrapper: React.FC = ({ children }) => { return ( @@ -59,6 +74,15 @@ const FeedItemElement: React.FC<{ item: FeedItem }> = ({ item }) => { You got {item.gems} for stage {item.stage} ); + case "lootboxPurchased": + return ( + + {item.user} got{" "} + + {themes.find((i) => i.id == item.reward)?.name} + + + ); } }; diff --git a/src/components/LeaderboardButton.tsx b/src/components/LeaderboardButton.tsx index 65995c0..2bfb4b9 100644 --- a/src/components/LeaderboardButton.tsx +++ b/src/components/LeaderboardButton.tsx @@ -27,20 +27,20 @@ const LeaderboardButton = ({ Leaderboard -
- {leaderboard?.map((_, i) => ( - -
{i + 1}.
-
- {leaderboard?.[i]?.user ?? "No User"} -
-
- Stage {leaderboard?.[i]?.stage ?? 0} -
-
- ))} -
+
+ {leaderboard?.map((_, i) => ( + +
{i + 1}.
+
+ {leaderboard?.[i]?.user ?? "No User"} +
+
+ Stage {leaderboard?.[i]?.stage ?? 0} +
+
+ ))} +
); diff --git a/src/components/Rarity.tsx b/src/components/Rarity.tsx new file mode 100644 index 0000000..4e19d4b --- /dev/null +++ b/src/components/Rarity.tsx @@ -0,0 +1,22 @@ +import { PropsWithChildren } from "react"; +import { cn } from "../lib/utils"; +import { rarities } from "../../shared/lootboxes"; + +export const Rarity: React.FC> = ({ + rarity, + children, +}) => { + return ( + + {children ?? rarities.find((r) => r.id === rarity)?.name} + + ); +}; diff --git a/src/index.css b/src/index.css index 8717290..31b1d71 100644 --- a/src/index.css +++ b/src/index.css @@ -4,6 +4,10 @@ --color-primary: #D9AFD9; --color-input: color-mix(in srgb, var(--color-white, #fff) 20%, transparent); --color-background: black; + --color-common: var(--color-sky-500); + --color-uncommon: var(--color-green-400); + --color-rare: var(--color-red-500); + --color-legendary: var(--color-amber-500); --bg-brand: -webkit-linear-gradient(225deg, rgb(251, 175, 21), rgb(251, 21, 242), rgb(21, 198, 251)) 0% 0% / 100% 300%; --bg-secondary: linear-gradient(90deg, #D9AFD9 0%, #97D9E1 100%) 0% 0% / 100% 300%; diff --git a/src/views/collection/Collection.tsx b/src/views/collection/Collection.tsx index 2c175e8..af305aa 100644 --- a/src/views/collection/Collection.tsx +++ b/src/views/collection/Collection.tsx @@ -28,9 +28,8 @@ const Collection = () => { const selected = collection?.entries.some( (e) => e.id === theme.id && e.selected, ); - const owned = collection?.entries.some( - (e) => e.id === theme.id && e.selected, - ); + const owned = collection?.entries.some((e) => e.id === theme.id); + if (!owned) return null; return (
@@ -40,32 +39,36 @@ const Collection = () => { (Owned) )} - - - - - - { - mutateSelected - .mutateAsync({ id: theme.id }) - .then(() => refetch()); - }} - > - Select - - - mutateShuffle.mutateAsync({ id: theme.id }) - } - > - {" "} - Add to shuffle - - - + {owned && ( + + + + + + { + mutateSelected + .mutateAsync({ id: theme.id }) + .then(() => refetch()); + }} + > + Select + + + mutateShuffle + .mutateAsync({ id: theme.id }) + .then(() => refetch()) + } + > + {" "} + Add to shuffle + + + + )}
{ + const openLootbox = useWSMutation("user.openLootbox"); + const [lootboxResult, setLootboxResult] = useAtom(lootboxResultAtom); + const currentLootbox = lootboxes.find((l) => l.id === lootboxResult?.lootbox); + + // this should be run only once per application lifetime + useEffect(() => { + initParticlesEngine(async (engine) => { + // you can initiate the tsParticles instance (engine) here, adding custom shapes or presets + // this loads the tsparticles package bundle, it's the easiest method for getting everything ready + // starting from v2 you can add only the features you need reducing the bundle size + //await loadAll(engine); + //await loadFull(engine); + await loadSlim(engine); + await loadSeaAnemonePreset(engine); + + //await loadBasic(engine); + }); + }, []); + return ( -
-

Store

-
-
-

Lootboxes

+ <> + setLootboxResult(undefined)} + > + + + + Opening {currentLootbox?.name} + + +
+ + + i.id == lootboxResult?.result, + )?.rarity + } + > + {themes.find((t) => t.id === lootboxResult?.result)?.name} + + +
+
+
+
+

Store

+
+
+ {lootboxes.map((lootbox) => ( +
+
+

{lootbox.name}

+ + + + + + + {lootbox.name} + +
+
+ +
+ Introducing {lootbox.name}, the first-ever + lootbox for Minesweeper, packed with a variety of + stylish skins to customize your game like never + before! With {lootbox.name}, every click + becomes a statement, as you explore new looks for + your mines, tiles, and flags. Transform your + Minesweeper grid with these visually captivating + designs and make each puzzle feel fresh and + exciting. +
+

+ What's Inside? +

+
    +
  • + Mine Skins: Swap out the classic mine icons + for creative alternatives like skulls, treasure + chests, or high-tech drones. +
  • +
  • + Tile Skins: Replace the plain tiles with + vibrant patterns such as tropical beaches, + medieval bricks, or sleek metallic plates. +
  • +
  • + Flag Skins: Mark suspected mines in style + with custom flag designs including pirate flags, + futuristic beacons, or glowing crystals. +
  • +
  • + Backgrounds: Enhance your gameplay + experience with unique backgrounds, from serene + mountain landscapes to bustling cityscapes or + outer space vistas. +
  • +
    + Step up your Minesweeper game and express your + personality with {lootbox.name}. Unlock new + looks and turn every game into a visual + masterpiece! +
    +
+
+
+
+
Skin
+
Rarity
+ {lootbox.items.map((item) => ( + +
+ {themes.find((t) => t.id === item.id)?.name} +
+ +
+ ))} +
+
+
+
+
+
+ + +
+ ))} +
-
+ ); }; diff --git a/src/wsClient.ts b/src/wsClient.ts index bde07e7..edd4f36 100644 --- a/src/wsClient.ts +++ b/src/wsClient.ts @@ -56,9 +56,11 @@ const createWSClient = () => { ): Promise>> => { if (ws.readyState !== WebSocket.OPEN) { await new Promise((res) => { - ws.onopen = () => { + const onOpen = () => { + ws.removeEventListener("open", onOpen); res(); }; + ws.addEventListener("open", onOpen); }); } const requestId = crypto.randomUUID(); diff --git a/vite.config.ts b/vite.config.ts index 9163aca..9ffaf19 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -5,5 +5,8 @@ import { imagetools } from "vite-imagetools"; // https://vitejs.dev/config/ export default defineConfig({ + server: { + port: 3003, + }, plugins: [react(), tailwindcss(), imagetools()], });