mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 03:59:50 +02:00
feat: start of all tanks
This commit is contained in:
1 parent
53d05e73a6
commit
fcfc97b964
14 files changed
+174
-58
No files matched your search
@@ -0,0 +1,20 @@
|
|||||||
|
import type { CharacteristicOutput } from "../core/tankopedia/characteristics";
|
||||||
|
import type { CharacteristicRenderConfig } from "../core/tankopedia/characteristicsOrder";
|
||||||
|
import { renderCharacteristic } from "../core/tankopedia/renderCharacteristic";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
characteristic: CharacteristicOutput;
|
||||||
|
config: CharacteristicRenderConfig;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TankopediaCharacteristic({ characteristic, config }: Props) {
|
||||||
|
if (characteristic === null) return null;
|
||||||
|
|
||||||
|
const rendered = renderCharacteristic(characteristic, config);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
{config.name}: {rendered} {config.units && `${config.units}`} (1 / 1)
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
interface Props {
|
||||||
|
toy: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TankopediaToy({ toy }: Props) {
|
||||||
|
return <p>toy: {toy}</p>;
|
||||||
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
import type { Tank } from "../../protos/tank";
|
import type { Tank } from "../../protos/tank";
|
||||||
|
import type { TankList } from "../../protos/tank_list";
|
||||||
import type { Tanks } from "../../protos/tanks";
|
import type { Tanks } from "../../protos/tanks";
|
||||||
|
|
||||||
export abstract class AbstractAPI {
|
export abstract class AbstractAPI {
|
||||||
abstract tanks(): Promise<Tanks>;
|
abstract tanks(): Promise<Tanks>;
|
||||||
|
|
||||||
|
abstract tankList(): Promise<TankList>;
|
||||||
|
|
||||||
abstract strings(
|
abstract strings(
|
||||||
locale: string,
|
locale: string,
|
||||||
prefix?: string
|
prefix?: string
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
|
import { fetchPB } from "@blitzkit/core";
|
||||||
import { Tank } from "../../protos/tank";
|
import { Tank } from "../../protos/tank";
|
||||||
|
import { TankList } from "../../protos/tank_list";
|
||||||
|
import { Tanks } from "../../protos/tanks";
|
||||||
import { AbstractAPI } from "./abstract";
|
import { AbstractAPI } from "./abstract";
|
||||||
|
|
||||||
export class ClientAPI extends AbstractAPI {
|
export class ClientAPI extends AbstractAPI {
|
||||||
async tank(id: string) {
|
tanks() {
|
||||||
const response = await fetch(`/api/tanks/${id}.pb`);
|
return fetchPB("/api/tanks.pb", Tanks);
|
||||||
const buffer = await response.arrayBuffer();
|
}
|
||||||
const array = new Uint8Array(buffer);
|
|
||||||
|
|
||||||
return Tank.decode(array);
|
tankList() {
|
||||||
|
return fetchPB("/api/tanks/list.pb", TankList);
|
||||||
|
}
|
||||||
|
|
||||||
|
tank(id: string) {
|
||||||
|
return fetchPB(`/api/tanks/${id}.pb`, Tank);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,8 @@ import { sluggify } from "@blitzkit/core";
|
|||||||
import type { RemoteStorageComponent } from "@protos/blitz_static_remote_storage_component";
|
import type { RemoteStorageComponent } from "@protos/blitz_static_remote_storage_component";
|
||||||
import { parse } from "yaml";
|
import { parse } from "yaml";
|
||||||
import type { Tank } from "../../protos/tank";
|
import type { Tank } from "../../protos/tank";
|
||||||
import type { TanksEntry } from "../../protos/tanks";
|
import type { TankListEntry } from "../../protos/tank_list";
|
||||||
|
import type { Tanks } from "../../protos/tanks";
|
||||||
import { AbstractAPI } from "./abstract";
|
import { AbstractAPI } from "./abstract";
|
||||||
|
|
||||||
if (typeof window !== "undefined") {
|
if (typeof window !== "undefined") {
|
||||||
@@ -91,9 +92,9 @@ export class ServerAPI extends AbstractAPI {
|
|||||||
return strings;
|
return strings;
|
||||||
}
|
}
|
||||||
|
|
||||||
async tanks() {
|
async tankList() {
|
||||||
const group = this.metadata.group("TankEntity");
|
const group = this.metadata.group("TankEntity");
|
||||||
const tanks: TanksEntry[] = [];
|
const list: TankListEntry[] = [];
|
||||||
const strings = await this.strings("en");
|
const strings = await this.strings("en");
|
||||||
|
|
||||||
for (const item of group) {
|
for (const item of group) {
|
||||||
@@ -108,17 +109,37 @@ export class ServerAPI extends AbstractAPI {
|
|||||||
const id = item.name;
|
const id = item.name;
|
||||||
const slug = sluggify(name);
|
const slug = sluggify(name);
|
||||||
|
|
||||||
tanks.push({ id, slug });
|
list.push({ id, slug });
|
||||||
}
|
}
|
||||||
|
|
||||||
return { tanks };
|
return { list };
|
||||||
|
}
|
||||||
|
|
||||||
|
async tanks() {
|
||||||
|
const tankList = await this.tankList();
|
||||||
|
const data: Tanks = { tanks: {} };
|
||||||
|
|
||||||
|
for (const { id } of tankList.list) {
|
||||||
|
console.log(id);
|
||||||
|
data.tanks[id] = await this.tank(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
async tank(id: string) {
|
async tank(id: string) {
|
||||||
|
const tankList = await this.tankList();
|
||||||
|
const tankListEntry = tankList.list.find((tank) => tank.id === id);
|
||||||
|
|
||||||
|
if (!tankListEntry) {
|
||||||
|
throw new Error(`Tank with id ${id} not found`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { slug } = tankListEntry;
|
||||||
const item = this.metadata.item(`TankEntity.${id}`);
|
const item = this.metadata.item(`TankEntity.${id}`);
|
||||||
const tank = item.TankCatalog();
|
const tank = item.TankCatalog();
|
||||||
const compensation = item.Compensation();
|
const compensation = item.Compensation();
|
||||||
|
|
||||||
return { tank, compensation } satisfies Tank;
|
return { tank, compensation, slug } satisfies Tank;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -12,6 +12,11 @@ import {
|
|||||||
} from "./characteristics";
|
} from "./characteristics";
|
||||||
import type { TankState } from "./tankState";
|
import type { TankState } from "./tankState";
|
||||||
|
|
||||||
|
export type ComputedCharacteristics = Record<
|
||||||
|
CharacteristicName,
|
||||||
|
CharacteristicOutput
|
||||||
|
>;
|
||||||
|
|
||||||
export function computeCharacteristics(
|
export function computeCharacteristics(
|
||||||
id: string,
|
id: string,
|
||||||
tank: TankCatalogComponent,
|
tank: TankCatalogComponent,
|
||||||
@@ -23,8 +28,7 @@ export function computeCharacteristics(
|
|||||||
tank.upgrade_stages.slice(0, state.stage)
|
tank.upgrade_stages.slice(0, state.stage)
|
||||||
);
|
);
|
||||||
|
|
||||||
const computed: Partial<Record<CharacteristicName, CharacteristicOutput>> =
|
const computed: Partial<ComputedCharacteristics> = {};
|
||||||
{};
|
|
||||||
|
|
||||||
function characteristic(name: CharacteristicName) {
|
function characteristic(name: CharacteristicName) {
|
||||||
if (name in computed) return computed[name] as CharacteristicOutput;
|
if (name in computed) return computed[name] as CharacteristicOutput;
|
||||||
@@ -99,10 +103,7 @@ export function computeCharacteristics(
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
characteristics: computed as Record<
|
characteristics: computed as ComputedCharacteristics,
|
||||||
CharacteristicName,
|
|
||||||
CharacteristicOutput
|
|
||||||
>,
|
|
||||||
parameters,
|
parameters,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import type { TankCatalogComponent } from "@protos/blitz_static_tank_component";
|
||||||
|
import { TerrainHardness } from "./tankState";
|
||||||
|
|
||||||
|
export function createTankState(tank: TankCatalogComponent) {
|
||||||
|
return {
|
||||||
|
stage: tank.upgrade_stages.length,
|
||||||
|
shell: 0,
|
||||||
|
isGunDamaged: false,
|
||||||
|
isHullTraversing: false,
|
||||||
|
isShooting: false,
|
||||||
|
isTurretTraversing: false,
|
||||||
|
speed: 0,
|
||||||
|
terrainHardness: TerrainHardness.Hard,
|
||||||
|
isCaughtOnFire: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { api } from "../../core/api/dynamic";
|
||||||
|
import { Tanks } from "../../protos/tanks";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Literally all the tanks in one place lmao.
|
||||||
|
*/
|
||||||
|
export async function GET() {
|
||||||
|
const tanks = await api.tanks();
|
||||||
|
return new Response(Tanks.encode(tanks).finish());
|
||||||
|
}
|
||||||
@@ -3,9 +3,9 @@ import { api } from "../../../core/api/dynamic";
|
|||||||
import { Tank } from "../../../protos/tank";
|
import { Tank } from "../../../protos/tank";
|
||||||
|
|
||||||
export const getStaticPaths = (async () => {
|
export const getStaticPaths = (async () => {
|
||||||
const { tanks } = await api.tanks();
|
const { list } = await api.tankList();
|
||||||
|
|
||||||
return tanks.map(({ id }) => ({ params: { id } }));
|
return list.map(({ id }) => ({ params: { id } }));
|
||||||
}) satisfies GetStaticPaths;
|
}) satisfies GetStaticPaths;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import { times } from "lodash-es";
|
import { times } from "lodash-es";
|
||||||
import { Suspense, useMemo } from "react";
|
import { Suspense, useMemo } from "react";
|
||||||
|
import { TankopediaCharacteristic } from "../../../components/TankopediaCharacteristic";
|
||||||
|
import { TankopediaToy } from "../../../components/TankopediaToy";
|
||||||
import { api } from "../../../core/api/dynamic";
|
import { api } from "../../../core/api/dynamic";
|
||||||
import { characteristicsOrder } from "../../../core/tankopedia/characteristicsOrder";
|
import { characteristicsOrder } from "../../../core/tankopedia/characteristicsOrder";
|
||||||
import { computeCharacteristics } from "../../../core/tankopedia/computeCharacteristics";
|
import { computeCharacteristics } from "../../../core/tankopedia/computeCharacteristics";
|
||||||
import { renderCharacteristic } from "../../../core/tankopedia/renderCharacteristic";
|
|
||||||
import { TerrainHardness } from "../../../core/tankopedia/tankState";
|
import { TerrainHardness } from "../../../core/tankopedia/tankState";
|
||||||
import { useAwait } from "../../../hooks/useAwait";
|
import { useAwait } from "../../../hooks/useAwait";
|
||||||
import { Tankopedia } from "../../../stores/tankopedia";
|
import { Tankopedia, TankopediaCompare } from "../../../stores/tankopedia";
|
||||||
|
|
||||||
export function Page({ id }: { id: string }) {
|
export function Page({ id }: { id: string }) {
|
||||||
return (
|
return (
|
||||||
@@ -18,10 +19,12 @@ export function Page({ id }: { id: string }) {
|
|||||||
|
|
||||||
function Content({ id }: { id: string }) {
|
function Content({ id }: { id: string }) {
|
||||||
const { tank, compensation } = useAwait(() => api.tank(id), `tank-${id}`);
|
const { tank, compensation } = useAwait(() => api.tank(id), `tank-${id}`);
|
||||||
|
const otherTanks = useAwait(() => api.tanks(), "tanks");
|
||||||
|
|
||||||
Tankopedia.useInitialization(tank!.upgrade_stages.length);
|
Tankopedia.useInitialization(tank!);
|
||||||
|
|
||||||
const protagonist = Tankopedia.use((state) => state.protagonist);
|
const protagonist = Tankopedia.use((state) => state.protagonist);
|
||||||
|
const compare = Tankopedia.use((state) => state.compare);
|
||||||
const { characteristics, parameters } = useMemo(
|
const { characteristics, parameters } = useMemo(
|
||||||
() => computeCharacteristics(id, tank!, compensation!, protagonist),
|
() => computeCharacteristics(id, tank!, compensation!, protagonist),
|
||||||
[protagonist]
|
[protagonist]
|
||||||
@@ -181,27 +184,41 @@ function Content({ id }: { id: string }) {
|
|||||||
|
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
|
<div style={{ display: "flex", flexWrap: "wrap" }}>
|
||||||
|
{Object.values(TankopediaCompare).map(
|
||||||
|
(c) =>
|
||||||
|
typeof c === "number" && (
|
||||||
|
<button
|
||||||
|
key={c}
|
||||||
|
onClick={() => {
|
||||||
|
Tankopedia.mutate((draft) => {
|
||||||
|
draft.compare = c;
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{TankopediaCompare[c]} {c === compare && "(selected)"}
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
{characteristicsOrder.map(({ group, order }) => (
|
{characteristicsOrder.map(({ group, order }) => (
|
||||||
<>
|
<>
|
||||||
<h2 key={group}>{group}</h2>
|
<h2 key={group}>{group}</h2>
|
||||||
|
|
||||||
{order.map((config) => {
|
{order.map((config) =>
|
||||||
if ("toy" in config) {
|
"toy" in config ? (
|
||||||
return <p key={`toy-${config.toy}`}>toy: {config.toy}</p>;
|
<TankopediaToy key={`toy-${config.toy}`} toy={config.toy} />
|
||||||
} else {
|
) : (
|
||||||
const characteristic = characteristics[config.name];
|
<TankopediaCharacteristic
|
||||||
|
key={`characteristic-${config.name}`}
|
||||||
if (characteristic === null) return null;
|
characteristic={characteristics[config.name]}
|
||||||
|
config={config}
|
||||||
const rendered = renderCharacteristic(characteristic, config);
|
/>
|
||||||
|
)
|
||||||
return (
|
)}
|
||||||
<p key={`characteristic-${config.name}`}>
|
|
||||||
{config.name}: {rendered} {config.units && `${config.units}`}
|
|
||||||
</p>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</>
|
</>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -8,4 +8,6 @@ import "blitz_static_tank_component.proto";
|
|||||||
message Tank {
|
message Tank {
|
||||||
blitz_static_tank_component.TankCatalogComponent tank = 1;
|
blitz_static_tank_component.TankCatalogComponent tank = 1;
|
||||||
blitz_static_compensation_component.CompensationComponent compensation = 2;
|
blitz_static_compensation_component.CompensationComponent compensation = 2;
|
||||||
|
|
||||||
|
string slug = 3;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package blitzkit;
|
||||||
|
|
||||||
|
message TankList {
|
||||||
|
repeated TankListEntry list = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TankListEntry {
|
||||||
|
string id = 1;
|
||||||
|
string slug = 2;
|
||||||
|
}
|
||||||
@@ -2,11 +2,8 @@ syntax = "proto3";
|
|||||||
|
|
||||||
package blitzkit;
|
package blitzkit;
|
||||||
|
|
||||||
message Tanks {
|
import "tank.proto";
|
||||||
repeated TanksEntry tanks = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
message TanksEntry {
|
message Tanks {
|
||||||
string id = 1;
|
map<string, Tank> tanks = 1;
|
||||||
string slug = 2;
|
|
||||||
}
|
}
|
||||||
@@ -1,19 +1,22 @@
|
|||||||
|
import type { TankCatalogComponent } from "@protos/blitz_static_tank_component";
|
||||||
import { Varuna } from "varuna";
|
import { Varuna } from "varuna";
|
||||||
import { TerrainHardness, type TankState } from "../core/tankopedia/tankState";
|
import { createTankState } from "../core/tankopedia/createTankState";
|
||||||
|
import { type TankState } from "../core/tankopedia/tankState";
|
||||||
|
|
||||||
interface Tankopedia {
|
interface Tankopedia {
|
||||||
|
compare: TankopediaCompare;
|
||||||
protagonist: TankState;
|
protagonist: TankState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Tankopedia = new Varuna<Tankopedia, number>((maxStage) => ({
|
export enum TankopediaCompare {
|
||||||
protagonist: {
|
TierAndClass,
|
||||||
stage: maxStage,
|
Tier,
|
||||||
shell: 0,
|
All,
|
||||||
isGunDamaged: false,
|
}
|
||||||
isHullTraversing: false,
|
|
||||||
isShooting: false,
|
export const Tankopedia = new Varuna<Tankopedia, TankCatalogComponent>(
|
||||||
isTurretTraversing: false,
|
(tank) => ({
|
||||||
speed: 0,
|
compare: TankopediaCompare.TierAndClass,
|
||||||
terrainHardness: TerrainHardness.Hard,
|
protagonist: createTankState(tank),
|
||||||
},
|
})
|
||||||
}));
|
);
|
||||||
Reference in new issue
Block a user