SOURCE / PINNED RELEASE
Made of little things.
Chase'n Those Narly Waves
- Release
- 0c7dde9d4921…
- Author-recorded commit
- 88e58516be17…
- License
- LICENSE
- Author’s source reference
- nostr://npub1ny7l44q0au5xkv3mjgt6zks5f0e2ydefslq4nhh2ve2xypgc2mqqat8qx2/wss%3A%2F%2Fgit.napplet.soy%2F/n-836af5a08b9
Archive hash verified: e1acfdbe67c54dd4…. The source-to-build association is the author’s claim; it has not been independently rebuilt.
// All 55 surf tricks, and the words that pop up when you land one.
// Want a new trick or a new saying? Add it to the lists below!
//
// IMPORTANT combo rule: a trick's keys can't be the START of another trick.
// Example: "↑ ↑" is Big Air, so "↑ ↑ →" could never happen (Big Air fires first).
// That's why some 2-key combos (like "↑ ←") are not tricks: they start longer ones.
import type { Arrow } from './input.js';
/** How the surfer moves during the trick (drawn in render.ts). */
export type TrickMove =
| 'jump'
| 'spin'
| 'double-spin'
| 'flip'
| 'big-flip'
| 'roll'
| 'grab'
| 'wiggle'
| 'carve-left'
| 'carve-right';
export interface Trick {
name: string;
combo: Arrow[];
points: number;
/** How long the trick takes, in seconds. */
duration: number;
/** In the air? Then you can jump right over rocks! */
airborne: boolean;
move: TrickMove;
}
/** How long each kind of move takes, and whether you leave the water. */
const MOVES: Record<TrickMove, { duration: number; airborne: boolean }> = {
jump: { duration: 0.8, airborne: true },
spin: { duration: 0.9, airborne: true },
'double-spin': { duration: 1.1, airborne: true },
flip: { duration: 1.0, airborne: true },
'big-flip': { duration: 1.2, airborne: true },
roll: { duration: 0.9, airborne: true },
grab: { duration: 0.8, airborne: true },
wiggle: { duration: 0.9, airborne: false },
'carve-left': { duration: 0.7, airborne: false },
'carve-right': { duration: 0.7, airborne: false },
};
const KEYS: Record<string, Arrow> = { '↑': 'up', '↓': 'down', '←': 'left', '→': 'right' };
/** Make a trick from a name, arrow keys like "↑↓←", points and a move. */
function trick(name: string, keys: string, points: number, move: TrickMove): Trick {
const combo = [...keys].map((key) => KEYS[key]!);
return { name, combo, points, move, ...MOVES[move] };
}
/** The 8 basic tricks (2 keys). These are shown on the start menu. */
export const BASIC_TRICKS: Trick[] = [
trick('Big Air', '↑↑', 300, 'jump'),
trick('Ollie Pop', '↑↓', 150, 'jump'),
trick('Board Grab', '↓↓', 200, 'grab'),
trick('Bottom Turn', '↓←', 150, 'carve-left'),
trick('Left Slash', '←←', 200, 'carve-left'),
trick('Cutback', '←→', 200, 'carve-left'),
trick('Floater', '→←', 200, 'carve-right'),
trick('Right Slash', '→→', 200, 'carve-right'),
];
/** 27 secret tricks with 3 keys. */
const THREE_KEY_TRICKS: Trick[] = [
trick('Tail Whip', '↑←↓', 400, 'spin'),
trick('Left Air Reverse', '↑←←', 450, 'spin'),
trick('Grab and Go', '↑←→', 400, 'grab'),
trick('Kickflip', '↑→↓', 450, 'roll'),
trick('Right Air Reverse', '↑→→', 450, 'spin'),
trick('Shaka Jump', '↑→←', 400, 'grab'),
trick('Backflip', '↓↑↑', 700, 'flip'),
trick('Frontflip', '↓↑↓', 650, 'flip'),
trick('Corkscrew', '↓↑←', 550, 'roll'),
trick('Rodeo Flip', '↓↑→', 650, 'flip'),
trick('Sky Hook', '↓→↑', 400, 'jump'),
trick('Snap Back', '↓→←', 350, 'carve-right'),
trick('Power Carve', '↓→→', 350, 'carve-right'),
trick('Spinner 360', '←↑→', 500, 'spin'),
trick('Rocket Air', '←↑↑', 450, 'jump'),
trick('Tornado', '←↑←', 600, 'double-spin'),
trick('Helicopter', '←↑↓', 600, 'double-spin'),
trick('Low Rider', '←↓↓', 350, 'wiggle'),
trick('Tube Stall', '←↓→', 400, 'wiggle'),
trick('Lip Launch', '←↓↑', 450, 'jump'),
trick('Reverse Spinner', '→↑←', 500, 'spin'),
trick('Moon Leap', '→↑↑', 450, 'jump'),
trick('Whirlwind', '→↑→', 600, 'double-spin'),
trick('Superman', '→↑↓', 550, 'grab'),
trick('Hang Five', '→↓↓', 350, 'wiggle'),
trick('Noseslide', '→↓←', 400, 'wiggle'),
trick('Ramp Rocket', '→↓↑', 450, 'jump'),
];
/** 20 super secret tricks with 4 keys. Worth the most points! */
const FOUR_KEY_TRICKS: Trick[] = [
trick('Hang Ten', '↑←↑↑', 800, 'wiggle'),
trick('Sky Dive', '↑←↑↓', 1100, 'big-flip'),
trick('Cyclone 720', '↑←↑←', 1000, 'double-spin'),
trick('Mega Air', '↑←↑→', 900, 'jump'),
trick('Rocket Ship', '↑→↑↑', 900, 'jump'),
trick('Double Backflip', '↑→↑↓', 1200, 'big-flip'),
trick('Barrel Roll', '↑→↑←', 950, 'roll'),
trick('Hurricane 720', '↑→↑→', 1000, 'double-spin'),
trick('Wave Dancer', '←↓←↓', 800, 'wiggle'),
trick('Tsunami Slash', '←↓←←', 750, 'carve-left'),
trick('Turtle Roll', '←↓←→', 900, 'roll'),
trick('Dolphin Dive', '←↓←↑', 950, 'flip'),
trick('Noodle Legs', '→↓→↓', 800, 'wiggle'),
trick('Thunder Carve', '→↓→→', 750, 'carve-right'),
trick('Crazy Eight', '→↓→←', 1000, 'double-spin'),
trick('Starfish Jump', '→↓→↑', 850, 'grab'),
trick('Head Dip', '↓→↓↓', 800, 'wiggle'),
trick('Shark Hop', '↓→↓↑', 900, 'jump'),
trick('Twister', '↓→↓←', 950, 'roll'),
trick('Dragon Flip', '↓→↓→', 1200, 'big-flip'),
];
export const TRICKS: Trick[] = [...BASIC_TRICKS, ...THREE_KEY_TRICKS, ...FOUR_KEY_TRICKS];
/** Longest combos are checked first. */
export const TRICKS_LONGEST_FIRST = [...TRICKS].sort((a, b) => b.combo.length - a.combo.length);
/** The longest combo, so we know how many key presses to remember. */
export const LONGEST_COMBO = TRICKS_LONGEST_FIRST[0]?.combo.length ?? 4;
export const CHEERS = [
'TOTALLY RAD, BRO!',
'NARLY STUNT, MAN!',
'NO WAY, DUDE!',
'COWABUNGA!',
'TUBULAR!',
'RADICAL!',
'STOKED!',
'BODACIOUS!',
'SICK MOVES, DUDE!',
'HANG TEN!',
'CRUSHED IT, BRAH!',
'SO NARLY!',
'EPIC, BRO!',
'FAR OUT, MAN!',
'WICKED!',
'SHAKA, BRAH!',
"YOU'RE A LEGEND!",
'OUT OF CONTROL!',
'RIDE ON, DUDE!',
'MEGA STOKED!',
];
const ARROW_GLYPHS: Record<Arrow, string> = { up: '↑', down: '↓', left: '←', right: '→' };
export function comboText(combo: Arrow[]): string {
return combo.map((arrow) => ARROW_GLYPHS[arrow]).join(' ');
}
let lastCheer = '';
/** Pick a random cheer, but never the same one twice in a row. */
export function randomCheer(): string {
let cheer = lastCheer;
while (cheer === lastCheer) {
cheer = CHEERS[Math.floor(Math.random() * CHEERS.length)] ?? 'RAD!';
}
lastCheer = cheer;
return cheer;
}
