- 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
222 lines
14 KiB
Markdown
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.
|