Fable will fix it! Pt 2
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
<script>
|
||||
let count = $state(0)
|
||||
</script>
|
||||
|
||||
<button type="button" class="counter" onclick={() => count++}>Count is {count}</button>
|
||||
27
frontend/src/lib/cards.js
Normal file
27
frontend/src/lib/cards.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// Card-code helpers shared across components.
|
||||
// Codes look like "10D", "KH", "Joker1" (black) / "Joker2" (red).
|
||||
export const SUIT_EMOJI = { C: '♣️', S: '♠️', H: '♥️', D: '♦️' };
|
||||
|
||||
export function isJoker(code) {
|
||||
return !!code && code.startsWith('Joker');
|
||||
}
|
||||
|
||||
// "10D" -> "10"
|
||||
export function cardValue(code) {
|
||||
return code.slice(0, -1);
|
||||
}
|
||||
|
||||
// "10D" -> "D"
|
||||
export function cardSuit(code) {
|
||||
return code.slice(-1);
|
||||
}
|
||||
|
||||
export function getCardDisplay(code) {
|
||||
if (!code) return '';
|
||||
if (isJoker(code)) return '🃏 JOKER';
|
||||
return `${cardValue(code)}${SUIT_EMOJI[cardSuit(code)] || cardSuit(code)}`;
|
||||
}
|
||||
|
||||
export function playerName(players, id) {
|
||||
return players.find(p => p.id === id)?.name || '???';
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
import { apiRequest } from './api';
|
||||
|
||||
// Auto-generated math-pirate suggestions pool for character creation
|
||||
const SUGGESTIONS = {
|
||||
"look": [
|
||||
@@ -1211,227 +1213,42 @@ const SUGGESTIONS = {
|
||||
"They stubbornly argues about the slide rule and has the abacus tattoos to prove it.",
|
||||
"They accidently misplaces the dry gunpowder bags and has a certificate from the Stowaway Academy.",
|
||||
"They frequently drops the slide rule but gets confused by negative numbers."
|
||||
],
|
||||
"techniques": [
|
||||
"I meant to do that",
|
||||
"The floor was always a trap",
|
||||
"Carlos owes me one",
|
||||
"Barry already disarmed it",
|
||||
"Jaspar took the blame",
|
||||
"Greg always comes back",
|
||||
"We left Greg on purpose",
|
||||
"The seagulls work for me",
|
||||
"My whiskers told me so",
|
||||
"I was behind you the whole time",
|
||||
"I am wearing two eyepatches",
|
||||
"Nobody suspects the small one",
|
||||
"I licked it, so it's mine",
|
||||
"The grog made me do it",
|
||||
"I know a guy",
|
||||
"The guy knows another guy",
|
||||
"I greased the entire ship",
|
||||
"I hid the cannonball in my cheeks",
|
||||
"I taught the parrot everything it knows",
|
||||
"Hold my grog",
|
||||
"Watch this",
|
||||
"I counted to Pi",
|
||||
"Pi is exactly three when I'm in a hurry",
|
||||
"I cast Pie, again",
|
||||
"I memorized the wrong map on purpose",
|
||||
"I sneezed at exactly the right moment",
|
||||
"I read about this in a book I ate",
|
||||
"I learned this from a ghost",
|
||||
"The barrel was empty on purpose",
|
||||
"This is my emotional support cannon",
|
||||
"That wasn't even my final form",
|
||||
"I've been holding my breath since Tuesday",
|
||||
"It's a rental",
|
||||
"The ship likes me better",
|
||||
"My cousin is a cannonball",
|
||||
"My scars spell out a map",
|
||||
"My other gat is a crossbow",
|
||||
"I prepared this speech in advance",
|
||||
"I read the rulebook upside down",
|
||||
"Technically, that's legal",
|
||||
"Objection!",
|
||||
"It's called a heist now",
|
||||
"You wouldn't hit a rat with glasses",
|
||||
"These aren't even my real glasses",
|
||||
"I have the high ground",
|
||||
"You activated my trap card",
|
||||
"It was me all along",
|
||||
"Behold, a distraction",
|
||||
"We meet again, stairs",
|
||||
"I've seen this in a dream",
|
||||
"The prophecy said nothing about Tuesdays",
|
||||
"Catch!",
|
||||
"Think fast",
|
||||
"Look behind you",
|
||||
"No, seriously, look behind you",
|
||||
"This one goes to eleven",
|
||||
"Critical squeak",
|
||||
"Nothing up my sleeves but more sleeves",
|
||||
"The anchor was a decoy",
|
||||
"Six rats in a coat",
|
||||
"The Deep blinked first",
|
||||
"The Squid owed me a favor",
|
||||
"The Albatross vouches for me",
|
||||
"The Mermaid pinky-swore",
|
||||
"It worked in the bathtub",
|
||||
"We rehearsed this exactly once",
|
||||
"The smell did the negotiating",
|
||||
"Carry me, I'm dramatic",
|
||||
"It's only stealing if you get caught",
|
||||
"Borrowed permanently",
|
||||
"Never trust a dry deck",
|
||||
"Never trust a barefoot vampire",
|
||||
"Never duel before lunch",
|
||||
"Never sing the second verse",
|
||||
"Never play cards with a Goblin",
|
||||
"Never wake a sleeping Bean Whale",
|
||||
"Never bite the hand that feeds you cheese",
|
||||
"Never bring a sword to a math fight",
|
||||
"A rat always has an exit",
|
||||
"Two exits, actually",
|
||||
"A wet rat fears no rain",
|
||||
"A captain never looks up",
|
||||
"A good plan smells like cheese",
|
||||
"The loudest rat is rarely the problem",
|
||||
"Dead men file no complaints",
|
||||
"The best sword is a longer sword",
|
||||
"If you can't tie a knot, tie a lot",
|
||||
"When in doubt, scream and leap",
|
||||
"Every storm is someone's fault",
|
||||
"All maps lead to treasure if you're stubborn enough",
|
||||
"The sea forgives nothing but forgets everything",
|
||||
"Steal the boat first, the hearts will follow",
|
||||
"You miss every shot you don't blame on the wind",
|
||||
"There's no such thing as too much rope",
|
||||
"It is always squid o'clock somewhere",
|
||||
"Fortune favors the furry",
|
||||
"Don't count your doubloons until they're stolen",
|
||||
"Cheese before glory",
|
||||
"Glory before breakfast",
|
||||
"Gravity is a suggestion",
|
||||
"Rule one: there are no rules",
|
||||
"Rule two: see rule one",
|
||||
"The knot unties itself if you believe",
|
||||
"Smile until they get nervous",
|
||||
"Just keep nodding",
|
||||
"Pretend it's Tuesday",
|
||||
"Speak softly and carry a loud gat",
|
||||
"Improvise, adapt, overboard",
|
||||
"Cry first, ask questions later",
|
||||
"The tide pays its debts",
|
||||
"Apologize mid-swing",
|
||||
"Bite first, monologue later",
|
||||
"Lose dramatically, win quietly",
|
||||
"Fight like the ghosts are watching",
|
||||
"Trip over the right thing",
|
||||
"Be the cannonball",
|
||||
"Always fall on the soft pirate",
|
||||
"Flee now, gloat later",
|
||||
"Dramatic timing is a weapon",
|
||||
"Cheese knows the way home",
|
||||
"Trust the mustache",
|
||||
"The plank is a state of mind",
|
||||
"Every plank is a trapdoor if you stomp hard enough",
|
||||
"The pointy end goes in the other guy",
|
||||
"An apology and a gat beats an apology",
|
||||
"It's not a bribe, it's a gift",
|
||||
"The fourth wall is load-bearing",
|
||||
"Vampires can't resist counting spilled rice",
|
||||
"Distract them with interpretive dance",
|
||||
"Sing the shanty of unreasonable confidence",
|
||||
"The Cheddar Gambit",
|
||||
"The Gouda Guillotine",
|
||||
"The Brie Breach",
|
||||
"The Camembert Cannonade",
|
||||
"The Reverse Plank Walk",
|
||||
"The Drunken Compass Defense",
|
||||
"The Whisker Feint",
|
||||
"The Triple Barrel Roll",
|
||||
"The Stowaway Shuffle",
|
||||
"The Bilge Rat Backflip",
|
||||
"The Captain's Distraction Waltz",
|
||||
"The Forbidden Knot",
|
||||
"The Unforgivable Cannonball",
|
||||
"The Polite Mutiny",
|
||||
"The Reverse Mutiny",
|
||||
"The Squeak of a Thousand Regrets",
|
||||
"The Tail-First Retreat",
|
||||
"The Upside-Down Boarding Party",
|
||||
"The Invisible Rope Trick",
|
||||
"The Two-Hat Bamboozle",
|
||||
"The Ghost Pepper Defense",
|
||||
"The Barnacle Embrace",
|
||||
"The Crow's Nest Cannonball",
|
||||
"The Rigging Tango",
|
||||
"The Anchor Yo-Yo",
|
||||
"The Powder Keg Lullaby",
|
||||
"The Mermaid's IOU",
|
||||
"The Goblin Refund Policy",
|
||||
"The Fifth Ace",
|
||||
"The Sixth Ace",
|
||||
"The Emergency Wedding",
|
||||
"The Slow-Motion Walk Away",
|
||||
"The Borrowed Thunder",
|
||||
"The Cheese Wheel of Fortune",
|
||||
"The Last Rat Standing",
|
||||
"The First Rat Running",
|
||||
"The Old Switcheroo",
|
||||
"The Old Double Switcheroo",
|
||||
"The Long Con (Short Version)",
|
||||
"The Accidental Masterpiece",
|
||||
"The Sympathy Limp",
|
||||
"The Decoy Funeral",
|
||||
"The Surprise Encore",
|
||||
"The Overly Specific Alibi",
|
||||
"The Group Hug Ambush",
|
||||
"The Cannonball Curveball",
|
||||
"The Soup of Truth",
|
||||
"The Hat Trick (With Actual Hats)",
|
||||
"The Thousand-Yard Squint",
|
||||
"The Completely Legal Salvage",
|
||||
"Carry the one",
|
||||
"Round down, shoot up",
|
||||
"Divide and run away",
|
||||
"Subtract yourself from the situation",
|
||||
"Multiply by chaos",
|
||||
"Add insult to injury",
|
||||
"The remainder goes in my pocket",
|
||||
"Long division, short fuse",
|
||||
"Triangulate, then detonate",
|
||||
"Roll for cheese",
|
||||
"Yo ho ho and a barrel of math",
|
||||
"Ask the cook, he's seen worse",
|
||||
"Barry has a boat for this",
|
||||
"Tell Jaspar I said hello",
|
||||
"The first mate signs my alibis",
|
||||
"Piss Whiskers takes the watch",
|
||||
"We voted, you lost",
|
||||
"The cheese was a lie",
|
||||
"One more verse won't hurt",
|
||||
"Abandon ship, but stylishly"
|
||||
]
|
||||
};
|
||||
|
||||
export function getSuggestion(type, exclude = []) {
|
||||
let arr = SUGGESTIONS[type];
|
||||
if (!arr) return "";
|
||||
function pickFrom(arr, exclude = []) {
|
||||
const taken = new Set(exclude.map(s => s.trim().toLowerCase()));
|
||||
const available = arr.filter(s => !taken.has(s.trim().toLowerCase()));
|
||||
if (available.length > 0) arr = available;
|
||||
return arr[Math.floor(Math.random() * arr.length)];
|
||||
const pool = available.length > 0 ? available : arr;
|
||||
return pool[Math.floor(Math.random() * pool.length)];
|
||||
}
|
||||
|
||||
// Draws `count` distinct suggestions from a pool.
|
||||
export function getSuggestions(type, count) {
|
||||
export function getSuggestion(type, exclude = []) {
|
||||
const arr = SUGGESTIONS[type];
|
||||
if (!arr) return "";
|
||||
return pickFrom(arr, exclude);
|
||||
}
|
||||
|
||||
// Secret Pirate Technique names live in the backend (it also uses them to equip
|
||||
// re-rolled recruits); fetch the pool once and pick from it client-side.
|
||||
let techniquePoolPromise = null;
|
||||
function loadTechniquePool() {
|
||||
if (!techniquePoolPromise) {
|
||||
techniquePoolPromise = apiRequest('/suggestions/techniques').then(d => d.techniques);
|
||||
}
|
||||
return techniquePoolPromise;
|
||||
}
|
||||
|
||||
export async function getTechniqueSuggestion(exclude = []) {
|
||||
return pickFrom(await loadTechniquePool(), exclude);
|
||||
}
|
||||
|
||||
// Draws `count` distinct technique suggestions.
|
||||
export async function getTechniqueSuggestions(count, exclude = []) {
|
||||
const pool = await loadTechniquePool();
|
||||
const picked = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const s = getSuggestion(type, picked);
|
||||
if (!s) break;
|
||||
picked.push(s);
|
||||
picked.push(pickFrom(pool, [...exclude, ...picked]));
|
||||
}
|
||||
return picked;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user