mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +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 { TankList } from "../../protos/tank_list";
|
||||
import type { Tanks } from "../../protos/tanks";
|
||||
|
||||
export abstract class AbstractAPI {
|
||||
abstract tanks(): Promise<Tanks>;
|
||||
|
||||
abstract tankList(): Promise<TankList>;
|
||||
|
||||
abstract strings(
|
||||
locale: string,
|
||||
prefix?: string
|
||||
|
||||
@@ -1,12 +1,19 @@
|
||||
import { fetchPB } from "@blitzkit/core";
|
||||
import { Tank } from "../../protos/tank";
|
||||
import { TankList } from "../../protos/tank_list";
|
||||
import { Tanks } from "../../protos/tanks";
|
||||
import { AbstractAPI } from "./abstract";
|
||||
|
||||
export class ClientAPI extends AbstractAPI {
|
||||
async tank(id: string) {
|
||||
const response = await fetch(`/api/tanks/${id}.pb`);
|
||||
const buffer = await response.arrayBuffer();
|
||||
const array = new Uint8Array(buffer);
|
||||
tanks() {
|
||||
return fetchPB("/api/tanks.pb", Tanks);
|
||||
}
|
||||
|
||||
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 { parse } from "yaml";
|
||||
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";
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
@@ -91,9 +92,9 @@ export class ServerAPI extends AbstractAPI {
|
||||
return strings;
|
||||
}
|
||||
|
||||
async tanks() {
|
||||
async tankList() {
|
||||
const group = this.metadata.group("TankEntity");
|
||||
const tanks: TanksEntry[] = [];
|
||||
const list: TankListEntry[] = [];
|
||||
const strings = await this.strings("en");
|
||||
|
||||
for (const item of group) {
|
||||
@@ -108,17 +109,37 @@ export class ServerAPI extends AbstractAPI {
|
||||
const id = item.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) {
|
||||
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 tank = item.TankCatalog();
|
||||
const compensation = item.Compensation();
|
||||
|
||||
return { tank, compensation } satisfies Tank;
|
||||
return { tank, compensation, slug } satisfies Tank;
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,11 @@ import {
|
||||
} from "./characteristics";
|
||||
import type { TankState } from "./tankState";
|
||||
|
||||
export type ComputedCharacteristics = Record<
|
||||
CharacteristicName,
|
||||
CharacteristicOutput
|
||||
>;
|
||||
|
||||
export function computeCharacteristics(
|
||||
id: string,
|
||||
tank: TankCatalogComponent,
|
||||
@@ -23,8 +28,7 @@ export function computeCharacteristics(
|
||||
tank.upgrade_stages.slice(0, state.stage)
|
||||
);
|
||||
|
||||
const computed: Partial<Record<CharacteristicName, CharacteristicOutput>> =
|
||||
{};
|
||||
const computed: Partial<ComputedCharacteristics> = {};
|
||||
|
||||
function characteristic(name: CharacteristicName) {
|
||||
if (name in computed) return computed[name] as CharacteristicOutput;
|
||||
@@ -99,10 +103,7 @@ export function computeCharacteristics(
|
||||
}
|
||||
|
||||
return {
|
||||
characteristics: computed as Record<
|
||||
CharacteristicName,
|
||||
CharacteristicOutput
|
||||
>,
|
||||
characteristics: computed as ComputedCharacteristics,
|
||||
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";
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { times } from "lodash-es";
|
||||
import { Suspense, useMemo } from "react";
|
||||
import { TankopediaCharacteristic } from "../../../components/TankopediaCharacteristic";
|
||||
import { TankopediaToy } from "../../../components/TankopediaToy";
|
||||
import { api } from "../../../core/api/dynamic";
|
||||
import { characteristicsOrder } from "../../../core/tankopedia/characteristicsOrder";
|
||||
import { computeCharacteristics } from "../../../core/tankopedia/computeCharacteristics";
|
||||
import { renderCharacteristic } from "../../../core/tankopedia/renderCharacteristic";
|
||||
import { TerrainHardness } from "../../../core/tankopedia/tankState";
|
||||
import { useAwait } from "../../../hooks/useAwait";
|
||||
import { Tankopedia } from "../../../stores/tankopedia";
|
||||
import { Tankopedia, TankopediaCompare } from "../../../stores/tankopedia";
|
||||
|
||||
export function Page({ id }: { id: string }) {
|
||||
return (
|
||||
@@ -18,10 +19,12 @@ export function Page({ id }: { id: string }) {
|
||||
|
||||
function Content({ id }: { id: string }) {
|
||||
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 compare = Tankopedia.use((state) => state.compare);
|
||||
const { characteristics, parameters } = useMemo(
|
||||
() => computeCharacteristics(id, tank!, compensation!, protagonist),
|
||||
[protagonist]
|
||||
@@ -181,27 +184,41 @@ function Content({ id }: { id: string }) {
|
||||
|
||||
<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 }) => (
|
||||
<>
|
||||
<h2 key={group}>{group}</h2>
|
||||
|
||||
{order.map((config) => {
|
||||
if ("toy" in config) {
|
||||
return <p key={`toy-${config.toy}`}>toy: {config.toy}</p>;
|
||||
} else {
|
||||
const characteristic = characteristics[config.name];
|
||||
|
||||
if (characteristic === null) return null;
|
||||
|
||||
const rendered = renderCharacteristic(characteristic, config);
|
||||
|
||||
return (
|
||||
<p key={`characteristic-${config.name}`}>
|
||||
{config.name}: {rendered} {config.units && `${config.units}`}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
})}
|
||||
{order.map((config) =>
|
||||
"toy" in config ? (
|
||||
<TankopediaToy key={`toy-${config.toy}`} toy={config.toy} />
|
||||
) : (
|
||||
<TankopediaCharacteristic
|
||||
key={`characteristic-${config.name}`}
|
||||
characteristic={characteristics[config.name]}
|
||||
config={config}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
</>
|
||||
))}
|
||||
</>
|
||||
|
||||
@@ -8,4 +8,6 @@ import "blitz_static_tank_component.proto";
|
||||
message Tank {
|
||||
blitz_static_tank_component.TankCatalogComponent tank = 1;
|
||||
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;
|
||||
|
||||
message Tanks {
|
||||
repeated TanksEntry tanks = 1;
|
||||
}
|
||||
import "tank.proto";
|
||||
|
||||
message TanksEntry {
|
||||
string id = 1;
|
||||
string slug = 2;
|
||||
message Tanks {
|
||||
map<string, Tank> tanks = 1;
|
||||
}
|
||||
@@ -1,19 +1,22 @@
|
||||
import type { TankCatalogComponent } from "@protos/blitz_static_tank_component";
|
||||
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 {
|
||||
compare: TankopediaCompare;
|
||||
protagonist: TankState;
|
||||
}
|
||||
|
||||
export const Tankopedia = new Varuna<Tankopedia, number>((maxStage) => ({
|
||||
protagonist: {
|
||||
stage: maxStage,
|
||||
shell: 0,
|
||||
isGunDamaged: false,
|
||||
isHullTraversing: false,
|
||||
isShooting: false,
|
||||
isTurretTraversing: false,
|
||||
speed: 0,
|
||||
terrainHardness: TerrainHardness.Hard,
|
||||
},
|
||||
}));
|
||||
export enum TankopediaCompare {
|
||||
TierAndClass,
|
||||
Tier,
|
||||
All,
|
||||
}
|
||||
|
||||
export const Tankopedia = new Varuna<Tankopedia, TankCatalogComponent>(
|
||||
(tank) => ({
|
||||
compare: TankopediaCompare.TierAndClass,
|
||||
protagonist: createTankState(tank),
|
||||
})
|
||||
);
|
||||
Reference in new issue
Block a user