# 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.