added theme weights
This commit is contained in:
parent
86fcef5ade
commit
1296848339
|
|
@ -15,6 +15,7 @@ const requestDuration = new Histogram({
|
||||||
help: "Request duration",
|
help: "Request duration",
|
||||||
labelNames: ["action"],
|
labelNames: ["action"],
|
||||||
});
|
});
|
||||||
|
// promClient.register.registerMetric(requestDuration);
|
||||||
|
|
||||||
const metricsUser = process.env.METRICS_USER;
|
const metricsUser = process.env.METRICS_USER;
|
||||||
const metricsPassword = process.env.METRICS_PASSWORD;
|
const metricsPassword = process.env.METRICS_PASSWORD;
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,19 @@ export const pickRandom = <T>(arr: T[]) => {
|
||||||
return arr[index];
|
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) => {
|
export const hashStr = (str: string) => {
|
||||||
return [...str].reduce(
|
return Number(`0.${bashHashStr(str)}`);
|
||||||
(hash, c) => (Math.imul(31, hash) + c.charCodeAt(0)) | 0,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const weightedPickRandom = <T>(
|
export const weightedPickRandom = <T>(
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,25 @@
|
||||||
import {
|
import {
|
||||||
ReactNode,
|
type ReactNode,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} 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 { Container, Sprite, Stage, useTick } from "@pixi/react";
|
||||||
import Viewport from "./pixi/PixiViewport";
|
import Viewport from "./pixi/PixiViewport";
|
||||||
import type { Viewport as PixiViewport } from "pixi-viewport";
|
import type { Viewport as PixiViewport } from "pixi-viewport";
|
||||||
import {
|
import {
|
||||||
ClientGame,
|
type ClientGame,
|
||||||
getValue,
|
getValue,
|
||||||
isServerGame,
|
isServerGame,
|
||||||
ServerGame,
|
type ServerGame,
|
||||||
} from "../../shared/game";
|
} from "../../shared/game";
|
||||||
import { useWSQuery } from "../hooks";
|
import { useWSQuery } from "../hooks";
|
||||||
import { Texture } from "pixi.js";
|
import { Texture } from "pixi.js";
|
||||||
|
|
@ -37,6 +42,7 @@ import "@pixi/canvas-sprite-tiling";
|
||||||
import "@pixi/canvas-sprite";
|
import "@pixi/canvas-sprite";
|
||||||
import "@pixi/canvas-text";
|
import "@pixi/canvas-text";
|
||||||
import { themes } from "../themes";
|
import { themes } from "../themes";
|
||||||
|
import { hashStr, weightedPickRandom } from "../../shared/utils";
|
||||||
|
|
||||||
interface BoardProps {
|
interface BoardProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
|
|
@ -271,6 +277,20 @@ const Tile = ({
|
||||||
onRightClick,
|
onRightClick,
|
||||||
onLeftClick,
|
onLeftClick,
|
||||||
}: TileProps) => {
|
}: 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 i = x;
|
||||||
const j = y;
|
const j = y;
|
||||||
const isRevealed = game.isRevealed[i][j];
|
const isRevealed = game.isRevealed[i][j];
|
||||||
|
|
@ -285,11 +305,13 @@ const Tile = ({
|
||||||
const isQuestionMark = game.isQuestionMark[i][j];
|
const isQuestionMark = game.isQuestionMark[i][j];
|
||||||
const base =
|
const base =
|
||||||
isRevealed || (isMine && !isFlagged) ? (
|
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 touchStart = useRef<number>(0);
|
||||||
const isMove = useRef<boolean>(false);
|
const isMove = useRef<boolean>(false);
|
||||||
const startX = useRef<number>(0);
|
const startX = useRef<number>(0);
|
||||||
|
|
@ -323,14 +345,24 @@ const Tile = ({
|
||||||
);
|
);
|
||||||
let content: ReactNode = null;
|
let content: ReactNode = null;
|
||||||
if (isFlagged) {
|
if (isFlagged) {
|
||||||
content = <Sprite key="c" texture={theme.flag} {...baseProps} />;
|
content = (
|
||||||
|
<Sprite key="c" texture={resolveSprite(theme.flag)} {...baseProps} />
|
||||||
|
);
|
||||||
} else if (isMine) {
|
} 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) {
|
} else if (value !== -1 && isRevealed) {
|
||||||
const img = theme[value.toString() as keyof Theme] as Texture;
|
const img = theme[value.toString() as keyof Theme] as Texture;
|
||||||
content = img ? <Sprite key="c" texture={img} {...baseProps} /> : null;
|
content = img ? <Sprite key="c" texture={img} {...baseProps} /> : null;
|
||||||
} else if (isQuestionMark) {
|
} 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 [, setCursorX] = useAtom(cursorXAtom);
|
||||||
const [, setCursorY] = useAtom(cursorYAtom);
|
const [, setCursorY] = useAtom(cursorYAtom);
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,17 @@ import { useEffect, useState } from "react";
|
||||||
type Png = typeof import("*.png");
|
type Png = typeof import("*.png");
|
||||||
type LazySprite = () => Promise<Png>;
|
type LazySprite = () => Promise<Png>;
|
||||||
|
|
||||||
|
interface WeightedLazySprites {
|
||||||
|
weight: number;
|
||||||
|
sprite: LazySprite;
|
||||||
|
}
|
||||||
|
|
||||||
export interface Theme {
|
export interface Theme {
|
||||||
size: number;
|
size: number;
|
||||||
mine: LazySprite;
|
mine: LazySprite | WeightedLazySprites[];
|
||||||
tile: LazySprite;
|
tile: LazySprite | WeightedLazySprites[];
|
||||||
revealed: LazySprite;
|
revealed: LazySprite;
|
||||||
flag: LazySprite;
|
flag: LazySprite | WeightedLazySprites[];
|
||||||
questionMark: LazySprite;
|
questionMark: LazySprite;
|
||||||
lastPos: LazySprite;
|
lastPos: LazySprite;
|
||||||
1: LazySprite;
|
1: LazySprite;
|
||||||
|
|
@ -22,7 +27,17 @@ export interface Theme {
|
||||||
8: LazySprite;
|
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;
|
size: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -34,13 +49,25 @@ export const useTheme = (theme: Theme) => {
|
||||||
const loadTheme = async () => {
|
const loadTheme = async () => {
|
||||||
const loadedEntries = await Promise.all(
|
const loadedEntries = await Promise.all(
|
||||||
Object.entries(theme).map(async ([key, value]) => {
|
Object.entries(theme).map(async ([key, value]) => {
|
||||||
const loaded =
|
let loaded = value;
|
||||||
typeof value === "function"
|
if (typeof value === "function") {
|
||||||
? await Assets.load((await value()).default)
|
loaded = await Assets.load((await value()).default);
|
||||||
: value;
|
}
|
||||||
|
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;
|
return [key, loaded] as const;
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
console.log("loaded", Object.fromEntries(loadedEntries));
|
||||||
setLoadedTheme(Object.fromEntries(loadedEntries) as LoadedTheme);
|
setLoadedTheme(Object.fromEntries(loadedEntries) as LoadedTheme);
|
||||||
};
|
};
|
||||||
loadTheme();
|
loadTheme();
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,17 @@
|
||||||
import { Theme } from "./Theme";
|
import type { Theme } from "./Theme";
|
||||||
|
|
||||||
export const techiesDireTheme: Theme = {
|
export const techiesDireTheme: Theme = {
|
||||||
size: 32,
|
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"),
|
tile: () => import("../assets/themes/techies/dire/tile-1.png"),
|
||||||
revealed: () => import("../assets/themes/techies/dire/revealed.png"),
|
revealed: () => import("../assets/themes/techies/dire/revealed.png"),
|
||||||
flag: () => import("../assets/themes/techies/flag.png"),
|
flag: () => import("../assets/themes/techies/flag.png"),
|
||||||
|
|
|
||||||
|
|
@ -184,11 +184,11 @@ const Store = () => {
|
||||||
variant="outline"
|
variant="outline"
|
||||||
size="default"
|
size="default"
|
||||||
className="mx-auto items-center"
|
className="mx-auto items-center"
|
||||||
onClick={() =>
|
onClick={() => {
|
||||||
openLootbox
|
openLootbox
|
||||||
.mutateAsync({ id: lootbox.id })
|
.mutateAsync({ id: lootbox.id })
|
||||||
.then(() => refetch())
|
.then(() => refetch());
|
||||||
}
|
}}
|
||||||
>
|
>
|
||||||
Buy for <b>{lootbox.priceText}</b> <GemsIcon />
|
Buy for <b>{lootbox.priceText}</b> <GemsIcon />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue