feat: THE CREATOR god-tier character, bitcoin choreographies, omni-morph, cameos

- Guy Fawkes mask archetype with golden outline, bitcoin chest symbol, laptop, tier-gated effects
- 12 custom bitcoin-themed choreographies (8 regular + 4 exclusive ultimates)
- Omni-morph system: creator morphs into any of 80+ archetypes instead of cycling 3
- 6% per-round cameo in non-creator fights — drops golden ₿ gifts to fighters
- Custom golden code rain entrance with persistent orbiting particles
- pickChoreography: 50% ultimate chance (100% on crits), all tier ultimates + 4 exclusive
- Server auto-assigns the_creator archetype on login/register for creator pubkey
- FightViewer, payments, queue, orchestrator improvements

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-08 12:08:18 +00:00
co-authored by Claude Opus 4.6
parent 8a3480e5ab
commit 6d390f69b2
14 changed files with 1513 additions and 104 deletions
@@ -34,6 +34,8 @@ import { chef, firefighter, astronautArch, clown, detective, nurse, lumberjack,
import { elephant, giraffe, hippo, lion, monkey, parrot, raccoon, snakeArch, turtle, whale, crocodile, flamingo, hedgehog, panda, hamster } from './batch_animals2'
// Batch 8: Silly & Objects
import { sockPuppet, trafficCone, toiletMan, potato, cloudMan, rockMan, balloonMan, trashCan, rubberDuckArch, snowman, scarecrow, jackOLantern, gardenGnome, lampPost, broomMan } from './batch_silly'
// The Creator (game founder — unique god-tier character)
import { theCreator } from './the_creator'
export const archetypes: Archetype[] = [
// Original 6
@@ -54,6 +56,8 @@ export const archetypes: Archetype[] = [
elephant, giraffe, hippo, lion, monkey, parrot, raccoon, snakeArch, turtle, whale, crocodile, flamingo, hedgehog, panda, hamster,
// Batch 8: silly & objects
sockPuppet, trafficCone, toiletMan, potato, cloudMan, rockMan, balloonMan, trashCan, rubberDuckArch, snowman, scarecrow, jackOLantern, gardenGnome, lampPost, broomMan,
// The Creator (god-tier, weight: 0 — never randomly rolled)
theCreator,
]
export function rollArchetype(roll: number): Archetype {
@@ -0,0 +1,313 @@
import type { Archetype } from '../constants'
export const theCreator: Archetype = {
name: 'the_creator',
weight: 0, // Never randomly rolled — only assigned via pubkey detection
canHaveVisor: false,
canHaveMohawk: false,
canHaveHorns: false,
drawFeatures: (p) => {
const { px, fill, pal, ox, oy, t, frame, tier, bounce,
idle, atk, kick, special, hit, knockback, ko, win,
cx, hOff, vBounce, globalY, koSlump,
bx, by, bw, bh, hx, hy, hw, hh,
armLx, armAttach, armW, armH, feetY } = p
// ═══════════════════════════════════════════
// THE CREATOR — God-tier unique character
// Guy Fawkes mask, hooded cloak, laptop,
// bitcoin chest emblem, orbiting gold particles,
// matrix code rain, tier-gated divine effects
// ═══════════════════════════════════════════
// Color constants
const MASK = '#f5f0e0' // warm ivory
const MASK_SHD = '#ddd5c0' // mask shadow
const HOOD = '#1a1a1a' // dark hood
const HOOD_LT = '#2a2a2a' // hood highlight
const GOLD = '#ffd700' // bitcoin gold
const GOLD_DK = '#c8a000' // dark gold
const GOLD_LT = '#ffee88' // light gold
const MATRIX = '#00ff41' // matrix green
const MATRIX_DK = '#00cc33' // darker green
const LAPTOP = '#333333' // laptop body
const SCREEN = '#00ff41' // laptop screen
// ── HOOD / CLOAK ──
// Dark hood drapes over head and extends down behind body
if (!ko) {
// Hood over head (extends above and around the head)
for (let iy = hy - 2; iy < hy + Math.floor(hh * 0.5); iy++) {
for (let ix = hx - 1; ix <= hx + hw; ix++) {
px(ix, iy, HOOD, ox, oy)
}
}
// Hood peak
px(cx + hOff, hy - 3, HOOD, ox, oy)
px(cx + hOff - 1, hy - 3, HOOD, ox, oy)
px(cx + hOff + 1, hy - 3, HOOD, ox, oy)
px(cx + hOff, hy - 4, HOOD, ox, oy)
// Hood highlight
px(hx, hy - 1, HOOD_LT, ox, oy)
px(hx + 1, hy - 2, HOOD_LT, ox, oy)
// Cloak behind body (darker layer extending from shoulders)
const cloakTop = by - 1
const cloakBot = feetY + globalY + 2
for (let iy = cloakTop; iy < cloakBot; iy++) {
const spread = Math.floor((iy - cloakTop) / 3)
px(bx - 2 - spread, iy, HOOD, ox, oy)
px(bx + bw + 1 + spread, iy, HOOD, ox, oy)
// Cloak back fill
if (iy > by + bh) {
for (let ix = bx - 1 - spread; ix <= bx + bw + spread; ix++) {
px(ix, iy, HOOD, ox, oy)
}
}
}
// Matrix code rain through cloak (animated green pixels)
const codeStreams = 3 + Math.floor(tier * 0.5)
for (let s = 0; s < codeStreams; s++) {
const streamX = bx - 1 + Math.floor((bw + 2) * s / codeStreams)
const phase = (t + s * 0.3) % 1
const streamLen = 3 + (s % 2)
for (let d = 0; d < streamLen; d++) {
const sy = cloakTop + Math.floor(phase * (cloakBot - cloakTop)) + d
if (sy < cloakBot && sy > by + bh - 2) {
const alpha = d === 0 ? MATRIX : d === 1 ? MATRIX_DK : '#006622'
px(streamX, sy, alpha, ox, oy)
}
}
}
}
// ── GUY FAWKES MASK ──
// Distinctive features: pointed chin, arched brows, upward mustache, eternal smile
if (!ko) {
const faceTop = hy + Math.floor(hh * 0.25)
const faceBot = hy + Math.floor(hh * 0.8)
const faceCx = cx + hOff
// Mask base — cream/ivory covering the face area
for (let iy = faceTop; iy < faceBot; iy++) {
const inset = iy === faceTop || iy === faceBot - 1 ? 1 : 0
for (let ix = hx + 2 + inset; ix < hx + hw - 2 - inset; ix++) {
px(ix, iy, MASK, ox, oy)
}
// Right-side shadow
px(hx + hw - 3, iy, MASK_SHD, ox, oy)
}
// Pointed chin extending below face
const chinY = faceBot
px(faceCx, chinY, MASK, ox, oy)
px(faceCx - 1, chinY, MASK_SHD, ox, oy)
px(faceCx + 1, chinY, MASK_SHD, ox, oy)
px(faceCx, chinY + 1, MASK_SHD, ox, oy)
// Eye slits — narrow golden glow
const eyeY = faceTop + Math.max(1, Math.floor((faceBot - faceTop) * 0.25))
const leX = faceCx - Math.floor(hw * 0.2)
const reX = faceCx + Math.floor(hw * 0.15)
// Arched eyebrows above eyes
px(leX - 1, eyeY - 1, '#333333', ox, oy)
px(leX, eyeY - 1, '#333333', ox, oy)
px(leX + 1, eyeY - 1, '#333333', ox, oy)
px(reX - 1, eyeY - 1, '#333333', ox, oy)
px(reX, eyeY - 1, '#333333', ox, oy)
px(reX + 1, eyeY - 1, '#333333', ox, oy)
// Eye slits with golden glow (pulsing)
const eyeColor = frame % 3 === 0 ? GOLD_LT : GOLD
px(leX, eyeY, eyeColor, ox, oy)
px(leX + 1, eyeY, eyeColor, ox, oy)
px(reX, eyeY, eyeColor, ox, oy)
px(reX + 1, eyeY, eyeColor, ox, oy)
// Eye glow halo (subtle)
if (special || atk) {
px(leX - 1, eyeY, GOLD_DK, ox, oy)
px(reX + 2, eyeY, GOLD_DK, ox, oy)
}
// Rosy cheeks
const cheekY = eyeY + 2
px(leX - 1, cheekY, '#cc8888', ox, oy)
px(reX + 2, cheekY, '#cc8888', ox, oy)
// Upward-curling mustache
const stacheY = cheekY + 1
px(faceCx - 1, stacheY, '#222222', ox, oy)
px(faceCx, stacheY, '#222222', ox, oy)
px(faceCx + 1, stacheY, '#222222', ox, oy)
// Curled tips upward
px(faceCx - 2, stacheY - 1, '#222222', ox, oy)
px(faceCx + 2, stacheY - 1, '#222222', ox, oy)
// Eternal smile
const smileY = stacheY + 1
px(faceCx - 2, smileY, '#333333', ox, oy)
px(faceCx - 1, smileY + 1, '#333333', ox, oy)
px(faceCx, smileY + 1, '#333333', ox, oy)
px(faceCx + 1, smileY + 1, '#333333', ox, oy)
px(faceCx + 2, smileY, '#333333', ox, oy)
// Thin goatee below chin
px(faceCx, chinY + 2, '#222222', ox, oy)
}
// ── BITCOIN SYMBOL ON CHEST ──
// Always visible — pulsing gold ₿
const btcColor = frame % 4 < 2 ? GOLD : GOLD_LT
const btcX = cx + hOff - 1
const btcY = by + Math.max(1, Math.floor(bh * 0.25))
// Vertical stroke of ₿
px(btcX, btcY, btcColor, ox, oy)
px(btcX, btcY + 1, btcColor, ox, oy)
px(btcX, btcY + 2, btcColor, ox, oy)
px(btcX, btcY + 3, btcColor, ox, oy)
// Right bumps of ₿
px(btcX + 1, btcY, btcColor, ox, oy)
px(btcX + 2, btcY + 1, btcColor, ox, oy)
px(btcX + 1, btcY + 2, btcColor, ox, oy)
px(btcX + 2, btcY + 3, btcColor, ox, oy)
px(btcX + 1, btcY + 4, btcColor, ox, oy)
// Top/bottom serifs
px(btcX, btcY - 1, GOLD_DK, ox, oy)
px(btcX, btcY + 4, GOLD_DK, ox, oy)
// ── LAPTOP IN LEFT HAND ──
// Replaces left arm visual — laptop body + green terminal screen
if (!ko && !knockback) {
const lapX = armLx - 2 + hOff
const lapY = armAttach + Math.floor(armH * 0.3) + globalY + vBounce
// Laptop body
fill(lapX, lapY, 6, 4, LAPTOP, ox, oy)
px(lapX, lapY, '#444444', ox, oy) // corner highlight
// Screen
const screenColor = (atk || special) ? '#ffffff' : SCREEN
fill(lapX + 1, lapY + 1, 4, 2, screenColor, ox, oy)
// Blinking cursor
if (frame % 2 === 0 && !atk && !special) {
px(lapX + 3, lapY + 2, '#000000', ox, oy)
}
// Magic emanating from laptop during attacks
if ((atk || special) && t > 0.2) {
const sparkDist = Math.floor(t * 6)
px(lapX + 3, lapY - sparkDist, GOLD, ox, oy)
px(lapX + 1, lapY - sparkDist - 1, MATRIX, ox, oy)
px(lapX + 5, lapY - sparkDist + 1, GOLD_LT, ox, oy)
if (sparkDist > 2) {
px(lapX + 2, lapY - sparkDist - 2, '#ffffff', ox, oy)
}
}
}
// ── FLOATING BITCOIN PARTICLES ──
// 4-6 gold dots orbiting the character always
if (!ko) {
const numParticles = 4 + Math.floor(tier * 0.5)
const orbitCx = cx + hOff
const orbitCy = by + Math.floor(bh / 2) + vBounce + globalY
for (let i = 0; i < numParticles; i++) {
const angle = t * Math.PI * 2 + i * (Math.PI * 2 / numParticles)
const radius = 8 + i * 2
const px2 = orbitCx + Math.round(Math.cos(angle) * radius)
const py2 = orbitCy + Math.round(Math.sin(angle) * radius * 0.6)
const colors = [GOLD, GOLD_LT, '#ff8c00', GOLD_DK, '#ffffff']
if ((frame + i) % 2 === 0) {
px(px2, py2, colors[i % colors.length], ox, oy)
}
}
}
// ── TIER 3+: LIGHTNING CROWN ──
if (tier >= 3 && !ko) {
const crownY = hy - 5 + vBounce + globalY
// 3 golden lightning points
for (let i = -1; i <= 1; i++) {
const crownX = cx + hOff + i * 3
const flash = (frame + i * 2) % 3 === 0 ? '#ffffff' : GOLD
px(crownX, crownY, flash, ox, oy)
px(crownX, crownY + 1, GOLD_DK, ox, oy)
}
// Lightning bolts connecting crown points
if (frame % 2 === 0) {
px(cx + hOff - 1, crownY, GOLD_LT, ox, oy)
px(cx + hOff + 1, crownY, GOLD_LT, ox, oy)
}
}
// ── TIER 4+: CODE WINGS ──
if (tier >= 4 && !ko && !knockback) {
const wingY = by + 1 + vBounce + globalY
const wingSpan = 4 + tier
// Left wing
for (let w = 0; w < wingSpan; w++) {
const wy = wingY + Math.floor(w * 0.4)
px(bx - 3 - w, wy, MATRIX, ox, oy)
if (w % 2 === 0) px(bx - 3 - w, wy - 1, MATRIX_DK, ox, oy)
}
// Right wing
for (let w = 0; w < wingSpan; w++) {
const wy = wingY + Math.floor(w * 0.4)
px(bx + bw + 2 + w, wy, MATRIX, ox, oy)
if (w % 2 === 0) px(bx + bw + 2 + w, wy - 1, MATRIX_DK, ox, oy)
}
// Wing glow particles
if (idle || win) {
const wgPhase = t * Math.PI * 2
px(bx - 4 - wingSpan, wingY + Math.round(Math.sin(wgPhase) * 2), GOLD, ox, oy)
px(bx + bw + 3 + wingSpan, wingY + Math.round(Math.cos(wgPhase) * 2), GOLD, ox, oy)
}
}
// ── TIER 5+: SATOSHI HALO ──
if (tier >= 5 && !ko) {
const haloY = hy - 7 + vBounce + globalY
const haloCx = cx + hOff
const haloR = 5
// 6 orbiting satoshi dots forming a halo ring
for (let i = 0; i < 6; i++) {
const angle = t * Math.PI * 1.5 + i * (Math.PI * 2 / 6)
const sx = haloCx + Math.round(Math.cos(angle) * haloR)
const sy = haloY + Math.round(Math.sin(angle) * 2)
const sColor = i % 2 === 0 ? GOLD : '#ffffff'
px(sx, sy, sColor, ox, oy)
}
}
// ── WIN POSE: GOLDEN EXPLOSION ──
if (win) {
const burstCx = cx + hOff
const burstCy = by + Math.floor(bh / 2) + vBounce + globalY
const burstR = 12 + tier * 2
for (let i = 0; i < 8; i++) {
const angle = t * Math.PI * 4 + i * (Math.PI / 4)
const dist = burstR * (0.5 + t * 0.5)
const bpx = burstCx + Math.round(Math.cos(angle) * dist)
const bpy = burstCy + Math.round(Math.sin(angle) * dist * 0.6)
const bColor = i % 3 === 0 ? GOLD : i % 3 === 1 ? GOLD_LT : '#ffffff'
if ((frame + i) % 2 === 0) px(bpx, bpy, bColor, ox, oy)
}
}
// ── KO: MASK CRACKS + FADING ──
if (ko) {
// Cracked mask fragments scattered
const faceCx = cx + hOff
const fY = hy + Math.floor(hh * 0.4)
px(faceCx - 1, fY + koSlump, MASK_SHD, ox, oy)
px(faceCx + 1, fY + 1 + koSlump, MASK, ox, oy)
px(faceCx, fY + 2 + koSlump, MASK_SHD, ox, oy)
// Fading golden eye
if (frame % 3 === 0) {
px(faceCx - 1, fY - 1 + koSlump, GOLD_DK, ox, oy)
}
// Laptop fallen on ground
fill(faceCx + 3, feetY + globalY - 1, 4, 2, LAPTOP, ox, oy)
px(faceCx + 4, feetY + globalY - 1, SCREEN, ox, oy)
}
},
}
+13
View File
@@ -34,6 +34,19 @@ export function generateSpriteSheet(
const finalSecondary = customization?.secondaryColor || secondaryColor
const pal = makePal(finalPrimary, finalSecondary, tier)
// The Creator — forced golden palette + golden outline (totally unique)
if ((customization?.archetype || archetypeOverride) === 'the_creator') {
pal.body = 'hsl(43, 85%, 52%)'
pal.dark = 'hsl(43, 85%, 32%)'
pal.light = 'hsl(43, 95%, 67%)'
pal.acc = 'hsl(30, 90%, 55%)'
pal.accDark = 'hsl(30, 90%, 35%)'
pal.accLight = 'hsl(30, 95%, 70%)'
pal.out = '#c8a000' // golden outline instead of black
pal.skin = '#f5f0e0' // mask ivory
pal.skinDark = '#ddd5c0'
}
let sh = 0
for (let i = 0; i < seed.length; i++) sh = ((sh << 5) - sh + seed.charCodeAt(i)) | 0
const rng = () => { sh = (sh * 16807) % 2147483647; return (sh & 0x7fffffff) / 2147483647 }