This commit is contained in:
CutieCat2804 2024-10-18 17:09:56 +02:00
commit 2efb88787f
6 changed files with 104 additions and 27 deletions

View File

@ -15,6 +15,7 @@ const requestDuration = new Histogram({
help: "Request duration",
labelNames: ["action"],
});
// promClient.register.registerMetric(requestDuration);
const metricsUser = process.env.METRICS_USER;
const metricsPassword = process.env.METRICS_PASSWORD;

View File

@ -3,11 +3,19 @@ export const pickRandom = <T>(arr: T[]) => {
return arr[index];
};
function bashHashStr(str: string) {
let hash = 5381,
i = str.length;
while (i) {
hash = (hash * 33) ^ str.charCodeAt(--i);
}
return hash >>> 0;
}
export const hashStr = (str: string) => {
return [...str].reduce(
(hash, c) => (Math.imul(31, hash) + c.charCodeAt(0)) | 0,
0,
);
return Number(`0.${bashHashStr(str)}`);
};
export const weightedPickRandom = <T>(

View File

@ -1,20 +1,25 @@
import {
ReactNode,
type ReactNode,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from "react";
import { LoadedTheme, Theme, useTheme } from "../themes/Theme";
import {
type LoadedTexture,
type LoadedTheme,
type Theme,
useTheme,
} from "../themes/Theme";
import { Container, Sprite, Stage, useTick } from "@pixi/react";
import Viewport from "./pixi/PixiViewport";
import type { Viewport as PixiViewport } from "pixi-viewport";
import {
ClientGame,
type ClientGame,
getValue,
isServerGame,
ServerGame,
type ServerGame,
} from "../../shared/game";
import { useWSQuery } from "../hooks";
import { Texture } from "pixi.js";
@ -37,6 +42,7 @@ import "@pixi/canvas-sprite-tiling";
import "@pixi/canvas-sprite";
import "@pixi/canvas-text";
import { themes } from "../themes";
import { hashStr, weightedPickRandom } from "../../shared/utils";
interface BoardProps {
className?: string;
@ -271,6 +277,20 @@ const Tile = ({
onRightClick,
onLeftClick,
}: TileProps) => {
const resolveSprite = useCallback(
(lt: LoadedTexture) => {
if (Array.isArray(lt)) {
console.log("hash:", hashStr(game.uuid + ";" + x + ";" + y));
return weightedPickRandom(
lt,
(i) => i.weight,
(tw) => hashStr(game.uuid + ";" + x + ";" + y) * tw,
).sprite;
}
return lt;
},
[game.uuid, x, y],
);
const i = x;
const j = y;
const isRevealed = game.isRevealed[i][j];
@ -285,11 +305,13 @@ const Tile = ({
const isQuestionMark = game.isQuestionMark[i][j];
const base =
isRevealed || (isMine && !isFlagged) ? (
<Sprite key="b" texture={theme.revealed} />
<Sprite key="b" texture={resolveSprite(theme.revealed)} />
) : (
<Sprite key="b" texture={theme.tile} />
<Sprite key="b" texture={resolveSprite(theme.tile)} />
);
const extra = isLastPos ? <Sprite key="e" texture={theme.lastPos} /> : null;
const extra = isLastPos ? (
<Sprite key="e" texture={resolveSprite(theme.lastPos)} />
) : null;
const touchStart = useRef<number>(0);
const isMove = useRef<boolean>(false);
const startX = useRef<number>(0);
@ -323,14 +345,24 @@ const Tile = ({
);
let content: ReactNode = null;
if (isFlagged) {
content = <Sprite key="c" texture={theme.flag} {...baseProps} />;
content = (
<Sprite key="c" texture={resolveSprite(theme.flag)} {...baseProps} />
);
} else if (isMine) {
content = <Sprite key="c" texture={theme.mine} {...baseProps} />;
content = (
<Sprite key="c" texture={resolveSprite(theme.mine)} {...baseProps} />
);
} else if (value !== -1 && isRevealed) {
const img = theme[value.toString() as keyof Theme] as Texture;
content = img ? <Sprite key="c" texture={img} {...baseProps} /> : null;
} else if (isQuestionMark) {
content = <Sprite key="c" texture={theme.questionMark} {...baseProps} />;
content = (
<Sprite
key="c"
texture={resolveSprite(theme.questionMark)}
{...baseProps}
/>
);
}
const [, setCursorX] = useAtom(cursorXAtom);
const [, setCursorY] = useAtom(cursorYAtom);

View File

@ -4,12 +4,17 @@ import { useEffect, useState } from "react";
type Png = typeof import("*.png");
type LazySprite = () => Promise<Png>;
interface WeightedLazySprites {
weight: number;
sprite: LazySprite;
}
export interface Theme {
size: number;
mine: LazySprite;
tile: LazySprite;
mine: LazySprite | WeightedLazySprites[];
tile: LazySprite | WeightedLazySprites[];
revealed: LazySprite;
flag: LazySprite;
flag: LazySprite | WeightedLazySprites[];
questionMark: LazySprite;
lastPos: LazySprite;
1: LazySprite;
@ -22,7 +27,17 @@ export interface Theme {
8: LazySprite;
}
export type LoadedTheme = Record<Exclude<keyof Theme, "size">, Texture> & {
export type LoadedTexture =
| Texture
| {
weight: number;
sprite: Texture;
}[];
export type LoadedTheme = Record<
Exclude<keyof Theme, "size">,
LoadedTexture
> & {
size: number;
};
@ -34,13 +49,25 @@ export const useTheme = (theme: Theme) => {
const loadTheme = async () => {
const loadedEntries = await Promise.all(
Object.entries(theme).map(async ([key, value]) => {
const loaded =
typeof value === "function"
? await Assets.load((await value()).default)
: value;
let loaded = value;
if (typeof value === "function") {
loaded = await Assets.load((await value()).default);
}
if (Array.isArray(value)) {
loaded = await Promise.all(
loaded.map(async (sprite: WeightedLazySprites) => {
return {
weight: sprite.weight,
sprite: await Assets.load((await sprite.sprite()).default),
};
}),
);
}
return [key, loaded] as const;
}),
);
console.log("loaded", Object.fromEntries(loadedEntries));
setLoadedTheme(Object.fromEntries(loadedEntries) as LoadedTheme);
};
loadTheme();

View File

@ -1,8 +1,17 @@
import { Theme } from "./Theme";
import type { Theme } from "./Theme";
export const techiesDireTheme: Theme = {
size: 32,
mine: () => import("../assets/themes/techies/dire/mine-1.png"),
mine: [
{
weight: 0.5,
sprite: () => import("../assets/themes/techies/dire/mine-1.png"),
},
{
weight: 0.5,
sprite: () => import("../assets/themes/techies/dire/mine-2.png"),
},
],
tile: () => import("../assets/themes/techies/dire/tile-1.png"),
revealed: () => import("../assets/themes/techies/dire/revealed.png"),
flag: () => import("../assets/themes/techies/flag.png"),

View File

@ -184,11 +184,11 @@ const Store = () => {
variant="outline"
size="default"
className="mx-auto items-center"
onClick={() =>
onClick={() => {
openLootbox
.mutateAsync({ id: lootbox.id })
.then(() => refetch())
}
.then(() => refetch());
}}
>
Buy for <b>{lootbox.priceText}</b> <GemsIcon />
</Button>