mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-24 12:09:51 +02:00
Compare commits
24
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0834ed572 | ||
|
|
caf238dd32 | ||
|
|
0a7a220af3 | ||
|
|
434d1ede3c | ||
|
|
0f47208f5e | ||
|
|
cb2d795461 | ||
|
|
6e934e0ff1 | ||
|
|
375adadbe5 | ||
|
|
082a756f1b | ||
|
|
9fc8a8e2a2 | ||
|
|
1413475679 | ||
|
|
86504e685e | ||
|
|
9df81044d1 | ||
|
|
c5fd7591ea | ||
|
|
cd8e43f945 | ||
|
|
8df112aab4 | ||
|
|
94c04ce102 | ||
|
|
3a170202ca | ||
|
|
46937693a0 | ||
|
|
b391d0b036 | ||
|
|
1061e003f7 | ||
|
|
a2b573abca | ||
|
|
4571d141e6 | ||
|
|
d70225b03c |
No files matched your search
Vendored
+1
@@ -192,6 +192,7 @@
|
||||
"searchtanks",
|
||||
"SHAREDEXP",
|
||||
"SKLLD",
|
||||
"sluggify",
|
||||
"smoothstep",
|
||||
"Smri",
|
||||
"Specularity",
|
||||
|
||||
@@ -173,6 +173,7 @@
|
||||
"react-promise-suspense": "^0.3.4",
|
||||
"three": "^0.180.0",
|
||||
"three-stdlib": "^2.36.0",
|
||||
"transliteration": "^2.6.1",
|
||||
"typescript": "^5.9.3",
|
||||
"urlon": "^3.1.0",
|
||||
"varuna": "workspace:*",
|
||||
@@ -2427,6 +2428,8 @@
|
||||
|
||||
"tr46": ["[email protected]", "", { "dependencies": { "punycode": "^2.1.0" } }, "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA=="],
|
||||
|
||||
"transliteration": ["[email protected]", "", { "bin": { "transliterate": "dist/bin/transliterate", "slugify": "dist/bin/slugify" } }, "sha512-hJ9BhrQAOnNTbpOr1MxsNjZISkn7ppvF5TKUeFmTE1mG4ZPD/XVxF0L0LUoIUCWmQyxH0gJpVtfYLAWf298U9w=="],
|
||||
|
||||
"trim-lines": ["[email protected]", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="],
|
||||
|
||||
"troika-three-text": ["[email protected]", "", { "dependencies": { "bidi-js": "^1.0.2", "troika-three-utils": "^0.52.4", "troika-worker-utils": "^0.52.0", "webgl-sdf-generator": "1.1.1" }, "peerDependencies": { "three": ">=0.125.0" } }, "sha512-V50EwcYGruV5rUZ9F4aNsrytGdKcXKALjEtQXIOBfhVoZU9VAqZNIoGQ3TMiooVqFAbR1w15T+f+8gkzoFzawg=="],
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# BlitzKit 1.30.7
|
||||
|
||||
Thank you to [Mark Greene](https://github.com/Hitjack007) for your contributions this update.
|
||||
|
||||
## Features
|
||||
|
||||
- Dragging items in the Tier List now automatically scrolls the screen if you reach the top/bottom edge of the screen
|
||||
|
||||
## Fixes
|
||||
|
||||
- [Mark Greene] Clicking on the armor multiple times with HE no longer crashes BlitzKit
|
||||
- [Mark Greene] Ricochets that later intersect with external modules no longer falsely claim a successful damaging penetration
|
||||
- The lighting animation no longer stops prematurely when BlitzKit is unfocused
|
||||
- The spot light angle has been increased to more evenly light up larger tanks
|
||||
- BlitzKit once again uses soft shadows, reducing the pixelated shadows
|
||||
- Fixed the weird patterns that formed on the tracks of tanks under self-shadows
|
||||
- Increased the floor size and light decay to create the illusion that the floor is infinite
|
||||
- [Mark Greene] Safari no longer crashes when unloading textures for unloaded tanks
|
||||
- The gun depression transition between two constraints (like often behind tanks) now uses a gentler zoned and can never be 0
|
||||
- [Mark Greene] Armor plate names in Static Armor are now properly capitalized in English and translated to other languages
|
||||
@@ -0,0 +1,8 @@
|
||||
# BlitzKit 1.30.8
|
||||
|
||||
Support for tanks with Chinese names.
|
||||
|
||||
## Fixes
|
||||
|
||||
- The URL generator for BlitzKit now handles names that have only Chinese characters
|
||||
- The Mixed Virtual File System now handles non-existent directories correctly
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "blitzkit",
|
||||
"version": "1.30.6",
|
||||
"version": "1.30.8",
|
||||
"license": "BlitzKit License",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
|
||||
@@ -1,49 +1,50 @@
|
||||
export * from './assertSecret';
|
||||
export * from './asset';
|
||||
export * from './availableProvisions';
|
||||
export * from './averageDefinitions';
|
||||
export * from "./assertSecret";
|
||||
export * from "./asset";
|
||||
export * from "./availableProvisions";
|
||||
export * from "./averageDefinitions";
|
||||
// export * from './camouflageDefinitions';
|
||||
// export * from './checkConsumableProvisionInclusivity';
|
||||
export * from './coefficient';
|
||||
export * from './consumableDefinitions';
|
||||
export * from "./coefficient";
|
||||
export * from "./consumableDefinitions";
|
||||
// export * from './context';
|
||||
export * from './createDefaultSkills';
|
||||
export * from './degressiveStat';
|
||||
export * from './discoveredIdDefinitions';
|
||||
export * from './emblemURL';
|
||||
export * from './equipmentDefinitions';
|
||||
export * from './eventManager';
|
||||
export * from './gameDefinitions';
|
||||
export * from './getArchivedLatestSeasonNumber';
|
||||
export * from "./createDefaultSkills";
|
||||
export * from "./degressiveStat";
|
||||
export * from "./discoveredIdDefinitions";
|
||||
export * from "./emblemURL";
|
||||
export * from "./equipmentDefinitions";
|
||||
export * from "./eventManager";
|
||||
export * from "./gameDefinitions";
|
||||
export * from "./getArchivedLatestSeasonNumber";
|
||||
export * from "./sluggify";
|
||||
// export * from './getArchivedRatingInfo';
|
||||
export * from './getArchivedRatingLeaderboard';
|
||||
export * from "./getArchivedRatingLeaderboard";
|
||||
// export * from './getArchivedRatingMidnightLeaderboard';
|
||||
export * from './getTimeDaysAgo';
|
||||
export * from './isExplosive';
|
||||
export * from './mapDefinitions';
|
||||
export * from './modelDefinitions';
|
||||
export * from './normalizeBoundingBox';
|
||||
export * from "./getTimeDaysAgo";
|
||||
export * from "./isExplosive";
|
||||
export * from "./mapDefinitions";
|
||||
export * from "./modelDefinitions";
|
||||
export * from "./normalizeBoundingBox";
|
||||
// export * from './parseBkni';
|
||||
// export * from './patientFetch';
|
||||
export * from './patientFetchJSON';
|
||||
export * from './progressiveStat';
|
||||
export * from './provisionDefinitions';
|
||||
export * from './resolveDpm';
|
||||
export * from './resolvePenetrationCoefficient';
|
||||
export * from './reviews';
|
||||
export * from './skillDefinitions';
|
||||
export * from './sum';
|
||||
export * from './tankDefinitions';
|
||||
export * from './tankIcon';
|
||||
export * from './tankNames';
|
||||
export * from './unionBoundingBox';
|
||||
export * from './uniqueGuns';
|
||||
export * from './unwrapBlitzkitResponse';
|
||||
export * from './votes';
|
||||
export * from "./patientFetchJSON";
|
||||
export * from "./progressiveStat";
|
||||
export * from "./provisionDefinitions";
|
||||
export * from "./resolveDpm";
|
||||
export * from "./resolvePenetrationCoefficient";
|
||||
export * from "./reviews";
|
||||
export * from "./skillDefinitions";
|
||||
export * from "./sum";
|
||||
export * from "./tankDefinitions";
|
||||
export * from "./tankIcon";
|
||||
export * from "./tankNames";
|
||||
export * from "./unionBoundingBox";
|
||||
export * from "./uniqueGuns";
|
||||
export * from "./unwrapBlitzkitResponse";
|
||||
export * from "./votes";
|
||||
// export * from './withCORSProxy';
|
||||
export * from './createDefaultProvisions';
|
||||
export * from './gallery';
|
||||
export * from './metaSortTank';
|
||||
export * from './popularTanks';
|
||||
export * from './romanize';
|
||||
export * from './youtubers';
|
||||
export * from "./createDefaultProvisions";
|
||||
export * from "./gallery";
|
||||
export * from "./metaSortTank";
|
||||
export * from "./popularTanks";
|
||||
export * from "./romanize";
|
||||
export * from "./youtubers";
|
||||
@@ -8,12 +8,12 @@ export function resolvePenetrationCoefficient(
|
||||
) {
|
||||
const calibrated = calibratedShells
|
||||
? type === ShellType.SHELL_TYPE_AP
|
||||
? 1.08
|
||||
? 1.06
|
||||
: type === ShellType.SHELL_TYPE_APCR
|
||||
? 1.05
|
||||
? 1.06
|
||||
: type === ShellType.SHELL_TYPE_HEAT
|
||||
? 1.13
|
||||
: 1.08
|
||||
? 1.07
|
||||
: 1.07
|
||||
: 1;
|
||||
const equalizer = (equalize ? equalizerObj?.penetration : undefined) ?? 1;
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
import { slugify } from "transliteration";
|
||||
|
||||
const nonAlphanumericRegex = /[^a-z0-9]/g;
|
||||
const multipleDashesRegex = /--+/g;
|
||||
const trailingDashRegex = /-$/g;
|
||||
const leadingDashRegex = /^-/g;
|
||||
|
||||
export function sluggify(value: string) {
|
||||
let slug = slugify(value);
|
||||
slug = slug.replaceAll(nonAlphanumericRegex, "-");
|
||||
slug = slug.replaceAll(multipleDashesRegex, "-");
|
||||
slug = slug.replaceAll(trailingDashRegex, "");
|
||||
slug = slug.replaceAll(leadingDashRegex, "");
|
||||
|
||||
return slug;
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
import { assertSecret } from "@blitzkit/core";
|
||||
import { AbstractVFS } from "../core/vfs/abstract";
|
||||
import { LocalVFS } from "../core/vfs/local";
|
||||
import { MixedVFS } from "../core/vfs/mixed";
|
||||
import { SteamVFS } from "../core/vfs/steam";
|
||||
import { ZipVFS } from "../core/vfs/zip";
|
||||
import { MixedVFS } from "../core/vfs/mixed";
|
||||
|
||||
export const PATCHES_ROOT = "../../temp/patches";
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
ResearchCost,
|
||||
ShellType,
|
||||
SkillDefinitions,
|
||||
sluggify,
|
||||
TankClass,
|
||||
TankDefinitions,
|
||||
TankPrice,
|
||||
@@ -34,7 +35,6 @@ import {
|
||||
import { SUPPORTED_LOCALE_BLITZ_MAP } from "@blitzkit/i18n";
|
||||
import locales from "@blitzkit/i18n/locales.json";
|
||||
import { readFile } from "fs/promises";
|
||||
import { deburr } from "lodash-es";
|
||||
import { parse as parsePath } from "path";
|
||||
import type { Vector3Tuple } from "three";
|
||||
import { parse as parseYaml } from "yaml";
|
||||
@@ -43,11 +43,6 @@ import { vfs } from "./constants";
|
||||
import type { Avatar } from "./skillIcons";
|
||||
import type { TankParameters } from "./tankIcons";
|
||||
|
||||
const nonAlphanumericRegex = /[^a-z0-9]/g;
|
||||
const multipleDashesRegex = /--+/g;
|
||||
const trailingDashRegex = /-$/g;
|
||||
const leadingDashRegex = /^-/g;
|
||||
|
||||
const nationSlugDiscriminators = {
|
||||
china: "cn",
|
||||
european: "eu",
|
||||
@@ -770,11 +765,7 @@ export async function definitions() {
|
||||
getString(tank.userString)
|
||||
).locales.en;
|
||||
|
||||
let slug = deburr(name).toLowerCase();
|
||||
slug = slug.replaceAll(nonAlphanumericRegex, "-");
|
||||
slug = slug.replaceAll(multipleDashesRegex, "-");
|
||||
slug = slug.replaceAll(trailingDashRegex, "");
|
||||
slug = slug.replaceAll(leadingDashRegex, "");
|
||||
let slug = sluggify(name);
|
||||
|
||||
idToNation[tankId] = nation;
|
||||
|
||||
|
||||
@@ -24,7 +24,11 @@ export class LocalVFS extends AbstractVFS {
|
||||
}
|
||||
|
||||
async dir(path: string) {
|
||||
return await readdir(`${this.base}/${path}`);
|
||||
try {
|
||||
return await readdir(`${this.base}/${path}`);
|
||||
} catch (_) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
dispose() {}
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"react-promise-suspense": "^0.3.4",
|
||||
"three": "^0.180.0",
|
||||
"three-stdlib": "^2.36.0",
|
||||
"transliteration": "^2.6.1",
|
||||
"typescript": "^5.9.3",
|
||||
"urlon": "^3.1.0",
|
||||
"varuna": "workspace:*",
|
||||
|
||||
@@ -22,7 +22,7 @@ export function ArmorPlateDisplay() {
|
||||
const highlightArmor = Tankopedia.use((state) => state.highlightArmor);
|
||||
const developerMode = App.useDeferred((state) => state.developerMode, false);
|
||||
const input = useRef<HTMLInputElement>(null);
|
||||
const { locale } = useLocale();
|
||||
const { locale, strings } = useLocale();
|
||||
|
||||
if (highlightArmor === undefined) return null;
|
||||
|
||||
@@ -44,7 +44,10 @@ export function ArmorPlateDisplay() {
|
||||
>
|
||||
<Flex direction="column">
|
||||
<Text weight="bold">
|
||||
{layerTypeNames[highlightArmor.type]}{" "}
|
||||
{
|
||||
strings.website.tools.tankopedia.sandbox.dynamic.shot_card
|
||||
.element[layerTypeNames[highlightArmor.type]]
|
||||
}{" "}
|
||||
{highlightArmor.thickness.toFixed(0)}
|
||||
<Text size="1" weight="regular">
|
||||
mm
|
||||
|
||||
+1
-1
@@ -129,7 +129,7 @@ export function PrimaryArmorSceneComponent({
|
||||
: undefined) ?? 1;
|
||||
|
||||
material.uniforms.thickness.value =
|
||||
thickness * (hasEnhancedArmor ? 1.03 : 1) * equalizer;
|
||||
thickness * (hasEnhancedArmor ? 1.04 : 1) * equalizer;
|
||||
|
||||
if (!noInvalidate) invalidate();
|
||||
}
|
||||
|
||||
@@ -42,10 +42,10 @@ const TRACER_THIN = TRACER_THICK / 2;
|
||||
|
||||
export function ShotDisplay() {
|
||||
const shot = Tankopedia.use((state) => state.shot);
|
||||
const inTracer = useRef<Mesh>(null!);
|
||||
const outTracer = useRef<Mesh>(null!);
|
||||
const splashRadiusWrapper = useRef<Group>(null!);
|
||||
const splashRadiusMaterial = useRef<MeshBasicMaterial>(null!);
|
||||
const inTracer = useRef<Mesh>(null);
|
||||
const outTracer = useRef<Mesh>(null);
|
||||
const splashRadiusWrapper = useRef<Group>(null);
|
||||
const splashRadiusMaterial = useRef<MeshBasicMaterial>(null);
|
||||
const { locale } = useLocale();
|
||||
|
||||
useEffect(() => {
|
||||
@@ -63,10 +63,10 @@ export function ShotDisplay() {
|
||||
outlineMaterial,
|
||||
).computeLineDistances();
|
||||
|
||||
splashRadiusWrapper.current.add(outline);
|
||||
splashRadiusWrapper.current?.add(outline);
|
||||
|
||||
return () => {
|
||||
splashRadiusWrapper.current.remove(outline);
|
||||
splashRadiusWrapper.current?.remove(outline);
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -255,11 +255,11 @@ export function ShotDisplay() {
|
||||
}
|
||||
|
||||
interface AnimatorProps {
|
||||
inTracer: RefObject<Mesh>;
|
||||
outTracer: RefObject<Mesh>;
|
||||
inTracer: RefObject<Mesh | null>;
|
||||
outTracer: RefObject<Mesh | null>;
|
||||
|
||||
splashRadiusWrapper: RefObject<Group>;
|
||||
splashRadiusMaterial: RefObject<MeshBasicMaterial>;
|
||||
splashRadiusWrapper: RefObject<Group | null>;
|
||||
splashRadiusMaterial: RefObject<MeshBasicMaterial | null>;
|
||||
|
||||
inLength: number;
|
||||
outLength: number;
|
||||
@@ -283,17 +283,23 @@ function Animator({
|
||||
const tracerT1 = t % 1;
|
||||
const tracerT2 = (t + 0.5) % 1;
|
||||
|
||||
inTracer.current.scale.set(1, 1 - 2 * Math.abs(tracerT1 - 0.5), 1);
|
||||
inTracer.current.position.set(0, (1 - tracerT1) * inLength, 0);
|
||||
if (inTracer.current) {
|
||||
inTracer.current.scale.set(1, 1 - 2 * Math.abs(tracerT1 - 0.5), 1);
|
||||
inTracer.current.position.set(0, (1 - tracerT1) * inLength, 0);
|
||||
|
||||
if (outTracer.current) {
|
||||
outTracer.current.scale.set(1, 1 - 2 * Math.abs(tracerT2 - 0.5), 1);
|
||||
outTracer.current.position.set(0, tracerT2 * outLength, 0);
|
||||
if (outTracer.current) {
|
||||
outTracer.current.scale.set(1, 1 - 2 * Math.abs(tracerT2 - 0.5), 1);
|
||||
outTracer.current.position.set(0, tracerT2 * outLength, 0);
|
||||
}
|
||||
|
||||
invalidate();
|
||||
}
|
||||
|
||||
invalidate();
|
||||
|
||||
if (shot.splashRadius !== undefined) {
|
||||
if (
|
||||
shot.splashRadius !== undefined &&
|
||||
splashRadiusWrapper.current &&
|
||||
splashRadiusMaterial.current
|
||||
) {
|
||||
if (animationStartTime === null) animationStartTime = t;
|
||||
|
||||
const scale =
|
||||
|
||||
+1
-1
@@ -65,7 +65,7 @@ export function SpacedArmorSubExternal({
|
||||
: undefined) ?? 1;
|
||||
|
||||
material.uniforms.thickness.value =
|
||||
thickness * (hasEnhancedArmor ? 1.03 : 1) * equalizer;
|
||||
thickness * (hasEnhancedArmor ? 1.04 : 1) * equalizer;
|
||||
}
|
||||
function handleAntagonistEquipmentChange() {
|
||||
const equipment = Duel.state.antagonist.equipmentMatrix;
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ export function SpacedArmorSubSpaced({
|
||||
: undefined) ?? 1;
|
||||
|
||||
material.uniforms.thickness.value =
|
||||
thickness * (hasEnhancedArmor ? 1.03 : 1) * equalizer;
|
||||
thickness * (hasEnhancedArmor ? 1.04 : 1) * equalizer;
|
||||
}
|
||||
function handleAntagonistEquipmentChange() {
|
||||
const equipment = Duel.state.antagonist.equipmentMatrix;
|
||||
|
||||
+5
-4
@@ -129,7 +129,7 @@ export function SpacedArmorSceneComponent({
|
||||
antagonistEqualizer,
|
||||
);
|
||||
const thicknessCoefficient =
|
||||
(hasEnhancedArmor ? 1.03 : 1) * protagonistEqualizer.armor;
|
||||
(hasEnhancedArmor ? 1.04 : 1) * protagonistEqualizer.armor;
|
||||
const filteredIntersections = intersections.filter(
|
||||
(intersection) =>
|
||||
"type" in intersection.object.userData &&
|
||||
@@ -161,9 +161,10 @@ export function SpacedArmorSceneComponent({
|
||||
|
||||
if (
|
||||
!allowRicochet &&
|
||||
noDuplicateIntersections.every((intersection) => {
|
||||
intersection.object.userData.type !== ArmorType.Primary;
|
||||
})
|
||||
noDuplicateIntersections.every(
|
||||
(intersection) =>
|
||||
intersection.object.userData.type !== ArmorType.Primary,
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export function StaticArmorSceneComponent({
|
||||
const tank = Duel.use((state) => state.protagonist.tank);
|
||||
const equalize = Duel.use((state) => state.equalize);
|
||||
const equalizer = (equalize ? tank.equalizer : undefined) ?? defaultEqualizer;
|
||||
const thicknessCoefficient = (hasEnhancedArmor ? 1.03 : 1) * equalizer.armor;
|
||||
const thicknessCoefficient = (hasEnhancedArmor ? 1.04 : 1) * equalizer.armor;
|
||||
const thickness = thicknessRaw * thicknessCoefficient;
|
||||
const x = thickness / thicknessRange.value;
|
||||
const xClamped = clamp(x, 0, 1);
|
||||
|
||||
@@ -43,7 +43,7 @@ export function ConsumablesManager({
|
||||
cooldown={
|
||||
timers
|
||||
? consumable.cooldown *
|
||||
(hasConsumableDeliverySystem ? 0.88 : 1) *
|
||||
(hasConsumableDeliverySystem ? 1 - 0.17 : 1) *
|
||||
(1 - cooldownBooster! * 0.1)
|
||||
: undefined
|
||||
}
|
||||
|
||||
+28
-18
@@ -1,14 +1,20 @@
|
||||
import { useFrame } from "@react-three/fiber";
|
||||
import { clamp, times } from "lodash-es";
|
||||
import { Quicklime } from "quicklime";
|
||||
import { useEffect, useRef, useState, type RefObject } from "react";
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
type RefObject,
|
||||
} from "react";
|
||||
import { HemisphereLight, SpotLight, type Group } from "three";
|
||||
import { degToRad, lerp } from "three/src/math/MathUtils.js";
|
||||
import { Tankopedia } from "../../../../../../stores/tankopedia";
|
||||
import { TankopediaPersistent } from "../../../../../../stores/tankopediaPersistent";
|
||||
import { HelpingSpotLight } from "../../../../../HelpingSpotLight";
|
||||
|
||||
const ANGLE = degToRad(10);
|
||||
const ANGLE = degToRad(12);
|
||||
const REVEAL_ANIMATION_TIME = 3;
|
||||
const TRANSITION_ANIMATION_TIME = 0.5;
|
||||
|
||||
@@ -17,8 +23,8 @@ const THETA_OFFSET = degToRad(180 - 45);
|
||||
const LIGHT_DISTANCE = 20;
|
||||
const LIGHT_HEIGHT_0 = 4;
|
||||
const LIGHT_HEIGHT_1 = 6;
|
||||
const INTENSITY_0 = 6;
|
||||
const INTENSITY_1 = 3;
|
||||
const INTENSITY_0 = 6 * 9;
|
||||
const INTENSITY_1 = 3 * 9;
|
||||
const HEMISPHERE_INTENSITY = 2;
|
||||
|
||||
export const transitionEvent = new Quicklime<number>(0);
|
||||
@@ -36,8 +42,6 @@ export function Lighting() {
|
||||
|
||||
const [animate, setAnimate] = useState(true);
|
||||
|
||||
console.log(animate);
|
||||
|
||||
useEffect(() => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
draft.revealed = true;
|
||||
@@ -89,7 +93,7 @@ export function Lighting() {
|
||||
intensity={intensity}
|
||||
penumbra={1}
|
||||
castShadow={highGraphics}
|
||||
decay={0}
|
||||
decay={1}
|
||||
color="#ffffff"
|
||||
angle={0}
|
||||
/>
|
||||
@@ -111,17 +115,7 @@ interface AnimatorProps {
|
||||
function Animator({ stop, t0, animationTime, wrapper }: AnimatorProps) {
|
||||
const requestedDisplay = Tankopedia.use((state) => state.requestedDisplay);
|
||||
|
||||
useFrame(({ invalidate }) => {
|
||||
const dt = performance.now() / 1e3 - t0.current;
|
||||
const x = clamp(dt / animationTime.current, 0, 2);
|
||||
|
||||
if (x === 2) {
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
const t = (0.5 * Math.sin(Math.PI * (x + 0.5)) + 0.5) ** 2;
|
||||
|
||||
const apply = useCallback((t: number) => {
|
||||
for (const child of wrapper.current.children) {
|
||||
if (child instanceof SpotLight) {
|
||||
child.angle = ANGLE * t;
|
||||
@@ -131,6 +125,22 @@ function Animator({ stop, t0, animationTime, wrapper }: AnimatorProps) {
|
||||
}
|
||||
|
||||
transitionEvent.dispatch(t);
|
||||
}, []);
|
||||
|
||||
useFrame(({ invalidate }) => {
|
||||
const dt = performance.now() / 1e3 - t0.current;
|
||||
const x = clamp(dt / animationTime.current, 0, 2);
|
||||
|
||||
if (x === 2) {
|
||||
apply(1);
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
|
||||
// https://www.desmos.com/calculator/awsbahxjku
|
||||
const t = (0.5 * Math.sin(Math.PI * (x + 0.5)) + 0.5) ** 2;
|
||||
|
||||
apply(t);
|
||||
invalidate();
|
||||
|
||||
if (x >= 1 && Tankopedia.state.display !== requestedDisplay) {
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { Quicklime, type QuicklimeEvent } from "quicklime";
|
||||
import { useEffect, useRef } from "react";
|
||||
import { Mesh, MeshStandardMaterial } from "three";
|
||||
|
||||
const SIZE = 14;
|
||||
const SIZE = 2 ** 4;
|
||||
|
||||
export const screenshotReadyEvent = new Quicklime(false);
|
||||
|
||||
|
||||
+1
-1
@@ -156,7 +156,7 @@ export const TankSandbox = forwardRef<HTMLCanvasElement, TankSandboxProps>(
|
||||
localClippingEnabled: true,
|
||||
preserveDrawingBuffer: true,
|
||||
}}
|
||||
shadows="basic"
|
||||
shadows="soft"
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerMissed={() => {
|
||||
Tankopedia.mutate((draft) => {
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
import type { TankDefinition } from '@blitzkit/core';
|
||||
import { Box } from '@radix-ui/themes';
|
||||
import type { TankDefinition } from "@blitzkit/core";
|
||||
import { Box } from "@radix-ui/themes";
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
type PointerEvent as ReactPointerEvent,
|
||||
} from 'react';
|
||||
import { TierList } from '../../stores/tierList';
|
||||
import { TankCard } from '../TankCard';
|
||||
import { tierListCardElements, tierListRowElements } from './Table/constants';
|
||||
} from "react";
|
||||
import { TierList } from "../../stores/tierList";
|
||||
import { TankCard } from "../TankCard";
|
||||
import { tierListCardElements, tierListRowElements } from "./Table/constants";
|
||||
|
||||
const SCROLL_ZONE = 1 / 3;
|
||||
|
||||
type TierListTileProps = {
|
||||
tank: TankDefinition;
|
||||
@@ -26,6 +28,33 @@ type TierListTileProps = {
|
||||
export function TierListTile(props: TierListTileProps) {
|
||||
const card = useRef<HTMLDivElement>(null);
|
||||
const lastPosition = useRef({ x: 0, y: 0 });
|
||||
const autoScroll = useRef(false);
|
||||
const lastT = useRef(0);
|
||||
|
||||
const autoScrollLoop = useCallback(() => {
|
||||
if (!autoScroll.current) return;
|
||||
|
||||
let scrollSpeed = 0;
|
||||
const v = lastPosition.current.y / window.innerHeight;
|
||||
|
||||
if (v <= SCROLL_ZONE) {
|
||||
scrollSpeed = v / SCROLL_ZONE - 1;
|
||||
} else if (v >= 1 - SCROLL_ZONE) {
|
||||
scrollSpeed = 1 - (1 - v) / SCROLL_ZONE;
|
||||
}
|
||||
|
||||
const t = performance.now();
|
||||
const dt = t - lastT.current;
|
||||
lastT.current = t;
|
||||
|
||||
window.scrollBy({
|
||||
behavior: "instant",
|
||||
top: scrollSpeed * dt,
|
||||
});
|
||||
|
||||
requestAnimationFrame(autoScrollLoop);
|
||||
}, []);
|
||||
|
||||
const handlePointerDown = useCallback(
|
||||
(event: ReactPointerEvent<HTMLSpanElement>) => {
|
||||
if (!card.current) return;
|
||||
@@ -34,22 +63,26 @@ export function TierListTile(props: TierListTileProps) {
|
||||
|
||||
const rect = card.current.getBoundingClientRect();
|
||||
|
||||
card.current.style.position = 'fixed';
|
||||
card.current.style.cursor = 'grabbing';
|
||||
card.current.style.zIndex = '1';
|
||||
card.current.style.position = "fixed";
|
||||
card.current.style.cursor = "grabbing";
|
||||
card.current.style.zIndex = "1";
|
||||
card.current.style.left = `${rect.left}px`;
|
||||
card.current.style.top = `${rect.top}px`;
|
||||
|
||||
lastPosition.current = { x: event.clientX, y: event.clientY };
|
||||
|
||||
window.scrollTo({ behavior: 'smooth', top: 124 });
|
||||
window.scrollTo({ behavior: "instant", top: 124 });
|
||||
|
||||
window.addEventListener('pointermove', handlePointerMove);
|
||||
window.addEventListener('pointerup', handlePointerUp);
|
||||
window.addEventListener("pointermove", handlePointerMove);
|
||||
window.addEventListener("pointerup", handlePointerUp);
|
||||
|
||||
TierList.mutate((draft) => {
|
||||
draft.dragging = true;
|
||||
});
|
||||
|
||||
autoScroll.current = true;
|
||||
lastT.current = performance.now();
|
||||
autoScrollLoop();
|
||||
},
|
||||
[],
|
||||
);
|
||||
@@ -141,12 +174,14 @@ export function TierListTile(props: TierListTileProps) {
|
||||
});
|
||||
}
|
||||
|
||||
card.current.style.position = 'static';
|
||||
card.current.style.zIndex = 'unset';
|
||||
card.current.style.cursor = 'grab';
|
||||
card.current.style.position = "static";
|
||||
card.current.style.zIndex = "unset";
|
||||
card.current.style.cursor = "grab";
|
||||
|
||||
window.removeEventListener('pointermove', handlePointerMove);
|
||||
window.removeEventListener('pointerup', handlePointerUp);
|
||||
window.removeEventListener("pointermove", handlePointerMove);
|
||||
window.removeEventListener("pointerup", handlePointerUp);
|
||||
|
||||
autoScroll.current = false;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -168,7 +203,7 @@ export function TierListTile(props: TierListTileProps) {
|
||||
data-tile-index={props.isPlaced ? props.tileIndex : undefined}
|
||||
data-tile-id={props.tank.id}
|
||||
onPointerDown={handlePointerDown}
|
||||
style={{ cursor: 'grab', touchAction: 'none' }}
|
||||
style={{ cursor: "grab", touchAction: "none" }}
|
||||
>
|
||||
<TankCard noLink tank={props.tank} />
|
||||
</Box>
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
import { clamp } from "lodash-es";
|
||||
import { degToRad } from "three/src/math/MathUtils.js";
|
||||
|
||||
export const DEFAULT_PITCH_TRANSITION = 15;
|
||||
export const DEFAULT_PITCH_TRANSITION = 20;
|
||||
|
||||
export function applyPitchYawLimits(
|
||||
pitch: number, // rad
|
||||
@@ -31,7 +31,7 @@ export function applyPitchYawLimits(
|
||||
let lowerPitch = -degToRad(pitchLimits.max) - depressionBuff;
|
||||
let upperPitch = -degToRad(pitchLimits.min) + elevationBuff;
|
||||
const transition = degToRad(
|
||||
pitchLimits.transition ?? DEFAULT_PITCH_TRANSITION,
|
||||
pitchLimits.transition || DEFAULT_PITCH_TRANSITION,
|
||||
);
|
||||
|
||||
if (pitchLimits.back) {
|
||||
|
||||
@@ -193,7 +193,7 @@ export function tankCharacteristics(
|
||||
coefficient(
|
||||
[hasSandbagArmor, 0.03],
|
||||
[hasEnhancedSandbagArmor, 0.06],
|
||||
[hasImprovedAssembly, 0.04],
|
||||
[hasImprovedAssembly, 0.05],
|
||||
) * equalizer.health;
|
||||
const shellVelocityCoefficient = coefficient(
|
||||
[hasSupercharger, 0.35],
|
||||
@@ -201,40 +201,34 @@ export function tankCharacteristics(
|
||||
);
|
||||
const aimTimeCoefficient =
|
||||
coefficient(
|
||||
[hasEnhancedGunLayingDrive, -0.15],
|
||||
[hasEnhancedGunLayingDrive, -0.12],
|
||||
[hasReticleCalibration, -0.3],
|
||||
) * coefficient([true, degressiveStat(gunnerMastery)]);
|
||||
const dispersionStillCoefficient =
|
||||
coefficient([hasRefinedGun, -0.15], [hasReticleCalibration, -0.3]) *
|
||||
coefficient([hasRefinedGun, -0.11], [hasReticleCalibration, -0.3]) *
|
||||
coefficient([true, degressiveStat(gunnerMastery)]);
|
||||
const dispersionDamagedCoefficient = coefficient([
|
||||
hasVerticalStabilizer,
|
||||
-0.12,
|
||||
-0.17,
|
||||
]);
|
||||
const dispersionHullTraverseCoefficient = coefficient(
|
||||
[hasVerticalStabilizer, -0.12],
|
||||
[hasVerticalStabilizer, -0.17],
|
||||
[true, -crewSkills.smooth_turn / 100],
|
||||
);
|
||||
const dispersionAfterShotCoefficient = coefficient(
|
||||
[hasVerticalStabilizer, -0.12],
|
||||
[hasVerticalStabilizer, -0.17],
|
||||
[true, -crewSkills.soft_recoil / 100],
|
||||
);
|
||||
const dispersionMovingCoefficient = coefficient(
|
||||
[hasVerticalStabilizer, -0.12],
|
||||
[hasVerticalStabilizer, -0.17],
|
||||
[true, -crewSkills.smooth_driving / 100],
|
||||
);
|
||||
const dispersionTurretTraverseCoefficient = coefficient(
|
||||
[hasVerticalStabilizer, -0.12],
|
||||
[hasVerticalStabilizer, -0.17],
|
||||
[true, -crewSkills.smooth_turret * (2 / 100)],
|
||||
);
|
||||
const enginePowerCoefficient = coefficient(
|
||||
[
|
||||
hasEngineAccelerator,
|
||||
tank.class === TankClass.TANK_CLASS_HEAVY ||
|
||||
tank.class === TankClass.TANK_CLASS_TANK_DESTROYER
|
||||
? 0.05
|
||||
: 0.04,
|
||||
],
|
||||
[hasEngineAccelerator, 0.06],
|
||||
[hasReducedEnginePowerBoost, 0.1],
|
||||
[hasEnginePowerBoost, 0.2],
|
||||
[hasImprovedEnginePowerBoost, 0.35],
|
||||
@@ -248,7 +242,7 @@ export function tankCharacteristics(
|
||||
coefficient([true, progressiveStat(gunnerMastery)]);
|
||||
const hullTraverseCoefficient =
|
||||
coefficient(
|
||||
[hasImprovedControl, 0.11],
|
||||
[hasImprovedControl, 0.12],
|
||||
[hasImprovedEnginePowerBoost, 0.01],
|
||||
[true, crewSkills.virtuoso / 100],
|
||||
) * coefficient([true, progressiveStat(driverMastery)]);
|
||||
@@ -305,8 +299,8 @@ export function tankCharacteristics(
|
||||
: tank.class === TankClass.TANK_CLASS_MEDIUM
|
||||
? 0.04
|
||||
: tank.class === TankClass.TANK_CLASS_LIGHT
|
||||
? 0.07
|
||||
: 0.1,
|
||||
? 0.06
|
||||
: 0.07,
|
||||
],
|
||||
);
|
||||
const camouflageCoefficientStill = sum(
|
||||
|
||||
@@ -36,7 +36,10 @@ export function useDispose(gltf: GLTF & ObjectMap, path: string) {
|
||||
for (const map of maps) {
|
||||
if (!map) continue;
|
||||
|
||||
map.source.data.close();
|
||||
if (map.source.data instanceof ImageBitmap) {
|
||||
map.source.data.close();
|
||||
}
|
||||
|
||||
map.dispose();
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user