feat: implement upper lounge, manual stairs, cabaret, and casino terminals
- Native player-controlled stairs, upper lounge bar, and Hell Tower infernal portal - Cabaret stage with musical acts, audience seating, and interaction - Dedicated cash/trophy wall terminals with native balance and achievement screens - Projecting 3D wall lanterns with ray/plane intersection - Six roaming guests with seated poses and card rank rendering updates - Synchronized launcher hash and updated binaries
8
.gitignore
vendored
|
|
@ -15,6 +15,14 @@ __pycache__/
|
|||
/build/actions/
|
||||
/build/arrival/
|
||||
/build/billiards/
|
||||
/build/card-ranks/
|
||||
/build/terminals/
|
||||
/build/hall-expansion/
|
||||
/build/guest-cast-preview.png
|
||||
/build/cabaret-ensemble-preview.png
|
||||
/build/cabaret-musicians-preview.png
|
||||
/build/upper-motion-preview.png
|
||||
/build/*.before-expansion
|
||||
/build/buckshot/
|
||||
/build/coin/
|
||||
/build/dice/
|
||||
|
|
|
|||
BIN
assets/animations/hall/cabaret-ensemble-v1.bin
Normal file
BIN
assets/animations/hall/cabaret-guitar-v1.bin
Normal file
BIN
assets/animations/hall/cabaret-piano-v1.bin
Normal file
BIN
assets/animations/hall/guest-cast-v1.bin
Normal file
BIN
assets/animations/hall/terminal-screen-v1.bin
Normal file
BIN
assets/animations/hall/terminals-v1.bin
Normal file
BIN
assets/animations/hall/terminals-v2.bin
Normal file
BIN
assets/animations/hall/upper-bar-v2.bin
Normal file
BIN
assets/animations/hall/upper-braziers-v3.bin
Normal file
BIN
assets/animations/hall/upper-games-v2.bin
Normal file
BIN
assets/animations/hall/upper-levers-v2.bin
Normal file
BIN
assets/animations/hall/upper-levers-v3.bin
Normal file
BIN
assets/animations/hall/upper-materials-v1.bin
Normal file
BIN
assets/animations/hall/upper-portal-v2.bin
Normal file
BIN
assets/animations/hall/upper-portal-v3.bin
Normal file
BIN
assets/sprites/hall/cabaret-ensemble-v1.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/sprites/hall/cabaret-musicians-v1.png
Normal file
|
After Width: | Height: | Size: 2 MiB |
BIN
assets/sprites/hall/guest-cast-v1.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
53
assets/sprites/hall/prompts-v7.json
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"tool": "built-in image_gen",
|
||||
"runtime": "MASM32 only; Python files are offline packers and native-test orchestration",
|
||||
"assets": [
|
||||
{
|
||||
"path": "assets/sprites/hall/cabaret-musicians-v1.png",
|
||||
"prompt": "Production sprite sheet for a retro realistic detailed hand-painted pixel art casino cabaret, transparent alpha background. EXACTLY TWO columns and FOUR rows, EIGHT clearly separated cells, generous transparent gaps, no grid lines, no captions. LEFT COLUMN in all four rows: the SAME adult male pianist in a black tuxedo seated on a proper piano bench AT THE KEYBOARD of the SAME beautiful glossy black BABY GRAND PIANO, entire instrument, all three legs, pedals, open triangular lid, white and black keys and pianist fully visible. Audience front three-quarter view. Pianist sits at the LEFT end of the piano facing RIGHT toward its keyboard; both forearms naturally extend to the actual keys immediately in front of his body, fingers touch keys. The piano's curved body and raised lid extend to his RIGHT. His head looks down toward his hands on the keys, he is not turned sideways away from the keyboard. This is ONE coherent combined pianist-and-piano sprite, not detached pieces and not a rectangular counter. RIGHT COLUMN in all four rows: SAME adult male jazz guitarist, burgundy waistcoat, rolled-up white shirt sleeves, black trousers, full body standing, holding a golden-brown hollow-body jazz guitar in natural playing position. Left hand on guitar neck, right hand over strings. Rows1,2,3,4 are successive SUBTLE playing poses, coherent placement, identity, piano shape and dimensions; no camera movement and no scale change. Warm amber stage light with deep burgundy shadows, sharp detailed Doom-era bitmap game art, NOT cartoon, no blurry painted scenery. Entire piano, bench, feet, head, guitar and neck remain inside each cell. Truly transparent backdrop, no floor, no background scenery, no particles, no text.",
|
||||
"transparencyEditPrompt": "Edit this sprite sheet ONLY to remove the brown background completely. Make EVERY background pixel genuinely transparent alpha, including all brown glows, shadows between cells, spaces under piano and bench, gaps around legs and inside the piano silhouette. Preserve the exact eight coherent musician sprites, all their colours, all black piano and guitar details, current pixel detail, identities, positions and sizes unchanged. The four left sprites each include pianist seated AT the actual grand piano keyboard, hands on keys; preserve the whole piano and bench. The four right sprites are the guitarist. Do not add any backdrop, glow, floor, frame, vignette or text. Transparent cutouts only.",
|
||||
"approvedLayout": {
|
||||
"size": [
|
||||
1024,
|
||||
1536
|
||||
],
|
||||
"rows": 4,
|
||||
"columnBoundaries": [
|
||||
0,
|
||||
640,
|
||||
1024
|
||||
],
|
||||
"anchor": "common opaque bounds across all four poses per performer"
|
||||
},
|
||||
"outputs": [
|
||||
"assets/animations/hall/cabaret-piano-v1.bin",
|
||||
"assets/animations/hall/cabaret-guitar-v1.bin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "assets/sprites/hall/guest-cast-v1.png",
|
||||
"approvedLayout": {
|
||||
"columns": 6,
|
||||
"rows": 5,
|
||||
"framesPerGuest": 6,
|
||||
"walkMapping": [
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
2
|
||||
]
|
||||
},
|
||||
"output": "assets/animations/hall/guest-cast-v1.bin"
|
||||
},
|
||||
{
|
||||
"path": "assets/sprites/hall/cabaret-ensemble-v1.png",
|
||||
"prompt": "A production sprite sheet on a genuinely transparent alpha background, for a retro realistic hand-painted detailed 1990s casino PC game. EXACTLY 3 columns and 4 rows, 12 separate full body sprites, precisely evenly spaced cells, no grid lines or text. SAME character in each COLUMN: column1 adult woman cabaret jazz singer, silver floor-length evening gown, short dark wavy hair, holding a handheld vintage microphone; column2 adult male pianist in black tuxedo seated on a small piano stool, arms extended playing invisible keyboard in front of him, NO PIANO included; column3 adult woman saxophonist in black and gold evening trouser outfit, holding brass saxophone. ROW1 base performing pose, ROW2 small natural alternate pose, ROW3 expressive performing pose, ROW4 returning pose. Consistent costume, face, camera angle, scale and foot baseline throughout each column. Entire heads, shoes, sax and microphone in each cell, generous empty margins, do not touch adjacent cells. Warm amber stage highlights and burgundy shadows. Straight-on audience eye view. No scenery, no stage floor, no spotlight beam, no piano, no frame, no decorative particles. Sharp pixel-art edges, not blurry and not cartoon.",
|
||||
"output": "assets/animations/hall/cabaret-ensemble-v1.bin"
|
||||
},
|
||||
{
|
||||
"path": "assets/sprites/hall/upper-materials-v1.png",
|
||||
"prompt": "Production environment texture atlas for detailed retro realistic pixel art casino, EXACT 2 columns by 2 rows of square seamless material tiles, no perspective, orthographic flat material scan, crisp hand-painted bitmap texture, no captions, no borders between tiles. Top-left: dark patinated steel service door surface, riveted broad metal panels, subtle scratches and worn paint, NO handle or opening, no text. Top-right: luxurious deep teal and charcoal marble countertop with fine pale natural veins, no objects, no lamps. Bottom-left: dark burgundy geometric Art Deco carpet with muted gold and cool green fine ornament, seamless pattern at small scale, not wood. Bottom-right: midnight teal casino ceiling with understated embossed square coffer pattern and warm brass thin seams, seamless, not a wall or outdoor facade. Entire image opaque, all four tiles equally sized and cleanly separated, no people, no bottles, no furniture, no windows, no logos.",
|
||||
"output": "assets/animations/hall/upper-materials-v1.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
62
assets/sprites/hall/prompts-v8.json
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
{
|
||||
"tool": "built-in image_gen",
|
||||
"runtime": "All rendering, physics, camera, interaction and animation timing are MASM32; these Python packers are offline tools only.",
|
||||
"assets": [
|
||||
{
|
||||
"path": "assets/sprites/hall/guest-cast-v1.png",
|
||||
"output": "assets/animations/hall/guest-cast-v1.bin",
|
||||
"normalization": "Mirror only the emerald guest's seated pose so the paired upper-lounge chairs face toward their shared table; idle and walking frames stay unchanged."
|
||||
},
|
||||
{
|
||||
"path": "assets/sprites/hall/upper-portal-v2.png",
|
||||
"outputs": [
|
||||
"assets/animations/hall/upper-portal-v2.bin",
|
||||
"assets/animations/hall/upper-levers-v2.bin"
|
||||
],
|
||||
"usage": "Preserved source and offline inactive-portal reference; superseded for runtime animation."
|
||||
},
|
||||
{
|
||||
"path": "assets/sprites/hall/upper-furniture-v2.png",
|
||||
"outputs": [
|
||||
"assets/animations/hall/upper-games-v2.bin",
|
||||
"assets/animations/hall/upper-bar-v2.bin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"path": "assets/sprites/hall/upper-portal-v3.png",
|
||||
"prompt": "Use the supplied atlas as a STRICT identity reference for the gothic arched portal in its first four cells. Generate a NEW game animation sprite sheet: EXACTLY THREE columns and FOUR rows, TWELVE separate full arch sprites in row-major playback order, generous transparent margins between cells, no text, no grid. Every frame shows the SAME complete activated black iron and brass arched portal from exactly the same front three-quarter camera, with the same red gem at its apex, same columns, same base, same outline, same size and fixed ground anchor. Only the crimson/amber fire vortex INSIDE the arch animates: a coherent clockwise flowing spiral advances gradually across twelve frames; glow gently pulses, a few tiny sparks are contained inside the opening, loop frame12 naturally into frame1. The solid frame must NEVER change shape, tilt, move or scale. Entire arch, top gem and base visible in every cell, no clipping. Match the supplied detailed realistic 1990s retro bitmap game sprite style. Genuinely transparent alpha background, including all space around the arch, no brown backdrop, no floor, no room, no scenery, no other objects and NO LEVERS on this new sheet. Each portal cutout isolated cleanly.",
|
||||
"outputs": [
|
||||
"assets/animations/hall/upper-portal-v3.bin"
|
||||
],
|
||||
"layout": {
|
||||
"columns": 3,
|
||||
"rows": 4
|
||||
},
|
||||
"normalization": "Rigid arch locked; only generated interior vortex varies. One inactive frame plus twelve active frames, 256x512 for native power-of-two wall sampling."
|
||||
},
|
||||
{
|
||||
"path": "assets/sprites/hall/upper-levers-v3.png",
|
||||
"prompt": "Use ONLY the two floor-mounted lever sprites in the bottom row of this supplied atlas as strict visual reference. Generate a six-frame mechanical lever pull animation sheet, EXACTLY THREE columns TWO rows, six isolated full pedestal sprites in row-major time order. Same ornate black iron and brass pedestal, same bolted broad base, same crimson inset, same red teardrop handle attached to the same round side pivot. Fixed camera, fixed pedestal dimensions, fixed bottom anchor; the pedestal itself must not change between frames. Frame1 handle fully UP; frames2,3,4,5 show SMALL successive angular steps of the handle rotating DOWN clockwise around the pivot; frame6 fully DOWN diagonally toward lower-right, green indicator illuminated. The handle is a rigid lever attached to pivot, never detached. Final green light only comes on in last frame. Fine crisp realistic retro hand-painted 1990s bitmap game sprite detail, match supplied style. Generous empty margins, entire base and all handle positions in every cell, no portal, no room, no floor, no labels, no text, no captions or grid. GENUINELY transparent alpha background without a brown backdrop or glow outside the objects.",
|
||||
"outputs": [
|
||||
"assets/animations/hall/upper-levers-v3.bin"
|
||||
],
|
||||
"layout": {
|
||||
"columns": 3,
|
||||
"rows": 2
|
||||
},
|
||||
"normalization": "Common bounds and foot anchor. Six positions across a 600ms native lever pull."
|
||||
},
|
||||
{
|
||||
"path": "assets/sprites/hall/upper-braziers-v3.png",
|
||||
"prompt": "Game sprite animation atlas, exactly TWO columns TWO rows, FOUR full-body isolated successive frames of the SAME gothic floor brazier. A heavy black wrought iron tripod pedestal with a broad dark brass bowl and pointed iron ribs, old chains hanging tightly from bowl, deep glowing coals and a sinister CRIMSON red fire with amber core. The four frames animate the flames flickering naturally while the identical rigid metal bowl, chains, legs, scale, viewpoint and ground anchor stay fixed. Whole tall pedestal and full flames visible in every cell, generous gaps and margins. Realistic detailed hand-painted 1990s retro pixel game art for a dark casino's infernal portal alcove. No people, no skulls, no creatures, no room, no floor, no writing or grid. Truly transparent alpha background; no brown backdrop, no light halo or pixels outside the sprite silhouette.",
|
||||
"outputs": [
|
||||
"assets/animations/hall/upper-braziers-v3.bin"
|
||||
],
|
||||
"layout": {
|
||||
"columns": 2,
|
||||
"rows": 2
|
||||
},
|
||||
"normalization": "Four flame phases; rigid lower pedestal retained."
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/sprites/hall/upper-braziers-v3.png
Normal file
|
After Width: | Height: | Size: 2.1 MiB |
BIN
assets/sprites/hall/upper-furniture-v2.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
assets/sprites/hall/upper-levers-v3.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
assets/sprites/hall/upper-materials-v1.png
Normal file
|
After Width: | Height: | Size: 3.1 MiB |
BIN
assets/sprites/hall/upper-portal-v2.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
assets/sprites/hall/upper-portal-v3.png
Normal file
|
After Width: | Height: | Size: 2.7 MiB |
BIN
assets/sprites/terminals/body-side-rear-v2.png
Normal file
|
After Width: | Height: | Size: 2 MiB |
BIN
assets/sprites/terminals/cash-front-v1.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
assets/sprites/terminals/display-v1.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
assets/sprites/terminals/lantern-glass-v1.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
26
assets/sprites/terminals/prompts.json
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"tool": "Built-in image_gen",
|
||||
"purpose": "Retro casino kiosk front surfaces, lantern glass and native fullscreen kiosk background. Game runtime remains MASM x86.",
|
||||
"assets": [
|
||||
{
|
||||
"file": "cash-front-v1.png",
|
||||
"prompt": "Use case: stylized-concept. Asset type: front-face texture for a human-scale 2.5D retro casino cash terminal, not a scene. Create ONE full-bleed rectangular front elevation, perfectly orthographic, width-to-height ratio 1:2, edges at image borders. A carefully crafted 1930s American casino Art Deco cash/balance kiosk: aged dark walnut cabinet, engraved brass corners and trim, green glass CRT screen occupying upper central third, recessed metal numerical keypad below, slim receipt slot, brass coin ornament on lower door, realistic material construction interpreted as crisp hand-painted 1990s adventure-game pixel art, consistent individual pixels, restrained details, excellent readability, not photorealistic, not flat primitive rectangles. Screen dark emerald with subtle luminous center and NO text or symbols, a blank horizontal brass title plaque above. Light from upper left. No perspective sides, no background, no floor, no shadow beyond boundary, no people, no labels, no letters, no watermark. Whole front is a rectangle filled with cabinet material, not transparent."
|
||||
},
|
||||
{
|
||||
"file": "trophy-front-v1.png",
|
||||
"prompt": "Use case: stylized-concept. Asset type: front-face texture for a human-scale 2.5D retro casino achievements terminal. ONE full-bleed rectangular front elevation, perfectly orthographic, aspect ratio 1:2, cabinet edges touch image borders. Crafted vintage casino Art Deco walnut-and-brass trophy kiosk, same 1930s furniture family as a brass-trimmed cash machine: blank brass header plaque, broad recessed burgundy-black glass display in upper third, a small golden laurel and trophy relief inside the LOWER door, beautiful carved dark walnut panels and brass corner caps, two inset physical arrow buttons below the glass. Glass screen must be blank and very dark, NO text or icons on the screen. Crisp hand-painted 1990s adventure-game pixel art, solid dimensional material shading, not photorealism, not flat vector. Neutral warm light from upper left. No people, no perspective sides, no separate objects, no backdrop or floor, no protruding shadows, no labels/letters/digits/watermark. Completely opaque rectangular texture."
|
||||
},
|
||||
{
|
||||
"file": "lantern-glass-v1.png",
|
||||
"prompt": "Use case: stylized-concept. Asset type: seamless front elevation texture for the rectangular glass body of a small wall-mounted casino lantern. ONE completely opaque full-bleed rectangle, width-to-height ratio 1:2. Aged dark bronze cage with slim brass edge rails and two slender crossbars; warm ivory-amber glowing frosted glass panes fill the middle. Rich but readable crisp hand-painted 1990s adventure-game pixel art consistent with dark walnut/brass casino furniture; controlled pixel clusters, luminous glass, dark solid borders. The entire image is the lantern body front surface, no mounting arm, no roof, no perspective side, no background, no transparency, no detached glow or shadows outside object, no people, no text, no symbols, no watermark. It will wrap a genuine three-dimensional small box with separate mounting arm and roof."
|
||||
},
|
||||
{
|
||||
"file": "display-v1.png",
|
||||
"prompt": "Use case: stylized-concept. Asset type: full-screen interactive kiosk background for a retro 2.5D casino game, landscape 4:3. Straight-on close-up of the upper display of a 1930s Art Deco casino terminal, carved walnut narrow outer frame and thin aged brass trim, large dark nearly uniform green-black glass screen occupying x=6%..94%, y=10%..86% of the composition, perfectly empty to display 8-bit Russian text through the game renderer. Two inset brass triangular paging buttons at the bottom left and bottom right outside the main screen. Narrow blank brass title strip along the top. A few tactile screws and very restrained reflections at the extreme edges only. Crisp hand-painted pixel art, material shading and carved relief, late-1990s adventure game furniture. No perspective, no people, no keyboard, no letters, no digits, no icons inside the screen, no decorative objects obscuring it, no watermark. Screen must remain extremely dark and flat, free of grain, glare or bright gradients in the text area. The display fills the whole frame, not a small monitor floating against background."
|
||||
},
|
||||
{
|
||||
"file": "body-side-rear-v2.png",
|
||||
"prompt": "Use case: stylized-concept. Asset type: dedicated SIDE and REAR surface texture of a vintage casino COMPUTER terminal; ONE full-bleed perfectly orthographic rectangle, aspect ratio 1:2. Dark walnut veneered equipment housing matching an Art Deco brass-trimmed 1930s casino cash kiosk. Mostly clean continuous vertical woodgrain, narrow brass corner strips at left and right edges, small recessed dark bronze ventilation grille in the upper quarter, four tiny service screws, flush lower rectangular metal service hatch without a handle. It must read as a COMPUTER EQUIPMENT ENCLOSURE, not furniture storage, not a bar or a refrigerator. Crisp hand-painted 1990s adventure-game pixel art with controlled pixel clusters, consistent scale, subtle surface wear, warm upper-left light. NO shelves, NO bottles, NO alcohol, NO glasses, NO dishes, NO contents, NO doors with handles, NO countertop, NO screen, NO keyboard, NO letters, NO numbers, NO text, NO logos, NO background, NO perspective, NO separate objects. Opaque texture fills every pixel to the borders."
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/sprites/terminals/trophy-front-v1.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
BIN
bin/poker.exe
|
|
@ -8,7 +8,7 @@ namespace AssemblerCasinoLauncher
|
|||
{
|
||||
static class Program
|
||||
{
|
||||
const string ExpectedPokerSha256 = "3CE946233ABE1D9139501EB56A75B2BE0D9A99714048C5F0A56A150D35F2295C";
|
||||
const string ExpectedPokerSha256 = "C0B5A1DD9A678E68599724138AF009D29C935EEFDD6D70D28789AABF071395F8";
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
|
|
|
|||
91
casino3d.inc
|
|
@ -806,6 +806,30 @@ col_loop:
|
|||
sar eax, 16
|
||||
mov mapY, eax
|
||||
|
||||
cmp portalRendering3D,0
|
||||
je wall_ray_start_ready
|
||||
mov ebx,col
|
||||
mov ecx,portalClipDepth3D[ebx*4]
|
||||
test ecx,ecx
|
||||
jnz @F
|
||||
mov zBuffer[ebx*4],7FFFFFFFh
|
||||
jmp next_col
|
||||
@@:
|
||||
; Start DDA just inside the destination aperture, never in its rear wall.
|
||||
add ecx,FP_1/100
|
||||
mov eax,rayDirX
|
||||
imul ecx
|
||||
shrd eax,edx,16
|
||||
add eax,playerX
|
||||
sar eax,16
|
||||
mov mapX,eax
|
||||
mov eax,rayDirY
|
||||
imul ecx
|
||||
shrd eax,edx,16
|
||||
add eax,playerY
|
||||
sar eax,16
|
||||
mov mapY,eax
|
||||
wall_ray_start_ready:
|
||||
mov eax, rayDirX
|
||||
test eax, eax
|
||||
jnz calc_delta_x
|
||||
|
|
@ -1165,6 +1189,20 @@ wall_sample_material:
|
|||
mov eax, [ebx+eax*4]
|
||||
jmp wall_shade_pixel
|
||||
wall_sample_indoor_hd:
|
||||
cmp edx,122
|
||||
jb wall_sample_existing_hd
|
||||
cmp edx,125
|
||||
ja wall_sample_existing_hd
|
||||
mov ebx,upperMaterialArt3D.pixels
|
||||
test ebx,ebx
|
||||
jz wall_upper_material_missing
|
||||
sub edx,122
|
||||
shl edx,18
|
||||
add ebx,edx
|
||||
jmp wall_room_hd_coordinates
|
||||
wall_upper_material_missing:
|
||||
mov edx,102
|
||||
wall_sample_existing_hd:
|
||||
cmp edx, 80
|
||||
jb wall_sample_standard
|
||||
cmp edx, 103
|
||||
|
|
@ -1180,6 +1218,7 @@ wall_sample_room_hd:
|
|||
sub edx, 80
|
||||
shl edx, 18
|
||||
add ebx, edx
|
||||
wall_room_hd_coordinates:
|
||||
mov eax, edi
|
||||
shr eax, 15
|
||||
and eax, 255
|
||||
|
|
@ -1368,6 +1407,12 @@ RenderMinimap PROC uses ebx esi edi
|
|||
radar_spr_loop:
|
||||
cmp esi, NUM_SPRITES
|
||||
jge draw_player_radar
|
||||
cmp esi,17
|
||||
je radar_skip_sprite
|
||||
cmp esi,19
|
||||
je radar_skip_sprite
|
||||
cmp esi,21
|
||||
je radar_skip_sprite
|
||||
mov eax, esi
|
||||
imul eax, SIZEOF SpriteObject
|
||||
mov eax, [casinoSprites + eax].posX
|
||||
|
|
@ -1386,6 +1431,7 @@ radar_spr_loop:
|
|||
add eax, radarY
|
||||
|
||||
invoke DrawPixel, ebx, eax, 00FFAA00h
|
||||
radar_skip_sprite:
|
||||
inc esi
|
||||
jmp radar_spr_loop
|
||||
|
||||
|
|
@ -2392,18 +2438,12 @@ RenderShopGame ENDP
|
|||
; IN-ENGINE 2.5D MINI-GAME: ATM TERMINAL
|
||||
; ==============================================================================
|
||||
HandleATMInput PROC
|
||||
call HandleTerminal3D
|
||||
ret
|
||||
HandleATMInput ENDP
|
||||
|
||||
RenderATMGame PROC
|
||||
invoke DrawBox, 0, 0, 320, 240, 00172120h
|
||||
invoke DrawString8x8, 24, 30, OFFSET textATM3D, 00D8BC6Ah
|
||||
invoke DrawString8x8, 24, 70, OFFSET textBank3D, 00FFFFFFh
|
||||
invoke FormatNumber, bankroll, OFFSET hudNumBuffer
|
||||
invoke DrawString8x8, 96, 70, OFFSET hudNumBuffer, 0089DAB2h
|
||||
invoke DrawString8x8, 24, 115, OFFSET textATMNote3D, 00B7C8C1h
|
||||
invoke DrawString8x8, 24, 140, OFFSET textATMNote2_3D, 00B7C8C1h
|
||||
invoke DrawString8x8, 24, 208, OFFSET textBack3D, 00D8BC6Ah
|
||||
call RenderTerminal3D
|
||||
ret
|
||||
RenderATMGame ENDP
|
||||
|
||||
|
|
@ -4378,8 +4418,11 @@ unlock_done:
|
|||
UnlockMouse3D ENDP
|
||||
|
||||
UpdateMouseLockState PROC
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je lock_interior_check
|
||||
cmp engineMode, MODE_WALK
|
||||
jne unlock_it
|
||||
lock_interior_check:
|
||||
cmp paused3D, 0
|
||||
jne unlock_it
|
||||
invoke GetForegroundWindow
|
||||
|
|
@ -4441,6 +4484,10 @@ skip_mouse_look:
|
|||
|
||||
; Movement speed with sprint (Shift = 10h)
|
||||
mov eax, deltaTime3D
|
||||
cmp eax,50
|
||||
jbe @F
|
||||
mov eax,50
|
||||
@@:
|
||||
imul eax, 200
|
||||
mov speed, eax
|
||||
cmp keys3D[10h], 0
|
||||
|
|
@ -4555,11 +4602,14 @@ check_total_movement:
|
|||
mov playerY, eax
|
||||
|
||||
input_action:
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je player_input_complete
|
||||
invoke Pressed3D, 45h
|
||||
test eax, eax
|
||||
jz @F
|
||||
call Trigger3DInteraction
|
||||
@@:
|
||||
player_input_complete:
|
||||
ret
|
||||
HandlePlayerInput ENDP
|
||||
|
||||
|
|
@ -4568,10 +4618,20 @@ CheckInteractions PROC uses ebx esi edi
|
|||
mov nearest, 262144
|
||||
mov interactTargetId, 0
|
||||
mov interactPromptPtr, 0
|
||||
cmp hallLevel3D,0
|
||||
jne interaction_done
|
||||
xor esi, esi
|
||||
interaction_scan:
|
||||
cmp esi, NUM_SPRITES
|
||||
jge interaction_done
|
||||
cmp esi,3
|
||||
jb interaction_next
|
||||
cmp esi,17
|
||||
je interaction_next
|
||||
cmp esi,19
|
||||
je interaction_next
|
||||
cmp esi,21
|
||||
je interaction_next
|
||||
imul edi, esi, SIZEOF SpriteObject
|
||||
cmp [casinoSprites+edi].interactId, 0
|
||||
je interaction_next
|
||||
|
|
@ -4757,13 +4817,13 @@ check_target_shop:
|
|||
check_target_atm:
|
||||
cmp eax, INTERACT_ATM
|
||||
jne check_target_terminal
|
||||
mov engineMode, MODE_ATM
|
||||
invoke BeginTerminal3D,0
|
||||
ret
|
||||
|
||||
check_target_terminal:
|
||||
cmp eax, INTERACT_TERMINAL
|
||||
jne check_target_observer
|
||||
call BeginUnavailable3D
|
||||
invoke BeginTerminal3D,1
|
||||
ret
|
||||
|
||||
check_target_observer:
|
||||
|
|
@ -5169,8 +5229,11 @@ dispatch_frame:
|
|||
call ArrivalEscape3D
|
||||
jmp engine_tick
|
||||
@@:
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je pause_interior_walk
|
||||
cmp engineMode, MODE_WALK
|
||||
jne leave_table
|
||||
pause_interior_walk:
|
||||
xor paused3D, 1
|
||||
jmp engine_tick
|
||||
leave_table:
|
||||
|
|
@ -5231,6 +5294,8 @@ check_walk_mode:
|
|||
call UpdateCamera3D
|
||||
call CheckInteractions
|
||||
call HandlePlayerInput
|
||||
call UpdateInteriorTravel3D
|
||||
call UpdateInteriorEye3D
|
||||
call UpdateCamera3D
|
||||
call RenderCeilingAndFloor
|
||||
call RenderWalls
|
||||
|
|
@ -5241,6 +5306,12 @@ check_walk_mode:
|
|||
call RenderMinimap
|
||||
jmp present_frame
|
||||
check_slots_mode:
|
||||
cmp engineMode,MODE_CABARET
|
||||
jne @F
|
||||
call HandleCabaret3D
|
||||
call RenderCabaret3D
|
||||
jmp present_frame
|
||||
@@:
|
||||
cmp engineMode,MODE_SCRATCH
|
||||
jne @F
|
||||
call HandleScratch3D
|
||||
|
|
|
|||
BIN
dist/Assembler Casino.exe
vendored
23
docs/CASINO_NEXT_PASS.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Casino Completion Pass
|
||||
|
||||
Runtime remains MASM32 assembly. Existing player saves and unrelated edits stay intact.
|
||||
|
||||
## Integrated World Pass
|
||||
|
||||
- [x] Six visually distinct roaming guests, including women; matching seated poses.
|
||||
- [x] Cabaret with changing acts, audience seats, a programme and meaningful interaction.
|
||||
- [x] Player-controlled stairs and an opening steel basement door, without timed camera travel.
|
||||
- [x] Upper lounge with its own bar and access to Hell Tower, Buckshot and Russian roulette.
|
||||
|
||||
## Following Integration
|
||||
|
||||
- [ ] Native three/four-seat poker: betting, all-ins, side pots and split pots.
|
||||
- [ ] European roulette: split, street, corner and six-line bets, winning bet highlights.
|
||||
- [ ] Game-specific achievements with saved progress and unlock notifications.
|
||||
- [ ] More ergonomic billiards power control without changing the verified physics/rules.
|
||||
- [ ] Scene audit: dimensions, fixtures, intersections and dedicated materials.
|
||||
- [ ] Video poker; then consider baccarat and Sic Bo as separate complete games.
|
||||
|
||||
An unchecked item is not implemented or not yet verified. Asset generation alone
|
||||
does not count as a finished feature. Published build details belong in the
|
||||
corresponding feature document after native verification.
|
||||
93
docs/CASINO_TERMINALS.md
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
# Casino Terminals and Wall Lanterns
|
||||
|
||||
The runtime is MASM x86 only. Python is used solely to compile art and run offline
|
||||
native regression fixtures, never to implement a second game runtime.
|
||||
|
||||
## World Objects
|
||||
|
||||
- Cash and achievement stations retain interaction IDs 7 and 17. They now stand
|
||||
against the south face of the east lounge's wall at y=13: centres (28,13.22)
|
||||
and (29.5,13.22), screens facing south into the hall. Their backs leave a 2 cm
|
||||
gap to the wall rather than intersecting it. The old floating positions
|
||||
(26,16) and (26,20) are no longer rendered or collidable.
|
||||
Interaction/minimap anchors and the visitor ATM approach move with the cabinets.
|
||||
- Each new cabinet has a 0.7 x 0.4 metre footprint, 1.6 metre height, textured
|
||||
dedicated walnut equipment sides/rear, a metal lid and a brass-trimmed front.
|
||||
Solid footprints stop the player walking through the cabinets while preserving
|
||||
the front approach.
|
||||
- Eight wall lanterns now have a solid glass body projecting 0.3 metres from
|
||||
the wall, a horizontal metal arm, a mounting plate and top/bottom surfaces.
|
||||
They use real ray/plane intersections and per-pixel depth, not a wall decal
|
||||
or a sprite that rotates towards the player. Existing baked lighting remains.
|
||||
|
||||
## Native Screens
|
||||
|
||||
Both stations open the same full-screen terminal, initially showing their own
|
||||
page. The four pages are balance and three achievement pages. Mouse arrows and
|
||||
Left/Right cycle through them; Exit, E and the engine's Escape handler return to
|
||||
the hall without opening a console or changing a wager.
|
||||
|
||||
Balance shows the actual wallet, owned card backs out of 13 and unlocked
|
||||
achievements out of eight. No new money transfer or deposit is implied.
|
||||
|
||||
Eight achievements have visible conditions and progress:
|
||||
|
||||
| Condition | Saved Bit |
|
||||
| --- | --- |
|
||||
| Reach a wallet of 50,000 chips | 8 |
|
||||
| Reach 100,000 chips | 9 |
|
||||
| Reach 500,000 chips | 10 |
|
||||
| Reach 1,000,000 chips | 11 |
|
||||
| Purchase a card back | 4, existing merchant bit |
|
||||
| Own five purchased backs, excluding the default | 12 |
|
||||
| Claim a visitor contract reward | 13 |
|
||||
| Claim six rewards during the current visitor/journal rotation | 14 |
|
||||
|
||||
Conditions are checked whenever native progress saves and when opening a kiosk.
|
||||
Unlocks are permanent even if the wallet falls or visitors change. Existing
|
||||
achievement bits are not cleared. The existing signed profile field stores the
|
||||
mask without a save-format change; no reward currency is granted for these
|
||||
achievements. Old peaks that were never recorded cannot be reconstructed.
|
||||
|
||||
## Art
|
||||
|
||||
Five original generated PNG sources and the exact built-in image_gen prompts
|
||||
are in `assets/sprites/terminals/`. `tools/generators/pack_terminals.py` resizes
|
||||
the art, compiles Russian cabinet plaques using the game's CP1251 font, and
|
||||
writes `terminals-v2.bin` (four 256x512 BGRA frames) plus
|
||||
`terminal-screen-v1.bin` (one 640x480 frame). Sources remain unchanged.
|
||||
|
||||
The first version incorrectly reused bar-shelf material 86 on the enclosure,
|
||||
which exposed bottles along the cabinet edges from the rear. V2 uses dedicated
|
||||
material 121 for every side/rear face and a metal lid. Its source is
|
||||
`assets/sprites/terminals/body-side-rear-v2.png`, generated with the built-in
|
||||
image_gen tool. Existing front, lamp and screen pixels are unchanged. The old
|
||||
v1 pack is retained for pre-update executables, not modified in place.
|
||||
|
||||
Missing art leaves a safe, readable native screen; it does not block exit or
|
||||
change a wallet. World pack dimensions and frame counts are validated on load.
|
||||
|
||||
## Verification
|
||||
|
||||
Run `C:\Python314\python.exe tools/tests/check_terminals.py` to compile fresh
|
||||
MASM handlers, geometry and interaction dispatch against the existing native
|
||||
test helpers. The helper object is copied with COFF visibility changes only;
|
||||
shipping instructions and binaries are never patched.
|
||||
|
||||
The fixture checks threshold/sticky unlocks, preserved unrelated bits, actual
|
||||
signed profile save/load in `build/terminals/test-profile.sav`, mouse/key paging
|
||||
and exit, missing art, cabinet collisions and fourteen native framebuffer captures,
|
||||
including the requested wall/lounging-area view, real front interaction, clear
|
||||
old footprints and front/side/rear views of both stations. Rear inspection uses
|
||||
an isolated model frame because the back is now against a solid wall in play.
|
||||
The former bar material is
|
||||
temporarily poisoned in test memory; isolated side/rear cabinet frames must
|
||||
remain pixel-identical. `--prove-regression` compiles a test-only old-material
|
||||
variant and asserts that this test rejects it. Shipping sources are not mutated.
|
||||
It cannot write the user's profile. `--parity` compares the tested procedures
|
||||
with a completed shipping object, normalizing internal/external call encoding.
|
||||
Do not run parity while the shipping assembler is still writing the object.
|
||||
|
||||
The full game is still assembled through `tools/tests/build_graphics.ps1` and
|
||||
published through `tools/tests/publish_graphics.ps1`. The latter refreshes the
|
||||
existing C# launcher's integrity hash; that launcher is not assembly code.
|
||||
126
docs/HALL_EXPANSION.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# Upper Lounge, Manual Stairs and Cabaret
|
||||
|
||||
## Native Implementation
|
||||
|
||||
All movement, collision, camera height, room changes, interaction, rendering and
|
||||
animation run in MASM32. The Python files only compile bitmap packs or build and
|
||||
run the actual native test executable. No alternate game runtime was added.
|
||||
|
||||
The east entrance, at approximately (30,21) in the main hall, leads to an enclosed
|
||||
wooden stairwell. WASD, turning and mouse look stay active. Twenty treads rise
|
||||
2.6 world units; the camera height follows position, not elapsed animation time.
|
||||
The stairwell may be stopped on or reversed anywhere. Crossing its landing joins
|
||||
the upper lounge; returning uses the same route. Room-local coordinate translations
|
||||
at door thresholds preserve facing and never advance the player on a timer.
|
||||
|
||||
The upper lounge is a compact 13-by-12-unit room with a dedicated carpet, teal
|
||||
coffers, marble bar, seating, guests and projecting lanterns. Buckshot, Russian
|
||||
roulette and Hell Tower are available there; their old ground-floor billboards
|
||||
and interaction targets are suppressed. Their gameplay code is unchanged.
|
||||
The two game stations now use distinct detailed table cutouts: a shotgun,
|
||||
shells and handcuffs for Buckshot; an octagonal revolver/whiskey table for
|
||||
Russian roulette. Their collision and interaction centres remain shared.
|
||||
The bar uses a full-body bartender in the walking view and a separate close
|
||||
serving animation when seated. Bar stools do not obstruct that seated view.
|
||||
|
||||
The original iron door remains in the east-facing alcove at (28.5,10.5), using
|
||||
the same door artwork as before the stair expansion. Its control opens it sideways
|
||||
without moving the player
|
||||
or change modes. The closed leaf blocks the doorway. Walking through its open
|
||||
threshold enters the concrete stairwell, which also uses normal movement. The
|
||||
old timed descent has been removed. The descent goes east, with a level landing
|
||||
where the player turns north into the existing bunker. The ticket counter is
|
||||
restored to its original (29,7.5) position.
|
||||
|
||||
Both sides of all stair entrances show the adjacent native scene through a
|
||||
depth-tested aperture. Remote wall rays begin just beyond that aperture, not
|
||||
inside a closed map cell behind the camera. Foreground geometry still occludes
|
||||
the view. This repairs the former visible-wall/walk-through-wall mismatch;
|
||||
room-local translations at thresholds are retained, with no timed player travel.
|
||||
|
||||
## Infernal Portal
|
||||
|
||||
Hell Tower is no longer represented by an ordinary table. A 2.5-unit-wide,
|
||||
almost ceiling-height arch is mounted in the south wall, with a projecting
|
||||
dark metal surround, chains and two animated crimson braziers. Its floor glow
|
||||
is applied to the existing carpet, not drawn as an opaque rectangle.
|
||||
|
||||
Each of the two floor levers has six generated positions over a 600ms pull.
|
||||
The portal stays sealed until both complete. Its vortex then loops through
|
||||
twelve generated phases at 80ms per frame. The rigid arch is registered and
|
||||
locked across those phases; the brazier fires have four separate phases.
|
||||
Walking into the open aperture, or interacting with it, starts the existing
|
||||
Hell Tower mode. Returning cannot immediately retrigger it until the player
|
||||
backs away from the entrance.
|
||||
|
||||
## Visitors
|
||||
|
||||
Six new appearances include three men and three women, with separate standing,
|
||||
walking and seated frames. Both visitor rotations have a mixed cast. Dialogue
|
||||
names follow the selected appearance. Existing routes and challenge identities
|
||||
are retained. These are front/three-quarter sprites, not complete eight-direction
|
||||
animation sets. Upper-lounge guests are currently seated, not another roaming
|
||||
population; this limitation is deliberate and not presented as finished NPC AI.
|
||||
Their chairs are grouped around lamp tables rather than placed singly at blank
|
||||
walls; the emerald guest's seated pose faces inward toward the shared table.
|
||||
|
||||
## Cabaret
|
||||
|
||||
A nearby seat interaction opens a seated view of the stage. Three thirty-second
|
||||
acts cycle: the existing twelve-frame dance chorus, a singer/pianist/guitar/sax
|
||||
quartet, and a duet. The pianist and grand piano are one coherent generated
|
||||
cutout, with his bench, hands on the actual keyboard, pedals and raised lid.
|
||||
The guitarist has four separate playing poses, as does the piano/pianist.
|
||||
Their common packing bounds retain fixed scale and foot anchors. The seated
|
||||
camera frames all four performers without clipping the piano or saxophonist.
|
||||
The programme shows the current act and time to the next one. Requesting
|
||||
the next act or leaving a tip costs 100; insufficient funds never create debt.
|
||||
Standing up and Escape return to the hall. New act-specific music, eight-direction
|
||||
performer sprites and audience applause recordings are not included in this pass.
|
||||
|
||||
## Assets
|
||||
|
||||
Generated using the built-in image generator, with no API key:
|
||||
|
||||
- `assets/sprites/hall/guest-cast-v1.png`
|
||||
- `assets/sprites/hall/cabaret-ensemble-v1.png`
|
||||
- `assets/sprites/hall/cabaret-musicians-v1.png`
|
||||
- `assets/sprites/hall/upper-materials-v1.png`
|
||||
- `assets/sprites/hall/upper-furniture-v2.png`
|
||||
- `assets/sprites/hall/upper-portal-v3.png`
|
||||
- `assets/sprites/hall/upper-levers-v3.png`
|
||||
- `assets/sprites/hall/upper-braziers-v3.png`
|
||||
|
||||
Exact prompts and packing outputs: `assets/sprites/hall/prompts-v7.json` and
|
||||
`assets/sprites/hall/prompts-v8.json`.
|
||||
Packers: `pack_guest_cast.py`, `pack_cabaret_ensemble.py`,
|
||||
`pack_cabaret_musicians.py`, `pack_upper_materials.py`, `pack_upper_v2.py`,
|
||||
`pack_upper_motion.py`
|
||||
under `tools/generators`. Original source images are preserved.
|
||||
|
||||
## Verification
|
||||
|
||||
`python tools/tests/check_hall_expansion.py` compiles current MASM procedures and
|
||||
tests opening without movement, closed-door collision, manual traversal and
|
||||
reversal of both stairwells, no idle timed movement, upper-floor game/bar targets,
|
||||
bar camera restoration, cabaret costs and exits. It records native frames
|
||||
under `build/hall-expansion`. `--parity` checks new runtime procedures against the
|
||||
shipping object after the main build. Helpers are reused by changing COFF symbol
|
||||
visibility only; no helper instruction bytes or player save files are modified.
|
||||
All four jazz poses are rendered and checked for visible changes in both the
|
||||
piano and guitar regions. The loader probe also covers both new musician packs.
|
||||
The repair tests cover all eight doorway views, foreground occlusion, original
|
||||
door/ticket positions, the two-lever lock, intermediate/final lever poses, all
|
||||
twelve visible vortex phases, tower entry/return and single/dual-thread pixel
|
||||
parity at the stair entrance. `--prove-seam-regression` disables only aperture
|
||||
near clipping and confirms that the old solid rear-wall block reappears.
|
||||
|
||||
Published on 2026-10-18 to `poker.exe` and `bin/poker.exe`, with the existing
|
||||
launcher integrity hash updated. Both copies and `build/graphics/poker.exe` have
|
||||
SHA-256 `C0B5A1DD9A678E68599724138AF009D29C935EEFDD6D70D28789AABF071395F8`.
|
||||
Native scene/input tests, asset loading from root/TEMP, card-rank parity and the
|
||||
eight-ball ownership/retained-turn regression passed. The full legacy action
|
||||
suite and an interactive end-to-end playthrough were not rerun for this pass.
|
||||
|
||||
The remaining poker, roulette, achievements, billiards controls and new-game work
|
||||
is tracked separately in `CASINO_NEXT_PASS.md`; it is not claimed complete here.
|
||||
BIN
poker.exe
|
|
@ -18,4 +18,4 @@ DebOut=
|
|||
[FILES]
|
||||
1=poker.asm
|
||||
[SHOWFILE]
|
||||
1=000000000000000014060000740300000000000003000000F201000087
|
||||
1=000000000000000014060000740300000000000003000000850300001C
|
||||
|
|
|
|||
188
src_graphics/cabaret.inc
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
UpdateCabaret3D PROC
|
||||
mov eax,deltaTime3D
|
||||
add eax,cabaretTime3D
|
||||
xor edx,edx
|
||||
mov ecx,90000
|
||||
div ecx
|
||||
mov cabaretTime3D,edx
|
||||
mov eax,edx
|
||||
xor edx,edx
|
||||
mov ecx,30000
|
||||
div ecx
|
||||
mov cabaretAct3D,eax
|
||||
ret
|
||||
UpdateCabaret3D ENDP
|
||||
|
||||
BeginCabaret3D PROC
|
||||
mov engineMode,MODE_CABARET
|
||||
mov mouseClick3D,0
|
||||
call ClearKeys3D
|
||||
ret
|
||||
BeginCabaret3D ENDP
|
||||
|
||||
HandleCabaret3D PROC
|
||||
call UpdateCabaret3D
|
||||
cmp mouseClick3D,0
|
||||
je cabaret_handle_keys
|
||||
cmp mouseY3D,210
|
||||
jb cabaret_handle_keys
|
||||
cmp mouseX3D,236
|
||||
jae cabaret_leave
|
||||
cmp mouseX3D,148
|
||||
jae cabaret_tip
|
||||
cmp bankroll,100
|
||||
jb cabaret_handled
|
||||
sub bankroll,100
|
||||
mov eax,cabaretAct3D
|
||||
inc eax
|
||||
cmp eax,3
|
||||
jb @F
|
||||
xor eax,eax
|
||||
@@:
|
||||
imul eax,30000
|
||||
mov cabaretTime3D,eax
|
||||
call SaveProgress3D
|
||||
jmp cabaret_handled
|
||||
cabaret_tip:
|
||||
cmp bankroll,100
|
||||
jb cabaret_handled
|
||||
sub bankroll,100
|
||||
invoke CopyString3D,OFFSET cabaretApplause3D,OFFSET flashMessage
|
||||
mov flashTimer,120
|
||||
call SaveProgress3D
|
||||
jmp cabaret_handled
|
||||
cabaret_handle_keys:
|
||||
invoke Pressed3D,45h
|
||||
test eax,eax
|
||||
jnz cabaret_leave
|
||||
jmp cabaret_handled
|
||||
cabaret_leave:
|
||||
mov engineMode,MODE_WALK
|
||||
call ClearKeys3D
|
||||
cabaret_handled:
|
||||
mov mouseClick3D,0
|
||||
ret
|
||||
HandleCabaret3D ENDP
|
||||
|
||||
RenderCabaretActors3D PROC uses ebx esi edi
|
||||
mov eax,animationClock3D
|
||||
xor edx,edx
|
||||
mov ecx,200
|
||||
div ecx
|
||||
xor edx,edx
|
||||
mov ecx,12
|
||||
div ecx
|
||||
mov edi,edx
|
||||
cmp cabaretAct3D,1
|
||||
je cabaret_jazz
|
||||
; Existing dancers retain their complete twelve-frame loop.
|
||||
xor esi,esi
|
||||
cabaret_dancer:
|
||||
mov eax,edi
|
||||
add eax,esi
|
||||
xor edx,edx
|
||||
mov ecx,12
|
||||
div ecx
|
||||
add edx,10
|
||||
shl edx,16
|
||||
add edx,OFFSET texturesBuffer
|
||||
mov hallLegacyPack3D.pixels,edx
|
||||
mov cabaretActor3D.asset,OFFSET hallLegacyPack3D
|
||||
mov cabaretActor3D.frame,0
|
||||
imul eax,esi,3*FP_1
|
||||
add eax,13*FP_1
|
||||
mov cabaretActor3D.posX,eax
|
||||
mov cabaretActor3D.posY,4*FP_1
|
||||
invoke DrawCityObject3D,OFFSET cabaretActor3D,0
|
||||
inc esi
|
||||
cmp cabaretAct3D,2
|
||||
je cabaret_singer
|
||||
cmp esi,3
|
||||
jb cabaret_dancer
|
||||
ret
|
||||
cabaret_jazz:
|
||||
mov eax,edi
|
||||
and eax,3
|
||||
mov cabaretPiano3D.frame,eax
|
||||
invoke DrawCityObject3D,OFFSET cabaretPiano3D,0
|
||||
mov eax,edi
|
||||
and eax,3
|
||||
mov cabaretGuitar3D.frame,eax
|
||||
invoke DrawCityObject3D,OFFSET cabaretGuitar3D,0
|
||||
mov cabaretActor3D.asset,OFFSET cabaretArt3D
|
||||
mov cabaretActor3D.posX,FP_1*187/10
|
||||
mov cabaretActor3D.posY,4*FP_1
|
||||
mov eax,edi
|
||||
and eax,3
|
||||
add eax,8
|
||||
mov cabaretActor3D.frame,eax
|
||||
invoke DrawCityObject3D,OFFSET cabaretActor3D,0
|
||||
cabaret_singer:
|
||||
mov cabaretActor3D.asset,OFFSET cabaretArt3D
|
||||
mov cabaretActor3D.posX,FP_1*162/10
|
||||
mov cabaretActor3D.posY,4*FP_1
|
||||
mov eax,edi
|
||||
and eax,3
|
||||
mov cabaretActor3D.frame,eax
|
||||
invoke DrawCityObject3D,OFFSET cabaretActor3D,0
|
||||
ret
|
||||
RenderCabaretActors3D ENDP
|
||||
|
||||
RenderCabaret3D PROC uses ebx
|
||||
local oldEye:DWORD,oldHorizon:DWORD,oldX:DWORD,oldY:DWORD,oldAngle:DWORD
|
||||
mov eax,playerX
|
||||
mov oldX,eax
|
||||
mov eax,playerY
|
||||
mov oldY,eax
|
||||
mov eax,playerAngle
|
||||
mov oldAngle,eax
|
||||
mov playerX,16*FP_1
|
||||
mov playerY,FP_1*92/10
|
||||
mov playerAngle,384
|
||||
mov eax,viewEye3D
|
||||
mov oldEye,eax
|
||||
mov eax,viewHorizon3D
|
||||
mov oldHorizon,eax
|
||||
mov viewEye3D,FP_1*3/4
|
||||
mov viewHorizon3D,HALF_H
|
||||
call UpdateCamera3D
|
||||
call RenderCeilingAndFloor
|
||||
call RenderWalls
|
||||
call PrepareCityDepth3D
|
||||
call RenderSprites
|
||||
call RenderHall3D
|
||||
invoke DrawBox,0,0,320,25,0014191Dh
|
||||
invoke DrawString8x8,12,8,OFFSET cabaretTitle3D,00E7CA88h
|
||||
invoke DrawBox,0,178,320,62,0014191Dh
|
||||
mov eax,cabaretAct3D
|
||||
invoke DrawString8x8,8,183,cabaretActNames3D[eax*4],00E7CA88h
|
||||
invoke DrawString8x8,8,197,OFFSET cabaretNext3D,0098A9AAh
|
||||
mov eax,cabaretTime3D
|
||||
xor edx,edx
|
||||
mov ecx,30000
|
||||
div ecx
|
||||
mov eax,30000
|
||||
sub eax,edx
|
||||
xor edx,edx
|
||||
mov ecx,1000
|
||||
div ecx
|
||||
invoke DrawNumber,200,197,eax,00E7CA88h
|
||||
invoke DrawBoxBorder,5,212,139,23,00252B29h,00836D42h,1
|
||||
invoke DrawBoxBorder,148,212,84,23,00252B29h,00836D42h,1
|
||||
invoke DrawBoxBorder,236,212,79,23,00252B29h,00836D42h,1
|
||||
invoke DrawString8x8,12,219,OFFSET cabaretRequest3D,00E7CA88h
|
||||
invoke DrawString8x8,152,219,OFFSET cabaretTip3D,00E7CA88h
|
||||
invoke DrawString8x8,244,219,OFFSET cabaretStand3D,00E7CA88h
|
||||
mov eax,oldEye
|
||||
mov viewEye3D,eax
|
||||
mov eax,oldHorizon
|
||||
mov viewHorizon3D,eax
|
||||
mov eax,oldX
|
||||
mov playerX,eax
|
||||
mov eax,oldY
|
||||
mov playerY,eax
|
||||
mov eax,oldAngle
|
||||
mov playerAngle,eax
|
||||
call UpdateCamera3D
|
||||
ret
|
||||
RenderCabaret3D ENDP
|
||||
32
src_graphics/cabaret_data.inc
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
MODE_CABARET equ 24
|
||||
INTERACT_CABARET_SEAT equ 42
|
||||
UpdateCabaret3D PROTO
|
||||
BeginCabaret3D PROTO
|
||||
HandleCabaret3D PROTO
|
||||
RenderCabaret3D PROTO
|
||||
RenderCabaretActors3D PROTO
|
||||
.data
|
||||
cabaretArt3D AnimPack3D <>
|
||||
cabaretPath3D db "assets\animations\hall\cabaret-ensemble-v1.bin",0
|
||||
cabaretPianoArt3D AnimPack3D <>
|
||||
cabaretPianoPath3D db "assets\animations\hall\cabaret-piano-v1.bin",0
|
||||
cabaretGuitarArt3D AnimPack3D <>
|
||||
cabaretGuitarPath3D db "assets\animations\hall\cabaret-guitar-v1.bin",0
|
||||
cabaretTime3D dd 0
|
||||
cabaretAct3D dd 0
|
||||
cabaretActSeen3D dd 0
|
||||
cabaretActor3D CityProp3D <16*FP_1,4*FP_1,FP_1*4/5,FP_1*13/10,0,OFFSET cabaretArt3D,FP_1/8>
|
||||
cabaretPiano3D CityProp3D <FP_1*147/10,FP_1*36/10,FP_1*26/10,FP_1*175/100,0,OFFSET cabaretPianoArt3D,FP_1/8>
|
||||
cabaretGuitar3D CityProp3D <FP_1*175/10,FP_1*4,FP_1*9/10,FP_1*7/5,0,OFFSET cabaretGuitarArt3D,FP_1/8>
|
||||
cabaretTitle3D db 202,192,193,192,208,197,32,34,207,206,203,205,206,215,220,34,0
|
||||
cabaretSeat3D db 69,58,32,209,197,209,210,220,32,200,32,209,204,206,210,208,197,210,220,32,216,206,211,0
|
||||
cabaretActNames3D dd OFFSET cabaretChorus3D,OFFSET cabaretJazz3D,OFFSET cabaretFinale3D
|
||||
cabaretChorus3D db 194,197,215,197,208,205,197,197,32,194,192,208,220,197,210,197,0
|
||||
cabaretJazz3D db 196,198,192,199,206,194,219,201,32,202,194,192,208,210,197,210,0
|
||||
cabaretFinale3D db 209,197,208,197,193,208,223,205,219,201,32,196,211,221,210,0
|
||||
cabaretRequest3D db 205,206,204,197,208,32,36,49,48,48,0
|
||||
cabaretTip3D db 215,192,201,32,36,49,48,48,0
|
||||
cabaretStand3D db 194,209,210,192,210,220,0
|
||||
cabaretNext3D db 196,206,32,209,204,197,205,219,32,205,206,204,197,208,192,0
|
||||
cabaretApplause3D db 192,240,242,232,241,242,251,32,225,235,224,227,238,228,224,240,255,242,32,231,224,32,239,238,228,228,229,240,230,234,243,0
|
||||
.code
|
||||
|
|
@ -215,10 +215,15 @@ card_fill_ready:
|
|||
card_draw_face:
|
||||
mov eax, x
|
||||
add eax, 3
|
||||
cmp cardWidth, 20
|
||||
jae @F
|
||||
sub eax, 2
|
||||
@@:
|
||||
mov edx, y
|
||||
add edx, 3
|
||||
mov ecx, rank
|
||||
lea ecx, cardRankNames3D[ecx*2]
|
||||
lea ecx, [ecx+ecx*2]
|
||||
add ecx, OFFSET cardRankNames3D
|
||||
invoke DrawString8x8, eax, edx, ecx, ink
|
||||
mov eax, x
|
||||
add eax, 3
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ cardTableHeader3D dd 4 dup(0)
|
|||
cardRanksHeader3D dd 4 dup(0)
|
||||
cardTablePath3D db "assets\textures\card-table.bin",0
|
||||
cardRanksPath3D db "assets\data\poker-ranks.bin",0
|
||||
cardRankNames3D db "2",0,"3",0,"4",0,"5",0,"6",0,"7",0,"8",0,"9",0,"T",0,"J",0,"Q",0,"K",0,"A",0
|
||||
cardRankNames3D db "2",0,0,"3",0,0,"4",0,0,"5",0,0,"6",0,0,"7",0,0,"8",0,0,"9",0,0,"10",0,"J",0,0,"Q",0,0,"K",0,0,"A",0,0
|
||||
cardSuitMasks3D db 18h,3Ch,18h,7Eh,0FFh,7Eh,18h,3Ch
|
||||
db 18h,3Ch,7Eh,0FFh,7Eh,3Ch,18h,0
|
||||
db 66h,0FFh,0FFh,7Eh,3Ch,18h,0,0
|
||||
|
|
|
|||
|
|
@ -5,6 +5,20 @@ cityRayY3D dd SCREEN_W dup(?)
|
|||
; Exterior-only materials do not consume or overwrite the hall's texture slots.
|
||||
ResolveCitySurface3D PROC material:DWORD
|
||||
mov eax,material
|
||||
cmp eax,122
|
||||
jb @F
|
||||
cmp eax,130
|
||||
ja @F
|
||||
invoke ResolveUpperMaterial3D,material
|
||||
ret
|
||||
@@:
|
||||
cmp eax,118
|
||||
jb @F
|
||||
cmp eax,122
|
||||
jae @F
|
||||
invoke ResolveTerminalSurface3D,material
|
||||
ret
|
||||
@@:
|
||||
cmp eax,115
|
||||
jb @F
|
||||
cmp eax,118
|
||||
|
|
@ -217,6 +231,12 @@ city_vertical_along:
|
|||
city_vertical_pixel:
|
||||
mov edi, destination
|
||||
mov eax, depth
|
||||
cmp portalRendering3D,0
|
||||
je @F
|
||||
mov ebx,column
|
||||
cmp eax,portalClipDepth3D[ebx*4]
|
||||
jb city_vertical_next_pixel
|
||||
@@:
|
||||
cmp eax, cityDepthBuffer3D[edi]
|
||||
ja city_vertical_next_pixel
|
||||
mov eax, sourceY
|
||||
|
|
@ -422,6 +442,12 @@ city_horizontal_pixel:
|
|||
cmp eax, yMax
|
||||
jge city_horizontal_advance
|
||||
mov eax, depth
|
||||
cmp portalRendering3D,0
|
||||
je @F
|
||||
mov ebx,column
|
||||
cmp eax,portalClipDepth3D[ebx*4]
|
||||
jb city_horizontal_advance
|
||||
@@:
|
||||
cmp eax, cityDepthBuffer3D[edi]
|
||||
ja city_horizontal_advance
|
||||
mov eax, pointX
|
||||
|
|
|
|||
|
|
@ -272,6 +272,11 @@ city_object_project:
|
|||
mov ebx, xStart
|
||||
city_object_column:
|
||||
mov eax, depth
|
||||
cmp portalRendering3D,0
|
||||
je @F
|
||||
cmp eax,portalClipDepth3D[ebx*4]
|
||||
jb city_object_next_column
|
||||
@@:
|
||||
cmp eax, zBuffer[ebx*4]
|
||||
jge city_object_next_column
|
||||
mov eax, ebx
|
||||
|
|
|
|||
|
|
@ -166,4 +166,7 @@ stairsReturnX3D dd 0
|
|||
stairsReturnY3D dd 0
|
||||
stairsReturnAngle3D dd 0
|
||||
include src_graphics\world_dressing_data.inc
|
||||
include src_graphics\terminal_data.inc
|
||||
include src_graphics\hall_expansion_data.inc
|
||||
include src_graphics\cabaret_data.inc
|
||||
.code
|
||||
|
|
|
|||
885
src_graphics/hall_expansion.inc
Normal file
|
|
@ -0,0 +1,885 @@
|
|||
; Connected stairwells use the normal player movement and body collision routines.
|
||||
; Only room-local coordinates are translated at a landing, never by a timer.
|
||||
ResolveUpperMaterial3D PROC material:DWORD
|
||||
cmp material,129
|
||||
jne @F
|
||||
mov eax,upperPortal3D.frame
|
||||
imul eax,256*512*4
|
||||
cmp upperPortalArt3D.pixels,0
|
||||
je upper_material_missing
|
||||
add eax,upperPortalArt3D.pixels
|
||||
mov ecx,256
|
||||
mov edx,512
|
||||
ret
|
||||
@@:
|
||||
cmp material,130
|
||||
jne @F
|
||||
mov eax,OFFSET upperGlowPixels3D
|
||||
mov ecx,32
|
||||
mov edx,32
|
||||
ret
|
||||
@@:
|
||||
cmp material,128
|
||||
jne @F
|
||||
mov eax,hallProps3D.pixels
|
||||
test eax,eax
|
||||
jz upper_material_missing
|
||||
add eax,5*256*256*4
|
||||
mov ecx,256
|
||||
mov edx,256
|
||||
ret
|
||||
@@:
|
||||
cmp material,126
|
||||
jne @F
|
||||
mov eax,OFFSET upperFeltPixels3D
|
||||
mov ecx,32
|
||||
mov edx,32
|
||||
ret
|
||||
@@:
|
||||
cmp material,127
|
||||
jne @F
|
||||
mov eax,OFFSET upperKeyPixels3D
|
||||
mov ecx,32
|
||||
mov edx,32
|
||||
ret
|
||||
@@:
|
||||
mov eax,upperMaterialArt3D.pixels
|
||||
test eax,eax
|
||||
jz upper_material_missing
|
||||
mov ecx,material
|
||||
sub ecx,122
|
||||
cmp ecx,3
|
||||
ja upper_material_missing
|
||||
shl ecx,18
|
||||
add eax,ecx
|
||||
mov ecx,256
|
||||
mov edx,256
|
||||
ret
|
||||
upper_material_missing:
|
||||
xor eax,eax
|
||||
ret
|
||||
ResolveUpperMaterial3D ENDP
|
||||
|
||||
InitUpperSurfaces3D PROC uses esi
|
||||
xor esi,esi
|
||||
upper_surface_pixel:
|
||||
; Small native material swatches, not a replacement external renderer.
|
||||
mov eax,esi
|
||||
imul eax,1103515245
|
||||
xor eax,5A63h
|
||||
and eax,7
|
||||
add eax,0FF1B5039h
|
||||
mov upperFeltPixels3D[esi*4],eax
|
||||
and eax,7
|
||||
add eax,0FF311114h
|
||||
mov upperGlowPixels3D[esi*4],eax
|
||||
mov eax,esi
|
||||
and eax,31
|
||||
and eax,3
|
||||
cmp eax,3
|
||||
je upper_key_dark
|
||||
cmp esi,16*32
|
||||
jae upper_key_white
|
||||
test eax,eax
|
||||
jz upper_key_dark
|
||||
upper_key_white:
|
||||
mov eax,0FFECE3C8h
|
||||
jmp upper_key_store
|
||||
upper_key_dark:
|
||||
mov eax,0FF16191Bh
|
||||
upper_key_store:
|
||||
mov upperKeyPixels3D[esi*4],eax
|
||||
inc esi
|
||||
cmp esi,1024
|
||||
jb upper_surface_pixel
|
||||
ret
|
||||
InitUpperSurfaces3D ENDP
|
||||
|
||||
DrawUpperGameTable3D PROC uses ebx esi edi centreX:DWORD,centreY:DWORD
|
||||
local x0:DWORD,x1:DWORD,y0:DWORD,y1:DWORD,legX:DWORD,legY:DWORD
|
||||
mov eax,centreX
|
||||
sub eax,FP_1
|
||||
mov x0,eax
|
||||
add eax,2*FP_1
|
||||
mov x1,eax
|
||||
mov eax,centreY
|
||||
sub eax,FP_1/2
|
||||
mov y0,eax
|
||||
add eax,FP_1
|
||||
mov y1,eax
|
||||
invoke DrawCityBoxSides3D,x0,x1,y0,y1,FP_1/2,FP_1*13/20,85
|
||||
invoke DrawCityHorizontal3D,x0,x1,y0,y1,FP_1*13/20,85
|
||||
mov eax,x0
|
||||
add eax,FP_1/10
|
||||
mov ebx,x1
|
||||
sub ebx,FP_1/10
|
||||
mov ecx,y0
|
||||
add ecx,FP_1/10
|
||||
mov edx,y1
|
||||
sub edx,FP_1/10
|
||||
invoke DrawCityHorizontal3D,eax,ebx,ecx,edx,FP_1*33/50,126
|
||||
xor esi,esi
|
||||
upper_table_leg:
|
||||
mov eax,x0
|
||||
add eax,FP_1/8
|
||||
test esi,1
|
||||
jz @F
|
||||
mov eax,x1
|
||||
sub eax,FP_1/4
|
||||
@@:
|
||||
mov legX,eax
|
||||
mov eax,y0
|
||||
add eax,FP_1/8
|
||||
test esi,2
|
||||
jz @F
|
||||
mov eax,y1
|
||||
sub eax,FP_1/4
|
||||
@@:
|
||||
mov legY,eax
|
||||
mov ebx,legX
|
||||
add ebx,FP_1/8
|
||||
add eax,FP_1/8
|
||||
invoke DrawCityBoxSides3D,legX,ebx,legY,eax,0,FP_1/2,85
|
||||
inc esi
|
||||
cmp esi,4
|
||||
jb upper_table_leg
|
||||
ret
|
||||
DrawUpperGameTable3D ENDP
|
||||
|
||||
BeginWalkableStairs3D PROC route:DWORD,fromFarEnd:DWORD
|
||||
mov eax,route
|
||||
mov stairRoute3D,eax
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov interactTargetId,0
|
||||
mov interactPromptPtr,0
|
||||
cmp route,1
|
||||
jne interior_begin_upper
|
||||
mov worldMap3D,OFFSET basementStairMap3D
|
||||
mov floorTexture3D,94
|
||||
sub playerX,25*FP_1
|
||||
add playerY,FP_1*21/2
|
||||
cmp fromFarEnd,0
|
||||
je interior_begin_finish
|
||||
add playerX,12*FP_1
|
||||
sub playerY,32*FP_1
|
||||
jmp interior_begin_finish
|
||||
interior_begin_upper:
|
||||
mov worldMap3D,OFFSET upperStairMap3D
|
||||
mov floorTexture3D,85
|
||||
sub playerX,27*FP_1
|
||||
cmp fromFarEnd,0
|
||||
je interior_begin_finish
|
||||
add playerX,39*FP_1
|
||||
interior_begin_finish:
|
||||
call UpdateStairHeight3D
|
||||
ret
|
||||
BeginWalkableStairs3D ENDP
|
||||
|
||||
UpdateInteriorTravel3D PROC
|
||||
call UpdateUpperEffects3D
|
||||
mov eax,animationClock3D
|
||||
cmp eax,interiorDoorClock3D
|
||||
je interior_travel_check
|
||||
mov interiorDoorClock3D,eax
|
||||
mov eax,deltaTime3D
|
||||
cmp eax,100
|
||||
jbe @F
|
||||
mov eax,100
|
||||
@@:
|
||||
imul eax,80
|
||||
add eax,basementDoorOpen3D
|
||||
cmp eax,basementDoorTarget3D
|
||||
jbe @F
|
||||
mov eax,basementDoorTarget3D
|
||||
@@:
|
||||
mov basementDoorOpen3D,eax
|
||||
interior_travel_check:
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je interior_travel_stairs
|
||||
cmp engineMode,MODE_WALK
|
||||
jne interior_travel_done
|
||||
cmp hallLevel3D,1
|
||||
je interior_travel_bunker
|
||||
cmp hallLevel3D,2
|
||||
je interior_travel_upper
|
||||
cmp playerY,20*FP_1
|
||||
jb interior_travel_door
|
||||
cmp playerY,22*FP_1
|
||||
ja interior_travel_done
|
||||
cmp playerX,FP_1*303/10
|
||||
jb interior_travel_done
|
||||
invoke BeginWalkableStairs3D,2,0
|
||||
ret
|
||||
interior_travel_door:
|
||||
cmp basementDoorOpen3D,FP_1*9/10
|
||||
jb interior_travel_done
|
||||
cmp playerX,FP_1*286/10
|
||||
jb interior_travel_done
|
||||
cmp playerY,FP_1*99/10
|
||||
jb interior_travel_done
|
||||
cmp playerY,FP_1*111/10
|
||||
ja interior_travel_done
|
||||
invoke BeginWalkableStairs3D,1,0
|
||||
ret
|
||||
interior_travel_bunker:
|
||||
cmp playerY,FP_1*277/10
|
||||
jb interior_travel_done
|
||||
; Bunker landing (16.5,27.7) continues to the stairwell (16.5,14.7).
|
||||
sub playerY,13*FP_1
|
||||
mov stairRoute3D,1
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov worldMap3D,OFFSET basementStairMap3D
|
||||
mov floorTexture3D,94
|
||||
call UpdateStairHeight3D
|
||||
ret
|
||||
interior_travel_upper:
|
||||
call TryWalkUpperPortal3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne interior_travel_done
|
||||
cmp playerY,20*FP_1
|
||||
jb interior_travel_done
|
||||
cmp playerY,22*FP_1
|
||||
ja interior_travel_done
|
||||
cmp playerX,FP_1*32/10
|
||||
ja interior_travel_done
|
||||
; Upper west landing and the far east end of its stairwell share an axis.
|
||||
add playerX,12*FP_1
|
||||
mov stairRoute3D,2
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov worldMap3D,OFFSET upperStairMap3D
|
||||
mov floorTexture3D,85
|
||||
call UpdateStairHeight3D
|
||||
ret
|
||||
interior_travel_stairs:
|
||||
cmp stairRoute3D,1
|
||||
jne interior_upper_thresholds
|
||||
cmp playerX,FP_1*31/10
|
||||
jbe interior_exit_basement_top
|
||||
cmp playerY,FP_1*143/10
|
||||
ja interior_travel_done
|
||||
mov hallLevel3D,1
|
||||
mov worldMap3D,OFFSET bunkerMap3D
|
||||
add playerY,13*FP_1
|
||||
mov floorTexture3D,94
|
||||
jmp interior_finish
|
||||
interior_exit_basement_top:
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
add playerX,25*FP_1
|
||||
sub playerY,FP_1*21/2
|
||||
mov floorTexture3D,100
|
||||
jmp interior_finish
|
||||
interior_upper_thresholds:
|
||||
cmp playerX,FP_1*31/10
|
||||
jbe interior_exit_upper_bottom
|
||||
cmp playerX,16*FP_1
|
||||
jb interior_travel_done
|
||||
mov hallLevel3D,2
|
||||
mov worldMap3D,OFFSET upperLoungeMap3D
|
||||
sub playerX,12*FP_1
|
||||
mov floorTexture3D,124
|
||||
jmp interior_finish
|
||||
interior_exit_upper_bottom:
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
add playerX,27*FP_1
|
||||
mov floorTexture3D,100
|
||||
interior_finish:
|
||||
mov engineMode,MODE_WALK
|
||||
mov interactTargetId,0
|
||||
mov interactPromptPtr,0
|
||||
call UpdateInteriorEye3D
|
||||
interior_travel_done:
|
||||
ret
|
||||
UpdateInteriorTravel3D ENDP
|
||||
|
||||
UpdateStairHeight3D PROC
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne interior_height_done
|
||||
mov viewHorizon3D,HALF_H
|
||||
mov viewCeiling3D,FP_1*11/2
|
||||
cmp stairRoute3D,1
|
||||
jne interior_height_upper
|
||||
mov viewCeiling3D,4*FP_1
|
||||
mov eax,13*FP_1
|
||||
sub eax,playerX
|
||||
jle interior_height_zero
|
||||
cmp eax,8*FP_1
|
||||
jle @F
|
||||
mov eax,8*FP_1
|
||||
@@:
|
||||
imul eax,3
|
||||
cdq
|
||||
mov ecx,16
|
||||
idiv ecx
|
||||
jmp interior_height_store
|
||||
interior_height_upper:
|
||||
mov eax,playerX
|
||||
sub eax,5*FP_1
|
||||
jle interior_height_zero
|
||||
cmp eax,8*FP_1
|
||||
jle @F
|
||||
mov eax,8*FP_1
|
||||
@@:
|
||||
imul eax,13
|
||||
cdq
|
||||
mov ecx,40
|
||||
idiv ecx
|
||||
jmp interior_height_store
|
||||
interior_height_zero:
|
||||
xor eax,eax
|
||||
interior_height_store:
|
||||
add eax,HALL_STANDING_EYE
|
||||
mov viewEye3D,eax
|
||||
interior_height_done:
|
||||
ret
|
||||
UpdateStairHeight3D ENDP
|
||||
|
||||
UpdateInteriorEye3D PROC
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je UpdateStairHeight3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne interior_eye_done
|
||||
mov viewEye3D,HALL_STANDING_EYE
|
||||
mov viewCeiling3D,FP_1*5/2
|
||||
interior_eye_done:
|
||||
ret
|
||||
UpdateInteriorEye3D ENDP
|
||||
|
||||
InteriorDoorCanWalk3D PROC px:DWORD,py:DWORD
|
||||
cmp px,29*FP_1-FP_1/6
|
||||
jb interior_check_steel
|
||||
cmp py,FP_1*1955/100
|
||||
jb interior_check_steel
|
||||
cmp py,20*FP_1+FP_1/6
|
||||
jbe interior_portal_blocked
|
||||
cmp py,22*FP_1-FP_1/6
|
||||
jb interior_check_steel
|
||||
cmp py,FP_1*2245/100
|
||||
jbe interior_portal_blocked
|
||||
interior_check_steel:
|
||||
cmp py,9*FP_1
|
||||
jb interior_door_clear
|
||||
cmp py,12*FP_1
|
||||
jae interior_door_clear
|
||||
cmp px,FP_1*2835/100
|
||||
jb interior_door_clear
|
||||
cmp py,FP_1*96/10
|
||||
jb interior_portal_blocked
|
||||
cmp py,FP_1*114/10
|
||||
ja interior_portal_blocked
|
||||
cmp basementDoorOpen3D,FP_1*9/10
|
||||
jae interior_door_clear
|
||||
interior_portal_blocked:
|
||||
xor eax,eax
|
||||
ret
|
||||
interior_door_clear:
|
||||
mov eax,1
|
||||
ret
|
||||
InteriorDoorCanWalk3D ENDP
|
||||
|
||||
RenderHallPortals3D PROC uses ebx
|
||||
; Retain the original iron door artwork in the original east-facing alcove.
|
||||
invoke DrawCityBoxSides3D,FP_1*284/10,FP_1*287/10,9*FP_1,FP_1*975/100,0,FP_1*11/5,102
|
||||
invoke DrawCityBoxSides3D,FP_1*284/10,FP_1*287/10,FP_1*1125/100,12*FP_1,0,FP_1*11/5,102
|
||||
mov eax,basementDoorOpen3D
|
||||
imul eax,2
|
||||
add eax,FP_1*975/100
|
||||
lea ebx,[eax+FP_1*3/2]
|
||||
invoke DrawCityVertical3D,0,FP_1*285/10,eax,ebx,0,FP_1*19/10,128
|
||||
invoke DrawCityBoxSides3D,FP_1*284/10,FP_1*287/10,FP_1*975/100,FP_1*1125/100,FP_1*19/10,FP_1*11/5,102
|
||||
; East stair entrance: projecting jambs, a low first tread and overhead lintel.
|
||||
invoke DrawCityBoxSides3D,29*FP_1,31*FP_1,FP_1*197/10,20*FP_1,0,FP_1*5/2,102
|
||||
invoke DrawCityBoxSides3D,29*FP_1,31*FP_1,22*FP_1,FP_1*223/10,0,FP_1*5/2,102
|
||||
invoke DrawCityHorizontal3D,30*FP_1,31*FP_1,20*FP_1,22*FP_1,FP_1/8,85
|
||||
invoke DrawCityVertical3D,0,30*FP_1,20*FP_1,22*FP_1,0,FP_1/8,102
|
||||
ret
|
||||
RenderHallPortals3D ENDP
|
||||
|
||||
RenderWalkableStairs3D PROC uses ebx esi edi
|
||||
local treadStart:DWORD,treadEnd:DWORD,z0:DWORD,z1:DWORD
|
||||
cmp stairRoute3D,1
|
||||
jne interior_render_upper_steps
|
||||
; East-facing descent, then a level northward turn into the old bunker.
|
||||
xor esi,esi
|
||||
interior_concrete_step:
|
||||
mov eax,esi
|
||||
imul eax,FP_1/3
|
||||
add eax,5*FP_1
|
||||
mov treadStart,eax
|
||||
add eax,FP_1/3
|
||||
mov treadEnd,eax
|
||||
imul eax,esi,FP_1/16
|
||||
neg eax
|
||||
add eax,FP_1*3/2
|
||||
mov z0,eax
|
||||
sub eax,FP_1/16
|
||||
mov z1,eax
|
||||
invoke DrawCityHorizontal3D,treadStart,treadEnd,20*FP_1,23*FP_1,z1,94
|
||||
invoke DrawCityVertical3D,0,treadEnd,20*FP_1,23*FP_1,z1,z0,92
|
||||
inc esi
|
||||
cmp esi,24
|
||||
jb interior_concrete_step
|
||||
invoke DrawCityHorizontal3D,2*FP_1,5*FP_1,20*FP_1,23*FP_1,FP_1*3/2,94
|
||||
invoke DrawCityHorizontal3D,13*FP_1,18*FP_1,20*FP_1,23*FP_1,0,94
|
||||
ret
|
||||
interior_render_upper_steps:
|
||||
xor esi,esi
|
||||
interior_wood_step:
|
||||
imul eax,esi,FP_1*2/5
|
||||
add eax,5*FP_1
|
||||
mov treadStart,eax
|
||||
add eax,FP_1*2/5
|
||||
mov treadEnd,eax
|
||||
imul eax,esi,FP_1*13/100
|
||||
mov z0,eax
|
||||
add eax,FP_1*13/100
|
||||
mov z1,eax
|
||||
invoke DrawCityHorizontal3D,treadStart,treadEnd,20*FP_1,22*FP_1,z1,85
|
||||
invoke DrawCityVertical3D,0,treadStart,20*FP_1,22*FP_1,z0,z1,102
|
||||
mov eax,z1
|
||||
add eax,FP_1*3/4
|
||||
mov ebx,eax
|
||||
add eax,FP_1/20
|
||||
mov edi,eax
|
||||
invoke DrawCityVertical3D,1,FP_1*201/10,treadStart,treadEnd,ebx,edi,110
|
||||
invoke DrawCityVertical3D,1,FP_1*219/10,treadStart,treadEnd,ebx,edi,110
|
||||
; Posts follow the slope instead of a billboard painted on the wall.
|
||||
test esi,3
|
||||
jnz @F
|
||||
mov eax,z1
|
||||
add eax,FP_1*3/4
|
||||
mov ebx,eax
|
||||
invoke DrawCityVertical3D,0,treadStart,FP_1*201/10,FP_1*202/10,z1,ebx,110
|
||||
invoke DrawCityVertical3D,0,treadStart,FP_1*218/10,FP_1*219/10,z1,ebx,110
|
||||
@@:
|
||||
inc esi
|
||||
cmp esi,20
|
||||
jb interior_wood_step
|
||||
invoke DrawCityHorizontal3D,13*FP_1,18*FP_1,20*FP_1,22*FP_1,FP_1*13/5,85
|
||||
invoke DrawCityVertical3D,1,20*FP_1,2*FP_1,18*FP_1,0,FP_1*11/2,102
|
||||
invoke DrawCityVertical3D,1,22*FP_1,2*FP_1,18*FP_1,0,FP_1*11/2,102
|
||||
ret
|
||||
RenderWalkableStairs3D ENDP
|
||||
|
||||
UpperCanWalk3D PROC px:DWORD,py:DWORD
|
||||
; The same station centres drive art, interaction and collision.
|
||||
cmp px,10*FP_1-FP_1/5
|
||||
jb upper_walk_tables
|
||||
cmp px,15*FP_1+FP_1/5
|
||||
ja upper_walk_tables
|
||||
cmp py,13*FP_1-FP_1/5
|
||||
jb upper_walk_tables
|
||||
cmp py,15*FP_1+FP_1/5
|
||||
jbe upper_walk_block
|
||||
upper_walk_tables:
|
||||
xor ecx,ecx
|
||||
upper_walk_station:
|
||||
mov eax,px
|
||||
sub eax,upperStationX3D[ecx*4]
|
||||
test eax,eax
|
||||
jns @F
|
||||
neg eax
|
||||
@@:
|
||||
cmp eax,FP_1*11/10
|
||||
ja upper_walk_next
|
||||
mov eax,py
|
||||
sub eax,upperStationY3D[ecx*4]
|
||||
test eax,eax
|
||||
jns @F
|
||||
neg eax
|
||||
@@:
|
||||
cmp eax,FP_1*7/10
|
||||
jbe upper_walk_block
|
||||
upper_walk_next:
|
||||
inc ecx
|
||||
cmp ecx,2
|
||||
jb upper_walk_station
|
||||
xor ecx,ecx
|
||||
upper_walk_guest:
|
||||
mov eax,px
|
||||
sub eax,upperGuestX3D[ecx*4]
|
||||
test eax,eax
|
||||
jns @F
|
||||
neg eax
|
||||
@@:
|
||||
cmp eax,FP_1*2/5
|
||||
ja upper_walk_guest_next
|
||||
mov eax,py
|
||||
sub eax,upperGuestY3D[ecx*4]
|
||||
test eax,eax
|
||||
jns @F
|
||||
neg eax
|
||||
@@:
|
||||
cmp eax,FP_1*2/5
|
||||
jbe upper_walk_block
|
||||
upper_walk_guest_next:
|
||||
inc ecx
|
||||
cmp ecx,3
|
||||
jb upper_walk_guest
|
||||
cmp upperPortalReady3D,1
|
||||
je upper_walk_clear
|
||||
cmp py,24*FP_1
|
||||
jb upper_walk_clear
|
||||
cmp px,FP_1*95/10
|
||||
jb upper_walk_clear
|
||||
cmp px,FP_1*115/10
|
||||
jbe upper_walk_block
|
||||
upper_walk_clear:
|
||||
mov eax,1
|
||||
ret
|
||||
upper_walk_block:
|
||||
xor eax,eax
|
||||
ret
|
||||
UpperCanWalk3D ENDP
|
||||
|
||||
CheckUpperInteractions3D PROC
|
||||
mov interactTargetId,0
|
||||
mov interactPromptPtr,0
|
||||
invoke HallTarget3D,3*FP_1,21*FP_1,0,OFFSET upperExitPrompt3D
|
||||
invoke HallTarget3D,upperStationX3D[0],upperStationY3D[0],INTERACT_BUCKSHOT,OFFSET promptBuckshot
|
||||
invoke HallTarget3D,upperStationX3D[4],upperStationY3D[4],INTERACT_RUSSIAN_ROULETTE,OFFSET promptRussianRoulette
|
||||
cmp upperPortalReady3D,1
|
||||
je upper_check_portal
|
||||
test upperLeverMask3D,1
|
||||
jnz @F
|
||||
invoke HallTarget3D,FP_1*91/10,FP_1*226/10,INTERACT_UPPER_LEFT,OFFSET upperLeftPrompt3D
|
||||
@@:
|
||||
test upperLeverMask3D,2
|
||||
jnz upper_check_bar
|
||||
invoke HallTarget3D,FP_1*119/10,FP_1*226/10,INTERACT_UPPER_RIGHT,OFFSET upperRightPrompt3D
|
||||
jmp upper_check_bar
|
||||
upper_check_portal:
|
||||
invoke HallTarget3D,FP_1*105/10,FP_1*237/10,INTERACT_HELL_TOWER,OFFSET upperPortalPrompt3D
|
||||
upper_check_bar:
|
||||
cmp playerX,10*FP_1
|
||||
jb @F
|
||||
cmp playerX,15*FP_1
|
||||
ja @F
|
||||
cmp playerY,15*FP_1
|
||||
jb @F
|
||||
invoke HallTarget3D,playerX,15*FP_1,INTERACT_BAR,OFFSET hallBarPrompt3D
|
||||
@@:
|
||||
ret
|
||||
CheckUpperInteractions3D ENDP
|
||||
|
||||
RenderUpperLounge3D PROC uses ebx esi
|
||||
; Small midnight lounge: dark panels, brass ribs, a marble-topped straight bar.
|
||||
invoke DrawCityBoxSides3D,10*FP_1,15*FP_1,14*FP_1,15*FP_1,0,FP_1*13/20,102
|
||||
invoke DrawCityHorizontal3D,10*FP_1,15*FP_1,14*FP_1,15*FP_1,FP_1*13/20,123
|
||||
invoke DrawCityVertical3D,1,13*FP_1+FP_1/20,10*FP_1,15*FP_1,FP_1/2,2*FP_1,87
|
||||
invoke DrawCityBoxSides3D,10*FP_1,15*FP_1,13*FP_1,FP_1*132/10,0,FP_1/2,102
|
||||
invoke DrawCityBoxSides3D,10*FP_1,15*FP_1,13*FP_1,FP_1*132/10,2*FP_1,FP_1*21/10,102
|
||||
invoke DrawCityVertical3D,1,FP_1*152/10,10*FP_1,15*FP_1,FP_1/8,FP_1*16/100,110
|
||||
mov upperBarTender3D.asset,OFFSET upperBarArt3D
|
||||
mov upperBarTender3D.worldW,FP_1*4/5
|
||||
mov upperBarTender3D.worldH,FP_1*7/5
|
||||
mov upperBarTender3D.groundZ,0
|
||||
mov upperBarTender3D.frame,0
|
||||
mov upperBarTender3D.posX,FP_1*25/2
|
||||
mov upperBarTender3D.posY,FP_1*27/2
|
||||
cmp engineMode,MODE_BAR
|
||||
jne upper_draw_bartender
|
||||
mov upperBarTender3D.posX,FP_1*115/10
|
||||
mov upperBarTender3D.asset,OFFSET hallBartender3D
|
||||
mov upperBarTender3D.worldW,FP_1
|
||||
mov upperBarTender3D.worldH,FP_1
|
||||
mov upperBarTender3D.groundZ,FP_1*9/20
|
||||
mov eax,barSelection3D
|
||||
shl eax,3
|
||||
mov upperBarTender3D.frame,eax
|
||||
mov eax,barTime3D
|
||||
xor edx,edx
|
||||
mov ecx,375
|
||||
div ecx
|
||||
cmp eax,7
|
||||
jbe @F
|
||||
mov eax,7
|
||||
@@:
|
||||
add upperBarTender3D.frame,eax
|
||||
upper_draw_bartender:
|
||||
invoke DrawCityObject3D,OFFSET upperBarTender3D,0
|
||||
xor esi,esi
|
||||
upper_draw_table:
|
||||
mov ebx,upperStationX3D[esi*4]
|
||||
mov upperTable3D.posX,ebx
|
||||
mov eax,upperStationY3D[esi*4]
|
||||
mov upperTable3D.posY,eax
|
||||
mov upperTable3D.worldW,2*FP_1
|
||||
mov upperTable3D.worldH,FP_1*6/5
|
||||
mov upperTable3D.asset,OFFSET upperGamesArt3D
|
||||
mov upperTable3D.frame,esi
|
||||
invoke DrawCityObject3D,OFFSET upperTable3D,0
|
||||
mov upperDealer3D.posX,ebx
|
||||
mov eax,upperStationY3D[esi*4]
|
||||
sub eax,FP_1*3/4
|
||||
mov upperDealer3D.posY,eax
|
||||
mov upperDealer3D.frame,esi
|
||||
invoke DrawCityObject3D,OFFSET upperDealer3D,0
|
||||
inc esi
|
||||
cmp esi,2
|
||||
jb upper_draw_table
|
||||
mov upperPortal3D.frame,0
|
||||
cmp upperPortalReady3D,1
|
||||
jne upper_draw_portal
|
||||
mov eax,animationClock3D
|
||||
xor edx,edx
|
||||
mov ecx,80
|
||||
div ecx
|
||||
xor edx,edx
|
||||
mov ecx,12
|
||||
div ecx
|
||||
inc edx
|
||||
mov upperPortal3D.frame,edx
|
||||
upper_draw_portal:
|
||||
invoke DrawCityVertical3D,1,FP_1*249/10,9*FP_1,12*FP_1,0,FP_1*5/2,122
|
||||
invoke DrawCityBoxSides3D,FP_1*89/10,FP_1*915/100,FP_1*243/10,25*FP_1,0,FP_1*5/2,122
|
||||
invoke DrawCityBoxSides3D,FP_1*1185/100,FP_1*121/10,FP_1*243/10,25*FP_1,0,FP_1*5/2,122
|
||||
invoke DrawCityBoxSides3D,9*FP_1,12*FP_1,FP_1*243/10,25*FP_1,FP_1*24/10,FP_1*5/2,122
|
||||
invoke DrawCityVertical3D,1,FP_1*247/10,FP_1*925/100,FP_1*1175/100,0,FP_1*245/100,129
|
||||
mov eax,animationClock3D
|
||||
shr eax,7
|
||||
and eax,3
|
||||
mov upperBrazier3D.frame,eax
|
||||
mov upperBrazier3D.posX,FP_1*84/10
|
||||
invoke DrawCityObject3D,OFFSET upperBrazier3D,0
|
||||
mov upperBrazier3D.posX,FP_1*126/10
|
||||
invoke DrawCityObject3D,OFFSET upperBrazier3D,0
|
||||
invoke DrawCityVertical3D,1,FP_1*242/10,FP_1*92/10,FP_1*924/100,FP_1*16/10,FP_1*25/10,110
|
||||
invoke DrawCityVertical3D,1,FP_1*242/10,FP_1*1176/100,FP_1*118/10,FP_1*16/10,FP_1*25/10,110
|
||||
mov upperLever3D.posX,FP_1*91/10
|
||||
mov eax,upperLeverTime3D
|
||||
xor edx,edx
|
||||
mov ecx,120
|
||||
div ecx
|
||||
mov upperLever3D.frame,eax
|
||||
invoke DrawCityObject3D,OFFSET upperLever3D,0
|
||||
mov upperLever3D.posX,FP_1*119/10
|
||||
mov eax,upperLeverTime3D[4]
|
||||
xor edx,edx
|
||||
mov ecx,120
|
||||
div ecx
|
||||
mov upperLever3D.frame,eax
|
||||
invoke DrawCityObject3D,OFFSET upperLever3D,0
|
||||
mov upperGuest3D.asset,OFFSET npcGuestArt3D
|
||||
xor esi,esi
|
||||
upper_draw_guest:
|
||||
mov eax,upperGuestX3D[esi*4]
|
||||
mov upperGuest3D.posX,eax
|
||||
mov eax,upperGuestY3D[esi*4]
|
||||
mov upperGuest3D.posY,eax
|
||||
mov eax,upperGuestFrame3D[esi*4]
|
||||
mov upperGuest3D.frame,eax
|
||||
invoke DrawCityObject3D,OFFSET upperGuest3D,0
|
||||
inc esi
|
||||
cmp esi,3
|
||||
jb upper_draw_guest
|
||||
mov upperChair3D.posX,FP_1*56/10
|
||||
mov upperChair3D.posY,FP_1*225/10
|
||||
mov upperChair3D.worldW,FP_1*3/5
|
||||
mov upperChair3D.worldH,FP_1*7/10
|
||||
mov upperChair3D.asset,OFFSET hallLampFurniture3D
|
||||
invoke DrawCityObject3D,OFFSET upperChair3D,0
|
||||
mov upperChair3D.posX,FP_1*143/10
|
||||
mov upperChair3D.posY,FP_1*221/10
|
||||
invoke DrawCityObject3D,OFFSET upperChair3D,0
|
||||
mov upperChair3D.asset,OFFSET hallChairFurniture3D
|
||||
mov upperChair3D.worldW,FP_1*3/4
|
||||
mov upperChair3D.worldH,FP_1
|
||||
mov upperChair3D.posX,15*FP_1
|
||||
mov upperChair3D.posY,FP_1*227/10
|
||||
invoke DrawCityObject3D,OFFSET upperChair3D,0
|
||||
invoke DrawCityObject3D,OFFSET upperSofa3D,0
|
||||
cmp engineMode,MODE_BAR
|
||||
je upper_bar_stools_done
|
||||
xor esi,esi
|
||||
upper_bar_stools:
|
||||
imul eax,esi,FP_1*14/10
|
||||
add eax,FP_1*109/10
|
||||
mov upperStool3D.posX,eax
|
||||
invoke DrawCityObject3D,OFFSET upperStool3D,0
|
||||
inc esi
|
||||
cmp esi,3
|
||||
jb upper_bar_stools
|
||||
upper_bar_stools_done:
|
||||
invoke DrawHallLantern3D,3*FP_1+FP_1/50,15*FP_1,1
|
||||
invoke DrawHallLantern3D,3*FP_1+FP_1/50,18*FP_1,1
|
||||
invoke DrawHallLantern3D,16*FP_1-FP_1/50,17*FP_1,-1
|
||||
invoke DrawHallLantern3D,16*FP_1-FP_1/50,22*FP_1,-1
|
||||
invoke DrawCityVertical3D,0,16*FP_1-FP_1/40,14*FP_1,16*FP_1,FP_1,2*FP_1,115
|
||||
invoke DrawCityVertical3D,0,3*FP_1+FP_1/40,15*FP_1,17*FP_1,FP_1,2*FP_1,115
|
||||
invoke DrawCityHorizontal3D,3*FP_1,5*FP_1,20*FP_1,22*FP_1,0,85
|
||||
call ApplyUpperPortalGlow3D
|
||||
ret
|
||||
RenderUpperLounge3D ENDP
|
||||
|
||||
TryUpperInteraction3D PROC
|
||||
cmp hallLevel3D,2
|
||||
jne upper_interaction_none
|
||||
cmp interactTargetId,INTERACT_UPPER_LEFT
|
||||
jne @F
|
||||
or upperLeverMask3D,1
|
||||
jmp upper_interaction_handled
|
||||
@@:
|
||||
cmp interactTargetId,INTERACT_UPPER_RIGHT
|
||||
jne @F
|
||||
or upperLeverMask3D,2
|
||||
jmp upper_interaction_handled
|
||||
@@:
|
||||
cmp interactTargetId,INTERACT_HELL_TOWER
|
||||
jne upper_interaction_none
|
||||
cmp upperPortalReady3D,1
|
||||
jne upper_interaction_locked
|
||||
mov upperPortalEntered3D,1
|
||||
call InitHellTower3D
|
||||
jmp upper_interaction_handled
|
||||
upper_interaction_locked:
|
||||
invoke CopyString3D,OFFSET upperPortalLocked3D,OFFSET flashMessage
|
||||
mov flashTimer,120
|
||||
upper_interaction_handled:
|
||||
call ClearKeys3D
|
||||
mov eax,1
|
||||
ret
|
||||
upper_interaction_none:
|
||||
xor eax,eax
|
||||
ret
|
||||
TryUpperInteraction3D ENDP
|
||||
|
||||
TryWalkUpperPortal3D PROC
|
||||
cmp playerY,FP_1*235/10
|
||||
jae @F
|
||||
mov upperPortalEntered3D,0
|
||||
@@:
|
||||
cmp upperPortalReady3D,1
|
||||
jne upper_portal_walk_done
|
||||
cmp upperPortalEntered3D,0
|
||||
jne upper_portal_walk_done
|
||||
cmp playerX,10*FP_1
|
||||
jb upper_portal_walk_done
|
||||
cmp playerX,11*FP_1
|
||||
ja upper_portal_walk_done
|
||||
cmp playerY,FP_1*2425/100
|
||||
jb upper_portal_walk_done
|
||||
mov upperPortalEntered3D,1
|
||||
call InitHellTower3D
|
||||
call ClearKeys3D
|
||||
upper_portal_walk_done:
|
||||
ret
|
||||
TryWalkUpperPortal3D ENDP
|
||||
|
||||
UpdateUpperEffects3D PROC uses ebx esi
|
||||
mov eax,animationClock3D
|
||||
cmp eax,upperEffectClock3D
|
||||
je upper_effects_done
|
||||
mov upperEffectClock3D,eax
|
||||
xor esi,esi
|
||||
mov ebx,1
|
||||
upper_effect_lever:
|
||||
test upperLeverMask3D,ebx
|
||||
jz upper_effect_next
|
||||
mov eax,deltaTime3D
|
||||
cmp eax,100
|
||||
jbe @F
|
||||
mov eax,100
|
||||
@@:
|
||||
add eax,upperLeverTime3D[esi*4]
|
||||
cmp eax,600
|
||||
jbe @F
|
||||
mov eax,600
|
||||
@@:
|
||||
mov upperLeverTime3D[esi*4],eax
|
||||
upper_effect_next:
|
||||
shl ebx,1
|
||||
inc esi
|
||||
cmp esi,2
|
||||
jb upper_effect_lever
|
||||
cmp upperLeverTime3D,600
|
||||
jne upper_effects_done
|
||||
cmp upperLeverTime3D[4],600
|
||||
jne upper_effects_done
|
||||
mov upperPortalReady3D,1
|
||||
upper_effects_done:
|
||||
ret
|
||||
UpdateUpperEffects3D ENDP
|
||||
|
||||
ApplyUpperPortalGlow3D PROC uses ebx esi edi
|
||||
local row:DWORD,column:DWORD,depth:DWORD,wx:DWORD,amount:DWORD,pixel:DWORD,pulse:DWORD
|
||||
mov eax,animationClock3D
|
||||
shr eax,7
|
||||
and eax,3
|
||||
add eax,10
|
||||
cmp upperPortalReady3D,1
|
||||
jne @F
|
||||
add eax,8
|
||||
@@:
|
||||
mov pulse,eax
|
||||
mov eax,viewHorizon3D
|
||||
add eax,2
|
||||
mov row,eax
|
||||
upper_glow_row:
|
||||
mov ecx,row
|
||||
sub ecx,viewHorizon3D
|
||||
mov eax,viewEye3D
|
||||
imul eax,SCREEN_H
|
||||
cdq
|
||||
idiv ecx
|
||||
mov depth,eax
|
||||
mov eax,row
|
||||
imul eax,SCREEN_W*4
|
||||
mov edi,eax
|
||||
mov column,0
|
||||
upper_glow_pixel:
|
||||
mov ebx,column
|
||||
mov eax,depth
|
||||
cmp eax,cityDepthBuffer3D[edi]
|
||||
ja upper_glow_next
|
||||
imul cityRayX3D[ebx*4]
|
||||
shrd eax,edx,16
|
||||
add eax,playerX
|
||||
sub eax,FP_1*105/10
|
||||
test eax,eax
|
||||
jns @F
|
||||
neg eax
|
||||
@@:
|
||||
mov wx,eax
|
||||
mov eax,depth
|
||||
imul cityRayY3D[ebx*4]
|
||||
shrd eax,edx,16
|
||||
add eax,playerY
|
||||
sub eax,FP_1*243/10
|
||||
test eax,eax
|
||||
jns @F
|
||||
neg eax
|
||||
@@:
|
||||
add eax,wx
|
||||
cmp eax,3*FP_1
|
||||
jae upper_glow_next
|
||||
neg eax
|
||||
add eax,3*FP_1
|
||||
imul pulse
|
||||
shrd eax,edx,16
|
||||
mov amount,eax
|
||||
mov eax,screenBuffer[edi]
|
||||
mov pixel,eax
|
||||
shr eax,16
|
||||
and eax,255
|
||||
add eax,amount
|
||||
cmp eax,255
|
||||
jbe @F
|
||||
mov eax,255
|
||||
@@:
|
||||
shl eax,16
|
||||
mov ebx,pixel
|
||||
and ebx,0000FFFFh
|
||||
or eax,ebx
|
||||
mov screenBuffer[edi],eax
|
||||
upper_glow_next:
|
||||
add edi,4
|
||||
inc column
|
||||
cmp column,SCREEN_W
|
||||
jb upper_glow_pixel
|
||||
inc row
|
||||
cmp row,SCREEN_H
|
||||
jb upper_glow_row
|
||||
ret
|
||||
ApplyUpperPortalGlow3D ENDP
|
||||
79
src_graphics/hall_expansion_data.inc
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
UpdateInteriorTravel3D PROTO
|
||||
UpdateInteriorEye3D PROTO
|
||||
UpdateStairHeight3D PROTO
|
||||
RenderWalkableStairs3D PROTO
|
||||
RenderUpperLounge3D PROTO
|
||||
RenderHallPortals3D PROTO
|
||||
CheckUpperInteractions3D PROTO
|
||||
InteriorDoorCanWalk3D PROTO :DWORD,:DWORD
|
||||
UpperCanWalk3D PROTO :DWORD,:DWORD
|
||||
ResolveUpperMaterial3D PROTO :DWORD
|
||||
InitUpperSurfaces3D PROTO
|
||||
DrawUpperGameTable3D PROTO :DWORD,:DWORD
|
||||
BeginWalkableStairs3D PROTO :DWORD,:DWORD
|
||||
RenderInteriorViews3D PROTO
|
||||
DrawInteriorView3D PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
|
||||
TryUpperInteraction3D PROTO
|
||||
TryWalkUpperPortal3D PROTO
|
||||
UpdateUpperEffects3D PROTO
|
||||
ApplyUpperPortalGlow3D PROTO
|
||||
INTERACT_UPPER_LEFT equ 43
|
||||
INTERACT_UPPER_RIGHT equ 44
|
||||
; stairRoute: 1=basement, 2=upper lounge. Direction is controlled by movement.
|
||||
.data
|
||||
stairRoute3D dd 0
|
||||
basementDoorTarget3D dd 0
|
||||
basementDoorOpen3D dd 0
|
||||
interiorDoorClock3D dd -1
|
||||
portalRendering3D dd 0
|
||||
portalPixelsWritten3D dd 0
|
||||
upperLeverMask3D dd 0
|
||||
upperPortalReady3D dd 0
|
||||
upperPortalEntered3D dd 0
|
||||
upperEffectClock3D dd -1
|
||||
upperLeverTime3D dd 0,0
|
||||
upperGamesArt3D AnimPack3D <>
|
||||
upperPortalArt3D AnimPack3D <>
|
||||
upperLeverArt3D AnimPack3D <>
|
||||
upperBarArt3D AnimPack3D <>
|
||||
upperBrazierArt3D AnimPack3D <>
|
||||
upperGamesPath3D db "assets\animations\hall\upper-games-v2.bin",0
|
||||
upperPortalPath3D db "assets\animations\hall\upper-portal-v3.bin",0
|
||||
upperLeverPath3D db "assets\animations\hall\upper-levers-v3.bin",0
|
||||
upperBarPath3D db "assets\animations\hall\upper-bar-v2.bin",0
|
||||
upperBrazierPath3D db "assets\animations\hall\upper-braziers-v3.bin",0
|
||||
upperLeftPrompt3D db 69,58,32,207,206,210,223,205,211,210,220,32,203,197,194,219,201,32,208,219,215,192,195,0
|
||||
upperRightPrompt3D db 69,58,32,207,206,210,223,205,211,210,220,32,207,208,192,194,219,201,32,208,219,215,192,195,0
|
||||
upperPortalPrompt3D db 69,58,32,194,206,201,210,200,32,194,32,193,192,216,205,222,32,192,196,192,0
|
||||
upperPortalLocked3D db 205,211,198,205,206,32,207,206,210,223,205,211,210,220,32,206,193,192,32,208,219,215,192,195,192,0
|
||||
upperPortal3D CityProp3D <FP_1*105/10,FP_1*247/10,FP_1*25/10,FP_1*245/100,0,OFFSET upperPortalArt3D>
|
||||
upperBrazier3D CityProp3D <0,FP_1*236/10,FP_1*55/100,FP_1*11/10,0,OFFSET upperBrazierArt3D>
|
||||
upperLever3D CityProp3D <0,FP_1*226/10,FP_1*45/100,FP_1*85/100,0,OFFSET upperLeverArt3D>
|
||||
upperStool3D CityProp3D <0,FP_1*157/10,FP_1*55/100,FP_1*9/10,1,OFFSET upperBarArt3D>
|
||||
upperSofa3D CityProp3D <FP_1*48/10,FP_1*142/10,FP_1*25/10,FP_1*11/10,6,OFFSET hallProps3D>
|
||||
upperMaterialArt3D AnimPack3D <>
|
||||
upperMaterialPath3D db "assets\animations\hall\upper-materials-v1.bin",0
|
||||
upperBarName3D db 205,206,215,205,206,201,32,193,192,208,32,34,199,192,210,204,197,205,200,197,34,0
|
||||
upperStairPrompt3D db 203,197,209,210,205,200,214,192,32,205,192,32,194,210,206,208,206,201,32,221,210,192,198,0
|
||||
upperExitPrompt3D db 203,197,209,210,205,200,214,192,32,194,32,195,203,192,194,205,219,201,32,199,192,203,0
|
||||
upperBarTender3D CityProp3D <26*FP_1,14*FP_1,FP_1,FP_1,0,OFFSET hallBartender3D,FP_1*9/20>
|
||||
upperTable3D CityProp3D <8*FP_1,7*FP_1,FP_1*8/5,FP_1*17/20,0,OFFSET hallBackFurniture3D>
|
||||
upperChair3D CityProp3D <8*FP_1,9*FP_1,FP_1*3/4,FP_1,0,OFFSET hallChairFurniture3D>
|
||||
upperStationX3D dd 7*FP_1,12*FP_1,9*FP_1
|
||||
upperStationY3D dd 17*FP_1,18*FP_1,FP_1*237/10
|
||||
upperDealer3D CityProp3D <0,0,FP_1*3/5,FP_1*7/5,0,OFFSET hallProps3D>
|
||||
upperGuest3D CityProp3D <0,0,FP_1*3/5,FP_1*6/5,5,0>
|
||||
upperGuestX3D dd FP_1*48/10,FP_1*64/10,FP_1*143/10
|
||||
upperGuestY3D dd FP_1*231/10,FP_1*231/10,FP_1*231/10
|
||||
upperGuestFrame3D dd 11,35,5
|
||||
.data?
|
||||
upperFeltPixels3D dd 1024 dup(?)
|
||||
upperKeyPixels3D dd 1024 dup(?)
|
||||
upperGlowPixels3D dd 1024 dup(?)
|
||||
portalClipTop3D dd SCREEN_W dup(?)
|
||||
portalClipBottom3D dd SCREEN_W dup(?)
|
||||
portalClipDepth3D dd SCREEN_W dup(?)
|
||||
portalSourcePixels3D dd SCREEN_SIZE dup(?)
|
||||
portalSourceDepth3D dd SCREEN_SIZE dup(?)
|
||||
portalSourceWalls3D dd SCREEN_W dup(?)
|
||||
.code
|
||||
|
|
@ -64,4 +64,100 @@ bunkerMap3D db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
|||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
upperLoungeMap3D db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,0,0,0,0,0,0,0,0,0,0,0,0,0,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
db 125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125
|
||||
upperStairMap3D db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102
|
||||
basementStairMap3D db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92
|
||||
.code
|
||||
|
|
|
|||
|
|
@ -14,8 +14,12 @@ hall_plane_row:
|
|||
mov eax, viewCeiling3D
|
||||
sub eax, viewEye3D
|
||||
mov material, 101
|
||||
cmp hallLevel3D, 0
|
||||
je @F
|
||||
cmp hallLevel3D,2
|
||||
jne @F
|
||||
mov material,125
|
||||
@@:
|
||||
cmp hallLevel3D, 1
|
||||
jne @F
|
||||
mov material, 93
|
||||
@@:
|
||||
jmp hall_plane_distance
|
||||
|
|
@ -118,6 +122,21 @@ hall_plane_sample:
|
|||
hall_plane_wood:
|
||||
mov ebx, 85
|
||||
hall_plane_choose_hd:
|
||||
cmp ebx,122
|
||||
jb hall_plane_existing_hd
|
||||
cmp ebx,125
|
||||
ja hall_plane_existing_hd
|
||||
mov ecx,upperMaterialArt3D.pixels
|
||||
test ecx,ecx
|
||||
jz hall_plane_upper_fallback
|
||||
sub ebx,122
|
||||
shl ebx,18
|
||||
add ecx,ebx
|
||||
jmp hall_plane_hd_coordinates
|
||||
hall_plane_upper_fallback:
|
||||
mov ebx,85
|
||||
mov material,ebx
|
||||
hall_plane_existing_hd:
|
||||
cmp distance, 6*FP_1
|
||||
ja hall_plane_low_sample
|
||||
mov ecx, hallRoomHD3D.pixels
|
||||
|
|
@ -135,6 +154,7 @@ hall_plane_old_hd:
|
|||
sub ebx, 80
|
||||
shl ebx, 18
|
||||
add ecx, ebx
|
||||
hall_plane_hd_coordinates:
|
||||
mov eax, wy
|
||||
shr eax, 8
|
||||
and eax, 255
|
||||
|
|
@ -197,12 +217,26 @@ hall_plane_next_row:
|
|||
RenderCeilingAndFloor ENDP
|
||||
|
||||
RenderSprites PROC uses ebx esi edi
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je hall_sprites_done
|
||||
cmp hallLevel3D, 0
|
||||
jne hall_sprites_done
|
||||
xor esi, esi
|
||||
hall_sprite_loop:
|
||||
cmp esi,3
|
||||
jb hall_sprite_next
|
||||
cmp esi,17
|
||||
je hall_sprite_next
|
||||
cmp esi,19
|
||||
je hall_sprite_next
|
||||
cmp esi,21
|
||||
je hall_sprite_next
|
||||
cmp esi, 14
|
||||
je hall_sprite_next
|
||||
cmp esi, 15
|
||||
je hall_sprite_next
|
||||
cmp esi, 16
|
||||
je hall_sprite_next
|
||||
imul ebx, esi, SIZEOF SpriteObject
|
||||
mov eax, casinoSprites[ebx].posX
|
||||
mov hallSpriteTemp3D.posX, eax
|
||||
|
|
@ -302,12 +336,36 @@ init_plane_lut:
|
|||
jmp init_plane_lut
|
||||
init_plane_done:
|
||||
call InitCasinoNpcMind
|
||||
call PositionTerminalStations3D
|
||||
mov scrStation3D.posX,29*FP_1
|
||||
mov scrStation3D.posY,FP_1*15/2
|
||||
call InitNpcLife3D
|
||||
call BuildHallLighting3D
|
||||
ret
|
||||
InitHall3D ENDP
|
||||
|
||||
LoadHallAssets3D PROC
|
||||
call InitUpperSurfaces3D
|
||||
invoke LoadAnimPack3D, OFFSET upperMaterialArt3D, OFFSET upperMaterialPath3D
|
||||
invoke ValidateCityPack3D, OFFSET upperMaterialArt3D,256,256,4
|
||||
invoke LoadAnimPack3D,OFFSET upperGamesArt3D,OFFSET upperGamesPath3D
|
||||
invoke ValidateCityPack3D,OFFSET upperGamesArt3D,320,224,2
|
||||
invoke LoadAnimPack3D,OFFSET upperPortalArt3D,OFFSET upperPortalPath3D
|
||||
invoke ValidateCityPack3D,OFFSET upperPortalArt3D,256,512,13
|
||||
invoke LoadAnimPack3D,OFFSET upperLeverArt3D,OFFSET upperLeverPath3D
|
||||
invoke ValidateCityPack3D,OFFSET upperLeverArt3D,128,224,6
|
||||
invoke LoadAnimPack3D,OFFSET upperBarArt3D,OFFSET upperBarPath3D
|
||||
invoke ValidateCityPack3D,OFFSET upperBarArt3D,160,256,2
|
||||
invoke LoadAnimPack3D,OFFSET upperBrazierArt3D,OFFSET upperBrazierPath3D
|
||||
invoke ValidateCityPack3D,OFFSET upperBrazierArt3D,128,256,4
|
||||
invoke LoadAnimPack3D, OFFSET cabaretArt3D, OFFSET cabaretPath3D
|
||||
invoke ValidateCityPack3D, OFFSET cabaretArt3D,160,256,12
|
||||
invoke LoadAnimPack3D, OFFSET cabaretPianoArt3D, OFFSET cabaretPianoPath3D
|
||||
invoke ValidateCityPack3D, OFFSET cabaretPianoArt3D,384,256,4
|
||||
invoke LoadAnimPack3D, OFFSET cabaretGuitarArt3D, OFFSET cabaretGuitarPath3D
|
||||
invoke ValidateCityPack3D, OFFSET cabaretGuitarArt3D,160,256,4
|
||||
invoke LoadAnimPack3D, OFFSET npcGuestArt3D, OFFSET npcGuestPath3D
|
||||
invoke ValidateCityPack3D, OFFSET npcGuestArt3D,128,224,36
|
||||
call LoadWorldDressing3D
|
||||
call LoadScratchArt3D
|
||||
invoke LoadAnimPack3D, OFFSET hallCardFurniture3D, OFFSET hallCardFurniturePath3D
|
||||
|
|
@ -379,6 +437,16 @@ hall_load_sign:
|
|||
LoadHallAssets3D ENDP
|
||||
|
||||
FreeHallAssets3D PROC
|
||||
invoke FreeAnimPack3D, OFFSET upperMaterialArt3D
|
||||
invoke FreeAnimPack3D,OFFSET upperGamesArt3D
|
||||
invoke FreeAnimPack3D,OFFSET upperPortalArt3D
|
||||
invoke FreeAnimPack3D,OFFSET upperLeverArt3D
|
||||
invoke FreeAnimPack3D,OFFSET upperBarArt3D
|
||||
invoke FreeAnimPack3D,OFFSET upperBrazierArt3D
|
||||
invoke FreeAnimPack3D, OFFSET cabaretArt3D
|
||||
invoke FreeAnimPack3D, OFFSET cabaretPianoArt3D
|
||||
invoke FreeAnimPack3D, OFFSET cabaretGuitarArt3D
|
||||
invoke FreeAnimPack3D, OFFSET npcGuestArt3D
|
||||
call FreeWorldDressing3D
|
||||
call FreeNativeWheelArt3D
|
||||
call FreeNativeCoinArt3D
|
||||
|
|
@ -469,6 +537,18 @@ surface_missing:
|
|||
ResolveWorldSurface3D ENDP
|
||||
|
||||
RenderHall3D PROC uses ebx esi
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne @F
|
||||
call RenderWalkableStairs3D
|
||||
call RenderInteriorViews3D
|
||||
ret
|
||||
@@:
|
||||
cmp hallLevel3D,2
|
||||
jne @F
|
||||
call RenderUpperLounge3D
|
||||
call RenderInteriorViews3D
|
||||
ret
|
||||
@@:
|
||||
cmp hallLevel3D, 0
|
||||
jne hall_render_bunker
|
||||
; Fixed bar counter/stage are world geometry, with height and occlusion.
|
||||
|
|
@ -538,6 +618,8 @@ hall_render_props:
|
|||
je hall_render_next_prop
|
||||
imul eax, esi, SIZEOF CityProp3D
|
||||
add eax, OFFSET hallObjects3D
|
||||
cmp esi,6
|
||||
je hall_render_next_prop
|
||||
invoke DrawCityObject3D, eax, 0
|
||||
hall_render_next_prop:
|
||||
inc esi
|
||||
|
|
@ -560,30 +642,14 @@ hall_render_lounge:
|
|||
cmp esi, HALL_DECOR_COUNT
|
||||
jb hall_render_lounge
|
||||
call RenderPatrons3D
|
||||
call RenderCabaretActors3D
|
||||
invoke DrawCityObject3D,OFFSET scrStation3D,0
|
||||
call RenderHallDressing3D
|
||||
call RenderHallPortals3D
|
||||
call ApplyHallLighting3D
|
||||
call RenderInteriorViews3D
|
||||
ret
|
||||
hall_render_bunker:
|
||||
mov esi, 0
|
||||
hall_stair_step:
|
||||
mov eax, esi
|
||||
imul eax, FP_1/2
|
||||
add eax, 24*FP_1
|
||||
lea ebx, [eax+FP_1/2]
|
||||
lea ecx, [esi+1]
|
||||
imul ecx, FP_1/8
|
||||
invoke DrawCityHorizontal3D,15*FP_1,18*FP_1,eax,ebx,ecx,88
|
||||
mov eax, esi
|
||||
imul eax, FP_1/2
|
||||
add eax, 24*FP_1
|
||||
mov ecx, esi
|
||||
imul ecx, FP_1/8
|
||||
lea edx, [ecx+FP_1/8]
|
||||
invoke DrawCityVertical3D,1,eax,15*FP_1,18*FP_1,ecx,edx,92
|
||||
inc esi
|
||||
cmp esi, 8
|
||||
jb hall_stair_step
|
||||
invoke DrawCityBoxSides3D, 14*FP_1,20*FP_1,14*FP_1,15*FP_1,0,FP_1/2,96
|
||||
invoke DrawCityHorizontal3D, 14*FP_1,20*FP_1,14*FP_1,15*FP_1,FP_1/2,97
|
||||
invoke DrawCityVertical3D,1,11*FP_1+FP_1/10,14*FP_1,16*FP_1,FP_1/4,FP_1*8/5,95
|
||||
|
|
@ -607,7 +673,7 @@ hall_stair_step:
|
|||
invoke DrawCityObject3D, OFFSET bunkerMerchant3D, 0
|
||||
invoke DrawCityObject3D, OFFSET bunkerDisplay3D, 0
|
||||
invoke DrawCityObject3D, OFFSET bunkerDesk3D, 0
|
||||
invoke DrawCityObject3D, OFFSET bunkerDoor3D, 0
|
||||
call RenderInteriorViews3D
|
||||
ret
|
||||
RenderHall3D ENDP
|
||||
|
||||
|
|
@ -645,6 +711,11 @@ hall_target_done:
|
|||
HallTarget3D ENDP
|
||||
|
||||
CheckHallInteractions3D PROC
|
||||
cmp hallLevel3D,2
|
||||
jne @F
|
||||
call CheckUpperInteractions3D
|
||||
ret
|
||||
@@:
|
||||
cmp hallLevel3D, 0
|
||||
jne hall_check_bunker
|
||||
cmp playerX, 3*FP_1
|
||||
|
|
@ -656,20 +727,30 @@ 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,16*FP_1,FP_1*73/10,INTERACT_CABARET_SEAT,OFFSET cabaretSeat3D
|
||||
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,FP_1*285/10,FP_1*105/10,INTERACT_BASEMENT,OFFSET hallBunkerPrompt3D
|
||||
invoke HallTarget3D, 27*FP_1,25*FP_1,INTERACT_BILLIARDS,OFFSET hallPoolPrompt3D
|
||||
invoke HallTarget3D,FP_1*305/10,21*FP_1,0,OFFSET upperStairPrompt3D
|
||||
call CheckNpcInteractions3D
|
||||
ret
|
||||
hall_check_bunker:
|
||||
mov interactTargetId, 0
|
||||
mov interactPromptPtr, 0
|
||||
invoke HallTarget3D, 16*FP_1,14*FP_1+FP_HALF,INTERACT_SHOP,OFFSET promptShop
|
||||
invoke HallTarget3D, 16*FP_1,27*FP_1,INTERACT_UPSTAIRS,OFFSET hallUpPrompt3D
|
||||
ret
|
||||
CheckHallInteractions3D ENDP
|
||||
|
||||
TriggerHallInteraction3D PROC
|
||||
call TryUpperInteraction3D
|
||||
test eax,eax
|
||||
jnz hall_trigger_handled
|
||||
cmp interactTargetId,INTERACT_CABARET_SEAT
|
||||
jne @F
|
||||
call BeginCabaret3D
|
||||
mov eax,1
|
||||
ret
|
||||
@@:
|
||||
cmp interactTargetId,INTERACT_SCRATCH
|
||||
jne @F
|
||||
call BeginScratch3D
|
||||
|
|
@ -693,21 +774,9 @@ TriggerHallInteraction3D PROC
|
|||
ret
|
||||
hall_trigger_stairs:
|
||||
cmp interactTargetId, INTERACT_BASEMENT
|
||||
je hall_begin_stairs
|
||||
cmp interactTargetId, INTERACT_UPSTAIRS
|
||||
jne hall_trigger_none
|
||||
hall_begin_stairs:
|
||||
cmp hallLevel3D, 0
|
||||
jne @F
|
||||
mov eax, playerX
|
||||
mov stairsReturnX3D, eax
|
||||
mov eax, playerY
|
||||
mov stairsReturnY3D, eax
|
||||
mov eax, playerAngle
|
||||
mov stairsReturnAngle3D, eax
|
||||
@@:
|
||||
mov engineMode, MODE_STAIRS
|
||||
mov stairsTime3D, 0
|
||||
; The button opens the steel leaf. Walking across its threshold is separate.
|
||||
mov basementDoorTarget3D,FP_1
|
||||
call ClearKeys3D
|
||||
mov eax, 1
|
||||
ret
|
||||
|
|
@ -721,113 +790,25 @@ hall_trigger_none:
|
|||
ret
|
||||
@@:
|
||||
xor eax, eax
|
||||
hall_trigger_handled:
|
||||
ret
|
||||
TriggerHallInteraction3D ENDP
|
||||
|
||||
HandleStairs3D PROC
|
||||
mov eax, deltaTime3D
|
||||
add stairsTime3D, eax
|
||||
cmp stairsTime3D, 1600
|
||||
jb stairs_handle_done
|
||||
xor hallLevel3D, 1
|
||||
cmp hallLevel3D, 0
|
||||
je stairs_back_to_hall
|
||||
mov worldMap3D, OFFSET bunkerMap3D
|
||||
mov floorTexture3D, 94
|
||||
mov viewCeiling3D, FP_1*5/2
|
||||
mov playerX, 16*FP_1+FP_HALF
|
||||
mov playerY, 23*FP_1+FP_HALF
|
||||
mov playerAngle, 384
|
||||
jmp stairs_complete
|
||||
stairs_back_to_hall:
|
||||
mov worldMap3D, OFFSET casinoMap
|
||||
mov floorTexture3D, 100
|
||||
mov viewCeiling3D, FP_1*5/2
|
||||
mov eax, stairsReturnX3D
|
||||
mov playerX, eax
|
||||
mov eax, stairsReturnY3D
|
||||
mov playerY, eax
|
||||
mov eax, stairsReturnAngle3D
|
||||
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:
|
||||
call UpdateStairHeight3D
|
||||
call HandlePlayerInput
|
||||
call UpdateInteriorTravel3D
|
||||
call UpdateStairHeight3D
|
||||
ret
|
||||
HandleStairs3D ENDP
|
||||
|
||||
RenderStairs3D PROC uses ebx esi
|
||||
local oldX:DWORD,oldY:DWORD,oldAngle:DWORD,oldMap:DWORD,oldEye:DWORD,oldCeiling:DWORD,oldLevel:DWORD,oldFloor:DWORD
|
||||
mov eax, playerX
|
||||
mov oldX, eax
|
||||
mov eax, playerY
|
||||
mov oldY, eax
|
||||
mov eax, playerAngle
|
||||
mov oldAngle, eax
|
||||
mov eax, worldMap3D
|
||||
mov oldMap, eax
|
||||
mov eax, viewEye3D
|
||||
mov oldEye, eax
|
||||
mov eax, viewCeiling3D
|
||||
mov oldCeiling, eax
|
||||
mov eax, hallLevel3D
|
||||
mov oldLevel, eax
|
||||
mov eax, floorTexture3D
|
||||
mov oldFloor, eax
|
||||
mov hallLevel3D, 1
|
||||
mov worldMap3D, OFFSET bunkerMap3D
|
||||
mov floorTexture3D, 94
|
||||
mov viewCeiling3D, FP_1*5/2
|
||||
mov playerX, 16*FP_1+FP_HALF
|
||||
mov playerAngle, 384
|
||||
mov eax, stairsTime3D
|
||||
cmp eax, 1600
|
||||
jbe @F
|
||||
mov eax, 1600
|
||||
@@:
|
||||
cmp oldLevel, 0
|
||||
je @F
|
||||
neg eax
|
||||
add eax, 1600
|
||||
@@:
|
||||
imul eax, 4*FP_1
|
||||
xor edx, edx
|
||||
mov ecx, 1600
|
||||
div ecx
|
||||
mov ebx, 27*FP_1+FP_HALF
|
||||
sub ebx, eax
|
||||
mov playerY, ebx
|
||||
shr eax, 2
|
||||
neg eax
|
||||
add eax, FP_1*37/20
|
||||
mov viewEye3D, eax
|
||||
call UpdateCamera3D
|
||||
call RenderCeilingAndFloor
|
||||
call RenderWalls
|
||||
call PrepareCityDepth3D
|
||||
call RenderHall3D
|
||||
mov eax, oldX
|
||||
mov playerX, eax
|
||||
mov eax, oldY
|
||||
mov playerY, eax
|
||||
mov eax, oldAngle
|
||||
mov playerAngle, eax
|
||||
mov eax, oldMap
|
||||
mov worldMap3D, eax
|
||||
mov eax, oldEye
|
||||
mov viewEye3D, eax
|
||||
mov eax, oldCeiling
|
||||
mov viewCeiling3D, eax
|
||||
mov eax, oldLevel
|
||||
mov hallLevel3D, eax
|
||||
mov eax, oldFloor
|
||||
mov floorTexture3D, eax
|
||||
call UpdateCamera3D
|
||||
call RenderHUD
|
||||
ret
|
||||
RenderStairs3D ENDP
|
||||
|
||||
|
|
@ -932,6 +913,11 @@ RenderBar3D PROC uses ebx
|
|||
mov playerX, 6*FP_1
|
||||
mov playerY, 25*FP_1+FP_1/3
|
||||
mov playerAngle, 384
|
||||
cmp hallLevel3D,2
|
||||
jne @F
|
||||
mov playerX,FP_1*25/2
|
||||
mov playerY,16*FP_1+FP_1/3
|
||||
@@:
|
||||
mov viewHorizon3D, HALF_H
|
||||
mov viewEye3D, HALL_STANDING_EYE
|
||||
call UpdateCamera3D
|
||||
|
|
@ -956,7 +942,12 @@ bar_active_scene:
|
|||
invoke DrawString8x8,176,91,ebx,00E7CA88h
|
||||
bar_draw_title:
|
||||
invoke DrawBox,0,0,320,28,0014191Dh
|
||||
invoke DrawString8x8, 18,15,OFFSET barName3D,00E7CA88h
|
||||
mov eax,OFFSET barName3D
|
||||
cmp hallLevel3D,2
|
||||
jne @F
|
||||
mov eax,OFFSET upperBarName3D
|
||||
@@:
|
||||
invoke DrawString8x8, 18,15,eax,00E7CA88h
|
||||
cmp barPhase3D, 2
|
||||
jne bar_render_done
|
||||
mov eax, barTime3D
|
||||
|
|
@ -1043,11 +1034,27 @@ city_world_guard_right:
|
|||
RenderCityGuards3D ENDP
|
||||
|
||||
CanWalkHall3D PROC uses esi ebx px:DWORD,py:DWORD
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne @F
|
||||
mov eax,1
|
||||
ret
|
||||
@@:
|
||||
cmp hallLevel3D,2
|
||||
jne @F
|
||||
invoke UpperCanWalk3D,px,py
|
||||
ret
|
||||
@@:
|
||||
cmp hallLevel3D, 0
|
||||
jne hall_collision_bunker
|
||||
invoke ScratchCanWalk3D,px,py
|
||||
test eax,eax
|
||||
jz hall_collision_blocked
|
||||
invoke InteriorDoorCanWalk3D,px,py
|
||||
test eax,eax
|
||||
jz hall_collision_blocked
|
||||
invoke TerminalCanWalk3D,px,py
|
||||
test eax,eax
|
||||
jz hall_collision_blocked
|
||||
xor esi, esi
|
||||
hall_collision_decor:
|
||||
imul ebx, esi, SIZEOF CityProp3D
|
||||
|
|
@ -1221,24 +1228,10 @@ hall_city_next_pilaster:
|
|||
RenderCityFacade3D ENDP
|
||||
|
||||
UpdateHall3D PROC
|
||||
call UpdateCabaret3D
|
||||
call UpdateInteriorTravel3D
|
||||
call UpdatePatrons3D
|
||||
mov viewHorizon3D, HALF_H
|
||||
mov viewEye3D, FP_1*17/20
|
||||
cmp hallLevel3D, 0
|
||||
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
|
||||
shr eax, 2
|
||||
cmp eax, FP_1
|
||||
jbe hall_step_eye
|
||||
mov eax, FP_1
|
||||
hall_step_eye:
|
||||
add viewEye3D, eax
|
||||
@@:
|
||||
call UpdateInteriorEye3D
|
||||
cmp barIntoxication3D, 0
|
||||
je hall_update_done
|
||||
mov eax, deltaTime3D
|
||||
|
|
@ -1352,4 +1345,8 @@ patron_render_loop:
|
|||
ret
|
||||
RenderPatrons3D ENDP
|
||||
include src_graphics\world_dressing.inc
|
||||
include src_graphics\terminal.inc
|
||||
include src_graphics\hall_expansion.inc
|
||||
include src_graphics\interior_portals.inc
|
||||
include src_graphics\cabaret.inc
|
||||
include src_graphics\hall_lighting.inc
|
||||
|
|
|
|||
244
src_graphics/interior_portals.inc
Normal file
|
|
@ -0,0 +1,244 @@
|
|||
; Doorways show the adjacent native scene through a depth-tested screen-space
|
||||
; aperture. Camera translations are the same as the walking thresholds.
|
||||
DrawInteriorView3D PROC uses ebx esi edi axis:DWORD,plane:DWORD,lo:DWORD,hi:DWORD,z0:DWORD,z1:DWORD,offsetX:DWORD,offsetY:DWORD,offsetZ:DWORD,targetMap:DWORD,targetMode:DWORD,targetLevel:DWORD,targetFloor:DWORD,targetRoute:DWORD
|
||||
local oldX:DWORD,oldY:DWORD,oldEye:DWORD,oldCeiling:DWORD,oldMap:DWORD
|
||||
local oldMode:DWORD,oldLevel:DWORD,oldFloor:DWORD,oldRoute:DWORD
|
||||
local found:DWORD,depth:DWORD,row:DWORD,pixel:DWORD
|
||||
mov found,0
|
||||
xor ebx,ebx
|
||||
portal_mask_column:
|
||||
mov portalClipDepth3D[ebx*4],0
|
||||
cmp axis,0
|
||||
jne portal_mask_y
|
||||
invoke CityRayPlane3D,plane,playerX,cityRayX3D[ebx*4]
|
||||
jmp portal_mask_depth
|
||||
portal_mask_y:
|
||||
invoke CityRayPlane3D,plane,playerY,cityRayY3D[ebx*4]
|
||||
portal_mask_depth:
|
||||
cmp eax,2048
|
||||
jle portal_mask_next
|
||||
cmp eax,8*FP_1
|
||||
ja portal_mask_next
|
||||
cmp eax,zBuffer[ebx*4]
|
||||
ja portal_mask_next
|
||||
mov depth,eax
|
||||
cmp axis,0
|
||||
jne portal_along_x
|
||||
imul cityRayY3D[ebx*4]
|
||||
shrd eax,edx,16
|
||||
add eax,playerY
|
||||
jmp portal_mask_along
|
||||
portal_along_x:
|
||||
imul cityRayX3D[ebx*4]
|
||||
shrd eax,edx,16
|
||||
add eax,playerX
|
||||
portal_mask_along:
|
||||
cmp eax,lo
|
||||
jl portal_mask_next
|
||||
cmp eax,hi
|
||||
jge portal_mask_next
|
||||
mov eax,viewEye3D
|
||||
sub eax,z1
|
||||
imul eax,SCREEN_H
|
||||
cdq
|
||||
idiv depth
|
||||
add eax,viewHorizon3D
|
||||
test eax,eax
|
||||
jns @F
|
||||
xor eax,eax
|
||||
@@:
|
||||
mov portalClipTop3D[ebx*4],eax
|
||||
mov eax,viewEye3D
|
||||
sub eax,z0
|
||||
imul eax,SCREEN_H
|
||||
cdq
|
||||
idiv depth
|
||||
add eax,viewHorizon3D
|
||||
cmp eax,SCREEN_H
|
||||
jle @F
|
||||
mov eax,SCREEN_H
|
||||
@@:
|
||||
cmp eax,portalClipTop3D[ebx*4]
|
||||
jle portal_mask_next
|
||||
mov portalClipBottom3D[ebx*4],eax
|
||||
mov eax,depth
|
||||
mov portalClipDepth3D[ebx*4],eax
|
||||
inc found
|
||||
portal_mask_next:
|
||||
inc ebx
|
||||
cmp ebx,SCREEN_W
|
||||
jb portal_mask_column
|
||||
cmp found,0
|
||||
je portal_view_done
|
||||
cld
|
||||
lea esi,screenBuffer
|
||||
lea edi,portalSourcePixels3D
|
||||
mov ecx,SCREEN_SIZE
|
||||
rep movsd
|
||||
lea esi,cityDepthBuffer3D
|
||||
lea edi,portalSourceDepth3D
|
||||
mov ecx,SCREEN_SIZE
|
||||
rep movsd
|
||||
lea esi,zBuffer
|
||||
lea edi,portalSourceWalls3D
|
||||
mov ecx,SCREEN_W
|
||||
rep movsd
|
||||
mov eax,playerX
|
||||
mov oldX,eax
|
||||
add eax,offsetX
|
||||
mov playerX,eax
|
||||
mov eax,playerY
|
||||
mov oldY,eax
|
||||
add eax,offsetY
|
||||
mov playerY,eax
|
||||
mov eax,viewEye3D
|
||||
mov oldEye,eax
|
||||
add eax,offsetZ
|
||||
mov viewEye3D,eax
|
||||
mov eax,viewCeiling3D
|
||||
mov oldCeiling,eax
|
||||
mov viewCeiling3D,FP_1*5/2
|
||||
cmp targetMode,MODE_STAIRS
|
||||
jne @F
|
||||
mov viewCeiling3D,FP_1*11/2
|
||||
@@:
|
||||
mov eax,worldMap3D
|
||||
mov oldMap,eax
|
||||
mov eax,targetMap
|
||||
mov worldMap3D,eax
|
||||
mov eax,engineMode
|
||||
mov oldMode,eax
|
||||
mov eax,targetMode
|
||||
mov engineMode,eax
|
||||
mov eax,hallLevel3D
|
||||
mov oldLevel,eax
|
||||
mov eax,targetLevel
|
||||
mov hallLevel3D,eax
|
||||
mov eax,floorTexture3D
|
||||
mov oldFloor,eax
|
||||
mov eax,targetFloor
|
||||
mov floorTexture3D,eax
|
||||
mov eax,stairRoute3D
|
||||
mov oldRoute,eax
|
||||
mov eax,targetRoute
|
||||
mov stairRoute3D,eax
|
||||
mov portalRendering3D,1
|
||||
call UpdateCamera3D
|
||||
call RenderCeilingAndFloor
|
||||
call RenderWalls
|
||||
call PrepareCityDepth3D
|
||||
call RenderSprites
|
||||
call RenderHall3D
|
||||
mov portalRendering3D,0
|
||||
mov eax,oldX
|
||||
mov playerX,eax
|
||||
mov eax,oldY
|
||||
mov playerY,eax
|
||||
mov eax,oldEye
|
||||
mov viewEye3D,eax
|
||||
mov eax,oldCeiling
|
||||
mov viewCeiling3D,eax
|
||||
mov eax,oldMap
|
||||
mov worldMap3D,eax
|
||||
mov eax,oldMode
|
||||
mov engineMode,eax
|
||||
mov eax,oldLevel
|
||||
mov hallLevel3D,eax
|
||||
mov eax,oldFloor
|
||||
mov floorTexture3D,eax
|
||||
mov eax,oldRoute
|
||||
mov stairRoute3D,eax
|
||||
mov row,0
|
||||
xor edi,edi
|
||||
portal_compose_row:
|
||||
xor ebx,ebx
|
||||
portal_compose_pixel:
|
||||
mov eax,portalClipDepth3D[ebx*4]
|
||||
test eax,eax
|
||||
jz portal_restore_pixel
|
||||
mov ecx,row
|
||||
cmp ecx,portalClipTop3D[ebx*4]
|
||||
jl portal_restore_pixel
|
||||
cmp ecx,portalClipBottom3D[ebx*4]
|
||||
jge portal_restore_pixel
|
||||
cmp eax,portalSourceDepth3D[edi]
|
||||
ja portal_restore_pixel
|
||||
; The rendered destination is already in screenBuffer at this exact ray.
|
||||
mov cityDepthBuffer3D[edi],eax
|
||||
inc portalPixelsWritten3D
|
||||
jmp portal_compose_next
|
||||
portal_restore_pixel:
|
||||
mov eax,portalSourcePixels3D[edi]
|
||||
mov screenBuffer[edi],eax
|
||||
mov eax,portalSourceDepth3D[edi]
|
||||
mov cityDepthBuffer3D[edi],eax
|
||||
portal_compose_next:
|
||||
add edi,4
|
||||
inc ebx
|
||||
cmp ebx,SCREEN_W
|
||||
jb portal_compose_pixel
|
||||
inc row
|
||||
cmp row,SCREEN_H
|
||||
jb portal_compose_row
|
||||
lea esi,portalSourceWalls3D
|
||||
lea edi,zBuffer
|
||||
mov ecx,SCREEN_W
|
||||
rep movsd
|
||||
call UpdateCamera3D
|
||||
call PrepareCityRays3D
|
||||
portal_view_done:
|
||||
ret
|
||||
DrawInteriorView3D ENDP
|
||||
|
||||
RenderInteriorViews3D PROC
|
||||
cmp portalRendering3D,0
|
||||
jne interior_views_done
|
||||
mov portalPixelsWritten3D,0
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je interior_views_stairs
|
||||
cmp hallLevel3D,2
|
||||
je interior_views_lounge
|
||||
cmp hallLevel3D,1
|
||||
je interior_views_bunker
|
||||
cmp playerX,FP_1*303/10
|
||||
jae @F
|
||||
invoke DrawInteriorView3D,0,FP_1*3025/100,20*FP_1,22*FP_1,0,FP_1*5/2,-27*FP_1,0,0,OFFSET upperStairMap3D,MODE_STAIRS,0,85,2
|
||||
@@:
|
||||
cmp basementDoorOpen3D,FP_1*9/10
|
||||
jb interior_views_done
|
||||
cmp playerX,FP_1*286/10
|
||||
jae interior_views_done
|
||||
invoke DrawInteriorView3D,0,FP_1*285/10,FP_1*975/100,FP_1*1125/100,0,FP_1*19/10,-25*FP_1,FP_1*21/2,FP_1*3/2,OFFSET basementStairMap3D,MODE_STAIRS,0,94,1
|
||||
ret
|
||||
interior_views_lounge:
|
||||
cmp playerX,FP_1*32/10
|
||||
jbe interior_views_done
|
||||
invoke DrawInteriorView3D,0,FP_1*325/100,20*FP_1,22*FP_1,0,FP_1*5/2,12*FP_1,0,FP_1*13/5,OFFSET upperStairMap3D,MODE_STAIRS,2,85,2
|
||||
ret
|
||||
interior_views_bunker:
|
||||
cmp playerY,FP_1*277/10
|
||||
jae interior_views_done
|
||||
invoke DrawInteriorView3D,1,FP_1*2765/100,15*FP_1,18*FP_1,0,FP_1*5/2,0,-13*FP_1,0,OFFSET basementStairMap3D,MODE_STAIRS,1,94,1
|
||||
ret
|
||||
interior_views_stairs:
|
||||
cmp stairRoute3D,1
|
||||
je interior_views_concrete
|
||||
cmp playerX,FP_1*31/10
|
||||
jbe @F
|
||||
invoke DrawInteriorView3D,0,FP_1*315/100,20*FP_1,22*FP_1,0,FP_1*5/2,27*FP_1,0,0,OFFSET casinoMap,MODE_WALK,0,100,0
|
||||
@@:
|
||||
cmp playerX,16*FP_1
|
||||
jae interior_views_done
|
||||
invoke DrawInteriorView3D,0,FP_1*159/10,20*FP_1,22*FP_1,FP_1*13/5,FP_1*51/10,-12*FP_1,0,-FP_1*13/5,OFFSET upperLoungeMap3D,MODE_WALK,2,124,0
|
||||
ret
|
||||
interior_views_concrete:
|
||||
cmp playerX,FP_1*31/10
|
||||
jbe @F
|
||||
invoke DrawInteriorView3D,0,FP_1*315/100,FP_1*2025/100,FP_1*2175/100,FP_1*3/2,FP_1*34/10,25*FP_1,-FP_1*21/2,-FP_1*3/2,OFFSET casinoMap,MODE_WALK,0,100,0
|
||||
@@:
|
||||
cmp playerY,FP_1*143/10
|
||||
jbe interior_views_done
|
||||
invoke DrawInteriorView3D,1,FP_1*144/10,15*FP_1,18*FP_1,0,FP_1*5/2,0,13*FP_1,0,OFFSET bunkerMap3D,MODE_WALK,1,94,0
|
||||
interior_views_done:
|
||||
ret
|
||||
RenderInteriorViews3D ENDP
|
||||
|
|
@ -58,9 +58,11 @@ DrawBJMiniCard3D PROC uses ebx esi x:DWORD,y:DWORD,card:DWORD,hidden:DWORD
|
|||
@@:
|
||||
mov ink,00BF263Ch
|
||||
bj_mini_face:
|
||||
lea edx,cardRankNames3D[edx*2]
|
||||
mov edx,rank
|
||||
lea edx,[edx+edx*2]
|
||||
add edx,OFFSET cardRankNames3D
|
||||
mov eax,x
|
||||
add eax,3
|
||||
add eax,2
|
||||
mov ecx,y
|
||||
add ecx,3
|
||||
invoke DrawString8x8,eax,ecx,edx,ink
|
||||
|
|
|
|||
|
|
@ -360,7 +360,7 @@ NpcNameText3D PROC uses esi edi identity:DWORD
|
|||
mov eax,identity
|
||||
cmp eax,NPC_IDENTITY_COUNT
|
||||
jae npc_name_empty
|
||||
mov esi,npcNames3D[eax*4]
|
||||
mov esi,npcGuestNames3D[eax*4]
|
||||
lea edi,npcNameBuffer3D
|
||||
mov ecx,60
|
||||
npc_name_copy:
|
||||
|
|
|
|||
|
|
@ -466,20 +466,19 @@ npc_draw_person:
|
|||
mov npcSprite3D.posY,eax
|
||||
mov npcSprite3D.worldW,FP_1*3/5
|
||||
mov npcSprite3D.worldH,FP_1*6/5
|
||||
mov npcSprite3D.frame,0
|
||||
cmp npcLife3D[ebx].identity,11
|
||||
jne @F
|
||||
mov npcSprite3D.frame,4
|
||||
@@:
|
||||
mov npcSprite3D.asset,OFFSET npcGuestArt3D
|
||||
cmp npcGuestArt3D.pixels,0
|
||||
je npc_draw_next
|
||||
mov eax,npcLife3D[ebx].identity
|
||||
mov eax,npcGuestAppearance3D[eax*4]
|
||||
imul eax,6
|
||||
mov npcSprite3D.frame,eax
|
||||
cmp npcLife3D[ebx].state,NPC_SIT
|
||||
jne npc_draw_walk
|
||||
mov npcSprite3D.frame,8
|
||||
cmp npcLife3D[ebx].identity,11
|
||||
jne @F
|
||||
mov npcSprite3D.frame,10
|
||||
@@:
|
||||
mov npcSprite3D.worldW,FP_1*4/5
|
||||
mov npcSprite3D.worldH,FP_1
|
||||
add npcSprite3D.frame,5
|
||||
; Seated pose is already shorter within the same foot-anchored canvas.
|
||||
mov npcSprite3D.worldW,FP_1*3/5
|
||||
mov npcSprite3D.worldH,FP_1*6/5
|
||||
jmp npc_draw_sprite
|
||||
npc_draw_walk:
|
||||
cmp npcLife3D[ebx].state,NPC_WALK
|
||||
|
|
@ -489,6 +488,7 @@ npc_draw_walk:
|
|||
mov ecx,180
|
||||
div ecx
|
||||
and eax,3
|
||||
inc eax
|
||||
add npcSprite3D.frame,eax
|
||||
npc_draw_sprite:
|
||||
invoke DrawCityObject3D,OFFSET npcSprite3D,0
|
||||
|
|
|
|||
|
|
@ -44,6 +44,18 @@ NpcLife3D ENDS
|
|||
.data
|
||||
npcLife3D NpcLife3D NPC_LIFE_COUNT dup(<>)
|
||||
npcLifeReady3D dd 0
|
||||
npcGuestArt3D AnimPack3D <>
|
||||
npcGuestPath3D db "assets\animations\hall\guest-cast-v1.bin",0
|
||||
; The two rotations share six appearances, with a mixed cast on every visit.
|
||||
npcGuestAppearance3D dd 0,2,4,1,3,5,2,0,4,1,3,5
|
||||
npcGuestNames3D dd OFFSET npcName0,OFFSET npcName1,OFFSET npcName2,OFFSET npcGuestAnna3D
|
||||
dd OFFSET npcGuestMarina3D,OFFSET npcGuestVera3D,OFFSET npcName6,OFFSET npcName7
|
||||
dd OFFSET npcName8,OFFSET npcGuestOlga3D,OFFSET npcGuestDaria3D,OFFSET npcName11
|
||||
npcGuestAnna3D db 192,237,237,224,32,204,232,240,237,224,255,0
|
||||
npcGuestMarina3D db 204,224,240,232,237,224,32,203,243,237,237,224,255,0
|
||||
npcGuestVera3D db 194,229,240,224,32,209,229,240,229,225,240,255,237,224,255,0
|
||||
npcGuestOlga3D db 206,235,252,227,224,32,204,232,240,237,224,255,0
|
||||
npcGuestDaria3D db 196,224,240,252,255,32,193,238,227,224,242,224,255,0
|
||||
npcLifeClock3D dd 0
|
||||
npcSelected3D dd -1
|
||||
npcDialogIdentity3D dd 0
|
||||
|
|
|
|||
|
|
@ -562,6 +562,7 @@ IFDEF CASINO_ACTION_TEST
|
|||
ENDIF
|
||||
cmp arrivalConfirmed3D, 0
|
||||
je progress_saved
|
||||
call RefreshTerminalAchievements3D
|
||||
cmp nativeProfileActive3D, 0
|
||||
je @F
|
||||
call SaveGraphicProfile3D
|
||||
|
|
|
|||
378
src_graphics/terminal.inc
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
LoadTerminalArt3D PROC
|
||||
invoke LoadAnimPack3D,OFFSET terminalWorldArt3D,OFFSET terminalWorldPath3D
|
||||
invoke ValidateCityPack3D,OFFSET terminalWorldArt3D,256,512,4
|
||||
invoke LoadAnimPack3D,OFFSET terminalScreenArt3D,OFFSET terminalScreenPath3D
|
||||
invoke ValidateCityPack3D,OFFSET terminalScreenArt3D,640,480,1
|
||||
ret
|
||||
LoadTerminalArt3D ENDP
|
||||
|
||||
FreeTerminalArt3D PROC
|
||||
invoke FreeAnimPack3D,OFFSET terminalWorldArt3D
|
||||
invoke FreeAnimPack3D,OFFSET terminalScreenArt3D
|
||||
ret
|
||||
FreeTerminalArt3D ENDP
|
||||
|
||||
ResolveTerminalSurface3D PROC material:DWORD
|
||||
mov eax,material
|
||||
sub eax,118
|
||||
cmp eax,4
|
||||
jae terminal_surface_missing
|
||||
cmp terminalWorldArt3D.pixels,0
|
||||
je terminal_surface_missing
|
||||
shl eax,19
|
||||
add eax,terminalWorldArt3D.pixels
|
||||
mov ecx,256
|
||||
mov edx,512
|
||||
ret
|
||||
terminal_surface_missing:
|
||||
xor eax,eax
|
||||
ret
|
||||
ResolveTerminalSurface3D ENDP
|
||||
|
||||
PositionTerminalStations3D PROC
|
||||
mov casinoSprites[15*SIZEOF SpriteObject].SpriteObject.posX,TERMINAL_CASH_X
|
||||
mov casinoSprites[15*SIZEOF SpriteObject].SpriteObject.posY,TERMINAL_CENTER_Y
|
||||
mov casinoSprites[16*SIZEOF SpriteObject].SpriteObject.posX,TERMINAL_TROPHY_X
|
||||
mov casinoSprites[16*SIZEOF SpriteObject].SpriteObject.posY,TERMINAL_CENTER_Y
|
||||
mov casinoWaypoints3D[14*SIZEOF NpcWaypoint].NpcWaypoint.posX,TERMINAL_CASH_X
|
||||
mov casinoWaypoints3D[14*SIZEOF NpcWaypoint].NpcWaypoint.posY,FP_1*29/2
|
||||
ret
|
||||
PositionTerminalStations3D ENDP
|
||||
|
||||
DrawTerminalCabinet3D PROC uses ebx esi edi wx:DWORD,wy:DWORD,material:DWORD
|
||||
local x0:DWORD,x1:DWORD,y0:DWORD,y1:DWORD
|
||||
mov eax,wx
|
||||
sub eax,FP_1*7/20
|
||||
mov x0,eax
|
||||
add eax,FP_1*7/10
|
||||
mov x1,eax
|
||||
mov eax,wy
|
||||
sub eax,FP_1/5
|
||||
mov y0,eax
|
||||
add eax,FP_1*2/5
|
||||
mov y1,eax
|
||||
invoke DrawCityBoxSides3D,x0,x1,y0,y1,0,FP_1*8/5,121
|
||||
invoke DrawCityHorizontal3D,x0,x1,y0,y1,FP_1*8/5,110
|
||||
mov eax,y1
|
||||
add eax,FP_1/500
|
||||
invoke DrawCityVertical3D,1,eax,x0,x1,0,FP_1*8/5,material
|
||||
ret
|
||||
DrawTerminalCabinet3D ENDP
|
||||
|
||||
RenderTerminalCabinets3D PROC
|
||||
invoke DrawTerminalCabinet3D,TERMINAL_CASH_X,TERMINAL_CENTER_Y,118
|
||||
invoke DrawTerminalCabinet3D,TERMINAL_TROPHY_X,TERMINAL_CENTER_Y,119
|
||||
ret
|
||||
RenderTerminalCabinets3D ENDP
|
||||
|
||||
TerminalCanWalk3D PROC uses esi px:DWORD,py:DWORD
|
||||
mov eax,py
|
||||
sub eax,TERMINAL_CENTER_Y
|
||||
test eax,eax
|
||||
jns @F
|
||||
neg eax
|
||||
@@:
|
||||
cmp eax,FP_1*7/20
|
||||
ja terminal_walk_clear
|
||||
mov esi,TERMINAL_CASH_X
|
||||
terminal_walk_station:
|
||||
mov eax,px
|
||||
sub eax,esi
|
||||
test eax,eax
|
||||
jns @F
|
||||
neg eax
|
||||
@@:
|
||||
cmp eax,FP_HALF
|
||||
jbe terminal_walk_blocked
|
||||
add esi,FP_1*3/2
|
||||
cmp esi,TERMINAL_TROPHY_X
|
||||
jbe terminal_walk_station
|
||||
terminal_walk_clear:
|
||||
mov eax,1
|
||||
ret
|
||||
terminal_walk_blocked:
|
||||
xor eax,eax
|
||||
ret
|
||||
TerminalCanWalk3D ENDP
|
||||
|
||||
; A projecting solid bracket supports a glass body, not a plane on the wall.
|
||||
DrawHallLantern3D PROC uses ebx esi edi wallX:DWORD,wy:DWORD,direction:DWORD
|
||||
local x0:DWORD,x1:DWORD,y0:DWORD,y1:DWORD,bodyX:DWORD
|
||||
mov eax,wallX
|
||||
mov ebx,direction
|
||||
imul ebx,FP_1*3/10
|
||||
add eax,ebx
|
||||
mov bodyX,eax
|
||||
mov eax,wy
|
||||
sub eax,FP_1*13/100
|
||||
mov y0,eax
|
||||
add eax,FP_1*26/100
|
||||
mov y1,eax
|
||||
mov eax,bodyX
|
||||
sub eax,FP_1*3/25
|
||||
mov x0,eax
|
||||
add eax,FP_1*6/25
|
||||
mov x1,eax
|
||||
invoke DrawCityBoxSides3D,x0,x1,y0,y1,FP_1*7/5,FP_1*19/10,120
|
||||
invoke DrawCityHorizontal3D,x0,x1,y0,y1,FP_1*19/10,110
|
||||
invoke DrawCityHorizontal3D,x0,x1,y0,y1,FP_1*7/5,110
|
||||
; A short horizontal arm and wall plate remain visible at grazing angles.
|
||||
mov eax,wallX
|
||||
mov edx,bodyX
|
||||
cmp eax,edx
|
||||
jle @F
|
||||
xchg eax,edx
|
||||
@@:
|
||||
mov x0,eax
|
||||
mov x1,edx
|
||||
mov eax,wy
|
||||
sub eax,FP_1/30
|
||||
mov y0,eax
|
||||
add eax,FP_1/15
|
||||
mov y1,eax
|
||||
invoke DrawCityBoxSides3D,x0,x1,y0,y1,FP_1*31/20,FP_1*8/5,110
|
||||
invoke DrawCityHorizontal3D,x0,x1,y0,y1,FP_1*8/5,110
|
||||
mov eax,wallX
|
||||
sub eax,FP_1/100
|
||||
mov x0,eax
|
||||
add eax,FP_1/50
|
||||
mov x1,eax
|
||||
mov eax,wy
|
||||
sub eax,FP_1/12
|
||||
mov y0,eax
|
||||
add eax,FP_1/6
|
||||
mov y1,eax
|
||||
invoke DrawCityBoxSides3D,x0,x1,y0,y1,FP_1*29/20,FP_1*7/4,110
|
||||
ret
|
||||
DrawHallLantern3D ENDP
|
||||
|
||||
TerminalCountBits3D PROC value:DWORD
|
||||
mov ecx,value
|
||||
xor eax,eax
|
||||
terminal_count_bit:
|
||||
test ecx,ecx
|
||||
jz terminal_count_done
|
||||
lea edx,[ecx-1]
|
||||
and ecx,edx
|
||||
inc eax
|
||||
jmp terminal_count_bit
|
||||
terminal_count_done:
|
||||
ret
|
||||
TerminalCountBits3D ENDP
|
||||
|
||||
TerminalProgress3D PROC index:DWORD
|
||||
cmp index,4
|
||||
jae terminal_progress_collection
|
||||
mov eax,bankroll
|
||||
ret
|
||||
terminal_progress_collection:
|
||||
cmp index,6
|
||||
jae terminal_progress_quests
|
||||
mov eax,ownedBacks3D
|
||||
and eax,1FFEh
|
||||
invoke TerminalCountBits3D,eax
|
||||
ret
|
||||
terminal_progress_quests:
|
||||
mov eax,npcClaimed3D
|
||||
and eax,0FFFh
|
||||
invoke TerminalCountBits3D,eax
|
||||
ret
|
||||
TerminalProgress3D ENDP
|
||||
|
||||
; Existing saved achievement bits are preserved; the merchant still owns bit 4.
|
||||
RefreshTerminalAchievements3D PROC uses esi
|
||||
xor esi,esi
|
||||
terminal_refresh_next:
|
||||
invoke TerminalProgress3D,esi
|
||||
cmp eax,terminalGoals3D[esi*4]
|
||||
jb @F
|
||||
mov ecx,terminalBits3D[esi*4]
|
||||
bts achievements,ecx
|
||||
@@:
|
||||
inc esi
|
||||
cmp esi,8
|
||||
jb terminal_refresh_next
|
||||
ret
|
||||
RefreshTerminalAchievements3D ENDP
|
||||
|
||||
BeginTerminal3D PROC kind:DWORD
|
||||
mov eax,kind
|
||||
and eax,1
|
||||
mov terminalTab3D,eax
|
||||
mov terminalPage3D,0
|
||||
call NpcEnsureJournal3D
|
||||
call RefreshTerminalAchievements3D
|
||||
call SaveProgress3D
|
||||
mov engineMode,MODE_ATM
|
||||
call ClearKeys3D
|
||||
mov mouseClick3D,0
|
||||
ret
|
||||
BeginTerminal3D ENDP
|
||||
|
||||
HandleTerminal3D PROC
|
||||
cmp mouseClick3D,0
|
||||
je terminal_handle_keys
|
||||
cmp mouseY3D,8
|
||||
jb terminal_handle_keys
|
||||
cmp mouseY3D,25
|
||||
ja terminal_handle_bottom
|
||||
cmp mouseX3D,248
|
||||
jae terminal_close
|
||||
terminal_handle_bottom:
|
||||
cmp mouseY3D,203
|
||||
jb terminal_handle_keys
|
||||
cmp mouseX3D,48
|
||||
jb terminal_previous
|
||||
cmp mouseX3D,272
|
||||
ja terminal_next
|
||||
terminal_handle_keys:
|
||||
invoke Pressed3D,45h
|
||||
test eax,eax
|
||||
jnz terminal_close
|
||||
invoke Pressed3D,25h
|
||||
test eax,eax
|
||||
jnz terminal_previous
|
||||
invoke Pressed3D,27h
|
||||
test eax,eax
|
||||
jnz terminal_next
|
||||
jmp terminal_handled
|
||||
terminal_previous:
|
||||
cmp terminalTab3D,0
|
||||
je terminal_previous_from_cash
|
||||
cmp terminalPage3D,0
|
||||
je terminal_show_cash
|
||||
dec terminalPage3D
|
||||
jmp terminal_handled
|
||||
terminal_previous_from_cash:
|
||||
mov terminalTab3D,1
|
||||
mov terminalPage3D,2
|
||||
jmp terminal_handled
|
||||
terminal_next:
|
||||
cmp terminalTab3D,0
|
||||
je terminal_next_from_cash
|
||||
cmp terminalPage3D,2
|
||||
jae terminal_show_cash
|
||||
inc terminalPage3D
|
||||
jmp terminal_handled
|
||||
terminal_next_from_cash:
|
||||
mov terminalTab3D,1
|
||||
mov terminalPage3D,0
|
||||
jmp terminal_handled
|
||||
terminal_show_cash:
|
||||
mov terminalTab3D,0
|
||||
mov terminalPage3D,0
|
||||
jmp terminal_handled
|
||||
terminal_close:
|
||||
mov engineMode,MODE_WALK
|
||||
call ClearKeys3D
|
||||
terminal_handled:
|
||||
mov mouseClick3D,0
|
||||
ret
|
||||
HandleTerminal3D ENDP
|
||||
|
||||
RenderTerminal3D PROC uses ebx esi edi
|
||||
local rowY:DWORD,opened:DWORD,progress:DWORD
|
||||
invoke DrawBox,0,0,320,240,000B1815h
|
||||
cmp terminalScreenArt3D.pixels,0
|
||||
je @F
|
||||
invoke DrawAnimSprite3D,OFFSET terminalScreenArt3D,0,0,0,640,480
|
||||
@@:
|
||||
mov edx,OFFSET termCash
|
||||
cmp terminalTab3D,0
|
||||
je @F
|
||||
mov edx,OFFSET termTrophy
|
||||
@@:
|
||||
invoke DrawString8x8,64,12,edx,002D2116h
|
||||
invoke DrawBoxBorder,248,8,56,17,001A2522h,00C8AD75h,1
|
||||
invoke DrawString8x8,256,12,OFFSET termClose,00F2E6C6h
|
||||
mov opened,0
|
||||
xor esi,esi
|
||||
terminal_count_unlocked:
|
||||
mov ecx,terminalBits3D[esi*4]
|
||||
bt achievements,ecx
|
||||
jnc @F
|
||||
inc opened
|
||||
@@:
|
||||
inc esi
|
||||
cmp esi,8
|
||||
jb terminal_count_unlocked
|
||||
cmp terminalTab3D,1
|
||||
je terminal_render_achievements
|
||||
invoke DrawString8x8,24,45,OFFSET termAccount,00DDC99Dh
|
||||
invoke DrawString8x8,24,69,OFFSET termBalance,0090B19Fh
|
||||
invoke DrawNumber,24,87,bankroll,00F0E6C3h
|
||||
invoke DrawBox,24,110,272,1,00495B4Ah
|
||||
invoke DrawString8x8,24,128,OFFSET termCollection,0090B19Fh
|
||||
mov eax,ownedBacks3D
|
||||
and eax,1FFFh
|
||||
invoke TerminalCountBits3D,eax
|
||||
invoke DrawNumber,216,128,eax,00F0E6C3h
|
||||
invoke DrawString8x8,232,128,OFFSET termSlash,0090B19Fh
|
||||
invoke DrawNumber,248,128,13,00F0E6C3h
|
||||
invoke DrawString8x8,24,156,OFFSET termTrophy,0090B19Fh
|
||||
invoke DrawNumber,216,156,opened,00F0E6C3h
|
||||
invoke DrawString8x8,232,156,OFFSET termSlash,0090B19Fh
|
||||
invoke DrawNumber,248,156,8,00F0E6C3h
|
||||
jmp terminal_render_footer
|
||||
terminal_render_achievements:
|
||||
invoke DrawString8x8,24,40,OFFSET termOpened,0090B19Fh
|
||||
invoke DrawNumber,232,40,opened,00F0E6C3h
|
||||
invoke DrawString8x8,248,40,OFFSET termSlash,0090B19Fh
|
||||
invoke DrawNumber,264,40,8,00F0E6C3h
|
||||
imul esi,terminalPage3D,3
|
||||
mov rowY,62
|
||||
mov edi,3
|
||||
terminal_render_row:
|
||||
cmp esi,8
|
||||
jae terminal_render_footer
|
||||
mov ebx,0090B19Fh
|
||||
mov ecx,terminalBits3D[esi*4]
|
||||
bt achievements,ecx
|
||||
jnc @F
|
||||
mov ebx,00E7C479h
|
||||
invoke DrawString8x8,256,rowY,OFFSET termEarned,008CD8AFh
|
||||
@@:
|
||||
invoke DrawString8x8,24,rowY,terminalNames3D[esi*4],ebx
|
||||
mov eax,rowY
|
||||
add eax,12
|
||||
invoke DrawString8x8,24,eax,terminalConditions3D[esi*4],0090B19Fh
|
||||
invoke TerminalProgress3D,esi
|
||||
mov ecx,terminalGoals3D[esi*4]
|
||||
cmp eax,ecx
|
||||
jbe @F
|
||||
mov eax,ecx
|
||||
@@:
|
||||
mov edx,terminalBits3D[esi*4]
|
||||
bt achievements,edx
|
||||
jnc @F
|
||||
mov eax,ecx
|
||||
@@:
|
||||
mov progress,eax
|
||||
mov eax,rowY
|
||||
add eax,24
|
||||
invoke DrawNumber,24,eax,progress,00D2DCCEh
|
||||
mov eax,rowY
|
||||
add eax,24
|
||||
invoke DrawString8x8,112,eax,OFFSET termSlash,0090B19Fh
|
||||
mov eax,rowY
|
||||
add eax,24
|
||||
invoke DrawNumber,128,eax,terminalGoals3D[esi*4],00D2DCCEh
|
||||
mov eax,rowY
|
||||
add eax,35
|
||||
invoke DrawBox,24,eax,272,1,00314538h
|
||||
add rowY,44
|
||||
inc esi
|
||||
dec edi
|
||||
jnz terminal_render_row
|
||||
terminal_render_footer:
|
||||
mov eax,terminalPage3D
|
||||
add eax,2
|
||||
cmp terminalTab3D,0
|
||||
jne @F
|
||||
mov eax,1
|
||||
@@:
|
||||
invoke DrawNumber,140,216,eax,00E3CCA0h
|
||||
invoke DrawString8x8,156,216,OFFSET termSlash,00C1A982h
|
||||
invoke DrawNumber,172,216,4,00E3CCA0h
|
||||
ret
|
||||
RenderTerminal3D ENDP
|
||||
60
src_graphics/terminal_data.inc
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
LoadTerminalArt3D PROTO
|
||||
PositionTerminalStations3D PROTO
|
||||
FreeTerminalArt3D PROTO
|
||||
ResolveTerminalSurface3D PROTO :DWORD
|
||||
RenderTerminalCabinets3D PROTO
|
||||
TerminalCanWalk3D PROTO :DWORD,:DWORD
|
||||
DrawTerminalCabinet3D PROTO :DWORD,:DWORD,:DWORD
|
||||
DrawHallLantern3D PROTO :DWORD,:DWORD,:DWORD
|
||||
BeginTerminal3D PROTO :DWORD
|
||||
HandleTerminal3D PROTO
|
||||
RenderTerminal3D PROTO
|
||||
RefreshTerminalAchievements3D PROTO
|
||||
TerminalProgress3D PROTO :DWORD
|
||||
TerminalCountBits3D PROTO :DWORD
|
||||
TERMINAL_CASH_X equ 28*FP_1
|
||||
TERMINAL_TROPHY_X equ FP_1*59/2
|
||||
TERMINAL_WALL_Y equ 13*FP_1
|
||||
TERMINAL_CENTER_Y equ TERMINAL_WALL_Y+FP_1*11/50
|
||||
.data
|
||||
terminalWorldArt3D AnimPack3D <>
|
||||
terminalScreenArt3D AnimPack3D <>
|
||||
terminalWorldPath3D db "assets\animations\hall\terminals-v2.bin",0
|
||||
terminalScreenPath3D db "assets\animations\hall\terminal-screen-v1.bin",0
|
||||
terminalTab3D dd 0
|
||||
terminalPage3D dd 0
|
||||
terminalBits3D dd 8,9,10,11,4,12,13,14
|
||||
terminalGoals3D dd 50000,100000,500000,1000000,1,5,1,6
|
||||
terminalNames3D dd OFFSET termName0,OFFSET termName1,OFFSET termName2,OFFSET termName3
|
||||
dd OFFSET termName4,OFFSET termName5,OFFSET termName6,OFFSET termName7
|
||||
terminalConditions3D dd OFFSET termCond0,OFFSET termCond1,OFFSET termCond2,OFFSET termCond3
|
||||
dd OFFSET termCond4,OFFSET termCond5,OFFSET termCond6,OFFSET termCond7
|
||||
; Numeric CP1251 labels keep all runtime text in Russian.
|
||||
termCash db 202,192,209,209,192,0
|
||||
termTrophy db 196,206,209,210,200,198,197,205,200,223,0
|
||||
termClose db 194,219,213,206,196,0
|
||||
termAccount db 203,200,215,205,219,201,32,209,215,168,210,0
|
||||
termBalance db 212,200,216,202,200,32,205,192,32,209,215,168,210,197,0
|
||||
termCollection db 208,211,193,192,216,202,200,0
|
||||
termOpened db 206,210,202,208,219,210,206,0
|
||||
termEarned db 197,209,210,220,0
|
||||
termSlash db "/",0
|
||||
termLeft db "<",0
|
||||
termRight db ">",0
|
||||
termName0 db 205,192,32,202,206,205,197,0
|
||||
termName1 db 202,208,211,207,205,219,201,32,200,195,208,206,202,0
|
||||
termName2 db 207,206,203,204,200,203,203,200,206,205,192,0
|
||||
termName3 db 204,200,203,203,200,206,205,197,208,0
|
||||
termName4 db 207,197,208,194,192,223,32,207,206,202,211,207,202,192,0
|
||||
termName5 db 202,206,203,203,197,202,214,200,206,205,197,208,0
|
||||
termName6 db 209,194,206,201,32,215,197,203,206,194,197,202,0
|
||||
termName7 db 216,197,209,210,220,32,207,206,208,211,215,197,205,200,201,0
|
||||
termCond0 db 205,224,234,238,239,232,242,252,32,53,48,32,48,48,48,32,244,232,248,229,234,0
|
||||
termCond1 db 205,224,234,238,239,232,242,252,32,49,48,48,32,48,48,48,32,244,232,248,229,234,0
|
||||
termCond2 db 205,224,234,238,239,232,242,252,32,53,48,48,32,48,48,48,32,244,232,248,229,234,0
|
||||
termCond3 db 205,224,234,238,239,232,242,252,32,49,32,48,48,48,32,48,48,48,32,244,232,248,229,234,0
|
||||
termCond4 db 202,243,239,232,242,252,32,240,243,225,224,248,234,243,32,228,235,255,32,234,224,240,242,0
|
||||
termCond5 db 209,238,225,240,224,242,252,32,53,32,239,238,234,243,239,237,251,245,32,240,243,225,224,248,229,234,0
|
||||
termCond6 db 199,224,225,240,224,242,252,32,237,224,227,240,224,228,243,32,231,224,32,239,238,240,243,247,229,237,232,229,0
|
||||
termCond7 db 199,224,225,240,224,242,252,32,54,32,237,224,227,240,224,228,32,243,32,227,238,241,242,229,233,0
|
||||
.code
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
LoadWorldDressing3D PROC uses ebx esi
|
||||
call LoadTerminalArt3D
|
||||
invoke LoadAnimPack3D,OFFSET worldDressingArt3D,OFFSET worldDressingPath3D
|
||||
invoke ValidateCityPack3D,OFFSET worldDressingArt3D,128,128,3
|
||||
cmp worldStreetSized3D,0
|
||||
|
|
@ -87,6 +88,7 @@ street_ground_object:
|
|||
UpdateStreetGround3D ENDP
|
||||
|
||||
FreeWorldDressing3D PROC
|
||||
call FreeTerminalArt3D
|
||||
invoke FreeAnimPack3D,OFFSET worldDressingArt3D
|
||||
mov hallLightReady3D,0
|
||||
ret
|
||||
|
|
@ -112,6 +114,7 @@ ResolveDressingSurface3D ENDP
|
|||
RenderHallDressing3D PROC uses ebx esi edi
|
||||
cmp hallLevel3D,0
|
||||
jne hall_dressing_done
|
||||
call RenderTerminalCabinets3D
|
||||
lea esi,hallWallArt3D
|
||||
mov edi,WORLD_WALL_ART_COUNT
|
||||
hall_wall_painting:
|
||||
|
|
@ -121,14 +124,8 @@ hall_wall_painting:
|
|||
jnz hall_wall_painting
|
||||
xor esi,esi
|
||||
hall_wall_sconce:
|
||||
mov eax,hallSconceY3D[esi*4]
|
||||
lea ebx,[eax-FP_1*9/50]
|
||||
add eax,FP_1*9/50
|
||||
invoke DrawCityVertical3D,0,FP_1*1025/1000,ebx,eax,FP_1*27/20,FP_1*39/20,105
|
||||
mov eax,hallSconceY3D[esi*4]
|
||||
lea ebx,[eax-FP_1*9/50]
|
||||
add eax,FP_1*9/50
|
||||
invoke DrawCityVertical3D,0,FP_1*30975/1000,ebx,eax,FP_1*27/20,FP_1*39/20,105
|
||||
invoke DrawHallLantern3D,FP_1*102/100,hallSconceY3D[esi*4],1
|
||||
invoke DrawHallLantern3D,FP_1*3098/100,hallSconceY3D[esi*4],-1
|
||||
inc esi
|
||||
cmp esi,4
|
||||
jb hall_wall_sconce
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ def main():
|
|||
hall[8][x] = hall[12][x] = 1
|
||||
targets = [(12, 8), (16, 10), (20, 8), (24, 8), (6, 8), (6, 11),
|
||||
(6, 14), (10, 15), (14, 15), (18, 15), (22, 15), (28, 10),
|
||||
(26, 16), (26, 20), (12, 22), (20, 22), (16, 25), (18, 25),
|
||||
(28, 14), (29, 14), (12, 22), (20, 22), (16, 25), (18, 25),
|
||||
(16, 29), (6, 25), (27, 25)]
|
||||
assert all(point in reachable(hall, (16, 27)) for point in targets)
|
||||
# Dedicated indoor concrete, never a street facade or refuse-container texture.
|
||||
|
|
@ -46,8 +46,30 @@ def main():
|
|||
for x in range(15, 18):
|
||||
bunker[y][x] = 0
|
||||
assert (16, 12) in reachable(bunker, (16, 26))
|
||||
upper = [[125]*32 for _ in range(32)]
|
||||
for y in range(13,25):
|
||||
for x in range(3,16):
|
||||
upper[y][x] = 0
|
||||
# Enclosed, manually traversed stairwells with level landings at either end.
|
||||
up_stairs = [[102]*32 for _ in range(32)]
|
||||
for y in range(20,22):
|
||||
for x in range(2,18):
|
||||
up_stairs[y][x] = 0
|
||||
down_stairs = [[92]*32 for _ in range(32)]
|
||||
for y in range(20,23):
|
||||
for x in range(2,18):
|
||||
down_stairs[y][x] = 0
|
||||
for y in range(13,23):
|
||||
for x in range(15,18):
|
||||
down_stairs[y][x] = 0
|
||||
for grid, start, targets in ((upper,(4,21),[(7,18),(12,20),(9,23),(13,16)]),
|
||||
(up_stairs,(3,21),[(16,21)]),
|
||||
(down_stairs,(3,21),[(16,21),(16,14)])):
|
||||
assert all(point in reachable(grid,start) for point in targets)
|
||||
output = ["; Generated by make_hall_layout.py. Doorways and interactable approaches are checked.", ".data"]
|
||||
for name, grid in (("hallLayout3D", hall), ("bunkerMap3D", bunker)):
|
||||
for name, grid in (("hallLayout3D", hall), ("bunkerMap3D", bunker),
|
||||
("upperLoungeMap3D",upper), ("upperStairMap3D",up_stairs),
|
||||
("basementStairMap3D",down_stairs)):
|
||||
for index, row in enumerate(grid):
|
||||
output.append((name if index == 0 else " " * len(name)) + " db " + ",".join(map(str, row)))
|
||||
output.append(".code")
|
||||
|
|
|
|||
36
tools/generators/pack_cabaret_ensemble.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
"""Normalize the generated ensemble; native MASM owns timing and staging."""
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from PIL import Image
|
||||
from pack_guest_cast import isolate
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def main():
|
||||
sheet = Image.open(ROOT / "assets/sprites/hall/cabaret-ensemble-v1.png").convert("RGBA")
|
||||
frames = []
|
||||
for column in range(3):
|
||||
cells = [isolate(sheet.crop((column*sheet.width//3, row*sheet.height//4,
|
||||
(column+1)*sheet.width//3,(row+1)*sheet.height//4)))
|
||||
for row in range(4)]
|
||||
scale = min(142/max(cell.width for cell in cells),
|
||||
(180 if column == 1 else 242)/max(cell.height for cell in cells))
|
||||
for cell in cells:
|
||||
size = (round(cell.width*scale),round(cell.height*scale))
|
||||
canvas = Image.new("RGBA",(160,256))
|
||||
canvas.alpha_composite(cell.resize(size,Image.Resampling.LANCZOS),
|
||||
((160-size[0])//2,250-size[1]))
|
||||
frames.append(canvas)
|
||||
raw = b"".join(frame.tobytes("raw","BGRA") for frame in frames)
|
||||
(ROOT / "assets/animations/hall/cabaret-ensemble-v1.bin").write_bytes(
|
||||
struct.pack("<4s4I",b"ANM1",160,256,len(frames),len(raw))+raw)
|
||||
preview = Image.new("RGBA",(480,1024),(24,28,29,255))
|
||||
for index,frame in enumerate(frames):
|
||||
preview.alpha_composite(frame,((index//4)*160,(index%4)*256))
|
||||
preview.save(ROOT / "build/cabaret-ensemble-preview.png")
|
||||
print("Packed three performers, four poses each")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
47
tools/generators/pack_cabaret_musicians.py
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
"""Pack the approved piano/pianist and guitarist cutouts for the MASM stage."""
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from PIL import Image
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def pack(sheet, left, right, width, name):
|
||||
cells = [sheet.crop((left, row * 384, right, (row + 1) * 384))
|
||||
for row in range(4)]
|
||||
bounds = [cell.getchannel("A").point(lambda a: 255 if a >= 128 else 0).getbbox()
|
||||
for cell in cells]
|
||||
assert all(bounds), "each playing pose must contain opaque pixels"
|
||||
box = (min(b[0] for b in bounds), min(b[1] for b in bounds),
|
||||
max(b[2] for b in bounds), max(b[3] for b in bounds))
|
||||
scale = min((width - 12) / (box[2] - box[0]), 244 / (box[3] - box[1]))
|
||||
size = (round((box[2] - box[0]) * scale), round((box[3] - box[1]) * scale))
|
||||
frames = []
|
||||
for cell in cells:
|
||||
sprite = cell.crop(box).resize(size, Image.Resampling.LANCZOS)
|
||||
# The native sprite rasterizer uses a binary alpha test.
|
||||
sprite.putalpha(sprite.getchannel("A").point(lambda a: 255 if a >= 128 else 0))
|
||||
canvas = Image.new("RGBA", (width, 256))
|
||||
canvas.alpha_composite(sprite, ((width - size[0]) // 2, 250 - size[1]))
|
||||
frames.append(canvas)
|
||||
raw = b"".join(frame.tobytes("raw", "BGRA") for frame in frames)
|
||||
(ROOT / f"assets/animations/hall/{name}-v1.bin").write_bytes(
|
||||
struct.pack("<4s4I", b"ANM1", width, 256, 4, len(raw)) + raw)
|
||||
return frames
|
||||
|
||||
|
||||
def main():
|
||||
sheet = Image.open(ROOT / "assets/sprites/hall/cabaret-musicians-v1.png").convert("RGBA")
|
||||
assert sheet.size == (1024, 1536)
|
||||
piano = pack(sheet, 0, 640, 384, "cabaret-piano")
|
||||
guitar = pack(sheet, 640, 1024, 160, "cabaret-guitar")
|
||||
preview = Image.new("RGBA", (544, 1024), (24, 28, 29, 255))
|
||||
for row in range(4):
|
||||
preview.alpha_composite(piano[row], (0, row * 256))
|
||||
preview.alpha_composite(guitar[row], (384, row * 256))
|
||||
preview.save(ROOT / "build/cabaret-musicians-preview.png")
|
||||
print("Packed coherent grand piano/pianist and guitarist: four anchored poses each")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
68
tools/generators/pack_guest_cast.py
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
"""Offline conversion of generated guest art into the existing ANM1 format."""
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from PIL import Image
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SOURCE = ROOT / "assets/sprites/hall/guest-cast-v1.png"
|
||||
TARGET = ROOT / "assets/animations/hall/guest-cast-v1.bin"
|
||||
|
||||
|
||||
def isolate(cell):
|
||||
alpha = cell.getchannel("A")
|
||||
pixels = alpha.load()
|
||||
remaining = {(x,y) for y in range(cell.height) for x in range(cell.width)
|
||||
if pixels[x,y] >= 48}
|
||||
components = []
|
||||
while remaining:
|
||||
point = remaining.pop()
|
||||
component, pending = {point}, [point]
|
||||
while pending:
|
||||
x,y = pending.pop()
|
||||
for other in ((x-1,y),(x+1,y),(x,y-1),(x,y+1)):
|
||||
if other in remaining:
|
||||
remaining.remove(other)
|
||||
component.add(other)
|
||||
pending.append(other)
|
||||
components.append(component)
|
||||
largest = max(components,key=len)
|
||||
mask = Image.new("L",cell.size)
|
||||
for x,y in largest:
|
||||
mask.putpixel((x,y),pixels[x,y])
|
||||
cell.putalpha(mask)
|
||||
return cell.crop(cell.getbbox())
|
||||
|
||||
|
||||
def main():
|
||||
sheet = Image.open(SOURCE).convert("RGBA")
|
||||
frames = []
|
||||
# The approved sheet contains idle, three gait poses and a seated row.
|
||||
# Repeat the passing pose to close the four-phase walk without a jump.
|
||||
for column in range(6):
|
||||
boundaries = (0,277,535,795,1055,sheet.height)
|
||||
cells = [isolate(sheet.crop((column*sheet.width//6, boundaries[row],
|
||||
(column+1)*sheet.width//6, boundaries[row+1])))
|
||||
for row in range(5)]
|
||||
scale = min(120/max(cell.width for cell in cells[:4]),
|
||||
204/max(cell.height for cell in cells[:4]))
|
||||
for row in (0, 1, 2, 3, 2, 4):
|
||||
cell = cells[row]
|
||||
pose_scale = min(scale,124/cell.width,160/cell.height) if row == 4 else scale
|
||||
size = (max(1, round(cell.width*pose_scale)), max(1, round(cell.height*pose_scale)))
|
||||
cell = cell.resize(size, Image.Resampling.LANCZOS)
|
||||
if column == 1 and row == 4:
|
||||
cell = cell.transpose(Image.Transpose.FLIP_LEFT_RIGHT)
|
||||
frame = Image.new("RGBA", (128, 224))
|
||||
frame.alpha_composite(cell, ((128-size[0])//2, 220-size[1]))
|
||||
frames.append(frame)
|
||||
payload = b"".join(frame.tobytes("raw", "BGRA") for frame in frames)
|
||||
TARGET.write_bytes(struct.pack("<4s4I", b"ANM1",128,224,len(frames),len(payload))+payload)
|
||||
contact = Image.new("RGBA", (6*128,6*224), (24,28,29,255))
|
||||
for index, frame in enumerate(frames):
|
||||
contact.alpha_composite(frame, ((index//6)*128,(index%6)*224))
|
||||
contact.save(ROOT / "build/guest-cast-preview.png")
|
||||
print("Packed six guests, six frames each:", TARGET)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
41
tools/generators/pack_terminals.py
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
"""Offline packing of generated kiosk/lantern art for the assembly renderer."""
|
||||
from pathlib import Path
|
||||
import re
|
||||
import struct
|
||||
from PIL import Image
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
SOURCE = ROOT / "assets/sprites/terminals"
|
||||
OUT = ROOT / "assets/animations/hall"
|
||||
|
||||
|
||||
def pack(path, frames):
|
||||
w, h = frames[0].size
|
||||
raw = b"".join(image.tobytes("raw", "BGRA") for image in frames)
|
||||
path.write_bytes(struct.pack("<4s4I", b"ANM1", w, h, len(frames), len(raw)) + raw)
|
||||
|
||||
|
||||
def title(image, text, y):
|
||||
font = bytes(int(x, 16) for x in re.findall(r"\b([0-9A-Fa-f]{3})h\b",
|
||||
(ROOT / "font8x8.inc").read_text(encoding="cp1251")))
|
||||
x = (image.width - len(text) * 8) // 2
|
||||
for n, char in enumerate(text):
|
||||
for row in range(8):
|
||||
for col in range(8):
|
||||
if font[char * 8 + row] & (128 >> col):
|
||||
image.putpixel((x + n * 8 + col, y + row), (48, 30, 16, 255))
|
||||
|
||||
|
||||
def main():
|
||||
frames = [Image.open(SOURCE / name).convert("RGBA").resize((256, 512), Image.Resampling.LANCZOS)
|
||||
for name in ("cash-front-v1.png", "trophy-front-v1.png", "lantern-glass-v1.png", "body-side-rear-v2.png")]
|
||||
title(frames[0], b"\xca\xc0\xd1\xd1\xc0", 27)
|
||||
title(frames[1], b"\xc4\xce\xd1\xd2\xc8\xc6\xc5\xcd\xc8\xdf", 25)
|
||||
pack(OUT / "terminals-v2.bin", frames)
|
||||
screen = Image.open(SOURCE / "display-v1.png").convert("RGBA").resize((640, 480), Image.Resampling.LANCZOS)
|
||||
pack(OUT / "terminal-screen-v1.bin", [screen])
|
||||
print("Packed four 256x512 world materials, including a dedicated equipment enclosure, and one 640x480 screen")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
14
tools/generators/pack_upper_materials.py
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
"""Compile the generated four-tile upper-lounge material atlas."""
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from PIL import Image
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sheet = Image.open(ROOT / "assets/sprites/hall/upper-materials-v1.png").convert("RGBA")
|
||||
frames = [sheet.crop((x*sheet.width//2,y*sheet.height//2,
|
||||
(x+1)*sheet.width//2,(y+1)*sheet.height//2)).resize((256,256),Image.Resampling.LANCZOS)
|
||||
for y in range(2) for x in range(2)]
|
||||
raw = b"".join(frame.tobytes("raw","BGRA") for frame in frames)
|
||||
(ROOT / "assets/animations/hall/upper-materials-v1.bin").write_bytes(
|
||||
struct.pack("<4s4I",b"ANM1",256,256,4,len(raw))+raw)
|
||||
print("Packed steel, marble, carpet and coffer materials")
|
||||
74
tools/generators/pack_upper_motion.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
"""Register generated motion strips to rigid arch/pedestal anchors."""
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from PIL import Image, ImageDraw
|
||||
from pack_guest_cast import isolate
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def cells(name, columns, rows):
|
||||
sheet = Image.open(ROOT/f"assets/sprites/hall/{name}.png").convert("RGBA")
|
||||
return [sheet.crop((c*sheet.width//columns,r*sheet.height//rows,
|
||||
(c+1)*sheet.width//columns,(r+1)*sheet.height//rows))
|
||||
for r in range(rows) for c in range(columns)]
|
||||
|
||||
|
||||
def write(name, frames):
|
||||
w,h = frames[0].size
|
||||
raw = b"".join(frame.tobytes("raw","BGRA") for frame in frames)
|
||||
(ROOT/f"assets/animations/hall/{name}.bin").write_bytes(
|
||||
struct.pack("<4s4I",b"ANM1",w,h,len(frames),len(raw))+raw)
|
||||
|
||||
|
||||
def anchored(source, width, height):
|
||||
bounds = [cell.getchannel("A").point(lambda a: 255 if a >=128 else 0).getbbox()
|
||||
for cell in source]
|
||||
box = min(b[0] for b in bounds),min(b[1] for b in bounds),max(b[2] for b in bounds),max(b[3] for b in bounds)
|
||||
scale = min((width-12)/(box[2]-box[0]),(height-12)/(box[3]-box[1]))
|
||||
size = round((box[2]-box[0])*scale),round((box[3]-box[1])*scale)
|
||||
result = []
|
||||
for cell in source:
|
||||
sprite = cell.crop(box).resize(size,Image.Resampling.LANCZOS)
|
||||
sprite.putalpha(sprite.getchannel("A").point(lambda a: 255 if a >=128 else 0))
|
||||
frame = Image.new("RGBA",(width,height))
|
||||
frame.alpha_composite(sprite,((width-size[0])//2,height-6-size[1]))
|
||||
result.append(frame)
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
source = [isolate(cell) for cell in cells("upper-portal-v3",3,4)]
|
||||
active = []
|
||||
for cell in source:
|
||||
sprite = cell.resize((244,308),Image.Resampling.LANCZOS)
|
||||
sprite.putalpha(sprite.getchannel("A").point(lambda a: 255 if a >=128 else 0))
|
||||
frame = Image.new("RGBA",(256,320))
|
||||
frame.alpha_composite(sprite,(6,6))
|
||||
active.append(frame)
|
||||
# Keep every rigid arch pixel identical; register only the generated vortex.
|
||||
aperture = Image.new("L",(256,320))
|
||||
ImageDraw.Draw(aperture).polygon([(77,295),(77,145),(82,115),(99,94),
|
||||
(128,73),(157,94),(174,115),(179,145),(179,295)],fill=255)
|
||||
locked = [Image.composite(frame,active[0],aperture) for frame in active]
|
||||
old = (ROOT/"assets/animations/hall/upper-portal-v2.bin").read_bytes()
|
||||
inactive = Image.frombytes("RGBA",(224,320),old[20:20+224*320*4],"raw","BGRA")
|
||||
inactive = inactive.resize((256,320),Image.Resampling.LANCZOS)
|
||||
inactive = Image.composite(inactive,active[0],aperture)
|
||||
write("upper-portal-v3",[frame.resize((256,512),Image.Resampling.LANCZOS)
|
||||
for frame in [inactive,*locked]])
|
||||
levers = anchored(cells("upper-levers-v3",3,2),128,224)
|
||||
write("upper-levers-v3",levers)
|
||||
braziers = anchored(cells("upper-braziers-v3",2,2),128,256)
|
||||
for frame in braziers[1:]:
|
||||
frame.paste(braziers[0].crop((0,100,128,256)),(0,100))
|
||||
write("upper-braziers-v3",braziers)
|
||||
preview = Image.new("RGBA",(768,640),(20,22,23,255))
|
||||
for i,frame in enumerate([inactive,locked[0],locked[5],*levers[:3]]):
|
||||
preview.alpha_composite(frame,((i%3)*256,(i//3)*320))
|
||||
preview.save(ROOT/"build/upper-motion-preview.png")
|
||||
print("Packed 12 vortex phases plus inactive arch, six lever positions, four brazier phases")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
44
tools/generators/pack_upper_v2.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
"""Offline cutout packing; all scene and lever behaviour lives in MASM."""
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from PIL import Image
|
||||
from pack_guest_cast import isolate
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def save(name, cells, width, height):
|
||||
frames = []
|
||||
for cell in cells:
|
||||
cell = isolate(cell)
|
||||
scale = min((width-12)/cell.width, (height-12)/cell.height)
|
||||
size = round(cell.width*scale),round(cell.height*scale)
|
||||
sprite = cell.resize(size,Image.Resampling.LANCZOS)
|
||||
sprite.putalpha(sprite.getchannel("A").point(lambda a: 255 if a >= 128 else 0))
|
||||
frame = Image.new("RGBA",(width,height))
|
||||
frame.alpha_composite(sprite,((width-size[0])//2,height-6-size[1]))
|
||||
frames.append(frame)
|
||||
raw = b"".join(frame.tobytes("raw","BGRA") for frame in frames)
|
||||
(ROOT/f"assets/animations/hall/{name}.bin").write_bytes(
|
||||
struct.pack("<4s4I",b"ANM1",width,height,len(frames),len(raw))+raw)
|
||||
return frames
|
||||
|
||||
|
||||
def main():
|
||||
portal = Image.open(ROOT/"assets/sprites/hall/upper-portal-v2.png").convert("RGBA")
|
||||
cells = [portal.crop((c*512,r*512,(c+1)*512,(r+1)*512))
|
||||
for r in range(3) for c in range(2)]
|
||||
save("upper-portal-v2",cells[:4],224,320)
|
||||
save("upper-levers-v2",cells[4:],128,224)
|
||||
sheet = Image.open(ROOT/"assets/sprites/hall/upper-furniture-v2.png").convert("RGBA")
|
||||
tables = [sheet.crop((0,0,768,512)),sheet.crop((768,0,1536,512))]
|
||||
save("upper-games-v2",tables,320,224)
|
||||
# The approved sheet places the bartender's head above the lower row line.
|
||||
bartender = sheet.crop((180,400,650,1024))
|
||||
stool = sheet.crop((860,540,1160,1024))
|
||||
save("upper-bar-v2",[bartender,stool],160,256)
|
||||
print("Packed two distinct game tables, full bartender/stool, portal and two lever states")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
146
tools/tests/check_card_ranks.py
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
"""Compile the actual MASM card renderers and check every visible rank."""
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import re
|
||||
import struct
|
||||
import subprocess
|
||||
|
||||
from PIL import Image
|
||||
|
||||
from check_card_exit_build import Coff
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
OUT = ROOT / "build/card-ranks"
|
||||
RANKS = ("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A")
|
||||
FUNCTIONS = {
|
||||
"casino3d.inc": ("DrawPixel", "DrawBox", "DrawBoxBorder", "DrawChar8x8", "DrawString8x8"),
|
||||
"src_graphics/card_table_assets.inc": ("DrawNativeSuit3D", "DrawNativeCard3D"),
|
||||
"src_graphics/native_blackjack_party_render.inc": ("DrawBJMiniCard3D",),
|
||||
}
|
||||
|
||||
|
||||
def read(path):
|
||||
return (ROOT / path).read_text(encoding="cp1251")
|
||||
|
||||
|
||||
def procedure(source, name):
|
||||
return re.search(rf"^{name} PROC\b.*?^{name} ENDP", source, re.M | re.S).group()
|
||||
|
||||
|
||||
def prepare():
|
||||
OUT.mkdir(exist_ok=True)
|
||||
ranks = re.search(r"^cardRankNames3D db .*", read("src_graphics/card_tables_data.inc"), re.M).group()
|
||||
suits = re.search(r"^cardSuitMasks3D db .*\n(?: db .*\n){3}", read("src_graphics/card_tables_data.inc"), re.M).group()
|
||||
backs = re.search(r"^cardBackColors3D dd .*\n dd .*", read("src_graphics/platform_data.inc"), re.M).group()
|
||||
parts = [""".386
|
||||
.model flat,stdcall
|
||||
option casemap:none
|
||||
SCREEN_W equ 640
|
||||
SCREEN_H equ 480
|
||||
ExitProcess PROTO :DWORD
|
||||
CreateFileA PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
|
||||
WriteFile PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
|
||||
CloseHandle PROTO :DWORD
|
||||
DrawBox PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
|
||||
DrawAnimSprite3D PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
|
||||
AnimPack3D STRUCT
|
||||
magic dd ?
|
||||
imageWidth dd ?
|
||||
height dd ?
|
||||
frames dd ?
|
||||
bytes dd ?
|
||||
pixels dd ?
|
||||
AnimPack3D ENDS
|
||||
.data
|
||||
include font8x8.inc
|
||||
selectedBack3D dd 0
|
||||
backsPack3D AnimPack3D <>
|
||||
actionBmpHeader db 42h,4Dh
|
||||
dd 1228854
|
||||
dw 0,0
|
||||
dd 54,40,640,-480
|
||||
dw 1,32
|
||||
dd 0,1228800,0,0,0,0
|
||||
""", ranks, suits, backs]
|
||||
for suit in range(4):
|
||||
for page in range(2):
|
||||
parts.append(f'sheet{suit}_{page} db "build/card-ranks/suit-{suit}-{page}.bmp",0')
|
||||
parts.append(".data?\nscreenBuffer dd 640*480 dup(?)\n.code")
|
||||
for path, names in FUNCTIONS.items():
|
||||
source = read(path)
|
||||
parts.extend(procedure(source, name) for name in names)
|
||||
parts.append(procedure(read("tools/tests/casino_actions_test.inc"), "WriteActionSnapshot3D"))
|
||||
parts.append("""DrawAnimSprite3D PROC pack:DWORD,frame:DWORD,x:DWORD,y:DWORD,w:DWORD,h:DWORD
|
||||
invoke ExitProcess,99
|
||||
ret
|
||||
DrawAnimSprite3D ENDP
|
||||
CardRanksStart PROC
|
||||
""")
|
||||
for suit in range(4):
|
||||
for page in range(2):
|
||||
parts.append("invoke DrawBox,0,0,320,240,00213629h")
|
||||
for rank in range(page * 7, min(page * 7 + 7, 13)):
|
||||
x = 6 + (rank % 7) * 44
|
||||
for mode, width in enumerate((34, 20, 18, 20)):
|
||||
y = 4 + mode * 60
|
||||
if mode == 3:
|
||||
parts.append(f"invoke DrawBJMiniCard3D,{x},{y},{suit*13+rank},0")
|
||||
else:
|
||||
parts.append(f"invoke DrawNativeCard3D,{x},{y},{suit*13+rank},0,{width}")
|
||||
parts.append(f"invoke WriteActionSnapshot3D,OFFSET sheet{suit}_{page}\ntest eax,eax\njz failed")
|
||||
parts.append("invoke ExitProcess,0\nfailed:\ninvoke ExitProcess,1\nCardRanksStart ENDP\nend CardRanksStart")
|
||||
(OUT / "ranks.asm").write_text("\n".join(parts), encoding="ascii")
|
||||
|
||||
|
||||
def verify():
|
||||
font = bytes(int(x, 16) for x in re.findall(r"\b([0-9A-Fa-f]{3})h\b", read("font8x8.inc")))
|
||||
assert len(font) == 256 * 8
|
||||
for suit in range(4):
|
||||
ink = (191, 38, 60) if suit in (1, 2) else (25, 31, 36)
|
||||
for page in range(2):
|
||||
image = Image.open(OUT / f"suit-{suit}-{page}.bmp").convert("RGB")
|
||||
for rank in range(page * 7, min(page * 7 + 7, 13)):
|
||||
x = 6 + (rank % 7) * 44
|
||||
for mode, width in enumerate((34, 20, 18, 20)):
|
||||
y = 4 + mode * 60
|
||||
offset = 2 if mode == 3 else (1 if width == 18 else 3)
|
||||
expected = {(x + offset + letter * 8 + col, y + 3 + row)
|
||||
for letter, char in enumerate(RANKS[rank])
|
||||
for row in range(8) for col in range(8)
|
||||
if font[ord(char) * 8 + row] & (128 >> col)}
|
||||
actual = {(px, py) for px in range(x, x + width)
|
||||
for py in range(y + 3, y + 11)
|
||||
if image.getpixel((px * 2, py * 2)) == ink}
|
||||
assert actual == expected, (suit, rank, mode, actual ^ expected)
|
||||
assert all(x < px < x + width - 1 for px, _ in expected)
|
||||
image.save(OUT / f"suit-{suit}-{page}.png")
|
||||
print("PASS: 208 native card faces; 10/A and all ranks, four suits, narrow/normal/guest cards")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--parity", action="store_true")
|
||||
args = parser.parse_args()
|
||||
if args.parity:
|
||||
tested, shipped = Coff(OUT / "ranks.obj"), Coff(ROOT / "build/graphics/poker.obj")
|
||||
for name, count in (("DrawNativeCard3D", 20), ("DrawBJMiniCard3D", 16)):
|
||||
symbol = f"_{name}@{count}"
|
||||
assert tested.signature(symbol) == shipped.signature(symbol), name
|
||||
table = b"".join(rank.encode().ljust(3, b"\0") for rank in RANKS)
|
||||
assert table in tested.data and table in shipped.data
|
||||
verify()
|
||||
print("PASS: shipping card renderers and rank strings match the tested MASM")
|
||||
return
|
||||
prepare()
|
||||
masm = Path("F:/MASM32/masm32")
|
||||
subprocess.run([str(masm / "bin/ml.exe"), "/c", "/coff", "/Cp", "/nologo", "/I.",
|
||||
"/Fobuild/card-ranks/ranks.obj", "build/card-ranks/ranks.asm"], cwd=ROOT, check=True)
|
||||
subprocess.run([str(masm / "bin/link.exe"), "/nologo", "/SUBSYSTEM:CONSOLE",
|
||||
f"/LIBPATH:{masm / 'lib'}", "/OUT:build/card-ranks/ranks.exe",
|
||||
"build/card-ranks/ranks.obj", "kernel32.lib"], cwd=ROOT, check=True)
|
||||
subprocess.run([str(OUT / "ranks.exe")], cwd=ROOT, check=True)
|
||||
verify()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
229
tools/tests/check_hall_expansion.py
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
"""Compile and execute fresh MASM navigation, actors and room renderers."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import re
|
||||
import struct
|
||||
import subprocess
|
||||
from PIL import Image
|
||||
from check_card_exit_build import Coff
|
||||
from check_terminals import signature
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
OUT = ROOT / "build/hall-expansion"
|
||||
|
||||
|
||||
def read(path):
|
||||
data = (ROOT / path).read_bytes()
|
||||
try:
|
||||
return data.decode("utf8").replace("\r\r\n","\n").replace("\r\n","\n")
|
||||
except UnicodeDecodeError:
|
||||
return data.decode("cp1251").replace("\r\r\n","\n").replace("\r\n","\n")
|
||||
|
||||
|
||||
def proc(source, name):
|
||||
return re.search(rf"^{name} PROC\b.*?^{name} ENDP",source,re.M|re.S).group()
|
||||
|
||||
|
||||
def prepare():
|
||||
OUT.mkdir(exist_ok=True)
|
||||
code = read("src_graphics/hall_expansion.inc") + "\n" + read("src_graphics/cabaret.inc")
|
||||
code += "\n" + read("src_graphics/interior_portals.inc")
|
||||
for path, names in (
|
||||
("src_graphics/hall_scene.inc",["RenderCeilingAndFloor","RenderSprites","InitHall3D",
|
||||
"LoadHallAssets3D","FreeHallAssets3D","RenderHall3D","HallTarget3D",
|
||||
"CheckHallInteractions3D","TriggerHallInteraction3D","HandleStairs3D","RenderStairs3D",
|
||||
"CanWalkHall3D","UpdateHall3D","UpdatePatrons3D","RenderPatrons3D","RenderBar3D"]),
|
||||
("src_graphics/platform_code.inc",["CanWalk3D"]),
|
||||
("casino3d.inc",["HandlePlayerInput","CheckInteractions","RenderWallsRange","RenderWalls","RaycastWorkerProc","Trigger3DInteraction"]),
|
||||
("src_graphics/city_bins.inc",["ResolveCitySurface3D","PrepareCityDepth3D","PrepareCityRays3D",
|
||||
"CityRayPlane3D","DrawCityVertical3D","DrawCityHorizontal3D","DrawCityBoxSides3D"]),
|
||||
("src_graphics/npc_life.inc",["RenderNpcLife3D"]),
|
||||
("src_graphics/city_objects.inc",["DrawCityObject3D"]),
|
||||
("src_graphics/npc_contracts.inc",["NpcNameText3D"])):
|
||||
code += "\n" + "\n".join(proc(read(path),name) for name in names)
|
||||
own_funcs = set(re.findall(r"^(\w+) PROC",code,re.M))
|
||||
npc = read("src_graphics/npc_life_data.inc")
|
||||
npc = npc[npc.index("npcGuestArt3D "):npc.index("npcLifeClock3D ")]
|
||||
data = "\n".join(read(path) for path in ("src_graphics/hall_expansion_data.inc",
|
||||
"src_graphics/cabaret_data.inc","src_graphics/hall_layout.inc")) + "\n.data\n" + npc
|
||||
own_data = set(re.findall(r"^(\w+) (?:dd|db|AnimPack3D|CityProp3D)\b",data,re.M))
|
||||
coff = Coff(ROOT / "build/graphics/poker.obj")
|
||||
helper = bytearray(coff.data)
|
||||
at = struct.unpack_from("<I",helper,8)[0]
|
||||
sections = set()
|
||||
for sec in range(1,struct.unpack_from("<H",helper,2)[0]+1):
|
||||
name = bytes(helper[20+(sec-1)*40:28+(sec-1)*40]).rstrip(b"\0")
|
||||
if name in (b".data",b".bss",b".const"):
|
||||
sections.add(sec)
|
||||
externs, protos = [], {}
|
||||
types = {"casinoSprites":"SpriteObject","npcLife3D":"NpcLife3D",
|
||||
"casinoNpcs3D":"NpcMindEntity","hallSpriteTemp3D":"CityProp3D",
|
||||
"hallObjects3D":"CityProp3D","hallLights3D":"CityProp3D",
|
||||
"hallLoungeDecor3D":"CityProp3D","hallGuests3D":"CityProp3D",
|
||||
"npcSprite3D":"CityProp3D","scrStation3D":"CityProp3D"}
|
||||
all_source = "\n".join(read(str(path.relative_to(ROOT))) for path in
|
||||
[ROOT/"casino3d.inc",ROOT/"casino3d_data.inc",*sorted(path for path in
|
||||
(ROOT/"src_graphics").glob("*.inc") if path.name != "renderer.inc")])
|
||||
for name in re.findall(r"^(\w+) AnimPack3D\b",all_source,re.M):
|
||||
types[name] = "AnimPack3D"
|
||||
for name in re.findall(r"^(\w+) CityProp3D\b",all_source,re.M):
|
||||
types[name] = "CityProp3D"
|
||||
for name in re.findall(r"^(\w+) CitySpriteCrop3D\b",all_source,re.M):
|
||||
types[name] = "CitySpriteCrop3D"
|
||||
for name,kind in re.findall(r"^(\w+)\s+(db|dw|dd)\b",all_source,re.M|re.I):
|
||||
types.setdefault(name,{"db":"BYTE","dw":"WORD","dd":"DWORD"}[kind.lower()])
|
||||
for index,name in coff.symbols.items():
|
||||
pos = at+index*18
|
||||
_,sec,kind,storage,_ = struct.unpack_from("<IhHBB",helper,pos+8)
|
||||
if sec in sections and re.fullmatch(r"[A-Za-z_]\w*",name) and storage in (2,3):
|
||||
helper[pos+16] = 3 if name in own_data else 2
|
||||
if name not in own_data:
|
||||
externs.append(f"EXTERN {name}:{types.get(name,'DWORD')}")
|
||||
match = re.fullmatch(r"_(\w+)@(\d+)",name)
|
||||
if match and kind == 0x20 and sec > 0:
|
||||
name,size = match[1],int(match[2])//4
|
||||
if name in own_funcs:
|
||||
helper[pos+16] = 3
|
||||
else:
|
||||
protos[name] = size
|
||||
for name in own_funcs:
|
||||
protos[name] = proc(code,name).splitlines()[0].count(":DWORD")
|
||||
# Local helper instructions remain byte-for-byte intact.
|
||||
allowed = {at+index*18+16 for index in coff.symbols}
|
||||
assert all(i in allowed for i,(a,b) in enumerate(zip(helper,coff.data)) if a != b)
|
||||
(OUT/"helpers.obj").write_bytes(helper)
|
||||
structures = []
|
||||
for name in ("C3D_POINT","SpriteObject","AnimPack3D","CityProp3D","CitySpriteCrop3D","NpcLife3D","NpcMindEntity"):
|
||||
structures.append(re.search(rf"^{name} STRUCT\b.*?^{name} ENDS",all_source,re.M|re.S).group())
|
||||
constants = dict(re.findall(r"^(\w+)\s+equ\s+([^;\n]+)",all_source,re.M))
|
||||
already = set(re.findall(r"^(\w+) equ",data,re.M))
|
||||
used, ordered = set(), []
|
||||
def constant(name):
|
||||
if name in used or name in already or name not in constants:
|
||||
return
|
||||
used.add(name)
|
||||
for dep in re.findall(r"\b[A-Za-z_]\w*",constants[name]):
|
||||
constant(dep)
|
||||
ordered.append(f"{name} equ {constants[name]}")
|
||||
for name in re.findall(r"\b[A-Za-z_]\w*",code+data+read("tools/tests/hall_expansion_test.inc")):
|
||||
constant(name)
|
||||
# Win32 prototypes are not declarations in the helper object.
|
||||
for name,args in re.findall(r"^(\w+)\s+PROTO([^\n]*)",all_source,re.M):
|
||||
protos.setdefault(name,args.count(":DWORD"))
|
||||
protos.update({"ExitProcess":1,"WriteFile":5})
|
||||
header = ".386\n.model flat\noption casemap:none\n" + "\n".join(structures+ordered+externs)
|
||||
header += "\noption language:stdcall\n" + "\n".join(
|
||||
name+" PROTO"+(" "+",".join([":DWORD"]*size) if size else "")
|
||||
for name,size in protos.items())
|
||||
text = header+"\n"+data+"\n.code\n"+code+"\ninclude tools/tests/hall_expansion_test.inc\nend ExpansionTestStart\n"
|
||||
(OUT/"module.asm").write_text(text,encoding="cp1251")
|
||||
return own_funcs
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--parity",action="store_true")
|
||||
parser.add_argument("--prove-alpha-regression",action="store_true")
|
||||
parser.add_argument("--prove-seam-regression",action="store_true")
|
||||
args = parser.parse_args()
|
||||
if args.parity:
|
||||
native,shipping = Coff(OUT/"module.obj"),Coff(ROOT/"build/graphics/poker.obj")
|
||||
tested_source = (OUT/"module.asm").read_text(encoding="cp1251").split(
|
||||
"include tools/tests/hall_expansion_test.inc")[0]
|
||||
for name in re.findall(r"^(\w+) PROC",tested_source,re.M):
|
||||
symbol = next(key for key in native.functions if key.startswith("_"+name+"@"))
|
||||
assert signature(native,symbol) == signature(shipping,symbol),name
|
||||
print("PASS: all tested navigation, collision, hall/upper rendering, guests and cabaret procedures match the shipping assembly")
|
||||
return
|
||||
prepare()
|
||||
stem = "module"
|
||||
if args.prove_alpha_regression:
|
||||
stem = "module-alpha-regression"
|
||||
source = (OUT/"module.asm").read_text(encoding="cp1251")
|
||||
before = proc(source,"InitUpperSurfaces3D")
|
||||
after = before.replace("0FF1B5039h","001B5039h").replace("0FFECE3C8h","00ECE3C8h").replace("0FF16191Bh","0016191Bh")
|
||||
assert before != after
|
||||
(OUT/(stem+".asm")).write_text(source.replace(before,after),encoding="cp1251")
|
||||
if args.prove_seam_regression:
|
||||
stem = "module-old-seam"
|
||||
source = (OUT/"module.asm").read_text(encoding="cp1251")
|
||||
source = source.replace("include tools/tests/hall_expansion_test.inc",
|
||||
read("tools/tests/hall_expansion_test.inc"))
|
||||
before = "cmp portalRendering3D,0\n je wall_ray_start_ready"
|
||||
assert source.count(before) == 1
|
||||
source = source.replace(before,"jmp wall_ray_start_ready")
|
||||
source = source.replace("build\\hall-expansion\\","build\\hall-expansion\\old-seam-")
|
||||
source = source.replace("invoke ExpansionPortalSnapshot,OFFSET expansionUpEntry",
|
||||
"invoke ExpansionPortalSnapshot,OFFSET expansionUpEntry\n invoke ExitProcess,0")
|
||||
(OUT/(stem+".asm")).write_text(source,encoding="cp1251")
|
||||
masm = Path("F:/MASM32/masm32")
|
||||
subprocess.run([str(masm/"bin/ml.exe"),"/c","/coff","/Cp","/nologo","/I.",
|
||||
f"/Fobuild/hall-expansion/{stem}.obj",f"build/hall-expansion/{stem}.asm"],cwd=ROOT,check=True)
|
||||
sdk = sorted(Path("C:/Program Files (x86)/Windows Kits/10/Lib").glob("*/um/x86"))[-1]
|
||||
subprocess.run([str(masm/"bin/link.exe"),"/nologo","/SUBSYSTEM:CONSOLE","/ENTRY:ExpansionTestStart@0",
|
||||
f"/LIBPATH:{sdk}",f"/LIBPATH:{masm/'lib'}",f"/OUT:build/hall-expansion/{stem}.exe",
|
||||
f"build/hall-expansion/{stem}.obj","build/hall-expansion/helpers.obj",
|
||||
"kernel32.lib","user32.lib","gdi32.lib","winmm.lib","ws2_32.lib"],cwd=ROOT,check=True)
|
||||
result = subprocess.run([str(OUT/(stem+".exe"))],cwd=ROOT,timeout=180)
|
||||
if args.prove_alpha_regression:
|
||||
assert result.returncode == 1,result.returncode
|
||||
print("PASS: transparent felt/keyboard regression rejected by actual native assertions")
|
||||
return
|
||||
assert result.returncode == 0,f"native case {result.returncode}"
|
||||
entrance = Image.open(OUT / (("old-seam-" if args.prove_seam_regression else "") +
|
||||
"upper-entrance-open.bmp")).convert("RGB").crop((200,150,300,350))
|
||||
dominant = max(count for count,_ in entrance.getcolors(20000))/20000
|
||||
if args.prove_seam_regression:
|
||||
assert dominant > 0.9,"negative control must reproduce the solid rear-wall block"
|
||||
print("PASS: removing portal DDA near clipping reproduces the native solid-wall regression")
|
||||
return
|
||||
assert dominant < 0.5,"stair entrance must show textured continuation on both sides, not a solid rear-wall block"
|
||||
for path in OUT.glob("*.bmp"):
|
||||
image = Image.open(path).convert("RGB")
|
||||
assert image.size == (640,480) and len(image.getcolors(640*480)) > 100
|
||||
image.save(path.with_suffix(".png"))
|
||||
lounge = Image.open(OUT / "upper-lounge.bmp").convert("RGB")
|
||||
jazz = [Image.open(OUT / ("cabaret-jazz" + (f"-{i}" if i else "") + ".bmp")).convert("RGB")
|
||||
for i in range(4)]
|
||||
for region in ((110,150,300,290),(420,180,500,300)):
|
||||
poses = [frame.crop(region).tobytes() for frame in jazz]
|
||||
assert len(set(poses)) == 4, "piano and guitarist must both animate in the native scene"
|
||||
vortex = [Image.open(OUT/f"portal-{i:02}.bmp").crop((245,145,395,320)).tobytes()
|
||||
for i in range(12)]
|
||||
assert len(set(vortex)) == 12,"all twelve vortex phases must be visible in native frames"
|
||||
for name,expected in (("guest-cast-v1.bin",(128,224,36)),
|
||||
("cabaret-ensemble-v1.bin",(160,256,12)),
|
||||
("cabaret-piano-v1.bin",(384,256,4)),
|
||||
("cabaret-guitar-v1.bin",(160,256,4)),
|
||||
("upper-materials-v1.bin",(256,256,4)),
|
||||
("upper-games-v2.bin",(320,224,2)),
|
||||
("upper-bar-v2.bin",(160,256,2)),
|
||||
("upper-portal-v3.bin",(256,512,13)),
|
||||
("upper-levers-v3.bin",(128,224,6)),
|
||||
("upper-braziers-v3.bin",(128,256,4))):
|
||||
raw = (ROOT / "assets/animations/hall" / name).read_bytes()
|
||||
magic,w,h,count,size = struct.unpack_from("<4s4I",raw)
|
||||
assert magic == b"ANM1" and (w,h,count) == expected
|
||||
assert size == w*h*count*4 == len(raw)-20
|
||||
frames = [raw[20+i*w*h*4:20+(i+1)*w*h*4] for i in range(count)]
|
||||
if name.startswith("guest"):
|
||||
assert len(set(frames[::6])) == 6, "all six guest appearances must differ"
|
||||
if name.startswith(("upper-portal", "upper-levers", "upper-braziers", "upper-games")):
|
||||
assert len(set(frames)) == count,"new animation phases and game tables must differ"
|
||||
if name.startswith("upper-portal"):
|
||||
base = Image.frombytes("RGBA",(w,h),frames[1],"raw","BGRA")
|
||||
for frame in frames[2:]:
|
||||
sprite = Image.frombytes("RGBA",(w,h),frame,"raw","BGRA")
|
||||
for box in ((0,0,74,h),(183,0,w,h),(0,0,w,110),(0,482,w,h)):
|
||||
assert sprite.crop(box).tobytes() == base.crop(box).tobytes(),"rigid arch must not wobble"
|
||||
if name.startswith(("cabaret-piano", "cabaret-guitar")):
|
||||
assert len(set(frames)) == 4, "four genuinely different playing poses"
|
||||
for frame in frames:
|
||||
sprite = Image.frombytes("RGBA", (w,h), frame, "raw", "BGRA")
|
||||
x0,y0,x1,y1 = sprite.getbbox()
|
||||
assert 0 < x0 < x1 < w and 0 < y0 < y1 < h, "uncropped musician cutout"
|
||||
print("PASS: native reversible stairs, all eight doorway views, foreground occlusion, original door/ticket positions, two-lever lock and twelve visible portal phases")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
217
tools/tests/check_terminals.py
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
"""Native MASM kiosk/lantern regression and framebuffer checks; no game simulation."""
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import re
|
||||
import struct
|
||||
import subprocess
|
||||
from PIL import Image, ImageChops
|
||||
from check_card_exit_build import Coff
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
OUT = ROOT / "build/terminals"
|
||||
|
||||
|
||||
def read(path):
|
||||
return (ROOT / path).read_text(encoding="cp1251")
|
||||
|
||||
|
||||
def proc(text, name):
|
||||
return re.search(rf"^{name} PROC\b.*?^{name} ENDP", text, re.M | re.S).group()
|
||||
|
||||
|
||||
def signature(coff, name):
|
||||
result = []
|
||||
for code, refs, target in coff.signature(name):
|
||||
if code[:1] == b"\xe8" and len(refs) == 1 and refs[0][1][1] == 20:
|
||||
target = refs[0][1][0]
|
||||
refs = ()
|
||||
result.append((code, refs, target))
|
||||
return result
|
||||
|
||||
|
||||
def prepare():
|
||||
OUT.mkdir(exist_ok=True)
|
||||
terminal = read("src_graphics/terminal.inc")
|
||||
city = read("src_graphics/city_bins.inc")
|
||||
city_names = re.findall(r"^(\w+) PROC", city, re.M)[:7]
|
||||
code = terminal + "\n" + "\n".join(proc(city, name) for name in city_names)
|
||||
code += "\n" + proc(read("src_graphics/world_dressing.inc"), "RenderHallDressing3D")
|
||||
# The shared surface dispatcher also knows the separately packed upper lounge.
|
||||
code += "\n" + proc(read("src_graphics/hall_expansion.inc"), "ResolveUpperMaterial3D")
|
||||
trigger = proc(read("casino3d.inc"), "Trigger3DInteraction")
|
||||
code += "\n" + trigger
|
||||
if "_InitHellTower3D@0" not in Coff(ROOT / "build/actions/actions-test.obj").functions:
|
||||
# This unrelated initializer is newer than the reusable helper object.
|
||||
# The kiosk fixture must never enter it; fail immediately if it does.
|
||||
code += "\nInitHellTower3D PROC\ninvoke ExitProcess,98\nret\nInitHellTower3D ENDP\n"
|
||||
own_functions = set(re.findall(r"^(\w+) PROC", code, re.M))
|
||||
own_data = set(re.findall(r"^(\w+) (?:dd|db|AnimPack3D)", read("src_graphics/terminal_data.inc"), re.M))
|
||||
own_data.update(("cityRayX3D", "cityRayY3D"))
|
||||
own_data.update(("upperMaterialArt3D","upperFeltPixels3D","upperKeyPixels3D",
|
||||
"upperGlowPixels3D","upperPortalArt3D","upperPortal3D",
|
||||
"portalRendering3D","portalClipDepth3D"))
|
||||
coff = Coff(ROOT / "build/actions/actions-test.obj")
|
||||
data = bytearray(coff.data)
|
||||
at = struct.unpack_from("<I", data, 8)[0]
|
||||
count = struct.unpack_from("<H", data, 2)[0]
|
||||
data_sections = set()
|
||||
for section in range(1, count + 1):
|
||||
offset = 20 + (section - 1) * 40
|
||||
name = bytes(data[offset:offset+8]).rstrip(b"\0")
|
||||
if name in (b".data", b".bss", b".const"):
|
||||
data_sections.add(section)
|
||||
externs = []
|
||||
protos = []
|
||||
for index, name in coff.symbols.items():
|
||||
pos = at + index * 18
|
||||
_, section, kind, storage, _ = struct.unpack_from("<IhHBB", data, pos+8)
|
||||
if section in data_sections and re.fullmatch(r"[A-Za-z_]\w*", name) and storage in (2, 3):
|
||||
if name in own_data:
|
||||
data[pos+16] = 3
|
||||
else:
|
||||
data[pos+16] = 2
|
||||
typ = "AnimPack3D" if name in ("cityDecorPack3D","hallProps3D") else "DWORD"
|
||||
externs.append(f"EXTERN {name}:{typ}")
|
||||
match = re.fullmatch(r"_(\w+)@(\d+)", name)
|
||||
if match and kind == 0x20 and section > 0:
|
||||
function, size = match[1], int(match[2])
|
||||
if function in own_functions:
|
||||
data[pos+16] = 3
|
||||
else:
|
||||
protos.append(function + " PROTO" + (" " + ",".join([":DWORD"] * (size//4)) if size else ""))
|
||||
# Visibility-only helper copy. Never patch instructions or the shipping object.
|
||||
allowed = {at + index * 18 + 16 for index in coff.symbols}
|
||||
assert all(i in allowed for i, (a, b) in enumerate(zip(data, coff.data)) if a != b)
|
||||
(OUT / "helpers.obj").write_bytes(data)
|
||||
header = """.386
|
||||
.model flat
|
||||
option casemap:none
|
||||
FP_1 equ 65536
|
||||
FP_HALF equ 32768
|
||||
SCREEN_W equ 640
|
||||
SCREEN_H equ 480
|
||||
MODE_WALK equ 0
|
||||
MODE_ATM equ 5
|
||||
WORLD_WALL_ART_COUNT equ 6
|
||||
AnimPack3D STRUCT
|
||||
magic dd ?
|
||||
imageWidth dd ?
|
||||
height dd ?
|
||||
frames dd ?
|
||||
bytes dd ?
|
||||
pixels dd ?
|
||||
AnimPack3D ENDS
|
||||
CityProp3D STRUCT
|
||||
posX dd ?
|
||||
posY dd ?
|
||||
worldW dd ?
|
||||
worldH dd ?
|
||||
frame dd ?
|
||||
asset dd ?
|
||||
groundZ dd 0
|
||||
CityProp3D ENDS
|
||||
SpriteObject STRUCT
|
||||
posX dd ?
|
||||
posY dd ?
|
||||
baseTexId dd ?
|
||||
frameCount dd ?
|
||||
curFrame dd ?
|
||||
animTimer dd ?
|
||||
animSpeed dd ?
|
||||
interactId dd ?
|
||||
promptPtr dd ?
|
||||
distSq dd ?
|
||||
SpriteObject ENDS
|
||||
NpcWaypoint STRUCT
|
||||
posX dd ?
|
||||
posY dd ?
|
||||
neighbor0 dd ?
|
||||
neighbor1 dd ?
|
||||
neighbor2 dd ?
|
||||
neighbor3 dd ?
|
||||
zoneType dd ?
|
||||
NpcWaypoint ENDS
|
||||
"""
|
||||
for name in city_names:
|
||||
line = proc(city, name).splitlines()[0]
|
||||
size = line.count(":DWORD")
|
||||
protos.append(name + " PROTO" + (" " + ",".join([":DWORD"] * size) if size else ""))
|
||||
text = header + "\n".join(externs) + "\noption language:stdcall\nExitProcess PROTO :DWORD\n" + "\n".join(protos)
|
||||
constants = {}
|
||||
for path in [ROOT / "casino3d.inc", *sorted((ROOT / "src_graphics").glob("*_data.inc"))]:
|
||||
constants.update(re.findall(r"^(\w+)\s+equ\s+([^;\n]+)", path.read_text(encoding="cp1251"), re.M))
|
||||
for name in sorted(set(re.findall(r"\b(?:MODE_|INTERACT_)\w+", trigger)) - {"MODE_WALK", "MODE_ATM"}):
|
||||
text += f"\n{name} equ {constants[name]}"
|
||||
text += "\nResolveUpperMaterial3D PROTO :DWORD\ninclude src_graphics/terminal_data.inc\n.data\nupperMaterialArt3D AnimPack3D <>\nupperPortalArt3D AnimPack3D <>\nupperPortal3D CityProp3D <>\nportalRendering3D dd 0\n.data?\nupperFeltPixels3D dd 1024 dup(?)\nupperKeyPixels3D dd 1024 dup(?)\nupperGlowPixels3D dd 1024 dup(?)\nportalClipDepth3D dd 640 dup(?)\ncityRayX3D dd 640 dup(?)\ncityRayY3D dd 640 dup(?)\n.code\n"
|
||||
text += code + "\ninclude tools/tests/terminal_test.inc\nend TerminalTestStart\n"
|
||||
(OUT / "module.asm").write_text(text, encoding="ascii")
|
||||
|
||||
|
||||
def verify():
|
||||
for path, expected in (("terminals-v2.bin", (256, 512, 4)), ("terminal-screen-v1.bin", (640, 480, 1))):
|
||||
data = (ROOT / "assets/animations/hall" / path).read_bytes()
|
||||
magic, w, h, frames, size = struct.unpack_from("<4s4I", data)
|
||||
assert magic == b"ANM1" and (w, h, frames) == expected
|
||||
assert size == w*h*frames*4 == len(data)-20
|
||||
assert set(data[23::4]) == {255}
|
||||
data = (ROOT / "assets/animations/hall/terminals-v2.bin").read_bytes()
|
||||
old = (ROOT / "assets/animations/hall/terminals-v1.bin").read_bytes()
|
||||
assert data[20:20+3*256*512*4] == old[20:]
|
||||
body = Image.open(ROOT / "assets/sprites/terminals/body-side-rear-v2.png").convert("RGBA").resize((256,512),Image.Resampling.LANCZOS)
|
||||
assert data[20+3*256*512*4:] == body.tobytes("raw","BGRA")
|
||||
cabinet = proc(read("src_graphics/terminal.inc"),"DrawTerminalCabinet3D")
|
||||
assert "FP_1*8/5,121" in cabinet and "FP_1*8/5,86" not in cabinet
|
||||
images = {}
|
||||
for name in ("cash", "ach-0", "ach-1", "ach-2", "ach-locked", "cabinet-front", "cabinet-side", "cabinet-rear", "trophy-front", "trophy-side", "trophy-rear", "wall-placement", "lantern-front", "lantern-side"):
|
||||
image = Image.open(OUT / (name + ".bmp")).convert("RGB")
|
||||
assert image.size == (640, 480)
|
||||
assert len(image.getcolors(640*480)) > 1000, name
|
||||
image.save(OUT / (name + ".png"))
|
||||
images[name] = image
|
||||
for a, b in (("cash", "ach-0"), ("ach-0", "ach-1"), ("ach-1", "ach-2"),
|
||||
("cabinet-front", "cabinet-side"), ("lantern-front", "lantern-side")):
|
||||
assert ImageChops.difference(images[a], images[b]).getbbox(), (a, b)
|
||||
print("PASS: wall placement, real front interaction, old footprints clear, dedicated enclosure/bar independence, native save/load/input, fourteen captures")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--parity", action="store_true")
|
||||
parser.add_argument("--prove-regression", action="store_true")
|
||||
args = parser.parse_args()
|
||||
if args.parity:
|
||||
a, b = Coff(OUT / "module.obj"), Coff(ROOT / "build/graphics/poker.obj")
|
||||
names = re.findall(r"^(\w+) PROC[^\n]*", read("src_graphics/terminal.inc"), re.M)
|
||||
for name in names + ["Trigger3DInteraction", "RenderHallDressing3D", "ResolveCitySurface3D", "DrawCityVertical3D", "DrawCityHorizontal3D", "DrawCityBoxSides3D"]:
|
||||
symbol = next(key for key in a.functions if key.startswith("_" + name + "@"))
|
||||
assert signature(a,symbol) == signature(b,symbol), name
|
||||
verify()
|
||||
print("PASS: shipping terminal/lantern renderer matches fresh native tests")
|
||||
return
|
||||
prepare()
|
||||
stem = "module"
|
||||
if args.prove_regression:
|
||||
stem = "old-body"
|
||||
source = (OUT / "module.asm").read_text(encoding="ascii")
|
||||
before = "invoke DrawCityBoxSides3D,x0,x1,y0,y1,0,FP_1*8/5,121"
|
||||
assert source.count(before) == 1
|
||||
source = source.replace(before, before[:-3] + "86")
|
||||
(OUT / "old-body.asm").write_text(source, encoding="ascii")
|
||||
masm = Path("F:/MASM32/masm32")
|
||||
subprocess.run([str(masm / "bin/ml.exe"), "/c", "/coff", "/Cp", "/nologo", "/I.",
|
||||
f"/Fobuild/terminals/{stem}.obj", f"build/terminals/{stem}.asm"], cwd=ROOT, check=True)
|
||||
sdk = sorted(Path("C:/Program Files (x86)/Windows Kits/10/Lib").glob("*/um/x86"))[-1]
|
||||
subprocess.run([str(masm / "bin/link.exe"), "/nologo", "/SUBSYSTEM:CONSOLE", "/ENTRY:TerminalTestStart@0", f"/LIBPATH:{sdk}",
|
||||
f"/LIBPATH:{masm / 'lib'}", f"/OUT:build/terminals/{stem}.exe", f"build/terminals/{stem}.obj",
|
||||
"build/terminals/helpers.obj", "kernel32.lib", "user32.lib", "gdi32.lib", "winmm.lib", "ws2_32.lib"], cwd=ROOT, check=True)
|
||||
result = subprocess.run([str(OUT / (stem + ".exe"))], cwd=ROOT, check=False)
|
||||
if args.prove_regression:
|
||||
assert result.returncode == 6, result.returncode
|
||||
print("PASS: restoring the old bar material is caught by the native texture-dependency regression")
|
||||
return
|
||||
result.check_returncode()
|
||||
verify()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
598
tools/tests/hall_expansion_test.inc
Normal file
|
|
@ -0,0 +1,598 @@
|
|||
.data
|
||||
expansionStage dd 1
|
||||
expansionOcclusionPixel dd 0
|
||||
expansionBmpHeader db 42h,4Dh
|
||||
dd 1228854
|
||||
dw 0,0
|
||||
dd 54,40,640,-480
|
||||
dw 1,32
|
||||
dd 0,1228800,0,0,0,0
|
||||
expansionDoor db "build\hall-expansion\basement-door.bmp",0
|
||||
expansionStairs db "build\hall-expansion\upper-stairs.bmp",0
|
||||
expansionStairsMid db "build\hall-expansion\upper-stairs-mid.bmp",0
|
||||
expansionUpper db "build\hall-expansion\upper-lounge.bmp",0
|
||||
expansionUpperBar db "build\hall-expansion\upper-bar.bmp",0
|
||||
expansionJazz db "build\hall-expansion\cabaret-jazz.bmp",0
|
||||
expansionJazz1 db "build\hall-expansion\cabaret-jazz-1.bmp",0
|
||||
expansionJazz2 db "build\hall-expansion\cabaret-jazz-2.bmp",0
|
||||
expansionJazz3 db "build\hall-expansion\cabaret-jazz-3.bmp",0
|
||||
expansionChorus db "build\hall-expansion\cabaret-chorus.bmp",0
|
||||
expansionCast db "build\hall-expansion\guest-cast.bmp",0
|
||||
expansionUpEntry db "build\hall-expansion\upper-entrance-open.bmp",0
|
||||
expansionUpReturn db "build\hall-expansion\upper-stair-return.bmp",0
|
||||
expansionUpLanding db "build\hall-expansion\upper-landing-open.bmp",0
|
||||
expansionUpExit db "build\hall-expansion\upper-exit-open.bmp",0
|
||||
expansionDownClosed db "build\hall-expansion\old-basement-closed.bmp",0
|
||||
expansionDownReturn db "build\hall-expansion\basement-stair-return.bmp",0
|
||||
expansionDownLanding db "build\hall-expansion\basement-landing-open.bmp",0
|
||||
expansionBunkerReturn db "build\hall-expansion\bunker-return-open.bmp",0
|
||||
expansionPortalClosed db "build\hall-expansion\portal-closed.bmp",0
|
||||
expansionBuckshotTable db "build\hall-expansion\upper-buckshot-table.bmp",0
|
||||
expansionRussianTable db "build\hall-expansion\upper-russian-table.bmp",0
|
||||
expansionBarWorld db "build\hall-expansion\upper-bar-world.bmp",0
|
||||
expansionSeating db "build\hall-expansion\upper-seating.bmp",0
|
||||
expansionLeverMid db "build\hall-expansion\lever-mid-pull.bmp",0
|
||||
expansionPortal0 db "build\hall-expansion\portal-00.bmp",0
|
||||
expansionPortal1 db "build\hall-expansion\portal-01.bmp",0
|
||||
expansionPortal2 db "build\hall-expansion\portal-02.bmp",0
|
||||
expansionPortal3 db "build\hall-expansion\portal-03.bmp",0
|
||||
expansionPortal4 db "build\hall-expansion\portal-04.bmp",0
|
||||
expansionPortal5 db "build\hall-expansion\portal-05.bmp",0
|
||||
expansionPortal6 db "build\hall-expansion\portal-06.bmp",0
|
||||
expansionPortal7 db "build\hall-expansion\portal-07.bmp",0
|
||||
expansionPortal8 db "build\hall-expansion\portal-08.bmp",0
|
||||
expansionPortal9 db "build\hall-expansion\portal-09.bmp",0
|
||||
expansionPortal10 db "build\hall-expansion\portal-10.bmp",0
|
||||
expansionPortal11 db "build\hall-expansion\portal-11.bmp",0
|
||||
expansionPortalPaths dd OFFSET expansionPortal0,OFFSET expansionPortal1,OFFSET expansionPortal2,OFFSET expansionPortal3,OFFSET expansionPortal4,OFFSET expansionPortal5
|
||||
dd OFFSET expansionPortal6,OFFSET expansionPortal7,OFFSET expansionPortal8,OFFSET expansionPortal9,OFFSET expansionPortal10,OFFSET expansionPortal11
|
||||
.data?
|
||||
expansionSinglePixels dd SCREEN_SIZE dup(?)
|
||||
.code
|
||||
ExpansionSnapshot PROC uses ebx path:DWORD
|
||||
local count:DWORD
|
||||
invoke CreateFileA,path,40000000h,0,0,2,80h,0
|
||||
cmp eax,-1
|
||||
je expansion_snapshot_fail
|
||||
mov ebx,eax
|
||||
invoke WriteFile,ebx,OFFSET expansionBmpHeader,54,ADDR count,0
|
||||
invoke WriteFile,ebx,OFFSET screenBuffer,1228800,ADDR count,0
|
||||
invoke CloseHandle,ebx
|
||||
ret
|
||||
expansion_snapshot_fail:
|
||||
invoke ExitProcess,99
|
||||
ret
|
||||
ExpansionSnapshot ENDP
|
||||
|
||||
ExpansionFrame PROC
|
||||
call UpdateCamera3D
|
||||
call RenderCeilingAndFloor
|
||||
call RenderWalls
|
||||
call PrepareCityDepth3D
|
||||
call RenderSprites
|
||||
call RenderHall3D
|
||||
ret
|
||||
ExpansionFrame ENDP
|
||||
|
||||
ExpansionWalkFrame PROC
|
||||
add animationClock3D,16
|
||||
call UpdateInteriorTravel3D
|
||||
call HandlePlayerInput
|
||||
call UpdateInteriorTravel3D
|
||||
call UpdateInteriorEye3D
|
||||
ret
|
||||
ExpansionWalkFrame ENDP
|
||||
|
||||
ExpansionPortalSnapshot PROC path:DWORD
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
cmp portalPixelsWritten3D,1000
|
||||
jae @F
|
||||
invoke ExitProcess,expansionStage
|
||||
@@:
|
||||
invoke ExpansionSnapshot,path
|
||||
ret
|
||||
ExpansionPortalSnapshot ENDP
|
||||
|
||||
ExpansionTestStart PROC
|
||||
call InitPaths3D
|
||||
call LoadTextures3D
|
||||
call InitializeCasinoSprites3D
|
||||
call InitHall3D
|
||||
call LoadHallAssets3D
|
||||
cmp npcGuestArt3D.pixels,0
|
||||
je expansion_fail
|
||||
cmp cabaretArt3D.pixels,0
|
||||
je expansion_fail
|
||||
cmp cabaretPianoArt3D.pixels,0
|
||||
je expansion_fail
|
||||
cmp cabaretGuitarArt3D.pixels,0
|
||||
je expansion_fail
|
||||
cmp upperPortalArt3D.frames,13
|
||||
jne expansion_fail
|
||||
cmp upperLeverArt3D.frames,6
|
||||
jne expansion_fail
|
||||
cmp upperBrazierArt3D.frames,4
|
||||
jne expansion_fail
|
||||
cmp scrStation3D.posX,29*FP_1
|
||||
jne expansion_fail
|
||||
cmp scrStation3D.posY,FP_1*15/2
|
||||
jne expansion_fail
|
||||
cmp upperFeltPixels3D[0],0FF1B5039h
|
||||
jb expansion_fail
|
||||
cmp upperKeyPixels3D[0],80000000h
|
||||
jb expansion_fail
|
||||
mov deltaTime3D,16
|
||||
mov engineMode,MODE_WALK
|
||||
mov hallLevel3D,0
|
||||
mov expansionStage,2
|
||||
invoke CanWalk3D,FP_1*285/10,FP_1*105/10
|
||||
test eax,eax
|
||||
jnz expansion_fail
|
||||
mov interactTargetId,INTERACT_BASEMENT
|
||||
call TriggerHallInteraction3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne expansion_fail
|
||||
cmp basementDoorTarget3D,FP_1
|
||||
jne expansion_fail
|
||||
xor esi,esi
|
||||
expansion_open_door:
|
||||
add animationClock3D,16
|
||||
call UpdateInteriorTravel3D
|
||||
inc esi
|
||||
cmp esi,60
|
||||
jb expansion_open_door
|
||||
cmp basementDoorOpen3D,FP_1
|
||||
jne expansion_fail
|
||||
invoke CanWalk3D,FP_1*285/10,FP_1*105/10
|
||||
cmp eax,1
|
||||
jne expansion_fail
|
||||
mov playerX,27*FP_1
|
||||
mov playerY,FP_1*105/10
|
||||
mov playerAngle,0
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionDoor
|
||||
mov expansionStage,3
|
||||
mov keys3D[57h],1
|
||||
xor esi,esi
|
||||
expansion_down:
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne @F
|
||||
cmp playerX,FP_1*165/10
|
||||
jb @F
|
||||
mov playerAngle,384
|
||||
@@:
|
||||
call ExpansionWalkFrame
|
||||
cmp engineMode,MODE_WALK
|
||||
jne @F
|
||||
cmp hallLevel3D,1
|
||||
je expansion_at_bunker
|
||||
@@:
|
||||
inc esi
|
||||
cmp esi,800
|
||||
jb expansion_down
|
||||
jmp expansion_fail
|
||||
expansion_at_bunker:
|
||||
mov keys3D[57h],0
|
||||
cmp playerAngle,384
|
||||
jne expansion_fail
|
||||
cmp worldMap3D,OFFSET bunkerMap3D
|
||||
jne expansion_fail
|
||||
mov expansionStage,4
|
||||
mov playerAngle,128
|
||||
mov keys3D[57h],1
|
||||
xor esi,esi
|
||||
expansion_up_basement:
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne @F
|
||||
cmp playerY,21*FP_1
|
||||
jb @F
|
||||
mov playerAngle,256
|
||||
@@:
|
||||
call ExpansionWalkFrame
|
||||
cmp engineMode,MODE_WALK
|
||||
jne @F
|
||||
cmp hallLevel3D,0
|
||||
je expansion_back_hall
|
||||
@@:
|
||||
inc esi
|
||||
cmp esi,800
|
||||
jb expansion_up_basement
|
||||
jmp expansion_fail
|
||||
expansion_back_hall:
|
||||
mov keys3D[57h],0
|
||||
cmp playerX,28*FP_1-FP_1/10
|
||||
jb expansion_fail
|
||||
cmp playerX,FP_1*282/10
|
||||
ja expansion_fail
|
||||
mov expansionStage,5
|
||||
mov playerX,FP_1*294/10
|
||||
mov playerY,21*FP_1
|
||||
mov playerAngle,0
|
||||
mov keys3D[57h],1
|
||||
xor esi,esi
|
||||
expansion_upper_walk:
|
||||
call ExpansionWalkFrame
|
||||
cmp engineMode,MODE_WALK
|
||||
jne @F
|
||||
cmp hallLevel3D,2
|
||||
je expansion_at_upper
|
||||
@@:
|
||||
inc esi
|
||||
cmp esi,1100
|
||||
jb expansion_upper_walk
|
||||
jmp expansion_fail
|
||||
expansion_at_upper:
|
||||
mov keys3D[57h],0
|
||||
cmp worldMap3D,OFFSET upperLoungeMap3D
|
||||
jne expansion_fail
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionUpper
|
||||
mov expansionStage,6
|
||||
mov playerX,7*FP_1
|
||||
mov playerY,FP_1*187/10
|
||||
mov playerAngle,384
|
||||
call UpdateCamera3D
|
||||
call CheckInteractions
|
||||
cmp interactTargetId,INTERACT_BUCKSHOT
|
||||
jne expansion_fail
|
||||
mov playerX,12*FP_1
|
||||
mov playerY,FP_1*197/10
|
||||
call CheckInteractions
|
||||
cmp interactTargetId,INTERACT_RUSSIAN_ROULETTE
|
||||
jne expansion_fail
|
||||
mov playerX,9*FP_1
|
||||
mov playerY,FP_1*221/10
|
||||
mov playerX,FP_1*91/10
|
||||
mov playerAngle,128
|
||||
call UpdateCamera3D
|
||||
call CheckInteractions
|
||||
cmp interactTargetId,INTERACT_UPPER_LEFT
|
||||
jne expansion_fail
|
||||
mov playerX,13*FP_1
|
||||
mov playerY,FP_1*163/10
|
||||
mov playerAngle,384
|
||||
call UpdateCamera3D
|
||||
call CheckInteractions
|
||||
cmp interactTargetId,INTERACT_BAR
|
||||
jne expansion_fail
|
||||
call TriggerHallInteraction3D
|
||||
cmp engineMode,MODE_BAR
|
||||
jne expansion_fail
|
||||
call RenderBar3D
|
||||
cmp playerX,13*FP_1
|
||||
jne expansion_fail
|
||||
cmp hallLevel3D,2
|
||||
jne expansion_fail
|
||||
invoke ExpansionSnapshot,OFFSET expansionUpperBar
|
||||
mov expansionStage,7
|
||||
mov engineMode,MODE_WALK
|
||||
mov playerX,4*FP_1
|
||||
mov playerY,21*FP_1
|
||||
mov playerAngle,256
|
||||
mov keys3D[57h],1
|
||||
xor esi,esi
|
||||
expansion_descend_upper:
|
||||
call ExpansionWalkFrame
|
||||
cmp engineMode,MODE_WALK
|
||||
jne @F
|
||||
cmp hallLevel3D,0
|
||||
je expansion_upper_returned
|
||||
@@:
|
||||
inc esi
|
||||
cmp esi,1100
|
||||
jb expansion_descend_upper
|
||||
jmp expansion_fail
|
||||
expansion_upper_returned:
|
||||
mov keys3D[57h],0
|
||||
; No key held: neither position nor floor changes even after a long wait.
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov stairRoute3D,2
|
||||
mov worldMap3D,OFFSET upperStairMap3D
|
||||
mov floorTexture3D,85
|
||||
mov playerX,7*FP_1
|
||||
mov playerY,21*FP_1
|
||||
mov playerAngle,0
|
||||
mov deltaTime3D,5000
|
||||
call HandleStairs3D
|
||||
cmp playerX,7*FP_1
|
||||
jne expansion_fail
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne expansion_fail
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionStairs
|
||||
mov playerX,11*FP_1
|
||||
call UpdateStairHeight3D
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionStairsMid
|
||||
mov expansionStage,8
|
||||
mov deltaTime3D,16
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
mov floorTexture3D,100
|
||||
mov playerX,16*FP_1
|
||||
mov playerY,FP_1*67/10
|
||||
mov playerAngle,384
|
||||
mov cabaretTime3D,30000
|
||||
call UpdateCabaret3D
|
||||
call BeginCabaret3D
|
||||
mov animationClock3D,0
|
||||
call RenderCabaret3D
|
||||
invoke ExpansionSnapshot,OFFSET expansionJazz
|
||||
mov animationClock3D,200
|
||||
call RenderCabaret3D
|
||||
cmp cabaretPiano3D.frame,1
|
||||
jne expansion_fail
|
||||
cmp cabaretGuitar3D.frame,1
|
||||
jne expansion_fail
|
||||
invoke ExpansionSnapshot,OFFSET expansionJazz1
|
||||
mov animationClock3D,400
|
||||
call RenderCabaret3D
|
||||
invoke ExpansionSnapshot,OFFSET expansionJazz2
|
||||
mov animationClock3D,600
|
||||
call RenderCabaret3D
|
||||
cmp cabaretPiano3D.frame,3
|
||||
jne expansion_fail
|
||||
cmp cabaretGuitar3D.frame,3
|
||||
jne expansion_fail
|
||||
invoke ExpansionSnapshot,OFFSET expansionJazz3
|
||||
mov cabaretTime3D,0
|
||||
call UpdateCabaret3D
|
||||
call RenderCabaret3D
|
||||
invoke ExpansionSnapshot,OFFSET expansionChorus
|
||||
mov pressedKeys3D[45h],1
|
||||
call HandleCabaret3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne expansion_fail
|
||||
mov bankroll,100
|
||||
call BeginCabaret3D
|
||||
mov mouseClick3D,1
|
||||
mov mouseX3D,40
|
||||
mov mouseY3D,220
|
||||
call HandleCabaret3D
|
||||
cmp bankroll,0
|
||||
jne expansion_fail
|
||||
mov mouseClick3D,1
|
||||
call HandleCabaret3D
|
||||
cmp bankroll,0
|
||||
jne expansion_fail
|
||||
mov mouseClick3D,1
|
||||
mov mouseX3D,280
|
||||
call HandleCabaret3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne expansion_fail
|
||||
mov expansionStage,9
|
||||
; Each visitor maps to their own frame block, not the first man's idle pose.
|
||||
mov playerX,16*FP_1
|
||||
mov playerY,20*FP_1
|
||||
mov playerAngle,384
|
||||
xor esi,esi
|
||||
expansion_cast_positions:
|
||||
imul ebx,esi,SIZEOF NpcMindEntity
|
||||
imul eax,esi,FP_1
|
||||
add eax,FP_1*27/2
|
||||
mov casinoNpcs3D[ebx].posX,eax
|
||||
mov casinoNpcs3D[ebx].posY,16*FP_1
|
||||
imul ebx,esi,SIZEOF NpcLife3D
|
||||
mov npcLife3D[ebx].identity,esi
|
||||
mov npcLife3D[ebx].state,NPC_STAND
|
||||
inc esi
|
||||
cmp esi,6
|
||||
jb expansion_cast_positions
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionCast
|
||||
mov hallLevel3D,2
|
||||
mov worldMap3D,OFFSET upperLoungeMap3D
|
||||
mov floorTexture3D,124
|
||||
mov playerX,7*FP_1
|
||||
mov playerY,FP_1*194/10
|
||||
mov playerAngle,384
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionBuckshotTable
|
||||
mov playerX,12*FP_1
|
||||
mov playerY,FP_1*204/10
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionRussianTable
|
||||
mov playerX,FP_1*125/10
|
||||
mov playerY,FP_1*168/10
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionBarWorld
|
||||
mov playerX,FP_1*56/10
|
||||
mov playerY,FP_1*205/10
|
||||
mov playerAngle,128
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionSeating
|
||||
mov expansionStage,20
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
mov floorTexture3D,100
|
||||
mov playerX,FP_1*285/10
|
||||
mov playerY,21*FP_1
|
||||
mov playerAngle,0
|
||||
invoke ExpansionPortalSnapshot,OFFSET expansionUpEntry
|
||||
invoke DrawCityVertical3D,0,29*FP_1,FP_1*208/10,FP_1*212/10,0,FP_1*22/10,122
|
||||
mov eax,screenBuffer[(240*SCREEN_W+320)*4]
|
||||
mov expansionOcclusionPixel,eax
|
||||
call RenderInteriorViews3D
|
||||
mov eax,screenBuffer[(240*SCREEN_W+320)*4]
|
||||
cmp eax,expansionOcclusionPixel
|
||||
jne expansion_fail
|
||||
mov expansionStage,21
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov stairRoute3D,2
|
||||
mov worldMap3D,OFFSET upperStairMap3D
|
||||
mov floorTexture3D,85
|
||||
mov playerX,FP_1*48/10
|
||||
mov playerAngle,256
|
||||
invoke ExpansionPortalSnapshot,OFFSET expansionUpReturn
|
||||
mov expansionStage,22
|
||||
mov playerX,15*FP_1
|
||||
mov playerAngle,0
|
||||
invoke ExpansionPortalSnapshot,OFFSET expansionUpLanding
|
||||
mov expansionStage,23
|
||||
mov engineMode,MODE_WALK
|
||||
mov hallLevel3D,2
|
||||
mov worldMap3D,OFFSET upperLoungeMap3D
|
||||
mov floorTexture3D,124
|
||||
mov playerX,FP_1*45/10
|
||||
mov playerAngle,256
|
||||
invoke ExpansionPortalSnapshot,OFFSET expansionUpExit
|
||||
mov expansionStage,24
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
mov floorTexture3D,100
|
||||
mov playerX,FP_1*267/10
|
||||
mov playerY,FP_1*105/10
|
||||
mov playerAngle,0
|
||||
mov basementDoorOpen3D,0
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
cmp portalPixelsWritten3D,0
|
||||
jne expansion_fail
|
||||
invoke ExpansionSnapshot,OFFSET expansionDownClosed
|
||||
mov basementDoorOpen3D,FP_1
|
||||
invoke ExpansionPortalSnapshot,OFFSET expansionDoor
|
||||
mov expansionStage,25
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov stairRoute3D,1
|
||||
mov worldMap3D,OFFSET basementStairMap3D
|
||||
mov floorTexture3D,94
|
||||
mov playerX,FP_1*45/10
|
||||
mov playerY,21*FP_1
|
||||
mov playerAngle,256
|
||||
invoke ExpansionPortalSnapshot,OFFSET expansionDownReturn
|
||||
mov expansionStage,26
|
||||
mov playerX,FP_1*165/10
|
||||
mov playerY,FP_1*155/10
|
||||
mov playerAngle,384
|
||||
invoke ExpansionPortalSnapshot,OFFSET expansionDownLanding
|
||||
mov expansionStage,27
|
||||
mov engineMode,MODE_WALK
|
||||
mov hallLevel3D,1
|
||||
mov worldMap3D,OFFSET bunkerMap3D
|
||||
mov playerY,FP_1*265/10
|
||||
mov playerAngle,128
|
||||
invoke ExpansionPortalSnapshot,OFFSET expansionBunkerReturn
|
||||
mov expansionStage,28
|
||||
mov hallLevel3D,2
|
||||
mov worldMap3D,OFFSET upperLoungeMap3D
|
||||
mov floorTexture3D,124
|
||||
mov playerX,FP_1*105/10
|
||||
mov playerY,FP_1*193/10
|
||||
mov upperLeverMask3D,0
|
||||
mov upperPortalReady3D,0
|
||||
mov upperLeverTime3D,0
|
||||
mov upperLeverTime3D[4],0
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionPortalClosed
|
||||
mov interactTargetId,INTERACT_UPPER_LEFT
|
||||
call TriggerHallInteraction3D
|
||||
mov esi,40
|
||||
expansion_first_lever:
|
||||
add animationClock3D,16
|
||||
call UpdateUpperEffects3D
|
||||
dec esi
|
||||
jnz expansion_first_lever
|
||||
cmp upperLeverTime3D,600
|
||||
jne expansion_fail
|
||||
cmp upperLeverTime3D[4],0
|
||||
jne expansion_fail
|
||||
cmp upperPortalReady3D,0
|
||||
jne expansion_fail
|
||||
mov playerY,FP_1*244/10
|
||||
call TryWalkUpperPortal3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne expansion_fail
|
||||
mov playerY,FP_1*193/10
|
||||
mov interactTargetId,INTERACT_UPPER_RIGHT
|
||||
call TriggerHallInteraction3D
|
||||
mov esi,20
|
||||
expansion_second_lever_half:
|
||||
add animationClock3D,16
|
||||
call UpdateUpperEffects3D
|
||||
dec esi
|
||||
jnz expansion_second_lever_half
|
||||
cmp upperPortalReady3D,0
|
||||
jne expansion_fail
|
||||
call ExpansionFrame
|
||||
cmp upperLever3D.frame,2
|
||||
jne expansion_fail
|
||||
invoke ExpansionSnapshot,OFFSET expansionLeverMid
|
||||
mov esi,20
|
||||
expansion_second_lever_end:
|
||||
add animationClock3D,16
|
||||
call UpdateUpperEffects3D
|
||||
dec esi
|
||||
jnz expansion_second_lever_end
|
||||
cmp upperPortalReady3D,1
|
||||
jne expansion_fail
|
||||
xor esi,esi
|
||||
expansion_portal_motion:
|
||||
imul eax,esi,80
|
||||
mov animationClock3D,eax
|
||||
call ExpansionFrame
|
||||
mov eax,esi
|
||||
inc eax
|
||||
cmp upperPortal3D.frame,eax
|
||||
jne expansion_fail
|
||||
invoke ExpansionSnapshot,expansionPortalPaths[esi*4]
|
||||
inc esi
|
||||
cmp esi,12
|
||||
jb expansion_portal_motion
|
||||
mov playerY,FP_1*244/10
|
||||
call TryWalkUpperPortal3D
|
||||
cmp engineMode,MODE_HELL_TOWER
|
||||
jne expansion_fail
|
||||
call TowerExitToHall
|
||||
call TryWalkUpperPortal3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne expansion_fail
|
||||
mov expansionStage,30
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
mov floorTexture3D,100
|
||||
mov playerX,FP_1*285/10
|
||||
mov playerY,21*FP_1
|
||||
mov playerAngle,0
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
cld
|
||||
lea esi,screenBuffer
|
||||
lea edi,expansionSinglePixels
|
||||
mov ecx,SCREEN_SIZE
|
||||
rep movsd
|
||||
invoke CreateEventA,0,0,0,0
|
||||
mov hRaycastStartEvent,eax
|
||||
invoke CreateEventA,0,0,0,0
|
||||
mov hRaycastDoneEvent,eax
|
||||
mov bRaycastQuit,0
|
||||
invoke CreateThread,0,65536,OFFSET RaycastWorkerProc,0,0,OFFSET raycastThreadId
|
||||
mov hRaycastThread,eax
|
||||
test eax,eax
|
||||
jz expansion_fail
|
||||
call ExpansionFrame
|
||||
lea esi,screenBuffer
|
||||
lea edi,expansionSinglePixels
|
||||
mov ecx,SCREEN_SIZE
|
||||
repe cmpsd
|
||||
jne expansion_fail
|
||||
mov bRaycastQuit,1
|
||||
invoke SetEvent,hRaycastStartEvent
|
||||
invoke WaitForSingleObject,hRaycastThread,2000
|
||||
test eax,eax
|
||||
jnz expansion_fail
|
||||
invoke CloseHandle,hRaycastThread
|
||||
invoke CloseHandle,hRaycastStartEvent
|
||||
invoke CloseHandle,hRaycastDoneEvent
|
||||
mov hRaycastThread,0
|
||||
mov hRaycastStartEvent,0
|
||||
mov hRaycastDoneEvent,0
|
||||
invoke ExitProcess,0
|
||||
expansion_fail:
|
||||
invoke ExitProcess,expansionStage
|
||||
ret
|
||||
ExpansionTestStart ENDP
|
||||
|
|
@ -93,6 +93,15 @@ include tools\tests\wheel_scene_test.inc
|
|||
|
||||
include tools\tests\npc_life_test.inc
|
||||
|
||||
HallTestWalkFrame3D PROC
|
||||
add animationClock3D,16
|
||||
call UpdateInteriorTravel3D
|
||||
call HandlePlayerInput
|
||||
call UpdateInteriorTravel3D
|
||||
call UpdateInteriorEye3D
|
||||
ret
|
||||
HallTestWalkFrame3D ENDP
|
||||
|
||||
RunHallTest3D PROC uses ebx esi edi
|
||||
local oldBank:DWORD,oldSeed:DWORD,endX:DWORD,endV:DWORD
|
||||
mov eax, bankroll
|
||||
|
|
@ -185,32 +194,83 @@ hall_test_patron_route:
|
|||
test eax, eax
|
||||
jnz hall_test_fail
|
||||
mov interactTargetId, INTERACT_BASEMENT
|
||||
mov playerX, 27*FP_1
|
||||
mov playerY, FP_1*105/10
|
||||
mov playerAngle,0
|
||||
mov deltaTime3D,16
|
||||
call ClearKeys3D
|
||||
call TriggerHallInteraction3D
|
||||
mov stairsTime3D, 800
|
||||
cmp engineMode,MODE_WALK
|
||||
jne hall_test_fail
|
||||
mov esi,60
|
||||
hall_test_open_door:
|
||||
add animationClock3D,16
|
||||
call UpdateInteriorTravel3D
|
||||
dec esi
|
||||
jnz hall_test_open_door
|
||||
mov keys3D[57h],1
|
||||
mov esi,800
|
||||
hall_test_enter_stairs:
|
||||
call HallTestWalkFrame3D
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je hall_test_stair_frame
|
||||
dec esi
|
||||
jnz hall_test_enter_stairs
|
||||
jmp hall_test_fail
|
||||
hall_test_stair_frame:
|
||||
call RenderStairs3D
|
||||
invoke WriteActionSnapshot3D,OFFSET hallTestStair3D
|
||||
mov deltaTime3D, 1600
|
||||
call HandleStairs3D
|
||||
mov esi,800
|
||||
hall_test_descend_basement:
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne @F
|
||||
cmp playerX,FP_1*165/10
|
||||
jb @F
|
||||
mov playerAngle,384
|
||||
@@:
|
||||
call HallTestWalkFrame3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne @F
|
||||
cmp hallLevel3D, 1
|
||||
jne hall_test_fail
|
||||
je hall_test_basement_arrived
|
||||
@@:
|
||||
dec esi
|
||||
jnz hall_test_descend_basement
|
||||
jmp hall_test_fail
|
||||
hall_test_basement_arrived:
|
||||
mov keys3D[57h],0
|
||||
cmp worldMap3D, OFFSET bunkerMap3D
|
||||
jne hall_test_fail
|
||||
invoke CanWalk3D,16*FP_1+FP_HALF,16*FP_1
|
||||
cmp eax, 1
|
||||
jne hall_test_fail
|
||||
mov playerY, 17*FP_1
|
||||
call UpdateCamera3D
|
||||
call RenderCeilingAndFloor
|
||||
call RenderWalls
|
||||
call PrepareCityDepth3D
|
||||
call RenderHall3D
|
||||
invoke WriteActionSnapshot3D,OFFSET hallTestBunker3D
|
||||
mov interactTargetId, INTERACT_UPSTAIRS
|
||||
call TriggerHallInteraction3D
|
||||
mov deltaTime3D, 1600
|
||||
call HandleStairs3D
|
||||
cmp hallLevel3D, 0
|
||||
jne hall_test_fail
|
||||
mov playerAngle,128
|
||||
mov keys3D[57h],1
|
||||
mov esi,800
|
||||
hall_test_return_basement:
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne @F
|
||||
cmp playerY,21*FP_1
|
||||
jb @F
|
||||
mov playerAngle,256
|
||||
@@:
|
||||
call HallTestWalkFrame3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne @F
|
||||
cmp hallLevel3D,0
|
||||
je hall_test_back_in_hall
|
||||
@@:
|
||||
dec esi
|
||||
jnz hall_test_return_basement
|
||||
jmp hall_test_fail
|
||||
hall_test_back_in_hall:
|
||||
mov keys3D[57h],0
|
||||
cmp worldMap3D, OFFSET casinoMap
|
||||
jne hall_test_fail
|
||||
mov hallTestStage3D, 72
|
||||
|
|
|
|||
|
|
@ -20,6 +20,9 @@ LoadNativeDiceArt3D PROTO
|
|||
FreeNativeDiceArt3D PROTO
|
||||
LoadScratchArt3D PROTO
|
||||
FreeScratchArt3D PROTO
|
||||
LoadTerminalArt3D PROTO
|
||||
FreeTerminalArt3D PROTO
|
||||
ResolveTerminalSurface3D PROTO :DWORD
|
||||
.data
|
||||
streetPack dd 6 dup(0)
|
||||
streetPath db "assets\animations\hall\street-life-v4.bin",0
|
||||
|
|
@ -41,6 +44,18 @@ wakeKneesProbePath db "assets\animations\arrival\wake-knees-v1.bin",0
|
|||
wakeSleeveProbePath db "assets\animations\arrival\wake-sleeve-v1.bin",0
|
||||
alleyMaterialsProbePath db "assets\animations\arrival\alley-materials-v1.bin",0
|
||||
worldDressingProbePath db "assets\animations\hall\world-dressing-v6.bin",0
|
||||
terminalWorldProbePath db "assets\animations\hall\terminals-v2.bin",0
|
||||
terminalScreenProbePath db "assets\animations\hall\terminal-screen-v1.bin",0
|
||||
guestCastProbePath db "assets\animations\hall\guest-cast-v1.bin",0
|
||||
cabaretProbePath db "assets\animations\hall\cabaret-ensemble-v1.bin",0
|
||||
cabaretPianoProbePath db "assets\animations\hall\cabaret-piano-v1.bin",0
|
||||
cabaretGuitarProbePath db "assets\animations\hall\cabaret-guitar-v1.bin",0
|
||||
upperMaterialsProbePath db "assets\animations\hall\upper-materials-v1.bin",0
|
||||
upperGamesProbePath db "assets\animations\hall\upper-games-v2.bin",0
|
||||
upperBarProbePath db "assets\animations\hall\upper-bar-v2.bin",0
|
||||
upperPortalProbePath db "assets\animations\hall\upper-portal-v3.bin",0
|
||||
upperLeversProbePath db "assets\animations\hall\upper-levers-v3.bin",0
|
||||
upperBraziersProbePath db "assets\animations\hall\upper-braziers-v3.bin",0
|
||||
furniturePaths dd OFFSET cardFurniturePath,320,208,1
|
||||
dd OFFSET wheelFurniturePath,288,268,1
|
||||
dd OFFSET backFurniturePath,304,236,1
|
||||
|
|
@ -58,6 +73,19 @@ furniturePaths dd OFFSET cardFurniturePath,320,208,1
|
|||
dd OFFSET wakeSleeveProbePath,256,384,1
|
||||
dd OFFSET alleyMaterialsProbePath,128,128,4
|
||||
dd OFFSET worldDressingProbePath,128,128,3
|
||||
dd OFFSET terminalWorldProbePath,256,512,4
|
||||
dd OFFSET terminalScreenProbePath,640,480,1
|
||||
dd OFFSET guestCastProbePath,128,224,36
|
||||
dd OFFSET cabaretProbePath,160,256,12
|
||||
dd OFFSET cabaretPianoProbePath,384,256,4
|
||||
dd OFFSET cabaretGuitarProbePath,160,256,4
|
||||
dd OFFSET upperMaterialsProbePath,256,256,4
|
||||
dd OFFSET upperGamesProbePath,320,224,2
|
||||
dd OFFSET upperBarProbePath,160,256,2
|
||||
dd OFFSET upperPortalProbePath,256,512,13
|
||||
dd OFFSET upperLeversProbePath,128,224,6
|
||||
dd OFFSET upperBraziersProbePath,128,256,4
|
||||
furniturePathCount equ ($-furniturePaths)/16
|
||||
.code
|
||||
RunStreetAssetProbe PROC
|
||||
call InitPaths3D
|
||||
|
|
@ -100,8 +128,20 @@ RunStreetAssetProbe PROC
|
|||
cmp eax,1
|
||||
jne probe_failed
|
||||
call FreeScratchArt3D
|
||||
call LoadTerminalArt3D
|
||||
invoke ResolveTerminalSurface3D,118
|
||||
test eax,eax
|
||||
jz probe_failed
|
||||
cmp ecx,256
|
||||
jne probe_failed
|
||||
cmp edx,512
|
||||
jne probe_failed
|
||||
call FreeTerminalArt3D
|
||||
invoke ResolveTerminalSurface3D,118
|
||||
test eax,eax
|
||||
jnz probe_failed
|
||||
mov esi,OFFSET furniturePaths
|
||||
mov edi,17
|
||||
mov edi,furniturePathCount
|
||||
probe_furniture:
|
||||
invoke LoadAnimPack3D,OFFSET streetPack,[esi]
|
||||
cmp eax,1
|
||||
|
|
|
|||
314
tools/tests/terminal_test.inc
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
.data
|
||||
termTestJournal db "build\terminals\isolated-profile",0
|
||||
termTestDir db "build\terminals",0
|
||||
termTestSaveName db "test-profile.sav",0
|
||||
termTestCash db "build\terminals\cash.bmp",0
|
||||
termTestAch0 db "build\terminals\ach-0.bmp",0
|
||||
termTestAch1 db "build\terminals\ach-1.bmp",0
|
||||
termTestAch2 db "build\terminals\ach-2.bmp",0
|
||||
termTestLocked db "build\terminals\ach-locked.bmp",0
|
||||
termTestFront db "build\terminals\cabinet-front.bmp",0
|
||||
termTestSide db "build\terminals\cabinet-side.bmp",0
|
||||
termTestRear db "build\terminals\cabinet-rear.bmp",0
|
||||
termTestTrophyFront db "build\terminals\trophy-front.bmp",0
|
||||
termTestTrophySide db "build\terminals\trophy-side.bmp",0
|
||||
termTestTrophyRear db "build\terminals\trophy-rear.bmp",0
|
||||
termTestLamp db "build\terminals\lantern-front.bmp",0
|
||||
termTestLampSide db "build\terminals\lantern-side.bmp",0
|
||||
termTestWall db "build\terminals\wall-placement.bmp",0
|
||||
termTestStage dd 1
|
||||
.data?
|
||||
termTestOriginalBar dd 65536 dup(?)
|
||||
termTestReferenceFrame dd 307200 dup(?)
|
||||
.code
|
||||
TerminalTestIsolatedCabinet PROC
|
||||
invoke DrawBox,0,0,320,240,00121C20h
|
||||
lea edi,zBuffer
|
||||
mov ecx,640
|
||||
mov eax,100*FP_1
|
||||
rep stosd
|
||||
call UpdateCamera3D
|
||||
call PrepareCityDepth3D
|
||||
call RenderTerminalCabinets3D
|
||||
ret
|
||||
TerminalTestIsolatedCabinet ENDP
|
||||
|
||||
; Poison the former bar material. A terminal must render identically without it.
|
||||
TerminalTestNoBarTexture PROC uses ebx esi edi
|
||||
local texture:DWORD,unchanged:DWORD
|
||||
mov eax,DWORD PTR hallRoomHD3D[20]
|
||||
test eax,eax
|
||||
jz terminal_bar_test_failed
|
||||
add eax,6*256*256*4
|
||||
mov texture,eax
|
||||
call TerminalTestIsolatedCabinet
|
||||
lea esi,screenBuffer
|
||||
lea edi,termTestReferenceFrame
|
||||
mov ecx,307200
|
||||
rep movsd
|
||||
mov esi,texture
|
||||
lea edi,termTestOriginalBar
|
||||
mov ecx,65536
|
||||
rep movsd
|
||||
mov edi,texture
|
||||
mov eax,00F000FFh
|
||||
mov ecx,65536
|
||||
rep stosd
|
||||
call TerminalTestIsolatedCabinet
|
||||
mov unchanged,1
|
||||
lea esi,screenBuffer
|
||||
lea edi,termTestReferenceFrame
|
||||
mov ecx,307200
|
||||
repe cmpsd
|
||||
je @F
|
||||
mov unchanged,0
|
||||
@@:
|
||||
lea esi,termTestOriginalBar
|
||||
mov edi,texture
|
||||
mov ecx,65536
|
||||
rep movsd
|
||||
mov eax,unchanged
|
||||
ret
|
||||
terminal_bar_test_failed:
|
||||
xor eax,eax
|
||||
ret
|
||||
TerminalTestNoBarTexture ENDP
|
||||
.code
|
||||
TerminalTestWorldFrame PROC
|
||||
call UpdateCamera3D
|
||||
call RenderCeilingAndFloor
|
||||
call RenderWalls
|
||||
call PrepareCityDepth3D
|
||||
call RenderHallDressing3D
|
||||
ret
|
||||
TerminalTestWorldFrame ENDP
|
||||
|
||||
TerminalTestStart PROC
|
||||
call InitPaths3D
|
||||
mov npcJournalBasePtr3D,OFFSET termTestJournal
|
||||
call LoadTextures3D
|
||||
call InitializeCasinoSprites3D
|
||||
call InitHall3D
|
||||
call PositionTerminalStations3D
|
||||
call LoadHallAssets3D
|
||||
call LoadTerminalArt3D
|
||||
cmp terminalWorldArt3D.pixels,0
|
||||
je terminal_test_fail
|
||||
cmp terminalScreenArt3D.pixels,0
|
||||
je terminal_test_fail
|
||||
mov termTestStage,2
|
||||
mov achievements,80h
|
||||
mov bankroll,49999
|
||||
mov ownedBacks3D,1
|
||||
mov npcClaimed3D,0
|
||||
call RefreshTerminalAchievements3D
|
||||
cmp achievements,80h
|
||||
jne terminal_test_fail
|
||||
mov bankroll,50000
|
||||
call RefreshTerminalAchievements3D
|
||||
cmp achievements,180h
|
||||
jne terminal_test_fail
|
||||
mov bankroll,1
|
||||
call RefreshTerminalAchievements3D
|
||||
cmp achievements,180h
|
||||
jne terminal_test_fail
|
||||
mov termTestStage,3
|
||||
mov bankroll,1000000
|
||||
mov ownedBacks3D,3Fh
|
||||
mov npcClaimed3D,3Fh
|
||||
call RefreshTerminalAchievements3D
|
||||
cmp achievements,7F90h
|
||||
jne terminal_test_fail
|
||||
; Round-trip through the real profile serializer, isolated from user saves.
|
||||
mov nativeProfileActive3D,1
|
||||
mov nativeProfilesDirPtr3D,OFFSET termTestDir
|
||||
invoke CopyCString,OFFSET saveFileName,OFFSET termTestSaveName
|
||||
call SaveGraphicProfile3D
|
||||
mov achievements,0
|
||||
mov bankroll,1
|
||||
call LoadSave
|
||||
cmp achievements,7F90h
|
||||
jne terminal_test_fail
|
||||
cmp bankroll,1000000
|
||||
jne terminal_test_fail
|
||||
mov nativeProfileActive3D,0
|
||||
call RefreshTerminalAchievements3D
|
||||
cmp achievements,7F90h
|
||||
jne terminal_test_fail
|
||||
mov termTestStage,4
|
||||
invoke TerminalCanWalk3D,TERMINAL_CASH_X,TERMINAL_CENTER_Y
|
||||
test eax,eax
|
||||
jnz terminal_test_fail
|
||||
invoke TerminalCanWalk3D,TERMINAL_TROPHY_X,TERMINAL_CENTER_Y
|
||||
test eax,eax
|
||||
jnz terminal_test_fail
|
||||
invoke TerminalCanWalk3D,TERMINAL_CASH_X,FP_1*71/5
|
||||
cmp eax,1
|
||||
jne terminal_test_fail
|
||||
invoke TerminalCanWalk3D,26*FP_1,16*FP_1
|
||||
cmp eax,1
|
||||
jne terminal_test_fail
|
||||
invoke TerminalCanWalk3D,26*FP_1,20*FP_1
|
||||
cmp eax,1
|
||||
jne terminal_test_fail
|
||||
cmp BYTE PTR casinoMap[12*32+28],0
|
||||
je terminal_test_fail
|
||||
cmp BYTE PTR casinoMap[13*32+28],0
|
||||
jne terminal_test_fail
|
||||
cmp casinoSprites[15*SIZEOF SpriteObject].SpriteObject.posX,TERMINAL_CASH_X
|
||||
jne terminal_test_fail
|
||||
cmp casinoSprites[16*SIZEOF SpriteObject].SpriteObject.posY,TERMINAL_CENTER_Y
|
||||
jne terminal_test_fail
|
||||
cmp casinoWaypoints3D[14*SIZEOF NpcWaypoint].NpcWaypoint.posX,TERMINAL_CASH_X
|
||||
jne terminal_test_fail
|
||||
mov eax,casinoWaypoints3D[14*SIZEOF NpcWaypoint].NpcWaypoint.posX
|
||||
mov edx,casinoWaypoints3D[14*SIZEOF NpcWaypoint].NpcWaypoint.posY
|
||||
invoke TerminalCanWalk3D,eax,edx
|
||||
cmp eax,1
|
||||
jne terminal_test_fail
|
||||
invoke TerminalCanWalk3D,26*FP_1,18*FP_1
|
||||
cmp eax,1
|
||||
jne terminal_test_fail
|
||||
mov termTestStage,5
|
||||
mov engineMode,MODE_WALK
|
||||
mov playerX,TERMINAL_CASH_X
|
||||
mov playerY,FP_1*71/5
|
||||
mov playerAngle,384
|
||||
call UpdateCamera3D
|
||||
call CheckInteractions
|
||||
cmp interactTargetId,INTERACT_ATM
|
||||
jne terminal_test_fail
|
||||
call Trigger3DInteraction
|
||||
cmp engineMode,MODE_ATM
|
||||
jne terminal_test_fail
|
||||
cmp terminalTab3D,0
|
||||
jne terminal_test_fail
|
||||
call RenderTerminal3D
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestCash
|
||||
mov engineMode,MODE_WALK
|
||||
mov playerX,TERMINAL_TROPHY_X
|
||||
call CheckInteractions
|
||||
cmp interactTargetId,INTERACT_TERMINAL
|
||||
jne terminal_test_fail
|
||||
call Trigger3DInteraction
|
||||
cmp terminalTab3D,1
|
||||
jne terminal_test_fail
|
||||
call RenderTerminal3D
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestAch0
|
||||
mov pressedKeys3D[27h],1
|
||||
call HandleTerminal3D
|
||||
cmp terminalPage3D,1
|
||||
jne terminal_test_fail
|
||||
call RenderTerminal3D
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestAch1
|
||||
mov mouseClick3D,1
|
||||
mov mouseX3D,290
|
||||
mov mouseY3D,220
|
||||
call HandleTerminal3D
|
||||
cmp terminalPage3D,2
|
||||
jne terminal_test_fail
|
||||
call RenderTerminal3D
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestAch2
|
||||
mov pressedKeys3D[27h],1
|
||||
call HandleTerminal3D
|
||||
cmp terminalTab3D,0
|
||||
jne terminal_test_fail
|
||||
mov mouseClick3D,1
|
||||
mov mouseX3D,25
|
||||
mov mouseY3D,220
|
||||
call HandleTerminal3D
|
||||
cmp terminalPage3D,2
|
||||
jne terminal_test_fail
|
||||
mov mouseClick3D,1
|
||||
mov mouseX3D,270
|
||||
mov mouseY3D,15
|
||||
call HandleTerminal3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne terminal_test_fail
|
||||
cmp mouseClick3D,0
|
||||
jne terminal_test_fail
|
||||
invoke BeginTerminal3D,1
|
||||
mov pressedKeys3D[45h],1
|
||||
call HandleTerminal3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne terminal_test_fail
|
||||
mov achievements,0
|
||||
mov bankroll,10000
|
||||
mov ownedBacks3D,1
|
||||
mov npcClaimed3D,0
|
||||
call RenderTerminal3D
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestLocked
|
||||
mov termTestStage,6
|
||||
mov viewHorizon3D,240
|
||||
mov playerX,TERMINAL_CASH_X
|
||||
mov playerY,FP_1*31/2
|
||||
mov playerAngle,384
|
||||
call TerminalTestWorldFrame
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestFront
|
||||
mov playerX,FP_1*53/2
|
||||
mov playerY,FP_1*29/2
|
||||
mov playerAngle,448
|
||||
call TerminalTestNoBarTexture
|
||||
test eax,eax
|
||||
jz terminal_test_fail
|
||||
call TerminalTestWorldFrame
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestSide
|
||||
mov playerX,TERMINAL_CASH_X
|
||||
mov playerY,12*FP_1
|
||||
mov playerAngle,128
|
||||
call TerminalTestNoBarTexture
|
||||
test eax,eax
|
||||
jz terminal_test_fail
|
||||
call TerminalTestIsolatedCabinet
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestRear
|
||||
mov playerX,TERMINAL_TROPHY_X
|
||||
mov playerY,FP_1*31/2
|
||||
mov playerAngle,384
|
||||
call TerminalTestWorldFrame
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestTrophyFront
|
||||
mov playerX,FP_1*61/2
|
||||
mov playerY,FP_1*29/2
|
||||
mov playerAngle,320
|
||||
call TerminalTestWorldFrame
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestTrophySide
|
||||
mov playerX,TERMINAL_TROPHY_X
|
||||
mov playerY,12*FP_1
|
||||
mov playerAngle,128
|
||||
call TerminalTestIsolatedCabinet
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestTrophyRear
|
||||
mov playerX,28*FP_1
|
||||
mov playerY,20*FP_1
|
||||
mov playerAngle,384
|
||||
call TerminalTestWorldFrame
|
||||
invoke DrawCityObject3D,OFFSET hallLoungeDecor3D+3*28,0
|
||||
invoke DrawCityObject3D,OFFSET hallLoungeDecor3D+4*28,0
|
||||
invoke DrawCityObject3D,OFFSET hallLoungeDecor3D+5*28,0
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestWall
|
||||
mov playerX,3*FP_1
|
||||
mov playerY,16*FP_1
|
||||
mov playerAngle,256
|
||||
call TerminalTestWorldFrame
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestLamp
|
||||
mov playerX,FP_1*5/2
|
||||
mov playerY,14*FP_1
|
||||
mov playerAngle,175
|
||||
call TerminalTestWorldFrame
|
||||
invoke WriteActionSnapshot3D,OFFSET termTestLampSide
|
||||
mov termTestStage,7
|
||||
mov ebx,terminalWorldArt3D.pixels
|
||||
mov terminalWorldArt3D.pixels,0
|
||||
invoke ResolveTerminalSurface3D,118
|
||||
test eax,eax
|
||||
jnz terminal_test_fail
|
||||
mov terminalWorldArt3D.pixels,ebx
|
||||
mov ebx,terminalScreenArt3D.pixels
|
||||
mov terminalScreenArt3D.pixels,0
|
||||
call RenderTerminal3D
|
||||
mov terminalScreenArt3D.pixels,ebx
|
||||
call FreeTerminalArt3D
|
||||
cmp terminalWorldArt3D.pixels,0
|
||||
jne terminal_test_fail
|
||||
invoke ExitProcess,0
|
||||
terminal_test_fail:
|
||||
invoke ExitProcess,termTestStage
|
||||
TerminalTestStart ENDP
|
||||
|
|
@ -26,5 +26,5 @@ try {
|
|||
if ($LASTEXITCODE -ne 0) { throw "Street pack failed to load from $directory" }
|
||||
} finally { Pop-Location }
|
||||
}
|
||||
Write-Output 'PASS: shipping MASM loader loads street, roulette, coin/dice, seven scratch-ticket kinds, 777/Clover, both billiards ball sets/table, eight hall furniture, wall decor and five wake-up/alley packs from root/TEMP and clears failed headers'
|
||||
Write-Output 'PASS: shipping MASM loader loads game packs, street/alley, hall furniture, terminals/lanterns, six guests, cabaret quartet and upper-floor materials from root/TEMP and clears failed headers'
|
||||
} finally { Pop-Location }
|
||||
|
|
|
|||