KAZIK/docs/BOWLING.md
Artvasil09 7e081beeca feat: enhance bowling physics, audio, ball weight selection, and visitor cast routines
- Bowling feel: impulse contact solver, string pinsetter cycle, in-world lengthwise return rack and visible ball return lift

- Ball weights: 4/5/6/7 kg selection with equal launch work physics, momentum balance, and projected picking

- Audio: dedicated MCI channels with 7 synthesized waveaudio cues (release, rolling, impacts, applause, pinsetter, return)

- First-person animations: 33-frame player underarm throw/follow-through sequence (player-underarm-v2)

- Visitors: 10 persistent shared cast identities with exclusive reservations, activity transitions, and chair sit/rise animations

- Tests & verification: comprehensive native regression test suites and updated documentation

- Synchronize binaries and update launcher hash to 536E23BED4055562BC63871826FF44FFED037923A9BE8E0F591131DB4FBD104B
2026-11-04 18:44:12 +03:00

222 lines
14 KiB
Markdown

# Bowling Lounge
The bowling lounge is a walkable room with playable ten-frame bowling against a bot.
Enter through the wide opening in the north wall of the main casino, near the
slot-machine side of the hall. Walk through to enter or leave; no interaction
button or timed stair/elevator animation is used.
## Room
- Eight lanes with wooden approaches, gutters, ten pins and overhead displays.
- Three occupied lanes and five free lanes.
- Paired sofas, ball returns, dining booths, a staffed bar and circulating guests.
- All ten visitors use the shared character cast, including seated, drinking,
walking, pick-up, throwing and return animations. See `SHARED_CHARACTERS.md`.
- E sits on an available lounge seat or gets up. W/S also gets up.
- The bar uses the existing drink purchase flow and restores the room camera.
All room geometry, rendering, input, scoring and visitor simulation run in MASM.
Furniture is built from fixed world-space surfaces, not rotating room billboards.
Human sprites retain the game's existing 2.5D presentation.
## Player Game
Approach a free lane's foul line and press E on the play prompt. Occupied visitor
lanes stay unavailable. There is no entry fee or bankroll change. The camera and
room position are restored on exit; leaving abandons the current lane match.
The throw has two separate timing gates, both accepting either left mouse click
or a fresh Space press. First stop the oscillating direction arrow on its curved
horizontal scale. Then stop the vertical power indicator. The second press
commits the launch. Holding Space cannot accidentally consume both stages.
The arrow and meter update from elapsed time, not frame count. The ball follows
the committed parameters without additional random error.
Player and bot alternate complete frames. A second ball uses the remaining rack;
strikes, spares and tenth-frame bonuses use the shared native bowling scorer.
The lane monitor shows the two scores and current phase. Tab toggles a cumulative
ten-frame score sheet with every roll, X for a strike, / for a spare and - for a
miss; unresolved cumulative bonuses stay blank rather than inventing a total.
The sheet pauses the current throw. Escape or the Exit command returns to the room.
After a completed match, click or Space starts a fresh match.
The player game uses an arcade, not regulation-grade, collision approximation.
Each 16-ms fixed tick has four 4-ms collision substeps. Normal impulses include
relative velocity, restitution and the ball/pin mass ratio; overlap correction
does not apply a second impulse to opening contacts. Billiards-style contact
resolution is reused without changing the billiards module. Contact normals and
pin axes retain fractional precision and are renormalized after yaw: even tiny
negative impulses cannot turn an ellipsoid into a clipped rectangular slab. Fallen pins still
collide and transfer momentum. Kickbacks and the back cushion return pins to the
deck; friction damps sliding and spin, increasing after the initial collision
cascade. Bounds include the tilted pin's full silhouette. Gutters latch once entered.
The match uses a shopping-centre-style string pinsetter with cords intentionally
not drawn. Invisible slack tethers allow about 0.5 world units of travel from
each spot, damping outward motion only at the limit rather than pulling pins
back during the roll. Constraints run after every 4-ms contact substep.
A separate 5.2-second reset cycle closes a smooth metal pin shield, raises and
straightens the pins behind it, holds them aloft, lowers only the remaining pins
after the first ball (or all ten for a fresh rack), then opens the shield. A
first-ball miss leaves the standing rack alone, without running the motor.
Knocked-down pins remain physically represented aloft behind the hood, rather
than disappearing halfway up. The close camera eases back to frame the machine.
Rendering this cycle never changes scoring or advances the turn twice.
The sequence is based on Brunswick StringPin's documented standing-pin, strike,
second-ball and gutter cycles, with a front shield as on contemporary string machines:
https://brunswickbowling.com/uploads/document-library/Service-Manuals/Pinsetters/StringPin/BB_Service_Operations_Manual_StringPin_55-900004-000_En_Rev-06-16.pdf
Video reference: https://www.youtube.com/watch?v=EUwpfHz1Fp4
Impact excites a separate spring/gravity rocking angle: a small nudge can recover,
while a pin crossing the fall threshold is scored. Pins keep falling and hitting
one another after the ball passes; scoring waits for settlement with a bounded
timeout. Oriented ray/ellipsoid surfaces follow each pin's fall direction and
rolling yaw. This remains a floor-plane impulse solver with angular toppling,
not an unconstrained 3D rigid-body simulation. Other exhibition lanes retain
their existing automatic simulation; player and match opponent share the new
solver. Removed pins cannot be scored twice.
Private lane RNG chooses only the opponent's launch aim and power. The player
has a 33-frame first-person underarm sequence: the backswing leaves the view,
the camera bends toward the low release, and the actual ball begins moving at
the release event. The held-ball sprite is never drawn over the rolling sphere.
The rejected forward-push draft is not shipped. Direction/power controls remain
unchanged, with the power meter on the left, away from the player's arm.
The camera follows the ball and returns to the approach as the reset finishes.
The bot uses a full-body shared-cast throwing animation.
Each ball is a native shaded sphere with a procedural marble surface and
rotating finger-hole details, not a new neural or image runtime.
After every roll, including a first-ball miss and the last ball of the match,
the lane enters a 2.8-second ball-return phase. The ball disappears into the rear
pit, travels through the hidden return, then visibly emerges from the paired
return hood, rolls along the metal guides and stops in the tray. The camera
turns toward the correct left/right return unit and eases back to the approach;
the player's held-ball pose rises into view only after the tray ball is hidden.
The returning ball retains its committed colour; its storage slot stays empty.
Input during return cannot launch or lock the next throw. The existing scorer
advances exactly once before presentation; the final result overlay waits until
the ball has returned. Tab pauses this phase and its motor sound; Escape still
restores the saved room position and camera. The room's four return machines
have native hood/rail geometry and collision around their extended trays.
The return cycle does not change launch parameters or scores.
### Ball Mass
Before a match, the camera faces the actual paired return's ball storage rack.
Four balls (4 kg green, 5 kg red, 6 kg blue, 7 kg amber) rest lengthwise on two
metal rails beside the separate automatic return track. Click a sphere or its
projected weight plate to take it; Space takes the default/current ball. Keys
1-4 also select a ball. The weight command during aiming reopens this same rack.
Sphere picking and plates use the same world projection on either lane side.
The chosen ball's slot is empty while it is held, rolled or returned. A cached
single-frame palette transform colours only blue ball pixels in the hand atlas,
preserving skin, sleeve and alpha without duplicating the full animation pack.
Selecting a mass consumes its own event; it never also locks direction.
The default and opponent use 6 kg. The committed mass is frozen
for the whole throw; changing the next selection cannot alter an in-flight ball.
Each pin has mass 1600 grams, within the USBC standard pin range:
https://bowl.com/welcome/how-bowling-pins-are-made
The normal impulse solver uses Q16 inverse masses proportional to 1600/mass_g,
with signed 64-bit products to avoid overflow. It retains restitution 0.5 and
mass-weighted separation. A heavier ball loses a smaller fraction of its incoming
velocity at an otherwise identical contact. No random accuracy penalty, strike
bonus or mass-dependent floor-friction multiplier is added.
The gauge fixes the launch work W, calibrated to the original 6 kg release:
v_reference = 3 + 6 * power/100 m/s, W = 0.5 * 6 * v_reference^2 joules.
Actual speed is v_reference * sqrt(6 / mass_kg). The launch direction is normalized
so aiming away from the centre does not add extra speed. This is translational
launch work, not a complete model of arm biomechanics or rolling rotational energy.
Greater mass is therefore not an unconditional advantage: lighter balls launch
faster at equal work, while heavier balls resist collision deflection better.
The opponent and two nearby spectators use their existing persistent identities
and react to the result. Their restaurant duplicates are suppressed during the
match. Strikes, spares and strong throws trigger shared-cast cheering; misses
leave conversational poses rather than forcing a celebration.
Seven dedicated MCI waveaudio channels provide release, rolling, light/heavy pin
impacts, applause, the pinsetter motor and ball return. Impacts are driven by contact energy and rate-limited,
not triggered by rendering. The current sound-volume setting, including mute,
is applied before playback. Opening the score sheet stops the rolling channel;
leaving the match closes every alias. Effects are original seeded offline PCM
synthesis, not recordings taken from another game.
## Visitor Simulation
Each occupied lane owns a timer, rack mask, two players, frame counters and roll
history. The sequence is pick-up, approach/release, ball travel, pin impact,
pinsetter sweep, return and a pause before the next attempt/player.
Scoreboards use native ten-frame bowling scoring, including strike/spare bonuses
and tenth-frame bonus balls. These exhibition games do not change the bankroll.
The room owns its random state; visitor games do not consume the casino RNG.
Simulation pauses when the player is in an unrelated room. Artwork is loaded on
the first visible entrance/visit, retained for subsequent visits and freed with
the other hall assets. No city mesh or rooftop OpenGL pass is used here.
## Sources And Rebuild
- `src_graphics/bowling*.inc`: native room and lane simulation.
- `bowling_physics.inc`, `bowling_deck.inc`, `bowling_pinsetter.inc`, `bowling_return.inc`,
`bowling_weight.inc`,
`bowling_pin_shape.inc`, `bowling_scorecard.inc`,
`bowling_performance.inc`, `bowling_audio.inc`: match physics and presentation.
- `tools/generators/pack_bowling_feel.py`: first-person atlas normalization,
offline in-betweens and original PCM Foley.
- `assets/sprites/bowling/player-underarm-v2.png` and `.json`: retained built-in
imagegen source and generation brief. The packed 33-frame ANM1 asset lives in
`assets/animations/bowling/player-underarm-v2.bin`.
- `assets/audio/bowling/`: seven distributable WAV effects.
- `tools/generators/pack_bowling.py`: offline atlas packing, frame normalization,
in-between frames and furniture/collision tables.
- `assets/sprites/bowling/`: retained image-generation sources.
- `assets/animations/bowling/`: packed shipping assets.
Image-generation source briefs: twelve bowling-room material swatches (maple,
teal/coral textile, acoustic panels, mural, marble, metal and ball-return front);
a whole-body male approach/underarm-release/pick-up sequence; the corresponding
female bowler sequence; four distinct seated adult visitors in idle and
conversation/drinking poses. The edited female sheet preserves transparency.
These are original generated assets, not extracted FNAF artwork.
Run the offline packer with Python, then build the MASM executable normally.
## Checks
`tools/tests/check_hall_expansion.py --bowling-only` compiles fresh assembly and
checks entrance visibility, walkable access, both room transitions, lane/furniture
collision, sitting/restoration, 600 simulated seconds of lane lifecycles, perfect
game/all-spare/open-frame score vectors, and captures room/throw screenshots.
The additional `bowling_play_test.inc` covers LMB/Space input through native window
events, stage separation, deterministic impacts at two frame deltas, gutters,
empty/remaining racks, alternating turns, spare feedback, perfect games and the
tenth-frame bonuses, unresolved score cells, scene captures and exit restoration.
`bowling_feel_test.inc` additionally checks recoverable rocking, strong toppling,
opening and repeated fallen-pin contacts, late settlement, tenth-frame glyphs,
63 aim/power launches, invisible travel limits, complete tilted-body bounds,
automatic guard/hoist/respots, first-miss idle, duplicate-score prevention,
tiny-impact axis/silhouette regressions, per-substep travel limits,
eight pinsetter captures,
33 native hand captures and real muted MCI playback/cleanup.
`bowling_return_test.inc` covers the paired return positions and camera turns,
visible rise/coast, held-ball handoff, blocked throw input, pause/resume, final-ball
return before results, unchanged scores/turns, tray collision and exit restoration.
Eight native return captures also produce `build/hall-expansion/bowling-native-return.gif`.
`bowling_weight_test.inc` checks world-projected sphere/plate selection on both
lane pairs, non-overlapping kg labels, storage-rail collision, colour and alpha
preservation in source/scratch hand pixels, native mouse/key input, immutable
shot mass, bot independence, equal launch work, momentum balance, restitution and
non-increasing translational contact energy. An additional 84 complete high-power
throws across all four masses verify settlement and deck bounds without replacing
the simulated outcomes.
The handoff and follow-through convention follows the USBC release guidance:
https://bowl.com/welcome/developing-your-release-f7b6dd1aff6c612e4aea7fce9dbb9194
The complete hall suite includes the same bowling checks alongside existing
stairs, doors, rooftop, fortune-room, bar and cabaret regressions. `--parity`
compares the tested procedures with the shipping assembly object.
Screenshots are native framebuffer captures, not a substitute for final manual
playtesting on the target laptop.