mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-23 11:36:19 -05:00
Merge remote-tracking branch 'origin/rewrite' into rewrite
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
},
|
||||
{ "name": "builds", "url": "builds" },
|
||||
{ "name": "analyzer", "url": "analyzer" },
|
||||
{ "name": "object-damage-calculator", "url": "object-damage-calculator" },
|
||||
{ "name": "calendar", "url": "calendar" },
|
||||
{ "name": "maps", "url": "maps" },
|
||||
{ "name": "badges", "url": "badges" },
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
import type { DamageType } from "./types";
|
||||
import type objectDamages from "./object-dmg.json";
|
||||
|
||||
export const MAX_LDE_INTENSITY = 21;
|
||||
export const MAX_AP = 57;
|
||||
|
||||
export const DAMAGE_RECEIVERS = [
|
||||
"Bomb_TorpedoBullet", // Torpedo
|
||||
"Chariot", // Crab Tank
|
||||
"Gachihoko_Barrier", // Rainmaker Shield
|
||||
"GreatBarrier_Barrier", // Big Bubbler Shield
|
||||
"GreatBarrier_WeakPoint", // Big Bubbler Weak Point
|
||||
// "InkRail", // InkRail
|
||||
"NiceBall_Armor", // Booyah Bomb Armor
|
||||
"ShockSonar", // Wave Breaker
|
||||
// "Sponge_Versus", // Sponge
|
||||
"GreatBarrier_Barrier", // Big Bubbler Shield
|
||||
"GreatBarrier_WeakPoint", // Big Bubbler Weak Point
|
||||
"Gachihoko_Barrier", // Rainmaker Shield
|
||||
"Wsb_Flag", // Squid Beakon
|
||||
"Wsb_Shield", // Splash Wall
|
||||
"Wsb_Sprinkler", // Sprinkler
|
||||
"Bomb_TorpedoBullet", // Torpedo
|
||||
"BulletUmbrellaCanopyCompact", // Undercover Brella Canopy
|
||||
"BulletUmbrellaCanopyNormal", // Splat Brella Canopy
|
||||
"BulletUmbrellaCanopyWide", // Tenta Brella Canopy
|
||||
@@ -24,6 +23,7 @@ export const DAMAGE_RECEIVERS = [
|
||||
export const DAMAGE_TYPE = [
|
||||
"NORMAL_MIN",
|
||||
"NORMAL_MAX",
|
||||
"NORMAL_MAX_FULL_CHARGE", // Hydra Splatling goes from 32 to 40 dmg when fully charged
|
||||
"DIRECT",
|
||||
"FULL_CHARGE",
|
||||
"MAX_CHARGE",
|
||||
@@ -39,6 +39,7 @@ export const damageTypeToWeaponType: Record<
|
||||
> = {
|
||||
NORMAL_MIN: "MAIN",
|
||||
NORMAL_MAX: "MAIN",
|
||||
NORMAL_MAX_FULL_CHARGE: "MAIN",
|
||||
DIRECT: "MAIN",
|
||||
FULL_CHARGE: "MAIN",
|
||||
MAX_CHARGE: "MAIN",
|
||||
@@ -47,3 +48,107 @@ export const damageTypeToWeaponType: Record<
|
||||
BOMB_NORMAL: "SUB",
|
||||
BOMB_DIRECT: "SUB",
|
||||
};
|
||||
|
||||
export const objectDamageJsonKeyPriority: Record<
|
||||
keyof typeof objectDamages,
|
||||
Array<DamageType> | null
|
||||
> = {
|
||||
Blaster_BlasterMiddle: null,
|
||||
Blaster_BlasterShort: null,
|
||||
Blaster_KillOneShot: ["DIRECT"],
|
||||
Blaster: null,
|
||||
BlowerExhale_BombCore: null,
|
||||
BlowerInhale: null,
|
||||
BombFlower: null,
|
||||
Bomb_CurlingBullet: null,
|
||||
Bomb_DirectHit: ["BOMB_DIRECT"],
|
||||
Bomb_Fizzy: null,
|
||||
Bomb_Suction: null,
|
||||
Bomb_TorpedoBullet: null,
|
||||
Bomb_TorpedoSplashBurst: null,
|
||||
Bomb_Trap: null,
|
||||
Bomb: null,
|
||||
BrushCore: null,
|
||||
BrushSplash: null,
|
||||
CannonMissile: null,
|
||||
ChargerFull_Light: ["FULL_CHARGE"],
|
||||
ChargerFull_Long: ["FULL_CHARGE"],
|
||||
ChargerFull: ["FULL_CHARGE"],
|
||||
Charger_Light: ["MAX_CHARGE", "TAP_SHOT"],
|
||||
Charger_Long: ["MAX_CHARGE", "TAP_SHOT"],
|
||||
Charger: ["MAX_CHARGE", "TAP_SHOT"],
|
||||
Chariot_Body: null,
|
||||
Chariot_Cannon: null,
|
||||
Default: null,
|
||||
EnemyFlyingHohei_BombCore: null,
|
||||
GachihokoTimeUpBurst: null,
|
||||
Gachihoko_BombCore: null,
|
||||
Gachihoko_Bullet: null,
|
||||
Geyser: null,
|
||||
GoldenIkuraAttack: null,
|
||||
InkStormRain: null,
|
||||
InkStorm: null,
|
||||
Jetpack_BombCore: null,
|
||||
Jetpack_Bullet: null,
|
||||
Jetpack_Coop: null,
|
||||
Jetpack_Jet: null,
|
||||
Maneuver_Short: null,
|
||||
Maneuver: null,
|
||||
MicroLaser: null,
|
||||
MissionSalmonBuddy: null,
|
||||
MovePainter_Burst: null,
|
||||
MovePainter_Direct: null,
|
||||
MultiMissile_BombCore: null,
|
||||
MultiMissile_Bullet: null,
|
||||
NiceBall: null,
|
||||
ObjectEffect_Up: null,
|
||||
RollerCore: null,
|
||||
RollerSplash_Compact: null,
|
||||
RollerSplash_Heavy: null,
|
||||
RollerSplash_Hunter: null,
|
||||
RollerSplash: null,
|
||||
Saber_ChargeShot: null,
|
||||
Saber_ChargeSlash: null,
|
||||
Saber_Shot: null,
|
||||
Saber_Slash: null,
|
||||
SakerocketBullet: null,
|
||||
ShelterCanopy_Compact: null,
|
||||
ShelterCanopy_Wide: null,
|
||||
ShelterCanopy: null,
|
||||
ShelterShot_Compact: null,
|
||||
ShelterShot_Wide: null,
|
||||
ShelterShot: null,
|
||||
Shield: null,
|
||||
ShockSonar_Wave: null,
|
||||
Shooter_Blaze: ["NORMAL_MAX", "NORMAL_MIN"],
|
||||
Shooter_Expert: ["NORMAL_MAX", "NORMAL_MIN"],
|
||||
Shooter_First: ["NORMAL_MAX", "NORMAL_MIN"],
|
||||
Shooter_FlashRepeat: ["NORMAL_MAX", "NORMAL_MIN"],
|
||||
Shooter_Flash: ["NORMAL_MAX", "NORMAL_MIN"],
|
||||
Shooter_Gravity: ["NORMAL_MAX", "NORMAL_MIN"],
|
||||
Shooter_Heavy: ["NORMAL_MAX", "NORMAL_MIN"],
|
||||
Shooter_Long: ["NORMAL_MAX", "NORMAL_MIN"],
|
||||
Shooter_Precision: ["NORMAL_MAX", "NORMAL_MIN"],
|
||||
Shooter_Short: ["NORMAL_MAX", "NORMAL_MIN"],
|
||||
Shooter_TripleMiddle: ["NORMAL_MAX", "NORMAL_MIN"],
|
||||
Shooter_TripleQuick: ["NORMAL_MAX", "NORMAL_MIN"],
|
||||
Shooter: null,
|
||||
Skewer_Body: null,
|
||||
Skewer: null,
|
||||
Slosher_Bathtub: null,
|
||||
Slosher_Bear: null,
|
||||
Slosher_WashtubBombCore: null,
|
||||
Slosher_Washtub: null,
|
||||
Slosher: null,
|
||||
Spinner: null,
|
||||
Sprinkler: null,
|
||||
Stringer_Short: null,
|
||||
Stringer: null,
|
||||
SuperHook: null,
|
||||
SuperLanding: null,
|
||||
TripleTornado: null,
|
||||
UltraShot: null,
|
||||
UltraStamp_Swing: null,
|
||||
UltraStamp_Throw_BombCore: null,
|
||||
UltraStamp_Throw: null,
|
||||
};
|
||||
|
||||
114
app/modules/analyzer/objectDamage.test.ts
Normal file
114
app/modules/analyzer/objectDamage.test.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import { suite } from "uvu";
|
||||
import * as assert from "uvu/assert";
|
||||
import type { MainWeaponId } from "../in-game-lists";
|
||||
import { calculateDamage } from "./objectDamage";
|
||||
import { buildStats } from "./stats";
|
||||
import type { AbilityPoints, DamageType } from "./types";
|
||||
|
||||
function calculate({
|
||||
mainWeaponId = 10,
|
||||
abilityPoints = new Map(),
|
||||
damageType = "NORMAL_MAX",
|
||||
}: {
|
||||
mainWeaponId?: MainWeaponId;
|
||||
abilityPoints?: AbilityPoints;
|
||||
damageType?: DamageType;
|
||||
}) {
|
||||
const analyzed = buildStats({
|
||||
weaponSplId: mainWeaponId,
|
||||
});
|
||||
|
||||
return calculateDamage({
|
||||
abilityPoints,
|
||||
analyzed,
|
||||
mainWeaponId,
|
||||
damageType,
|
||||
});
|
||||
}
|
||||
|
||||
const CalculateDamage = suite("calculateDamage()");
|
||||
|
||||
CalculateDamage("BRU increases Splash Wall hitpoints", () => {
|
||||
const withoutBRU = calculate({});
|
||||
const withBRU = calculate({
|
||||
abilityPoints: new Map([["BRU", { ap: 10, apBeforeTacticooler: 10 }]]),
|
||||
});
|
||||
|
||||
const hpWithoutBRU = withoutBRU.find(
|
||||
(d) => d.receiver === "Wsb_Shield"
|
||||
)?.hitPoints;
|
||||
const hpWithBRU = withBRU.find((d) => d.receiver === "Wsb_Shield")?.hitPoints;
|
||||
|
||||
assert.ok(typeof hpWithoutBRU === "number");
|
||||
assert.ok(typeof hpWithBRU === "number");
|
||||
assert.ok(hpWithoutBRU < hpWithBRU);
|
||||
});
|
||||
|
||||
CalculateDamage("SPU increases Big Bubbler hitpoints", () => {
|
||||
const withoutSPU = calculate({});
|
||||
const withSPU = calculate({
|
||||
abilityPoints: new Map([["SPU", { ap: 10, apBeforeTacticooler: 10 }]]),
|
||||
});
|
||||
|
||||
const hpWithoutSPU = withoutSPU.find(
|
||||
(d) => d.receiver === "GreatBarrier_Barrier"
|
||||
)?.hitPoints;
|
||||
const hpWithSPU = withSPU.find(
|
||||
(d) => d.receiver === "GreatBarrier_Barrier"
|
||||
)?.hitPoints;
|
||||
|
||||
assert.ok(typeof hpWithoutSPU === "number");
|
||||
assert.ok(typeof hpWithSPU === "number");
|
||||
assert.ok(hpWithoutSPU < hpWithSPU);
|
||||
});
|
||||
|
||||
const shotsToPopRM: Array<
|
||||
[
|
||||
weaponId: MainWeaponId,
|
||||
damageType: DamageType,
|
||||
shotsToPop: number,
|
||||
shotsToPopOS: number
|
||||
]
|
||||
> = [
|
||||
// Splattershot
|
||||
[40, "NORMAL_MAX", 28, 26],
|
||||
// Range Blaster
|
||||
[220, "DIRECT", 5, 4],
|
||||
// .96 Gal
|
||||
[80, "NORMAL_MAX", 17, 15],
|
||||
// Splat Charger
|
||||
[2010, "FULL_CHARGE", 4, 3],
|
||||
// E-liter 4K
|
||||
[2030, "TAP_SHOT", 13, 12],
|
||||
];
|
||||
|
||||
CalculateDamage(
|
||||
"Calculates matching HTD Rainmaker shield to in-game tests",
|
||||
() => {
|
||||
for (const [
|
||||
mainWeaponId,
|
||||
damageType,
|
||||
shotsToPop,
|
||||
shotsToPopOS,
|
||||
] of shotsToPopRM) {
|
||||
const damages = calculate({ mainWeaponId, damageType });
|
||||
|
||||
const damageVsRM = damages.find(
|
||||
(d) => d.receiver === "Gachihoko_Barrier"
|
||||
)!;
|
||||
|
||||
assert.equal(
|
||||
damageVsRM.damages.find((d) => !d.objectShredder)!.hitsToDestroy,
|
||||
shotsToPop,
|
||||
`Shots to pop wrong for weapon id: ${mainWeaponId}`
|
||||
);
|
||||
assert.equal(
|
||||
damageVsRM.damages.find((d) => d.objectShredder)!.hitsToDestroy,
|
||||
shotsToPopOS,
|
||||
`Shots to pop wrong with OS for weapon id: ${mainWeaponId}`
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
CalculateDamage.run();
|
||||
@@ -10,33 +10,14 @@ import type {
|
||||
SpecialWeaponId,
|
||||
SubWeaponId,
|
||||
} from "../in-game-lists";
|
||||
import { damageTypeToWeaponType, DAMAGE_RECEIVERS } from "./constants";
|
||||
import {
|
||||
damageTypeToWeaponType,
|
||||
DAMAGE_RECEIVERS,
|
||||
objectDamageJsonKeyPriority,
|
||||
} from "./constants";
|
||||
import { roundToNDecimalPlaces } from "~/utils/number";
|
||||
import { objectHitPoints } from "./objectHitPoints";
|
||||
|
||||
/** Keys to check in the json. Lower index takes priority over higher. If key is omitted means any key with valid weapon id is okay. One json key can only map to one DamageType. */
|
||||
const objectDamageJsonKeyPriority: Partial<
|
||||
Record<DamageType, Array<keyof typeof objectDamages>>
|
||||
> = {
|
||||
// NORMAL_MIN: [],
|
||||
// NORMAL_MAX: [],
|
||||
DIRECT: ["Blaster_KillOneShot"],
|
||||
FULL_CHARGE: [],
|
||||
MAX_CHARGE: [],
|
||||
TAP_SHOT: [],
|
||||
// DISTANCE: [],
|
||||
// BOMB_NORMAL: [],
|
||||
BOMB_DIRECT: ["Bomb_DirectHit"],
|
||||
};
|
||||
|
||||
const commonObjectDamageJsonKeys = () =>
|
||||
Object.keys(objectDamages).filter(
|
||||
(key) =>
|
||||
!Object.values(objectDamageJsonKeyPriority)
|
||||
.flat()
|
||||
.includes(key as any)
|
||||
) as Array<keyof typeof objectDamages>;
|
||||
|
||||
export function damageTypeToMultipliers({
|
||||
type,
|
||||
weapon,
|
||||
@@ -56,10 +37,7 @@ export function damageTypeToMultipliers({
|
||||
id: SpecialWeaponId;
|
||||
};
|
||||
}) {
|
||||
const keysToCheck =
|
||||
objectDamageJsonKeyPriority[type] ?? commonObjectDamageJsonKeys();
|
||||
|
||||
for (const key of keysToCheck) {
|
||||
for (const key of jsonKeysToCeck(type)) {
|
||||
const objectDamagesObj = objectDamages[key];
|
||||
|
||||
let ok = false;
|
||||
@@ -77,13 +55,38 @@ export function damageTypeToMultipliers({
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
console.log(`for ${type} used ${key ?? "FALLBACK"}`);
|
||||
return objectDamagesObj.rates;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
const objectDamageJsonKeyPriorityEntries = Object.entries(
|
||||
objectDamageJsonKeyPriority
|
||||
);
|
||||
|
||||
// for example blaster belongs to both Blaster_KillOneShot
|
||||
// and Blaster categories so it needs to be specified
|
||||
// which damage type uses which
|
||||
function jsonKeysToCeck(type: DamageType) {
|
||||
const result: Array<keyof typeof objectDamages> = [];
|
||||
|
||||
for (const [key, value] of objectDamageJsonKeyPriorityEntries) {
|
||||
if (value?.includes(type)) {
|
||||
result.push(key as keyof typeof objectDamages);
|
||||
}
|
||||
}
|
||||
|
||||
if (result.length) return result;
|
||||
|
||||
for (const [key, value] of objectDamageJsonKeyPriorityEntries) {
|
||||
if (!value) {
|
||||
result.push(key as keyof typeof objectDamages);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export function multipliersToRecordWithFallbacks(
|
||||
multipliers: ReturnType<typeof damageTypeToMultipliers>
|
||||
|
||||
@@ -75,7 +75,8 @@ export function buildStats({
|
||||
},
|
||||
stats: {
|
||||
specialPoint: specialPoint(input),
|
||||
specialSavedAfterDeath: specialSavedAfterDeath(input),
|
||||
specialLost: specialLost(input),
|
||||
specialLostSplattedByRP: specialLost(input, true),
|
||||
fullInkTankOptions: fullInkTankOptions(input),
|
||||
damages: damages(input),
|
||||
mainWeaponWhiteInkSeconds:
|
||||
@@ -97,7 +98,8 @@ export function buildStats({
|
||||
enemyInkDamageLimit: enemyInkDamageLimit(input),
|
||||
framesBeforeTakingDamageInEnemyInk:
|
||||
framesBeforeTakingDamageInEnemyInk(input),
|
||||
quickRespawnTime: quickRespawnTime(input),
|
||||
quickRespawnTime: respawnTime(input),
|
||||
quickRespawnTimeSplattedByRP: respawnTime(input, true),
|
||||
superJumpTimeGroundFrames: superJumpTimeGroundFrames(input),
|
||||
superJumpTimeTotal: superJumpTimeTotal(input),
|
||||
shotSpreadAir: shotSpreadAir(input),
|
||||
@@ -165,11 +167,11 @@ function specialPoint({
|
||||
}
|
||||
|
||||
const OWN_RESPAWN_PUNISHER_EXTRA_SPECIAL_LOST = 0.225;
|
||||
function specialSavedAfterDeath({
|
||||
abilityPoints,
|
||||
mainWeaponParams,
|
||||
mainOnlyAbilities,
|
||||
}: StatFunctionInput): AnalyzedBuild["stats"]["specialPoint"] {
|
||||
const ENEMY_RESPAWN_PUNISHER_EXTRA_SPECIAL_LOST = 0.15;
|
||||
function specialLost(
|
||||
{ abilityPoints, mainWeaponParams, mainOnlyAbilities }: StatFunctionInput,
|
||||
splattedByRP = false
|
||||
): AnalyzedBuild["stats"]["specialPoint"] {
|
||||
const SPECIAL_SAVED_AFTER_DEATH_ABILITY = "SS";
|
||||
const hasRespawnPunisher = mainOnlyAbilities.includes("RP");
|
||||
const extraSpecialLost = hasRespawnPunisher
|
||||
@@ -189,9 +191,17 @@ function specialSavedAfterDeath({
|
||||
weapon: mainWeaponParams,
|
||||
});
|
||||
|
||||
const splattedByExtraPenalty = splattedByRP
|
||||
? ENEMY_RESPAWN_PUNISHER_EXTRA_SPECIAL_LOST
|
||||
: 0;
|
||||
|
||||
return {
|
||||
baseValue: specialSavedAfterDeathForDisplay(baseEffect),
|
||||
value: specialSavedAfterDeathForDisplay(effect - extraSpecialLost),
|
||||
baseValue: specialSavedAfterDeathForDisplay(
|
||||
baseEffect - splattedByExtraPenalty
|
||||
),
|
||||
value: specialSavedAfterDeathForDisplay(
|
||||
effect - splattedByExtraPenalty - extraSpecialLost
|
||||
),
|
||||
modifiedBy: [SPECIAL_SAVED_AFTER_DEATH_ABILITY, "RP"],
|
||||
};
|
||||
}
|
||||
@@ -331,6 +341,7 @@ const damageTypeToParamsKey: Record<
|
||||
> = {
|
||||
NORMAL_MIN: "DamageParam_ValueMin",
|
||||
NORMAL_MAX: "DamageParam_ValueMax",
|
||||
NORMAL_MAX_FULL_CHARGE: "DamageParam_ValueFullChargeMax",
|
||||
DIRECT: "DamageParam_ValueDirect",
|
||||
DISTANCE: "BlastParam_DistanceDamage",
|
||||
FULL_CHARGE: "DamageParam_ValueFullCharge",
|
||||
@@ -388,7 +399,7 @@ function shotsToSplat({
|
||||
type: DamageType;
|
||||
isTripleShooter: boolean;
|
||||
}) {
|
||||
if (type !== "NORMAL_MAX") return;
|
||||
if (type !== "NORMAL_MAX" && type !== "NORMAL_MAX_FULL_CHARGE") return;
|
||||
|
||||
const multiplier = isTripleShooter ? 3 : 1;
|
||||
|
||||
@@ -516,9 +527,11 @@ function swimSpeed(
|
||||
|
||||
const RESPAWN_CHASE_FRAME = 150;
|
||||
const OWN_RESPAWN_PUNISHER_EXTRA_RESPAWN_FRAMES = 68;
|
||||
const ENEMY_RESPAWN_PUNISHER_EXTRA_RESPAWN_FRAMES = 45;
|
||||
const SPLATOON_3_FASTER_RESPAWN = 60;
|
||||
function quickRespawnTime(
|
||||
args: StatFunctionInput
|
||||
function respawnTime(
|
||||
args: StatFunctionInput,
|
||||
splattedByRP = false
|
||||
): AnalyzedBuild["stats"]["quickRespawnTime"] {
|
||||
const QUICK_RESPAWN_TIME_ABILITY = "QR";
|
||||
const hasRespawnPunisher = args.mainOnlyAbilities.includes("RP");
|
||||
@@ -542,14 +555,19 @@ function quickRespawnTime(
|
||||
weapon: args.mainWeaponParams,
|
||||
});
|
||||
|
||||
const extraFrames = hasRespawnPunisher
|
||||
const ownRPExtraFrames = hasRespawnPunisher
|
||||
? OWN_RESPAWN_PUNISHER_EXTRA_RESPAWN_FRAMES
|
||||
: 0;
|
||||
|
||||
const splattedByExtraFrames = splattedByRP
|
||||
? ENEMY_RESPAWN_PUNISHER_EXTRA_RESPAWN_FRAMES
|
||||
: 0;
|
||||
|
||||
return {
|
||||
baseValue: framesToSeconds(
|
||||
RESPAWN_CHASE_FRAME +
|
||||
chase.baseEffect +
|
||||
splattedByExtraFrames +
|
||||
around.baseEffect -
|
||||
SPLATOON_3_FASTER_RESPAWN
|
||||
),
|
||||
@@ -557,7 +575,8 @@ function quickRespawnTime(
|
||||
RESPAWN_CHASE_FRAME +
|
||||
chase.effect +
|
||||
around.effect +
|
||||
extraFrames -
|
||||
splattedByExtraFrames +
|
||||
ownRPExtraFrames -
|
||||
SPLATOON_3_FASTER_RESPAWN
|
||||
),
|
||||
modifiedBy: [QUICK_RESPAWN_TIME_ABILITY, "RP"],
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface MainWeaponParams {
|
||||
MoveSpeedFullCharge?: number;
|
||||
DamageParam_ValueMax?: number;
|
||||
DamageParam_ValueMin?: number;
|
||||
DamageParam_ValueFullChargeMax?: number;
|
||||
DamageParam_ValueDirect?: number;
|
||||
Jump_DegSwerve?: number;
|
||||
Stand_DegSwerve?: number;
|
||||
@@ -185,8 +186,8 @@ export interface AnalyzedBuild {
|
||||
};
|
||||
stats: {
|
||||
specialPoint: Stat;
|
||||
/** % of special charge saved when dying */
|
||||
specialSavedAfterDeath: Stat;
|
||||
specialLost: Stat;
|
||||
specialLostSplattedByRP: Stat;
|
||||
mainWeaponWhiteInkSeconds?: number;
|
||||
subWeaponWhiteInkSeconds: number;
|
||||
fullInkTankOptions: Array<FullInkTankOption & { id: string }>;
|
||||
@@ -202,6 +203,7 @@ export interface AnalyzedBuild {
|
||||
damageTakenInEnemyInkPerSecond: Stat;
|
||||
enemyInkDamageLimit: Stat;
|
||||
quickRespawnTime: Stat;
|
||||
quickRespawnTimeSplattedByRP: Stat;
|
||||
superJumpTimeGroundFrames: Stat;
|
||||
superJumpTimeTotal: Stat;
|
||||
shotSpreadAir?: Stat;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useSearchParams } from "@remix-run/react";
|
||||
import invariant from "tiny-invariant";
|
||||
import { type MainWeaponId } from "../in-game-lists";
|
||||
import { calculateDamage } from "./objectDamage";
|
||||
import { buildStats } from "./stats";
|
||||
@@ -37,7 +36,7 @@ export function useObjectDamage() {
|
||||
{
|
||||
weapon: String(newMainWeaponId),
|
||||
[ABILITY_POINTS_SP_KEY]: String(newAbilityPoints),
|
||||
[DAMAGE_TYPE_SP_KEY]: newDamageType,
|
||||
[DAMAGE_TYPE_SP_KEY]: newDamageType ?? "",
|
||||
},
|
||||
{ replace: true, state: { scroll: false } }
|
||||
);
|
||||
@@ -47,15 +46,17 @@ export function useObjectDamage() {
|
||||
mainWeaponId,
|
||||
subWeaponId: analyzed.weapon.subWeaponSplId,
|
||||
handleChange,
|
||||
damagesToReceivers: calculateDamage({
|
||||
abilityPoints: new Map([
|
||||
["BRU", { ap: abilityPoints, apBeforeTacticooler: abilityPoints }],
|
||||
["SPU", { ap: abilityPoints, apBeforeTacticooler: abilityPoints }],
|
||||
]),
|
||||
analyzed,
|
||||
mainWeaponId,
|
||||
damageType,
|
||||
}),
|
||||
damagesToReceivers: damageType
|
||||
? calculateDamage({
|
||||
abilityPoints: new Map([
|
||||
["BRU", { ap: abilityPoints, apBeforeTacticooler: abilityPoints }],
|
||||
["SPU", { ap: abilityPoints, apBeforeTacticooler: abilityPoints }],
|
||||
]),
|
||||
analyzed,
|
||||
mainWeaponId,
|
||||
damageType,
|
||||
})
|
||||
: null,
|
||||
abilityPoints: String(abilityPoints),
|
||||
damageType,
|
||||
allDamageTypes: Array.from(
|
||||
@@ -99,7 +100,6 @@ function validatedDamageTypeFromSearchParams({
|
||||
const fallbackFound = damageTypePriorityList.find((type) =>
|
||||
analyzed.stats.damages.some((d) => d.type === type)
|
||||
);
|
||||
invariant(fallbackFound);
|
||||
|
||||
return fallbackFound;
|
||||
}
|
||||
|
||||
@@ -375,7 +375,7 @@
|
||||
"InkConsume_WeaponSwingParam": 0.18
|
||||
},
|
||||
"1030": {
|
||||
"SpecialPoint": 200,
|
||||
"SpecialPoint": 210,
|
||||
"subWeaponId": 10,
|
||||
"specialWeaponId": 4,
|
||||
"InkConsume_WeaponVerticalSwingParam": 0.12,
|
||||
@@ -513,7 +513,7 @@
|
||||
"InkConsumeSlosher": 0.06
|
||||
},
|
||||
"3020": {
|
||||
"SpecialPoint": 200,
|
||||
"SpecialPoint": 210,
|
||||
"subWeaponId": 5,
|
||||
"specialWeaponId": 6,
|
||||
"MoveSpeed": 0.07,
|
||||
@@ -597,6 +597,7 @@
|
||||
"MoveSpeed_Charge": 0.04,
|
||||
"DamageParam_ValueMax": 320,
|
||||
"DamageParam_ValueMin": 160,
|
||||
"DamageParam_ValueFullChargeMax": 400,
|
||||
"Jump_DegSwerve": 6,
|
||||
"Stand_DegSwerve": 3,
|
||||
"InkRecoverStop": 40,
|
||||
@@ -748,7 +749,7 @@
|
||||
"InkConsumeFullCharge_ChargeParam": 0.085
|
||||
},
|
||||
"7020": {
|
||||
"SpecialPoint": 200,
|
||||
"SpecialPoint": 210,
|
||||
"subWeaponId": 6,
|
||||
"specialWeaponId": 4,
|
||||
"WeaponSpeedType": "Fast",
|
||||
@@ -881,7 +882,7 @@
|
||||
},
|
||||
"SubInkSaveLv": 1,
|
||||
"InkConsume": 0.6,
|
||||
"InkRecoverStop": 70,
|
||||
"InkRecoverStop": 85,
|
||||
"DistanceDamage_BlastParamArray": [
|
||||
[
|
||||
{
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { type LinksFunction, type MetaFunction } from "@remix-run/node";
|
||||
import { Link } from "@remix-run/react";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { AbilitiesSelector } from "~/components/AbilitiesSelector";
|
||||
@@ -37,9 +38,14 @@ import styles from "~/styles/analyzer.css";
|
||||
import { damageTypeTranslationString } from "~/utils/i18next";
|
||||
import { type SendouRouteHandle } from "~/utils/remix";
|
||||
import { makeTitle } from "~/utils/strings";
|
||||
import { specialWeaponImageUrl, subWeaponImageUrl } from "~/utils/urls";
|
||||
import {
|
||||
navIconUrl,
|
||||
objectDamageCalculatorPage,
|
||||
specialWeaponImageUrl,
|
||||
subWeaponImageUrl,
|
||||
} from "~/utils/urls";
|
||||
|
||||
export const CURRENT_PATCH = "1.1";
|
||||
export const CURRENT_PATCH = "1.2";
|
||||
|
||||
export const meta: MetaFunction = () => {
|
||||
return {
|
||||
@@ -69,6 +75,8 @@ export default function BuildAnalyzerPage() {
|
||||
effects,
|
||||
} = useAnalyzeBuild();
|
||||
|
||||
const objectShredderSelected = build[2][0] === "OS";
|
||||
|
||||
const mainWeaponCategoryItems = [
|
||||
analyzed.stats.shotSpreadAir && (
|
||||
<StatCard
|
||||
@@ -245,10 +253,15 @@ export default function BuildAnalyzerPage() {
|
||||
suffix={t("analyzer:suffix.specialPointsShort")}
|
||||
/>
|
||||
<StatCard
|
||||
stat={analyzed.stats.specialSavedAfterDeath}
|
||||
stat={analyzed.stats.specialLost}
|
||||
title={t("analyzer:stat.specialLost")}
|
||||
suffix="%"
|
||||
/>
|
||||
<StatCard
|
||||
stat={analyzed.stats.specialLostSplattedByRP}
|
||||
title={t("analyzer:stat.specialLostSplattedByRP")}
|
||||
suffix="%"
|
||||
/>
|
||||
{analyzed.stats.specialDurationInSeconds && (
|
||||
<StatCard
|
||||
stat={analyzed.stats.specialDurationInSeconds}
|
||||
@@ -544,6 +557,11 @@ export default function BuildAnalyzerPage() {
|
||||
title={t("analyzer:stat.quickRespawnTime")}
|
||||
suffix={t("analyzer:suffix.seconds")}
|
||||
/>
|
||||
<StatCard
|
||||
stat={analyzed.stats.quickRespawnTimeSplattedByRP}
|
||||
title={t("analyzer:stat.quickRespawnTimeSplattedByRP")}
|
||||
suffix={t("analyzer:suffix.seconds")}
|
||||
/>
|
||||
<StatCard
|
||||
stat={analyzed.stats.superJumpTimeGroundFrames}
|
||||
title={t("analyzer:stat.superJumpTimeGround")}
|
||||
@@ -554,6 +572,20 @@ export default function BuildAnalyzerPage() {
|
||||
suffix={t("analyzer:suffix.seconds")}
|
||||
/>
|
||||
</StatCategory>
|
||||
{objectShredderSelected && (
|
||||
<Link
|
||||
className="analyzer__noticeable-link"
|
||||
to={objectDamageCalculatorPage(mainWeaponId)}
|
||||
>
|
||||
<Image
|
||||
path={navIconUrl("object-damage-calculator")}
|
||||
width={24}
|
||||
height={24}
|
||||
alt=""
|
||||
/>
|
||||
{t("analyzer:objCalcAd")}
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Main>
|
||||
@@ -834,7 +866,7 @@ function DamageTable({
|
||||
|
||||
return (
|
||||
<tr key={val.id}>
|
||||
<td>{typeRowName}</td>
|
||||
<td>{t(typeRowName)}</td>
|
||||
<td>
|
||||
{damage}{" "}
|
||||
{val.shotsToSplat && (
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
CALENDAR_PAGE,
|
||||
mapsPage,
|
||||
navIconUrl,
|
||||
objectDamageCalculatorPage,
|
||||
plusSuggestionPage,
|
||||
userPage,
|
||||
} from "~/utils/urls";
|
||||
@@ -75,6 +76,12 @@ export default function Index() {
|
||||
description={t("front:analyzer.description")}
|
||||
to={analyzerPage()}
|
||||
/>
|
||||
<FeatureCard
|
||||
navItem="object-damage-calculator"
|
||||
title={t("common:pages.object-damage-calculator")}
|
||||
description={t("front:object-damage-calculator.description")}
|
||||
to={objectDamageCalculatorPage()}
|
||||
/>
|
||||
<FeatureCard
|
||||
navItem="plus"
|
||||
title={t("common:pages.plus")}
|
||||
|
||||
@@ -31,12 +31,15 @@ import { Ability } from "~/components/Ability";
|
||||
import { damageTypeTranslationString } from "~/utils/i18next";
|
||||
import { useSetTitle } from "~/hooks/useSetTitle";
|
||||
|
||||
export const CURRENT_PATCH = "1.2";
|
||||
|
||||
export const links: LinksFunction = () => {
|
||||
return [{ rel: "stylesheet", href: styles }];
|
||||
};
|
||||
|
||||
export const handle: SendouRouteHandle = {
|
||||
i18n: ["weapons", "analyzer"],
|
||||
navItemName: "object-damage-calculator",
|
||||
};
|
||||
|
||||
export default function ObjectDamagePage() {
|
||||
@@ -51,10 +54,6 @@ export default function ObjectDamagePage() {
|
||||
allDamageTypes,
|
||||
} = useObjectDamage();
|
||||
|
||||
if (process.env.NODE_ENV !== "development") {
|
||||
return <Main>WIP :)</Main>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Main className="stack lg">
|
||||
<div className="object-damage__controls">
|
||||
@@ -74,7 +73,7 @@ export default function ObjectDamagePage() {
|
||||
clearsInputOnFocus
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<div className={clsx({ invisible: !damagesToReceivers })}>
|
||||
<Label htmlFor="damage">{t("analyzer:labels.damageType")}</Label>
|
||||
<DamageTypesSelect
|
||||
handleChange={handleChange}
|
||||
@@ -90,6 +89,7 @@ export default function ObjectDamagePage() {
|
||||
<Ability ability="SPU" size="TINY" />
|
||||
</Label>
|
||||
<select
|
||||
className="object-damage__select"
|
||||
id="ap"
|
||||
value={abilityPoints}
|
||||
onChange={(e) =>
|
||||
@@ -105,10 +105,22 @@ export default function ObjectDamagePage() {
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<DamageReceiversGrid
|
||||
subWeaponId={subWeaponId}
|
||||
damagesToReceivers={damagesToReceivers}
|
||||
/>
|
||||
{damagesToReceivers ? (
|
||||
<DamageReceiversGrid
|
||||
subWeaponId={subWeaponId}
|
||||
damagesToReceivers={damagesToReceivers}
|
||||
/>
|
||||
) : (
|
||||
<div>{t("analyzer:noDmgData")}</div>
|
||||
)}
|
||||
<div className="object-damage__bottom-container">
|
||||
<div className="text-lighter text-xs">
|
||||
{t("analyzer:dmgHtdExplanation")}
|
||||
</div>
|
||||
<div className="object-damage__patch">
|
||||
{t("analyzer:patch")} {CURRENT_PATCH}
|
||||
</div>
|
||||
</div>
|
||||
</Main>
|
||||
);
|
||||
}
|
||||
@@ -126,6 +138,7 @@ function DamageTypesSelect({
|
||||
|
||||
return (
|
||||
<select
|
||||
className="object-damage__select"
|
||||
id="damage"
|
||||
value={damageType}
|
||||
onChange={(e) =>
|
||||
@@ -167,12 +180,14 @@ const damageReceiverImages: Record<DamageReceiver, string> = {
|
||||
function DamageReceiversGrid({
|
||||
subWeaponId,
|
||||
damagesToReceivers,
|
||||
}: Pick<
|
||||
ReturnType<typeof useObjectDamage>,
|
||||
"damagesToReceivers" | "subWeaponId"
|
||||
>) {
|
||||
}: {
|
||||
subWeaponId: ReturnType<typeof useObjectDamage>["subWeaponId"];
|
||||
damagesToReceivers: NonNullable<
|
||||
ReturnType<typeof useObjectDamage>["damagesToReceivers"]
|
||||
>;
|
||||
}) {
|
||||
const { t } = useTranslation(["weapons", "analyzer", "common"]);
|
||||
useSetTitle(t("common:pages.object-damage"));
|
||||
useSetTitle(t("common:pages.object-damage-calculator"));
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -55,6 +55,19 @@
|
||||
padding-inline: var(--s-2);
|
||||
}
|
||||
|
||||
.analyzer__noticeable-link {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--theme-transparent);
|
||||
font-size: var(--fonts-xxs);
|
||||
font-weight: var(--semi-bold);
|
||||
gap: var(--s-2);
|
||||
padding-block: var(--s-1);
|
||||
padding-inline: var(--s-2);
|
||||
}
|
||||
|
||||
.analyzer__ap-text {
|
||||
color: var(--text-lighter);
|
||||
font-size: var(--fonts-xxs);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
.object-damage__controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
gap: var(--s-4);
|
||||
}
|
||||
|
||||
.object-damage__ap-label {
|
||||
@@ -13,6 +15,7 @@
|
||||
display: grid;
|
||||
width: 100%;
|
||||
column-gap: var(--s-6);
|
||||
overflow-x: auto;
|
||||
place-items: center;
|
||||
row-gap: var(--s-3);
|
||||
}
|
||||
@@ -31,7 +34,9 @@
|
||||
font-size: var(--fonts-xs);
|
||||
font-weight: var(--semi-bold);
|
||||
padding-block: var(--s-2);
|
||||
padding-inline: var(--s-2);
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.object-damage__distance {
|
||||
@@ -56,6 +61,11 @@
|
||||
display: grid;
|
||||
column-gap: var(--s-2);
|
||||
grid-template-columns: 1fr 1fr;
|
||||
padding-inline: var(--s-2);
|
||||
}
|
||||
|
||||
.object-damage__select {
|
||||
width: initial;
|
||||
}
|
||||
|
||||
.object-damage__abbr {
|
||||
@@ -71,3 +81,20 @@
|
||||
letter-spacing: 0.5px;
|
||||
margin-block-start: var(--s-2);
|
||||
}
|
||||
|
||||
.object-damage__bottom-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.object-damage__patch {
|
||||
border-radius: var(--rounded);
|
||||
background-color: var(--theme-transparent);
|
||||
color: var(--text-lighter);
|
||||
font-size: var(--fonts-xxxs);
|
||||
font-weight: var(--bold);
|
||||
padding-block: var(--s-0-5);
|
||||
padding-inline: var(--s-1);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
.text-lg {
|
||||
font-size: var(--fonts-lg);
|
||||
}
|
||||
|
||||
.text-sm {
|
||||
font-size: var(--fonts-sm);
|
||||
}
|
||||
@@ -6,8 +10,8 @@
|
||||
font-size: var(--fonts-xs);
|
||||
}
|
||||
|
||||
.text-lg {
|
||||
font-size: var(--fonts-lg);
|
||||
.text-xxs {
|
||||
font-size: var(--fonts-xxs);
|
||||
}
|
||||
|
||||
.text-center {
|
||||
|
||||
@@ -86,6 +86,10 @@ export const analyzerPage = (args?: {
|
||||
)}`
|
||||
: ""
|
||||
}`;
|
||||
export const objectDamageCalculatorPage = (weaponId?: MainWeaponId) =>
|
||||
`/object-damage-calculator${
|
||||
typeof weaponId === "number" ? `?weapon=${weaponId}` : ""
|
||||
}`;
|
||||
|
||||
export const badgeUrl = ({
|
||||
code,
|
||||
|
||||
13
content/articles/inktv-announces-return.md
Normal file
13
content/articles/inktv-announces-return.md
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
title: InkTV Announces Its Return
|
||||
date: 2022-10-24
|
||||
author: Riczi
|
||||
---
|
||||
|
||||
In a [Twitter post](https://twitter.com/InkTVSplat/status/1584590544205930496) released on Monday, October 24th, 2022, InkTV announced that it would be returning to the Splatoon competitive scene. This platform is dedicated highlighting the players and teams involved in competitive Splatoon. They were heavily involved in developing tournaments, podcast feeds, and player interviews in an effort to grow the community.
|
||||
|
||||
InkTV was retired in early 2020, just as the Covid-19 pandemic began to escalate. Now with tournaments being held again and the incredibly successful launch of a new game in Splatoon 3, InkTV is ready to make its return.
|
||||
|
||||
The announcement video features some of Splatoon's most prominent community members, including caster NineWholeGrains and professional players ProChara and KyochaN. In the footage they talk about what InkTV did in the past and how it helped them grow and develop as individuals. Everyone was then surprised with the return announcement.
|
||||
|
||||
Be sure to follow InkTV on social media so you can be part of the ever-growing Splatoon community. Check out the video if you haven't already so you can get excited for what InkTV will have in store.
|
||||
BIN
public/img/layout/object-damage-calculator.avif
Normal file
BIN
public/img/layout/object-damage-calculator.avif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
BIN
public/img/layout/object-damage-calculator.png
Normal file
BIN
public/img/layout/object-damage-calculator.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
@@ -26,7 +26,7 @@
|
||||
"forms.team.add": "Tilføj hold",
|
||||
"forms.team.remove": "Fjern hold",
|
||||
"forms.team.name": "Holdnavn",
|
||||
"forms.team.placing": "Placerer",
|
||||
"forms.team.placing": "Placering",
|
||||
|
||||
"forms.team.player.header": "Spiller {{number}}",
|
||||
"forms.team.player.add": "Tilføj spiller",
|
||||
|
||||
@@ -14,6 +14,11 @@
|
||||
"header.logout": "Log ud",
|
||||
"header.login": "Log ind",
|
||||
|
||||
"auth.errors.aborted": "logindforsøg afbrudt",
|
||||
"auth.errors.failed": "Loginforsøg fejlet",
|
||||
"auth.errors.discordPermissions": "Før at du kan oprette en profil på Sendou.ink, skal Sendou.ink have adgang til din Discordprofils navn, brugerbillede og sociale forbindelser (de sociale medier, som du har tilknyttet din discordprofil).",
|
||||
"auth.errors.unknown": "Loginforsøget via discord fejlede grundet en ukendt årsag. Hvis dette bliver ved med at ske, så spørg om hjælp (på engelsk) på discord-serveren https://discord.gg/sendou via #helpdesk-chatten.",
|
||||
|
||||
"footer.github.subtitle": "Kildekode",
|
||||
"footer.twitter.subtitle": "Opdateringer",
|
||||
"footer.discord.subtitle": "Hjælp og feedback",
|
||||
@@ -29,6 +34,7 @@
|
||||
"actions.delete": "Slet",
|
||||
"actions.loadMore": "Indlæs flere",
|
||||
"actions.copyToClipboard": "Kopiér til udklipsholderen",
|
||||
"actions.close": "Luk",
|
||||
|
||||
"maps.createMapList": "Lav bane-liste",
|
||||
"maps.halfSz": "50% SZ",
|
||||
|
||||
@@ -10,12 +10,17 @@
|
||||
"stick": "Styrepind",
|
||||
"sens": "Følsomhed",
|
||||
|
||||
"results.placing": "Rang",
|
||||
"results.title": "Resultater",
|
||||
"results.placing": "Placering",
|
||||
"results.team": "Hold",
|
||||
"results.tournament": "Turnering",
|
||||
"results.participants": "Deltagere",
|
||||
"results.date": "Dato",
|
||||
"results.mates": "Holdkammerater",
|
||||
"results.highlights": "Højdepunkter",
|
||||
"results.nonHighlights": "Andre resultater",
|
||||
"results.highlights.choose": "Vælg højdepunkter",
|
||||
"results.highlights.explanation": "Vælg de resultater, som du vil fremhæve",
|
||||
|
||||
"forms.errors.invalidCustomUrl.numbers": "Brugerdefineret URL må ikke kun indeholde numre",
|
||||
"forms.errors.invalidCustomUrl.strangeCharacter": "Brugerdefineret URL må ikke indeholde specialtegn (Gælder også æ, ø og å)",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"attribute.weight.Fast": "Light",
|
||||
"attribute.weight.Slow": "Heavy",
|
||||
"attribute.weight.Normal": "Normal",
|
||||
"objCalcAd": "For info on Object Shredder check out Object DMG Calc",
|
||||
"stat.category.main": "Main weapon",
|
||||
"stat.category.sub": "Sub weapon",
|
||||
"stat.category.special": "Special weapon",
|
||||
@@ -16,9 +17,11 @@
|
||||
"stat.maxChargeHoldSeconds": "Max charge hold time",
|
||||
"stat.specialPoints": "Points to special",
|
||||
"stat.specialLost": "Special lost when splatted",
|
||||
"stat.specialLostSplattedByRP": "Special lost when splatted by RP user",
|
||||
"stat.whiteInk": "No ink recovery time after usage",
|
||||
"stat.squidFormInkRecoverySeconds": "Ink tank full recovery time (squid form)",
|
||||
"stat.quickRespawnTime": "Quick respawn time",
|
||||
"stat.quickRespawnTimeSplattedByRP": "Quick Respawn time (splatted by RP user)",
|
||||
"stat.superJumpTimeGround": "Super jump vulnerable frames",
|
||||
"stat.superJumpTimeTotal": "Super jump time (total)",
|
||||
"stat.jumpShotSpread": "Shot spread in degrees (jumping)",
|
||||
@@ -77,6 +80,7 @@
|
||||
"damage.toSplat": "{{count}} hits to splat",
|
||||
"damage.NORMAL_MIN": "Minimum",
|
||||
"damage.NORMAL_MAX": "Maximum",
|
||||
"damage.NORMAL_MAX_FULL_CHARGE": "Maximum (Fully charged)",
|
||||
"damage.DIRECT": "Direct",
|
||||
"damage.FULL_CHARGE": "Fully charged shot",
|
||||
"damage.MAX_CHARGE": "Maximum partial charge",
|
||||
@@ -99,5 +103,7 @@
|
||||
"hitsToDestroyShort": "HTD",
|
||||
"labels.amountOf": "Amount of",
|
||||
"labels.damageType": "Damage type",
|
||||
"labels.weapon": "Weapon"
|
||||
"labels.weapon": "Weapon",
|
||||
"dmgHtdExplanation": "DMG = Damage • HTD = Hits to destroy",
|
||||
"noDmgData": "No damage data yet for this weapon. Check back later!"
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"pages.builds": "Builds",
|
||||
"pages.analyzer": "Build Analyzer",
|
||||
"pages.maps": "Map Lists",
|
||||
"pages.object-damage": "Object Damage Calculator",
|
||||
"pages.object-damage-calculator": "Object DMG Calc",
|
||||
|
||||
"header.profile": "Profile",
|
||||
"header.logout": "Log out",
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
"badges.description": "List of all the badges you can earn for your profile",
|
||||
"analyzer.description": "Find out what your builds actually do",
|
||||
"maps.description": "Turn pool of maps into a list to play on",
|
||||
"object-damage-calculator.description": "Calculate damage dealt to different objects",
|
||||
"recentWinners": "Recent winners",
|
||||
"upcomingEvents": "Upcoming events",
|
||||
"articleBy": "by {{author}}"
|
||||
|
||||
@@ -172,6 +172,10 @@ function parametersToMainWeaponResult(
|
||||
DamageParam_ValueDirect,
|
||||
BlastParam_DistanceDamage: params["BlastParam"]?.["DistanceDamage"],
|
||||
DamageParam_ValueFullCharge: params["DamageParam"]?.["ValueFullCharge"],
|
||||
DamageParam_ValueFullChargeMax:
|
||||
params["DamageParam"]?.["ValueFullChargeMax"] !== DamageParam_ValueMax()
|
||||
? params["DamageParam"]?.["ValueFullChargeMax"]
|
||||
: undefined,
|
||||
DamageParam_ValueMaxCharge: params["DamageParam"]?.["ValueMaxCharge"],
|
||||
DamageParam_ValueMinCharge: params["DamageParam"]?.["ValueMinCharge"],
|
||||
CanopyHP: params["spl__BulletShelterCanopyParam"]?.["CanopyHP"],
|
||||
|
||||
@@ -3,7 +3,7 @@ import "dotenv/config";
|
||||
import { sql } from "~/db/sql";
|
||||
|
||||
sql
|
||||
.prepare(`update "User" set "customUrl" = NULL where "customUrl" like '%/%'`)
|
||||
.prepare(`update "User" set "customUrl" = NULL where "customUrl" like '%#%'`)
|
||||
.run();
|
||||
|
||||
console.log("Done");
|
||||
|
||||
@@ -2,9 +2,28 @@
|
||||
|
||||
## /da (🟡 In progress)
|
||||
|
||||
### 🟢 analyzer.json
|
||||
### 🟡 analyzer.json
|
||||
|
||||
**94/94**
|
||||
**94/107**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- objCalcAd
|
||||
- stat.specialLostSplattedByRP
|
||||
- stat.quickRespawnTimeSplattedByRP
|
||||
- damage.NORMAL_MAX_FULL_CHARGE
|
||||
- distanceInline
|
||||
- damageShort
|
||||
- hitsToDestroyLong
|
||||
- hitsToDestroyShort
|
||||
- labels.amountOf
|
||||
- labels.damageType
|
||||
- labels.weapon
|
||||
- dmgHtdExplanation
|
||||
- noDmgData
|
||||
|
||||
</details>
|
||||
|
||||
### 🟢 badges.json
|
||||
|
||||
@@ -20,16 +39,12 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**55/60**
|
||||
**60/61**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- auth.errors.aborted
|
||||
- auth.errors.failed
|
||||
- auth.errors.discordPermissions
|
||||
- auth.errors.unknown
|
||||
- actions.close
|
||||
- pages.object-damage-calculator
|
||||
|
||||
</details>
|
||||
|
||||
@@ -41,36 +56,51 @@
|
||||
|
||||
**6/6**
|
||||
|
||||
### 🟢 front.json
|
||||
### 🟡 front.json
|
||||
|
||||
**11/11**
|
||||
**11/12**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- object-damage-calculator.description
|
||||
|
||||
</details>
|
||||
|
||||
### 🟢 game-misc.json
|
||||
|
||||
**17/17**
|
||||
|
||||
### 🟡 user.json
|
||||
### 🟢 user.json
|
||||
|
||||
**20/25**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- results.title
|
||||
- results.highlights
|
||||
- results.nonHighlights
|
||||
- results.highlights.choose
|
||||
- results.highlights.explanation
|
||||
|
||||
</details>
|
||||
**25/25**
|
||||
|
||||
---
|
||||
|
||||
## /de (🟡 In progress)
|
||||
|
||||
### 🟢 analyzer.json
|
||||
### 🟡 analyzer.json
|
||||
|
||||
**94/94**
|
||||
**94/107**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- objCalcAd
|
||||
- stat.specialLostSplattedByRP
|
||||
- stat.quickRespawnTimeSplattedByRP
|
||||
- damage.NORMAL_MAX_FULL_CHARGE
|
||||
- distanceInline
|
||||
- damageShort
|
||||
- hitsToDestroyLong
|
||||
- hitsToDestroyShort
|
||||
- labels.amountOf
|
||||
- labels.damageType
|
||||
- labels.weapon
|
||||
- dmgHtdExplanation
|
||||
- noDmgData
|
||||
|
||||
</details>
|
||||
|
||||
### 🟢 badges.json
|
||||
|
||||
@@ -86,11 +116,12 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**59/60**
|
||||
**59/61**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- pages.object-damage-calculator
|
||||
- actions.copyToClipboard
|
||||
|
||||
</details>
|
||||
@@ -103,9 +134,16 @@
|
||||
|
||||
**6/6**
|
||||
|
||||
### 🟢 front.json
|
||||
### 🟡 front.json
|
||||
|
||||
**11/11**
|
||||
**11/12**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- object-damage-calculator.description
|
||||
|
||||
</details>
|
||||
|
||||
### 🟢 game-misc.json
|
||||
|
||||
@@ -121,14 +159,27 @@
|
||||
|
||||
### 🟡 analyzer.json
|
||||
|
||||
**91/94**
|
||||
**91/107**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- objCalcAd
|
||||
- stat.specialLostSplattedByRP
|
||||
- stat.quickRespawnTimeSplattedByRP
|
||||
- stat.shootingRunSpeed
|
||||
- stat.shootingRunSpeedCharging
|
||||
- stat.shootingRunSpeedFullCharge
|
||||
- damage.NORMAL_MAX_FULL_CHARGE
|
||||
- distanceInline
|
||||
- damageShort
|
||||
- hitsToDestroyLong
|
||||
- hitsToDestroyShort
|
||||
- labels.amountOf
|
||||
- labels.damageType
|
||||
- labels.weapon
|
||||
- dmgHtdExplanation
|
||||
- noDmgData
|
||||
|
||||
</details>
|
||||
|
||||
@@ -154,13 +205,14 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**48/60**
|
||||
**48/61**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- pages.s2
|
||||
- pages.maps
|
||||
- pages.object-damage-calculator
|
||||
- auth.errors.aborted
|
||||
- auth.errors.failed
|
||||
- auth.errors.discordPermissions
|
||||
@@ -184,12 +236,13 @@
|
||||
|
||||
### 🟡 front.json
|
||||
|
||||
**10/11**
|
||||
**10/12**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- maps.description
|
||||
- object-damage-calculator.description
|
||||
|
||||
</details>
|
||||
|
||||
@@ -242,14 +295,27 @@
|
||||
|
||||
### 🟡 analyzer.json
|
||||
|
||||
**91/94**
|
||||
**91/107**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- objCalcAd
|
||||
- stat.specialLostSplattedByRP
|
||||
- stat.quickRespawnTimeSplattedByRP
|
||||
- stat.shootingRunSpeed
|
||||
- stat.shootingRunSpeedCharging
|
||||
- stat.shootingRunSpeedFullCharge
|
||||
- damage.NORMAL_MAX_FULL_CHARGE
|
||||
- distanceInline
|
||||
- damageShort
|
||||
- hitsToDestroyLong
|
||||
- hitsToDestroyShort
|
||||
- labels.amountOf
|
||||
- labels.damageType
|
||||
- labels.weapon
|
||||
- dmgHtdExplanation
|
||||
- noDmgData
|
||||
|
||||
</details>
|
||||
|
||||
@@ -275,7 +341,7 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**46/60**
|
||||
**46/61**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -283,6 +349,7 @@
|
||||
- pages.s2
|
||||
- pages.analyzer
|
||||
- pages.maps
|
||||
- pages.object-damage-calculator
|
||||
- auth.errors.aborted
|
||||
- auth.errors.failed
|
||||
- auth.errors.discordPermissions
|
||||
@@ -315,7 +382,7 @@
|
||||
|
||||
### 🟡 front.json
|
||||
|
||||
**8/11**
|
||||
**8/12**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -323,6 +390,7 @@
|
||||
- buildsGoTo
|
||||
- analyzer.description
|
||||
- maps.description
|
||||
- object-damage-calculator.description
|
||||
|
||||
</details>
|
||||
|
||||
@@ -375,7 +443,7 @@
|
||||
|
||||
### 🔴 analyzer.json
|
||||
|
||||
**0/94**
|
||||
**0/107**
|
||||
|
||||
### 🔴 badges.json
|
||||
|
||||
@@ -391,7 +459,7 @@
|
||||
|
||||
### 🔴 common.json
|
||||
|
||||
**0/60**
|
||||
**0/61**
|
||||
|
||||
### 🔴 contributions.json
|
||||
|
||||
@@ -403,7 +471,7 @@
|
||||
|
||||
### 🔴 front.json
|
||||
|
||||
**0/11**
|
||||
**0/12**
|
||||
|
||||
### 🟡 game-misc.json
|
||||
|
||||
@@ -430,19 +498,22 @@
|
||||
|
||||
### 🟡 analyzer.json
|
||||
|
||||
**12/94**
|
||||
**12/107**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- objCalcAd
|
||||
- stat.canopyHp
|
||||
- stat.fullChargeSeconds
|
||||
- stat.maxChargeHoldSeconds
|
||||
- stat.specialPoints
|
||||
- stat.specialLost
|
||||
- stat.specialLostSplattedByRP
|
||||
- stat.whiteInk
|
||||
- stat.squidFormInkRecoverySeconds
|
||||
- stat.quickRespawnTime
|
||||
- stat.quickRespawnTimeSplattedByRP
|
||||
- stat.superJumpTimeGround
|
||||
- stat.superJumpTimeTotal
|
||||
- stat.jumpShotSpread
|
||||
@@ -501,6 +572,7 @@
|
||||
- damage.toSplat
|
||||
- damage.NORMAL_MIN
|
||||
- damage.NORMAL_MAX
|
||||
- damage.NORMAL_MAX_FULL_CHARGE
|
||||
- damage.DIRECT
|
||||
- damage.FULL_CHARGE
|
||||
- damage.MAX_CHARGE
|
||||
@@ -517,6 +589,15 @@
|
||||
- abilityPoints.short
|
||||
- consumptionExplanation
|
||||
- trackingSubDefExplanation
|
||||
- distanceInline
|
||||
- damageShort
|
||||
- hitsToDestroyLong
|
||||
- hitsToDestroyShort
|
||||
- labels.amountOf
|
||||
- labels.damageType
|
||||
- labels.weapon
|
||||
- dmgHtdExplanation
|
||||
- noDmgData
|
||||
|
||||
</details>
|
||||
|
||||
@@ -542,7 +623,7 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**46/60**
|
||||
**46/61**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -550,6 +631,7 @@
|
||||
- pages.s2
|
||||
- pages.analyzer
|
||||
- pages.maps
|
||||
- pages.object-damage-calculator
|
||||
- auth.errors.aborted
|
||||
- auth.errors.failed
|
||||
- auth.errors.discordPermissions
|
||||
@@ -581,7 +663,7 @@
|
||||
|
||||
### 🟡 front.json
|
||||
|
||||
**8/11**
|
||||
**8/12**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -589,6 +671,7 @@
|
||||
- buildsGoTo
|
||||
- analyzer.description
|
||||
- maps.description
|
||||
- object-damage-calculator.description
|
||||
|
||||
</details>
|
||||
|
||||
@@ -641,7 +724,7 @@
|
||||
|
||||
### 🔴 analyzer.json
|
||||
|
||||
**0/94**
|
||||
**0/107**
|
||||
|
||||
### 🟢 badges.json
|
||||
|
||||
@@ -665,7 +748,7 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**35/60**
|
||||
**35/61**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -673,6 +756,7 @@
|
||||
- pages.s2
|
||||
- pages.analyzer
|
||||
- pages.maps
|
||||
- pages.object-damage-calculator
|
||||
- auth.errors.aborted
|
||||
- auth.errors.failed
|
||||
- auth.errors.discordPermissions
|
||||
@@ -715,7 +799,7 @@
|
||||
|
||||
### 🟡 front.json
|
||||
|
||||
**8/11**
|
||||
**8/12**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -723,6 +807,7 @@
|
||||
- buildsGoTo
|
||||
- analyzer.description
|
||||
- maps.description
|
||||
- object-damage-calculator.description
|
||||
|
||||
</details>
|
||||
|
||||
@@ -773,9 +858,28 @@
|
||||
|
||||
## /nl (🟡 In progress)
|
||||
|
||||
### 🟢 analyzer.json
|
||||
### 🟡 analyzer.json
|
||||
|
||||
**94/94**
|
||||
**94/107**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- objCalcAd
|
||||
- stat.specialLostSplattedByRP
|
||||
- stat.quickRespawnTimeSplattedByRP
|
||||
- damage.NORMAL_MAX_FULL_CHARGE
|
||||
- distanceInline
|
||||
- damageShort
|
||||
- hitsToDestroyLong
|
||||
- hitsToDestroyShort
|
||||
- labels.amountOf
|
||||
- labels.damageType
|
||||
- labels.weapon
|
||||
- dmgHtdExplanation
|
||||
- noDmgData
|
||||
|
||||
</details>
|
||||
|
||||
### 🟢 badges.json
|
||||
|
||||
@@ -799,13 +903,14 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**48/60**
|
||||
**48/61**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- pages.analyzer
|
||||
- pages.maps
|
||||
- pages.object-damage-calculator
|
||||
- auth.errors.aborted
|
||||
- auth.errors.failed
|
||||
- auth.errors.discordPermissions
|
||||
@@ -829,12 +934,13 @@
|
||||
|
||||
### 🟡 front.json
|
||||
|
||||
**10/11**
|
||||
**10/12**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- maps.description
|
||||
- object-damage-calculator.description
|
||||
|
||||
</details>
|
||||
|
||||
@@ -875,7 +981,7 @@
|
||||
|
||||
### 🔴 analyzer.json
|
||||
|
||||
**0/94**
|
||||
**0/107**
|
||||
|
||||
### 🟢 badges.json
|
||||
|
||||
@@ -899,7 +1005,7 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**35/60**
|
||||
**35/61**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -907,6 +1013,7 @@
|
||||
- pages.s2
|
||||
- pages.analyzer
|
||||
- pages.maps
|
||||
- pages.object-damage-calculator
|
||||
- auth.errors.aborted
|
||||
- auth.errors.failed
|
||||
- auth.errors.discordPermissions
|
||||
@@ -949,7 +1056,7 @@
|
||||
|
||||
### 🟡 front.json
|
||||
|
||||
**8/11**
|
||||
**8/12**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -957,6 +1064,7 @@
|
||||
- buildsGoTo
|
||||
- analyzer.description
|
||||
- maps.description
|
||||
- object-damage-calculator.description
|
||||
|
||||
</details>
|
||||
|
||||
@@ -1007,9 +1115,28 @@
|
||||
|
||||
## /zh (🟡 In progress)
|
||||
|
||||
### 🟢 analyzer.json
|
||||
### 🟡 analyzer.json
|
||||
|
||||
**94/94**
|
||||
**94/107**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
|
||||
- objCalcAd
|
||||
- stat.specialLostSplattedByRP
|
||||
- stat.quickRespawnTimeSplattedByRP
|
||||
- damage.NORMAL_MAX_FULL_CHARGE
|
||||
- distanceInline
|
||||
- damageShort
|
||||
- hitsToDestroyLong
|
||||
- hitsToDestroyShort
|
||||
- labels.amountOf
|
||||
- labels.damageType
|
||||
- labels.weapon
|
||||
- dmgHtdExplanation
|
||||
- noDmgData
|
||||
|
||||
</details>
|
||||
|
||||
### 🟢 badges.json
|
||||
|
||||
@@ -1033,7 +1160,7 @@
|
||||
|
||||
### 🟡 common.json
|
||||
|
||||
**35/60**
|
||||
**35/61**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -1041,6 +1168,7 @@
|
||||
- pages.s2
|
||||
- pages.analyzer
|
||||
- pages.maps
|
||||
- pages.object-damage-calculator
|
||||
- auth.errors.aborted
|
||||
- auth.errors.failed
|
||||
- auth.errors.discordPermissions
|
||||
@@ -1083,7 +1211,7 @@
|
||||
|
||||
### 🟡 front.json
|
||||
|
||||
**8/11**
|
||||
**8/12**
|
||||
|
||||
<details>
|
||||
<summary>Missing</summary>
|
||||
@@ -1091,6 +1219,7 @@
|
||||
- buildsGoTo
|
||||
- analyzer.description
|
||||
- maps.description
|
||||
- object-damage-calculator.description
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user