Commit Graph
25 Commits
Author SHA1 Message Date
tiennm99 081ab00f2e feat(gacha): restyle the beta wish as a six-star wish sequence
The beta wish now cuts like an anime gacha game's six-star wish over 10.5
seconds: a dive through a painterly cloud vortex, a beam pass, a meteor
that stops inside a rainbow halo and bursts, a red flash, falling crystal
comets joined by a red meteor, a black emblem silhouette shedding shards
on a red disc, and a splash card with a name plate and six stars.

Cloud layers are painted once per roll onto a canvas with a seeded noise
displacement, so frames only move the baked image instead of running a
live SVG filter. The /api/gachabeta contract is unchanged.
2026-10-01 16:32:30 +07:00
tiennm99 b8d5544fd6 feat(gacha): rebuild the beta wish as a meteor shower reveal
The beta wish now renders an 8-second night sky over a snow-rimmed
mountain ridge, where coloured meteors glide down the diagonal. A hero
meteor in the rarity colour slows and burns out at the centre, where the
label appears under rank SSS. The toon cloud, comet, and perspective
camera are removed. The /api/gachabeta contract is unchanged.
2026-10-01 16:07:46 +07:00
tiennm99 adea684a2f feat(gacha): refine beta wish animation and SSS reveal 2026-10-01 15:42:54 +07:00
tiennm99 d2f09ecb33 feat(gacha): add /api/gachabeta toon-shaded wish style
The beta wish renders an 8-second astrology-themed night sky in toon
shading through a perspective action camera: it dollies toward a hero
cloud, a comet lights the cloud's rim from behind and bursts through it,
the camera chases the comet as its flare builds to a starburst, and the
label appears with an S, SS, or SSS rank. It shares the /api/gacha request
contract and render slots.
2026-10-01 15:07:56 +07:00
tiennm99 dccd524f9d feat(gacha): fly into the rank emblem with a rainbow sunburst and twinkling sky
The meteor now flies in from the upper left and lands on the rank emblem,
so the impact burst becomes the badge reveal. The 5-star rainbow ring is
replaced by a rainbow sunburst of counter-rotating rays. Sky stars twinkle
with short bright flares and cross glints, and each roll draws a fresh
layout from a per-request seed that the route picks when the caller sends
none.
2026-10-01 14:43:46 +07:00
tiennm99 9b93c53647 revert: restore the gacha animation to bd314b4
Reverts be27792 (measured straight glide) and 028ad08 (gravity arc), going
back to the B/A/S tier-escalation version with the original curved fall.
2026-10-01 14:26:33 +07:00
tiennm99 dc8f7f6ae7 feat(gacha): match the meteor glide to the source game's motion
Measured from reference footage, the meteor sweeps in along a straight line
from the upper left at about 23 degrees and eases out exponentially to a
hover above the horizon, replacing the gravity arc. The trail is now a soft
beam that narrows into the head with ribbons fanning along it, every tier
gets the horizontal lens flare, and sparkles drift above the beam.
2026-10-01 14:12:01 +07:00
tiennm99 004325d947 feat(gacha): make the meteor fall under real gravity
The meteor now follows a ballistic parabola: constant horizontal speed and
vertical speed growing under gravity solved from the launch and impact
points, so it climbs slightly, bends over, and dives while speeding up. The
trail samples past positions in time so it lengthens with speed, the head
stretches along its velocity, and sparks inherit the meteor's velocity and
fall under their own gravity.
2026-10-01 13:58:38 +07:00
tiennm99 adc2b94ae9 feat(gacha): escalate the wish animation by rarity with B/A/S rank emblems
Every beat now scales from a per-tier effects table: 4-star adds a bigger
meteor, lens flare, impact shake, and double shockwave; 5-star adds a
rainbow halo before landing, a gold sky flood, a starburst, counter-rotating
rays, falling sparkles, and an emblem sheen. The emblem shows the rank
letter instead of the label's first character.
2026-10-01 13:25:06 +07:00
tiennm99 97d8aeaadf feat(gacha): add /api/gacha wish animation rendered as MP4
A meteor coloured by rarity falls across a night sky, lands in a white
flash, and the label is revealed with its stars popping in. The caller
picks the result and rarity; the route only draws it. Renders as silent
H.264 so Telegram plays it as an animation without GIF banding, and
shares the render slots with /api/gif.
2026-10-01 11:43:42 +07:00
tiennm99 529da3621d feat(schema): allow single-entry wheels 2026-08-06 11:43:10 +07:00
tiennm99 ffa9729683 revert: restore tree to 757e41d869f20acbecfee64642514878b18dd528 2026-07-28 10:42:34 +07:00
tiennm99 3b28e46069 fix(remotion): tie the winner celebration to the winning slice
Draw the winner outline as an unfilled pie after every slice: slices paint
in index order, so the next one covered the shared radial edge and the
outline stopped short of wrapping the wedge.

