feat: implement native billiards, scratch tickets, first-person dice, and city wake scene

- Billiards: add native Russian Pyramid & American Eight-Ball pool with physics prediction, controls, and regression tests
- Scratch Tickets: add dedicated counter, mat presentation, elliptical coin scraping, 4 ticket types, and payout effects
- First-Person Dice: add native dice scene, hand anchor frames, throwing animation, and table perspective
- City Wake & Weather: add arrival cutscene, alley geometry, weather/rain effects, and lighter/rocker animation
- World Dressing: add hall paintings, furniture packs, hall lighting, and scene regression tests
- Build & Tooling: update build.bat for modern MSVC toolchain, update launcher hash, and refresh shipping binaries
This commit is contained in:
Игорь Федосеев 2026-10-16 12:36:47 +03:00
parent 4a27ea0f44
commit ca62db53e9
152 changed files with 10743 additions and 301 deletions

5
.gitignore vendored
View file

@ -13,12 +13,17 @@ __pycache__/
# Generated build/test output; keep packaging source and shipping exe files.
/build/actions/
/build/arrival/
/build/billiards/
/build/buckshot/
/build/coin/
/build/dice/
/build/hall/
/build/icon_work/
/build/scratch/
/build/startup/
/build/tables/
/build/wheel/
/build/world/
/build/*.res
/build/payload_*.zip
/build/PokerGameSetup_base.exe

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,443 @@
[
{
"file": "neon-ru-v3.png",
"winning": 5,
"players": 15,
"fields": [
{
"x": 160,
"y": 339,
"w": 51,
"h": 51,
"area": 1999
},
{
"x": 228,
"y": 339,
"w": 51,
"h": 51,
"area": 2015
},
{
"x": 297,
"y": 339,
"w": 51,
"h": 51,
"area": 2012
},
{
"x": 366,
"y": 339,
"w": 51,
"h": 51,
"area": 2010
},
{
"x": 435,
"y": 339,
"w": 51,
"h": 51,
"area": 2032
},
{
"x": 133,
"y": 433,
"w": 65,
"h": 47,
"area": 2973
},
{
"x": 211,
"y": 433,
"w": 65,
"h": 47,
"area": 2976
},
{
"x": 290,
"y": 433,
"w": 65,
"h": 47,
"area": 2972
},
{
"x": 369,
"y": 433,
"w": 64,
"h": 47,
"area": 2962
},
{
"x": 447,
"y": 433,
"w": 64,
"h": 47,
"area": 2940
},
{
"x": 133,
"y": 496,
"w": 65,
"h": 47,
"area": 2977
},
{
"x": 211,
"y": 496,
"w": 65,
"h": 47,
"area": 2978
},
{
"x": 290,
"y": 496,
"w": 65,
"h": 46,
"area": 2965
},
{
"x": 369,
"y": 496,
"w": 64,
"h": 47,
"area": 2932
},
{
"x": 447,
"y": 496,
"w": 64,
"h": 47,
"area": 2926
},
{
"x": 133,
"y": 558,
"w": 65,
"h": 47,
"area": 2962
},
{
"x": 211,
"y": 558,
"w": 65,
"h": 47,
"area": 2975
},
{
"x": 290,
"y": 558,
"w": 65,
"h": 47,
"area": 2981
},
{
"x": 369,
"y": 558,
"w": 64,
"h": 47,
"area": 2951
},
{
"x": 447,
"y": 558,
"w": 64,
"h": 47,
"area": 2942
}
]
},
{
"file": "ruby-ru-v3.png",
"winning": 6,
"players": 18,
"fields": [
{
"x": 136,
"y": 329,
"w": 50,
"h": 54,
"area": 2138
},
{
"x": 202,
"y": 329,
"w": 50,
"h": 53,
"area": 2130
},
{
"x": 267,
"y": 329,
"w": 51,
"h": 54,
"area": 2138
},
{
"x": 333,
"y": 329,
"w": 50,
"h": 54,
"area": 2143
},
{
"x": 398,
"y": 329,
"w": 50,
"h": 54,
"area": 2122
},
{
"x": 461,
"y": 329,
"w": 50,
"h": 53,
"area": 2125
},
{
"x": 135,
"y": 424,
"w": 54,
"h": 46,
"area": 2424
},
{
"x": 201,
"y": 424,
"w": 52,
"h": 46,
"area": 2336
},
{
"x": 265,
"y": 424,
"w": 52,
"h": 46,
"area": 2359
},
{
"x": 329,
"y": 424,
"w": 52,
"h": 46,
"area": 2351
},
{
"x": 393,
"y": 424,
"w": 52,
"h": 46,
"area": 2335
},
{
"x": 457,
"y": 424,
"w": 53,
"h": 46,
"area": 2380
},
{
"x": 135,
"y": 487,
"w": 54,
"h": 46,
"area": 2432
},
{
"x": 201,
"y": 487,
"w": 52,
"h": 46,
"area": 2338
},
{
"x": 265,
"y": 487,
"w": 52,
"h": 46,
"area": 2362
},
{
"x": 329,
"y": 487,
"w": 52,
"h": 46,
"area": 2353
},
{
"x": 393,
"y": 487,
"w": 52,
"h": 46,
"area": 2349
},
{
"x": 457,
"y": 487,
"w": 53,
"h": 46,
"area": 2381
},
{
"x": 135,
"y": 550,
"w": 54,
"h": 46,
"area": 2423
},
{
"x": 201,
"y": 550,
"w": 52,
"h": 46,
"area": 2335
},
{
"x": 265,
"y": 550,
"w": 52,
"h": 46,
"area": 2361
},
{
"x": 329,
"y": 550,
"w": 52,
"h": 46,
"area": 2340
},
{
"x": 393,
"y": 550,
"w": 52,
"h": 46,
"area": 2350
},
{
"x": 457,
"y": 550,
"w": 53,
"h": 46,
"area": 2381
}
]
},
{
"file": "sea-ru-v3.png",
"winning": 4,
"players": 12,
"fields": [
{
"x": 200,
"y": 335,
"w": 53,
"h": 53,
"area": 2213
},
{
"x": 270,
"y": 335,
"w": 53,
"h": 53,
"area": 2213
},
{
"x": 339,
"y": 335,
"w": 53,
"h": 53,
"area": 2209
},
{
"x": 409,
"y": 335,
"w": 53,
"h": 53,
"area": 2212
},
{
"x": 147,
"y": 427,
"w": 79,
"h": 48,
"area": 3758
},
{
"x": 242,
"y": 427,
"w": 81,
"h": 48,
"area": 3836
},
{
"x": 338,
"y": 427,
"w": 80,
"h": 48,
"area": 3765
},
{
"x": 434,
"y": 427,
"w": 79,
"h": 48,
"area": 3758
},
{
"x": 147,
"y": 490,
"w": 79,
"h": 48,
"area": 3714
},
{
"x": 242,
"y": 490,
"w": 81,
"h": 48,
"area": 3797
},
{
"x": 338,
"y": 490,
"w": 80,
"h": 48,
"area": 3719
},
{
"x": 434,
"y": 490,
"w": 79,
"h": 48,
"area": 3718
},
{
"x": 147,
"y": 554,
"w": 79,
"h": 48,
"area": 3756
},
{
"x": 242,
"y": 554,
"w": 81,
"h": 48,
"area": 3843
},
{
"x": 338,
"y": 553,
"w": 80,
"h": 49,
"area": 3762
},
{
"x": 434,
"y": 554,
"w": 79,
"h": 48,
"area": 3766
}
]
}
]

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1,4 +1,11 @@
{
"cityWakeLine1": "Вставай, самурай,",
"cityWakeLine2": "пора дэпнуть все в казино",
"poolSelectTitle3D": "БИЛЬЯРД",
"poolSelectPyramid3D": "1 ПИРАМИДА",
"poolSelectEight3D": "2 ПУЛ ВОСЬМЕРКА",
"poolCallText3D": "ЗАКАЗ",
"poolPredictionText3D": "ПРОГНОЗ",
"coinTitle3D": "МОНЕТКА",
"coinHeadsText3D": "ОРЁЛ",
"coinTailsText3D": "РЕШКА",
@ -355,5 +362,45 @@
"pyrBadEndText3D": "НЕВЕРНЫЙ УДАР",
"pyrPlayerName3D": "ВЫ",
"pyrAIName3D": "ИИ",
"pyrSkipText3D": "ПРОПУСК"
"pyrSkipText3D": "ПРОПУСК",
"scratchTitle3D": "ЛОТЕРЕЯ",
"scratchGame3D": "ЗОЛОТАЯ ТРОЙКА",
"scratchBuy3D": "КУПИТЬ",
"scratchNext3D": "НОВЫЙ",
"scratchPrice3D": "ЦЕНА",
"scratchWin3D": "ПРИЗ",
"scratchLose3D": "БЕЗ ВЫИГРЫША",
"scratchWait3D": "ОТКРЫТО",
"scratchPoor3D": "МАЛО ФИШЕК",
"scratchMissing3D": "НЕТ СПРАЙТОВ",
"scratchHall3D": "E: ЛОТЕРЕЙНЫЕ БИЛЕТЫ",
"scratchRule3D": "3 ОДИНАКОВЫХ = ПРИЗ",
"scratchActive3D": "БИЛЕТ",
"scratchNumbers3D": "СЧАСТЛИВЫЕ ЧИСЛА",
"scratchRows3D": "ТРИ В РЯД",
"scratchInstant3D": "МИГ УДАЧИ",
"scratchNumberRule3D": "СОВПАДЕНИЕ = ПРИЗ ПОД ЧИСЛОМ",
"scratchRowRule3D": "3 ОДИНАКОВЫХ В ОДНОМ РЯДУ",
"scratchInstantRule3D": "ПРИЗ УКАЗАН ПОД ЗАЩИТОЙ",
"scratchLucky3D": "ВЫИГРЫШНЫЕ ЧИСЛА",
"scratchYours3D": "ВАШИ ЧИСЛА",
"scratchAmount3D": "ВАШ ПРИЗ",
"scratchNeon3D": "НЕОНОВАЯ УДАЧА",
"scratchRuby3D": "РУБИНОВЫЙ ДЖЕКПОТ",
"scratchSea3D": "ВОЛНА СОКРОВИЩ",
"scratchBonusRule3D": "ЧИСЛО ИЛИ БОНУС = ПРИЗ",
"scratchMatch3D": "СОВПАДЕНИЕ: ПРИЗ",
"scratchNeonDouble3D": "x2: ДВОЙНОЙ ПРИЗ",
"scratchNeonTen3D": "x10: ПРИЗ x10",
"scratchRubyDouble3D": "x2: ДВОЙНОЙ ПРИЗ",
"scratchRubyAll3D": "ВСЕ: ВСЕ ПРИЗЫ",
"scratchSeaDouble3D": "x2: ДВОЙНОЙ ПРИЗ",
"scratchSeaAll3D": "ВСЕ: ВСЕ ПРИЗЫ",
"scratchAll3D": "ВСЕ",
"scratchField3D": "ПОЛЕ",
"scratchKind3D": "ВИД БИЛЕТА",
"scratchCost3D": "ЦЕНА БИЛЕТА",
"scratchPayout3D": "ВЫПЛАТА",
"scratchNet3D": "ЧИСТЫЙ ИТОГ",
"scratchPending3D": "БИЛЕТ НЕ ОТКРЫТ"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 758 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View file

@ -0,0 +1,6 @@
{
"actor": "Use case: game-asset. Primary request: transparent 4 by 4 sprite animation atlas, exactly 16 equally spaced full-body poses of the SAME fictional homeless cyberpunk rocker, loosely reminiscent of Johnny Silverhand: long unwashed dark hair, scruffy beard, dark aviator sunglasses, weathered black leather vest under patched dark coat, faded grey shirt, worn jeans, battered boots, LEFT forearm a crude silver mechanical prosthesis. Friendly sardonic expression, no weapons. Consistent detailed hand-painted late-1990s Doom/Build-engine pixel art, not photographic, not smooth 3D render; muted cold skin and desaturated clothes with small warm highlights matching brick rainy night alley. Clean transparent gutters, no background or ground shadows. All poses face the viewer or turn slightly left with consistent proportions, camera and illumination, full boots visible. Exactly four columns and four rows read left to right. Frames 1-4 crouching on bent knees, leaning toward someone seated on the ground, raising RIGHT natural hand to wake them; frames 5-8 still crouched, subtle mouth speaking and beckoning hand gesture; frames 9-12 smoothly standing from crouch; frames 13-16 turning to his left and walking past the seated viewer. Shared cell coordinates, identical SCALE between poses: crouched poses must be shorter than standing, do NOT enlarge crouched bodies to fill the cell. All feet on the same cell baseline, maximum standing figure occupies 84 percent of cell height, crouched 55 percent, enough headroom. No text, no labels, no logo, no duplicates, no cropped limbs. True transparent background.",
"lighter": "Use case: game-asset. Primary request: first-person LEFT HAND opening and igniting a battered silver flip-top lighter, transparent 4 by 4 sprite animation sheet with exactly 16 equally spaced frames. Crisp late-1990s Doom/Build-engine shaded PIXEL ART, not photographic, not flat cartoon. Adult light skin left hand, black worn leather jacket sleeve entering from lower LEFT of every cell. Natural anatomically correct fingers and LEFT thumb on spark wheel. NO RIGHT HAND, NO CIGARETTE. The ignition flame is meant to light a separate cigarette supplied by the game. Camera looking down at the lighter. Same hand identity, scale, position anchors and warm/cool lighting across all cells, clean transparent margins. Sequence read left to right: frames 1-3 closed lighter raised, frames 4-6 lid opens and thumb rolls spark wheel with tiny sparks, frames 7-12 bright small warm yellow flame fluttering ABOVE nozzle near the upper centre of the cell, frames 13-14 thumb stops flame and lid closes, frames 15-16 closed lighter lowers a little. Lighter nozzle always near x=50 percent, y=20 percent of cell, fingers and wrist below it; no flipping hand orientation. Solid opaque skin/metal/leather, transparency ONLY outside silhouettes and tiny flame wisps. No text, lettering, border, shadow matte or background. One square sprite sheet, exactly 4 rows by 4 columns.",
"knees": "Use case: game-asset. Asset: transparent first-person foreground sprite, wide landscape composition. View of one's OWN bent knees and shins in faded charcoal-blue jeans while seated on the ground of a rainy alley, knees occupying bottom left and bottom right edges, a clear transparent wide gap through the centre and entire upper half, trouser legs descend out of the bottom edge naturally. NO HANDS, NO arms, NO other person, NO background, NO shoes in top area, NO cigarette, NO text. Detailed hand-painted late-1990s Doom / Build engine pixel art with crisp pixels, muted cool denim and subtle warm edge highlights; not photoreal or smooth render. Large creased bent kneecaps at left and right lower corners, believable seated first-person perspective, consistent player black jacket/jeans character. True transparent background outside legs.",
"mode": "built-in"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

View file

@ -0,0 +1,7 @@
{
"mode": "built-in",
"actor": "Use case: identity-preserve. Production retro 2.5D game sprite animation atlas. Reference image is the approved scruffy homeless Johnny Silverhand-like rocker: preserve EXACT face, shoulder-length dark hair, dark sunglasses, beard, chrome LEFT arm, dirty black leather long coat, worn dark jeans, brown boots, pixel-art shading and proportions. Create ONE transparent atlas of EXACTLY 32 complete isolated character poses, 4 columns by 8 rows, read left-to-right top-to-bottom, clean generous transparent gutters, consistent ground anchor and common scale (do NOT enlarge crouching poses). First 16 frames form a smooth crouching speaking gesture cycle: tiny gradual head nod, mouth movement, shoulders breathing, extended right hand gradually gestures and returns, left chrome hand on knee. Next 8 frames are one smooth rise from crouch to standing then gradually turn away: frame16 crouched lean forward,17 lift hips,18 half rise,19 nearly straight,20 standing front,21 quarter turn,22 three-quarter back,23 full back. Last 8 frames show a smooth walking-away full-body cycle seen from back with gently swinging coat and chrome left arm. On every turned/back-facing coat clearly show Cyberpunk 2077 SAMURAI's large red horned skull/flame emblem with small SAMURAI lettering across upper back, NOT a generic samurai warrior. No new props, no scenery, no cell outlines, no captions, no watermarks. Same gritty detailed pixel-art style as input, no photorealism. Entire boots visible every frame. Transparent background.",
"sleeve": "Use case: precise-object-edit. Input image is STYLE reference of the approved first-person male lighter-hand sprites, not a request for another hand. Generate ONE isolated LEFT FOREARM IN BLACK WORN LEATHER COAT SLEEVE ONLY, no hand, fingers, lighter or flame. This sleeve extends the cropped left wrist in a retro 2.5D first-person game. Tall 2:3 transparent canvas: top cuff opening at horizontal center (50% width) on the very TOP edge, broad enough to disappear underneath the existing wrist; forearm runs smoothly diagonally down and toward lower-left and extends out of the BOTTOM edge, not a floating amputated sleeve. Three-dimensional tapered cylinder with believable elbow perspective, detailed dark leather folds, stitching, subdued brown-grey highlights. Keep same gritty crisp pixel-art shaded texture and scale as reference, no photorealistic skin, no blur. Solid continuous silhouette from TOP to BOTTOM, transparent around sleeve. No black rectangular matte, no labels, no scenery, no detached objects.",
"alley": "Use case: stylized-concept. Asset type: material atlas for retro 2.5D rain-soaked alley geometry. ONE square image exactly TWO columns and TWO rows of four square opaque game textures, no gaps or borders. Top-left: old dark galvanized ventilation grille with closely spaced horizontal louvers, rusty narrow frame, grimy bolts. Top-right: flat weathered pasted-over concert posters on dirty grey paper, small torn edges layered INSIDE the tile, faded crimson and ivory punk-print graphics, no readable modern advertisements. Bottom-left: battered wooden shipping crate planks with seams, dark wet grain, worn nails and diagonal wooden brace. Bottom-right: rusty dark electrical junction cabinet face, recessed door, hinges, small latch, peeling green-grey paint, NO luminous screen. Detailed gritty Doom/Build-engine pixel-art materials, crisp textured clusters, not photorealism, not vector illustration. All four textures orthographic front-on, no background scenery or perspective scene, no shadows outside tiles, entire canvas opaque, consistent subdued rainy-night palette. Each material must fill its own entire square.",
"logo_reference": "https://www.cyberpunk.net/en/news/30631/pre-order-your-samurai-bomber-jacket-now"
}

View file

@ -0,0 +1,4 @@
{
"mode": "built-in",
"walk": "Use case: identity-preserve. Use reference ONLY for approved character identity and pixel-art style. Production game sprite sheet: exactly EIGHT full-body frames in a FOUR column TWO row atlas, generous transparent gutters, no extra row. SAME homeless Johnny Silverhand-like rocker, dark shoulder-length hair, scruffy beard, sunglasses, chrome LEFT arm, worn black leather LONG COAT, dark jeans, brown boots. Camera sees him FROM THE BACK in EVERY frame. Large red Cyberpunk 2077 SAMURAI horned skull/flame emblem centered on coat back, small SAMURAI name above. Eight successive frames of WALK-IN-PLACE animation: hips, knees, arms and coat hem move gradually; heel lifts, passing legs, alternate foot contacts. He DOES NOT move away from camera and DOES NOT SHRINK. Exactly SAME camera distance and SAME body scale in all eight cells, full standing head-to-foot height constant, boots on common ground baseline, same lighting. All frames occupy about 85% slot height. Crisp gritty detailed pixel art identical to reference, not photo. No scenery, no labels, no borders, no shadows outside silhouette, true transparent background. Keep full boots visible."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View file

@ -0,0 +1,6 @@
{
"mode": "built-in",
"hands": "Use case: identity-preserve. Input image is approved STYLE and hand identity reference, NOT the coin to copy. Make a production first-person RIGHT HAND AND COMPLETE BLACK LEATHER FOREARM sprite atlas for throwing TWO dice supplied separately by the game. NO DICE AND NO COINS in this image. Exact 16 poses, 4 columns by 4 rows, transparent gutters. Same adult light skin, correct five fingers, gritty detailed retro pixel-art shading, black worn leather sleeve as reference. Forearm continues to bottom-right cell edge naturally, never cut at wrist. Palm opens upward, cupped near upper-left of each cell, fingers angled away from viewer. Frames0-3: subtly shifting cupped palm, enough room in palm for two dice. Frames4-7: palm smoothly pushes forward and fingers open to release dice. Frames8-11: open forward hand follows through. Frames12-15: gradually relax fingers and retract. Same camera, scale, light and palm anchor at approximately x=40%,y=28% in every cell; hand and long sleeve fill lower-right. No objects, no scenery, no labels, no mattes, no borders, no extra rows. Crisp pixel clusters matching reference, not photorealistic. Transparent background.",
"table": "Reuses assets/animations/coin/table-v1.bin unchanged.",
"dice": "Reuses the original cubeCorners3D/cubeFaces3D/cubeColors3D/DrawTumblingDie3D; no generated replacement dice."
}

View file

@ -0,0 +1,8 @@
{
"mode": "built-in image_gen",
"asset": "hands-v2.png",
"generation": "exec-4dd19e3a-0d6e-445e-bf78-4c4446b3105d",
"prompt_specification": "Transparent 4 by 4 atlas, exactly sixteen consistent first-person right-hand poses in the same black leather cuff, crisp retro pixel-art. Preserve the reference dice identity: small ivory cubes with dark red pips. First four poses: upward cupped palm, two thumb-width dice physically resting together in the centre of the palm, tiny contact shadows and finger overlap, never on the thumb, wrist or fingertips. Remaining twelve poses: empty palm opening, throwing follow-through and retracting. Fixed character, camera, scale, lighting and materials; full forearm to bottom right. No scenery, labels or extra rows.",
"references": ["previous native dice-ready screenshot", "hands-v1.png"],
"note": "Specification retained from the preceding generation; this is not a verbatim tool-prompt transcript."
}

View file

@ -0,0 +1,6 @@
{
"mode": "built-in image_gen",
"intent": "generate",
"output": "wall-paintings-v6.png",
"prompt": "Use case: stylized-concept. Asset type: two wall decoration textures for a retro 2.5D casino interior. Create a single sheet with EXACTLY TWO separate square framed paintings in a clean 2-column 1-row layout with generous transparent gutters. Both are strictly front-facing orthographic, identical compact aged brass/gold carved frames, no perspective tilt, no floor, no drop shadow outside the frames, no glow, no words, no letters, no price tags. Left painting: an old American brick downtown street on a rainy evening with warm shop windows and a small red awning; muted teal sky, no modern skyscrapers. Right painting: an atmospheric painted still life of playing cards with indistinct unlettered card faces, red/ivory/green casino chips, a short glass on deep green felt, not a diagram or game UI. Hand-painted, visibly pixel-art shading and crisp silhouettes appropriate to a classic software-rendered casino game, richly detailed but readable when each complete framed painting is reduced to 128x128 pixels. Both complete frames fully visible, each image square and aligned, solid opaque paint/canvas/frame, genuinely transparent background outside and between the two paintings. These will mount flush to actual wooden walls, not float as camera-facing billboards."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View file

@ -0,0 +1,4 @@
{
"mode": "built-in",
"prompt": "Use case: game-asset. Asset type: transparent sprite atlas for a retro 2.5D casino billiards minigame. Create exactly sixteen separate billiard ball sprites in a clean 4 by 4 equally spaced grid, orthographic straight overhead view, identical radius and center anchor, no perspective. Row 1: white cue ball, solid yellow, solid blue, solid red. Row 2: solid purple, solid orange, solid green, solid burgundy. Row 3: solid black eight ball, yellow stripe on white ball, blue stripe on white ball, red stripe on white ball. Row 4: purple stripe on white ball, orange stripe on white ball, green stripe on white ball, burgundy stripe on white ball. Every colored object ball has a small blank ivory circular number badge centered on top; NO numerals or lettering (engine adds correct digits). Each stripe goes horizontally through center with white caps above and below. Crisp polished shaded pixel art, recognizable high contrast colors, highlight upper left, subtle shading lower right, sharp round contours. Ball-only, no cast shadow outside the ball, no debris, no labels, no cloth, no decoration. Transparent background. Consistent sprite size occupying 65 percent of each grid cell with wide transparent gutters. Square atlas."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 704 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View file

@ -0,0 +1,17 @@
{
"mode": "built-in image_gen",
"ticket": {
"file": "ticket-v1.png",
"prompt": "Use case: stylized-concept. Asset type: production pixel-art lottery scratch-ticket paper frame for a native retro casino game. Generate a single landscape rectangular ticket seen straight from above, aspect ratio 3:2, on a genuinely transparent background, isolated and unrotated. Same warm lighting and crisp detailed pixel clusters as a richly decorated 1990s casino game. Dark emerald and burgundy printed ornamental borders, fine gold-foil engraved trim, tiny clover and diamond motifs ONLY in the corners. A large clean warm white matte paper central area, entirely blank for the game to draw nine scratching zones. Leave a broad blank emerald header strip within the ticket and a small blank white footer strip. Slightly worn paper edges, tactile stock, subtly embossed decoration. No lettering, no numerals, no scratch windows, no symbols in the central area, no coin, no hands, no scenery, no perspective distortion, no drop shadow extending outside the paper. It must read as an actual printed lottery ticket, not a UI card or a painting."
},
"coin": {
"file": "coin-edge-v1.png",
"prompt": "Use case: stylized-concept. Production game sprite animation atlas: exactly FOUR identical small brass coins in one horizontal row, FOUR evenly spaced cells, same fixed scale and same centre anchor. Genuinely transparent background, no scenery or hands. A worn brass casino token tilted steeply up on its thin ridged edge, seen from a first-person overhead view of scratching a flat lottery ticket. Coin face is a narrow visible ellipse, NOT a flat front-facing circle. Its BOTTOM-CENTRE rim is the contact point touching paper; keep that exact bottom-centre point constant in all four poses. Four subtle scraping rock positions (-6 degrees, neutral, +6 degrees, neutral), not spinning/flipping. Detailed engraved diamond emblem with a rim of tiny beads, no text or numerals. Crisp retro pixel-art clusters matching 1990s detailed casino sprites, warm metal highlights, sharp readable reeded edge. A thin coin, not a chunky poker chip. Entire coin contained in each cell, no background shadow, no sparkles, no labels. Single atlas, exactly four poses, no extra rows."
},
"counter": {
"file": "counter-v1.png",
"prompt": "Use case: stylized-concept. Asset type: isolated native retro 2.5D casino furniture sprite, front elevation, full object, genuinely transparent background. A waist-height narrow wooden lottery ticket sales counter with a green leather writing top, brass trim, small display of neatly fanned emerald and burgundy paper scratch tickets, and a slim upright sign holder with a blank emerald face. No printed text anywhere. A small tray of brass coins next to the tickets. Warm casino lighting, detailed crisp 1990s pixel-art consistent with Doom-like billboard props, physically plausible furniture proportions. Entire counter legs and base visible, grounded feet, no backdrop, no people, no multiple objects separated as an atlas, no shadow beyond feet. This is one furniture object with its ticket display attached, not a whole room or decorative image."
},
"foil": {"file": "foil-v1.png", "prompt": "Use case: stylized-concept. Asset type: flat seamless material texture for a retro pixel-art scratch lottery coating. A full-frame opaque landscape swatch of matte SILVER scratch-off latex foil. Fine silvery grey grain, tiny graphite speckles, subtle diagonal security hatching, flat even illumination with restrained metallic highlights. No perspective, no objects, no paper edge, no frame, no letters, no symbols, no numerals, no gradients, no glare spots. Crisp detailed pixel-art texture designed to be readable when reduced to about 100 by 40 game pixels. The entire image is just the silver protective coating material."},
"preserved": "Original Clover symbols are reused; the coin hotspot is the bottom-centre rim, not its face."
}

View file

@ -0,0 +1,9 @@
{
"mode": "built-in image_gen",
"prompts": {
"scratch_table_prompt": "Use case: stylized-concept. Production background bitmap for a native retro casino scratch-ticket game, landscape FOUR-TO-THREE composition. A first-person view looking almost directly DOWN at a real lottery counter tabletop, not looking at a person across a gambling table. The entire central area (about 75 percent of image width and 70 percent of image height) is a clean empty dark emerald-green felt writing mat, perfectly flat and nearly orthographic, so a landscape lottery paper ticket can be composited there without floating. Walnut wood edging and restrained brass corner details around the outside. Along the extreme left edge: a small stack of emerald printed paper tickets and a brass coin tray, partly cropped, outside the clean centre. Along the extreme right edge: a warm desk lamp base and small paper slips, partly cropped, also outside the centre. Warm light from upper left, crisp detailed 1990s casino pixel-art clusters, no blur, same rich emerald/burgundy/gold material family as our lottery frame. No dealer, no people, no faces, no hands, no loose coin in the central area, no UI, no text, no numbers, no lottery ticket already placed in the central mat. Centre must be uninterrupted empty green surface extending behind the future ticket. No front-facing upright counter, no horizon, no room view, no gradients. This is a playable close-up tabletop background, not an illustration poster.",
"scratch_numbers_prompt": "Use case: stylized-concept. Asset type: retro pixel casino scratch-off paper ticket, one landscape 14:9 flat rectangle viewed exactly from above, full frame. Primary request: a new 'lucky numbers' lottery ticket background in powder blue, deep petrol and metallic silver security-print guilloche, with small embossed number-ball motifs only in four corners. Detailed 1990s adventure-game bitmap art, crisp pixels, paper grain, consistent with an old luxurious casino. Layout: narrow ornate border, dark blank title band at top 8%-19%, absolutely empty light ivory writing area from x8%-92% y28%-80%, a narrow empty ivory footer strip y87%-95%. No letters, no numerals, no scratch panels, no shadows cast outside paper, no perspective, no UI, no watermark. Fill image with the ticket itself; all decorations confined to border/corners so program can overlay live text and scratch surfaces.",
"scratch_rows_prompt": "Use case: stylized-concept. Asset type: retro pixel casino scratch-off paper ticket, single landscape 14:9 flat rectangle viewed exactly from above filling frame. Primary request: 'three in a row' themed scratch ticket background, vermilion/red, charcoal and antique gold, decorative embossed cherries and bells ONLY in four corners, subtle sunburst security-print border. Detailed 1990s adventure-game bitmap art, crisp pixels, fine paper grain, old luxurious casino. Layout: narrow ornate border, dark blank title band at top 8%-19%, absolutely empty light ivory writing area from x8%-92% y28%-80%, narrow empty ivory footer strip y87%-95%. No letters, no numbers, no scratch panels, no outside shadows, no perspective, no UI, no watermark. Decorations confined to border and corners so program can overlay live symbols and round scratch spots.",
"scratch_instant_prompt": "Use case: stylized-concept. Asset type: retro pixel casino scratch-off paper ticket, one landscape 14:9 flat rectangle viewed exactly from above, full frame. Primary request: instant-prize lottery ticket background, turquoise teal with sunshine yellow and silver, embossed radiant lucky stars and coins confined to four corner motifs and guilloche border. Detailed 1990s adventure-game bitmap art, crisp pixels, fine paper grain, old luxurious casino. Layout: narrow ornate border, deep teal blank title band at top 8%-19%, absolutely empty light ivory writing area from x8%-92% y28%-80%, narrow empty ivory footer strip y87%-95%. No letters, numerals, scratch panels, outside shadows, perspective, UI or watermark. Image is the ticket itself, with empty central area large enough for a single big scratch panel and live prize."
}
}

View file

@ -0,0 +1,27 @@
{
"mode": "built-in image_gen",
"intent": "text-localization",
"prompt_record": "Recovered edit briefs, not verbatim tool prompts; the exact pre-compaction tool arguments were unavailable.",
"shared_edit_brief": "Localize the supplied portrait scratch ticket into Russian. Preserve its illustrated theme, composition, borders and exact number and placement of silver scratch fields. Keep each field empty silver for runtime replacement. Remove the fixed dollar price and numerical maximum-prize advertising so it does not contradict the game's selectable prices and actual awards. Preserve a portrait 3:4 layout.",
"assets": [
{
"input": "codex-clipboard-a52ed500-1ee3-4ee3-bff5-dd0970bfd3d8.png",
"output": "neon-ru-v3.png",
"generated_file": "exec-3ca37508-f784-4007-af7e-b9c49fcabb28.png",
"edit_brief": "Retain the neon cyan/magenta city, chips and diamonds. Russian title: НЕОНОВАЯ УДАЧА. Five winning circles and fifteen player rectangles. Retain number-match and double/ten-times bonus semantics. Replace the numeric top prize with a generic large-prize heading."
},
{
"input": "codex-clipboard-b32c02a1-cd6a-4b39-995d-eecafadf9a67.png",
"output": "ruby-ru-v3.png",
"generated_file": "exec-255460ed-b60a-43ac-8144-5d8fd5731880.png",
"edit_brief": "Retain red rubies, gold ornament and original layout. Russian title: РУБИНОВЫЙ ДЖЕКПОТ. Six winning circles and eighteen player rectangles. Preserve double-prize and all-prizes bonus semantics without advertising a fixed prize ceiling."
},
{
"input": "codex-clipboard-ea66185b-a5a8-4081-b022-e7e04556bded.png",
"output": "sea-ru-v3.png",
"generated_file": "exec-3bec4802-64d4-4f4b-9be1-4c779ab4b49b.png",
"edit_brief": "Retain the blue tropical sea, treasure, ship and gold details. Russian title: ВОЛНА СОКРОВИЩ. Four winning circles and twelve player rectangles. Preserve double-prize and all-prizes bonus semantics without fixed numerical price or maximum-prize claims."
}
],
"packing": "Offline pack_scratch_portraits.py resizes artwork to 540x720 BGRA and extracts 20/24/16 exact silver-region ID masks. All interactive rendering and gameplay remain MASM."
}

View file

@ -0,0 +1,7 @@
{
"mode": "built-in image_gen",
"intent": "generate",
"output": "payout-chips-v1.png",
"transparent_background": true,
"prompt": "Use case: stylized-concept. Asset type: isolated game sprite, casino payout chips for a retro 2.5D pixel-art casino. Create ONE compact cluster of three short stacks of casino chips, one deep emerald with ivory edge marks, one red with ivory edge marks, one warm ivory with dark emerald edge marks, and two small brass tokens lying beside their bases. View is almost overhead, slightly tilted toward the player, matching an overhead green felt writing desk lit warmly from upper left. Entire cluster fully in frame, clearly separated object silhouette, transparent background including all empty space. No desk, no tray, no hands, no cards, no words, no denomination numerals, no borders, no glow, no floating parts. All stacks rest on the same contact plane and cast a short subtle contact shadow. Detailed hand-painted pixel-art edges, crisp distinguishable chip layers, restrained highlights, not glossy photorealistic 3D. Wide compact silhouette suitable for reduction to a 96 by 72 pixel sprite. This is a new asset, not a sprite sheet."
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

View file

@ -1,7 +1,21 @@
@echo off
setlocal
rem Check standard MASM32 paths first
rem Check Visual Studio / MSVC BuildTools first (modern Windows compatible)
set "MSVC_BIN=C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Tools\MSVC\14.50.35717\bin\Hostx64\x86"
set "SDK_LIB=C:\Program Files (x86)\Windows Kits\10\Lib\10.0.26100.0\um\x86"
set "UCRT_LIB=C:\Program Files (x86)\Windows Kits\10\Lib\10.0.26100.0\ucrt\x86"
set "VC_LIB=C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Tools\MSVC\14.50.35717\lib\x86"
if exist "%MSVC_BIN%\ml.exe" (
set "ML_EXE=%MSVC_BIN%\ml.exe"
set "LINK_EXE=%MSVC_BIN%\link.exe"
set LIB_PATHS=/LIBPATH:"%SDK_LIB%" /LIBPATH:"%UCRT_LIB%" /LIBPATH:"%VC_LIB%"
set "EXTRA_FLAGS=/NOLOGO /SUBSYSTEM:WINDOWS"
goto do_build
)
rem Check standard MASM32 paths
if exist "D:\MASM32\masm32\bin\ml.exe" (
set "ML_EXE=D:\MASM32\masm32\bin\ml.exe"
set "LINK_EXE=D:\MASM32\masm32\bin\link.exe"
@ -16,7 +30,6 @@ if exist "C:\masm32\bin\ml.exe" (
set "EXTRA_FLAGS=/SUBSYSTEM:WINDOWS /RELEASE /VERSION:4.0"
goto do_build
)
if exist "F:\MASM32\masm32\bin\ml.exe" (
set "ML_EXE=F:\MASM32\masm32\bin\ml.exe"
set "LINK_EXE=F:\MASM32\masm32\bin\link.exe"
@ -25,20 +38,6 @@ if exist "F:\MASM32\masm32\bin\ml.exe" (
goto do_build
)
rem Fallback to Visual Studio / MSVC BuildTools
set "MSVC_BIN=C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Tools\MSVC\14.50.35717\bin\Hostx64\x86"
set "SDK_LIB=C:\Program Files (x86)\Windows Kits\10\Lib\10.0.26100.0\um\x86"
set "UCRT_LIB=C:\Program Files (x86)\Windows Kits\10\Lib\10.0.26100.0\ucrt\x86"
set "VC_LIB=C:\Program Files (x86)\Microsoft Visual Studio\18\BuildTools\VC\Tools\MSVC\14.50.35717\lib\x86"
if exist "%MSVC_BIN%\ml.exe" (
set "ML_EXE=%MSVC_BIN%\ml.exe"
set "LINK_EXE=%MSVC_BIN%\link.exe"
set LIB_PATHS=/LIBPATH:"%SDK_LIB%" /LIBPATH:"%UCRT_LIB%" /LIBPATH:"%VC_LIB%"
set "EXTRA_FLAGS=/NOLOGO /SUBSYSTEM:WINDOWS"
goto do_build
)
echo [ERROR] No compatible MASM/MSVC assembler found!
goto err

View file

@ -8,7 +8,7 @@ namespace AssemblerCasinoLauncher
{
static class Program
{
const string ExpectedPokerSha256 = "7E7165F5B9287FCF0F7A7E8F526DD23C0DA5F3A6CF72F08D1A40EB548FFCD428";
const string ExpectedPokerSha256 = "3CE946233ABE1D9139501EB56A75B2BE0D9A99714048C5F0A56A150D35F2295C";
[STAThread]
static void Main()

Binary file not shown.

View file

@ -167,6 +167,8 @@ include src_graphics\unavailable_data.inc
include src_graphics\native_slots_data.inc
include src_graphics\native_clover_data.inc
include src_graphics\native_coin_data.inc
include src_graphics\native_dice_data.inc
include src_graphics\scratch_data.inc
.data
ALIGN 4
@ -3081,13 +3083,15 @@ die_pips_done:
DrawCrapsDie ENDP
InitDiceGame PROC
call LoadNativeDiceArt3D
mov diceExitQueued3D,0
mov dice3d_state, 0
mov dice3d_timer, 0
mov dice3d_win, 0
ret
InitDiceGame ENDP
HandleDiceInput PROC
HandleDiceRules3D PROC
cmp dice3d_state, 0
je dice_handle_idle
cmp dice3d_state, 1
@ -3233,121 +3237,10 @@ dice_handle_result:
invoke PlayCasinoSound3D, OFFSET soundClick, 0, 1
dice_done:
ret
HandleDiceInput ENDP
HandleDiceRules3D ENDP
RenderDiceGame PROC uses ebx esi edi
local kx:DWORD
; Mahogany frame & Deep Felt Green table
invoke DrawBox, 0, 0, 320, 240, 00140906h
invoke DrawBoxBorder, 6, 6, 308, 228, 000A3816h, 00FFD700h, 2
; Header
invoke DrawBoxBorder, 20, 12, 280, 26, 00072E13h, 00FFD700h, 2
invoke DrawString8x8, 116, 21, OFFSET textDiceTitle, 00FFFF00h
; Status
invoke DrawBoxBorder, 20, 44, 110, 18, 00072E13h, 001B4B22h, 1
invoke DrawString8x8, 24, 49, OFFSET textBankPrompt, 003CFF3Ch
invoke FormatNumber, bankroll, OFFSET hudNumBuffer
invoke DrawString8x8, 72, 49, OFFSET hudNumBuffer, 003CFF3Ch
invoke DrawBoxBorder, 190, 44, 110, 18, 00072E13h, 001B4B22h, 1
invoke DrawString8x8, 194, 49, OFFSET textBetPrompt, 00FFD700h
invoke FormatNumber, dice3d_bet, OFFSET hudNumBuffer
invoke DrawString8x8, 258, 49, OFFSET hudNumBuffer, 00FFD700h
; [1] רÒ
cmp dice3d_choice, 1
jne dice_even_inactive
invoke DrawBoxBorder, 16, 70, 88, 48, 00072E13h, 00FFFF00h, 2
invoke DrawString8x8, 30, 76, OFFSET textDiceEven, 00FFFF00h
invoke DrawString8x8, 22, 90, OFFSET textDiceEvenNums, 00FFD700h
invoke DrawString8x8, 26, 104, OFFSET textCoinSelected, 003CFF3Ch
jmp dice_odd_side
dice_even_inactive:
invoke DrawBoxBorder, 16, 70, 88, 48, 00072E13h, 001B4B22h, 1
invoke DrawString8x8, 30, 76, OFFSET textDiceEven, 00D0D8E0h
invoke DrawString8x8, 22, 90, OFFSET textDiceEvenNums, 0080B090h
dice_odd_side:
; [2] ÍÅ×ÅÒ
cmp dice3d_choice, 2
jne dice_odd_inactive
invoke DrawBoxBorder, 216, 70, 88, 48, 00072E13h, 00FFFF00h, 2
invoke DrawString8x8, 226, 76, OFFSET textDiceOdd, 00FFFF00h
invoke DrawString8x8, 224, 90, OFFSET textDiceOddNums, 00FFD700h
invoke DrawString8x8, 226, 104, OFFSET textCoinSelected, 003CFF3Ch
jmp dice_center_cubes
dice_odd_inactive:
invoke DrawBoxBorder, 216, 70, 88, 48, 00072E13h, 001B4B22h, 1
invoke DrawString8x8, 226, 76, OFFSET textDiceOdd, 00D0D8E0h
invoke DrawString8x8, 224, 90, OFFSET textDiceOddNums, 0080B090h
dice_center_cubes:
call RenderDiceThrow3D
cmp dice3d_state, 1
je dice_render_rolling
cmp dice3d_state, 2
je dice_render_result
jmp dice_render_keys
dice_render_rolling:
invoke DrawString8x8, 76, 138, OFFSET textDiceRolling, 00FFFF00h
jmp dice_render_keys
dice_render_result:
invoke DrawBoxBorder, 36, 130, 248, 36, 00072E13h, 00FFD700h, 1
invoke DrawString8x8, 44, 135, OFFSET textDiceSumHdr, 00FFD700h
invoke FormatNumber, dice3d_val1, OFFSET hudNumBuffer
invoke DrawString8x8, 92, 135, OFFSET hudNumBuffer, 00FFFF00h
invoke DrawString8x8, 110, 135, OFFSET textDicePlus, 00FFD700h
invoke FormatNumber, dice3d_val2, OFFSET hudNumBuffer
invoke DrawString8x8, 126, 135, OFFSET hudNumBuffer, 00FFFF00h
invoke DrawString8x8, 144, 135, OFFSET textDiceEqual, 00FFD700h
invoke FormatNumber, dice3d_sum, OFFSET hudNumBuffer
invoke DrawString8x8, 168, 135, OFFSET hudNumBuffer, 00FFFF00h
mov eax, dice3d_sum
and eax, 1
jnz dice_render_odd_res
invoke DrawString8x8, 186, 135, OFFSET textDiceEvenResult, 003CFF3Ch
jmp dice_render_outcome
dice_render_odd_res:
invoke DrawString8x8, 186, 135, OFFSET textDiceOddResult, 003CFF3Ch
dice_render_outcome:
mov eax, dice3d_win
test eax, eax
jz dice_render_loss_outcome
invoke DrawString8x8, 68, 150, OFFSET textDiceWin, 003CFF3Ch
invoke FormatNumber, dice3d_win, OFFSET hudNumBuffer
invoke DrawString8x8, 156, 150, OFFSET hudNumBuffer, 003CFF3Ch
invoke DrawString8x8, 188, 150, OFFSET textDiceWinMult, 003CFF3Ch
jmp dice_render_keys
dice_render_loss_outcome:
invoke DrawString8x8, 116, 150, OFFSET textDiceLose, 00FF6464h
dice_render_keys:
mov kx, 16
xor eax, eax
cmp dice3d_choice, 1
sete al
invoke DrawKeycap3D, kx, 192, OFFSET textDiceKey1, eax
add kx, eax
xor eax, eax
cmp dice3d_choice, 2
sete al
invoke DrawKeycap3D, kx, 192, OFFSET textDiceKey2, eax
add kx, eax
invoke DrawKeycap3D, kx, 192, OFFSET textDiceKeySpace, 1
add kx, eax
invoke DrawKeycap3D, kx, 192, OFFSET textKeyEsc, 0
RenderDiceGame PROC
call RenderNativeDice3D
ret
RenderDiceGame ENDP
@ -5140,6 +5033,8 @@ include src_graphics\scene_animation.inc
include src_graphics\native_slots.inc
include src_graphics\native_clover.inc
include src_graphics\native_coin.inc
include src_graphics\native_dice.inc
include src_graphics\scratch.inc
include src_graphics\graphic_profiles.inc
include src_graphics\city_arrival_scene.inc
include src_graphics\arrival_scene.inc
@ -5212,6 +5107,16 @@ dispatch_frame:
xor paused3D, 1
jmp engine_tick
leave_table:
cmp engineMode,MODE_SCRATCH
jne @F
call LeaveScratch3D
jmp engine_tick
@@:
cmp engineMode,MODE_DICE
jne @F
call DiceEscape3D
jmp engine_tick
@@:
call CardTableEscape3D
test eax, eax
jnz engine_tick
@ -5264,6 +5169,14 @@ check_walk_mode:
call RenderMinimap
jmp present_frame
check_slots_mode:
cmp engineMode,MODE_SCRATCH
jne @F
call HandleScratch3D
cmp engineMode,MODE_WALK
je check_walk_mode
call RenderScratch3D
jmp present_frame
@@:
cmp engineMode,MODE_CLOVER
jne @F
call HandleNativeClover3D
@ -5494,6 +5407,7 @@ engine_idle:
invoke Sleep, 50
jmp engine_loop
engine_shutdown:
call SettleScratchOnQuit3D
call UnlockMouse3D
cmp hRaycastThread, 0
je @F

Binary file not shown.

103
docs/BILLIARDS_CURRENT.md Normal file
View file

@ -0,0 +1,103 @@
# Native Billiards
The casino table opens a selection of free Russian pyramid and American
eight-ball pool. All game simulation, rules, input, rendering and prediction
run in x86 MASM. Python only normalizes generated art and verifies captured
frames; no Python or external physics engine ships as the game runtime.
## Eight-Ball Turn Regression
The previous implementation retained a default/previous called ball and pocket
for human shots, while the AI supplied a new call before striking. A normal
human pot could therefore pass the turn despite correct contact and no foul.
Native regression case 12 reproduced that failure through aim, Space, rendering
and actual fixed-step motion before the fix. Scoring also credited every pot
to the shooter instead of the owner of the solid/striped group.
Automatic pre-shot calls now come from the existing physics prediction; manual
ball and pocket overrides remain binding. Group credit is reconstructed from
the actual pocketed balls and assigned groups. No collision, rail, pocket,
friction, force or pyramid-rule formulas were changed for this correction.
## Controls
- Select a discipline with the mouse or 1/2.
- Left-click/drag on the cloth to aim; arrows provide fine aim. Shift makes
arrow aim slower. Up/down or the force slider changes shot power.
- Space or the shot button strikes. P or the guide button toggles prediction.
- Pyramid: right-click a ball, B or the cue button selects the cue ball.
The break uses the colored ball; Shift-click places it in the home area.
- Eight-ball: right-click a legal object ball or use the order button/B to
call it. Right-click a pocket or click the pocket indicator to call a pocket.
Without an explicit override, the shared physics preview supplies the obvious
intended ball and pocket before striking, even with the guide hidden. Ball and
pocket overrides are independent and reset after settlement, not before it.
An unknown automatic call is shown as a dash rather than a stale previous call.
After a foul, click a clear place on the cloth to position the cue ball.
- Use the exit button or the casino's Escape flow to leave the table.
## Rules
Free pyramid uses fifteen white numbered balls and one colored ball, with
any active ball eligible as cue after the break. The first player to eight
credited balls wins. Legal cue-ball pots count after contact. Incorrectly
potted balls are respotted; the incoming player may take one penalty ball
and choose who plays next. Invalid breaks offer acceptance or a partial
re-rack. Ordered cushion/crossing histories determine legal non-pot shots.
Eight-ball uses the white cue, seven solids, seven stripes and black eight.
Groups are assigned by a legal called pot after the break. A wrong first
contact, scratch or no post-contact cushion/pot gives ball in hand.
The eight must be called and pocketed in the nominated pocket after clearing
the player's group; an early eight loses. A break eight is respotted, not an
automatic win. Invalid non-scratch breaks offer acceptance or a new break.
Pocketed solids/stripes are credited to their group's player, independently of
who struck or whether a foul occurred. Once groups are assigned, this includes
balls pocketed earlier on the open table. The white and black balls never add
to the group count. A legal called pot keeps the shooter at the table; a miss
passes the turn and a foul gives the opponent ball in hand.
These are playable casino adaptations, not a complete tournament referee.
Shot-clock/lag/protests, airborne balls, jump shots, deliberate push shots,
and all special tournament options for scratch/eight on the break are not
implemented. Backspin, topspin, side spin and curved trajectories are also
not implemented. Do not describe this simulation as ideal real-world physics.
## Physics And Prediction
- Q16.16 positions/velocities; fixed 2 ms steps independent of frame timing.
- Equal-mass normal impulses, separation, restitution and rolling deceleration.
- Distinct narrow pyramid / wider pool pockets and rounded jaw colliders.
- Pocket AABB rejection precedes squared-distance math, preventing long-vector
overflow from deleting a ball at the opposite end of the table.
- Fine aim uses 4096 interpolated phases and 64-bit projection numerators.
- Preview calls the same `PoolStep3D` as actual play. It restores all ball
arrays, collision histories and physics event fields before returning.
- Cached preview depends on the layout, discipline, cue, direction and force.
It samples up to 6000 steps (12 seconds), showing cue/object paths including
rebounds. This is a bounded guide; a moving ball beyond that horizon is not
claimed to have reached its final position.
## Verification
`tools/tests/test_casino_actions.ps1 -SkipPreview` runs native MASM assertions,
including disappearance regression, all pockets, 100 breaks, pyramid scoring
and penalties, eight-ball groups/fouls/wins, choice UI, and preview/live parity.
`eightball_regression.inc` adds actual aim/Space/render/physics sequences for
consecutive human pots, both actors, hidden/visible guides, manual calls and
group-based credit. `test_billiards_module.ps1` runs these plus the pre-existing
pyramid/choice tests against fresh MASM code and the native graphics helpers.
`tools/tests/check_billiards.py --parity` checks actual framebuffer captures
and equality of tested versus shipping machine-code procedures.
Generated art and exact built-in prompts are under `assets/sprites/pyramid`.
Packed game assets are under `assets/animations/pyramid`.
## References
- [FBSR pyramid rules, September 2025](https://www.fbsrf.ru/sites/default/files/04-novaya_redakciya_pravil_piramidy_2025-09.pdf)
- [WPA Rules of Play, January 2026](https://www.wpapool.com/wp-content/uploads/2026/01/2026.01.02-WPA-Rules.pdf)
- [Pooltool physics model organization](https://pooltool.readthedocs.io/en/latest/resources/custom_physics.html)
Pooltool is a reference for separating motion and collision models, not a
runtime dependency. No Pooltool source was copied into the MASM game.

View file

@ -1,5 +1,10 @@
# Native Billiards
This is the historical first-implementation plan. Current disciplines,
controls, physics coefficients and verification are documented in
[BILLIARDS_CURRENT.md](BILLIARDS_CURRENT.md); use that document for the game
now shipping rather than the older controls and coefficients below.
The first playable implementation is in `src_graphics/billiards.inc`, entirely
MASM32. The simulation uses positions and velocities, not predetermined shots.

52
docs/CITY_WAKE_SCENE.md Normal file
View file

@ -0,0 +1,52 @@
# Alley Wake-Up
The Eblan Games intro leads into the exterior cinematic on every launch.
Runtime camera, weather, sprite playback, compositing and input remain MASM.
PNG generation and normalization are offline tools only.
The protagonist opens their eyes while seated on the alley ground. Bent knees
remain in the foreground while a scruffy cyberpunk rocker crouches nearby and
speaks. The agreed dialogue is shown as Russian subtitles, without synthesized
voice: "Вставай, самурай, пора дэпнуть все в казино".
The protagonist raises a cigarette, opens and ignites a flip-top lighter,
looks into the cloudy sky and exhales, then rises smoothly, turns right and discards the butt
into the existing projected dumpster mouth and follows the city route to the
guards. Profile selection still happens at the casino entrance. Enter can
skip to the guards; existing Escape handling is preserved.
The seated view faces east, toward the rocker. Dumpsters are south of the
protagonist, off-screen to their right. The separate look-right phase rotates
the camera only after standing; the throw then uses the real dumpster mouth.
Traffic timing follows the pre-alley duration, preserving the safe red-light
crossing after the longer wake-up sequence.
There are thirty-two grounded rocker poses and sixteen lighter poses. The
speaking cycle has sixteen distinct crouched poses, then eight rise/turn poses
and eight walking-away poses. The coat's red SAMURAI back emblem follows the
[official jacket reference](https://www.cyberpunk.net/en/news/30631/pre-order-your-samurai-bomber-jacket-now).
Character
normalization uses whole connected silhouettes rather than nominal atlas cells,
so neighbouring fragments cannot move the feet anchor. Generated assets and
exact prompts are under `assets/sprites/arrival/wake-*`; packed runtime assets
are under `assets/animations/arrival/wake-*`.
The lighter forearm is a separate continuous leather sleeve placed underneath
the animated cuff using measured per-frame anchors. It extends out of the
viewport rather than ending at a floating wrist. New vent/poster/crate/cabinet
materials are in `alley-materials-v1.bin`; wall-mounted fittings, drainage
pipes, stacked crates and a floor drain use actual ray/depth-tested surfaces.
The centre walking strip and the right-hand dumpster reveal remain clear.
`wake-v2-prompts.json` records all three built-in image-generation prompts.
Rain uses three visual-depth layers with different speeds, length and opacity.
Streaks blend into the rendered scene rather than replacing it with blue blocks.
Ground splashes are rejected behind nearer geometry. Weather reads cinematic
time, not the gameplay random generator. Looking vertically into the sky blends
the raycast view into the sky rather than abruptly switching projections.
Verification uses the actual native action-test renderer, camera continuity
samples, route/vehicle checks, profile transitions, packed-asset checks and
framebuffer sequences. `check_arrival_frames.py` writes wake/ignition preview
GIFs from those native captures. `check_city_wake.py --parity` checks dialogue,
anchors and equality of shipping/tested cinematic procedures.

32
docs/DICE_FIRST_PERSON.md Normal file
View file

@ -0,0 +1,32 @@
# First-Person Dice
The ivory/red identity and MASM triangle renderer are retained. Held dice are
now integrated into the first four hand poses, with skin contact and shading,
rather than painted as unrelated cubes over the palm. Once released, procedural
MASM cubes take over at anchors measured from the last held sprite.
The scene reuses the coin table/dealer art and adds sixteen generated hand
poses with a full black-leather cuff. Preparation lasts 240 ms, followed by the
original three-bounce curve remapped into the remaining 1360 ms. The unchanged
parity wager settles once at 1600 ms. The stationary top faces and explicit
Russian sum/payout strip reveal the result. Ready and result states do not
consume random numbers during rendering.
Rotation completes full revolutions with eased angular speed. The resting
orientation is a yaw on the cloth plane, viewed from above, without sideways
roll. A projected bottom-face contact shadow and soft ambient shadow attach
each settled cube to the felt. Opposite faces sum to seven.
Mouse buttons map into the original keyboard rules. Escape or the Exit button
queues departure during a paid throw; settlement occurs before returning to
the hall. W/S, arrows, 1/2 and Space remain supported.
All rendering, input, animation, loading and gameplay are MASM. The Python
script only normalizes a transparent sprite atlas offline. Built-in generation
specification and provenance: `assets/sprites/dice/prompts-v2.json`.
Verification: the native action suite covers 300 paid throws at four update
rates, queued exits, mouse controls, missing-art fallback, six checkpoints and
forty production-renderer animation frames, release-boundary snapshots and all
six top-face results. `check_dice_scene.py --parity`
compares shipped and tested procedures and checks native Russian result glyphs.

185
docs/SCRATCH_TICKETS.md Normal file
View file

@ -0,0 +1,185 @@
# Scratch Tickets
The runtime is MASM-only. Python only normalizes generated artwork and checks
native framebuffer captures; there is no browser or console implementation.
## Scene
A dedicated ticket counter sits at hall coordinates (29,7.5), against the
right-wing wall near Durak. Approach it from the north. It has a grounded sprite and a solid collision footprint. The
interaction opens an overhead writing mat without launching another
window. The tabletop, paper, silver foil, counter and four tilted-coin poses are original
built-in ImageGen assets. Symbols reuse the existing Clover art.
The cursor hotspot is the bottom-centre scraping rim, (32,60) in a 64x64 sprite.
Pointer coordinates use the presented viewport, including letterboxing. Left
button capture ends on mouse-up, lost capture, lost focus or scene exit. Mouse
events are processed individually, including gestures completed between ticks.
Strokes interpolate a narrow elliptical rim every three native pixels.
## Presentation
Purchasing slides the entire ticket onto the mat, including its live coating
and printed contents, with a paper-shaped contact shadow. Presentation lasts
900 ms; pointer scratching and view changes are blocked until it settles.
Portrait tickets arrive at full-ticket scale and smoothly zoom into the playing
fields over the final 350 ms. Paper/zoom use an elapsed-time smoothstep, not a
fixed animation-frame list. Exiting the scene cancels the presentation, not the
already-paid ticket.
Fresh scratches emit three silver fragments into a 64-slot cosmetic pool.
Positions/velocities use 8.8 fixed point, gravity advances in fixed 8 ms steps,
and fragments settle on the mat before expiring. A private visual seed means
particles never consume the ticket RNG. Hovering and already-erased coating
produce neither fragments nor scraping audio; scraping audio is throttled to
one short sound per 150 ms.
Positive gross payouts display a grounded chip stack beside the ticket and
five small tokens travelling toward the bank display. The cosmetic payout
clock lasts 1800 ms. Settlement itself remains immediate and single-shot;
the animation cannot issue money, delay saving or block exit/new purchases.
Portrait winning fields also receive a pale green substrate inside their exact
source-space masks. Covered numbers are never tinted through the coating.
The payout-chip sprite was generated with built-in ImageGen, then normalized
offline to 96x72 binary-alpha BGRA. Its exact prompt is stored in
`assets/sprites/scratch/prompts-v4.json`. Three original short PCM effects
(paper slide, scrape and payout tokens) are generated offline by
`tools/generators/pack_scratch_polish.py`. Rendering, animation clocks, input
gates, debris physics and audio triggers are all assembly.
Each coating has its own byte mask (up to 240x120, nine slots). Repeated strokes do not
increment coverage twice, gutters and paper cannot be scratched, and a panel
cleans its remaining fragments after 72 percent coverage. Particles use no
gameplay randomness. Rendering is deterministic and cannot reroll a ticket.
## Rules
Prices: 50, 200 or 500 fictional chips. These are price tiers, not ticket types.
The upper-right "VID BILETA" control opens a seven-row named catalogue with
thumbnails. A short click keeps its original press coordinates even if the
pointer moves before the next game tick. Escape closes the catalogue first,
then returns to the hall on a subsequent press.
The original four tickets retain their distinct artwork and rules:
- Golden Triple: nine rectangular areas; three identical symbols anywhere pay
the printed multiplier, including the stake. Exactly one winning triple or
no triples at all.
- Lucky Numbers: three winning numbers above six player numbers. A match pays
the amount printed below the matching player number. All numbers are 1..30;
winning numbers are distinct, with exactly one matching player number on a
winning ticket and none on a losing ticket. The 104x34 panels leave separate
printed labels for the winning-number row and player-number rows.
- Three in a Row: nine circular areas. Only three matching symbols in the same
horizontal row pay. A scattered or diagonal triple is not a win.
- Instant Luck: one large 240x120 area reveals the direct gross prize, including
zero for a losing ticket.
Three additional portrait tickets preserve the layouts of the user's supplied
images, localized into Russian using built-in ImageGen:
- Neon Fortune: five winning-number circles and fifteen player fields.
Matching numbers pay the amount below them; x2 pays twice the printed prize,
x10 pays ten times the printed prize.
- Ruby Jackpot: six winning-number circles and eighteen player fields.
Matching numbers and x2 work as above; the ALL marker pays every player prize.
- Treasure Wave: four winning-number circles and twelve player fields, with
matching numbers, x2 and ALL as on Ruby Jackpot.
The native bonus markers are x2, x10 and the Russian ALL abbreviation; the
adjacent native rules use the same markers. All-prizes overrides individual
awards, never paying the same prize twice. Winning numbers are distinct 1..60.
The original artwork's fixed prices and advertised maximum jackpots were
removed: the actual selected price and payout are shown by the native HUD.
Full-ticket preview preserves the 3:4 aspect ratio. Purchase automatically
zooms into the play area; the +/- control, Z and mouse wheel switch views.
Amounts are printed under player numbers in the enlarged view. Hovering a
fully uncovered field shows its actual award, never a covered field's prize.
Each supplied silver region has an exact source-space ID mask. Rounded circles
and decorated frames are not rectangular scratch surfaces. Scaling covers the
whole source footprint of each screen pixel, so both views can be fully scratched
and switching views cannot restore removed coating.
New tickets clear old coating particles, so the previous ticket cannot leave
scraps over the next one's opaque fields. Ticket types can change only before
purchase or after settlement.
Prices and type are locked while a paid ticket is active. The rules, paper
contents, outcome and amount are fixed at purchase, never while scratching.
| Outcome | Ideal 0..999 rolls | Gross Multiplier |
| --- | --- | --- |
| No prize | 600 | 0 |
| Clover | 100 | 1 |
| Lemon | 100 | 1 |
| Cherries | 110 | 2 |
| Bell | 50 | 3 |
| Diamond | 25 | 5 |
| Chest | 12 | 10 |
| Seven | 3 | 25 |
The original four tickets' ideal table has 89% gross return; the existing engine's range generator has
modulo bias, so this is not an exact measured return guarantee. The ticket is
charged once. Revealing all panels settles it once, with the existing billion-
chip balance cap (one panel for Instant Luck, nine for the other original types,
20/24/16 for the portrait types). Buttons
cannot buy a second ticket while one is active.
Portrait generation uses 0..999 outcome rolls: 600 lose, 300 have one number
match, 80 have x2. Neon uses the remaining 20 for x10. Ruby and Sea use 15 for
two matching numbers and 5 for all-prizes. One-match amounts are weighted
1/2/5/25 times price (Neon cutoffs 700/900/990; Ruby/Sea 650/850/980).
Two-match prizes each equal price; all-prizes fields each equal price/5.
This is fictional-chip gameplay, not a representation of real ticket odds.
Completed tickets show gross PAYOUT and signed NET RESULT (gross minus price)
separately. A 50-chip ticket paying 50 has net zero, not +50. Golden Triple
highlights its three matching symbols and prints price x multiplier = payout.
An unfinished ticket survives trips into the hall during the current launch.
On a normal application close its already-generated outcome is automatically
settled and the balance saved. It is not resumed as a partially scratched
ticket after relaunch. Forced termination can bypass normal shutdown handling,
as with other in-progress games; no new per-profile save format is introduced.
## Verification
`scratch_syntax.asm` checks the isolated module before the full build.
`test_scratch_module.ps1` is a fast native iteration harness: it links fresh
scratch MASM against a test-only copy of completed native helper code. Only
COFF symbol visibility changes in the copy; instructions and relocations are
not modified. It is not a substitute for the full action/shipping builds.
The native action suite checks capture/release/focus/negative input, viewport
mapping, hover versus held input, fast connected strokes, disconnected strokes,
coverage, hall-return preservation, all price tiers, 500 immutable ticket
layouts, another 200 layouts for each of the four kinds, rule-derived prizes,
type locks, circular clipping, full physical uncovering of every layout,
single settlement and the balance cap. An additional 500 generated boards per
portrait kind are checked against independently derived number/bonus payouts;
fixed bonus fixtures cover x2/x10/all-prizes. Tests also cover all 60 physical
fields, both scales, catalogue clicks after pointer movement, price selection,
paid-type locks, modal Escape and positive/zero/negative net results.
Presentation tests additionally compare 186 native captures at equal timestamps
with 10/15/30 ms updates, exercise blocked scratching during delivery, compare
25 payout frames, assert unchanged bankroll/RNG under cosmetic updates, check
single-shot sound triggers/cooldowns and expire all 64 particle slots.
The suite writes ready, covered,
partly scratched, completed, missing-art and per-kind frames.
Generated asset prompts: `assets/sprites/scratch/prompts-v1.json` and
`assets/sprites/scratch/prompts-v2.json`. Portrait provenance and recovered edit
briefs are in `assets/sprites/scratch/prompts-v3.json` (briefs are not verbatim
tool prompts). Native stroke sequences and the hall
counter can be captured with `tools/tests/test_scratch_probe.ps1`; its 114 native
frames include 26 scratch frames per portrait kind and view-switch coverage
preservation assertions. The probe
links the tested game's rendering procedures, not a Python recreation.
Mechanic references, not source-code or art dependencies:
[Scratch the Ticket](https://store.steampowered.com/app/4626940/Scratch_the_Ticket/)
and [Scritchy Scratchy](https://store.steampowered.com/app/3948120/Scritchy_Scratchy/?l=russian).
Number-match inspiration: the official National Lottery
[Double Match procedures](https://www.national-lottery.co.uk/c/files/scratchcards/doublematch.pdf~3).
Our outcomes and prices are independent; the three portrait designs originate
from images supplied by the user, not scraped from lottery websites.

56
docs/WORLD_DRESSING.md Normal file
View file

@ -0,0 +1,56 @@
# Hall and Street Dressing
The game still runs entirely in MASM32 assembly. The Python packer and capture
checker are offline developer tools, not another game implementation.
## Hall
- Standing eye height is 1.05 world units. Basement eye height remains 0.85,
including its stair elevation. Gameplay, table sizes and NPC routes are unchanged.
- Six framed paintings and eight sconces are actual depth-tested wall planes.
They stay attached to the architecture as the camera turns and do not occupy
the walking area. They do not use camera-facing furniture billboards.
- A static 256x256 light map is rebuilt when the hall map is initialized. It
contains the existing chandeliers, lounge lamps, bar lights and new wall lights.
Radial falloff is quadratic, and solid map tiles interrupt the light rays.
- A separate contact map darkens the carpet underneath lounge furniture. This
is not a full dynamic shadow renderer for every NPC and table.
- The lighting pass runs after the world but before the HUD. It computes a gain
per 2x2 area and shades each original pixel separately, retaining texture detail.
It is skipped for the basement, outside scenes and when explicitly disabled.
## Street
- Walking eye height is 1.15 plus the six-centimetre pavement elevation. Seated
eye height and the ignition/waking animation remain unchanged; the rising
phase interpolates to the new standing height.
- Building storeys are 1.8 units instead of 1.2. Their bases stay on the ground.
- Hydrants, mailboxes, newspaper boxes, bollards and parking meters have individual
dimensions, smaller than a standing person. Fence height is reduced as well.
Bushes and the discarded billboard benches remain disabled.
- Low curbs separate roadway and pavement. Paving is drawn in individual metre
sections, not one stretched street-length texture. Raised sidewalk props share
the curb's floor anchor. The anchor is recalculated after each pedestrian/car
update, so newly positioned walkers do not keep their old ground height.
Props beyond the raised section and road vehicles stay on the lower ground.
- Three iron drains sit beside the curb, outside the walking strip. Existing
window/lamppost light maps also shade the new paving.
## Artwork and Verification
`assets/sprites/hall/wall-paintings-v6.png` was produced with the built-in image
generator. Its prompt is recorded in `prompts-v6.json`. The offline packer adds
a small procedural iron material and writes `world-dressing-v6.bin` (three
128x128 BGRA frames). Loading validates dimensions; missing art is safely skipped.
Run `tools/tests/test_casino_actions.ps1 -SkipPreview` for native checks of
surface loading, light occlusion, repeatable baking, floor anchors, prop scale,
unchanged gameplay RNG/bank/map, existing interaction paths and framebuffer captures.
Then `tools/tests/check_world_dressing.py --parity` compares the tested and shipping
procedures and validates the native captures under `build/actions/world-*.bmp`.
`test_world_dressing_probe.ps1` is a quick development preview against a saved
prior-release object. It only changes COFF symbol visibility in a test copy and
links current dressing/surface routines without patching instructions. It is
not evidence of shipping integration; the complete native action suite and
shipping parity check are required before publication.

BIN
poker.exe

Binary file not shown.

BIN
poker.obj

Binary file not shown.

View file

@ -18,4 +18,4 @@ DebOut=
[FILES]
1=poker.asm
[SHOWFILE]
1=0000000000000000140600007403000000000000030000004836000012
1=000000000000000014060000740300000000000003000000F201000087

Binary file not shown.

View file

@ -8,7 +8,42 @@ PoolSqrt3D PROC squared:DWORD
ret
PoolSqrt3D ENDP
InitPool3D PROC uses ebx esi edi
InitPool3D PROC
call ResetPool3D
mov poolState3D,POOL_SELECT
ret
InitPool3D ENDP
StartBilliards3D PROC variant:DWORD
cmp variant,1
ja @F
mov eax,variant
mov poolVariant3D,eax
call ResetPool3D
call ClearKeys3D
@@:
ret
StartBilliards3D ENDP
ResetPool3D PROC uses ebx esi edi
mov poolTraceValid3D,0
mov poolCapture2Q8,480*480
mov poolMiddleLeft3D,157*FP_1
mov poolMiddleRight3D,163*FP_1
lea esi,poolPyramidJawX3D
lea edi,poolJawX3D
mov ecx,24
rep movsd
cmp poolVariant3D,0
je @F
mov poolCapture2Q8,1024*1024
mov poolMiddleLeft3D,153*FP_1
mov poolMiddleRight3D,167*FP_1
lea esi,poolEightJawX3D
lea edi,poolJawX3D
mov ecx,24
rep movsd
@@:
lea edi, poolVX3D
xor eax, eax
mov ecx, 32
@ -59,9 +94,10 @@ pool_rack_ball:
mov poolStill3D, 1
mov poolCalled3D, 1
mov poolCalledPocket3D, 0
mov poolCallManual3D,0
call InitPyramidState3D
ret
InitPool3D ENDP
ResetPool3D ENDP
; Symmetric equal-mass impulse plus separation; opening contacts never gain energy.
PoolCollide3D PROC uses ebx esi edi a:DWORD,b:DWORD
@ -182,24 +218,24 @@ PoolPocketBall3D PROC uses ebx esi ball:DWORD
pool_pocket_test:
mov eax, poolX3D[esi*4]
sub eax, poolPocketX3D[ebx*4]
cmp eax,2*FP_1
cmp eax,5*FP_1
jg pool_pocket_next
cmp eax,-2*FP_1
cmp eax,-5*FP_1
jl pool_pocket_next
sar eax, 8
imul eax, eax
mov ecx, eax
mov eax, poolY3D[esi*4]
sub eax, poolPocketY3D[ebx*4]
cmp eax,2*FP_1
cmp eax,5*FP_1
jg pool_pocket_next
cmp eax,-2*FP_1
cmp eax,-5*FP_1
jl pool_pocket_next
sar eax, 8
imul eax, eax
add eax, ecx
; Capture is deep inside the narrow mouth; jaws are separate colliders.
cmp eax, 480*480
cmp eax, poolCapture2Q8
ja pool_pocket_next
pool_pocket_capture:
mov poolActive3D[esi*4], 0
@ -398,9 +434,11 @@ pool_rail_y:
jle pool_rail_throat_y
cmp poolX3D[esi*4], 283*FP_1
jge pool_rail_throat_y
cmp poolX3D[esi*4], 157*FP_1
mov eax,poolMiddleLeft3D
cmp poolX3D[esi*4], eax
jl pool_rail_test_y
cmp poolX3D[esi*4], 163*FP_1
mov eax,poolMiddleRight3D
cmp poolX3D[esi*4], eax
jg pool_rail_test_y
pool_rail_throat_y:
mov ebx, 51*FP_1
@ -461,6 +499,12 @@ pool_friction_ball:
test eax,eax
jns @F
neg eax
@@:
add pyrRoll3D[esi*4],eax
mov eax,poolVY3D[esi*4]
test eax,eax
jns @F
neg eax
@@:
add pyrRoll3D[esi*4],eax
mov eax, poolVX3D[esi*4]
@ -504,5 +548,7 @@ pool_friction_next:
PoolStep3D ENDP
include src_graphics\pyramid_rules.inc
include src_graphics\eightball.inc
include src_graphics\pyramid_play.inc
include src_graphics\billiards_prediction.inc
include src_graphics\pyramid_render.inc

View file

@ -8,7 +8,18 @@ POOL_RADIUS equ FP_1*5/2
POOL_DIAMETER equ 5*FP_1
POOL_PENALTY equ 5
POOL_CHOICE equ 6
POOL_SELECT equ 7
InitPool3D PROTO
ResetPool3D PROTO
StartBilliards3D PROTO :DWORD
EightLegal3D PROTO :DWORD
EightCount3D PROTO
EightFinish3D PROTO
EightHand3D PROTO
EightAutoCall3D PROTO
EightSyncGroups3D PROTO
PoolPredict3D PROTO
PoolDrawPrediction3D PROTO
HandlePool3D PROTO
RenderPool3D PROTO
PoolStep3D PROTO
@ -44,6 +55,9 @@ poolPotted3D dd 0
poolBreak3D dd 1
poolCalled3D dd 1
poolCalledPocket3D dd 0
poolCallManual3D dd 0
poolUnsetText3D db "-",0
ALIGN 4
poolAtStart3D dd 0
poolFoul3D dd 0
poolAngle3D dd 0
@ -52,6 +66,38 @@ poolCharge3D dd 0
poolAccumulator3D dd 0
poolTime3D dd 0
poolStill3D dd 0
poolVariant3D dd 0
poolCapture2Q8 dd 480*480
poolMiddleLeft3D dd 157*FP_1
poolMiddleRight3D dd 163*FP_1
poolEightJawX3D dd 37*FP_1,30*FP_1,153*FP_1,167*FP_1,290*FP_1,283*FP_1
dd 37*FP_1,30*FP_1,153*FP_1,167*FP_1,290*FP_1,283*FP_1
poolEightJawY3D dd 50*FP_1,57*FP_1,50*FP_1,50*FP_1,57*FP_1,50*FP_1
dd 180*FP_1,173*FP_1,180*FP_1,180*FP_1,173*FP_1,180*FP_1
poolPyramidJawX3D dd FP_1*137/4,30*FP_1,157*FP_1,163*FP_1,290*FP_1,FP_1*1143/4
dd FP_1*137/4,30*FP_1,157*FP_1,163*FP_1,290*FP_1,FP_1*1143/4
poolPyramidJawY3D dd 50*FP_1,FP_1*217/4,50*FP_1,50*FP_1,FP_1*217/4,50*FP_1
dd 180*FP_1,FP_1*703/4,180*FP_1,180*FP_1,FP_1*703/4,180*FP_1
poolEightPack3D AnimPack3D <>
poolEightPath3D db "assets\animations\pyramid\pool-balls-v1.bin",0
poolSelectTitle3D db 193,200,203,220,223,208,196,0
poolSelectPyramid3D db 49,32,207,200,208,192,204,200,196,192,0
poolSelectEight3D db 50,32,207,211,203,32,194,206,209,220,204,197,208,202,192,0
poolCallText3D db 199,192,202,192,199,0
poolPredictionText3D db 207,208,206,195,205,206,199,0
poolTraceValid3D dd 0
poolTraceKey3D dd 0
poolTraceCount3D dd 0
poolTraceSteps3D dd 0
poolTracePotted3D dd 0
poolTraceFirst3D dd -1
poolTraceX3D dd 8192 dup(0)
poolTraceY3D dd 8192 dup(0)
poolTraceActive3D dd 512 dup(0)
poolTraceFinal3D dd 112 dup(0)
poolPhysicsBackup3D dd 112 dup(0)
poolHistoryBackup3D dd 80 dup(0)
poolScalarBackup3D dd 7 dup(0)
poolTitle3D db 207,211,203,32,47,32,56,0
poolPlayerText3D db 200,195,208,206,202,0
poolDealerText3D db 196,200,203,197,208,0

View file

@ -0,0 +1,239 @@
; Prediction runs the shipping fixed-step integrator, without settlement or IO.
; Every field touched by PoolStep3D is saved/restored before returning.
PoolPredict3D PROC uses ebx esi edi
local steps:DWORD,hash:DWORD
cmp poolState3D,POOL_AIM
jne predict_done
call PyramidDirection3D
mov eax,2166136261
lea esi,poolX3D
mov ecx,112
predict_hash:
xor eax,[esi]
imul eax,16777619
add esi,4
loop predict_hash
xor eax,pyrCue3D
imul eax,16777619
xor eax,poolPower3D
imul eax,16777619
xor eax,pyrDirX3D
imul eax,16777619
xor eax,pyrDirY3D
xor eax,poolVariant3D
mov hash,eax
cmp poolTraceValid3D,0
je predict_refresh
cmp eax,poolTraceKey3D
je predict_done
predict_refresh:
lea esi,poolX3D
lea edi,poolPhysicsBackup3D
mov ecx,112
rep movsd
lea esi,pyrRoll3D
lea edi,poolHistoryBackup3D
mov ecx,80
rep movsd
mov eax,poolFirst3D
mov poolScalarBackup3D,eax
mov eax,poolRail3D
mov poolScalarBackup3D[4],eax
mov eax,poolBreakRails3D
mov poolScalarBackup3D[8],eax
mov eax,poolPotted3D
mov poolScalarBackup3D[12],eax
mov eax,poolStill3D
mov poolScalarBackup3D[16],eax
mov eax,pyrValidEnd3D
mov poolScalarBackup3D[20],eax
mov eax,pyrRailSide3D
mov poolScalarBackup3D[24],eax
lea edi,poolVX3D
xor eax,eax
mov ecx,32
rep stosd
lea edi,pyrHistoryRails3D
mov ecx,48
rep stosd
mov poolFirst3D,-1
mov poolRail3D,0
mov poolBreakRails3D,0
mov poolPotted3D,0
mov pyrValidEnd3D,0
mov ebx,pyrCue3D
mov eax,pyrDirX3D
imul poolPower3D
shrd eax,edx,16
mov poolVX3D[ebx*4],eax
mov eax,pyrDirY3D
imul poolPower3D
shrd eax,edx,16
mov poolVY3D[ebx*4],eax
mov poolTraceCount3D,0
mov steps,0
predict_sample:
mov edi,poolTraceCount3D
shl edi,6
xor esi,esi
xor ebx,ebx
predict_sample_ball:
mov eax,poolX3D[esi*4]
mov poolTraceX3D[edi],eax
mov eax,poolY3D[esi*4]
mov poolTraceY3D[edi],eax
cmp poolActive3D[esi*4],0
je @F
bts ebx,esi
@@:
add edi,4
inc esi
cmp esi,16
jb predict_sample_ball
mov ecx,poolTraceCount3D
mov poolTraceActive3D[ecx*4],ebx
inc poolTraceCount3D
cmp steps,0
je predict_step
cmp poolStill3D,1
je predict_restore
cmp steps,6000
jae predict_restore
predict_step:
call PoolStep3D
inc steps
cmp poolStill3D,1
je predict_sample
test steps,15
jz predict_sample
jmp predict_step
predict_restore:
mov eax,steps
mov poolTraceSteps3D,eax
mov eax,poolFirst3D
mov poolTraceFirst3D,eax
mov pyrTarget3D,eax
mov eax,poolPotted3D
mov poolTracePotted3D,eax
lea esi,poolX3D
lea edi,poolTraceFinal3D
mov ecx,112
rep movsd
lea esi,poolPhysicsBackup3D
lea edi,poolX3D
mov ecx,112
rep movsd
lea esi,poolHistoryBackup3D
lea edi,pyrRoll3D
mov ecx,80
rep movsd
mov eax,poolScalarBackup3D
mov poolFirst3D,eax
mov eax,poolScalarBackup3D[4]
mov poolRail3D,eax
mov eax,poolScalarBackup3D[8]
mov poolBreakRails3D,eax
mov eax,poolScalarBackup3D[12]
mov poolPotted3D,eax
mov eax,poolScalarBackup3D[16]
mov poolStill3D,eax
mov eax,poolScalarBackup3D[20]
mov pyrValidEnd3D,eax
mov eax,poolScalarBackup3D[24]
mov pyrRailSide3D,eax
mov eax,hash
mov poolTraceKey3D,eax
mov poolTraceValid3D,1
predict_done:
ret
PoolPredict3D ENDP
PoolTraceLine3D PROC uses ebx esi edi x1:DWORD,y1:DWORD,x2:DWORD,y2:DWORD,tint:DWORD
local deltaX:DWORD,deltaY:DWORD,extent:DWORD
mov eax,x2
sub eax,x1
mov deltaX,eax
test eax,eax
jns @F
neg eax
@@:
mov extent,eax
mov eax,y2
sub eax,y1
mov deltaY,eax
test eax,eax
jns @F
neg eax
@@:
cmp eax,extent
jbe @F
mov extent,eax
@@:
cmp extent,0
je trace_line_done
xor esi,esi
trace_line_pixel:
mov eax,deltaX
imul esi
idiv extent
add eax,x1
mov ebx,eax
mov eax,deltaY
imul esi
idiv extent
add eax,y1
invoke DrawPixel,ebx,eax,tint
add esi,2
cmp esi,extent
jbe trace_line_pixel
trace_line_done:
ret
PoolTraceLine3D ENDP
PoolDrawPrediction3D PROC uses ebx esi edi
local sx:DWORD,sy:DWORD,ex:DWORD,ey:DWORD,tint:DWORD
call PoolPredict3D
xor ebx,ebx
trace_draw_ball:
cmp ebx,pyrCue3D
je trace_draw_selected
cmp ebx,poolTraceFirst3D
jne trace_draw_next
trace_draw_selected:
mov tint,008DB5A9h
cmp ebx,pyrCue3D
jne @F
mov tint,00E8EBD5h
@@:
mov esi,1
trace_draw_segment:
cmp esi,poolTraceCount3D
jae trace_draw_next
mov edi,esi
dec edi
mov eax,poolTraceActive3D[edi*4]
bt eax,ebx
jnc trace_draw_next
shl edi,4
add edi,ebx
mov eax,poolTraceX3D[edi*4]
sar eax,16
mov sx,eax
mov eax,poolTraceY3D[edi*4]
sar eax,16
mov sy,eax
mov eax,poolTraceX3D[edi*4+64]
sar eax,16
mov ex,eax
mov eax,poolTraceY3D[edi*4+64]
sar eax,16
mov ey,eax
invoke PoolTraceLine3D,sx,sy,ex,ey,tint
inc esi
jmp trace_draw_segment
trace_draw_next:
inc ebx
cmp ebx,16
jb trace_draw_ball
ret
PoolDrawPrediction3D ENDP

View file

@ -0,0 +1,26 @@
; Keep the cinematic's central walking strip clear; all fittings touch a wall
; or the floor and use the same per-pixel depth buffer as the dumpster geometry.
RenderCityAlley3D PROC
cmp cityDecorPack3D.pixels,0
je city_alley_done
; East wall: louvres, layered posters and a shallow junction cabinet.
invoke DrawCityVertical3D,0,FP_1*997/100,FP_1*111/5,FP_1*233/10,FP_1*5/4,FP_1*17/10,111
invoke DrawCityVertical3D,0,FP_1*997/100,FP_1*119/5,FP_1*124/5,FP_1*11/20,FP_1*27/20,112
invoke DrawCityBoxSides3D,FP_1*49/5,FP_1*199/20,FP_1*251/10,FP_1*128/5,FP_1*3/5,FP_1*7/5,114
invoke DrawCityHorizontal3D,FP_1*49/5,FP_1*199/20,FP_1*251/10,FP_1*128/5,FP_1*7/5,110
; West wall: service hatch and poster patch, mounted rather than billboards.
invoke DrawCityVertical3D,0,FP_1*803/100,FP_1*183/10,FP_1*381/20,0,FP_1*17/10,114
invoke DrawCityVertical3D,0,FP_1*803/100,FP_1*117/5,FP_1*121/5,FP_1*4/5,FP_1*17/10,112
; Drain pipes rise along masonry, with a short overhead connection.
invoke DrawCityBoxSides3D,FP_1*803/100,FP_1*811/100,FP_1*207/10,FP_1*104/5,0,FP_1*9/2,110
invoke DrawCityBoxSides3D,FP_1*989/100,FP_1*997/100,FP_1*259/10,26*FP_1,0,FP_1*11/2,110
invoke DrawCityBoxSides3D,8*FP_1,10*FP_1,FP_1*221/10,FP_1*222/10,FP_1*19/5,FP_1*39/10,110
; Stacked crates and a low pallet stay against the walls, never in our path.
invoke DrawCityBenchBlock3D,FP_1*803/100,FP_1*207/25,FP_1*201/10,FP_1*104/5,0,FP_HALF,113
invoke DrawCityBenchBlock3D,FP_1*804/100,FP_1*827/100,FP_1*203/10,FP_1*207/10,FP_HALF,FP_1*39/50,113
invoke DrawCityBenchBlock3D,FP_1*39/4,FP_1*497/50,FP_1*93/5,FP_1*191/10,0,FP_1*9/20,113
invoke DrawCityBenchBlock3D,FP_1*803/100,FP_1*413/50,FP_1*223/10,FP_1*231/10,0,FP_1*3/25,113
invoke DrawCityHorizontal3D,FP_1*167/20,FP_1*91/10,FP_1*213/10,FP_1*431/20,FP_1/200,111
city_alley_done:
ret
RenderCityAlley3D ENDP

View file

@ -1,19 +1,40 @@
CITY_BLACK equ 0
CITY_WAKE equ 1
CITY_SMOKE equ 2
CITY_TOSS equ 3
CITY_TURN_ALLEY equ 4
CITY_ALLEY equ 5
CITY_TURN_STREET equ 6
CITY_STREET equ 7
CITY_TURN_DOOR equ 8
CITY_DOOR equ 9
CITY_LIGHT equ 2
CITY_SMOKE equ 3
CITY_STAND equ 4
CITY_LOOK_BIN equ 5
CITY_TOSS equ 6
CITY_TURN_ALLEY equ 7
CITY_ALLEY equ 8
CITY_TURN_STREET equ 9
CITY_STREET equ 10
CITY_TURN_DOOR equ 11
CITY_DOOR equ 12
CITY_BLACK_MS equ 700
CITY_WAKE_MS equ 4500
CITY_LIGHT_MS equ 2300
CITY_SMOKE_MS equ 3600
CITY_STAND_MS equ 1800
CITY_LOOK_BIN_MS equ 1100
CITY_TOSS_MS equ 1500
CITY_TURN_ALLEY_MS equ 1200
CITY_PRE_ALLEY_MS equ CITY_BLACK_MS+CITY_WAKE_MS+CITY_LIGHT_MS+CITY_SMOKE_MS+CITY_STAND_MS+CITY_LOOK_BIN_MS+CITY_TOSS_MS+CITY_TURN_ALLEY_MS
CITY_TRAFFIC_OFFSET_MS equ CITY_PRE_ALLEY_MS-8900
CITY_BUILDING_STOREYS equ 7
CITY_STOREY_HEIGHT equ FP_1*6/5
CITY_STOREY_HEIGHT equ FP_1*9/5
HALL_STANDING_EYE equ FP_1*21/20
CITY_STANDING_EYE equ FP_1*23/20
LoadCityArrival3D PROTO
RenderCityArrival3D PROTO
HandleCityApproach3D PROTO
PositionCityCamera3D PROTO
DrawCityWakeActor3D PROTO
DrawCitySeatedLegs3D PROTO
DrawCityLighter3D PROTO
DrawCityWakeSubtitle3D PROTO
BlendCityWeatherPixel3D PROTO :DWORD,:DWORD,:DWORD,:DWORD
BlendCitySky3D PROTO :DWORD
FadeCityFrame3D PROTO :DWORD
CITY_LIGHT_W equ 512
CITY_LIGHT_H equ 256
@ -30,12 +51,12 @@ floorTexture3D dd 3
outdoorRender3D dd 0
cityLighting3D dd 1
cityLightReady3D dd 0
viewEye3D dd FP_1*17/20
viewEye3D dd HALL_STANDING_EYE
viewCeiling3D dd FP_1*5/2
cityPhase3D dd CITY_BLACK
cityTime3D dd 0
cityClock3D dd 0
cityDurations3D dd 700,3100,2400,1500,1200,5400,900,7500,900,2800
cityDurations3D dd CITY_BLACK_MS,CITY_WAKE_MS,CITY_LIGHT_MS,CITY_SMOKE_MS,CITY_STAND_MS,CITY_LOOK_BIN_MS,CITY_TOSS_MS,CITY_TURN_ALLEY_MS,5400,900,7500,900,2800
cityTilesPack3D AnimPack3D <>
cityHandsPack3D AnimPack3D <>
cityPropsPack3D AnimPack3D <>
@ -43,6 +64,19 @@ citySmokePack3D AnimPack3D <>
cityCarsPack3D AnimPack3D <>
cityBinPack3D AnimPack3D <>
cityCloudPack3D AnimPack3D <>
cityWakePack3D AnimPack3D <>
cityLighterPack3D AnimPack3D <>
cityKneesPack3D AnimPack3D <>
citySleevePack3D AnimPack3D <>
cityDecorPack3D AnimPack3D <>
cityWakePath3D db "assets\animations\arrival\wake-rocker-v2.bin",0
cityLighterPath3D db "assets\animations\arrival\wake-lighter-v1.bin",0
cityKneesPath3D db "assets\animations\arrival\wake-knees-v1.bin",0
citySleevePath3D db "assets\animations\arrival\wake-sleeve-v1.bin",0
cityDecorPath3D db "assets\animations\arrival\alley-materials-v1.bin",0
include src_graphics\city_lighter_anchors.inc
cityWakeLine1 db 194,241,242,224,226,224,233,44,32,241,224,236,243,240,224,233,44,0
cityWakeLine2 db 239,238,240,224,32,228,253,239,237,243,242,252,32,226,241,229,32,226,32,234,224,231,232,237,238,0
cityTilesPath3D db "assets\animations\arrival\city-tiles-v4.bin",0
cityHandsPath3D db "assets\animations\arrival\city-hands.bin",0
cityPropsPath3D db "assets\animations\arrival\city-props.bin",0
@ -69,6 +103,7 @@ CityProp3D STRUCT
asset dd ?
groundZ dd 0
CityProp3D ENDS
cityWakeActor3D CityProp3D <FP_1*97/10,FP_1*49/2,FP_1*7/10,FP_1*7/5,0,OFFSET cityWakePack3D>
CITY_PROP_COUNT equ 47
CITY_FIRST_CAR equ 9
CITY_TRAFFIC_EAST equ 14
@ -165,6 +200,7 @@ cityObjectDepth3D dd CITY_PROP_COUNT dup(0)
.data?
cityLightMap3D dd CITY_LIGHT_W*CITY_LIGHT_H dup(?)
cityDepthBuffer3D dd SCREEN_SIZE dup(?)
citySkyBuffer3D dd SCREEN_SIZE dup(?)
IFDEF CASINO_ACTION_TEST
cityPixelOwner3D dd SCREEN_SIZE dup(?)
ENDIF

View file

@ -3,6 +3,7 @@ include src_graphics\city_traffic.inc
include src_graphics\city_objects.inc
include src_graphics\city_bins.inc
include src_graphics\city_furniture.inc
include src_graphics\city_alley.inc
include src_graphics\city_skyline.inc
include src_graphics\city_lighting.inc
ValidateCityPack3D PROC uses esi pack:DWORD,widthPx:DWORD,heightPx:DWORD,count:DWORD
@ -30,6 +31,16 @@ LoadCityArrival3D PROC uses esi edi
invoke LoadAnimPack3D, OFFSET cityCarsPack3D, OFFSET cityCarsPath3D
invoke LoadAnimPack3D, OFFSET cityBinPack3D, OFFSET cityBinPath3D
invoke LoadAnimPack3D, OFFSET cityCloudPack3D, OFFSET cityCloudPath3D
invoke LoadAnimPack3D, OFFSET cityWakePack3D, OFFSET cityWakePath3D
invoke LoadAnimPack3D, OFFSET cityLighterPack3D, OFFSET cityLighterPath3D
invoke LoadAnimPack3D, OFFSET cityKneesPack3D, OFFSET cityKneesPath3D
invoke LoadAnimPack3D, OFFSET citySleevePack3D, OFFSET citySleevePath3D
invoke LoadAnimPack3D, OFFSET cityDecorPack3D, OFFSET cityDecorPath3D
invoke ValidateCityPack3D, OFFSET cityWakePack3D, 192,320,32
invoke ValidateCityPack3D, OFFSET cityLighterPack3D, 256,256,16
invoke ValidateCityPack3D, OFFSET cityKneesPack3D, 640,240,1
invoke ValidateCityPack3D, OFFSET citySleevePack3D, 256,384,1
invoke ValidateCityPack3D, OFFSET cityDecorPack3D, 128,128,4
invoke ValidateCityPack3D, OFFSET cityHandsPack3D, 256, 192, 16
invoke ValidateCityPack3D, OFFSET cityPropsPack3D, 256, 256, 6
invoke ValidateCityPack3D, OFFSET citySmokePack3D, 96, 128, 4
@ -112,23 +123,62 @@ CityEase3D ENDP
PositionCityCamera3D PROC uses ebx
mov playerX, (8*FP_1+FP_HALF)
mov playerY, (24*FP_1+FP_HALF)
mov playerAngle, 128
mov playerAngle, 0
mov viewHorizon3D, 240
mov viewEye3D,CITY_STANDING_EYE
cmp arrivalState3D, ARRIVAL_WALK
jne city_camera_at_door
mov ebx, cityTime3D
cmp cityPhase3D,CITY_STAND
ja city_camera_turn_alley
mov viewEye3D,FP_1*2/5
cmp cityPhase3D, CITY_WAKE
jne city_camera_turn_alley
mov viewHorizon3D, 1600
cmp ebx, 1800
jne city_camera_light
cmp ebx,2800
jb city_camera_done
sub ebx, 1800
invoke CityEase3D, ebx, 1300
imul eax, 1360
sub ebx,2800
invoke CityEase3D,ebx,1700
imul eax,80
shr eax, 16
sub viewHorizon3D, eax
add viewHorizon3D,eax
jmp city_camera_done
city_camera_light:
cmp cityPhase3D,CITY_LIGHT
jne city_camera_smoke
mov viewHorizon3D,320
jmp city_camera_done
city_camera_smoke:
cmp cityPhase3D,CITY_SMOKE
jne city_camera_stand
mov viewHorizon3D,320
invoke CityEase3D,ebx,1200
imul eax,1280
shr eax,16
add viewHorizon3D,eax
jmp city_camera_done
city_camera_stand:
cmp cityPhase3D,CITY_STAND
jne city_camera_done
invoke CityEase3D,ebx,1800
mov ebx,eax
imul eax,CITY_STANDING_EYE-FP_1*2/5
shr eax,16
add viewEye3D,eax
mov eax,ebx
imul eax,1360
shr eax,16
mov viewHorizon3D,1600
sub viewHorizon3D,eax
jmp city_camera_done
city_camera_turn_alley:
cmp cityPhase3D,CITY_LOOK_BIN
jne city_camera_bin_ready
invoke CityEase3D,ebx,CITY_LOOK_BIN_MS
shr eax,9
mov playerAngle,eax
jmp city_camera_done
city_camera_bin_ready:
mov playerAngle,128
cmp cityPhase3D, CITY_TURN_ALLEY
jb city_camera_done
jne city_camera_alley
@ -215,6 +265,12 @@ city_camera_bob:
idiv ecx
add viewHorizon3D, eax
city_camera_done:
cmp playerY,12*FP_1
jb @F
cmp playerY,13*FP_1
jae @F
add viewEye3D,FP_1*3/50
@@:
ret
PositionCityCamera3D ENDP
@ -262,32 +318,7 @@ city_sky_pixel:
ret
DrawCitySky3D ENDP
DrawCityRain3D PROC uses ebx esi
xor esi, esi
city_rain_drop:
mov eax, esi
imul eax, 137
mov ebx, cityClock3D
shr ebx, 4
sub eax, ebx
and eax, 511
imul eax, 5
shr eax, 3
mov ebx, eax
mov eax, cityClock3D
shr eax, 2
mov ecx, esi
imul ecx, 83
add eax, ecx
xor edx, edx
mov ecx, 240
div ecx
invoke DrawBox, ebx, edx, 1, 4, 00395468h
inc esi
cmp esi, 70
jb city_rain_drop
ret
DrawCityRain3D ENDP
include src_graphics\city_weather.inc
FadeCityFrame3D PROC uses ebx esi amount:DWORD
@ -397,6 +428,11 @@ city_cigarette_ash:
cmp u, eax
jb city_cigarette_shade
mov color, 00EC6325h
cmp cityPhase3D,CITY_LIGHT
jne city_cigarette_draw
cmp cityTime3D,900
jae city_cigarette_draw
mov color,00DDD9C9h
jmp city_cigarette_draw
city_cigarette_shade:
mov eax, v
@ -474,6 +510,33 @@ DrawCitySmokingHand3D PROC uses ebx esi edi
mov pinchX, 500
mov pinchY, 310
mov offsetY, 0
cmp cityPhase3D,CITY_LIGHT
jne city_hand_sky_lower
invoke CityEase3D,cityTime3D,600
neg eax
add eax,FP_1
imul eax,110
shr eax,16
mov offsetY,eax
jmp city_hand_toss
city_hand_sky_lower:
cmp cityPhase3D,CITY_SMOKE
jne city_hand_rising
invoke CityEase3D,cityTime3D,900
imul eax,240
shr eax,16
mov offsetY,eax
jmp city_hand_toss
city_hand_rising:
cmp cityPhase3D,CITY_STAND
jne city_hand_toss
invoke CityEase3D,cityTime3D,1800
neg eax
add eax,FP_1
imul eax,240
shr eax,16
mov offsetY,eax
jmp city_hand_toss
cmp cityPhase3D, CITY_WAKE
jne city_hand_toss
mov eax, 3100
@ -601,7 +664,13 @@ city_hand_draw_held:
imul eax, 55
sar eax, 16
add eax, cityCigaretteY3D
cmp cityPhase3D,CITY_LIGHT
jne city_hand_tip_smoke
cmp cityTime3D,1000
jb city_hand_unlit
city_hand_tip_smoke:
invoke DrawCityCigaretteSmoke3D, ebx, eax
city_hand_unlit:
invoke DrawCityCigarette3D, cityCigaretteX3D, cityCigaretteY3D, 320, 4
city_hand_draw_sprite:
; The opaque fingers cover the filter, with the paper extending out of the grip.
@ -682,6 +751,8 @@ DrawCitySmokingHand3D ENDP
DrawCityExhaledSmoke3D PROC uses ebx esi edi
mov esi, cityTime3D
sub esi,600
js city_exhale_done
cmp esi, 2300
jae city_exhale_done
mov eax, esi
@ -732,9 +803,12 @@ city_exhale_done:
ret
DrawCityExhaledSmoke3D ENDP
include src_graphics\city_wake_scene.inc
RenderCityArrival3D PROC uses ebx esi edi
local savedX:DWORD,savedY:DWORD,savedAngle:DWORD,savedMap:DWORD
local savedHorizon:DWORD,savedFloor:DWORD,savedOutdoor:DWORD,savedEye:DWORD,bar:DWORD
local skyBlend:DWORD
cmp arrivalState3D, ARRIVAL_WALK
jne @F
cmp cityPhase3D, CITY_BLACK
@ -762,41 +836,79 @@ RenderCityArrival3D PROC uses ebx esi edi
mov worldMap3D, OFFSET cityMap3D
mov floorTexture3D, 60
mov outdoorRender3D, 1
mov viewEye3D, FP_1*17/20
call UpdateCamera3D
call DrawCitySky3D
mov skyBlend,0
cmp arrivalState3D, ARRIVAL_WALK
jne city_render_world
cmp cityPhase3D, CITY_WAKE
cmp cityPhase3D,CITY_SMOKE
jne city_render_stand_sky
mov eax,cityTime3D
sub eax,550
js city_render_world
invoke CityEase3D,eax,650
shr eax,8
mov skyBlend,eax
jmp city_render_capture_sky
city_render_stand_sky:
cmp cityPhase3D,CITY_STAND
jne city_render_world
cmp cityTime3D, 1800
jb city_render_sky_smoke
invoke CityEase3D,cityTime3D,900
shr eax,8
neg eax
add eax,256
mov skyBlend,eax
city_render_capture_sky:
cmp skyBlend,256
je city_render_sky_smoke
lea esi,screenBuffer
lea edi,citySkyBuffer3D
mov ecx,SCREEN_SIZE
rep movsd
city_render_world:
call RenderCeilingAndFloor
call DrawCitySkyline3D
call RenderWalls
call PrepareCityDepth3D
call RenderStreetDressing3D
call RenderCityObjects3D
call RenderCityFurniture3D
call RenderCityBins3D
call RenderCityAlley3D
call RenderCityFacade3D
call RenderCityGuards3D
cmp arrivalState3D,ARRIVAL_WALK
jne @F
call DrawCityWakeActor3D
@@:
cmp skyBlend,0
je @F
invoke BlendCitySky3D,skyBlend
@@:
call DrawCityRain3D
cmp arrivalState3D, ARRIVAL_WALK
jne city_render_restore
cmp cityPhase3D, CITY_TOSS
ja city_render_restore
call DrawCitySeatedLegs3D
cmp cityPhase3D, CITY_WAKE
jne @F
call DrawCityExhaledSmoke3D
@@:
call DrawCitySmokingHand3D
cmp cityPhase3D, CITY_WAKE
jne city_render_restore
call DrawCityWakeSubtitle3D
jmp city_wake_eye_mask
@@:
call DrawCityLighter3D
call DrawCitySmokingHand3D
cmp cityPhase3D,CITY_SMOKE
jne city_render_restore
call DrawCityExhaledSmoke3D
jmp city_render_restore
city_render_sky_smoke:
call DrawCityRain3D
cmp cityPhase3D,CITY_SMOKE
jne city_render_restore
call DrawCitySmokingHand3D
call DrawCityExhaledSmoke3D
jmp city_render_restore
city_wake_eye_mask:
mov eax, cityTime3D
cmp eax, 900

View file

@ -2,6 +2,36 @@
cityRayX3D dd SCREEN_W dup(?)
cityRayY3D dd SCREEN_W dup(?)
.code
; Exterior-only materials do not consume or overwrite the hall's texture slots.
ResolveCitySurface3D PROC material:DWORD
mov eax,material
cmp eax,115
jb @F
cmp eax,118
jae @F
invoke ResolveDressingSurface3D,material
ret
@@:
cmp eax,111
jb city_surface_shared
cmp eax,115
jae city_surface_shared
cmp cityDecorPack3D.pixels,0
je city_surface_missing
sub eax,111
shl eax,16
add eax,cityDecorPack3D.pixels
mov ecx,128
mov edx,128
ret
city_surface_missing:
xor eax,eax
ret
city_surface_shared:
invoke ResolveWorldSurface3D,material
ret
ResolveCitySurface3D ENDP
PrepareCityDepth3D PROC uses ebx esi edi
lea edi, cityDepthBuffer3D
mov ebx, SCREEN_H
@ -78,7 +108,7 @@ DrawCityVertical3D PROC uses ebx esi edi axis:DWORD,coordinate:DWORD,loBound:DWO
local depth:DWORD,top:DWORD,bottom:DWORD,span:DWORD,column:DWORD
local sourceX:DWORD,stepY:DWORD,sourceY:DWORD,destination:DWORD,row:DWORD,texture:DWORD
local textureW:DWORD,textureH:DWORD
invoke ResolveWorldSurface3D,material
invoke ResolveCitySurface3D,material
test eax, eax
jz city_vertical_done
mov texture, eax
@ -240,7 +270,7 @@ DrawCityHorizontal3D PROC uses ebx esi edi xMin:DWORD,xMax:DWORD,yMin:DWORD,yMax
local left:DWORD,right:DWORD,top:DWORD,bottom:DWORD,corner:DWORD,pointX:DWORD,pointY:DWORD
local depth:DWORD,row:DWORD,column:DWORD,stepX:DWORD,stepY:DWORD,scaleX:DWORD,scaleY:DWORD,texture:DWORD
local textureW:DWORD,textureH:DWORD,nearClipped:DWORD
invoke ResolveWorldSurface3D,material
invoke ResolveCitySurface3D,material
test eax, eax
jz city_horizontal_done
mov texture, eax
@ -411,6 +441,20 @@ city_horizontal_pixel:
add esi, edx
mov ebx, texture
mov eax, [ebx+esi*4]
cmp outdoorRender3D,0
je city_horizontal_alpha
cmp material,72
jne city_horizontal_alpha
mov ecx,depth
shr ecx,14
neg ecx
add ecx,256
cmp ecx,55
jge @F
mov ecx,55
@@:
invoke ShadeCityGround3D,eax,pointX,pointY,ecx
city_horizontal_alpha:
cmp material, 104
jb @F
cmp eax, 80000000h

View file

@ -0,0 +1,2 @@
cityLighterCuffX3D dd 66,64,65,129,66,66,65,65,65,65,65,65,63,63,64,63
cityLighterCuffY3D dd 232,232,232,224,220,220,222,222,222,222,222,222,219,215,215,215

View file

@ -353,6 +353,7 @@ IFDEF CASINO_ACTION_TEST
ENDIF
call AnimateCityCars3D
call AnimateCityPedestrians3D
call UpdateStreetGround3D
xor esi, esi
city_objects_depth:
imul ebx, esi, SIZEOF CityProp3D

View file

@ -2,6 +2,11 @@
AnimateCityCars3D PROC uses ebx esi edi
local trafficClock:DWORD
mov eax, cityClock3D
; Keep the crossing's safe stopped positions when the wake-up gets longer.
sub eax,CITY_TRAFFIC_OFFSET_MS
jns @F
xor eax,eax
@@:
; A red-light interval lets the protagonist cross from the alley.
; Resume from the stopped position instead of jumping forward in world space.
cmp eax,7000

View file

@ -0,0 +1,193 @@
DrawCityWakeActor3D PROC uses ebx
mov cityWakeActor3D.posX,FP_1*97/10
mov cityWakeActor3D.posY,FP_1*49/2
cmp cityPhase3D,CITY_WAKE
jne wake_actor_depart
mov eax,cityTime3D
cmp eax,2800
jae wake_actor_straighten
xor edx,edx
mov ecx,175
div ecx
and eax,15
jmp wake_actor_draw
wake_actor_straighten:
sub eax,2800
imul eax,8
xor edx,edx
mov ecx,1700
div ecx
cmp eax,7
jbe @F
mov eax,7
@@:
add eax,16
jmp wake_actor_draw
wake_actor_depart:
cmp cityPhase3D,CITY_LIGHT
jne wake_actor_done
mov eax,cityTime3D
sub eax,350
jns @F
xor eax,eax
@@:
invoke CityEase3D,eax,1950
imul eax,3
sub cityWakeActor3D.posY,eax
mov eax,cityTime3D
xor edx,edx
mov ecx,90
div ecx
and eax,7
add eax,24
wake_actor_draw:
mov cityWakeActor3D.frame,eax
invoke DrawCityObject3D,OFFSET cityWakeActor3D,0
wake_actor_done:
ret
DrawCityWakeActor3D ENDP
DrawCitySeatedLegs3D PROC uses ebx
cmp cityPhase3D,CITY_LIGHT
jbe wake_legs_seated
cmp cityPhase3D,CITY_SMOKE
jne wake_legs_done
invoke CityEase3D,cityTime3D,900
imul eax,300
shr eax,16
add eax,240
mov ebx,eax
jmp wake_legs_draw
wake_legs_seated:
mov ebx,240
wake_legs_draw:
invoke DrawAnimSprite3D,OFFSET cityKneesPack3D,0,0,ebx,640,240
wake_legs_done:
ret
DrawCitySeatedLegs3D ENDP
DrawCityLighter3D PROC uses ebx esi edi
local px:DWORD,py:DWORD,sleeveX:DWORD,sleeveY:DWORD
cmp cityPhase3D,CITY_LIGHT
jne wake_lighter_done
mov eax,cityTime3D
imul eax,16
xor edx,edx
mov ecx,2300
div ecx
cmp eax,15
jbe @F
mov eax,15
@@:
mov esi,eax
mov cityCigaretteX3D,500
mov eax,cityClock3D
shr eax,3
and eax,511
mov eax,tableCos[eax*4]
sar eax,14
add eax,310
mov edi,eax
invoke CityEase3D,cityTime3D,600
neg eax
add eax,FP_1
imul eax,110
shr eax,16
add eax,edi
mov cityCigaretteY3D,eax
mov eax,cityCigaretteX3D
sub eax,40
sub eax,110
mov px,eax
mov eax,cityCigaretteY3D
sub eax,40
add eax,8
sub eax,55
mov py,eax
cmp cityTime3D,1800
jb @F
mov eax,cityTime3D
sub eax,1800
invoke CityEase3D,eax,500
imul eax,220
shr eax,16
add py,eax
@@:
; Measured cuff anchors join the real forearm underneath each animated wrist.
mov eax,cityLighterCuffX3D[esi*4]
imul eax,220
shr eax,8
add eax,px
sub eax,110
mov sleeveX,eax
mov eax,cityLighterCuffY3D[esi*4]
imul eax,220
shr eax,8
add eax,py
sub eax,20
mov sleeveY,eax
invoke DrawAnimSprite3D,OFFSET citySleevePack3D,0,sleeveX,sleeveY,220,330
invoke DrawAnimSprite3D,OFFSET cityLighterPack3D,esi,px,py,220,220
wake_lighter_done:
ret
DrawCityLighter3D ENDP
DrawCityWakeSubtitle3D PROC
cmp cityTime3D,1100
jb wake_subtitle_done
cmp cityTime3D,4300
jae wake_subtitle_done
invoke DrawBox,0,205,320,35,00151B1Dh
invoke DrawString8x8,92,211,OFFSET cityWakeLine1,00E6DED2h
invoke DrawString8x8,60,225,OFFSET cityWakeLine2,00E6DED2h
wake_subtitle_done:
ret
DrawCityWakeSubtitle3D ENDP
; Sky-only raycasting cannot represent a vertical view. Blend the pitched world
; into the already rendered sky instead of popping between the two projections.
BlendCitySky3D PROC uses ebx esi edi amount:DWORD
local inverse:DWORD,red:DWORD,green:DWORD
mov ebx,amount
mov eax,256
sub eax,ebx
mov inverse,eax
xor esi,esi
wake_sky_blend:
mov eax,screenBuffer[esi*4]
mov edi,citySkyBuffer3D[esi*4]
movzx ecx,al
imul ecx,inverse
mov edx,edi
and edx,255
imul edx,ebx
add ecx,edx
shr ecx,8
mov red,ecx
movzx ecx,ah
imul ecx,inverse
mov edx,edi
shr edx,8
and edx,255
imul edx,ebx
add ecx,edx
shr ecx,8
shl ecx,8
mov green,ecx
shr eax,16
and eax,255
imul eax,inverse
shr edi,16
and edi,255
imul edi,ebx
add eax,edi
shr eax,8
shl eax,16
or eax,green
or eax,red
mov screenBuffer[esi*4],eax
inc esi
cmp esi,SCREEN_SIZE
jb wake_sky_blend
ret
BlendCitySky3D ENDP

View file

@ -0,0 +1,184 @@
; All weather uses visual time and stable seeds, never gameplay RNG.
BlendCityWeatherPixel3D PROC uses ebx esi edi px:DWORD,py:DWORD,tint:DWORD,opacity:DWORD
cmp px,640
jae weather_pixel_done
cmp py,480
jae weather_pixel_done
mov esi,py
imul esi,640
add esi,px
mov ebx,screenBuffer[esi*4]
mov edi,256
sub edi,opacity
movzx eax,bl
imul eax,edi
mov ecx,tint
and ecx,255
imul ecx,opacity
add eax,ecx
shr eax,8
mov edx,eax
movzx eax,bh
imul eax,edi
mov ecx,tint
shr ecx,8
and ecx,255
imul ecx,opacity
add eax,ecx
shr eax,8
shl eax,8
or edx,eax
shr ebx,16
and ebx,255
imul ebx,edi
mov eax,tint
shr eax,16
and eax,255
imul eax,opacity
add eax,ebx
shr eax,8
shl eax,16
or eax,edx
mov screenBuffer[esi*4],eax
weather_pixel_done:
ret
BlendCityWeatherPixel3D ENDP
DrawCityRain3D PROC uses ebx esi edi
local layer:DWORD,px:DWORD,py:DWORD,lengthPx:DWORD,opacity:DWORD
local radius:DWORD,alpha:DWORD,rowStart:DWORD,age:DWORD,groundDepth:DWORD
mov layer,0
rain_layer:
mov eax,layer
imul eax,3
add eax,4
mov lengthPx,eax
mov eax,layer
imul eax,28
add eax,50
mov opacity,eax
xor esi,esi
rain_drop:
mov eax,layer
imul eax,3
add eax,3
imul eax,cityClock3D
imul ecx,esi,829
add eax,ecx
xor edx,edx
mov ecx,7680
div ecx
shr edx,4
mov py,edx
imul eax,esi,137
imul ebx,layer,211
add eax,ebx
mov ecx,cityClock3D
shr ecx,6
sub eax,ecx
and eax,1023
imul eax,640
shr eax,10
mov px,eax
xor edi,edi
rain_streak:
mov eax,edi
shr eax,2
mov ebx,px
sub ebx,eax
mov ecx,py
add ecx,edi
invoke BlendCityWeatherPixel3D,ebx,ecx,00B2C5CDh,opacity
inc edi
cmp edi,lengthPx
jb rain_streak
inc esi
cmp esi,36
jb rain_drop
inc layer
cmp layer,3
jb rain_layer
mov eax,viewHorizon3D
add eax,50
cmp eax,310
jge @F
mov eax,310
@@:
cmp eax,450
jge rain_done
mov rowStart,eax
xor esi,esi
rain_splash:
imul eax,esi,97
add eax,cityClock3D
xor edx,edx
mov ecx,800
div ecx
cmp edx,250
jae rain_splash_next
mov age,edx
imul eax,esi,71
add eax,43
xor edx,edx
mov ecx,600
div ecx
add edx,20
mov px,edx
imul eax,esi,37
xor edx,edx
mov ecx,468
sub ecx,rowStart
div ecx
add edx,rowStart
mov py,edx
sub edx,viewHorizon3D
mov eax,viewEye3D
imul eax,480
cdq
mov ecx,py
sub ecx,viewHorizon3D
idiv ecx
sub eax,1024
mov groundDepth,eax
mov ecx,py
imul ecx,640
add ecx,px
mov eax,groundDepth
cmp cityDepthBuffer3D[ecx*4],eax
jl rain_splash_next
mov eax,age
xor edx,edx
mov ecx,40
div ecx
inc eax
mov radius,eax
mov eax,250
sub eax,age
imul eax,100
xor edx,edx
mov ecx,250
div ecx
mov alpha,eax
mov ebx,px
sub ebx,radius
invoke BlendCityWeatherPixel3D,ebx,py,00C5C6AEh,alpha
mov ebx,px
add ebx,radius
invoke BlendCityWeatherPixel3D,ebx,py,00C5C6AEh,alpha
mov eax,radius
shr eax,1
mov edi,py
sub edi,eax
invoke BlendCityWeatherPixel3D,px,edi,00C5C6AEh,alpha
mov eax,radius
shr eax,1
mov edi,py
add edi,eax
invoke BlendCityWeatherPixel3D,px,edi,00C5C6AEh,alpha
rain_splash_next:
inc esi
cmp esi,24
jb rain_splash
rain_done:
ret
DrawCityRain3D ENDP

View file

@ -0,0 +1,5 @@
; Generated by pack_dice_assets.py from the last held pose.
DICE_RELEASE_X1 EQU 213
DICE_RELEASE_Y1 EQU 146
DICE_RELEASE_X2 EQU 232
DICE_RELEASE_Y2 EQU 141

View file

@ -1,4 +1,5 @@
; Moving cube geometry shares the existing 320x240 logical game canvas.
include src_graphics/dice_anchors.inc
.data
cubeCorners3D dd -32768,-32768,-32768, 32768,-32768,-32768
dd 32768,32768,-32768, -32768,32768,-32768
@ -24,6 +25,7 @@ cubeCosZ3D dd 65536
cubeVertices3D dd 16 dup(0)
cubeFaceValues3D dd 6 dup(0)
cubeQuad3D dd 8 dup(0)
cubeContactShadow3D dd 0
.code
; Fill clockwise triangles. Edges are incremented per pixel, avoiding divisions.
@ -302,10 +304,10 @@ DrawTumblingDie3D PROC uses ebx esi edi centerX:DWORD,centerY:DWORD,pitch:DWORD,
mov edx, tableCos[eax*4]
mov cubeCosZ3D, edx
mov eax, value
mov cubeFaceValues3D, eax
mov cubeFaceValues3D[16], eax
mov edx, 7
sub edx, eax
mov cubeFaceValues3D[4], edx
mov cubeFaceValues3D[20], edx
dec eax
mov edx, cubeRight3D[eax*4]
mov cubeFaceValues3D[12], edx
@ -313,10 +315,10 @@ DrawTumblingDie3D PROC uses ebx esi edi centerX:DWORD,centerY:DWORD,pitch:DWORD,
sub ecx, edx
mov cubeFaceValues3D[8], ecx
mov edx, cubeTop3D[eax*4]
mov cubeFaceValues3D[16], edx
mov cubeFaceValues3D, edx
mov ecx, 7
sub ecx, edx
mov cubeFaceValues3D[20], ecx
mov cubeFaceValues3D[4], ecx
xor esi, esi
cube_project_loop:
imul edi, esi, 12
@ -326,6 +328,25 @@ cube_project_loop:
inc esi
cmp esi, 8
jb cube_project_loop
; A resting cube touches the cloth along its projected bottom face.
; Reverse the underside winding; the normal face pass culls it.
cmp cubeContactShadow3D,0
je cube_no_contact_shadow
mov eax,cubeVertices3D[28]
inc eax
mov ebx,cubeVertices3D[60]
inc ebx
mov edi,cubeVertices3D[52]
inc edi
invoke DrawTriangle3D,cubeVertices3D[24],eax,cubeVertices3D[56],ebx,cubeVertices3D[48],edi,00153122h
mov eax,cubeVertices3D[28]
inc eax
mov ebx,cubeVertices3D[52]
inc ebx
mov edi,cubeVertices3D[20]
inc edi
invoke DrawTriangle3D,cubeVertices3D[24],eax,cubeVertices3D[48],ebx,cubeVertices3D[16],edi,00153122h
cube_no_contact_shadow:
mov face, 0
cube_face_loop:
mov esi, face
@ -389,19 +410,48 @@ DrawTumblingDie3D ENDP
RenderDiceThrow3D PROC uses ebx esi edi
local t:DWORD,ground:DWORD,lift:DWORD,pitch:DWORD,yaw:DWORD,roll:DWORD
local cx1:DWORD,cx2:DWORD,cy:DWORD
mov t, 1600
local cx1:DWORD,cx2:DWORD,cy:DWORD,cy2:DWORD,oldScale:DWORD,shadowAlpha:DWORD
; Held dice are painted into the cupped palm: no second pair over skin.
cmp diceArtReady3D,1
jne throw_begin
cmp dice3d_state,0
je throw_done
cmp dice3d_state,1
jne throw_begin
cmp dice3d_timer,240
jb throw_done
throw_begin:
mov eax,cubeScale3D
mov oldScale,eax
mov t,0
cmp dice3d_state,2
jne @F
mov t,1600
@@:
cmp dice3d_state, 1
jne throw_pose
mov eax, dice3d_timer
sub eax,240
jns @F
xor eax,eax
@@:
cmp eax,1360
jbe @F
mov eax,1360
@@:
imul eax,1600
xor edx,edx
mov ecx,1360
div ecx
mov t, eax
throw_pose:
mov eax, t
imul eax, 59
mov cubeScale3D,12
mov eax,t
imul eax, DICE_RELEASE_Y1-146
xor edx, edx
mov ecx, 1600
div ecx
mov ebx, 155
mov ebx, DICE_RELEASE_Y1
sub ebx, eax
mov ground, ebx
mov lift, 0
@ -447,54 +497,88 @@ throw_position:
sub eax, lift
mov cy, eax
mov eax, t
imul eax, 12
imul eax, DICE_RELEASE_X1-134
xor edx, edx
mov ecx, 1600
div ecx
mov ebx, 146
mov ebx, DICE_RELEASE_X1
sub ebx, eax
mov cx1, ebx
add eax, 174
mov cx2, eax
mov eax,t
imul eax,DICE_RELEASE_X2-186
xor edx,edx
mov ecx,1600
div ecx
mov ebx,DICE_RELEASE_X2
sub ebx,eax
mov cx2,ebx
mov eax, 1600
sub eax, t
mov esi, eax
imul eax, 3
shr eax, 2
add eax, 40
mov pitch, eax
mov eax, esi
imul eax, 3
imul eax,eax
imul eax,1024
xor edx, edx
mov ecx, 5
mov ecx,2560000
div ecx
add eax, 44
mov yaw, eax
mov eax, esi
imul eax, esi
shl eax, 1
xor edx, edx
mov ecx, 1600
div ecx
mov roll, eax
mov eax, cx1
sub eax, 16
mov edx, ground
add edx, 18
invoke DrawBox, eax, edx, 33, 3, 00082011h
mov eax, cx2
sub eax, 16
mov edx, ground
add edx, 18
invoke DrawBox, eax, edx, 33, 3, 00082011h
invoke DrawTumblingDie3D, cx1, cy, pitch, yaw, roll, dice3d_val1
mov eax, roll
neg eax
mov edx, cy
cmp dice3d_state, 1
mov esi,1024
sub esi,eax
mov roll,esi
mov eax,esi
add eax,70
mov pitch,eax
mov eax,esi
shr eax,1
add eax,44
mov yaw,eax
mov cubeContactShadow3D,0
cmp t,1600
jne @F
sub edx, 3
mov cubeContactShadow3D,1
@@:
invoke DrawTumblingDie3D, cx2, edx, pitch, yaw, eax, dice3d_val2
mov eax,t
imul eax,DICE_RELEASE_Y1-DICE_RELEASE_Y2
xor edx,edx
mov ecx,1600
div ecx
add eax,cy
sub eax,DICE_RELEASE_Y1-DICE_RELEASE_Y2
mov cy2,eax
cmp dice3d_state,0
je throw_cubes
cmp dice3d_state,1
jne throw_shadows
cmp dice3d_timer,240
jb throw_cubes
throw_shadows:
cmp diceShadowPass3D,0
je throw_cubes
mov eax,85
sub eax,lift
mov shadowAlpha,eax
mov eax,cx1
shl eax,1
mov edx,cubeScale3D
shr edx,2
add edx,ground
shl edx,1
invoke DrawCoinShadow3D,eax,edx,cubeScale3D,6,shadowAlpha
mov eax,cx2
shl eax,1
mov edx,cubeScale3D
shr edx,2
add edx,ground
shl edx,1
invoke DrawCoinShadow3D,eax,edx,cubeScale3D,6,shadowAlpha
throw_cubes:
cmp diceShadowPass3D,0
jne throw_restore
invoke DrawTumblingDie3D, cx1, cy, pitch, yaw, roll, dice3d_val1
mov eax,roll
neg eax
invoke DrawTumblingDie3D, cx2, cy2, pitch, yaw, eax, dice3d_val2
throw_restore:
mov eax,oldScale
mov cubeScale3D,eax
mov cubeContactShadow3D,0
throw_done:
ret
RenderDiceThrow3D ENDP

338
src_graphics/eightball.inc Normal file
View file

@ -0,0 +1,338 @@
EightCount3D PROC uses ebx esi
mov poolAtStart3D,0
mov ecx,poolPlayer3D
mov ebx,poolGroups3D[ecx*4]
test ebx,ebx
jz eight_count_done
mov esi,1
eight_count_loop:
cmp esi,8
je eight_count_next
mov eax,1
cmp esi,8
jb @F
mov eax,2
@@:
cmp eax,ebx
jne eight_count_next
mov eax,poolActive3D[esi*4]
add poolAtStart3D,eax
eight_count_next:
inc esi
cmp esi,16
jb eight_count_loop
eight_count_done:
ret
EightCount3D ENDP
EightLegal3D PROC uses ebx ball:DWORD
xor eax,eax
mov ecx,ball
test ecx,ecx
jle eight_legal_done
cmp ecx,15
ja eight_legal_done
mov ebx,poolPlayer3D
mov ebx,poolGroups3D[ebx*4]
test ebx,ebx
jz eight_legal_open
cmp poolAtStart3D,0
jne eight_legal_group
cmp ecx,8
sete al
ret
eight_legal_group:
cmp ecx,8
je eight_legal_done
mov eax,1
cmp ecx,8
jb @F
mov eax,2
@@:
cmp eax,ebx
sete al
movzx eax,al
ret
eight_legal_open:
cmp ecx,8
setne al
eight_legal_done:
ret
EightLegal3D ENDP
; An obvious shot uses the same predictor as the visible guide. Explicit calls
; lock either ball or pocket independently; cached prediction never settles play.
EightAutoCall3D PROC uses ebx esi edi
cmp poolVariant3D,1
jne eight_auto_done
cmp poolState3D,POOL_AIM
jne eight_auto_done
call EightCount3D
call PoolPredict3D
test poolCallManual3D,1
jnz eight_auto_pocket
mov poolCalled3D,0
mov ebx,poolTraceFirst3D
invoke EightLegal3D,ebx
test eax,eax
jz eight_auto_scan
mov poolCalled3D,ebx
bt poolTracePotted3D,ebx
jnc eight_auto_scan
test poolCallManual3D,2
jz eight_auto_select
mov eax,poolTraceFinal3D[80*4+ebx*4]
cmp eax,poolCalledPocket3D
je eight_auto_select
eight_auto_scan:
mov ebx,1
eight_auto_next:
bt poolTracePotted3D,ebx
jnc eight_auto_advance
invoke EightLegal3D,ebx
test eax,eax
jz eight_auto_advance
eight_auto_filter:
test poolCallManual3D,2
jz eight_auto_select
mov eax,poolTraceFinal3D[80*4+ebx*4]
cmp eax,poolCalledPocket3D
je eight_auto_select
eight_auto_advance:
inc ebx
cmp ebx,16
jb eight_auto_next
jmp eight_auto_pocket
eight_auto_select:
mov poolCalled3D,ebx
eight_auto_pocket:
test poolCallManual3D,2
jnz eight_auto_done
mov poolCalledPocket3D,-1
mov ebx,poolCalled3D
cmp ebx,1
jb eight_auto_done
cmp ebx,15
ja eight_auto_done
bt poolTracePotted3D,ebx
jnc eight_auto_done
mov eax,poolTraceFinal3D[80*4+ebx*4]
cmp eax,5
ja eight_auto_done
mov poolCalledPocket3D,eax
eight_auto_done:
ret
EightAutoCall3D ENDP
; In pool the group owns the ball, not whoever happened to pocket it.
; Recounting also assigns balls pocketed while the table was still open.
EightSyncGroups3D PROC uses ebx esi edi
mov pyrScore3D,0
mov pyrScore3D[4],0
mov pyrOwner3D,-1
mov esi,1
eight_sync_ball:
mov pyrOwner3D[esi*4],-1
cmp esi,8
je eight_sync_next
cmp poolActive3D[esi*4],0
jne eight_sync_next
mov eax,1
cmp esi,8
jb @F
mov eax,2
@@:
xor ebx,ebx
cmp eax,poolGroups3D
je eight_sync_credit
inc ebx
cmp eax,poolGroups3D[4]
jne eight_sync_next
eight_sync_credit:
mov pyrOwner3D[esi*4],ebx
inc pyrScore3D[ebx*4]
eight_sync_next:
inc esi
cmp esi,16
jb eight_sync_ball
ret
EightSyncGroups3D ENDP
; Called-ball eight-ball rules share only simulation and aiming with pyramid.
EightFinish3D PROC uses ebx esi edi
local originalPots:DWORD
cmp pyrSettled3D,0
jne eight_finish_done
mov pyrSettled3D,1
mov poolCallManual3D,0
mov poolTraceValid3D,0
mov eax,poolPotted3D
mov originalPots,eax
mov poolFoul3D,0
cmp poolActive3D,0
je eight_finish_foul
cmp poolFirst3D,-1
je eight_finish_foul
cmp poolBreak3D,0
jne eight_finish_break
invoke EightLegal3D,poolFirst3D
test eax,eax
jz eight_finish_foul
cmp poolRail3D,0
jne eight_finish_eight
cmp poolPotted3D,0
jne eight_finish_eight
jmp eight_finish_foul
eight_finish_break:
mov eax,poolPotted3D
and eax,0FFFEh
jnz eight_finish_eight
mov eax,poolBreakRails3D
and eax,0FFFEh
xor ecx,ecx
eight_break_rails:
test eax,eax
jz @F
lea edx,[eax-1]
and eax,edx
inc ecx
jmp eight_break_rails
@@:
cmp ecx,4
jae eight_finish_eight
mov pyrIllegalBreak3D,1
mov pyrFoulReason3D,2
mov eax,poolPlayer3D
xor eax,1
mov poolPlayer3D,eax
mov pyrChoiceActor3D,eax
mov poolState3D,POOL_CHOICE
mov poolTime3D,0
call EightSyncGroups3D
ret
eight_finish_foul:
mov poolFoul3D,1
eight_finish_eight:
bt poolPotted3D,8
jnc eight_finish_continue
cmp poolBreak3D,0
je eight_finish_result
invoke PyramidRespot3D,8
btr poolPotted3D,8
jmp eight_finish_continue
eight_finish_result:
mov eax,poolPlayer3D
cmp poolFoul3D,0
jne eight_finish_lost
cmp poolAtStart3D,0
jne eight_finish_lost
mov ecx,poolGroups3D[eax*4]
test ecx,ecx
jz eight_finish_lost
cmp poolCalled3D,8
jne eight_finish_lost
mov ecx,poolCalledPocket3D
cmp poolPocket3D[32],ecx
je eight_finish_winner
eight_finish_lost:
xor eax,1
eight_finish_winner:
mov poolWinner3D,eax
mov poolState3D,POOL_RESULT
call EightSyncGroups3D
ret
eight_finish_continue:
cmp poolFoul3D,0
jne eight_finish_hand
cmp poolBreak3D,0
je eight_finish_called
mov eax,originalPots
and eax,0FFFEh
jnz eight_finish_aim
jmp eight_finish_switch
eight_finish_called:
mov ebx,poolCalled3D
cmp ebx,1
jb eight_finish_switch
cmp ebx,15
ja eight_finish_switch
bt poolPotted3D,ebx
jnc eight_finish_switch
mov eax,poolCalledPocket3D
cmp poolPocket3D[ebx*4],eax
jne eight_finish_switch
invoke EightLegal3D,ebx
test eax,eax
jz eight_finish_switch
mov ecx,poolPlayer3D
cmp poolGroups3D[ecx*4],0
jne eight_finish_aim
mov eax,1
cmp ebx,8
jb @F
mov eax,2
@@:
mov poolGroups3D[ecx*4],eax
xor ecx,1
xor eax,3
mov poolGroups3D[ecx*4],eax
jmp eight_finish_aim
eight_finish_hand:
xor poolPlayer3D,1
mov poolState3D,POOL_HAND
mov poolBreak3D,0
mov poolTime3D,0
mov pyrCue3D,0
mov poolActive3D,0
call EightSyncGroups3D
ret
eight_finish_switch:
xor poolPlayer3D,1
eight_finish_aim:
mov poolState3D,POOL_AIM
mov poolBreak3D,0
mov poolTime3D,0
mov pyrCue3D,0
call EightCount3D
call EightSyncGroups3D
eight_finish_done:
ret
EightFinish3D ENDP
EightHand3D PROC uses ebx esi
cmp poolPlayer3D,1
jne eight_hand_human
cmp poolAI3D,0
je eight_hand_human
mov esi,55*FP_1
eight_hand_row:
mov ebx,40*FP_1
eight_hand_cell:
invoke PoolPlaceCue3D,ebx,esi
test eax,eax
jnz eight_hand_placed
add ebx,6*FP_1
cmp ebx,280*FP_1
jle eight_hand_cell
add esi,6*FP_1
cmp esi,175*FP_1
jle eight_hand_row
ret
eight_hand_human:
cmp mouseClick3D,0
je eight_hand_done
mov mouseClick3D,0
mov eax,mouseX3D
shl eax,16
mov ebx,mouseY3D
shl ebx,16
invoke PoolPlaceCue3D,eax,ebx
test eax,eax
jz eight_hand_done
eight_hand_placed:
mov poolState3D,POOL_AIM
mov poolTime3D,0
mov poolTraceValid3D,0
eight_hand_done:
ret
EightHand3D ENDP

View file

@ -165,4 +165,5 @@ stairsTime3D dd 0
stairsReturnX3D dd 0
stairsReturnY3D dd 0
stairsReturnAngle3D dd 0
include src_graphics\world_dressing_data.inc
.code

View file

@ -0,0 +1,383 @@
; Static world-space light and contact maps, shaded only before the HUD.
HallLightVisible3D PROC uses ebx esi lx:DWORD,ly:DWORD,tx:DWORD,ty:DWORD
mov esi,1
hall_light_ray:
mov eax,tx
sub eax,lx
imul eax,esi
sar eax,3
add eax,lx
sar eax,3
mov ebx,eax
mov eax,ty
sub eax,ly
imul eax,esi
sar eax,3
add eax,ly
sar eax,3
shl eax,5
add eax,ebx
cmp casinoMap[eax],0
jne hall_light_blocked
inc esi
cmp esi,8
jb hall_light_ray
mov eax,1
ret
hall_light_blocked:
xor eax,eax
ret
HallLightVisible3D ENDP
AddHallLight3D PROC uses ebx esi edi wx:DWORD,wy:DWORD,radius:DWORD,power:DWORD
local lightX:DWORD,lightY:DWORD,gx:DWORD,gy:DWORD,minX:DWORD,maxX:DWORD,maxY:DWORD
local radiusSq:DWORD,weight:DWORD
mov eax,wx
sar eax,13
mov lightX,eax
mov eax,wy
sar eax,13
mov lightY,eax
mov eax,radius
imul eax,eax
mov radiusSq,eax
mov eax,lightX
sub eax,radius
jns @F
xor eax,eax
@@:
mov minX,eax
mov eax,lightX
add eax,radius
cmp eax,255
jle @F
mov eax,255
@@:
mov maxX,eax
mov eax,lightY
sub eax,radius
jns @F
xor eax,eax
@@:
mov gy,eax
mov eax,lightY
add eax,radius
cmp eax,255
jle @F
mov eax,255
@@:
mov maxY,eax
hall_light_bake_row:
mov eax,minX
mov gx,eax
hall_light_bake_cell:
mov eax,gx
sub eax,lightX
imul eax,eax
mov ebx,gy
sub ebx,lightY
imul ebx,ebx
add eax,ebx
cmp eax,radiusSq
jae hall_light_bake_next
neg eax
add eax,radiusSq
shl eax,8
xor edx,edx
div radiusSq
imul eax,eax
imul eax,power
shr eax,16
test eax,eax
jz hall_light_bake_next
mov weight,eax
invoke HallLightVisible3D,lightX,lightY,gx,gy
test eax,eax
jz hall_light_bake_next
mov edi,gy
shl edi,8
add edi,gx
movzx eax,hallLightMap3D[edi]
add eax,weight
cmp eax,160
jbe @F
mov eax,160
@@:
mov hallLightMap3D[edi],al
hall_light_bake_next:
inc gx
mov eax,gx
cmp eax,maxX
jle hall_light_bake_cell
inc gy
mov eax,gy
cmp eax,maxY
jle hall_light_bake_row
ret
AddHallLight3D ENDP
AddHallContact3D PROC uses ebx esi edi wx:DWORD,wy:DWORD,worldWidth:DWORD
local centerX:DWORD,centerY:DWORD,radius:DWORD,gx:DWORD,gy:DWORD,minX:DWORD,maxX:DWORD,maxY:DWORD
mov eax,wx
sar eax,13
mov centerX,eax
mov eax,wy
sar eax,13
mov centerY,eax
mov eax,worldWidth
shr eax,14
add eax,2
mov radius,eax
mov eax,centerX
sub eax,radius
jns @F
xor eax,eax
@@:
mov minX,eax
mov eax,centerX
add eax,radius
cmp eax,255
jle @F
mov eax,255
@@:
mov maxX,eax
mov eax,centerY
sub eax,4
jns @F
xor eax,eax
@@:
mov gy,eax
mov eax,centerY
add eax,4
cmp eax,255
jle @F
mov eax,255
@@:
mov maxY,eax
hall_contact_row:
mov eax,minX
mov gx,eax
hall_contact_cell:
mov eax,gx
sub eax,centerX
imul eax,256
cdq
idiv radius
imul eax,eax
mov ebx,eax
mov eax,gy
sub eax,centerY
imul eax,64
imul eax,eax
add eax,ebx
cmp eax,65536
jae hall_contact_next
neg eax
add eax,65536
imul eax,40
shr eax,16
mov edi,gy
shl edi,8
add edi,gx
cmp al,hallContactMap3D[edi]
jbe hall_contact_next
mov hallContactMap3D[edi],al
hall_contact_next:
inc gx
mov eax,gx
cmp eax,maxX
jle hall_contact_cell
inc gy
mov eax,gy
cmp eax,maxY
jle hall_contact_row
ret
AddHallContact3D ENDP
BuildHallLighting3D PROC uses ebx esi edi
lea edi,hallLightMap3D
xor eax,eax
mov ecx,WORLD_LIGHT_W*WORLD_LIGHT_H
rep stosb
lea edi,hallContactMap3D
mov ecx,WORLD_LIGHT_W*WORLD_LIGHT_H
rep stosb
lea esi,hallLights3D
mov edi,HALL_LIGHT_COUNT
hall_bake_chandelier:
invoke AddHallLight3D,[esi].CityProp3D.posX,[esi].CityProp3D.posY,32,86
add esi,SIZEOF CityProp3D
dec edi
jnz hall_bake_chandelier
lea esi,hallLoungeDecor3D
mov edi,HALL_DECOR_COUNT
hall_bake_furniture:
cmp [esi].CityProp3D.asset,OFFSET hallRopeFurniture3D
je hall_bake_furniture_next
invoke AddHallContact3D,[esi].CityProp3D.posX,[esi].CityProp3D.posY,[esi].CityProp3D.worldW
cmp [esi].CityProp3D.asset,OFFSET hallLampFurniture3D
jne hall_bake_furniture_next
invoke AddHallLight3D,[esi].CityProp3D.posX,[esi].CityProp3D.posY,20,110
hall_bake_furniture_next:
add esi,SIZEOF CityProp3D
dec edi
jnz hall_bake_furniture
xor esi,esi
hall_bake_sconce:
invoke AddHallLight3D,FP_1*5/4,hallSconceY3D[esi*4],22,110
invoke AddHallLight3D,FP_1*123/4,hallSconceY3D[esi*4],22,110
inc esi
cmp esi,4
jb hall_bake_sconce
invoke AddHallLight3D,4*FP_1,23*FP_1,22,90
invoke AddHallLight3D,6*FP_1,23*FP_1,22,90
invoke AddHallLight3D,8*FP_1,23*FP_1,22,90
mov hallLightReady3D,1
ret
BuildHallLighting3D ENDP
ApplyHallLighting3D PROC uses ebx esi edi
local rowY:DWORD,columnX:DWORD,planeDepth:DWORD,planeFloor:DWORD
local gainR:DWORD,gainG:DWORD,gainB:DWORD,cell:DWORD,floorHit:DWORD
local light:DWORD,contact:DWORD,pixelRow:DWORD,pixelColumn:DWORD
cmp hallLighting3D,0
je hall_light_draw_done
cmp hallLightReady3D,0
je hall_light_draw_done
cmp outdoorRender3D,0
jne hall_light_draw_done
cmp hallLevel3D,0
jne hall_light_draw_done
mov rowY,0
hall_light_draw_row:
mov eax,rowY
mov planeFloor,0
cmp eax,viewHorizon3D
jge hall_light_floor_depth
mov ecx,viewHorizon3D
sub ecx,eax
mov eax,viewCeiling3D
sub eax,viewEye3D
jmp hall_light_plane_depth
hall_light_floor_depth:
sub eax,viewHorizon3D
inc eax
mov ecx,eax
mov eax,viewEye3D
mov planeFloor,1
hall_light_plane_depth:
imul eax,480
cdq
idiv ecx
mov planeDepth,eax
mov columnX,0
hall_light_draw_column:
imul edi,rowY,640
add edi,columnX
mov eax,cityDepthBuffer3D[edi*4]
mov floorHit,0
cmp eax,planeDepth
jl @F
mov eax,planeDepth
mov edx,planeFloor
mov floorHit,edx
@@:
mov esi,eax
mov ebx,columnX
imul cityRayX3D[ebx*4]
shrd eax,edx,16
add eax,playerX
sar eax,13
cmp eax,256
jae hall_light_ambient
mov cell,eax
mov eax,esi
imul cityRayY3D[ebx*4]
shrd eax,edx,16
add eax,playerY
sar eax,13
cmp eax,256
jae hall_light_ambient
shl eax,8
add eax,cell
movzx ecx,hallLightMap3D[eax]
mov light,ecx
mov contact,0
cmp floorHit,0
je hall_light_gains
movzx eax,hallContactMap3D[eax]
mov contact,eax
jmp hall_light_gains
hall_light_ambient:
mov light,0
mov contact,0
hall_light_gains:
mov eax,light
add eax,184
sub eax,contact
mov gainR,eax
mov eax,light
imul eax,7
shr eax,3
add eax,188
sub eax,contact
mov gainG,eax
mov eax,light
shr eax,1
add eax,200
sub eax,contact
mov gainB,eax
mov pixelRow,0
hall_light_pixel_row:
mov pixelColumn,0
hall_light_pixel:
mov eax,pixelRow
imul eax,640
add eax,pixelColumn
add eax,edi
mov esi,eax
mov ebx,screenBuffer[esi*4]
movzx eax,bl
imul eax,gainB
shr eax,8
cmp eax,255
jbe @F
mov eax,255
@@:
mov ecx,eax
movzx eax,bh
imul eax,gainG
shr eax,8
cmp eax,255
jbe @F
mov eax,255
@@:
shl eax,8
or ecx,eax
mov eax,ebx
shr eax,16
and eax,255
imul eax,gainR
shr eax,8
cmp eax,255
jbe @F
mov eax,255
@@:
shl eax,16
or eax,ecx
mov screenBuffer[esi*4],eax
inc pixelColumn
cmp pixelColumn,2
jb hall_light_pixel
inc pixelRow
cmp pixelRow,2
jb hall_light_pixel_row
add columnX,2
cmp columnX,640
jb hall_light_draw_column
add rowY,2
cmp rowY,480
jb hall_light_draw_row
hall_light_draw_done:
ret
ApplyHallLighting3D ENDP

View file

@ -280,7 +280,7 @@ InitHall3D PROC uses esi edi
rep movsd
mov hallLevel3D, 0
mov worldMap3D, OFFSET casinoMap
mov viewEye3D, FP_1*17/20
mov viewEye3D, HALL_STANDING_EYE
mov viewCeiling3D, FP_1*5/2
mov floorTexture3D, 100
mov casinoSprites[14*SIZEOF SpriteObject].interactId, 0
@ -303,10 +303,13 @@ init_plane_lut:
init_plane_done:
call InitCasinoNpcMind
call InitNpcLife3D
call BuildHallLighting3D
ret
InitHall3D ENDP
LoadHallAssets3D PROC
call LoadWorldDressing3D
call LoadScratchArt3D
invoke LoadAnimPack3D, OFFSET hallCardFurniture3D, OFFSET hallCardFurniturePath3D
invoke LoadAnimPack3D, OFFSET hallWheelFurniture3D, OFFSET hallWheelFurniturePath3D
invoke LoadAnimPack3D, OFFSET hallBackFurniture3D, OFFSET hallBackFurniturePath3D
@ -376,6 +379,7 @@ hall_load_sign:
LoadHallAssets3D ENDP
FreeHallAssets3D PROC
call FreeWorldDressing3D
call FreeNativeWheelArt3D
call FreeNativeCoinArt3D
invoke FreeAnimPack3D, OFFSET hallCardFurniture3D
@ -556,6 +560,9 @@ hall_render_lounge:
cmp esi, HALL_DECOR_COUNT
jb hall_render_lounge
call RenderPatrons3D
invoke DrawCityObject3D,OFFSET scrStation3D,0
call RenderHallDressing3D
call ApplyHallLighting3D
ret
hall_render_bunker:
mov esi, 0
@ -649,6 +656,7 @@ CheckHallInteractions3D PROC
; A point on the serving edge remains in front even when standing close.
invoke HallTarget3D, playerX,23*FP_1+FP_HALF,INTERACT_BAR,OFFSET hallBarPrompt3D
hall_check_other:
invoke HallTarget3D,scrStation3D.posX,scrStation3D.posY,INTERACT_SCRATCH,OFFSET scratchHall3D
invoke HallTarget3D, 28*FP_1+FP_HALF,10*FP_1+FP_HALF,INTERACT_BASEMENT,OFFSET hallBunkerPrompt3D
invoke HallTarget3D, 27*FP_1,25*FP_1,INTERACT_BILLIARDS,OFFSET hallPoolPrompt3D
call CheckNpcInteractions3D
@ -662,6 +670,12 @@ hall_check_bunker:
CheckHallInteractions3D ENDP
TriggerHallInteraction3D PROC
cmp interactTargetId,INTERACT_SCRATCH
jne @F
call BeginScratch3D
mov eax,1
ret
@@:
cmp interactTargetId,INTERACT_NPC
jne @F
call OpenNpcDialog3D
@ -737,6 +751,10 @@ stairs_back_to_hall:
mov playerAngle, eax
stairs_complete:
mov viewEye3D, FP_1*17/20
cmp hallLevel3D,0
jne @F
mov viewEye3D,HALL_STANDING_EYE
@@:
mov engineMode, MODE_WALK
call ClearKeys3D
stairs_handle_done:
@ -915,7 +933,7 @@ RenderBar3D PROC uses ebx
mov playerY, 25*FP_1+FP_1/3
mov playerAngle, 384
mov viewHorizon3D, HALF_H
mov viewEye3D, FP_1*17/20
mov viewEye3D, HALL_STANDING_EYE
call UpdateCamera3D
call RenderCeilingAndFloor
call RenderWalls
@ -1027,6 +1045,9 @@ RenderCityGuards3D ENDP
CanWalkHall3D PROC uses esi ebx px:DWORD,py:DWORD
cmp hallLevel3D, 0
jne hall_collision_bunker
invoke ScratchCanWalk3D,px,py
test eax,eax
jz hall_collision_blocked
xor esi, esi
hall_collision_decor:
imul ebx, esi, SIZEOF CityProp3D
@ -1204,7 +1225,10 @@ UpdateHall3D PROC
mov viewHorizon3D, HALF_H
mov viewEye3D, FP_1*17/20
cmp hallLevel3D, 0
je @F
jne hall_update_bunker_eye
mov viewEye3D,HALL_STANDING_EYE
jmp @F
hall_update_bunker_eye:
mov eax, playerY
sub eax, 24*FP_1
jle @F
@ -1327,3 +1351,5 @@ patron_render_loop:
jb patron_render_loop
ret
RenderPatrons3D ENDP
include src_graphics\world_dressing.inc
include src_graphics\hall_lighting.inc

View file

@ -0,0 +1,249 @@
LoadNativeDiceArt3D PROC
cmp diceArtReady3D,1
je dice_art_loaded
invoke LoadAnimPack3D,OFFSET diceTableArt3D,OFFSET diceTablePath3D
invoke LoadAnimPack3D,OFFSET diceHandsArt3D,OFFSET diceHandsPath3D
invoke ValidateCityPack3D,OFFSET diceTableArt3D,640,480,1
invoke ValidateCityPack3D,OFFSET diceHandsArt3D,256,320,16
cmp diceTableArt3D.pixels,0
je dice_art_failed
cmp diceHandsArt3D.pixels,0
je dice_art_failed
mov diceArtReady3D,1
dice_art_loaded:
mov eax,1
ret
dice_art_failed:
call FreeNativeDiceArt3D
xor eax,eax
ret
LoadNativeDiceArt3D ENDP
FreeNativeDiceArt3D PROC
invoke FreeAnimPack3D,OFFSET diceTableArt3D
invoke FreeAnimPack3D,OFFSET diceHandsArt3D
mov diceArtReady3D,0
ret
FreeNativeDiceArt3D ENDP
DiceEscape3D PROC
mov paused3D,0
cmp dice3d_state,1
jne dice_escape_now
mov diceExitQueued3D,1
call ClearKeys3D
ret
dice_escape_now:
mov diceExitQueued3D,0
mov engineMode,MODE_WALK
call ClearKeys3D
invoke PlayCasinoSound3D,0,0,0
ret
DiceEscape3D ENDP
HandleDiceMouse3D PROC
cmp mouseClick3D,0
je dice_mouse_done
mov mouseClick3D,0
cmp mouseY3D,3
jb dice_mouse_done
cmp mouseY3D,17
jae dice_mouse_footer
cmp mouseX3D,252
jb dice_mouse_done
cmp mouseX3D,312
jae dice_mouse_done
call DiceEscape3D
ret
dice_mouse_footer:
cmp dice3d_state,1
je dice_mouse_done
cmp mouseY3D,218
jb dice_mouse_stake
cmp mouseY3D,236
jae dice_mouse_done
cmp mouseX3D,198
jb dice_mouse_choice
cmp mouseX3D,312
jae dice_mouse_done
mov pressedKeys3D[20h],1
ret
dice_mouse_choice:
cmp dice3d_state,0
jne dice_mouse_done
cmp mouseX3D,8
jb dice_mouse_done
cmp mouseX3D,96
jae @F
mov pressedKeys3D[31h],1
ret
@@:
cmp mouseX3D,100
jb dice_mouse_done
cmp mouseX3D,188
jae dice_mouse_done
mov pressedKeys3D[32h],1
ret
dice_mouse_stake:
cmp dice3d_state,0
jne dice_mouse_done
cmp mouseY3D,199
jb dice_mouse_done
cmp mouseY3D,215
jae dice_mouse_done
cmp mouseX3D,118
jb dice_mouse_done
cmp mouseX3D,134
jae @F
mov pressedKeys3D[28h],1
ret
@@:
cmp mouseX3D,140
jb dice_mouse_done
cmp mouseX3D,156
jae dice_mouse_done
mov pressedKeys3D[26h],1
dice_mouse_done:
ret
HandleDiceMouse3D ENDP
; Original payment/parity rules remain a single command path for both inputs.
HandleDiceInput PROC
call HandleDiceMouse3D
call HandleDiceRules3D
cmp diceExitQueued3D,1
jne dice_input_done
cmp dice3d_state,1
je dice_input_done
call DiceEscape3D
dice_input_done:
ret
HandleDiceInput ENDP
DrawDiceHand3D PROC uses ebx esi
local frame:DWORD,py:DWORD
mov frame,0
mov py,90
cmp dice3d_state,0
je dice_hand_draw
cmp dice3d_state,1
jne dice_hand_done
mov eax,dice3d_timer
cmp eax,240
jae dice_hand_release
imul eax,4
xor edx,edx
mov ecx,240
div ecx
mov frame,eax
jmp dice_hand_draw
dice_hand_release:
cmp eax,1000
jae dice_hand_lower
sub eax,240
imul eax,12
xor edx,edx
mov ecx,760
div ecx
add eax,4
mov frame,eax
jmp dice_hand_draw
dice_hand_lower:
cmp eax,1250
jae dice_hand_done
sub eax,1000
imul eax,260
xor edx,edx
mov ecx,250
div ecx
add py,eax
mov frame,15
dice_hand_draw:
invoke DrawAnimSprite3D,OFFSET diceHandsArt3D,frame,320,py,256,320
dice_hand_done:
ret
DrawDiceHand3D ENDP
RenderNativeDice3D PROC uses ebx esi edi
local evenTint:DWORD,oddTint:DWORD
cmp diceArtReady3D,1
jne dice_scene_fallback
mov esi,diceTableArt3D.pixels
lea edi,screenBuffer
mov ecx,SCREEN_SIZE
rep movsd
jmp dice_scene_objects
dice_scene_fallback:
invoke DrawBox,0,0,320,240,000B342Bh
invoke DrawString8x8,96,64,OFFSET coinMissingText3D,00E5D29Eh
dice_scene_objects:
; Cloth shadows belong behind the foreground hand, never on its skin.
mov diceShadowPass3D,1
call RenderDiceThrow3D
mov diceShadowPass3D,0
call DrawDiceHand3D
call RenderDiceThrow3D
invoke DrawBox,0,0,320,20,0014191Dh
invoke DrawString8x8,12,6,OFFSET textDiceTitle,00E5D29Eh
invoke DrawBoxBorder,252,3,60,14,00252D2Ah,0086754Eh,1
invoke DrawString8x8,266,6,OFFSET coinExitText3D,00E5D29Eh
invoke DrawBox,4,23,132,15,0014191Dh
invoke DrawString8x8,8,27,OFFSET textBankPrompt,00E5D29Eh
invoke FormatNumber,bankroll,OFFSET hudNumBuffer
invoke DrawString8x8,56,27,OFFSET hudNumBuffer,00FFFFFFh
invoke DrawBox,0,194,320,46,0014191Dh
invoke DrawString8x8,8,201,OFFSET coinStakeText3D,00E5D29Eh
invoke FormatNumber,dice3d_bet,OFFSET hudNumBuffer
invoke DrawString8x8,72,201,OFFSET hudNumBuffer,00FFFFFFh
invoke DrawBoxBorder,118,199,16,16,00252D2Ah,0086754Eh,1
invoke DrawString8x8,124,203,OFFSET coinMinusText3D,00E5D29Eh
invoke DrawBoxBorder,140,199,16,16,00252D2Ah,0086754Eh,1
invoke DrawString8x8,146,203,OFFSET coinPlusText3D,00E5D29Eh
mov evenTint,0086754Eh
mov oddTint,0086754Eh
cmp dice3d_choice,1
jne @F
mov evenTint,00E5D29Eh
jmp dice_scene_choices
@@:
mov oddTint,00E5D29Eh
dice_scene_choices:
invoke DrawBoxBorder,8,218,88,18,00252D2Ah,evenTint,1
invoke DrawString8x8,26,223,OFFSET textDiceEven,evenTint
invoke DrawBoxBorder,100,218,88,18,00252D2Ah,oddTint,1
invoke DrawString8x8,112,223,OFFSET textDiceOdd,oddTint
invoke DrawBoxBorder,198,218,114,18,00252D2Ah,0086754Eh,1
cmp dice3d_state,2
jne @F
invoke DrawString8x8,224,223,OFFSET diceAgainText3D,00E5D29Eh
jmp dice_scene_status
@@:
invoke DrawString8x8,226,223,OFFSET diceThrowText3D,00E5D29Eh
dice_scene_status:
cmp diceExitQueued3D,0
je @F
invoke DrawString8x8,164,201,OFFSET coinLeavingText3D,00E5D29Eh
@@:
cmp dice3d_state,2
jne dice_scene_done
invoke DrawBox,8,176,304,16,0014191Dh
invoke DrawString8x8,12,180,OFFSET textDiceSumHdr,00E5D29Eh
invoke FormatNumber,dice3d_val1,OFFSET hudNumBuffer
invoke DrawString8x8,72,180,OFFSET hudNumBuffer,00FFFFFFh
invoke DrawString8x8,86,180,OFFSET coinPlusText3D,00E5D29Eh
invoke FormatNumber,dice3d_val2,OFFSET hudNumBuffer
invoke DrawString8x8,98,180,OFFSET hudNumBuffer,00FFFFFFh
invoke DrawString8x8,112,180,OFFSET textDiceEqual+1,00E5D29Eh
invoke FormatNumber,dice3d_sum,OFFSET hudNumBuffer
invoke DrawString8x8,126,180,OFFSET hudNumBuffer,00FFFFFFh
cmp dice3d_win,0
je dice_scene_loss
invoke DrawString8x8,158,180,OFFSET textDiceWin,0090D5ADh
invoke FormatNumber,dice3d_win,OFFSET hudNumBuffer
invoke DrawString8x8,248,180,OFFSET hudNumBuffer,0090D5ADh
jmp dice_scene_done
dice_scene_loss:
invoke DrawString8x8,192,180,OFFSET textDiceLose,00FF6464h
dice_scene_done:
ret
RenderNativeDice3D ENDP

View file

@ -0,0 +1,17 @@
LoadNativeDiceArt3D PROTO
FreeNativeDiceArt3D PROTO
RenderNativeDice3D PROTO
HandleDiceInput PROTO
HandleDiceRules3D PROTO
DiceEscape3D PROTO
.data
diceTableArt3D AnimPack3D <>
diceHandsArt3D AnimPack3D <>
diceTablePath3D db "assets\animations\coin\table-v1.bin",0
diceHandsPath3D db "assets\animations\dice\hands-v2.bin",0
diceArtReady3D dd 0
diceExitQueued3D dd 0
diceShadowPass3D dd 0
diceThrowText3D db 193,208,206,209,206,202,0
diceAgainText3D db 197,217,168,32,208,192,199,0
.code

View file

@ -1,5 +1,12 @@
; Main and owned coin-detail windows are processed on the same thread; no CMD fallback.
WindowEvents3D PROC uses ebx esi edi hwnd:DWORD, uMsg:DWORD, wp:DWORD, lp:DWORD
cmp engineMode,MODE_SCRATCH
jne @F
invoke ScratchMouseEvent3D,hwnd,uMsg,wp,lp
test eax,eax
jz @F
ret
@@:
cmp uMsg, 102h
jne @F
invoke ArrivalChar3D, wp

View file

@ -1,4 +1,9 @@
PyramidSelect3D PROC uses ebx ball:DWORD
cmp poolVariant3D,1
jne @F
mov pyrCue3D,0
ret
@@:
cmp poolBreak3D,0
je @F
mov pyrCue3D,0
@ -135,6 +140,8 @@ PoolShoot3D PROC uses ebx esi
cmp poolX3D,95*FP_1
jge pyr_shoot_done
@@:
call PoolCountGroup3D
call EightAutoCall3D
mov poolFirst3D,-1
mov poolRail3D,0
mov poolBreakRails3D,0
@ -234,6 +241,8 @@ PoolChooseAI3D PROC uses ebx esi edi
local cue:DWORD,target:DWORD,pocket:DWORD,deltaX:DWORD,deltaY:DWORD,distance:DWORD
local gx:DWORD,gy:DWORD,best:DWORD,bestCue:DWORD,bestX:DWORD,bestY:DWORD,bestPower:DWORD
local shotLength:DWORD
local bestTarget:DWORD,bestPocket:DWORD
call PoolCountGroup3D
cmp poolBreak3D,0
je pyr_ai_search
mov pyrCue3D,0
@ -247,6 +256,11 @@ pyr_ai_search:
mov cue,0
pyr_ai_cue:
mov ecx,cue
cmp poolVariant3D,1
jne @F
test ecx,ecx
jnz pyr_ai_search_done
@@:
cmp poolActive3D[ecx*4],1
jne pyr_ai_next_cue
mov target,0
@ -256,6 +270,12 @@ pyr_ai_target:
je pyr_ai_next_target
cmp poolActive3D[esi*4],1
jne pyr_ai_next_target
cmp poolVariant3D,1
jne @F
invoke EightLegal3D,esi
test eax,eax
jz pyr_ai_next_target
@@:
mov pocket,0
pyr_ai_pocket:
mov edi,pocket
@ -332,6 +352,10 @@ pyr_ai_pocket:
mov bestX,ecx
mov ecx,gy
mov bestY,ecx
mov ecx,target
mov bestTarget,ecx
mov ecx,pocket
mov bestPocket,ecx
; v^2/(2*rolling loss) estimates the travel budget, with margin for a cut.
sar eax,16
imul eax,220
@ -353,6 +377,7 @@ pyr_ai_next_cue:
inc cue
cmp cue,16
jb pyr_ai_cue
pyr_ai_search_done:
cmp bestCue,-1
je pyr_ai_safety
mov eax,bestCue
@ -360,8 +385,17 @@ pyr_ai_next_cue:
invoke PoolAimPoint3D,bestX,bestY
mov eax,bestPower
mov poolPower3D,eax
cmp poolVariant3D,1
jne @F
mov eax,bestTarget
mov poolCalled3D,eax
mov eax,bestPocket
mov poolCalledPocket3D,eax
mov poolCallManual3D,3
@@:
ret
pyr_ai_safety:
mov poolCallManual3D,0
invoke PyramidSelect3D,-1
mov esi,pyrCue3D
xor edi,edi
@ -369,7 +403,10 @@ pyr_ai_safety_target:
cmp edi,esi
je @F
cmp poolActive3D[edi*4],1
je pyr_ai_safety_found
jne @F
invoke PoolBallLegal3D,edi
test eax,eax
jnz pyr_ai_safety_found
@@:
inc edi
cmp edi,16
@ -382,6 +419,45 @@ pyr_ai_safety_found:
PoolChooseAI3D ENDP
HandlePool3D PROC uses ebx esi edi
cmp poolState3D,POOL_SELECT
jne pyr_normal_input
invoke Pressed3D,31h
test eax,eax
jnz pyr_start_pyramid
invoke Pressed3D,32h
test eax,eax
jnz pyr_start_eight
cmp mouseClick3D,0
je pyr_handle_done
mov mouseClick3D,0
cmp mouseX3D,252
jb @F
cmp mouseY3D,17
jb pyr_selector_exit
@@:
cmp mouseX3D,40
jb pyr_handle_done
cmp mouseX3D,280
jae pyr_handle_done
cmp mouseY3D,80
jb pyr_handle_done
cmp mouseY3D,128
jb pyr_start_pyramid
cmp mouseY3D,144
jb pyr_handle_done
cmp mouseY3D,192
jae pyr_handle_done
pyr_start_eight:
invoke StartBilliards3D,1
ret
pyr_start_pyramid:
invoke StartBilliards3D,0
ret
pyr_selector_exit:
mov engineMode,MODE_WALK
call ClearKeys3D
ret
pyr_normal_input:
xor esi,esi
pyr_sink_tick:
mov eax,deltaTime3D
@ -409,6 +485,7 @@ pyr_sink_tick:
call SaveProgress3D
ret
pyr_update:
mov eax,deltaTime3D
cmp poolState3D,POOL_MOVING
jne pyr_strike
add poolAccumulator3D,eax
@ -451,9 +528,14 @@ pyr_idle:
cmp mouseX3D,202
jb pyr_handle_done
pyr_restart:
call InitPool3D
call ResetPool3D
ret
pyr_actor:
cmp poolState3D,POOL_HAND
jne @F
call EightHand3D
ret
@@:
cmp poolPlayer3D,1
jne pyr_human
cmp poolAI3D,0
@ -560,11 +642,45 @@ pyr_human_aim:
cmp pyrPickClick3D,0
je @F
mov pyrPickClick3D,0
cmp poolVariant3D,1
jne pyr_pick_object
xor esi,esi
eight_pick_pocket:
mov ecx,eax
sub ecx,poolPocketX3D[esi*4]
sar ecx,12
imul ecx,ecx
mov edx,ebx
sub edx,poolPocketY3D[esi*4]
sar edx,12
imul edx,edx
add ecx,edx
cmp ecx,81*16*16
jbe eight_pick_pocket_found
inc esi
cmp esi,6
jb eight_pick_pocket
pyr_pick_object:
invoke PyramidPick3D,eax,ebx
cmp eax,-1
je pyr_handle_done
cmp poolVariant3D,1
jne pyr_pick_pyramid
mov ebx,eax
call EightCount3D
invoke EightLegal3D,ebx
test eax,eax
jz pyr_handle_done
mov poolCalled3D,ebx
or poolCallManual3D,1
ret
pyr_pick_pyramid:
invoke PyramidSelect3D,eax
ret
eight_pick_pocket_found:
mov poolCalledPocket3D,esi
or poolCallManual3D,2
ret
@@:
cmp poolBreak3D,0
je pyr_mouse_aim
@ -578,6 +694,23 @@ pyr_mouse_aim:
invoke PoolAimPoint3D,eax,ebx
ret
pyr_mouse_footer:
cmp poolVariant3D,1
jne @F
cmp ebx,187
jb @F
cmp ebx,209
ja @F
cmp eax,128
jb @F
cmp eax,194
ja @F
inc poolCalledPocket3D
or poolCallManual3D,2
cmp poolCalledPocket3D,6
jb pyr_handle_done
mov poolCalledPocket3D,0
ret
@@:
cmp ebx,193
jb pyr_keys
cmp ebx,209
@ -669,6 +802,8 @@ pyr_toggle_guide:
xor pyrGuideOn3D,1
ret
pyr_cycle_cue:
cmp poolVariant3D,1
je eight_cycle_call
cmp poolBreak3D,0
jne pyr_handle_done
mov esi,pyrCue3D
@ -683,6 +818,26 @@ pyr_cycle_scan:
ret
pyr_cycle_found:
invoke PyramidSelect3D,esi
ret
eight_cycle_call:
call EightCount3D
mov esi,poolCalled3D
mov edi,16
eight_cycle_scan:
inc esi
and esi,15
cmp poolActive3D[esi*4],0
je eight_cycle_next
invoke EightLegal3D,esi
test eax,eax
jnz eight_cycle_found
eight_cycle_next:
dec edi
jnz eight_cycle_scan
ret
eight_cycle_found:
mov poolCalled3D,esi
or poolCallManual3D,1
pyr_handle_done:
ret
HandlePool3D ENDP

View file

@ -1,5 +1,16 @@
PyramidBall3D PROC uses ebx esi ball:DWORD,x:DWORD,y:DWORD,sizePx:DWORD
mov ebx,ball
cmp poolVariant3D,1
jne pyr_ball_palette
mov eax,sizePx
shr eax,1
mov edx,x
sub edx,eax
mov ecx,y
sub ecx,eax
invoke DrawAnimSprite3D,OFFSET poolEightPack3D,ebx,edx,ecx,sizePx,sizePx
jmp pyr_ball_number
pyr_ball_palette:
mov eax,pyrRoll3D[ebx*4]
shr eax,17
and eax,3
@ -15,6 +26,7 @@ PyramidBall3D PROC uses ebx esi ball:DWORD,x:DWORD,y:DWORD,sizePx:DWORD
mov ecx,y
sub ecx,eax
invoke DrawAnimSprite3D,OFFSET pyrBallsPack3D,esi,edx,ecx,sizePx,sizePx
pyr_ball_number:
cmp sizePx,12
jne pyr_ball_done
test ebx,ebx
@ -126,13 +138,40 @@ RenderPool3D PROC uses ebx esi edi
lea edi,screenBuffer
mov ecx,SCREEN_SIZE
rep movsd
cmp poolState3D,POOL_SELECT
jne pyr_render_game
invoke DrawBox,0,0,320,35,00151D20h
invoke DrawString8x8,16,12,OFFSET poolSelectTitle3D,00E8D7ADh
invoke DrawBoxBorder,252,3,60,14,002C3637h,00687672h,1
invoke DrawString8x8,258,6,OFFSET cardExitButton3D,00E9D8B0h
invoke DrawBoxBorder,40,80,240,48,00192720h,00D6B971h,2
invoke DrawString8x8,68,99,OFFSET poolSelectPyramid3D,00FFFFFFh
invoke DrawBoxBorder,40,144,240,48,00192720h,00D6B971h,2
invoke DrawString8x8,58,163,OFFSET poolSelectEight3D,00FFFFFFh
ret
pyr_render_game:
call EightAutoCall3D
xor esi,esi
pyr_render_mouths:
mov eax,poolPocketX3D[esi*4]
sar eax,16
mov ebx,poolPocketY3D[esi*4]
sar ebx,16
mov px,eax
mov py,ebx
invoke DrawCircle,eax,ebx,3,000B1010h
cmp poolVariant3D,1
jne @F
mov eax,poolPocketX3D[esi*4]
sar eax,16
mov ebx,poolPocketY3D[esi*4]
sar ebx,16
invoke DrawCircle,px,py,5,000B1010h
cmp esi,poolCalledPocket3D
jne @F
invoke DrawCircle,px,py,6,00D6B971h
invoke DrawCircle,px,py,4,000B1010h
@@:
inc esi
cmp esi,6
jb pyr_render_mouths
@ -148,7 +187,7 @@ pyr_render_mouths:
call PyramidDirection3D
cmp pyrGuideOn3D,0
je pyr_render_objects
call PyramidGuide3D
call PoolDrawPrediction3D
pyr_render_objects:
xor esi,esi
pyr_render_ball:
@ -160,6 +199,16 @@ pyr_render_ball:
mov eax,poolY3D[esi*4]
sar eax,15
mov py,eax
cmp poolVariant3D,1
jne @F
cmp esi,poolCalled3D
jne @F
mov eax,px
sar eax,1
mov ebx,py
sar ebx,1
invoke DrawCircle,eax,ebx,4,00D6B971h
@@:
cmp esi,pyrCue3D
jne @F
cmp poolState3D,POOL_AIM
@ -245,10 +294,20 @@ pyr_cue_draw:
jnz pyr_cue_draw
pyr_render_hud:
invoke DrawBox,0,0,320,20,00151D20h
invoke DrawString8x8,8,6,OFFSET pyrTitle3D,00E8D7ADh
mov ebx,OFFSET pyrTitle3D
cmp poolVariant3D,0
je @F
mov ebx,OFFSET poolTitle3D
@@:
invoke DrawString8x8,8,6,ebx,00E8D7ADh
invoke DrawNumber,112,6,pyrScore3D,00F4F3E8h
invoke DrawString8x8,128,6,OFFSET pyrScoreSep3D,00E8D7ADh
invoke DrawNumber,144,6,pyrScore3D[4],00F4F3E8h
cmp poolVariant3D,1
jne @F
invoke DrawString8x8,88,6,OFFSET pyrPlayerName3D,00E8D7ADh
invoke DrawString8x8,160,6,OFFSET pyrAIName3D,00E8D7ADh
@@:
mov ebx,OFFSET pyrPlayerName3D
cmp poolPlayer3D,0
je @F
@ -277,9 +336,20 @@ pyr_render_hud:
cmp poolState3D,POOL_STRIKE
jne @F
mov ebx,OFFSET poolShotText3D
@@:
cmp poolState3D,POOL_HAND
jne @F
mov ebx,OFFSET poolPlaceText3D
@@:
invoke DrawBox,0,22,320,15,00151D20h
invoke DrawString8x8,8,26,ebx,00E8D7ADh
cmp poolVariant3D,1
jne @F
mov ecx,poolPlayer3D
mov ecx,poolGroups3D[ecx*4]
mov eax,poolGroupTexts3D[ecx*4]
invoke DrawString8x8,176,26,eax,00E8D7ADh
@@:
cmp poolState3D,POOL_PENALTY
je @F
cmp poolState3D,POOL_CHOICE
@ -330,9 +400,36 @@ pyr_shelf_next:
add eax,201
invoke DrawBox,eax,199,2,9,00F4F0DDh
invoke DrawBoxBorder,12,215,74,22,0039412Fh,00AC9862h,1
cmp poolVariant3D,1
je eight_render_call
invoke DrawString8x8,18,222,OFFSET pyrCueText3D,00FFFFFFh
invoke DrawNumber,62,222,pyrCue3D,00EAD19Bh
invoke DrawBoxBorder,92,215,104,22,0039412Fh,00AC9862h,1
jmp eight_render_call_done
eight_render_call:
invoke DrawString8x8,18,222,OFFSET poolCallText3D,00FFFFFFh
cmp poolCalled3D,0
je eight_render_no_call
invoke DrawNumber,64,222,poolCalled3D,00EAD19Bh
jmp eight_render_pocket
eight_render_no_call:
invoke DrawString8x8,64,222,OFFSET poolUnsetText3D,00EAD19Bh
eight_render_pocket:
mov eax,poolCalledPocket3D
inc eax
jz eight_render_no_pocket
invoke DrawNumber,173,187,eax,00EAD19Bh
jmp eight_render_pocket_label
eight_render_no_pocket:
invoke DrawString8x8,173,187,OFFSET poolUnsetText3D,00EAD19Bh
eight_render_pocket_label:
invoke DrawString8x8,129,187,OFFSET poolPocketText3D,00FFFFFFh
eight_render_call_done:
mov ebx,0039412Fh
cmp pyrGuideOn3D,0
jne @F
mov ebx,00242A2Ch
@@:
invoke DrawBoxBorder,92,215,104,22,ebx,00AC9862h,1
invoke DrawString8x8,106,222,OFFSET pyrGuideText3D,00FFFFFFh
invoke DrawBoxBorder,202,215,106,22,00425232h,00BBA76Fh,1
mov ebx,OFFSET poolShotText3D

View file

@ -23,12 +23,14 @@ pyr_init_records:
jb pyr_init_records
invoke LoadAnimPack3D,OFFSET pyrTablePack3D,OFFSET pyrTablePath3D
invoke LoadAnimPack3D,OFFSET pyrBallsPack3D,OFFSET pyrBallsPath3D
invoke LoadAnimPack3D,OFFSET poolEightPack3D,OFFSET poolEightPath3D
ret
InitPyramidState3D ENDP
FreePyramidAssets3D PROC
invoke FreeAnimPack3D,OFFSET pyrTablePack3D
invoke FreeAnimPack3D,OFFSET pyrBallsPack3D
invoke FreeAnimPack3D,OFFSET poolEightPack3D
ret
FreePyramidAssets3D ENDP
@ -275,6 +277,11 @@ PoolPlaceCue3D PROC px:DWORD,py:DWORD
PoolPlaceCue3D ENDP
PoolBallLegal3D PROC ball:DWORD
cmp poolVariant3D,1
jne @F
invoke EightLegal3D,ball
ret
@@:
mov ecx,ball
xor eax,eax
cmp ecx,15
@ -288,11 +295,21 @@ PoolBallLegal3D PROC ball:DWORD
PoolBallLegal3D ENDP
PoolCountGroup3D PROC
cmp poolVariant3D,1
jne @F
call EightCount3D
ret
@@:
mov poolAtStart3D,0
ret
PoolCountGroup3D ENDP
PoolFinishShot3D PROC uses ebx esi edi
cmp poolVariant3D,1
jne @F
call EightFinish3D
ret
@@:
cmp pyrSettled3D,0
jne pyr_finish_done
mov pyrSettled3D,1
@ -426,6 +443,25 @@ PyramidChoice3D PROC uses ebx esi edi choice:DWORD
jne pyr_choice_done
cmp choice,3
ja pyr_choice_done
cmp poolVariant3D,1
jne pyr_choice_pyramid
mov ebx,pyrChoiceActor3D
test choice,1
jz @F
xor ebx,1
@@:
cmp choice,2
jb eight_choice_accept
call ResetPool3D
mov poolPlayer3D,ebx
ret
eight_choice_accept:
mov poolPlayer3D,ebx
mov poolBreak3D,0
mov poolState3D,POOL_AIM
mov poolTime3D,0
ret
pyr_choice_pyramid:
cmp choice,2
jb pyr_choice_actor
cmp pyrIllegalBreak3D,0

View file

@ -22,7 +22,7 @@ LoadAnimPack3D PROC uses ebx esi edi pack:DWORD,path:DWORD
mov ecx, [esi].AnimPack3D.height
test ecx, ecx
jz anim_close_failure
cmp ecx, 480
cmp ecx, 1024
ja anim_close_failure
imul eax, ecx
shl eax, 2
@ -293,6 +293,8 @@ props_copy_store:
InstallHallProps3D ENDP
FreeArrivalAssets3D PROC
call FreeScratchArt3D
call FreeNativeDiceArt3D
call FreeHallAssets3D
invoke FreeAnimPack3D, OFFSET streetPack3D
invoke FreeAnimPack3D, OFFSET guardPack3D
@ -309,6 +311,11 @@ FreeArrivalAssets3D PROC
invoke FreeAnimPack3D, OFFSET cityCarsPack3D
invoke FreeAnimPack3D, OFFSET cityBinPack3D
invoke FreeAnimPack3D, OFFSET cityCloudPack3D
invoke FreeAnimPack3D, OFFSET cityWakePack3D
invoke FreeAnimPack3D, OFFSET citySleevePack3D
invoke FreeAnimPack3D, OFFSET cityDecorPack3D
invoke FreeAnimPack3D, OFFSET cityLighterPack3D
invoke FreeAnimPack3D, OFFSET cityKneesPack3D
ret
FreeArrivalAssets3D ENDP

1211
src_graphics/scratch.inc Normal file

File diff suppressed because it is too large Load diff

Some files were not shown because too many files have changed in this diff Show more