diff --git a/lib/lists/abilities.ts b/lib/lists/abilities.ts index 0d5838a0c..ebc888a2c 100644 --- a/lib/lists/abilities.ts +++ b/lib/lists/abilities.ts @@ -49,3 +49,6 @@ export const isMainAbility = (ability: any) => (abilityObject) => abilityObject.code === ability && abilityObject.type !== "STACKABLE" ); + +export const isAbility = (ability: any) => + abilities.some((abilityObject) => abilityObject.code === ability); diff --git a/lib/lists/gear.ts b/lib/lists/gear.ts new file mode 100644 index 000000000..35554eb31 --- /dev/null +++ b/lib/lists/gear.ts @@ -0,0 +1,724 @@ +export const gear = [ + { + brand: "amiibo", + clothing: [ + "Enchanted Robe", + "Fresh Fish Gloves", + "Marinated Top", + "Pearlescent Hoodie", + "Power Armor", + "Power Armor Mk I", + "Samurai Jacket", + "School Cardigan", + "School Uniform", + "Squinja Suit", + "Steel Platemail", + ], + head: [ + "Enchanted Hat", + "Fresh Fish Head", + "Marinated Headphones", + "Pearlescent Crown", + "Power Mask", + "Power Mask Mk I", + "Samurai Helmet", + "Squid Clip-Ons", + "Squid Hairclip", + "Squinja Mask", + "Steel Helm", + ], + shoes: [ + "Enchanted Boots", + "Fresh Fish Feet", + "Fringed Loafers", + "Marinated Slip-Ons", + "Pearlescent Kicks", + "Power Boots", + "Power Boots Mk I", + "Samurai Shoes", + "School Shoes", + "Squinja Boots", + "Steel Greaves", + ], + }, + { + brand: "Annaki", + clothing: [ + "Annaki Blue Cuff", + "Annaki Drive Tee ", + "Annaki Evolution Tee", + "Annaki Flannel Hoodie", + "Annaki Polpo-Pic Tee", + "Annaki Red Cuff", + "Annaki Yellow Cuff", + "Crimson Parashooter", + "Hula Punk Shirt", + "Prune Parashooter", + "Red Hula Punk with Tie", + "Rockin' Leather Jacket", + ], + head: ["Annaki Beret", "Annaki Beret & Glasses", "Annaki Mask"], + shoes: ["Annaki Arachno Boots", "Annaki Habaneros", "Annaki Tigers"], + }, + { + brand: "Cuttlegear", + clothing: [ + "Armor Jacket Replica", + "Black Cuttlegear LS", + "Fresh Octo Tee", + "Hero Hoodie Replica", + "Hero Jacket Replica", + "Neo Octoling Armor", + "Null Armor Replica", + "Octarian Retro", + "Octo Layered LS", + "Octo Tee", + "Old-Timey clothing", + "Red Cuttlegear LS", + "Sennyu Suit", + ], + head: [ + "Armor Helmet Replica", + "Conductor Cap", + "Golden Toothpick", + "Hero Headphones Replica", + "Hero Headset Replica", + "Null Visor Replica", + "Octoling Shades", + "Old-Timey Hat", + "Studio Octophones", + ], + shoes: [ + "Armor Boot Replicas", + "Hero Runner Replicas", + "Hero Snowboots Replicas", + "Neo Octoling Boots", + "Null Boots Replica", + "Old-Timey Shoes", + ], + }, + { + brand: "Enperry", + clothing: [ + "Black Velour Octoking Tee ", + "Grape Hoodie", + "Green Velour Octoking Tee", + "King Jersey", + "Milky Eminence Jacket", + "N-Pacer Sweat", + "Navy Eminence Jacket", + "Navy King Tank", + "Octoking HK Jersey", + "Slash King Tank", + "White King Tank", + ], + head: [ + "Eminence Cuff", + "King Facemask", + "King Flip Mesh", + "Octoking Facemask", + ], + shoes: [ + "Black & Blue Squidkid V", + "Blue & Black Squidkid IV", + "Honey & Orange Squidkid V", + "Milky Enperrials", + "N-Pacer Ag", + "N-Pacer Au", + "N-Pacer CaO", + "Navy Enperrials", + "Online Squidkid V", + "Pearlescent Squidkid IV", + "Red & Black Squidkid IV", + "Red & White Squidkid V", + "Rina Squidkid IV", + "Sun & Shade Squidkid IV", + ], + }, + { + brand: "Firefin", + clothing: [ + "Baseball Jersey", + "Black 8-Bit FishFry", + "Blue 16-Bit FishFry", + "Camo Zip Hoodie", + "Carnivore Tee", + "Dark Urban Vest", + "Firefin Navy Sweat", + "Fugu Tee", + "Gray 8-Bit FishFry", + "Green Zip Hoodie", + "Herbivore Tee", + "Khaki 16-Bit FishFry", + "White 8-Bit FishFry", + "Yellow Urban Vest", + "ω-3 Tee", + ], + head: [ + "Black FishFry Bandana", + "Blowfish Bell Hat", + "Blowfish Newsie", + "Camo Mesh", + "Firefin Facemask", + "FishFry Biscuit Bandana", + "FishFry Visor", + "Fugu Bell Hat", + "Jet Cap", + "Jungle Hat", + "Knitted Hat", + "Octoglasses", + ], + shoes: ["Red FishFry Sandals", "Yellow FishFry Sandals"], + }, + { + brand: "Forge", + clothing: [ + "Aloha Shirt", + "Blue Sailor Suit", + "Brown FA-11 Bomber", + "Custom Painted F-3 ", + "FA-01 Jacket", + "FA-01 Reversed", + "Forge Inkling Parka", + "Forge Octarian Jacket", + "Gray FA-11 Bomber", + "Green Tee", + "Moist Ghillie Suit", + "White Leather F-3", + "White Sailor Suit", + ], + head: [ + "Deca Tackle Visor Helmet", + "Designer Headphones", + "Digi-Camo Forge Mask", + "Forge Mask", + "Gas Mask", + "Hockey Helmet", + "Moist Ghillie Helmet", + "Motocross Nose Guard", + "Noise Cancelers", + "Octo Tackle Helmet Deco", + "Paintball Mask", + "Pilot Goggles", + "Safari Hat", + "Sennyu Goggles", + "Sennyu Headphones", + "Skull Bandana", + "Snorkel Mask", + "Special Forces Beret", + "Splash Goggles", + "Squidfin Hook Cans", + "Squidlife Headphones", + "Stealth Goggles", + "Studio Headphones", + "Treasure Hunter", + ], + shoes: ["Moist Ghillie Boots"], + }, + { + brand: "Grizzco", + clothing: [ + "Anchor Life Vest", + "Crustwear XXL", + "Dev Uniform", + "Garden Gear", + "Juice Parka", + "North-Country Parka", + "Octoleet Armor", + "Office Attire", + "Online Jersey", + "Record Shop Look EP", + "Squiddor Polo", + "SRL Coat", + ], + head: [ + "Beekeeper Hat", + "Cap of Legend", + "Dust Blocker 2000", + "Headlamp Helmet", + "Oceanic Hard Hat", + "Octoleet Goggles", + "Sailor Cap", + "Welding Mask", + "Worker's Cap", + "Worker's Head Towel", + ], + shoes: [ + "Angry Rain Boots", + "Flipper Floppers", + "Friendship Bracelet", + "Non-slip Senseis", + "Octoleet Boots", + "Wooden Sandals", + ], + }, + { + brand: "Inkline", + clothing: [ + "Berry Ski Jacket", + "Blue Peaks Tee", + "Chili-Pepper Ski Jacket", + "Chilly Mountain Coat", + "Eggplant Mountain Coat", + "Fishing Vest", + "Forest Vest", + "Green Striped LS", + "Ivory Peaks Tee", + "Matcha Down Jacket", + "Mountain Vest", + "Olive Ski Jacket", + "Striped Peaks LS", + ], + head: [ + "Bamboo Hat", + "Camping Hat", + "Lightweight Cap", + "Mountie Hat", + "Seashell Bamboo Hat", + "Short Beanie", + "Tulip Parasol", + "Yamagiri Beanie", + ], + shoes: [ + "Acerola Rain Boots", + "Birch Climbing Shoes", + "Bubble Rain Boots", + "Custom Trail Boots", + "Green Rain Boots", + "Luminous Delta Straps", + "Musselforge Flip-Flops", + "Neon Delta Straps", + "Pro Trail Boots", + "Snow Delta Straps", + "Sunny Climbing Shoes", + "Trail Boots", + ], + }, + { + brand: "Krak-On", + clothing: [ + "Chili Octo Aloha", + "Krak-On 528", + "Missus Shrug Tee", + "Mister Shrug Tee", + "Octobowler Shirt", + "Rainy-Day Tee", + "Rodeo Shirt", + "Squid-Pattern Waistcoat", + "Squidstar Waistcoat", + "Sunny-Day Tee", + ], + head: [ + "Full Moon Glasses", + "Hickory Work Cap", + "Long-Billed Cap", + "Paisley Bandana", + "Two-Stripe Mesh", + "Woolly Urchins Classic", + ], + shoes: [ + "Banana Basics", + "Blue Slip-Ons", + "Blueberry Casuals", + "Choco Clogs", + "Clownfish Basics", + "Cream Basics", + "Cream Hi-Tops", + "Hunter Hi-Tops", + "Midnight Slip-Ons", + "Oyster Clogs", + "Plum Casuals", + "Polka-dot Slip-Ons", + "Red Hi-Tops", + "Red Slip-Ons", + "Squid-Stitch Slip-Ons", + "Truffle Canvas Hi-Tops", + ], + }, + { + brand: "Rockenberg", + clothing: [ + "Black Baseball LS", + "Black Inky Rider", + "Black Urchin Rock Tee", + "Chirpy Chips Band Tee", + "Hightide Era Band Tee", + "Lumberjack Shirt", + "Rockenberg Black", + "Rockenberg White", + "Round-Collar Shirt", + "Squid Squad Band Tee", + "Vintage Check Shirt", + "Wet Floor Band Tee", + "White Baseball LS", + "White Inky Rider", + "White Urchin Rock Tee", + ], + head: ["18K Aviators", "SV925 Circle Shades"], + shoes: [ + "Blue Moto Boots", + "Cherry Kicks", + "Deepsea Leather Boots", + "Gray Yellow-Soled Wingtips", + "Inky Kid Clams", + "Kid Clams", + "Moto Boots", + "Navy Red-Soled Wingtips", + "New-Leaf Leather Boots", + "Pearl Punk Crowns", + "Punk Blacks", + "Punk Cherries", + "Punk Whites", + "Punk Yellows", + "Red Work Boots", + "Roasted Brogues", + "Sennyu Inksoles", + "Smoky Wingtips", + "Squink Wingtips", + "Tan Work Boots", + "Turquoise Kicks", + "White Kicks", + ], + }, + { + brand: "Skalop", + clothing: [ + "Black Hoodie", + "Dots-On-Dots Shirt", + "Firewave Tee", + "Grape Tee", + "Gray Hoodie", + "Hothouse Hoodie", + "Icewave Tee", + "Mint Tee", + "Pearl Tee", + "Reggae Tee", + "Squid-Stitch Tee", + ], + head: [ + "Bike Helmet", + "Classic Straw Boater", + "Do-Rag, Cap, & Glasses", + "Hothouse Hat", + "Jellyvader Cap", + "Patched Hat", + "Skate Helmet", + "Sneaky Beanie", + "Sporty Bobble Hat", + "Squid Nordic", + "Squid-Stitch Cap", + "Squidvader Cap", + "Straw Boater", + "Streetstyle Cap", + "Urchins Cap", + "Visor Skate Helmet", + ], + shoes: [], + }, + { + brand: "Splash Mob", + clothing: [ + "Baby-Jelly Shirt", + "Baby-Jelly Shirt & Tie", + "Gray College Sweat", + "Green Cardigan", + "Lime Easy-Stripe Shirt", + "Linen Shirt", + "Navy College Sweat", + "Navy Striped LS", + "Orange Cardigan", + "Pink Easy-Stripe Shirt", + "Pink Hoodie", + "Pirate-Stripe Tee", + "Sage Polo", + "Sailor-Stripe Tee", + "Shirt & Tie", + "Shirt with Blue Hoodie", + "Shrimp-Pink Polo", + "Striped Shirt", + "Varsity Baseball LS", + "Whale-Knit Sweater", + "White Shirt", + "White Striped LS", + ], + head: [ + "Bobble Hat", + "Half-Rim Glasses", + "House-Tag Denim Cap", + "Pilot Hat", + "Retro Specs", + "Sennyu Bon Bon Beanie", + "Sennyu Specs", + "Striped Beanie", + ], + shoes: [ + "Green Laceups", + "Hunting Boots", + "Mawcasins", + "Piranha Moccasins", + "Shark Moccasins", + "Strapping Reds", + "Strapping Whites", + "Tea-Green Hunting Boots", + ], + }, + { + brand: "SquidForce", + clothing: [ + "Anchor Sweat", + "Basic Tee", + "Black Anchor Tee", + "Black Layered LS", + "Black Tee", + "Black V-Neck Tee", + "Camo Layered LS", + "Friend Tee", + "Green V-Neck Limited Tee", + "Layered Anchor LS", + "League Tee", + "Mecha Body - AKM", + "Octo Support Hoodie", + "Red V-Neck Limited Tee", + "Retro Sweat", + "Splatfest Tee", + "Squid Yellow Layered LS", + "Squidmark LS", + "Squidmark Sweat", + "SWC Logo Tee", + "White Anchor Tee", + "White Layered LS", + "White LS", + "White Tee", + "White V-Neck Tee", + "Yellow Layered LS", + ], + head: [ + "Anglerfish Mask", + "Bucket Hat", + "Eel-Cake Hat", + "Festive Party Cone", + "Hockey Mask", + "Koshien Bandana", + "Kyonshi Hat", + "Li'l Devil Horns", + "Mecha Head - HTR", + "New Year's Glasses DX", + "Painter's Mask", + "Squid Facemask", + "Twisty Headband", + "White Headband", + "Jetflame Crest", + "Fierce Fishskull", + "Hivemind Antenna", + "Eye of Justice", + ], + shoes: ["Mecha Legs - LBS"], + }, + { + brand: "Takoroka", + clothing: [ + "Choco Layered LS", + "FC Albacore", + "Gray Vector Tee", + "Layered Vector LS", + "Purple Camo LS", + "Red Vector Tee", + "Slipstream United", + "Striped Rugby", + "Takoroka Crazy Baseball LS", + "Takoroka Galactic Tie Dye", + "Takoroka Jersey", + "Takoroka Nylon Vintage", + "Takoroka Rainbow Tie Dye", + "Takoroka Windcrusher", + "Tricolor Rugby", + ], + head: ["Takoroka Mesh", "Takoroka Visor"], + shoes: [ + "Athletic Arrows", + "Blue Power Stripes", + "Crazy Arrows", + "LE Soccer Shoes", + "New-Day Arrows", + "Orange Arrows", + "Orca Hi-Tops", + "Orca Passion Hi-Tops", + "Orca Woven Hi-Tops", + "Red Power Stripes", + "Soccer Shoes", + "Sunset Orca Hi-Tops", + "Trooper Power Stripes", + "White Arrows", + ], + }, + { + brand: "Tentatek", + clothing: [ + "Black Squideye", + "Blue Tentatek Tee", + "Cycle King Jersey", + "Lob-Stars Jersey", + "Part-Time Pirate", + "Red Tentatek Tee", + "Silver Tentatek Vest", + "Sky-Blue Squideye", + "Tentatek Slogan Tee", + ], + head: [ + "Cycle King Cap", + "Fake Contacts", + "Green Novelty Visor", + "Orange Novelty Visor", + "Pink Novelty Visor", + "Purple Novelty Visor", + "Soccer Headband", + "Sun Visor", + "Tennis Headband", + ], + shoes: [ + "Amber Sea Slug Hi-Tops", + "Black Norimaki 750s", + "Black Trainers", + "Blue Iromaki 750s", + "Blue Sea Slugs", + "Canary Trainers", + "Cyan Trainers", + "Gray Sea-Slug Hi-Tops", + "Green Iromaki 750s", + "Icy Down Boots", + "Neon Sea Slugs", + "Orange Iromaki 750s", + "Pink Trainers", + "Purple Iromaki 750s", + "Purple Sea Slugs", + "Red Iromaki 750s", + "Red Sea Slugs", + "Red-Mesh Sneakers", + "Sea Slug Volt 95s", + "Sesame Salt 270s", + "Snowy Down Boots", + "Violet Trainers", + "White Norimaki 750s", + "Yellow Iromaki 750s", + "Yellow-Mesh Sneakers", + ], + }, + { + brand: "Toni Kensa", + clothing: [ + "Dark Bomber Jacket", + "Front Zip Vest", + "Half-Sleeve Sweater", + "Ink-Wash Shirt", + "Inkfall Shirt", + "Kensa Coat", + "Kung-Fu Zip-Up", + "Light Bomber Jacket", + "Negative Longcuff Sweater", + "Panda Kung-Fu Zip-Up", + "Positive Longcuff Sweater", + "Pullover Coat", + "Short Knit Layers", + "Toni K. Baseball Jersey", + ], + head: ["Face Visor", "Ink-Guard Goggles", "Toni Kensa Goggles"], + shoes: [ + "Arrow Pull-Ons", + "Toni Kensa Black Hi-Tops", + "Toni Kensa Soccer Shoes", + ], + }, + { + brand: "Zekko", + clothing: [ + "Birded Corduroy Jacket", + "Black LS", + "Black Polo", + "Deep-Octo Satin Jacket", + "Gray Mixed Shirt", + "Green-Check Shirt", + "Logo Aloha Shirt", + "Olive Zekko Parka", + "Red-Check Shirt", + "Reel Sweat", + "Squid Satin Jacket", + "Tumeric Zekko Coat", + "Varsity Jacket", + "Zapfish Satin Jacket", + "Zekko Baseball LS", + "Zekko Hoodie", + "Zekko Jade Coat", + "Zekko Long Carrot Tee", + "Zekko Long Radish Tee", + "Zekko Redleaf Coat", + ], + head: [ + "Backwards Cap", + "Black Arrowbands", + "Double Egg Shades", + "Five-Panel Cap", + "Jogging Headband", + "Matte Bike Helmet", + "MTB Helmet", + "Tinted Shades", + "White Arrowbands", + "Zekko Cap", + "Zekko Mesh", + ], + shoes: [ + "Black Flip-Flops", + "Blue Lo-Tops", + "LE Lo-Tops", + "Marination Lace-Ups", + "Orange Lo-Tops", + "Pearl-Scout Lace-Ups", + "Suede Gray Lace-Ups", + "Suede Marine Lace-Ups", + "Suede Nation Lace-Ups", + ], + }, + { + brand: "Zink", + clothing: [ + "B-ball Jersey (Away)", + "B-ball Jersey (Home)", + "Cycling Shirt", + "Dakro Golden Tee", + "Dakro Nana Tee", + "Inkopolis Squaps Jersey", + "Navy Deca Logo Tee", + "Retro Gamer Jersey", + "School Jersey", + "Urchins Jersey", + "White Deca Logo Tee", + "Zink Layered LS", + "Zink LS", + ], + head: [ + "B-ball Headband", + "Cycling Cap", + "Golf Visor", + "Squash Headband", + "Swim Goggles", + ], + shoes: [ + "Black Dakroniks", + "Black Seahorses", + "Blue Laceless Dakroniks", + "Chocolate Dakroniks", + "Gold Hi-Horses", + "Mint Dakroniks", + "Purple Hi-Horses", + "Red Hi-Horses", + "White Laceless Dakroniks", + "White Seahorses", + "Yellow Seahorses", + "Zombie Hi-Horses", + ], + }, +] as const; + +export const headGear = gear.flatMap((brand) => brand.head); +export const clothingGear = gear.flatMap((brand) => brand.clothing); +// for some reason TypeScript freaked out when just using .flatMap() here +export const shoesGear = gear.map((brand) => brand.shoes).flat(); diff --git a/lib/validators/build.ts b/lib/validators/build.ts new file mode 100644 index 000000000..7966ee088 --- /dev/null +++ b/lib/validators/build.ts @@ -0,0 +1,58 @@ +import { abilities } from "lib/lists/abilities"; +import { clothingGear, headGear, shoesGear } from "lib/lists/gear"; +import { weaponsWithHero } from "lib/lists/weaponsWithHero"; +import * as z from "zod"; +import { hasNoDuplicates } from "./common"; + +export const buildSchema = z.object({ + weapon: z.string().refine((val) => weaponsWithHero.includes(val as any)), + title: z.string().max(100).optional().nullable(), + description: z.string().max(1000).optional().nullable(), + modes: z + .array(z.string()) + .min(1) + .max(5) + .refine((arr) => + arr.every((val) => ["TW", "SZ", "TC", "RM", "CB"].includes(val as any)) + ) + .refine(hasNoDuplicates), + headAbilities: z + .array(z.string()) + .refine((arr) => isAbilityArray(arr, "HEAD")), + clothingAbilities: z + .array(z.string()) + .refine((arr) => isAbilityArray(arr, "CLOTHING")), + shoesAbilities: z + .array(z.string()) + .refine((arr) => isAbilityArray(arr, "SHOES")), + headGear: z + .string() + .refine((val) => headGear.includes(val as any)) + .optional() + .nullable(), + clothingGear: z + .string() + .refine((val) => clothingGear.includes(val as any)) + .optional() + .nullable(), + shoesGear: z + .string() + .refine((val) => shoesGear.includes(val as any)) + .optional() + .nullable(), +}); + +function isAbilityArray(arr: any[], type: "HEAD" | "CLOTHING" | "SHOES") { + if (arr.length !== 4) return false; + + return arr.every((value, index) => { + const ability = abilities.find( + (abilityCodeInArray) => value === abilityCodeInArray + ); + if (!ability) return false; + + if (index === 0) return ["STACKABLE", type].includes(ability.type); + + return ability.type === "STACKABLE"; + }); +} diff --git a/lib/validators/common.ts b/lib/validators/common.ts new file mode 100644 index 000000000..aed007845 --- /dev/null +++ b/lib/validators/common.ts @@ -0,0 +1 @@ +export const hasNoDuplicates = (arr: any[]) => new Set(arr).size === arr.length; diff --git a/lib/validators/profile.ts b/lib/validators/profile.ts index 385120e5a..a171a92e1 100644 --- a/lib/validators/profile.ts +++ b/lib/validators/profile.ts @@ -1,6 +1,7 @@ import { countries } from "countries-list"; import { weaponsWithHero } from "lib/lists/weaponsWithHero"; import * as z from "zod"; +import { hasNoDuplicates } from "./common"; export const PROFILE_CHARACTER_LIMIT = 7000; @@ -27,7 +28,7 @@ const profileRootSchema = z.object({ .array(z.string()) .max(5) .refine((arr) => arr.every((val) => weaponsWithHero.includes(val as any))) - .refine((arr) => new Set(arr).size === arr.length), + .refine(hasNoDuplicates), }); export const profileSchemaFrontend = profileRootSchema.extend({ diff --git a/prisma/scripts/dataFromMongo.ts b/prisma/scripts/dataFromMongo.ts index ad6234994..da6a16c43 100644 --- a/prisma/scripts/dataFromMongo.ts +++ b/prisma/scripts/dataFromMongo.ts @@ -35,11 +35,13 @@ for (const player of playersJson) { const discordIdToBuilds = new Map(); +const modes: any[] = []; + // @ts-ignore for (const build of buildsJson) { const existing = discordIdToBuilds.get(build.discord_id); - const newBuild = { + const newBuild: BuildCreateWithoutUserInput = { abilityPoints: buildToAp(build.headgear, build.clothing, build.shoes), jpn: build.discord_id === "312082701865713665", top500: build.top, @@ -53,6 +55,7 @@ for (const build of buildsJson) { shoesAbilities: build.shoes, shoesGear: build.shoesItem, title: build.title, + modes, } as const; if (existing) {