Derive the announcement colors from the winning slice - its own color as
the chip, a deepened version as the frame and the wedge outline - instead
of a fixed dark chip that read as unrelated to the wheel.

Raise the confetti above the announcement so the burst is no longer hidden
behind it, and steepen the launch fan so the field arcs around the name
rather than settling on it: horizontal reach is vx / drag, which parked the
flat fan at mid-canvas. Measured over the hold, particle samples inside the
pill drop from 22.4% to 6.2% while disc coverage holds at 85.5%.
2026-07-27 17:42:38 +07:00
tiennm99 f9accfd70a feat(remotion): rework winner celebration and spin motion
The animation's weakest moment was its final frame, which is also the frame
most chat clients show as the GIF's poster image.

Celebration:
- Confetti launches from two cannons framing the wheel instead of bursting
  inside the hub, using closed-form drag ballistics so chips decelerate and
  flutter rather than flying a clean parabola. The field previously drained
  from 70 particles to 3 before the GIF ended; it now stays populated, and a
  2500ms hold ends with 68 on screen instead of 0.
- The opening frame rendered at opacity 0, throwing away the one frame where
  particles are still clustered. Removed the fade-in and made the tail
  frame-relative and quantized to bound its palette cost.
- Chips streak along their direction of travel in proportion to speed, faking
  the motion blur a GIF cannot carry. A second volley, sized to the
  celebration window, keeps long holds from thinning out.
- Each theme carries its own confetti palette. Slice colors are tuned to hold
  dark text over a large area, which left five of six mono colors under 2.4:1
  against the background and invisible at particle size.
- The winner's name appears over the hub during the hold. At a 200px chat
  display the pointer is ~17px and a slice label ~9px, so the poster frame did
  not say who won.
- Winner emphasis is an opaque stroke, not a drop-shadow. A filter on a Pie
  paints outside the shape, so later siblings overpainted it and only one
  neighbour ever showed the halo.

Motion:
- Turn count scales with the frame budget and slice width. A fixed 5 turns put
  peak speed at ~40deg per frame regardless of option count, so 65 of 98
  frames advanced more than a full slice at 32 options and the wheel appeared
  to run backwards. Turn counts floor rather than round, since rounding up
  reintroduces the aliasing, and stay integral so the winner still lands on
  the detent.
- The spin launch now blends into the deceleration at a matched slope. The
  previous curve started at maximum speed, and a naive ease-in jumped from 18
  to 41deg per frame in a single frame.
- A damped recoil rocks the wheel into its detent, resolving to exactly zero
  so the landing and the flapper's rest position are unaffected.
- The pointer is drawn as SVG with a real outline and a tick as slices pass.
  clip-path had been discarding its border and shadow entirely.

Legibility:
- Labels flip by their rest-frame angle so every name reads upright in the
  final frame. This reverses an earlier decision to leave them slice-aligned;
  three of eight names rendered mirrored on a default wheel.
- Label text renders inside a promoted compositing layer, which switches
  Chromium from LCD sub-pixel to grayscale antialiasing. Sub-pixel fringes put
  2178 colored pixels into an all-grayscale theme and consumed 255 of 256
  palette entries. Promoting the one rotating container rather than each label
  keeps render time flat on dense wheels.
- Line boxes leave room for stacked Vietnamese diacritics and shrink to fit
  rather than clipping.
- Slice colors are ordered for colorblind separation, and the last slice
  avoids reusing the first slice's color when the option count leaves a
  remainder of one.
2026-07-25 15:18:04 +07:00
tiennm99 a6c65768f5 test: cover render concurrency limit and winner picker 2026-07-25 01:17:19 +07:00
tiennm99 376a7ae688 feat(renderer): add winner-reveal confetti burst
Deterministic frame-based confetti fired from the wheel center during the winner hold, seeded by winner index and colored from the theme palette. Uses Remotion's random() so each GIF frame renders identically.
2026-07-24 23:34:13 +07:00
tiennm99 8de459b131 fix(renderer): use consistent wheel label colors 2026-07-22 14:34:01 +07:00
tiennm99 566b06a461 feat(renderer): add local GIF render CLI 2026-07-15 15:50:34 +07:00
tiennm99 440ec899d2 fix(renderer): improve adaptive label readability 2026-07-15 14:42:33 +07:00
tiennm99 d57009e63b feat(renderer): add bold wrapped wheel labels 2026-07-15 12:21:14 +07:00
tiennm99 4632e87150 fix(renderer): apply single eased wheel spin 2026-07-07 00:11:32 +07:00
tiennm99 8ab45eb5ea fix(renderer): correct pie slice angle conversion 2026-07-06 23:44:41 +07:00
tiennm99 407d4f3a2c fix(renderer): align wheel pointer and labels 2026-07-06 23:12:51 +07:00
tiennm99 da92e9b443 fix: align wheel labels and compose env 2026-07-06 22:50:11 +07:00
tiennm99 3f5ed700bb feat: initialize wheelofnames API 2026-07-06 22:20:48 +07:00