@@ -157,7 +157,7 @@ function Groups() {
? data.groups.filter(
(group) =>
!data.likes.received.some(
- (like) => like.groupId === group.id,
+ (like) => like.teamId === group.id,
),
)
: neutralGroups
@@ -166,7 +166,7 @@ function Groups() {
key={group.id}
group={group}
action={
- data.likes.given.some((like) => like.groupId === group.id)
+ data.likes.given.some((like) => like.teamId === group.id)
? "UNLIKE"
: "LIKE"
}
diff --git a/app/features/tournament-lfg/tournament-lfg-schemas.server.ts b/app/features/tournament-lfg/tournament-lfg-schemas.server.ts
index 0a3dae5b0..6df7eea99 100644
--- a/app/features/tournament-lfg/tournament-lfg-schemas.server.ts
+++ b/app/features/tournament-lfg/tournament-lfg-schemas.server.ts
@@ -7,15 +7,15 @@ export const lookingSchema = z.union([
joinQueueFormSchema,
z.object({
_action: _action("LIKE"),
- targetGroupId: id,
+ targetTeamId: id,
}),
z.object({
_action: _action("UNLIKE"),
- targetGroupId: id,
+ targetTeamId: id,
}),
z.object({
_action: _action("ACCEPT"),
- targetGroupId: id,
+ targetTeamId: id,
}),
z.object({
_action: _action("GIVE_MANAGER"),
diff --git a/app/features/tournament-lfg/tournament-lfg-utils.ts b/app/features/tournament-lfg/tournament-lfg-utils.ts
index a26c92faa..2e568629c 100644
--- a/app/features/tournament-lfg/tournament-lfg-utils.ts
+++ b/app/features/tournament-lfg/tournament-lfg-utils.ts
@@ -1,20 +1,16 @@
import type { LFGGroup } from "./components/LFGGroupCard";
-export function findOwnGroup(groups: LFGGroup[], userId: number) {
- return groups.find((g) => g.members.some((m) => m.id === userId)) ?? null;
-}
-
-export function survivingGroupId({
+export function survivingTeamId({
ourGroup,
theirGroup,
}: {
ourGroup: LFGGroup;
theirGroup: LFGGroup;
}) {
- if (ourGroup.tournamentTeamId && !theirGroup.tournamentTeamId) {
+ if (!ourGroup.isPlaceholder && theirGroup.isPlaceholder) {
return ourGroup.id;
}
- if (!ourGroup.tournamentTeamId && theirGroup.tournamentTeamId) {
+ if (ourGroup.isPlaceholder && !theirGroup.isPlaceholder) {
return theirGroup.id;
}
diff --git a/app/features/tournament/TournamentRepository.server.ts b/app/features/tournament/TournamentRepository.server.ts
index 567efb7ef..be49e36c2 100644
--- a/app/features/tournament/TournamentRepository.server.ts
+++ b/app/features/tournament/TournamentRepository.server.ts
@@ -251,6 +251,7 @@ export async function findById(id: number) {
).as("team"),
])
.where("TournamentTeam.tournamentId", "=", id)
+ .where("TournamentTeam.isPlaceholder", "=", 0)
.orderBy("TournamentTeam.seed", "asc")
.orderBy("TournamentTeam.createdAt", "asc")
.orderBy("TournamentTeam.id", "asc"),
@@ -484,6 +485,7 @@ export function forShowcase() {
),
)
.whereRef("TournamentTeam.tournamentId", "=", "Tournament.id")
+ .where("TournamentTeam.isPlaceholder", "=", 0)
.where((eb) =>
eb.or([
eb("TournamentTeamCheckIn.checkedInAt", "is not", null),
diff --git a/app/features/tournament/routes/to.$id.register.tsx b/app/features/tournament/routes/to.$id.register.tsx
index a844ab6e2..084527a3e 100644
--- a/app/features/tournament/routes/to.$id.register.tsx
+++ b/app/features/tournament/routes/to.$id.register.tsx
@@ -69,6 +69,8 @@ import {
import { useTournament } from "./to.$id";
export { loader, action };
+// xxx: handle placeholder team made
+// xxx: allow promoting members to managers
export default function TournamentRegisterPage() {
const isMounted = useIsMounted();
const tournament = useTournament();
diff --git a/app/utils/team-name-data.ts b/app/utils/team-name-data.ts
new file mode 100644
index 000000000..e339f4112
--- /dev/null
+++ b/app/utils/team-name-data.ts
@@ -0,0 +1,2037 @@
+// Auto-generated team name data from adj.json and sub.json
+// Regenerate with: npx tsx scripts/generate-team-names.ts
+
+export const ADJECTIVES = [
+ "Inkless",
+ "Clocked-Out",
+ "Attention-Seeking",
+ "Southpaw",
+ "Splatsville's",
+ "Big-Money",
+ "Normcore",
+ "Faithful",
+ "Defensive",
+ "Champion",
+ "Royal",
+ "Ammo Knights",
+ "Naut Couture",
+ "Man-o'-Wardrobe",
+ "Crush Station",
+ "Hotlantis",
+ "Tableturf Battling",
+ "Research-Conducting",
+ "Apprentice",
+ "Part-Time",
+ "Go-Gettin'",
+ "Overachieving",
+ "Profreshional",
+ "Legendary",
+ "Mid-Rush",
+ "High-Tide",
+ "Cohock-Chargin'",
+ "Foggy",
+ "Low-Tide",
+ "Fall-Lovin'",
+ "Spring-Lovin'",
+ "Winter-Lovin'",
+ "Summer-Lovin'",
+ "Blissed-Out",
+ "Trendy",
+ "Sharp-Edged",
+ "Stylin'",
+ "Cool",
+ "Basic",
+ "Traditional",
+ "Trendsetting",
+ "All-Natural",
+ "Sizzlin'",
+ "Day-Seizing",
+ "Fall-Themed",
+ "Spring-Fresh",
+ "Captivating",
+ "The",
+ "Low-Rolling",
+ "High-Rolling",
+ "Commanding",
+ "Wannabe",
+ "Deadeye",
+ "Reckless",
+ "Chilled-Out",
+ "Silent",
+ "Ambidextrous",
+ "Stand-Up",
+ "Livestreaming",
+ "Meditative",
+ "Right-Handed",
+ "Low-Key",
+ "Pro",
+ "Amateur",
+ "Inkopolis Square's",
+ "Inkopolis's Own",
+ "Inkopolitan",
+ "Feral",
+ "Downtown",
+ "Country-Born",
+ "Greedy",
+ "Trending",
+ "Shortcut-Taking",
+ "Fluid",
+ "Optimistic",
+ "Daydreamin'",
+ "Clever",
+ "Hungry, Hungry",
+ "Competitive",
+ "Slapdash",
+ "Half-Hearted",
+ "Early-Bird",
+ "Daring",
+ "Hungry",
+ "Athletic",
+ "Early-Morning",
+ "Inkslinging",
+ "Up-and-at-'Em",
+ "Deep-Sea",
+ "Rise-and-Shine",
+ "Beachy",
+ "Freewheelin'",
+ "Old-Fashioned",
+ "World's Best",
+ "National",
+ "Stormy",
+ "Lonely",
+ "Groundbreaking",
+ "Assertive",
+ "Super-Scary",
+ "Vacationing",
+ "Nostalgic",
+ "Homecoming",
+ "Hard-Workin'",
+ "Sweet-Toothed",
+ "Seafaring",
+ "Self-Taught",
+ "Summer-Breaking",
+ "Processed",
+ "Not-So-Fresh",
+ "Salty",
+ "Destined",
+ "Gullible",
+ "Wiped-Out",
+ "Rockin'",
+ "Retro",
+ "Careless",
+ "Avaricious",
+ "Mischievous",
+ "Soft",
+ "Unrivaled",
+ "Swole",
+ "Hip",
+ "Brand-New",
+ "Full-Throttle",
+ "Peppy",
+ "Law-Breaking",
+ "At-Work",
+ "The First",
+ "Promising",
+ "Ink-Sloshing",
+ "Wave-Riding",
+ "Swanky",
+ "Fresh-Catch",
+ "Scientific",
+ "Orderly",
+ "The Lone",
+ "Sappy",
+ "Dodge-Rolling",
+ "Notable",
+ "Explosive",
+ "Shy",
+ "Calamari",
+ "Fishy",
+ "Puffed-Up",
+ "Fuzzy",
+ "The Great",
+ "Birthday-Celebratin'",
+ "Whimsical",
+ "Dangerous",
+ "Legit",
+ "Dandy",
+ "Ultra",
+ "Floaty",
+ "Indoor",
+ "Ink-Covered",
+ "Ink-Dripping",
+ "Fresh",
+ "Wicked-Fresh",
+ "Stale",
+ "A Simple",
+ "8-Tentacled",
+ "10-Tentacled",
+ "Midwinter",
+ "Mind-Blowing",
+ "Lifelong",
+ "Top-Ranked",
+ "Actual",
+ "Growing",
+ "Studious",
+ "Overpowering",
+ "Spineless",
+ "Headstrong",
+ "Leaping",
+ "Running",
+ "Protective",
+ "Attacking",
+ "Watchful",
+ "Cooperative",
+ "Hidden",
+ "Submersible",
+ "Brute",
+ "Stealthy",
+ "Everyday",
+ "Gaming",
+ "Logical",
+ "Reliable",
+ "Gentle",
+ "Confident",
+ "Serious",
+ "Mild-Mannered",
+ "Cheerful",
+ "High-End",
+ "Chatty",
+ "Cash-Stacking",
+ "Absentminded",
+ "Beloved",
+ "Windy",
+ "Watery",
+ "Sneaky",
+ "Fiery",
+ "Incandescent",
+ "Young",
+ "Organized",
+ "Perfect",
+ "Mysterious",
+ "Central",
+ "Fierce",
+ "Ambiguous",
+ "Instantaneous",
+ "Exemplary",
+ "Tear-Stained",
+ "Traveling",
+ "Next Year's",
+ "Twilight",
+ "The One and Only",
+ "Hibernating",
+ "Sharp-Eyed",
+ "Lost",
+ "Procrastinating",
+ "Honorary",
+ "Tasteless",
+ "Merciless",
+ "Infinite",
+ "Sleepy",
+ "Time-Traveling",
+ "Suave",
+ "Howling",
+ "Phantasmagoric",
+ "Celestial",
+ "Aged",
+ "Lethal",
+ "Secret",
+ "Esoteric",
+ "Exhausted",
+ "Better-Than-You",
+ "All-Purpose",
+ "Gleaming",
+ "Midday",
+ "Unnoticed",
+ "Unprecedented",
+ "Tenacious",
+ "Scorching",
+ "Daytime",
+ "Meaty",
+ "Enigmatic",
+ "Unflappable",
+ "Savvy",
+ "Fleeing",
+ "Drained",
+ "Reincarnated",
+ "Sun-Dried",
+ "AKA",
+ "Superhuman",
+ "Subterranean",
+ "Massive",
+ "Terrestrial",
+ "Important",
+ "Jumbo",
+ "Oceanic",
+ "Big",
+ "Easygoing",
+ "Ancient",
+ "Multiplying",
+ "Fully Automatic",
+ "Glimmering",
+ "Seasick",
+ "Unblemished",
+ "Fightin'",
+ "Front-Line",
+ "Endangered",
+ "Persistent",
+ "Sacred",
+ "Local",
+ "Turn-of-the-Century",
+ "This Century's Best",
+ "Aquarium-Bound",
+ "Fan-Favorite",
+ "Rising",
+ "Spicy",
+ "Lightning-Fast",
+ "Awe-Inspiring",
+ "Midnight",
+ "Stark-White",
+ "Deep-Red",
+ "Bright-Blue",
+ "Pitch-Black",
+ "Woodsy",
+ "Next-Generation",
+ "Novice",
+ "Sensational",
+ "Dead-Tired",
+ "Dazzling",
+ "Tagalong",
+ "Little",
+ "Third-Eye-Opened",
+ "Early-Summer",
+ "Alabaster",
+ "Pure",
+ "Grown-Up",
+ "Partying",
+ "Dapper",
+ "Evil",
+ "Jet-Black",
+ "Vindictive",
+ "Self-Proclaimed",
+ "Selfie-Snapping",
+ "Eternity's",
+ "Self-Identified",
+ "Childish",
+ "Job-Lovin'",
+ "Late-Summer",
+ "Cutting-Edge",
+ "Mountain-Dwelling",
+ "Picture-Taking",
+ "Yesterday's",
+ "Graceful",
+ "First-Wave",
+ "All-New",
+ "The Very First",
+ "The Final",
+ "This Year's",
+ "Limited-Edition",
+ "Ever-Present",
+ "Booming",
+ "Crimson",
+ "Steady",
+ "Nosy",
+ "Enlightened",
+ "Aloof",
+ "Hearty",
+ "Artsy",
+ "Flying",
+ "Die-Hard",
+ "Fortunate",
+ "Last Year's",
+ "The True Ultimate",
+ "Impish",
+ "Shining",
+ "Forgetful",
+ "Heartfelt",
+ "Unarmed",
+ "Sweet",
+ "Sentimental",
+ "Sandy",
+ "Overheated",
+ "Catch-of-the-Day",
+ "Pool-Ready",
+ "Anachronistic",
+ "Supersonic",
+ "Lovely",
+ "Movie-Lovin'",
+ "Shadowy",
+ "Fated",
+ "Cosmic",
+ "Zipped-Lips",
+ "Gone-Fishin'",
+ "24-Hour",
+ "Mostly",
+ "Otherworldly",
+ "Junior-Grade",
+ "Wild",
+ "A Real",
+ "Lucky",
+ "Seasoned",
+ "Soft-and-Fluffy",
+ "100%",
+ "Veggie-Lovin'",
+ "Fluffy",
+ "Modern",
+ "Lost-and-Found",
+ "Won't-Lose",
+ "Auspicious",
+ "Innocent",
+ "Miraculous",
+ "Poser",
+ "Comic-Lovin'",
+ "Vulnerable",
+ "Light",
+ "Bomb-tastic",
+ "Fluttering",
+ "Buoyant",
+ "Wibbly-Wobbly",
+ "Fruit-Lovin'",
+ "Dark",
+ "Fragrant",
+ "Fashionable",
+ "Big-Time",
+ "Drenched",
+ "Bearded",
+ "Frosty",
+ "Incredible",
+ "Faraway",
+ "Flashy",
+ "Fame-Seeking",
+ "Bursting",
+ "Designer-Label",
+ "Swoon-Worthy",
+ "Strict",
+ "Slippery",
+ "Painted",
+ "Climactic",
+ "Nameless",
+ "Nice",
+ "Muddy",
+ "Neighborly",
+ "Tidy",
+ "Barbed",
+ "Pointy",
+ "Big Honkin'",
+ "Smooth",
+ "Iced",
+ "Occasional",
+ "Just Another",
+ "Essential",
+ "Diving",
+ "Dynamite",
+ "Overall",
+ "Bargain-Binned",
+ "Slim",
+ "Out-of-Options",
+ "Steampunky",
+ "Numb",
+ "Hiking",
+ "Wandering",
+ "Unyielding",
+ "Economical",
+ "Gothic",
+ "Covert",
+ "High-Performing",
+ "Praiseworthy",
+ "Arcade-Hopping",
+ "Pretty Good",
+ "The GOAT",
+ "Glistening",
+ "Sparkling",
+ "Gigantified",
+ "Camera-Facing",
+ "Clockwork",
+ "Solid",
+ "Fully",
+ "Unperceived",
+ "Carnivorous",
+ "Homebound",
+ "Fish-Lovin'",
+ "Sketching",
+ "Precocious",
+ "Easy-Peasy",
+ "Made-to-Order",
+ "Elegant",
+ "Water-Breathing",
+ "The Eternal",
+ "Instant",
+ "The Usual",
+ "Ready-to-Go",
+ "Lively",
+ "Unstoppable",
+ "Underground",
+ "The Real Ultimate",
+ "Overflowing",
+ "Toon-Lovin'",
+ "Splendid",
+ "Warm-and-Cozy",
+ "Amped-Up",
+ "Outdoorsy",
+ "Tech-Minded",
+ "Lovable",
+ "amiibo-Collecting",
+ "5-Year-Planning",
+ "Decade-Experienced",
+ "Crab-N-Go",
+ "Summer-Fun",
+ "Winter-Ready",
+ "Not-Streaming",
+ "Daylit",
+ "Dawning",
+ "Realistic",
+ "Lightly Snacking",
+ "Late-Night",
+ "Comfy-Cozy",
+ "Fearful",
+ "Winter-Breaking",
+ "So-Called",
+ "Intuitive",
+ "A Complex",
+ "The Original",
+ "Second-Generation",
+ "The Best",
+ "Roaming",
+ "DJ",
+ "Badlands",
+ "Snowy",
+ "Seaside",
+ "Big-Sky",
+ "Dry",
+ "Raw",
+ "Awesome",
+ "SUPERFRESH",
+ "Splatoon",
+ "Avant-Garde",
+ "Punk",
+ "Mod",
+ "Goth",
+ "Rockabilly",
+ "Casual",
+ "Sporty",
+ "Breakin'",
+ "Glam",
+ "Vintage",
+ "Bohemian",
+ "Grunge",
+ "Hip-Hop",
+ "Thunderous",
+ "Turbo",
+ "Wizened",
+ "Today's",
+ "Virtual",
+ "Half",
+ "Microscopic",
+ "Swift",
+ "Everyone's",
+ "Carefree",
+ "New",
+ "High-Earning",
+ "Pool-Frequenting",
+ "Revived",
+ "Preposterous",
+ "Mighty",
+ "Chill",
+ "Gossip-Loving",
+ "Social",
+ "Crafty",
+ "Hopeful",
+ "Sealed",
+ "Theoretically Possible",
+ "Rookie",
+ "Sleeved",
+ "World's First",
+ "Nature-Loving",
+ "Inflexible",
+ "Prized",
+ "Faux",
+ "Reasonable",
+ "Unfettered",
+ "Unthinkable",
+ "Phantom",
+ "Cherished",
+ "Gold",
+ "Silver",
+ "Undefeated",
+ "New Year's",
+ "This Year's Final",
+ "Newly Released",
+ "Tonight's",
+ "Reclusive",
+ "Stewed",
+ "Cyber-Brainiac",
+ "Magical",
+ "Of-the-Moment",
+ "Forevermore",
+ "Anticipated",
+ "Automated",
+ "Drab",
+ "Reflective",
+ "Hoary",
+ "Parallel World's",
+ "Power-Concealing",
+ "Nourishing",
+ "Highly Nutritious",
+ "Ascending",
+ "Sprightly",
+ "Always-in-Our-Hearts",
+ "Sanguine",
+ "Daybreaking",
+ "Repeating",
+ "Endlessly Ringing",
+ "Listless",
+ "In-Season",
+ "Rampaging",
+ "Scintillating",
+ "Abyssal",
+ "Originally from",
+ "Unknowing",
+ "Currently Obsessed with",
+ "Nonstop",
+ "Vivacious",
+ "Aggressive",
+ "Immediate",
+ "Natural",
+ "High-Calorie",
+ "Low-Calorie",
+ "Recently",
+ "Esteemed",
+ "Treasured",
+ "Existing",
+ "Nonexistent",
+ "On-the-Job",
+ "Choosy",
+ "Delicate",
+ "Haphazard",
+ "Golden Egg–Like",
+ "Ice-Cold",
+ "Low-Battery",
+ "Fully Charged",
+ "Noontime",
+ "Break-Time",
+ "Unavailable",
+ "Protecting",
+ "Long-Awaited",
+ "Now-Stronger",
+ "Breath-Holding",
+ "Genuine",
+ "Nimble",
+ "Movable",
+ "At-Home",
+ "Hometown",
+ "Shooter-Using",
+ "Roller-Using",
+ "Charger-Using",
+ "Slosher-Using",
+ "Splatling-Using",
+ "Dualie-Using",
+ "Brella-Using",
+ "Blaster-Using",
+ "Brush-Using",
+ "Stringer-Using",
+ "Splatana-Using",
+ "Replete with",
+ "Watered",
+ "Triumphant",
+ "Tasteful",
+ "Outstanding",
+ "Stylish",
+ "Three-Star",
+ "Scheduled",
+ "Trailblazing",
+ "Satiating",
+ "Newish",
+ "Same-as-Ever",
+ "Multifaceted",
+ "Orthodox",
+ "Regal",
+ "Classic",
+ "Old-School",
+ "Simplistic",
+ "Advanced",
+ "Prototypical",
+ "Sincere",
+ "Flexible",
+ "Creative",
+ "Classy",
+ "Slender",
+ "Genteel",
+ "Shrouded-in-Mystery",
+ "Novel",
+ "Brisk",
+ "Undying",
+ "Powerful",
+ "Unwavering",
+ "Heroic",
+ "Swaggering",
+ "Astute",
+ "Pushy",
+ "Resilient",
+ "Burly",
+ "Resourceful",
+ "Lax",
+ "Daredevilish",
+ "Magnetic",
+ "Dynamic",
+ "Unique",
+ "Keen-and-Nimble",
+ "Cordial",
+ "Dignified",
+ "Flowy",
+ "Cryptic",
+ "High-Spirited",
+ "Mind-Changing",
+ "Chic",
+ "Witty",
+ "Cheery",
+ "Vivid",
+ "Rare",
+ "Closed-Off",
+ "Surreal",
+ "Methodical",
+ "Gorgeous",
+ "Cute",
+ "Awkward",
+ "Deep",
+ "Unadorned",
+ "Cushy",
+ "Gallant",
+ "Adept",
+ "Invigorating",
+ "Veiled",
+ "Discreet",
+ "Frank",
+ "Rational",
+ "Charming",
+ "Quiet",
+ "Wise",
+ "Introverted",
+ "Abstract",
+ "Sharp",
+ "Gutsy",
+ "Authentic",
+ "Learned",
+ "Experienced",
+ "Gifted",
+ "Ingenious",
+ "Healthy",
+ "Honest",
+ "Just-Right",
+ "Blustery",
+ "Funky",
+ "Exciting",
+ "Exuberant",
+ "Well-Mannered",
+ "Dutiful",
+ "Modest",
+ "Prestigious",
+ "Affable",
+ "Sociable",
+ "Good-Humored",
+ "Urbane",
+ "Meek",
+ "Well-Rounded",
+ "Well-Behaved",
+ "Upstanding",
+ "Big-Hearted",
+ "Influential",
+ "Mold-Breaking",
+ "Ghastly",
+ "Academic",
+ "Plucky",
+ "Cheeky",
+ "Glorious",
+ "Maniacal",
+ "Magnificent",
+ "Energetic",
+ "Misplaced",
+ "De Facto",
+ "333x",
+ "Ageless",
+ "Apparent",
+ "Ostensible",
+ "Heart-Thumping",
+ "Starchy",
+ "Barely",
+ "Narrowly",
+ "Amusing",
+ "Excited",
+ "Swishy",
+ "The Genuine",
+ "Unnamed",
+ "Glory-Hogging",
+ "Prickly",
+ "Lackadaisical",
+ "Surviving",
+ "Eccentric",
+ "Idiosyncratic",
+ "Rule-Breaking",
+ "Snazzy",
+ "Zesty",
+ "Most Popular",
+ "On-the-Rise",
+ "Skyrocketing",
+ "World-Wise",
+ "Assisting",
+ "Covering",
+ "Bourgeois",
+ "Clean",
+ "Almighty",
+ "Robust",
+ "Enlivened",
+ "Proactive",
+ "Unruly",
+ "Fully Committed",
+ "Delightful",
+ "Flower-Shaped",
+ "Extraordinary",
+ "Original",
+ "Top-Notch",
+ "Clandestine",
+ "Painful",
+ "Topical",
+ "Lavish",
+ "Thrilling",
+ "Deluxe",
+ "Triple",
+ "Twin",
+ "Double",
+ "Individual",
+ "Comical",
+ "Imperfect",
+ "Uncommon",
+ "Peerless",
+ "Vertebrate",
+ "Expert",
+ "Giant",
+ "Stalwart",
+ "Exquisite",
+ "Acrobatic",
+ "Captivated",
+ "Fledgling",
+ "Relaxed",
+ "Playful",
+ "True",
+ "Candid",
+ "Straightforward",
+ "Unalloyed",
+ "Magnanimous",
+ "Stoic",
+ "Angelic",
+ "Single-Minded",
+ "Bland",
+ "Rich",
+ "Earnest",
+ "Forward-Plunging",
+ "Total",
+ "Passionate",
+ "Inexorable",
+ "Plain",
+ "Thoughtful",
+ "Frizzy",
+ "Somehow",
+ "Upright",
+ "Peaceful",
+ "Ephemeral",
+ "Bright",
+ "Laid-Back",
+ "Dried-Out",
+ "Metallic",
+ "Common",
+ "Nonchalant",
+ "Stubborn",
+ "Custom",
+ "Self-Centered",
+ "Determined",
+ "Indomitable",
+ "Unswerving",
+ "Steadfast",
+ "All-Out",
+ "Imperative",
+ "Undiscouraged",
+ "Enduring",
+ "Persisting",
+ "Completing",
+ "Piercing",
+ "Hyped",
+ "Climbing",
+ "Proper",
+ "Noteworthy",
+ "Relieved",
+ "Brazen",
+ "Unaffected",
+ "Exceptional",
+ "Challenging",
+ "Intellectual",
+ "Fair",
+ "Discerning",
+ "First-Rate",
+ "Strong-Willed",
+ "Formidable",
+ "Direct",
+ "Ambitious",
+ "Sturdy",
+ "Dogged",
+ "Dependable",
+ "Meticulous",
+ "Enterprising",
+ "Courageous",
+ "Resolved",
+ "Zealous",
+ "Insider",
+ "Skilled",
+ "Enthusiastic",
+ "Wonderful",
+ "Heavy-Duty",
+ "Struggling",
+ "Ultimate",
+ "Convenient",
+ "Restless",
+ "Composed",
+ "Comprehensible",
+ "Abandoned",
+ "Last",
+ "Supernatural",
+ "Clamorous",
+ "Dense",
+ "Airy",
+ "My Very Own",
+ "Improved",
+ "Blue",
+ "Ridiculous",
+ "Very",
+ "Really",
+ "Dauntless",
+ "Perpetual",
+ "Recently Graduated",
+ "Retirement-Age",
+ "Commencing",
+ "Genius",
+ "Praying",
+ "Sometime",
+ "Occasionally",
+ "Overdone",
+ "Lunchtime",
+ "Speciality",
+ "Phoenix-Like",
+ "Slacking",
+ "Grateful",
+ "Absolute",
+ "Blatant",
+ "Wistful",
+ "Airborne",
+ "Temporary",
+ "Conspicuous",
+ "Mesmerizing",
+ "Bronzed",
+ "Normal",
+ "Lightly Seasoned",
+ "Pampered",
+ "Simulated",
+ "Splatfest's",
+ "Splatfest Battle's",
+ "Rugged",
+ "Grand Festival",
+ "Monochrome",
+ "Long-Sleeved",
+ "Collab-Loving",
+ "Swaggy",
+ "Refreshing",
+ "Simmering",
+ "Positive",
+ "Dim",
+ "Stable",
+ "Merry",
+ "Ebullient",
+ "Tailwind-Riding",
+ "Peak-Condition",
+ "Thank You for",
+ "Super-Duper Lucky",
+ "Super-Rare",
+ "One-in-a-Million",
+ "Ace",
+ "Astonishing",
+ "Adored",
+ "Charmed",
+ "Shell-Out",
+ "Splatlandian",
+ "Indian Ocean",
+ "North Atlantic",
+ "North Pacific",
+ "Antarctic Ocean",
+ "Arctic Ocean",
+ "South Atlantic",
+ "South Pacific",
+ "The Shoal's Own",
+ "Official-Grizzco",
+ "The Crater's",
+ "The City's Own",
+ "Lobby-Bound",
+ "Third-Gen",
+ "Chaotic",
+ "Midsummer",
+ "Alternan",
+ "Reminiscent",
+ "Memversal",
+ "Side Orderly",
+ "Swarming",
+ "Marching",
+ "Battering",
+ "Jelletonal",
+ "Drizzling",
+ "Panicking",
+ "Whirling",
+ "Springing",
+ "Gushing",
+ "Spawning",
+ "Homing",
+ "Towering",
+ "Layered-Rotator",
+ "Elusive-Bounder",
+ "Intensifying-Harmony",
+ "Fun",
+ "The Strongest",
+ "Crusty",
+ "Saddle-Straddlin'",
+ "Streamlined",
+ "Wandercrust",
+ "Tender",
+ "Moving",
+];
+
+export const SUBTITLES_PLURALIZED = [
+ "Sploosh-o-matic Users",
+ "Splattershot Jr. Users",
+ "Splash-o-matic Users",
+ "Aerospray Users",
+ "Splattershot Users",
+ ".52 Gal Users",
+ "N-ZAP Users",
+ "Splattershot Pro Users",
+ ".96 Gal Users",
+ "Jet Squelcher Users",
+ "Luna Blaster Users",
+ "Blaster Users",
+ "Range Blaster Users",
+ "Clash Blaster Users",
+ "Rapid Blaster Users",
+ "Rapid Blaster Pro Users",
+ "L-3 Nozzlenose Users",
+ "H-3 Nozzlenose Users",
+ "Squeezer Users",
+ "Carbon Roller Users",
+ "Splat Roller Users",
+ "Dynamo Roller Users",
+ "Flingza Roller Users",
+ "Inkbrush Users",
+ "Octobrush Users",
+ "Squiffer Users",
+ "Splat Charger Users",
+ "E-liter 4K Users",
+ "Bamboozler 14 Users",
+ "Goo Tuber Users",
+ "Slosher Users",
+ "Tri-Slosher Users",
+ "Sloshing Machine Users",
+ "Bloblobber Users",
+ "Explosher Users",
+ "Mini Splatling Users",
+ "Heavy Splatling Users",
+ "Hydra Splatling Users",
+ "Ballpoint Splatling Users",
+ "Nautilus Users",
+ "Dapple Dualies Users",
+ "Splat Dualies Users",
+ "Glooga Dualies Users",
+ "Dualie Squelchers Users",
+ "Tetra Dualies Users",
+ "Splat Brella Users",
+ "Tenta Brella Users",
+ "Undercover Brella Users",
+ "Tri-Stringer Users",
+ "Splatana Stamper Users",
+ "REEF-LUX 450 Users",
+ "Splatana Wiper Users",
+ "Splatterscope Users",
+ "E-liter 4K Scope Users",
+ "Splattershot Nova Users",
+ "Big Swig Roller Users",
+ "Snipewriter 5H Users",
+ "S-BLAST '92 Users",
+ "Painbrush Users",
+ "Dread Wringer Users",
+ "Heavy Edit Splatling Users",
+ "Douser Dualies FF Users",
+ "Recycled Brella 24 Mk I Users",
+ "Wellstring V Users",
+ "Decavitator Users",
+ "Splat Bomb Users",
+ "Suction Bomb Users",
+ "Burst Bomb Users",
+ "Curling Bomb Users",
+ "Autobomb Users",
+ "Ink Mine Users",
+ "Sprinkler Users",
+ "Point Sensor Users",
+ "Toxic Mist Users",
+ "Splash Wall Users",
+ "Squid Beakon Users",
+ "Fizzy Bomb Users",
+ "Torpedo Users",
+ "Angle Shooter Users",
+ "Ink Storm Users",
+ "Trizooka Users",
+ "Ultra Stamp Users",
+ "Big Bubbler Users",
+ "Inkjet Users",
+ "Wave Breaker Users",
+ "Zipcaster Users",
+ "Reefslider Users",
+ "Crab Tank Users",
+ "Booyah Bomb Users",
+ "Ink Vac Users",
+ "Tenta Missiles Users",
+ "Killer Wail 5.1 Users",
+ "Triple Inkstrike Users",
+ "Tacticooler Users",
+ "Super Chump Users",
+ "Kraken Royale Users",
+ "Triple Splashdown Users",
+ "Splattercolor Screen Users",
+ "SquidForce Models",
+ "Zink Models",
+ "Krak-On Models",
+ "Rockenberg Models",
+ "Zekko Models",
+ "Forge Models",
+ "Firefin Models",
+ "Skalop Models",
+ "Splash Mob Models",
+ "Inkline Models",
+ "Tentatek Models",
+ "Takoroka Models",
+ "Annaki Models",
+ "Enperry Models",
+ "Toni Kensa Models",
+ "Z+F Models",
+ "Barazushi Models",
+ "Emberz Models",
+ "Cuttlegear Models",
+ "Squid Squad Fans",
+ "Chirpy Chips Fans",
+ "Hightide Era Fans",
+ "Wet Floor Fans",
+ "Bottom Feeders Fans",
+ "Ink Theory Fans",
+ "SashiMori Fans",
+ "Diss-Pair Fans",
+ "ω-3 Fans",
+ "Turquoise October Fans",
+ "Dedf1sh Fans",
+ "C-Side Superfans",
+ "Front Roe Fans",
+ "Damp Socks Fans",
+ "Yoko & the Gold Bazookas Fans",
+ "H2Whoa Fans",
+ "Riot Act Fans",
+ "Now or Never Seven Fans",
+ "Hippies",
+ "Barbers",
+ "Double-Buns",
+ "Wave Riders",
+ "Pigtail Pros",
+ "Headbangers",
+ "Topknot Knights",
+ "Sharp Dressers",
+ "Buzz-Cuts",
+ "Ponytails",
+ "Spiked Hairs",
+ "Bowl Cuts",
+ "Braid Bearers",
+ "Classics",
+ "Wet Hairs",
+ "Cornrowses",
+ "Curly Hairs",
+ "Punks",
+ "Afros",
+ "Big Curlses",
+ "Fades",
+ "Wanderers",
+ "Greasers",
+ "Hipster s",
+ "Callie Fans",
+ "Naut Couture Regulars",
+ "Marina Fans",
+ "Frye Fans",
+ "Crush Station Regulars",
+ "Hotlantis Regulars",
+ "Squid Sisters Fans",
+ "Deep Cut Fans",
+ "Off the Hook Fans",
+ "Pearl Fans",
+ "Shiver Fans",
+ "Ammo Knights Regulars",
+ "Man-o'-Wardrobe Regulars",
+ "Marie Fans",
+ "Big Man Fans",
+ "Clam Blitz Enthusiasts",
+ "Splat Zones Enthusiasts",
+ "Rainmaker Enthusiasts",
+ "Tower Control Enthusiasts",
+ "Salmon Runners",
+ "Turf Warriors",
+ "Story Mode Heros",
+ "Clam Blitz Legends",
+ "Splat Zones Legends",
+ "Tower Control Legends",
+ "Turf War Legends",
+ "Rainmaker Legends",
+ "Salmon Run Legends",
+ "Story Mode Legends",
+ "Inklings",
+ "Octolings",
+ "Tee Models",
+ "Jacket Models",
+ "Shirt Models",
+ "Sneaker Models",
+ "Knit Models",
+ "Trendsetters",
+ "Hat Models",
+ "Glasses Models",
+ "Boot Models",
+ "Card Collectors",
+ "Item Collectors",
+ "Fashionistas",
+ "Locker Rock Stars",
+ "Squid Surgers",
+ "Squid Rollers",
+ "All-Arounders",
+ "Jumpers",
+ "Dodge Rollers",
+ "Slow Starters",
+ "Splat Bombers",
+ "Guardians",
+ "Raiders",
+ "Manta Rays",
+ "Octarians",
+ "Bears",
+ "Jellyfishes",
+ "Humans",
+ "Sea Creatures",
+ "Mammals",
+ "Artists",
+ "Pop Stars",
+ "Clock Punchers",
+ "Eel Tamers",
+ "Shark Tamers",
+ "Clerks",
+ "Office Drones",
+ "Ringleaders",
+ "Champs",
+ "Bass Droppers",
+ "Princes",
+ "Brothers",
+ "City Boys",
+ "Grooms",
+ "Kings",
+ "Bros",
+ "Oden Bowls",
+ "Party Lovers",
+ "Surfers",
+ "Festivalgoers",
+ "Flowers",
+ "Heat Haters",
+ "Snowpersons",
+ "Firecrackers",
+ "Eyes of the Storm",
+ "Fish",
+ "Deity of Destructions",
+ "Dogs",
+ "Dragons",
+ "Cats",
+ "Dinosaurs",
+ "Cephalopods",
+ "Bivalves",
+ "Dolphins",
+ "Bookworms",
+ "CEOs",
+ "Assistants",
+ "Athletes",
+ "Agents",
+ "Risk-Takers",
+ "Coaches",
+ "Coordinators",
+ "Comedians",
+ "Scientists",
+ "Samurais",
+ "Spies",
+ "Dancers",
+ "Designers",
+ "Ninjas",
+ "Part-Timers",
+ "Pilots",
+ "Hunters",
+ "Planners",
+ "Programmers",
+ "Producers",
+ "Painters",
+ "Managers",
+ "Models",
+ "Writers",
+ "Executives",
+ "Astronauts",
+ "Singers",
+ "Presidents",
+ "Vikings",
+ "Students",
+ "Directors",
+ "Leads",
+ "Anglers",
+ "Assistant Managers",
+ "Security Guards",
+ "Researchers",
+ "Authors",
+ "Workers",
+ "Butlers",
+ "VPs",
+ "Board Members",
+ "Gofers",
+ "Artisans",
+ "Teachers",
+ "Village Leaders",
+ "Head Honchos",
+ "Detectives",
+ "Mountaineers",
+ "Professors",
+ "Secretaries",
+ "Beauticians",
+ "Middle Managers",
+ "Vice Presidents",
+ "Adventurers",
+ "Comic Artists",
+ "Heros",
+ "Chefs",
+ "Layabouts",
+ "Big Mouths",
+ "Chow Hounds",
+ "Dynamos",
+ "Hotheads",
+ "Octoling Soldiers",
+ "Power Eggs",
+ "Villains",
+ "Fams",
+ "Turtles",
+ "Forms",
+ "Knights",
+ "Eggs",
+ "Entertainers",
+ "Comrades",
+ "Superheros",
+ "Jokesters",
+ "Magicians",
+ "Brainiacs",
+ "Fish Balls",
+ "Materials",
+ "Sushis",
+ "Callers",
+ "Seafood",
+ "Mincemeats",
+ "Squid Jerkies",
+ "Booyahs",
+ "Vitamins",
+ "Weapons",
+ "Dried Fish",
+ "Muscles",
+ "Illusions",
+ "Shields",
+ "Leaders",
+ "Viewers",
+ "Systems",
+ "Tournament Competitors",
+ "Agent 1",
+ "Agent 2",
+ "Agent 4",
+ "Agent 8",
+ "AIs",
+ "Object of Affections",
+ "Such and Suches",
+ "Outlaws",
+ "Heels",
+ "Tabletop Gamers",
+ "Androids",
+ "Legends",
+ "Squid Inks",
+ "Squid Lives",
+ "Squid Rings",
+ "Squid, Inc.s",
+ "Inks",
+ "Influencers",
+ "Moray Eels",
+ "Elegances",
+ "Big Deals",
+ "Big Bosses",
+ "Otakus",
+ "Dance Masters",
+ "Freebies",
+ "Kids",
+ "Pack Leaders",
+ "Photographers",
+ "Fried Chickens",
+ "Extroverts",
+ "Carpaccios",
+ "Synth Players",
+ "Guitarists",
+ "Campers",
+ "Cupids",
+ "Crabs",
+ "Creators",
+ "Grooves",
+ "Gamers",
+ "Ultimate Weapons",
+ "Survivalists",
+ "Iconoclasts",
+ "Supporters",
+ "Sharks",
+ "Salmon Lives",
+ "Swimmers",
+ "Styles",
+ "Strikers",
+ "Streamers",
+ "Gelatins",
+ "Celebs",
+ "Taste Testers",
+ "Title Holders",
+ "Octopus Inks",
+ "Octo Lives",
+ "Tuna Tatakis",
+ "Challengers",
+ "Butterfingerses",
+ "Drivers",
+ "Drummers",
+ "Tornados",
+ "Trainers",
+ "Party Animals",
+ "Polyglots",
+ "Rebels",
+ "Workhorses",
+ "Introverts",
+ "Clubgoers",
+ "Stand-Up Comics",
+ "Fighters",
+ "Mirages",
+ "Planktons",
+ "Brand Loyalists",
+ "Freelancers",
+ "Snowstorms",
+ "Friends",
+ "Headliners",
+ "Seniors",
+ "Vocalists",
+ "Board Gamers",
+ "Bosses",
+ "Bodyguards",
+ "Mermaids",
+ "Masks",
+ "Superstars",
+ "Maniacs",
+ "Mysteries",
+ "Bifocal Wearers",
+ "Therapists",
+ "Members",
+ "Lifestyles",
+ "Rappers",
+ "Remote Workers",
+ "Rookies",
+ "Robots",
+ "Enthusiasts",
+ "Ordinary People",
+ "Lone Wolves",
+ "Paupers",
+ "Prodigies",
+ "Monarches",
+ "Hot-Spring Enjoyers",
+ "Fish Finders",
+ "Saltwater Fish",
+ "Umibozus",
+ "Cold Chills",
+ "Sightseers",
+ "Prime Cuts",
+ "Moods",
+ "Memories",
+ "Demons",
+ "Giants",
+ "Fish Prints",
+ "Mirrors",
+ "Avocado Rolls",
+ "Moons",
+ "Fists",
+ "Cavemans",
+ "Experts",
+ "Novices",
+ "Foremans",
+ "Ruffians",
+ "Comic Reliefs",
+ "Advisers",
+ "Sashimis",
+ "Legendary Warriors",
+ "Underlings",
+ "Senseis",
+ "Free Spirits",
+ "Sidekicks",
+ "Fledglings",
+ "Employees",
+ "Greases",
+ "Beginners",
+ "Imps",
+ "Higher-Ups",
+ "Trainees",
+ "Rookie of the Years",
+ "Freshmans",
+ "Truth Tellers",
+ "Kingpins",
+ "Lives of the Party",
+ "Wallflowers",
+ "Lives Itself",
+ "Humanities",
+ "Supervisors",
+ "Fillets",
+ "Inkantations",
+ "Penny-Pinchers",
+ "Mountaintop Gurus",
+ "Mentors",
+ "Warriors",
+ "Berserkers",
+ "Blade of Grasses",
+ "Entities",
+ "Suns",
+ "Representatives",
+ "Cabinet Members",
+ "Freshwater Fish",
+ "Group Members",
+ "Style Icons",
+ "Bugs",
+ "Contenders",
+ "Birds",
+ "Stans",
+ "Fisherfolks",
+ "Apprentices",
+ "Targets",
+ "Maestros",
+ "Angels",
+ "Island Dwellers",
+ "Tofus",
+ "Foreheads",
+ "Plumbers",
+ "Content Creators",
+ "Merpersons",
+ "Sidesteppers",
+ "Flotsams",
+ "Subordinates",
+ "Walls",
+ "Predators",
+ "Equations",
+ "Archdemons",
+ "Pros",
+ "Lives",
+ "Fuzzballs",
+ "Actors",
+ "Ghosts",
+ "Atlanteans",
+ "Teardrops",
+ "Ronins",
+ "Rice Bowls",
+ "Outfits",
+ "Palettes",
+ "Snackses",
+ "Followers",
+ "Marinades",
+ "Switch-Hitters",
+ "Stir-Fries",
+ "Catfish",
+ "Grand Masters",
+ "Cubs",
+ "DJs",
+ "Paradigms",
+ "Geeks",
+ "Heads",
+ "Fits",
+ "B-Boys",
+ "Skaters",
+ "Dukes",
+ "Celebrities",
+ "Mariners",
+ "Fashion Designers",
+ "Commanders",
+ "Store Managers",
+ "Patrons",
+ "Sparklers",
+ "Fantasy Lovers",
+ "Horrorboroses",
+ "Cohozunas",
+ "Floats",
+ "Nuances",
+ "Aliens",
+ "Newcomers",
+ "Hopes",
+ "MCs",
+ "Special Attacks",
+ "Special Spaces",
+ "Rankers",
+ "Aces in the Hole",
+ "Decks",
+ "Hole Cardses",
+ "Test Takers",
+ "Personnels",
+ "Philosophers",
+ "One-Hit Wonders",
+ "Organizations",
+ "Sleeve Collectors",
+ "Rock Climbers",
+ "Bungee Jumpers",
+ "Soliloquys",
+ "Sea-Cucumber Phones",
+ "Ripples",
+ "Salts",
+ "Peppers",
+ "Mayonnaises",
+ "Seasonings",
+ "Values",
+ "Time Killers",
+ "Clumps",
+ "Repeat Customers",
+ "Mushrooms",
+ "Stars",
+ "Talented People",
+ "Superpersons",
+ "Seaweeds",
+ "Kelps",
+ "Marine Snows",
+ "Bento Boxes",
+ "Fish Eggses",
+ "Stews",
+ "Simmered Foods",
+ "Prix Fixes",
+ "Curries",
+ "Aficionados",
+ "Informations",
+ "Heart Valves",
+ "Hearts",
+ "Rocks",
+ "Jazzes",
+ "Solos",
+ "Soup Stocks",
+ "Soups",
+ "Salads",
+ "Champions",
+ "Rice Balls",
+ "Paradoxes",
+ "Dressings",
+ "Sauces",
+ "Spices",
+ "Specialists",
+ "Specialty Shops",
+ "Bandleaders",
+ "Runners",
+ "Speedruns",
+ "Jukeboxes",
+ "Demigods",
+ "Laughs",
+ "Theme Parks",
+ "Hairstyles",
+ "Encores",
+ "Settlers",
+ "Storms",
+ "Breezes",
+ "Observers",
+ "Right Arms",
+ "Sea Urchins",
+ "Pianos",
+ "Guitars",
+ "Keyboards",
+ "Drums",
+ "Castanets",
+ "Recorders",
+ "Cymbalses",
+ "Musical Instruments",
+ "Prime Ministers",
+ "Project Managers",
+ "Physicians",
+ "Lawyers",
+ "Accountants",
+ "Bodybuilders",
+ "Lads",
+ "Bases",
+ "Sambas",
+ "Machos",
+ "Cool Guys",
+ "Thespians",
+ "Hard Workers",
+ "Ship Captains",
+ "Chaperones",
+ "Time Travelers",
+ "Puddings",
+ "Counterfeits",
+ "Alter Egos",
+ "Grillers",
+ "Smallfries",
+ "Cohocks",
+ "Snatchers",
+ "Steelheads",
+ "Steel Eels",
+ "Scrappers",
+ "Stingers",
+ "Mawses",
+ "Drizzlers",
+ "Flyfishes",
+ "Fish Sticks",
+ "Flipper-Floppers",
+ "Slammin' Lids",
+ "Big Shots",
+ "Goldies",
+ "Motherships",
+ "Mudmouths",
+ "Glowflies",
+ "Ranks",
+ "Romanticists",
+ "Steel Bodies",
+ "Flavors",
+ "Elites",
+ "Determined Spirits",
+ "Person of Influences",
+ "Trailblazers",
+ "Casts",
+ "Aces",
+ "Number Ones",
+ "Foremost Figures",
+ "Mainstays",
+ "Emperors",
+ "Paragons",
+ "Intermediaries",
+ "Prototypes",
+ "Schemers",
+ "Brave Warriors",
+ "Person of Cultures",
+ "Honor Students",
+ "Brains",
+ "Trappers",
+ "Intellectuals",
+ "Impractical Theories",
+ "Dislikes",
+ "Meisters",
+ "Governors",
+ "Office Chiefs",
+ "Caretakers",
+ "Bank Presidents",
+ "Harlequins",
+ "Focal Points",
+ "Event Organizers",
+ "VIPs",
+ "Performers",
+ "Woodcutters",
+ "Sous-Chefs",
+ "Engineers",
+ "Contractors",
+ "Prosperous Merchants",
+ "Referees",
+ "Manila Clams",
+ "Concierges",
+ "Tentacles",
+ "Nobles",
+ "Temps",
+ "Pranksters",
+ "Great Regents",
+ "Main Characters",
+ "Leading Actors",
+ "Dark Horses",
+ "Key Figures",
+ "Masterminds",
+ "Tacticians",
+ "Vagabonds",
+ "Voyagers",
+ "Wandering Hermits",
+ "Nomads",
+ "Bounty Hunters",
+ "Timeline Jumpers",
+ "Generations",
+ "Elders",
+ "Captains",
+ "Guests",
+ "String Pullers",
+ "Galleries",
+ "Big Names",
+ "Regular Customers",
+ "Partners",
+ "Cyborgs",
+ "Successes",
+ "Shadow Commanders",
+ "Helpers",
+ "Backers",
+ "Millionaires",
+ "Custodians",
+ "Lieutenants",
+ "Origins",
+ "Czars",
+ "Cheer Teams",
+ "Staffs",
+ "Factotums",
+ "Energies",
+ "Engines",
+ "Consultants",
+ "Tutors",
+ "Puppet Masters",
+ "Buddies",
+ "Critics",
+ "Luck Pushers",
+ "Teams",
+ "Buffoons",
+ "Players",
+ "TV Personalities",
+ "Announcers",
+ "Geniuses",
+ "Golfers",
+ "Batters",
+ "Pitchers",
+ "Catchers",
+ "Benchwarmers",
+ "Virtuosos",
+ "Conductors",
+ "Musical Performers",
+ "Lyricists",
+ "Pianists",
+ "Ingenues",
+ "Narrators",
+ "Pro Wrestlers",
+ "Sprinters",
+ "Boxers",
+ "Extras",
+ "Cinematographers",
+ "Stunt Performers",
+ "Descendants",
+ "Offsprings",
+ "Sponsors",
+ "Suggestions",
+ "Facilitators",
+ "Showstoppers",
+ "Possessors",
+ "Housekeepers",
+ "Traders",
+ "Fortune Tellers",
+ "Globe-Trotters",
+ "Pinch Hitters",
+ "Leadoff Hitters",
+ "Musketeers",
+ "Crowds",
+ "Circles",
+ "Flocks",
+ "Cooperators",
+ "Units",
+ "Armies",
+ "Coteries",
+ "Quartets",
+ "Choruses",
+ "Podium Finishers",
+ "Guilds",
+ "Groups",
+ "Horses",
+ "Rabbits",
+ "Pandas",
+ "Koalas",
+ "Squirrels",
+ "Sea Otters",
+ "Elephants",
+ "Giraffes",
+ "Whales",
+ "Tigers",
+ "Lions",
+ "Hamsters",
+ "Deer",
+ "Bookkeepers",
+ "Idea People",
+ "Rock 'n' Rollers",
+ "Controllers",
+ "Storytellers",
+ "Illustrators",
+ "Finger Painters",
+ "Scriveners",
+ "Founders",
+ "Families",
+ "Craftsmans",
+ "Machines",
+ "Navigators",
+ "Inventors",
+ "Parties",
+ "Playmakers",
+ "Young Leaders",
+ "Mascots",
+ "Fairies",
+ "Joke Tellers",
+ "Voice Actors",
+ "Learners",
+ "Tenderfoots",
+ "Heads of the Class",
+ "University Students",
+ "Architects",
+ "Costumiers",
+ "Ferocious Fighters",
+ "Knowledgeable People",
+ "Lofty Dreams",
+ "Wizes",
+ "Prominent Figures",
+ "Grapplers",
+ "Natural Enemies",
+ "Horseshoe Crabs",
+ "Pond Snails",
+ "Nautiluses",
+ "Coconut Crabs",
+ "Sea Anemones",
+ "Goldfish",
+ "Horse Mackerels",
+ "Devil Rays",
+ "Sea Slugs",
+ "Shrimp",
+ "Spider Crabs",
+ "Rivals",
+ "Weaknesses",
+ "Types",
+ "Sweethearts",
+ "Imitations",
+ "Processes",
+ "Gatherings",
+ "Shoals",
+ "Crews",
+ "Beasts",
+ "Rulers",
+ "Fossils",
+ "Wardens",
+ "Collectors",
+ "Crushes",
+ "Souls",
+ "State of Minds",
+ "Marathons",
+ "Good People",
+ "Clients",
+ "Dolls",
+ "Noble Scions",
+ "Protagonists",
+ "Symbols",
+ "Authorities",
+ "Contributors",
+ "Cartoon Characters",
+ "Plushies",
+ "Spirits",
+ "Thoroughbreds",
+ "Person of Notes",
+ "Professionals",
+ "Gourmets",
+ "Sumo Wrestlers",
+ "Family Trees",
+ "Soldiers",
+ "Order of Knightses",
+ "Victors",
+ "Villagers",
+ "School Principals",
+ "Protectors",
+ "Explorers",
+ "Seekers",
+ "Pugilists",
+ "Paladins",
+ "Door Guards",
+ "Rangers",
+ "Monks",
+ "Sages",
+ "Doctors",
+ "Blacksmiths",
+ "Postdocs",
+ "Dignitaries",
+ "Artistes",
+ "Interns",
+ "Alchemists",
+ "Security Details",
+ "Ores",
+ "Guides",
+ "Chauffeurs",
+ "Bloggers",
+ "Operators",
+ "Librarians",
+ "Objet d'Arts",
+ "Successors",
+ "Owners",
+ "Proxies",
+ "Pupils",
+ "Disciples",
+ "Scholars",
+ "Graduates",
+ "Collegians",
+ "PhD Candidates",
+ "Ketchups",
+ "Bamboo Shoots",
+ "Pops",
+ "Psychics",
+ "Arbiters",
+ "Litterateurs",
+ "Megalodontias",
+ "Recollections",
+ "Stylists",
+ "Entrepreneurs",
+ "Musicians",
+ "Party Stars",
+ "Mercenaries",
+ "Alliances",
+ "Bards",
+ "Otherworldly Beings",
+ "Chaoses",
+ "Noobs",
+ "Vanguards",
+ "Luminaries",
+ "Playings",
+ "Lucky Ducks",
+ "Protected Specieses",
+ "Chosen Ones",
+ "Gadabouts",
+ "Point Guards",
+ "Optimists",
+ "Daredevils",
+ "Prizewinners",
+ "Youths",
+ "Squid",
+ "Octopuses",
+ "Salmon",
+ "Boys",
+ "Agent 3",
+ "Fathers",
+ "Dads",
+ "Sons",
+ "Big Brothers",
+ "Little Brothers",
+ "Grandpas",
+ "Grandchilds",
+ "Uncles",
+ "Youngsters",
+ "Pets",
+ "World Champs",
+ "Mediators",
+ "Beta Testers",
+ "Revelers",
+ "Languendos",
+ "Andantes",
+ "Lentos",
+ "Portals",
+ "Capricciosos",
+ "Alla Mambos",
+ "Accelerandos",
+ "Spiccatos",
+ "Trionfales",
+ "Accordos",
+ "Arpeggios",
+ "Nobilmentes",
+ "Asynchronous Rondos",
+ "Pinging Marciales",
+ "Parallel Canons",
+ "Tableturf Battlers",
+ "Card Gamers",
+ "Travelers",
+ "Cyclists",
+ "Tempuras",
+ "Boosters",
+ "Resiliences",
+ "Finales",
+];
diff --git a/app/utils/team-name.ts b/app/utils/team-name.ts
new file mode 100644
index 000000000..6428dfd61
--- /dev/null
+++ b/app/utils/team-name.ts
@@ -0,0 +1,12 @@
+import { ADJECTIVES, SUBTITLES_PLURALIZED } from "./team-name-data";
+
+/** Generates a random team name by combining a Splatoon 3 title adjective with a pluralized subtitle e.g. "Prestigious Heat Haters" */
+export function randomTeamName() {
+ const adjective = ADJECTIVES[Math.floor(Math.random() * ADJECTIVES.length)];
+ const subtitle =
+ SUBTITLES_PLURALIZED[
+ Math.floor(Math.random() * SUBTITLES_PLURALIZED.length)
+ ];
+
+ return `${adjective} ${subtitle}`;
+}
diff --git a/db-test.sqlite3 b/db-test.sqlite3
index 87474164b4c63d53094d7e9eed90d3a07b370725..24c6c020967d9522c64e50d29408a9885915efd5 100644
GIT binary patch
delta 5834
zcmeHLeN0=|6@S-$&kuib-vf+oY~u$6i~|op``LhL$H<~2Z5L&vO;$npr~?jtU@*bX
zN4rfSbcL!_^D(!*(<*HZXtZmRDk@o?BCXBGq(zmMG})HGJ{y*)Q>9H?J57^Vn(ktf
z6kp>`llCXb*OGtSbI$Lab3e|xqoWUvj&3~S;4e}Xuu=E_z_FvXG6w5jTwpNB!D;*r
zj7Nusnd4d?m^laL&8gQu)1{{223
Eqo5lHx1E8G6=kr72mK8YM9qjSPx|k)cRmbfm675s(7)qLeW=^*6qZ(B~@f5Xjan
z9bY(Z^PhUS{D(o{&68iHaX8tqkv`ozS55{u=a;VvXy-%?mZ&=@Z)_h*HqmoSAkPS2t!*@rG&2FH52N
zrObRGn*+tgfehh_2AZnG!Z^MAltdzj6PJpGV1`&_h^D+)EU;uPnoH1ObrA7iZzY%I
zK5I2G=Y+MCKxVCEMrfOtnA2pdAdr5WlR#27a+_SRksI-^HW%TnH|=at`Y+Bl+sRb#
zx33`x{K!ta_A5K-8Yn6CWkg%3peeU3$w6^%PmT<2DX|~Oka3sMlncuQ#N8{4R0-)7
zEc?aCNK#Bj`f}v3%uQ2eSdp|iZ)H`rr8Lzbr#>tplb!D%gWKdFx1ZvuBx3ZcgG|zU
z4l<4lSyHu58=+^b(@7jOmh?drki@<5-W=|xzV9R%UUrh!?>MUnTYi^^*jQY8WBEhr
z(>s^3=Ut?+v#wI&q%o)Sggr^d@h2Y3rOQ<6ZOd2!RmLSrN!+FE$M?r@4#{#uIxJE+
z=bS(|b*MChDMsEYwJ&EH2v@Obds4mF6NyEKdLt`dR#?*LE@@cCG!U+4Xv!x`QczTq
zPm4Pvy?ra6C|I_`opBFfWZW%m%h0w)LsK5C&Hsg(DnWWzON+Z&Nh*=7EA5#D_r!u5
z;GsbaZmmIUqW(R>3R;yF=sV|pn$wi#G#t-wHeWZ@7+*1hyq>%&c8XcU?qa>V8C{*$
zMN!WV53p9R7rfv=E!P;A`tw`#MPL-lmBi+Fd;s63uUr6bqmpQiMS7x7#$&yx>;iD*
zNwq?GY**jc(Xk(LRy25nv7z&q%{KHML93`Q-K2S~TK6G+7+Pnq-i
z8>vxLbd~9c-}dfit)UQ@*prOxj75?E4@{BTd7aS%B>bMYpe^&XLI3E6P1`rGd(=~l
zt{lT=3>QEll0Go8=tYkfqAP#YZAaH`FzXHD{9PwH{}(4b_nc4?%1$MwB%dl5lF|Li
zCD*pZlhLK$=7Vz*NK4NdK)?>?x)75&6yrfs_v1O+xZeLdGG2;AKt;bgDvCmJ?
zs?~bz{$oe3daQlfi4q9^pX#y2*Ofr{Oe_4d-tc)k(8F!8Z29kG8_cLwWeH7XL%(Q)
zYcsbw(zipRTS1|ANOY@6X@?RaINc5_mRG{ER25&Wg#CU=93F`e#rF*5sJzjgc1UzT
zNZJ93J{CQ(16C6T2GRy_shxfs!rj}-of(2h6CKaWwwC0|p5XsyiUjgQL!lEI+@O
zJ?s4rNOZ60b_XPGSXAE$8wiU}cV^8x-I+z=qfSV?@gPqZBziB@-UVw3^{FoCsb97T
zWy#Y)Q=%+|{bHhLL>Ux^;`?%h?C)J!o{QbEgs`x!8`crf;ciI0b)ie$B(+JQWwNDZ
zQsVA^-j)F8G5n6=s20jRpkJH!zV5QFpZSDop(nIgw6f-qhBf`nRF)s6T2Qsby{ujh
zaE}1Brh&VjN1HE#4QgqSTL;wn5QfohKleC|-zN8=Vjd>ck|1{poH@lAT2;dl?qATy
z{Dmq2Cn@#-Yt$WP{z(rqKc>sI=gb{uuj!PgUHcsP#BdgzG?eMb@#E*o8NT&ffc?tY
z13K$=ANu&Wd?ot9Yn%-fqnp#*lj?!jxjMiG#~O9kO=akq1KLpE>zobEyve<#e)l-{
z5I~XRT!UV3q4by(MX5JWaDtZ3KSS+Cn6`k9yv40SuLd|Teb74gCG?Y7UO>&WJX^B3
zvb&YuZSg%LLy^JgP;&FeO`Da0D6pYxQ=AihbCx&ZeucNA<~g3VCnF<$(d0HP^l)#v
z_h8Z3gRJ#yKFz@pIy%J_A^RL}MC)%eJbPCS0w3_DOYl7XSCamXK0Rf4jEP~6Bk0I9_aM4C$r(_~
zoGBkY(Wckk#|nzmbM6|$4bffh$X^Vd=zNEsL5F_BL-phvoDTQ06nF$ZU(c;oVLj&r
Qz%T=Z&)k#JFkVOh0gp3@0{{R3
delta 6972
zcmeHLeQZz;ehxkE$ihK4pgV>7;qVK|4)eDGt3%e;_S{;f$85{=9;
z_!loYJisZKV~JOkJf66Kf1p=EqBiD_pJE`Ar7?qYFoW{iqB6e>x%5i}=JN{Gm3$-^>OI!j>V|!8yA-%W{fO$
zi82+g^#oQ3RS5jHNYCS0kbk?_j^HRwB0{M*@d)LsCJv$e(1cnzk-fG!d=0=S?qo~2es)^PCgRil~Ra(%lSZOG_*U&5B5i*Su^d(EmSM6ckhN@wQ2*6h#C^~i(^s|jo=6o%>D6N*H#IB_wTtCWc?^%0mGih0u7g4qOZ
zW*ZX!wPti%?lG4jJTIFI5XyuZ%?M#}BRqafAwt<<$ww%03tA>`TF^rLv&D`$>z16Y
z6#j#=tMkxQ@6B6+81P~q>e@SbsB5^jph6aHu8_bSUcp<*@9xZ!p^etO{W2Mclfdjy
z#>4NNU!;hWcCc6E1A|dM8tBfFL#KnloDfJz@y!oaRzP6Y-uNvmnrwp&4X)pYmS4!W
z6p7JMn>Xe2WZ6j5p@z%MKdRl_MQA9)aK8-!;FxV*LC0atp=y
z<$P58RDKbnOSBiu3QH_xrNR<@(&?Q|*cs>@|CV?4%Kk^irNg43@97v3KOStMPw6c%B{eR$$e6`+>BUXX`aN=f>JD!wz&8w_^^=D)1O
zxYl8mG;D>V|3Di!=(U|4kfMBW6zFKoH9vq#MFgC>^Y1XF|5{K*Oh9&qs7&o
z>LTid;wZJ9a;p-ma_l+f$+)qhH);QnFe>STsvgShcH>X3PoBF*zK?_EMokX5d{ae(
zH!kZd!PR1=8gL~_4LFpb84$lt^2zdB#JeA}jPe%7yaiSLOL@z%j`V}vtK^P2*Kl4u
z$fq$6aGwc^;fBko=lTdXGH#F^}%swAJ(kMX#@jcR%JaLuO
z;2^YBlMAlgR6PPh*T}V_rU?=*6u_*xib
zrUaoDMnD8lwJ?RUe$`XdpXygdQQ!v#`@{Xa!t?jX9x&CyATtUOS{Y>81dp~dC5VC^
zNdat#r57AHW6eK5f!A9Zc~|QJe{5wc5f4h*7-Uig?QI!;em9fr?KTFPF~OZS2DxED
zRXbCSXx!JH!F9Gh!-$XD8RR7f6mMnZ6OsqCY-P$2`1n?)7`~v)c{?j^BQV}8z=yC%
z=U}Lh5BKlM60*N-&G4M>V62FS%^gfRLVCJ`L0+W5#SYZ8F|KZ`u5L_VXTE0yf?dMm
zcQE{pVWa+2?I+r&w7GPb<|XxQbrW@lDpie;9}rf>CQu@<-%jp-fvv^U&2F8*Uc`wf
z*2b14Z}{1I9DKc&jU}C{*e~Kq%EMk)<8%V&?mEs0w2-8qVgJE+;nN~RT)_-G^q=YX
z>27F`Xai7LLes1%Qolm&r#7f=5Lc8V*mssZS*cx#9I9tEgeqc|m
zhBDXGD88{D84ey=w|@O#|E__DgMHhAgR^6ToQ_*S{fC4lS59?tB{T;=;P?c&1U#S6
zlHjqAR5?JmMr{N)&e8<<@oCxxo}bVaaN+)F(8WcAd!uENOiIobifoDo9R0RHkj(lU#cD`9D-u?B$dS9<%Gr*T{*
zuIS9P_F$LGeZUa&Q)W?Aj6RShU77yjIZ}#djq5$56+q}Cl_{MM?j%b9_q(J8u(wH-
zN{at((07wCgR!6MU10Jw{S|ZqegC?SP0`kY+VffyxP6>`jH-++SDDv4!Eqa70WX|j
zEy*&W%;TP%Tvq*MnczsktO~iv7Fd
zTpdd{Bxvrl+OaxetnSfg>j(=?Suv*5a9&rV{gZYL{XO*&^(N{(Wlb0xrc~NtYwQb@
zc~gaAxF!{H4%9Sh)L{HBNrSd=R*iE~w87WLSq-Ra(wG201^=ZPXIaqJq|p$==GaQri4K!RQY1tmPfbGq2lFpzL>1{vWtXUy#
zd+o3@RtKH0x#zs8T-t9PeNz2)?1}^jJ3?KV+aq1yxJ`19NNq$=+M>w=N5)ybQ*tZK
z0X {
db.prepare(
- /*sql*/ `
- create table "TournamentLFGGroup" (
- "id" integer primary key,
- "tournamentId" integer not null,
- "tournamentTeamId" integer,
- "visibility" text,
- "chatCode" text not null,
- "createdAt" integer default (strftime('%s', 'now')) not null,
- foreign key ("tournamentId") references "Tournament"("id") on delete cascade,
- foreign key ("tournamentTeamId") references "TournamentTeam"("id") on delete cascade
- ) strict
- `,
+ /*sql*/ `alter table "TournamentTeam" add column "isLooking" integer not null default 0`,
+ ).run();
+ // xxx: index on isLooking too to get efficient queries? problem -> we query tournamentTeamMember.userId (different table)
+ db.prepare(
+ /*sql*/ `alter table "TournamentTeam" add column "isPlaceholder" integer not null default 0`,
+ ).run();
+ db.prepare(
+ /*sql*/ `alter table "TournamentTeam" add column "lfgVisibility" text`,
+ ).run();
+ db.prepare(
+ /*sql*/ `alter table "TournamentTeam" add column "lfgNote" text`,
+ ).run();
+ db.prepare(
+ /*sql*/ `alter table "TournamentTeam" add column "chatCode" text`,
).run();
db.prepare(
- /*sql*/ `create index tournament_lfg_group_tournament_id on "TournamentLFGGroup"("tournamentId")`,
+ /*sql*/ `alter table "TournamentTeamMember" add column "role" text not null default 'REGULAR'`,
).run();
db.prepare(
- /*sql*/ `create index tournament_lfg_group_tournament_team_id on "TournamentLFGGroup"("tournamentTeamId")`,
+ /*sql*/ `alter table "TournamentTeamMember" add column "isStayAsSub" integer not null default 0`,
).run();
db.prepare(
- /*sql*/ `
- create table "TournamentLFGGroupMember" (
- "groupId" integer not null,
- "tournamentId" integer not null,
- "userId" integer not null,
- "role" text not null,
- "note" text,
- "isStayAsSub" integer default 0 not null,
- "createdAt" integer default (strftime('%s', 'now')) not null,
- foreign key ("groupId") references "TournamentLFGGroup"("id") on delete cascade,
- foreign key ("userId") references "User"("id") on delete cascade,
- unique("tournamentId", "userId") on conflict rollback
- ) strict
- `,
- ).run();
-
- db.prepare(
- /*sql*/ `create index tournament_lfg_group_member_group_id on "TournamentLFGGroupMember"("groupId")`,
- ).run();
- db.prepare(
- /*sql*/ `create index tournament_lfg_group_member_user_id on "TournamentLFGGroupMember"("userId")`,
+ /*sql*/ `update "TournamentTeamMember" set "role" = 'OWNER' where "isOwner" = 1`,
).run();
db.prepare(
/*sql*/ `
create table "TournamentLFGLike" (
- "likerGroupId" integer not null,
- "targetGroupId" integer not null,
+ "likerTeamId" integer not null,
+ "targetTeamId" integer not null,
"createdAt" integer default (strftime('%s', 'now')) not null,
- foreign key ("likerGroupId") references "TournamentLFGGroup"("id") on delete cascade,
- foreign key ("targetGroupId") references "TournamentLFGGroup"("id") on delete cascade,
- unique("likerGroupId", "targetGroupId") on conflict rollback
+ foreign key ("likerTeamId") references "TournamentTeam"("id") on delete cascade,
+ foreign key ("targetTeamId") references "TournamentTeam"("id") on delete cascade,
+ unique("likerTeamId", "targetTeamId") on conflict rollback
) strict
`,
).run();
db.prepare(
- /*sql*/ `create index tournament_lfg_like_liker_group_id on "TournamentLFGLike"("likerGroupId")`,
+ /*sql*/ `create index tournament_lfg_like_liker_team_id on "TournamentLFGLike"("likerTeamId")`,
).run();
db.prepare(
- /*sql*/ `create index tournament_lfg_like_target_group_id on "TournamentLFGLike"("targetGroupId")`,
+ /*sql*/ `create index tournament_lfg_like_target_team_id on "TournamentLFGLike"("targetTeamId")`,
).run();
})();
}
From cd747fed6ae451109493e73d4412cd0498492dc1 Mon Sep 17 00:00:00 2001
From: Kalle <38327916+Sendouc@users.noreply.github.com>
Date: Tue, 24 Feb 2026 21:08:24 +0200
Subject: [PATCH 16/25] Add feminine names
---
app/utils/team-name-data.ts | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/app/utils/team-name-data.ts b/app/utils/team-name-data.ts
index e339f4112..7c80abfa4 100644
--- a/app/utils/team-name-data.ts
+++ b/app/utils/team-name-data.ts
@@ -996,7 +996,7 @@ export const ADJECTIVES = [
"Wandercrust",
"Tender",
"Moving",
-];
+] as const;
export const SUBTITLES_PLURALIZED = [
"Sploosh-o-matic Users",
@@ -1229,11 +1229,17 @@ export const SUBTITLES_PLURALIZED = [
"Champs",
"Bass Droppers",
"Princes",
+ "Princesses",
"Brothers",
+ "Sisters",
"City Boys",
+ "City Girls",
"Grooms",
+ "Brides",
"Kings",
+ "Queens",
"Bros",
+ "Sises",
"Oden Bowls",
"Party Lovers",
"Surfers",
@@ -1555,8 +1561,10 @@ export const SUBTITLES_PLURALIZED = [
"Heads",
"Fits",
"B-Boys",
+ "B-Girls",
"Skaters",
"Dukes",
+ "Duchesses",
"Celebrities",
"Mariners",
"Fashion Designers",
@@ -1659,6 +1667,7 @@ export const SUBTITLES_PLURALIZED = [
"Accountants",
"Bodybuilders",
"Lads",
+ "Lasses",
"Bases",
"Sambas",
"Machos",
@@ -1741,6 +1750,7 @@ export const SUBTITLES_PLURALIZED = [
"Great Regents",
"Main Characters",
"Leading Actors",
+ "Leading Actresses",
"Dark Horses",
"Key Figures",
"Masterminds",
@@ -1996,15 +2006,23 @@ export const SUBTITLES_PLURALIZED = [
"Octopuses",
"Salmon",
"Boys",
+ "Girls",
"Agent 3",
"Fathers",
+ "Mothers",
"Dads",
+ "Moms",
"Sons",
+ "Daughters",
"Big Brothers",
+ "Big Sisters",
"Little Brothers",
+ "Little Sisters",
"Grandpas",
+ "Grandmas",
"Grandchilds",
"Uncles",
+ "Aunts",
"Youngsters",
"Pets",
"World Champs",
@@ -2034,4 +2052,4 @@ export const SUBTITLES_PLURALIZED = [
"Boosters",
"Resiliences",
"Finales",
-];
+] as const;
From 1ec855d62b5e836f4b2ef5a2cfe07bf0427fce18 Mon Sep 17 00:00:00 2001
From: Kalle <38327916+Sendouc@users.noreply.github.com>
Date: Wed, 25 Feb 2026 20:46:08 +0200
Subject: [PATCH 17/25] Progress
---
app/db/seed/index.ts | 29 ++++------
.../TournamentLFGRepository.server.test.ts | 18 ++++++
.../TournamentLFGRepository.server.ts | 24 ++++++--
.../actions/to.$id.looking.server.ts | 4 ++
.../tournament-lfg/routes/to.$id.looking.tsx | 55 ++++++++++++-------
.../TournamentTeamRepository.server.ts | 1 +
locales/da/q.json | 2 +
locales/de/q.json | 2 +
locales/en/q.json | 3 +
locales/es-ES/q.json | 2 +
locales/es-US/q.json | 2 +
locales/fr-CA/q.json | 2 +
locales/fr-EU/q.json | 2 +
locales/he/q.json | 2 +
locales/it/q.json | 2 +
locales/ja/q.json | 2 +
locales/ko/q.json | 2 +
locales/nl/q.json | 2 +
locales/pl/q.json | 2 +
locales/pt-BR/q.json | 2 +
locales/ru/q.json | 2 +
locales/zh/q.json | 2 +
22 files changed, 119 insertions(+), 45 deletions(-)
diff --git a/app/db/seed/index.ts b/app/db/seed/index.ts
index 3e10306a3..beb13fb74 100644
--- a/app/db/seed/index.ts
+++ b/app/db/seed/index.ts
@@ -41,7 +41,6 @@ import { BANNED_MAPS } from "~/features/sendouq-settings/banned-maps";
import * as QSettingsRepository from "~/features/sendouq-settings/QSettingsRepository.server";
import { AMOUNT_OF_MAPS_IN_POOL_PER_MODE } from "~/features/sendouq-settings/q-settings-constants";
import { joinTeam } from "~/features/tournament/queries/joinLeaveTeam.server";
-import { TOURNAMENT } from "~/features/tournament/tournament-constants";
import { clearAllTournamentDataCache } from "~/features/tournament-bracket/core/Tournament.server";
import * as TournamentLFGRepository from "~/features/tournament-lfg/TournamentLFGRepository.server";
import * as TournamentOrganizationRepository from "~/features/tournament-organization/TournamentOrganizationRepository.server";
@@ -75,6 +74,7 @@ import {
} from "~/utils/dates";
import { shortNanoid } from "~/utils/id";
import invariant from "~/utils/invariant";
+import { randomTeamName } from "~/utils/team-name";
import { mySlugify } from "~/utils/urls";
import {
getArtFilename,
@@ -898,9 +898,7 @@ function calendarEvents() {
)
.run({
id,
- name: `${R.capitalize(faker.word.adjective())} ${R.capitalize(
- faker.word.noun(),
- )}`,
+ name: randomTeamName(),
description: faker.lorem.paragraph(),
discordInviteCode: faker.lorem.word(),
bracketUrl: faker.internet.url(),
@@ -1016,7 +1014,7 @@ async function calendarEventResults() {
.fill(null)
.map((_, i) => ({
placement: i + 1,
- teamName: R.capitalize(faker.word.noun()),
+ teamName: randomTeamName(),
players: new Array(
faker.helpers.arrayElement([1, 2, 3, 4, 4, 4, 4, 4, 5, 6]),
)
@@ -1378,12 +1376,7 @@ function calendarEventWithToToolsToSetMapPool() {
}
}
-const validTournamentTeamName = () => {
- while (true) {
- const name = faker.music.songName();
- if (name.length <= TOURNAMENT.TEAM_NAME_MAX_LENGTH) return name;
- }
-};
+const validTournamentTeamName = () => randomTeamName();
const availableStages: StageId[] = [1, 2, 3, 4, 6, 7, 8, 10, 11];
const availablePairs = rankedModesShort
@@ -1492,12 +1485,14 @@ function calendarEventWithToToolsTeams(
"tournamentTeamId",
"userId",
"isOwner",
- "createdAt"
+ "createdAt",
+ "role"
) values (
$tournamentTeamId,
$userId,
$isOwner,
- $createdAt
+ $createdAt,
+ $role
)
`,
)
@@ -1506,6 +1501,7 @@ function calendarEventWithToToolsTeams(
userId,
isOwner: i === 0 ? 1 : 0,
createdAt: dateToDatabaseTimestamp(yesterday),
+ role: i === 0 ? "OWNER" : "REGULAR",
});
}
@@ -1950,12 +1946,7 @@ function otherTeams() {
);
for (let i = 3; i < 50; i++) {
- const teamName =
- i === 3
- ? "Team Olive"
- : `${R.capitalize(faker.word.adjective())} ${R.capitalize(
- faker.word.noun(),
- )}`;
+ const teamName = i === 3 ? "Team Olive" : randomTeamName();
const teamCustomUrl = mySlugify(teamName);
sql
diff --git a/app/features/tournament-lfg/TournamentLFGRepository.server.test.ts b/app/features/tournament-lfg/TournamentLFGRepository.server.test.ts
index 300d42a5b..2383e8e63 100644
--- a/app/features/tournament-lfg/TournamentLFGRepository.server.test.ts
+++ b/app/features/tournament-lfg/TournamentLFGRepository.server.test.ts
@@ -280,6 +280,24 @@ describe("mergeTeams", () => {
).rejects.toThrow("Group has too many members after merge");
});
+ test("stops looking when merged team reaches max capacity", async () => {
+ const tournament = await createTournament();
+ const team1 = await createPlaceholder(tournament.id, 1);
+ const team2 = await createPlaceholder(tournament.id, 2);
+
+ await TournamentLFGRepository.mergeTeams({
+ survivingTeamId: team1.id,
+ otherTeamId: team2.id,
+ maxGroupSize: 2,
+ });
+
+ const groups = await TournamentLFGRepository.findLookingTeamsByTournamentId(
+ tournament.id,
+ );
+
+ expect(groups).toHaveLength(0);
+ });
+
test("clears likes on surviving team after merge", async () => {
const tournament = await createTournament();
const team1 = await createPlaceholder(tournament.id, 1);
diff --git a/app/features/tournament-lfg/TournamentLFGRepository.server.ts b/app/features/tournament-lfg/TournamentLFGRepository.server.ts
index 5b186953f..e86bb2794 100644
--- a/app/features/tournament-lfg/TournamentLFGRepository.server.ts
+++ b/app/features/tournament-lfg/TournamentLFGRepository.server.ts
@@ -164,10 +164,20 @@ export function mergeTeams({
.where("TournamentTeam.id", "=", otherTeamId)
.execute();
+ const memberCount = await getMemberCount(survivingTeamId, trx);
+
invariant(
- await isTeamCorrect(survivingTeamId, trx, maxGroupSize),
+ memberCount <= maxGroupSize,
"Group has too many members after merge",
);
+
+ if (memberCount >= maxGroupSize) {
+ await trx
+ .updateTable("TournamentTeam")
+ .set({ isLooking: 0 })
+ .where("id", "=", survivingTeamId)
+ .execute();
+ }
});
}
@@ -313,6 +323,11 @@ export function leaveLfg({
.set({ isLooking: 0 })
.where("id", "=", userTeam.tournamentTeamId)
.execute();
+ await trx
+ .updateTable("TournamentTeamMember")
+ .set({ isStayAsSub: 0 })
+ .where("tournamentTeamId", "=", userTeam.tournamentTeamId)
+ .execute();
return;
}
@@ -381,16 +396,15 @@ function deleteLikesByTeamId(teamId: number, trx: Transaction) {
.execute();
}
-async function isTeamCorrect(
+async function getMemberCount(
teamId: number,
trx: Transaction,
- maxGroupSize: number,
-): Promise {
+): Promise {
const members = await trx
.selectFrom("TournamentTeamMember")
.select("TournamentTeamMember.userId")
.where("TournamentTeamMember.tournamentTeamId", "=", teamId)
.execute();
- return members.length <= maxGroupSize;
+ return members.length;
}
diff --git a/app/features/tournament-lfg/actions/to.$id.looking.server.ts b/app/features/tournament-lfg/actions/to.$id.looking.server.ts
index 1d19d4751..7db1c227f 100644
--- a/app/features/tournament-lfg/actions/to.$id.looking.server.ts
+++ b/app/features/tournament-lfg/actions/to.$id.looking.server.ts
@@ -51,6 +51,10 @@ export const action = async ({ request, params }: ActionFunctionArgs) => {
const team = tournament.teamMemberOfByUser(user);
if (team) {
+ errorToastIfFalsy(
+ team.members.length < tournament.maxMembersPerTeam,
+ "Team is already at max capacity",
+ );
await TournamentLFGRepository.startLooking(team.id);
} else {
await TournamentLFGRepository.createPlaceholderTeam({
diff --git a/app/features/tournament-lfg/routes/to.$id.looking.tsx b/app/features/tournament-lfg/routes/to.$id.looking.tsx
index f356d1917..a95050c74 100644
--- a/app/features/tournament-lfg/routes/to.$id.looking.tsx
+++ b/app/features/tournament-lfg/routes/to.$id.looking.tsx
@@ -13,6 +13,7 @@ import {
import { FormWithConfirm } from "~/components/FormWithConfirm";
import { Placeholder } from "~/components/Placeholder";
import { SubmitButton } from "~/components/SubmitButton";
+import { useTournament } from "~/features/tournament/routes/to.$id";
import { SendouForm } from "~/form/SendouForm";
import { useAutoRefresh } from "~/hooks/useAutoRefresh";
import { useIsMounted } from "~/hooks/useIsMounted";
@@ -52,19 +53,28 @@ function TournamentLFGPage() {
function Groups() {
const { t } = useTranslation(["q"]);
const data = useLoaderData();
+ const tournament = useTournament();
const { width } = useWindowSize();
const isMobile = width < 750;
- const neutralGroups = data.groups.filter(
+ const ownMemberCount =
+ data.ownGroup?.members.length ?? data.ownTeam?.members.length ?? 0;
+ const availableSlots = tournament.maxMembersPerTeam - ownMemberCount;
+
+ const compatibleGroups = data.groups.filter(
+ (group) => group.members.length <= availableSlots,
+ );
+
+ const neutralGroups = compatibleGroups.filter(
(group) =>
!data.likes.given.some((like) => like.teamId === group.id) &&
!data.likes.received.some((like) => like.teamId === group.id),
);
- const likedGroups = data.groups.filter((group) =>
+ const likedGroups = compatibleGroups.filter((group) =>
data.likes.given.some((like) => like.teamId === group.id),
);
- const groupsReceivedLikesFrom = data.groups.filter((group) =>
+ const groupsReceivedLikesFrom = compatibleGroups.filter((group) =>
data.likes.received.some((like) => like.teamId === group.id),
);
@@ -84,7 +94,6 @@ function Groups() {
);
- const hasOwnSection = data.ownGroup ?? data.ownTeam;
const ownTabMemberCount = data.ownGroup
? data.ownGroup.members.length
: data.ownTeam
@@ -120,11 +129,9 @@ function Groups() {