import { assertUnreachable } from "~/utils/types"; import type { MainWeaponId, SpecialWeaponId, SubWeaponId } from "./types"; export const weaponCategories = [ { name: "SHOOTERS", weaponIds: [ 0, 1, 10, 11, 20, 21, 22, 30, 31, 32, 40, 41, 42, 45, 46, 47, 50, 51, 60, 61, 70, 71, 72, 80, 81, 82, 90, 91, 92, 100, 101, 300, 301, 302, 310, 311, 312, 400, 401, ], }, { name: "BLASTERS", weaponIds: [ 200, 201, 205, 210, 211, 212, 220, 221, 230, 231, 240, 241, 250, 251, 252, 260, 261, ], }, { name: "ROLLERS", weaponIds: [ 1000, 1001, 1002, 1010, 1011, 1015, 1020, 1021, 1022, 1030, 1031, 1040, 1041, 1042, ], }, { name: "BRUSHES", weaponIds: [1100, 1101, 1110, 1111, 1112, 1115, 1120, 1121, 1122], }, { name: "CHARGERS", weaponIds: [ 2000, 2001, 2010, 2011, 2012, 2015, 2020, 2021, 2022, 2030, 2031, 2040, 2041, 2050, 2051, 2060, 2061, 2070, 2071, ], }, { name: "SLOSHERS", weaponIds: [ 3000, 3001, 3005, 3010, 3011, 3012, 3020, 3021, 3030, 3031, 3040, 3041, 3050, 3051, 3052, ], }, { name: "SPLATLINGS", weaponIds: [ 4000, 4001, 4002, 4010, 4011, 4015, 4020, 4021, 4022, 4030, 4031, 4040, 4041, 4050, 4051, ], }, { name: "DUALIES", weaponIds: [ 5000, 5001, 5002, 5010, 5015, 5011, 5012, 5020, 5021, 5030, 5031, 5032, 5040, 5041, 5050, 5051, ], }, { name: "BRELLAS", weaponIds: [ 6000, 6001, 6005, 6010, 6011, 6012, 6020, 6021, 6022, 6030, 6031, ], }, { name: "STRINGERS", weaponIds: [7010, 7011, 7012, 7015, 7020, 7021, 7022, 7030, 7031], }, { name: "SPLATANAS", weaponIds: [8000, 8001, 8002, 8005, 8010, 8011, 8012, 8020, 8021], }, ] as const; export const mainWeaponIds = weaponCategories .flatMap((category) => category.weaponIds) .sort((a, b) => a - b); const weaponIdToAltId = new Map([ [40, [45, 47]], [41, 46], [200, 205], [1010, 1015], [1110, 1115], [2010, 2015], [3000, 3005], [4010, 4015], [5010, 5015], [6000, 6005], [7010, 7015], [8000, 8005], ]); const altWeaponIdToId = new Map([ [45, 40], [47, 40], [46, 41], [205, 200], [1015, 1010], [1115, 1110], [2015, 2010], [3005, 3000], [4015, 4010], [5015, 5010], [6005, 6000], [7015, 7010], [8005, 8000], ]); /** * Folds an alt-skin weapon id to its base id, leaving non-alt ids untouched. * * Unlike {@link weaponIdToBaseWeaponId}, alt kits are preserved (e.g. Tentatek * Splattershot stays 41) — only cosmetic alt skins collapse to their base. * * Mirrors the `BuildWeapon.canonicalWeaponSplId` column so TypeScript callers * and SQL queries agree on what a weapon's canonical id is. * * @example * // Splattershot — base weapon, returned unchanged * canonicalWeaponSplId(40); // -> 40 * * // Tentatek Splattershot — alt kit, returned unchanged * canonicalWeaponSplId(41); // -> 41 * * // Hero Shot Replica — alt skin, folded to Splattershot * canonicalWeaponSplId(45); // -> 40 */ export function canonicalWeaponSplId(weaponSplId: MainWeaponId): MainWeaponId { return altWeaponIdToId.get(weaponSplId) ?? weaponSplId; } /** * Converts a given weapon ID to an array containing the weapon ID and its alternate IDs. For example if you enter the ID 40 (Splattershot) it will return [40, 45, 47] (Splattershot, Hero Shot Replica, Order Shot Replica) */ export function weaponIdToArrayWithAlts(weaponId: MainWeaponId) { const altId = weaponIdToAltId.get(weaponId); if (altId !== undefined) { return [weaponId, ...(Array.isArray(altId) ? altId : [altId])]; } const regularId = altWeaponIdToId.get(weaponId); if (regularId !== undefined) { const altId = weaponIdToAltId.get(regularId); if (altId !== undefined) { return [regularId, ...(Array.isArray(altId) ? altId : [altId])]; } } return [weaponId]; } /** * Determines the type of weapon based on its ID * * @returns "ALT_SKIN" if the weapon is an alternate skin, "BASE" if it's a base weapon, or "ALT_KIT" if it's an alternate kit * * @example * // Splattershot is a base weapon * weaponIdToType(40); // -> "BASE" * * // Tentatek Splattershot is an alternate kit * weaponIdToType(41); // -> "ALT_KIT" * * // Hero Shot Replica is an alternate skin * weaponIdToType(45); // -> "ALT_SKIN" */ export const weaponIdToType = (weaponId: MainWeaponId) => { if (altWeaponIdToId.has(weaponId)) return "ALT_SKIN"; if (weaponId === weaponIdToBaseWeaponId(weaponId)) return "BASE"; return "ALT_KIT"; }; export const SPLAT_BOMB_ID = 0; export const SUCTION_BOMB_ID = 1; export const BURST_BOMB_ID = 2; export const SPRINKLER_ID = 3; export const SPLASH_WALL_ID = 4; export const FIZZY_BOMB_ID = 5; export const CURLING_BOMB_ID = 6; export const AUTO_BOMB_ID = 7; export const SQUID_BEAKON_ID = 8; export const POINT_SENSOR_ID = 9; export const INK_MINE_ID = 10; export const TOXIC_MIST_ID = 11; export const ANGLE_SHOOTER_ID = 12; export const TORPEDO_ID = 13; export const subWeaponIds = [ SPLAT_BOMB_ID, SUCTION_BOMB_ID, BURST_BOMB_ID, SPRINKLER_ID, SPLASH_WALL_ID, FIZZY_BOMB_ID, CURLING_BOMB_ID, AUTO_BOMB_ID, SQUID_BEAKON_ID, POINT_SENSOR_ID, INK_MINE_ID, TOXIC_MIST_ID, ANGLE_SHOOTER_ID, TORPEDO_ID, ] as const; export const nonBombSubWeaponIds = [ SPRINKLER_ID, SPLASH_WALL_ID, SQUID_BEAKON_ID, POINT_SENSOR_ID, TOXIC_MIST_ID, ] as SubWeaponId[]; export const TRIZOOKA_ID = 1; export const BIG_BUBBLER_ID = 2; export const ZIPCASTER_ID = 3; const TENTA_MISSILES_ID = 4; export const INK_STORM_ID = 5; export const BOOYAH_BOMB_ID = 6; export const WAVE_BREAKER_ID = 7; export const INK_VAC_ID = 8; export const KILLER_WAIL_ID = 9; const INKJET_ID = 10; const ULTRA_STAMP_ID = 11; export const CRAB_TANK_ID = 12; const REEF_SLIDER_ID = 13; const TRIPLE_INKSTRIKE_ID = 14; const TACTICOOLER_ID = 15; export const SUPER_CHUMP_ID = 16; const KRAKEN_ROYALE_ID = 17; export const TRIPLE_SPLASHDOWN_ID = 18; export const SPLATTERCOLOR_SCREEN_ID = 19; export const specialWeaponIds = [ TRIZOOKA_ID, BIG_BUBBLER_ID, ZIPCASTER_ID, TENTA_MISSILES_ID, INK_STORM_ID, BOOYAH_BOMB_ID, WAVE_BREAKER_ID, INK_VAC_ID, KILLER_WAIL_ID, INKJET_ID, ULTRA_STAMP_ID, CRAB_TANK_ID, REEF_SLIDER_ID, TRIPLE_INKSTRIKE_ID, TACTICOOLER_ID, SUPER_CHUMP_ID, KRAKEN_ROYALE_ID, TRIPLE_SPLASHDOWN_ID, SPLATTERCOLOR_SCREEN_ID, ] as const; export const nonDamagingSpecialWeaponIds = [BIG_BUBBLER_ID, TACTICOOLER_ID]; export const exampleMainWeaponIdWithSpecialWeaponId = ( specialWeaponId: SpecialWeaponId, ): MainWeaponId => { switch (specialWeaponId) { case TRIZOOKA_ID: return 40; case BIG_BUBBLER_ID: return 10; case ZIPCASTER_ID: return 8000; case TENTA_MISSILES_ID: return 1030; case INK_STORM_ID: return 3040; case BOOYAH_BOMB_ID: return 3020; case WAVE_BREAKER_ID: return 220; case INK_VAC_ID: return 2010; case KILLER_WAIL_ID: return 50; case INKJET_ID: return 3010; case ULTRA_STAMP_ID: return 4000; case CRAB_TANK_ID: return 20; case REEF_SLIDER_ID: return 5040; case TRIPLE_INKSTRIKE_ID: return 41; case TACTICOOLER_ID: return 60; case SUPER_CHUMP_ID: return 61; case KRAKEN_ROYALE_ID: return 4011; case TRIPLE_SPLASHDOWN_ID: return 211; case SPLATTERCOLOR_SCREEN_ID: return 401; default: { assertUnreachable(specialWeaponId); } } }; /** * Calculates the base weapon ID from a main weapon ID. * * @example * // Returns 40 (because Splattershot is the base weapon for its kit) * weaponIdToBaseWeaponId(40); // -> 40 * * // (41 is Tentatek Splattershot, which is an alt kit of Splattershot) * weaponIdToBaseWeaponId(41); // -> 40 * * // (45 is Hero Shot Replica, which is an alt skin of Splattershot) * weaponIdToBaseWeaponId(45); // -> 40 */ export const weaponIdToBaseWeaponId = (id: MainWeaponId) => (id - (id % 10)) as MainWeaponId;