Spaces:
Running
Running
Download core.js from CyberMax-tools/pawfoil: direct link, hf CLI and curl.
- Browser
- Download file 4.65 kB
-
https://huggingface.co/spaces/CyberMax-tools/pawfoil/resolve/main/core.js
- Command line
-
hf download hf://spaces/CyberMax-tools/pawfoil/core.js
-
curl -L -o core.js https://huggingface.co/spaces/CyberMax-tools/pawfoil/resolve/main/core.js
4.65 kB
| // Pawfoil core: turns a pet's name + kind into a collectible card's stats, moves and rarity (no DOM). | |
| // Deterministic for a given name, kind and roll number, so a re-roll is reproducible and testable. | |
| // (c) 2026 CyberMax. All rights reserved. | |
| import { rng } from './cmx.js'; | |
| export const KINDS = { | |
| dog: { label: 'Dog', emoji: '🐶', types: ['Zoomies', 'Loyal', 'Snack', 'Chaos'] }, | |
| cat: { label: 'Cat', emoji: '🐱', types: ['Shadow', 'Royal', 'Chaos', 'Nap'] }, | |
| bunny: { label: 'Bunny', emoji: '🐰', types: ['Hop', 'Fluff', 'Snack', 'Shadow'] }, | |
| bird: { label: 'Bird', emoji: '🐦', types: ['Sky', 'Song', 'Chaos', 'Royal'] }, | |
| other: { label: 'Other', emoji: '🐾', types: ['Mystery', 'Fluff', 'Chaos', 'Nap'] }, | |
| }; | |
| export const STATS = ['Cuteness', 'Chaos', 'Snack drive', 'Nap power', 'Zoomies', 'Loyalty']; | |
| export const RARITY = [ | |
| { id: 'common', label: 'Common', p: 0.5, stars: 1 }, | |
| { id: 'rare', label: 'Rare', p: 0.3, stars: 2 }, | |
| { id: 'epic', label: 'Epic', p: 0.15, stars: 3 }, | |
| { id: 'legendary', label: 'Legendary', p: 0.05, stars: 4 }, | |
| ]; | |
| const MOVES = { | |
| any: [['Puppy Eyes', 'Opponent forgets why they were mad.'], ['Loaf Mode', 'Becomes bread. Immune to chores.'], ['3 AM Zoomies', 'Runs laps. Wakes the house.'], | |
| ['Sock Heist', 'Steals one sock. Never returns it.'], ['Treat Radar', 'Detects a wrapper from 3 rooms away.'], ['Lap Claim', 'Sits on you. You can never leave.'], | |
| ['Dramatic Sigh', 'Guilt-trips everyone in range.'], ['Zoom Call Cameo', 'Appears on camera at the worst time.'], ['Vet Vanish', 'Disappears when the carrier comes out.'], | |
| ['Sunbeam Charge', 'Recharges fully in any sunny spot.'], ['Head Tilt', 'Confuses the enemy with pure cuteness.'], ['Couch Fortress', 'Holds the best seat all day.']], | |
| dog: [['Tail Helicopter', 'Knocks everything off the coffee table.'], ['Doorbell Alarm', 'Barks at every delivery, forever.'], ['Fetch Loop', 'Infinite fetch. You tire first.']], | |
| cat: [['Desk Sweep', 'Pushes one item off the table, slowly.'], ['Keyboard Nap', 'Types "jjjjjjjjj" into your email.'], ['Box Fit', 'If it fits, it sits.']], | |
| bunny: [['Binky Burst', 'A joyful jump that dodges all attacks.'], ['Cable Snip', 'Chews one charger. Critical hit.'], ['Hay Storm', 'Covers the floor in hay.']], | |
| bird: [['Morning Concert', 'Sings at sunrise. No snooze.'], ['Shoulder Ride', 'Takes the high ground.'], ['Mimic', 'Copies the microwave beep perfectly.']], | |
| other: [['Mystery Stare', 'Nobody knows what it is thinking.'], ['Tiny Speedrun', 'Escapes the enclosure in record time.'], ['Night Shift', 'Most active when you sleep.']], | |
| }; | |
| export function cleanPetName(s) { | |
| return String(s || '').normalize('NFKC').replace(/[^\p{L}\p{N} '.-]/gu, '').replace(/\s+/g, ' ').trim().slice(0, 18); | |
| } | |
| /** Card data for a pet. roll = 0 for the first card, 1, 2, … for re-rolls. pick() chooses rarity unless forced (Fun Pass). */ | |
| export function makeCard(name, kind = 'dog', roll = 0, forceRarity = null) { | |
| const k = KINDS[kind] ? kind : 'other'; | |
| const r = rng(`pawfoil:${cleanPetName(name).toLowerCase()}:${k}:${roll}`); | |
| let x = r(), rarity = RARITY[0]; | |
| for (const t of RARITY) { if (x < t.p) { rarity = t; break; } x -= t.p; } | |
| if (forceRarity) rarity = RARITY.find((t) => t.id === forceRarity) || rarity; | |
| const boost = { common: 0, rare: 6, epic: 12, legendary: 20 }[rarity.id]; | |
| const stats = STATS.map((s) => ({ s, v: Math.min(99, 35 + Math.floor(r() * 58) + boost) })); | |
| stats[0].v = Math.max(stats[0].v, 88); // every pet is very cute; that is the one fixed fact | |
| const pool = [...MOVES[k], ...MOVES.any]; | |
| const m1 = pool.splice(Math.floor(r() * MOVES[k].length), 1)[0]; | |
| const m2 = pool.splice(Math.floor(r() * pool.length), 1)[0]; | |
| const type = KINDS[k].types[Math.floor(r() * KINDS[k].types.length)]; | |
| const hp = 60 + Math.floor(r() * 9) * 10 + boost * 2; | |
| const power = Math.round(stats.reduce((a, b) => a + b.v, 0) / stats.length); | |
| const number = 1 + Math.floor(r() * 999); | |
| return { name: cleanPetName(name) || 'Mystery Pet', kind: k, kindLabel: KINDS[k].label, emoji: KINDS[k].emoji, type, rarity: rarity.id, rarityLabel: rarity.label, stars: rarity.stars, | |
| hp, power, stats, moves: [{ name: m1[0], text: m1[1], dmg: 10 * (2 + Math.floor(r() * 7)) }, { name: m2[0], text: m2[1], dmg: 10 * (3 + Math.floor(r() * 8)) }], number }; | |
| } | |
| export const FRAMES = { | |
| common: { a: '#E9E4D8', b: '#C9BFA8', ink: '#11141B', foil: false }, | |
| rare: { a: '#BFE3FF', b: '#5BA8E8', ink: '#0B2238', foil: false }, | |
| epic: { a: '#E5C8FF', b: '#9B5BE8', ink: '#1E0B38', foil: true }, | |
| legendary: { a: '#FFE9A8', b: '#E8A93A', ink: '#2B1A00', foil: true }, | |
| }; | |