mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 20:19:49 +02:00
feat: upgrade Guess
This commit is contained in:
1 parent
7d164e415a
commit
f56c31fb48
5 files changed
+80
-82
No files matched your search
@@ -1,13 +1,13 @@
|
|||||||
import { Box } from '@radix-ui/themes';
|
import { Box } from "@radix-ui/themes";
|
||||||
import { useEffect } from 'react';
|
import { useEffect } from "react";
|
||||||
import { awaitableModelDefinitions } from '../../core/awaitables/modelDefinitions';
|
import { awaitableModelDefinitions } from "../../core/awaitables/modelDefinitions";
|
||||||
import { awaitableProvisionDefinitions } from '../../core/awaitables/provisionDefinitions';
|
import { awaitableProvisionDefinitions } from "../../core/awaitables/provisionDefinitions";
|
||||||
import { tankToDuelMember } from '../../core/blitzkit/tankToDuelMember';
|
import { tankToDuelMember } from "../../core/blitzkit/tankToDuelMember";
|
||||||
import { Var } from '../../core/radix/var';
|
import { Var } from "../../core/radix/var";
|
||||||
import { Duel } from '../../stores/duel';
|
import { Duel } from "../../stores/duel";
|
||||||
import { GuessEphemeral } from '../../stores/guessEphemeral';
|
import { Guess } from "../../stores/guessEphemeral";
|
||||||
import { TankopediaEphemeral } from '../../stores/tankopediaEphemeral';
|
import { TankopediaEphemeral } from "../../stores/tankopediaEphemeral";
|
||||||
import './index.css';
|
import "./index.css";
|
||||||
|
|
||||||
const [modelDefinitions, provisionDefinitions] = await Promise.all([
|
const [modelDefinitions, provisionDefinitions] = await Promise.all([
|
||||||
awaitableModelDefinitions,
|
awaitableModelDefinitions,
|
||||||
@@ -15,8 +15,8 @@ const [modelDefinitions, provisionDefinitions] = await Promise.all([
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
export function GuessBackground() {
|
export function GuessBackground() {
|
||||||
const guessState = GuessEphemeral.use((state) => state.guessState);
|
const guessState = Guess.use((state) => state.guessState);
|
||||||
const tank = GuessEphemeral.use((state) => state.tank);
|
const tank = Guess.use((state) => state.tank);
|
||||||
const mutateDuel = Duel.useMutation();
|
const mutateDuel = Duel.useMutation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -37,13 +37,13 @@ export function GuessBackground() {
|
|||||||
top="0"
|
top="0"
|
||||||
left="0"
|
left="0"
|
||||||
style={{
|
style={{
|
||||||
transitionDuration: '2s',
|
transitionDuration: "2s",
|
||||||
backgroundColor: Var(
|
backgroundColor: Var(
|
||||||
guessState === null
|
guessState === null
|
||||||
? 'accent-2'
|
? "accent-2"
|
||||||
: guessState
|
: guessState
|
||||||
? 'jade-3'
|
? "jade-3"
|
||||||
: 'tomato-3',
|
: "tomato-3"
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -51,7 +51,7 @@ export function GuessBackground() {
|
|||||||
width="100%"
|
width="100%"
|
||||||
height="100%"
|
height="100%"
|
||||||
style={{
|
style={{
|
||||||
background: `radial-gradient(circle, ${Var('color-panel-translucent')}, ${Var('color-panel-solid')})`,
|
background: `radial-gradient(circle, ${Var("color-panel-translucent")}, ${Var("color-panel-solid")})`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -65,13 +65,13 @@ export function GuessBackground() {
|
|||||||
left="0"
|
left="0"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: `
|
backgroundImage: `
|
||||||
radial-gradient(circle, ${Var('white-a1')} 20%, transparent 20%),
|
radial-gradient(circle, ${Var("white-a1")} 20%, transparent 20%),
|
||||||
radial-gradient(circle at 0 0, ${Var('white-a1')} 10%, transparent 10%),
|
radial-gradient(circle at 0 0, ${Var("white-a1")} 10%, transparent 10%),
|
||||||
radial-gradient(circle at 100% 0, ${Var('white-a1')} 10%, transparent 10%),
|
radial-gradient(circle at 100% 0, ${Var("white-a1")} 10%, transparent 10%),
|
||||||
radial-gradient(circle at 0 100%, ${Var('white-a1')} 10%, transparent 10%),
|
radial-gradient(circle at 0 100%, ${Var("white-a1")} 10%, transparent 10%),
|
||||||
radial-gradient(circle at 100% 100%, ${Var('white-a1')} 10%, transparent 10%)
|
radial-gradient(circle at 100% 100%, ${Var("white-a1")} 10%, transparent 10%)
|
||||||
`,
|
`,
|
||||||
backgroundSize: '4rem 4rem',
|
backgroundSize: "4rem 4rem",
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import { GuessEphemeral } from '../stores/guessEphemeral';
|
import { Guess } from "../stores/guessEphemeral";
|
||||||
import { SmartCanvas } from './SmartCanvas';
|
import { SmartCanvas } from "./SmartCanvas";
|
||||||
import { Controls } from './Tankopedia/HeroSection/components/TankSandbox/components/Control';
|
import { Controls } from "./Tankopedia/HeroSection/components/TankSandbox/components/Control";
|
||||||
import { Lighting } from './Tankopedia/HeroSection/components/TankSandbox/components/Lighting';
|
import { Lighting } from "./Tankopedia/HeroSection/components/TankSandbox/components/Lighting";
|
||||||
import { TankModel } from './Tankopedia/HeroSection/components/TankSandbox/components/TankModel';
|
import { TankModel } from "./Tankopedia/HeroSection/components/TankSandbox/components/TankModel";
|
||||||
|
|
||||||
const BRIGHTNESS = 2 ** -5.5;
|
const BRIGHTNESS = 2 ** -5.5;
|
||||||
const CONTRAST = 2 ** 2;
|
const CONTRAST = 2 ** 2;
|
||||||
|
|
||||||
export function GuessRenderer() {
|
export function GuessRenderer() {
|
||||||
const guessState = GuessEphemeral.use((state) => state.guessState);
|
const guessState = Guess.use((state) => state.guessState);
|
||||||
const isRevealed = guessState !== null;
|
const isRevealed = guessState !== null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SmartCanvas
|
<SmartCanvas
|
||||||
style={{
|
style={{
|
||||||
filter: `grayscale(${isRevealed ? 0 : 1}) contrast(${isRevealed ? 1 : CONTRAST}) brightness(${isRevealed ? 1 : BRIGHTNESS})`,
|
filter: `grayscale(${isRevealed ? 0 : 1}) contrast(${isRevealed ? 1 : CONTRAST}) brightness(${isRevealed ? 1 : BRIGHTNESS})`,
|
||||||
transitionDuration: isRevealed ? '2s' : undefined,
|
transitionDuration: isRevealed ? "2s" : undefined,
|
||||||
transitionProperty: 'filter',
|
transitionProperty: "filter",
|
||||||
}}
|
}}
|
||||||
camera={{ position: [-5, 5, -5] }}
|
camera={{ position: [-5, 5, -5] }}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ import {
|
|||||||
SEARCH_KEYS,
|
SEARCH_KEYS,
|
||||||
TankDefinition,
|
TankDefinition,
|
||||||
TIER_ROMAN_NUMERALS,
|
TIER_ROMAN_NUMERALS,
|
||||||
} from '@blitzkit/core';
|
} from "@blitzkit/core";
|
||||||
import { literals } from '@blitzkit/i18n';
|
import { literals } from "@blitzkit/i18n";
|
||||||
import {
|
import {
|
||||||
ArrowRightIcon,
|
ArrowRightIcon,
|
||||||
MagnifyingGlassIcon,
|
MagnifyingGlassIcon,
|
||||||
PaperPlaneIcon,
|
PaperPlaneIcon,
|
||||||
} from '@radix-ui/react-icons';
|
} from "@radix-ui/react-icons";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Button,
|
Button,
|
||||||
@@ -17,16 +17,16 @@ import {
|
|||||||
Spinner,
|
Spinner,
|
||||||
Text,
|
Text,
|
||||||
TextField,
|
TextField,
|
||||||
} from '@radix-ui/themes';
|
} from "@radix-ui/themes";
|
||||||
import fuzzysort from 'fuzzysort';
|
import fuzzysort from "fuzzysort";
|
||||||
import { debounce } from 'lodash-es';
|
import { debounce } from "lodash-es";
|
||||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import { awaitableTankDefinitions } from '../core/awaitables/tankDefinitions';
|
import { awaitableTankDefinitions } from "../core/awaitables/tankDefinitions";
|
||||||
import { awaitableTankNames } from '../core/awaitables/tankNames';
|
import { awaitableTankNames } from "../core/awaitables/tankNames";
|
||||||
import { useLocale } from '../hooks/useLocale';
|
import { useLocale } from "../hooks/useLocale";
|
||||||
import { GuessEphemeral } from '../stores/guessEphemeral';
|
import { Guess } from "../stores/guessEphemeral";
|
||||||
import { classIcons } from './ClassIcon';
|
import { classIcons } from "./ClassIcon";
|
||||||
import { SearchResults } from './SearchResults';
|
import { SearchResults } from "./SearchResults";
|
||||||
|
|
||||||
const { go } = fuzzysort;
|
const { go } = fuzzysort;
|
||||||
|
|
||||||
@@ -38,16 +38,15 @@ const [tankNames, tankDefinitions] = await Promise.all([
|
|||||||
const ids = Object.keys(tankDefinitions.tanks);
|
const ids = Object.keys(tankDefinitions.tanks);
|
||||||
|
|
||||||
export function Guesser() {
|
export function Guesser() {
|
||||||
const tank = GuessEphemeral.use((state) => state.tank);
|
const tank = Guess.use((state) => state.tank);
|
||||||
const guessState = GuessEphemeral.use((state) => state.guessState);
|
const guessState = Guess.use((state) => state.guessState);
|
||||||
const correctGuesses = GuessEphemeral.use((state) => state.correctGuesses);
|
const correctGuesses = Guess.use((state) => state.correctGuesses);
|
||||||
const totalGuesses = GuessEphemeral.use((state) => state.totalGuesses);
|
const totalGuesses = Guess.use((state) => state.totalGuesses);
|
||||||
const { strings, unwrap } = useLocale();
|
const { strings, unwrap } = useLocale();
|
||||||
const input = useRef<HTMLInputElement>(null);
|
const input = useRef<HTMLInputElement>(null);
|
||||||
const [searching, setSearching] = useState(false);
|
const [searching, setSearching] = useState(false);
|
||||||
const [results, setResults] = useState<TankDefinition[] | null>(null);
|
const [results, setResults] = useState<TankDefinition[] | null>(null);
|
||||||
const [selected, setSelected] = useState<TankDefinition | null>(null);
|
const [selected, setSelected] = useState<TankDefinition | null>(null);
|
||||||
const mutateGuessEphemeral = GuessEphemeral.useMutation();
|
|
||||||
|
|
||||||
const requestSearch = useCallback(() => {
|
const requestSearch = useCallback(() => {
|
||||||
setSelected(null);
|
setSelected(null);
|
||||||
@@ -72,10 +71,10 @@ export function Guesser() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
setResults(
|
setResults(
|
||||||
searchResults.map((result) => tankDefinitions.tanks[result.obj.id]),
|
searchResults.map((result) => tankDefinitions.tanks[result.obj.id])
|
||||||
);
|
);
|
||||||
}, 500),
|
}, 500),
|
||||||
[],
|
[]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -88,7 +87,7 @@ export function Guesser() {
|
|||||||
position="absolute"
|
position="absolute"
|
||||||
bottom="0"
|
bottom="0"
|
||||||
left="50%"
|
left="50%"
|
||||||
style={{ transform: 'translateX(-50%)' }}
|
style={{ transform: "translateX(-50%)" }}
|
||||||
width="100%"
|
width="100%"
|
||||||
p="4"
|
p="4"
|
||||||
maxWidth="25rem"
|
maxWidth="25rem"
|
||||||
@@ -138,7 +137,7 @@ export function Guesser() {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Flex justify="center" style={{ userSelect: 'none' }}>
|
<Flex justify="center" style={{ userSelect: "none" }}>
|
||||||
<Text>
|
<Text>
|
||||||
{literals(strings.website.tools.guess.stats, [
|
{literals(strings.website.tools.guess.stats, [
|
||||||
`${correctGuesses}`,
|
`${correctGuesses}`,
|
||||||
@@ -164,12 +163,12 @@ export function Guesser() {
|
|||||||
|
|
||||||
<Button
|
<Button
|
||||||
size="3"
|
size="3"
|
||||||
color={guessState === null && selected === null ? 'red' : undefined}
|
color={guessState === null && selected === null ? "red" : undefined}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (guessState === null) {
|
if (guessState === null) {
|
||||||
const correct = selected !== null && selected.id === tank.id;
|
const correct = selected !== null && selected.id === tank.id;
|
||||||
|
|
||||||
mutateGuessEphemeral((draft) => {
|
Guess.mutate((draft) => {
|
||||||
draft.guessState = correct;
|
draft.guessState = correct;
|
||||||
draft.totalGuesses++;
|
draft.totalGuesses++;
|
||||||
draft.correctGuesses += correct ? 1 : 0;
|
draft.correctGuesses += correct ? 1 : 0;
|
||||||
@@ -178,12 +177,12 @@ export function Guesser() {
|
|||||||
const id = Number(ids[Math.floor(Math.random() * ids.length)]);
|
const id = Number(ids[Math.floor(Math.random() * ids.length)]);
|
||||||
const tank = tankDefinitions.tanks[id];
|
const tank = tankDefinitions.tanks[id];
|
||||||
|
|
||||||
mutateGuessEphemeral((draft) => {
|
Guess.mutate((draft) => {
|
||||||
if (!input.current) return;
|
if (!input.current) return;
|
||||||
|
|
||||||
draft.tank = tank;
|
draft.tank = tank;
|
||||||
draft.guessState = null;
|
draft.guessState = null;
|
||||||
input.current.value = '';
|
input.current.value = "";
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@@ -192,7 +191,7 @@ export function Guesser() {
|
|||||||
<>
|
<>
|
||||||
{
|
{
|
||||||
strings.website.tools.guess.search[
|
strings.website.tools.guess.search[
|
||||||
selected === null ? 'skip' : 'guess'
|
selected === null ? "skip" : "guess"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
{selected === null ? <ArrowRightIcon /> : <PaperPlaneIcon />}
|
{selected === null ? <ArrowRightIcon /> : <PaperPlaneIcon />}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
useLocale,
|
useLocale,
|
||||||
} from "../../../hooks/useLocale";
|
} from "../../../hooks/useLocale";
|
||||||
import { Duel } from "../../../stores/duel";
|
import { Duel } from "../../../stores/duel";
|
||||||
import { GuessEphemeral } from "../../../stores/guessEphemeral";
|
import { Guess } from "../../../stores/guessEphemeral";
|
||||||
import { TankopediaEphemeral } from "../../../stores/tankopediaEphemeral";
|
import { TankopediaEphemeral } from "../../../stores/tankopediaEphemeral";
|
||||||
|
|
||||||
const [tankDefinitions, modelDefinitions, provisionDefinitions] =
|
const [tankDefinitions, modelDefinitions, provisionDefinitions] =
|
||||||
@@ -26,20 +26,20 @@ const [tankDefinitions, modelDefinitions, provisionDefinitions] =
|
|||||||
const ids = Object.keys(tankDefinitions.tanks);
|
const ids = Object.keys(tankDefinitions.tanks);
|
||||||
|
|
||||||
export function Page({ locale }: LocaleAcceptorProps) {
|
export function Page({ locale }: LocaleAcceptorProps) {
|
||||||
const initialId = Number(ids[Math.floor(Math.random() * ids.length)]);
|
const id = Number(ids[Math.floor(Math.random() * ids.length)]);
|
||||||
const initialTank = tankDefinitions.tanks[initialId];
|
const tank = tankDefinitions.tanks[id];
|
||||||
|
|
||||||
|
Guess.useInitialization(tank);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GuessEphemeral.Provider data={initialTank}>
|
<LocaleProvider locale={locale}>
|
||||||
<LocaleProvider locale={locale}>
|
<Container />
|
||||||
<Container />
|
</LocaleProvider>
|
||||||
</LocaleProvider>
|
|
||||||
</GuessEphemeral.Provider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Container() {
|
function Container() {
|
||||||
const tank = GuessEphemeral.use((state) => state.tank);
|
const tank = Guess.use((state) => state.tank);
|
||||||
const model = modelDefinitions.models[tank.id];
|
const model = modelDefinitions.models[tank.id];
|
||||||
|
|
||||||
TankopediaEphemeral.useInitialization(model);
|
TankopediaEphemeral.useInitialization(model);
|
||||||
@@ -53,8 +53,8 @@ function Container() {
|
|||||||
|
|
||||||
function Content() {
|
function Content() {
|
||||||
const { unwrap } = useLocale();
|
const { unwrap } = useLocale();
|
||||||
const tank = GuessEphemeral.use((state) => state.tank);
|
const tank = Guess.use((state) => state.tank);
|
||||||
const guessState = GuessEphemeral.use((state) => state.guessState);
|
const guessState = Guess.use((state) => state.guessState);
|
||||||
const isRevealed = guessState !== null;
|
const isRevealed = guessState !== null;
|
||||||
const name = unwrap(tank.name);
|
const name = unwrap(tank.name);
|
||||||
const fontSize = `min(48vh, ${55 / name.length}vw)`;
|
const fontSize = `min(48vh, ${55 / name.length}vw)`;
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
import type { TankDefinition } from '@blitzkit/core';
|
import type { TankDefinition } from "@blitzkit/core";
|
||||||
import { create } from 'zustand';
|
import { Varuna } from "varuna";
|
||||||
import { createContextualStore } from '../core/zustand/createContextualStore';
|
|
||||||
|
|
||||||
export interface GuessEphemeral {
|
export interface Guess {
|
||||||
tank: TankDefinition;
|
tank: TankDefinition;
|
||||||
// true = correct, false = incorrect, null = not guessed
|
/**
|
||||||
|
* true = correct, false = incorrect, null = not guessed
|
||||||
|
*/
|
||||||
guessState: boolean | null;
|
guessState: boolean | null;
|
||||||
totalGuesses: number;
|
totalGuesses: number;
|
||||||
correctGuesses: number;
|
correctGuesses: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const GuessEphemeral = createContextualStore((tank: TankDefinition) =>
|
export const Guess = new Varuna<Guess, TankDefinition>((tank) => ({
|
||||||
create<GuessEphemeral>()(() => ({
|
tank,
|
||||||
tank,
|
guessState: null,
|
||||||
guessState: null,
|
totalGuesses: 0,
|
||||||
totalGuesses: 0,
|
correctGuesses: 0,
|
||||||
correctGuesses: 0,
|
}));
|
||||||
})),
|
|
||||||
);
|
|
||||||
Reference in new issue
Block a user