mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-31 14:39:51 +01:00
Compare commits
4
Commits
76dc9f0742
...
44e7cd7505
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44e7cd7505 | ||
|
|
3a60535564 | ||
|
|
9abfeb749c | ||
|
|
0442c789f1 |
No files matched your search
@@ -1,4 +1,62 @@
|
||||
{
|
||||
"nations": {
|
||||
"0": "Unspecified",
|
||||
"1": "USSR",
|
||||
"2": "Germany",
|
||||
"3": "USA",
|
||||
"4": "China",
|
||||
"5": "France",
|
||||
"6": "UK",
|
||||
"7": "Japan",
|
||||
"8": "European",
|
||||
"9": "Poland",
|
||||
"10": "Australia",
|
||||
"11": "Canada",
|
||||
"12": "Italy",
|
||||
"13": "Finland",
|
||||
"14": "Czechoslovakia",
|
||||
"15": "Hybrid Nation",
|
||||
"16": "GDR",
|
||||
"17": "Ireland",
|
||||
"18": "Scotland",
|
||||
"19": "Sweden",
|
||||
"20": "Argo Coalition",
|
||||
"21": "Titan Labs",
|
||||
"22": "Defenders",
|
||||
"23": "Progresston",
|
||||
"24": "Wasteland",
|
||||
"25": "Hunter's Moon",
|
||||
"26": "Cold Front",
|
||||
"27": "The Explorers",
|
||||
"28": "Fablegear, Inc.",
|
||||
"29": "Outlaws",
|
||||
"30": "Girls Und Panzer",
|
||||
"31": "Dune",
|
||||
"32": "Terminator",
|
||||
"33": "Warhammer 40,000",
|
||||
"34": "Valkyria Chronicles",
|
||||
"35": "Mecha",
|
||||
"36": "TMNT",
|
||||
"37": "Star Trek"
|
||||
},
|
||||
|
||||
"types": {
|
||||
"0": "Unspecified",
|
||||
"1": "Common",
|
||||
"2": "Premium",
|
||||
"3": "Collectible"
|
||||
},
|
||||
|
||||
"classes": {
|
||||
"full": {
|
||||
"0": "Unspecified",
|
||||
"1": "Heavy",
|
||||
"2": "Light",
|
||||
"3": "Medium",
|
||||
"4": "Tank Destroyer"
|
||||
}
|
||||
},
|
||||
|
||||
"tiers": {
|
||||
"vintage": "Vintage",
|
||||
"four": "IV",
|
||||
@@ -135,6 +193,10 @@
|
||||
},
|
||||
|
||||
"tanks": {
|
||||
"loadout": {
|
||||
"modules": "Modules"
|
||||
},
|
||||
|
||||
"search": {
|
||||
"placeholder": "Search tanks..."
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 5.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
@@ -77,6 +77,10 @@ import "@fontsource-variable/roboto-flex";
|
||||
--shadow-subtle: 0 var(--space-1) var(--space-3) var(--black-a5);
|
||||
--shadow-none: 0 0 0 transparent;
|
||||
|
||||
&.light {
|
||||
--shadow-subtle: 0 var(--space-1) var(--space-3) var(--black-a2);
|
||||
}
|
||||
|
||||
--blur-glass: blur(4rem);
|
||||
--blur-tint: blur(8rem);
|
||||
|
||||
@@ -86,3 +90,23 @@ import "@fontsource-variable/roboto-flex";
|
||||
--tank-card-min-width: 8rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
<style is:global>
|
||||
.composable-radius {
|
||||
&[data-radius="1"] {
|
||||
border-radius: var(--space-3);
|
||||
}
|
||||
|
||||
&[data-radius="2"] {
|
||||
border-radius: var(--space-4);
|
||||
}
|
||||
|
||||
&[data-radius="3"] {
|
||||
border-radius: var(--space-5);
|
||||
}
|
||||
|
||||
&[data-radius="max"] {
|
||||
border-radius: var(--max-size);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -7,7 +7,6 @@
|
||||
border-radius: var(--max-size);
|
||||
padding: var(--space-3) var(--space-4);
|
||||
|
||||
background-color: var(--accent-9);
|
||||
border: none;
|
||||
|
||||
font-family: var(--font-family-regular);
|
||||
@@ -23,4 +22,14 @@
|
||||
&[data-light-text="true"] {
|
||||
color: var(--white-a12);
|
||||
}
|
||||
|
||||
&[data-variant="solid"] {
|
||||
background-color: var(--accent-8);
|
||||
}
|
||||
|
||||
&[data-variant="surface"] {
|
||||
background-color: var(--accent-3);
|
||||
|
||||
outline: var(--space-1) solid var(--accent-4);
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,8 @@ import styles from "./index.module.css";
|
||||
|
||||
export interface ButtonProps extends ComponentProps<"button"> {
|
||||
color?: Color;
|
||||
variant?: "solid";
|
||||
radius?: `${1 | 2 | 3}` | "max";
|
||||
variant?: "solid" | "surface";
|
||||
}
|
||||
|
||||
export const LIGHT_TEXT_COLORS = new Set<Color>([
|
||||
@@ -28,14 +29,16 @@ export function Button({
|
||||
color,
|
||||
variant = "solid",
|
||||
style,
|
||||
radius = "max",
|
||||
...props
|
||||
}: ButtonProps) {
|
||||
return (
|
||||
<button
|
||||
className={classNames(styles.button, className)}
|
||||
className={classNames("composable-radius", styles.button, className)}
|
||||
data-color={color}
|
||||
data-variant={variant}
|
||||
data-light-text={variant === "solid"}
|
||||
data-radius={radius}
|
||||
style={{
|
||||
backgroundColor: color ? `var(--${color}-9)` : undefined,
|
||||
...style,
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.icon-button {
|
||||
padding: var(--space-3);
|
||||
|
||||
aspect-ratio: 1 / 1;
|
||||
|
||||
font-size: 2rem;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { classNames } from "../../core/ui/classNames";
|
||||
import type { Color } from "../../core/ui/color";
|
||||
import { Button, type ButtonProps } from "../Button";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
export interface IconButtonProps extends ButtonProps {
|
||||
color?: Color;
|
||||
variant?: "solid";
|
||||
}
|
||||
|
||||
export function IconButton({ className, ...props }: ButtonProps) {
|
||||
return (
|
||||
<Button
|
||||
className={classNames(className, styles["icon-button"])}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
.lines {
|
||||
display: flex;
|
||||
|
||||
gap: var(--space-3);
|
||||
|
||||
.line {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
gap: var(--space-2);
|
||||
|
||||
.stage {
|
||||
img {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
import type { UpgradeLine } from "@blitzkit/closed/protos/game/proto/legacy/blitz_static_tank_upgrade_line";
|
||||
import { CaretDownIcon } from "@radix-ui/react-icons";
|
||||
import { api } from "../../core/api/dynamic";
|
||||
import { useAwait } from "../../hooks/useAwait";
|
||||
import { useStrings } from "../../hooks/useStrings";
|
||||
import { Tankopedia } from "../../stores/tankopedia";
|
||||
import { Heading } from "../Heading";
|
||||
import { IconButton } from "../IconButton";
|
||||
import { Section } from "../Section";
|
||||
import styles from "./index.module.css";
|
||||
|
||||
export function TankopediaLoadout() {
|
||||
const strings = useStrings();
|
||||
|
||||
const id = Tankopedia.use((state) => state.protagonist.id);
|
||||
const tank = useAwait(() => api.tank(id), `tank-${id}`);
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<Heading size="3">{strings.tanks.loadout.modules}</Heading>
|
||||
|
||||
<div className={styles.lines}>
|
||||
{tank.tank!.upgrade_lines.map((line) => (
|
||||
<Line line={line} />
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
interface LineProps {
|
||||
line: UpgradeLine;
|
||||
}
|
||||
|
||||
function Line({ line }: LineProps) {
|
||||
const upgrades = Tankopedia.use((state) => state.protagonist.upgrades);
|
||||
const id = Tankopedia.use((state) => state.protagonist.id);
|
||||
const tank = useAwait(() => api.tank(id), `tank-${id}`);
|
||||
|
||||
return (
|
||||
<div className={styles.line}>
|
||||
{line.stages.map((stage, index) => {
|
||||
const isSelected = upgrades[line.name] === index;
|
||||
|
||||
return (
|
||||
<>
|
||||
{index !== 0 && <CaretDownIcon width="1em" height="1em" />}
|
||||
|
||||
<IconButton
|
||||
variant={isSelected ? "solid" : "surface"}
|
||||
radius="1"
|
||||
className={styles.stage}
|
||||
onClick={() => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.protagonist.upgrades[line.name] = index;
|
||||
|
||||
for (const required of stage.required_upgrades) {
|
||||
for (const line of tank.tank!.upgrade_lines) {
|
||||
let i = 0;
|
||||
|
||||
for (const stage of line.stages) {
|
||||
if (stage.tech_name === required) {
|
||||
draft.protagonist.upgrades[line.name] = Math.max(
|
||||
draft.protagonist.upgrades[line.name],
|
||||
i,
|
||||
);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const line of tank.tank!.upgrade_lines) {
|
||||
let i = draft.protagonist.upgrades[line.name];
|
||||
|
||||
while (i > 0) {
|
||||
const currentStage = line.stages[i];
|
||||
const valid = currentStage.required_upgrades.every(
|
||||
(required) => {
|
||||
for (const line of tank.tank!.upgrade_lines) {
|
||||
const idx = draft.protagonist.upgrades[line.name];
|
||||
|
||||
if (line.stages[idx]?.tech_name === required) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
);
|
||||
|
||||
if (valid) break;
|
||||
|
||||
i--;
|
||||
}
|
||||
|
||||
draft.protagonist.upgrades[line.name] = i;
|
||||
}
|
||||
});
|
||||
}}
|
||||
>
|
||||
<img src={`/api/tanks/modules/${stage.stage_type}.webp`} />
|
||||
</IconButton>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -46,7 +46,7 @@ export abstract class AbstractAPI {
|
||||
return supported;
|
||||
}
|
||||
|
||||
@Cache<[string, string, boolean]>((...args) => args.join("-"))
|
||||
@Cache()
|
||||
async groupedGameStrings(locale: string, group: string, prefix: boolean) {
|
||||
this.assertLocale(locale);
|
||||
|
||||
@@ -64,9 +64,7 @@ export abstract class AbstractAPI {
|
||||
}
|
||||
}
|
||||
|
||||
export function Cache<Arguments extends unknown[]>(
|
||||
discriminator: (...args: Arguments) => string = (...args) => args.join("-"),
|
||||
) {
|
||||
export function Cache<Arguments extends unknown[]>(disableInDev = false) {
|
||||
const cache = new WeakMap<object, Map<string, Promise<unknown>>>();
|
||||
|
||||
return function (
|
||||
@@ -77,7 +75,7 @@ export function Cache<Arguments extends unknown[]>(
|
||||
const original = descriptor.value;
|
||||
|
||||
descriptor.value = function (...args: Arguments) {
|
||||
const key = discriminator(...args);
|
||||
const key = args.join("-");
|
||||
|
||||
let thisCache = cache.get(this);
|
||||
|
||||
@@ -86,7 +84,9 @@ export function Cache<Arguments extends unknown[]>(
|
||||
cache.set(this, thisCache);
|
||||
}
|
||||
|
||||
if (thisCache.has(key)) {
|
||||
const disabled = disableInDev && import.meta.env.DEV;
|
||||
|
||||
if (thisCache.has(key) && !disabled) {
|
||||
return thisCache.get(key);
|
||||
}
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ export class ServerAPI extends AbstractAPI {
|
||||
return tiers;
|
||||
}
|
||||
|
||||
@Cache()
|
||||
@Cache(true)
|
||||
async strings(locale: string) {
|
||||
const localized = (await globbedStrings[
|
||||
`../../../../i18n/strings/${locale}.json`
|
||||
|
||||
@@ -50,6 +50,24 @@ export const characteristicsOrder: CharacteristicsOrder = [
|
||||
return gameStrings[output as string];
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "class",
|
||||
render({ output, strings }) {
|
||||
return strings.classes.full[output as TankClass];
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
render({ output, strings }) {
|
||||
return strings.types[output as TankType];
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nation",
|
||||
render({ output, strings }) {
|
||||
return strings.nations[output as Nation];
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "tier",
|
||||
render({ output, strings }) {
|
||||
@@ -62,24 +80,6 @@ export const characteristicsOrder: CharacteristicsOrder = [
|
||||
return strings.tiers[name as keyof typeof strings.tiers];
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "nation",
|
||||
render({ output }) {
|
||||
return Nation[output as Nation];
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "class",
|
||||
render({ output }) {
|
||||
return TankClass[output as TankClass];
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "type",
|
||||
render({ output }) {
|
||||
return TankType[output as TankType];
|
||||
},
|
||||
},
|
||||
{ name: "compensation" },
|
||||
{ name: "id" },
|
||||
{ name: "slug" },
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Grade } from "@blitzkit/closed/protos/game/proto/legacy/blitz_static_standard_grades_enum";
|
||||
import { StageParameters_StageType } from "@blitzkit/closed/protos/game/proto/legacy/blitz_static_tank_upgrade_single_stage";
|
||||
import { useMemo } from "react";
|
||||
import { TankopediaLoadout } from "../../../../components/TankopediaLoadout";
|
||||
import { api } from "../../../../core/api/dynamic";
|
||||
import { characteristicsOrder } from "../../../../core/tankopedia/characteristicsOrder";
|
||||
import { computeCharacteristics } from "../../../../core/tankopedia/computeCharacteristics";
|
||||
@@ -48,7 +47,9 @@ function Content() {
|
||||
|
||||
<br />
|
||||
|
||||
<h2>upgrades</h2>
|
||||
<TankopediaLoadout />
|
||||
|
||||
{/* <h2>upgrades</h2>
|
||||
|
||||
{protagonistTank.tank!.upgrade_lines.map((line) => {
|
||||
return (
|
||||
@@ -145,7 +146,7 @@ function Content() {
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
})}
|
||||
})} */}
|
||||
|
||||
<h2>characteristics</h2>
|
||||
|
||||
@@ -159,10 +160,13 @@ function Content() {
|
||||
return <span>toy: {item.toy}</span>;
|
||||
}
|
||||
|
||||
const value = characteristics[item.name];
|
||||
|
||||
if (value == null) return null;
|
||||
|
||||
return (
|
||||
<span>
|
||||
{item.name}:{" "}
|
||||
{renderCharacteristic(characteristics[item.name], item)}
|
||||
{item.name}: {renderCharacteristic(value, item)}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
|
||||
Reference in new issue
Block a user