feat: implement secret fortune teller room, lived-in den v3, and bar camera stabilization
- Secret Fortune Teller Room: arcade lantern toggle, sliding timber panel, dedicated staircase and compact cellar chamber - Lived-in den pass v3: cloth hangings, draped workbench, props/clutter, centered entrance, and custom light sources - Bar camera stabilization: remove alcohol horizon sway while retaining drink animations - Synchronize binaries and update launcher hash to EADBA9C8C9BD5B7E3CC4CE486ED647C1CE9383D1DCB6EBC94FE755EA1F4BF927
BIN
assets/animations/hall/basement-door-v2.bin
Normal file
BIN
assets/animations/hall/fortune-materials-v2.bin
Normal file
BIN
assets/animations/hall/fortune-materials-v3.bin
Normal file
BIN
assets/animations/hall/fortune-room-v1.bin
Normal file
BIN
assets/animations/hall/fortune-room-v2.bin
Normal file
BIN
assets/animations/hall/fortune-room-v3.bin
Normal file
BIN
assets/animations/hall/guest-walk-left-v2.bin
Normal file
BIN
assets/animations/hall/guest-walk-v2.bin
Normal file
BIN
assets/sprites/hall/basement-door-v2.png
Normal file
|
After Width: | Height: | Size: 1.4 MiB |
BIN
assets/sprites/hall/fortune-cabinets-v2.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
assets/sprites/hall/fortune-clutter-v3.png
Normal file
|
After Width: | Height: | Size: 2.9 MiB |
BIN
assets/sprites/hall/fortune-decor-v2.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
BIN
assets/sprites/hall/fortune-den-v3.png
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
8
assets/sprites/hall/fortune-den-v3.prompts.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"tool": "Built-in image_gen",
|
||||
"references": "User-supplied blue draped room and cluttered tarot den, used as mood references only.",
|
||||
"den": "Use case: stylized-concept. Asset type: four independent sprites in a strict 2x2 atlas, genuine transparent background. The two input images are mood/style references only for a lived-in cluttered fortune teller den, NOT edit targets and NOT a request for a full room background. Top left: a complete wide doorway-like frame of heavy draped indigo/teal fabric, upper valance and two gathered floor-length side curtains with ropes, uneven hems and faded stitched stars; the large opening between curtains is fully transparent. Top right: one weathered dark-wood occult bookcase, full height including feet, stuffed chaotically with leaning mismatched old books, odd glass bottles, small skull, rolled parchment, a blue cloth hanging over one shelf, spilled dried herbs; no neat repeated rows. Bottom left: a low cluttered workbench, uneven blue cloth draped over it, open grimoire, loose tarot cards, used candles, jars, piled scrolls and pouches, complete legs. Bottom right: an untidy FLOOR pile of crooked books, loose parchment sheets, a worn leather bag, rolled scrolls, little fabric bundles; three-quarter slightly elevated view, cohesive single clutter cluster. All four fully contained in their own quadrant with transparent gutters, no objects crossing cell edges. Painterly realistic retro adventure game sprite art, tactile worn fabrics, wood and dusty glass; sapphire and faded blue textiles, silver/copper/old wood accents, not uniformly blue lacquer or shiny palace furniture. No lettering, no watermarks, no scenery or ground plane.",
|
||||
"floor_clutter": "Use case: stylized-concept. Asset type: genuine transparent 2x2 sprite atlas for a lived-in, slightly untidy fortune teller den, in painterly realistic retro 2.5D adventure game art. Four isolated independent FLOOR clutter clusters, each completely within its quadrant, generous transparent gutters. Top left: squat blackened copper cauldron on three short feet with faint pale blue vapor, stained rim, a wooden spoon, herbs and a few potion jars lying beside it, cold cyan reflected light, no orange fire. Top right: an old open travel chest with wrinkled blue fabric, loosely spilling scrolls, book, strings of beads, little worn pouches and loose cards, full base. Bottom left: an untidy low wooden tray and little carpet scrap with mismatched glass bottles, spilled blue crystals, loose paper sheets, feather pen and partly unrolled parchment, floor height. Bottom right: three short half-melted thick blue-white candles sitting on uneven wax-coated floor stones with loose cards and tied scroll beside them, pale blue flames and no large external halo. Worn, handmade, dusty, slightly disordered rather than polished or symmetrical. Blue/teal textiles, natural dark wood/copper accents, muted parchment. View slightly elevated from standing eye height for floor objects, coherent scale, no full room background, no floor plane outside each object's silhouette, no text, no watermark.",
|
||||
"transparent_cleanup": "Use case: background-extraction. Edit target: the referenced four-object atlas. Remove the entire dark grey/brown gradient studio backdrop, every floor shadow patch and every broad glow halo. Make all space between and around the four isolated object clusters truly transparent with zero alpha, including all four corners and gutters. Keep the cauldron with its small pale blue vapor, chest with spilling contents, tray with parchment and bottles, and blue-white candles with cards exactly intact, same placement, same details and color. Do not repaint objects. Preserve fine herbs, parchment edges, wax, loose cards and little blue candle flames. Transparent background, no checkerboard painted into image, no new room or floor plane.",
|
||||
"materials": "Use case: stylized-concept. Asset type: opaque square 2x2 material texture atlas, exactly four equal square quadrants without gutters or border, orthographic flat texture scans for a worn, lived-in blue tarot den. Each quadrant fills its entire area and is independently tileable. Top left: heavy faded midnight-blue/teal fabric wall hanging with irregular natural vertical folds, patching and subtle worn embroidered threads, slightly dusty uneven cloth, no rigid panels, no stone blocks, no large symbols. Top right: overlapping slack indigo fabric canopy panels with diagonal soft sagging folds, subtle faded floral weave, dusty cloth, no wood coffer grid or stone ceiling. Bottom left: old irregular dark blue-grey rough stone flags with dusty joints and scattered tiny wax stains, no shiny patterned tiles. Bottom right: threadbare blue/teal woven rug with faded muted rust and silver geometric accents, small all-over repeated Persian-like weave, irregular wear but no outer border, no big medallion. Blue remains dominant, secondary natural muted accents, no uniform polished royal-blue luxury design. Detailed tactile painterly game textures suitable for 256px per quadrant, evenly lit with no directional shadows baked across edges, no text, no objects, no gradients."
|
||||
}
|
||||
BIN
assets/sprites/hall/fortune-materials-v2.png
Normal file
|
After Width: | Height: | Size: 3.5 MiB |
BIN
assets/sprites/hall/fortune-materials-v3.png
Normal file
|
After Width: | Height: | Size: 3.7 MiB |
BIN
assets/sprites/hall/fortune-room-v1.png
Normal file
|
After Width: | Height: | Size: 2 MiB |
3
assets/sprites/hall/fortune-room-v1.prompt.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
Generated with the built-in image generation tool, transparent background.
|
||||
|
||||
Production sprite atlas for an existing 640x480 retro 1990s raycaster casino game, detailed hand-painted pixel art realistic proportions, crisp readable silhouettes, no text, no UI. Transparent background. Exact regular 2x2 grid in a square 1024x1024 image, each quadrant a separate isolated asset with transparent gutters and nothing crossing quadrant boundaries. TOP LEFT: a mysterious elderly female tarot fortune teller, silver grey wavy hair, deep plum shawl with small gold embroidery, seated facing viewer behind a round dark carved walnut table covered with dark aubergine cloth, both hands resting next to a small fan of illustrated tarot cards, two short burning wax candles, tiny smoky glass crystal ball, complete table pedestal and feet visible, chair and entire woman fully visible, warm candlelight and cool muted jade accents, maximum occupied height 450px and width 420px centered in quadrant with floor baseline y=480 within quadrant. TOP RIGHT: exactly same woman and table same scale same anchor same props same face same clothes, very subtle alternate idle pose looking down toward cards with fingers slightly shifted and candle flames flickering, baseline same. BOTTOM LEFT: a tall antique curio bookcase, dark carved wood, aged books, glass bottles, skull ornament, brass moon globe, warm candles on shelf, isolated object front-facing complete feet and top, no room or backdrop, baseline y=480 within quadrant. BOTTOM RIGHT: hanging rectangular ornate dark burgundy velvet tapestry with brass suspension rod, central large golden moon and scattered embroidered stars, rich woven textile, tassels, flat front view isolated on transparency, no text. No cast floor plane, no background wall. Consistent viewpoint slightly looking downward onto tabletop as a standing person; no dramatic perspective or fisheye. These are world sprites not game scene screenshots.
|
||||
5
assets/sprites/hall/fortune-room-v2-extra.prompts.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"tool": "Built-in image_gen",
|
||||
"wall": "Use case: stylized-concept. Asset type: production 2x2 transparent atlas of four distinct WALL decorations for a compact blue/silver tarot room in a retro first-person 2.5D game. Strict four equal quadrant grid, each silhouette entirely contained with wide transparent gutter. Top left: ornate horizontal silver framed display of three illustrated tarot cards behind glass, deep blue velvet backing, hanging moon and star charms; no readable text. Top right: small wall-mounted dark blue carved scroll rack with three rolled parchment scrolls, a hanging silver key-ring and cyan sealing wax, front elevation, no floor support. Bottom left: hanging bundle of silvery blue eucalyptus, teal dried herbs and blue ribbon, with small crystal pendants, front elevation. Bottom right: suspended silver crescent lantern with pale icy-blue glass, hanging from a complete short silver chain, front elevation, small enclosed blue-white flame, complete upper hook. Genuine transparent background, no room, no floor plane, no text or watermark. Rich painterly realistic retro adventure sprite finish, details recognizable after 256px reduction, crisp silhouettes. Primarily light blue, sapphire, cyan, silver, no dominant brown, gold, orange or purple. Each object independent, no touching adjacent cells.",
|
||||
"cabinets": "Use case: stylized-concept. Asset type: four separate blue/silver occult room furniture sprites on genuinely transparent background, strict 2 by 2 equal quadrant grid with generous gutters and no overlap. Top left a tall narrow blackened sapphire wood apothecary cabinet with silver carved frame, glass doors and four shelves filled with distinctly shaped cyan potion bottles, tiny blue crystals, closed blue drawers below, full feet. Top right a low wide blue carved book cupboard, two shelves of different-sized dark blue books, rolled pale scrolls, silver clasps, full feet and flat top surface suitable for placing a crystal ball. Bottom left a small wall mounted silver framed glass relic case containing a single pale blue crystal, antique silver key and a lunar charm on blue velvet. Bottom right a small closed ornate dark blue occult chest with silver corners and crescent emblem, three-quarter view, complete base. Consistent richly detailed painterly pixel-adventure game art, furniture front elevation except chest, legible after 256px scaling, no perspective room or floor plane. Cool palette sapphire, cyan, light blue, silver, no dominant brown, gold, purple or orange. No people, text or watermarks, no large glow outside sprite silhouettes."
|
||||
}
|
||||
BIN
assets/sprites/hall/fortune-room-v2.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
6
assets/sprites/hall/fortune-room-v2.prompts.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"tool": "Built-in image_gen; first atlas edited from fortune-room-v1.png, other two generated.",
|
||||
"character": "Use case: precise-object-edit. Edit the referenced game sprite atlas: preserve the 2 by 2 quadrant arrangement, object sizes, isolated silhouettes, genuinely transparent background and full furniture legs. Replace the two fortune teller poses in the top row with a mysterious seated figure wearing a deep midnight blue hooded mantle, silver celestial embroidery and light blue cloth highlights. The entire face inside the hood must be completely invisible, pitch-black shadow, no eyes, nose, mouth or hair. Left pose hands resting beside tarot cards; right pose one hand slowly reaching over a card and head slightly tilted, face still totally hidden. Same round tarot table and perspective, now blue velvet cloth, silver tassels, cool cyan crystal ball and blue-white candles, no brown/gold/burgundy dominant colors. Bottom left ornate bookcase becomes blackened blue carved wood with silver trim, many cyan glass bottles, rolled scrolls, navy books, lunar instrument and blue crystals. Bottom right tapestry becomes deep rich blue with embroidered silver moon, constellations and pale cyan stars, silver rod and blue tassels. Painterly detailed retro adventure game sprite art legible at 256 pixels each, crisp edges. Keep all four separate, no overlap across quadrant borders, generous transparent gutters; no backdrop, no floor plane, no lettering.",
|
||||
"decor": "Use case: stylized-concept. Asset type: production sprite atlas for a retro 2.5D first-person tarot chamber. Exactly four separate fully visible upright furniture objects in a strict 2 by 2 grid on genuine transparent background, generous gutters, no touching quadrant boundaries. Top left: tall silver branched candelabrum on a carved dark blue pedestal with five blue-white candle flames and pooled wax, complete base. Top right: ornate silver armillary sphere with a cyan crystal moon on a narrow deep blue stone pedestal, celestial etchings, complete base. Bottom left: two-seat elegant navy-blue velvet waiting bench with silver feet, matching small side table bearing an open grimoire and cyan glass bottle, coherent single prop. Bottom right: tall oval silver framed scrying mirror on stand with smoky pale blue reflections, navy draped cloth and little hanging moon charms, complete feet. No people. Front-facing slightly elevated perspective, richly detailed painterly realistic pixel-adventure sprite style, crisp silhouettes recognizable after scaling to 256px, all feet visible, no floor plane, no shadows outside silhouettes, no lettering. Cool light-blue, sapphire, silver palette, black accents, no dominant brown or gold, no purple.",
|
||||
"materials": "Use case: stylized-concept. Asset type: a strictly square four-quadrant material texture atlas, 2 by 2 equal square tiles, for a first-person retro tarot chamber. Fully opaque, fills entire image with no external border, no gutters, no labels or objects, orthographic flat texture scan no perspective or lighting falloff. Top left tile: elegant sapphire blue dressed stone wall blocks with subtle silver celestial engraved vertical pilasters, blue-grey joints, clean dry interior no decay, not bathroom tiles. Top right tile: deep midnight blue coffered ceiling panels with small silver constellations and moon carvings in each panel, restrained light cyan details, no luminous gradient. Bottom left tile: cool blue-grey polished geometric stone floor with thin silver geometric inlay, restrained wear, not wooden planks. Bottom right tile: woven sapphire blue velvet carpet with silver lunar geometric medallions and smaller cyan stars, all-over repeat, no big outer border. All four tiles individually seamless on opposing edges with matching scale and lighting, fine detailed tactile painterly game texture finish suitable at 256px per tile. Cohesive blue, pale cyan and silver palette, no brown, tan, gold, purple, orange. No vignette, no text, no lamps."
|
||||
}
|
||||
BIN
assets/sprites/hall/fortune-wall-v2.png
Normal file
|
After Width: | Height: | Size: 2 MiB |
BIN
assets/sprites/hall/guest-walk-v2.png
Normal file
|
After Width: | Height: | Size: 1.6 MiB |
8
assets/sprites/hall/prompts-v9.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"tool": "built-in image_gen",
|
||||
"sources": ["F:/покер/assets/sprites/hall/guest-walk-v2.png", "F:/покер/assets/sprites/hall/basement-door-v2.png"],
|
||||
"packs": ["assets/animations/hall/guest-walk-v2.bin", "assets/animations/hall/guest-walk-left-v2.bin", "assets/animations/hall/basement-door-v2.bin"],
|
||||
"walking": "Use case: identity-preserve. Production transparent game sprite sheet, retro detailed painted 1990s casino raycaster sprites, matching the six people in reference image exactly. Create ONE coherent sheet, EIGHT columns and SIX rows, equal slots, full body with empty gutters and all shoes visible. Each ROW is one person: row1 stocky gray-haired man gray suit red tie; row2 brunette bob woman emerald pantsuit; row3 balding mustached cream dinner-jacket man; row4 redhead blue knee-length dress woman; row5 dark-skinned man green brocade waistcoat white shirt; row6 older silver-haired woman plum knee-length dress. All face three-quarter RIGHT throughout. Each row shows EIGHT sequential genuinely different phases of a natural walking cycle: left heel strike, left loading, right foot passing with bent lifted knee, left toe-off/right reaching; right heel strike, right loading, left foot passing lifted knee, right toe-off/left reaching. Alternate which foot is forward clearly, flex knees and ankles, counter-swing arms, dresses sway with knees; no duplicates, no sliding rigid legs. Preserve faces, clothing, anatomy and head/body proportions across all eight slots. Camera fixed, same height and baseline. Transparent background with actual alpha. No scene, no shadows on floor, no labels, no text or borders. Output wide square large sprite atlas, not poster.",
|
||||
"door": "Use case: precise-object-edit. Reference is existing casino prop sheet, use ONLY its middle-right heavy iron vault door as identity reference. Produce a production game texture extraction sheet with THREE equal COLUMNS on transparent canvas. LEFT column: full-height heavy rectangular dark worn steel door LEAF alone perfectly FRONT ORTHOGRAPHIC, brass rivets, inset rounded steel panel, hinge plates at left, small brass locking mechanism right, rich detailed retro painted pixel sprite. IMPORTANT remove the large central handwheel entirely: plain uninterrupted steel at its old location. No frame, no pipes, no floor, leaf only. MIDDLE column: the stationary door FRAME alone, straight-on front orthographic, heavy industrial riveted steel jambs and lintel, brass plumbing around top and right and small amber lamp on upper right. Centre of frame is an EMPTY transparent opening, NO door leaf or wheel. RIGHT column: separate large round BRASS FIVE-SPOKE HANDWHEEL alone facing straight at camera, exactly circular, central hub, clean transparency between spokes, no steel background; centered at middle of its slot with generous blank margin. Match the reference door and warm casino lighting, not a futuristic door. All three extracted pieces wholly visible, isolated, crisp texture, real alpha transparency. No scene, no floor shadows, no people, no text, no labels, no border. Large landscape sheet three columns, components must not touch.",
|
||||
"normalization": "Offline ANM1 packing; one fixed scale per appearance; eight phases plus mirrors; supporting foot anchored at bottom row. Door handwheel rotates on a fixed steel leaf before native hinge motion."
|
||||
}
|
||||
BIN
bin/poker.exe
|
|
@ -8,7 +8,7 @@ namespace AssemblerCasinoLauncher
|
|||
{
|
||||
static class Program
|
||||
{
|
||||
const string ExpectedPokerSha256 = "071F09A64F0E2F3EC267AC1C963D9849AA3A1DBDA81B3417C90E3499C1D5102F";
|
||||
const string ExpectedPokerSha256 = "EADBA9C8C9BD5B7E3CC4CE486ED647C1CE9383D1DCB6EBC94FE755EA1F4BF927";
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
|
|
|
|||
18
casino3d.inc
|
|
@ -1190,6 +1190,24 @@ wall_sample_material:
|
|||
mov eax, [ebx+eax*4]
|
||||
jmp wall_shade_pixel
|
||||
wall_sample_indoor_hd:
|
||||
cmp edx,132
|
||||
jb wall_sample_other_hd
|
||||
cmp edx,135
|
||||
ja wall_sample_other_hd
|
||||
mov ebx,fortuneMaterialArt3D.pixels
|
||||
test ebx,ebx
|
||||
jz wall_fortune_material_missing
|
||||
sub edx,132
|
||||
shl edx,18
|
||||
add ebx,edx
|
||||
jmp wall_room_hd_coordinates
|
||||
wall_fortune_material_missing:
|
||||
mov edx,92
|
||||
mov eax,edi
|
||||
shr eax,16
|
||||
and eax,127
|
||||
jmp wall_sample_standard
|
||||
wall_sample_other_hd:
|
||||
cmp edx,122
|
||||
jb wall_sample_existing_hd
|
||||
cmp edx,125
|
||||
|
|
|
|||
BIN
dist/Assembler Casino.exe
vendored
|
|
@ -7,7 +7,11 @@ Runtime remains MASM32 assembly. Existing player saves and unrelated edits stay
|
|||
- [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] Distant doorway continuation, automatic wheel-then-hinge opening and distance-driven visitor gait.
|
||||
- [x] Upper lounge with its own bar and access to Hell Tower, Buckshot and Russian roulette.
|
||||
- [x] Secret pull-down lantern, manually walked cellar stairs and furnished fortune teller room with an honest unavailable-dialogue placeholder.
|
||||
- [x] Independent blue/silver tarot chamber and stairs, concealed hooded face, extra celestial furniture and native palette/collision checks.
|
||||
- [x] Centred tarot-room entrance with matched stair continuation; worn drapery, cloth ceiling and irregular floor/workbench clutter based on the supplied references.
|
||||
|
||||
## Following Integration
|
||||
|
||||
|
|
@ -17,6 +21,7 @@ Runtime remains MASM32 assembly. Existing player saves and unrelated edits stay
|
|||
- [ ] 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.
|
||||
- [ ] Real offline fortune teller dialogue and tarot interpretations; teacher form and grade lottery remain deferred with it.
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -23,20 +23,36 @@ 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
|
||||
The iron door remains in the original east-facing alcove. Its frame is at
|
||||
(28.38,10.5), while the leaf and hinge are recessed to x=28.9. Approaching from
|
||||
the hall or the upper end of the basement stairs automatically requests opening;
|
||||
the old E interaction remains an optional request, not a required teleport.
|
||||
The dogging handwheel turns through seventeen texture phases over 700ms, then
|
||||
the leaf swings through 90 degrees over 1000ms around a fixed hinge. A native
|
||||
ray/rotated-plane intersection renders perspective and occlusion. It does not
|
||||
slide sideways or turn to face the camera. Leaving the wider safety zone starts
|
||||
a 1200ms hold, followed by the reverse sequence: shut the leaf, then lock the
|
||||
handwheel. Both sides of the doorway and the whole swept area hold it open.
|
||||
Returning during either phase reverses from the current pose without snapping;
|
||||
the wheel must unlock fully before the leaf can reopen. The wider hold radius
|
||||
prevents boundary jitter. The closed leaf blocks the doorway.
|
||||
The same leaf/frame is rendered and collision-checked in the overlapping stair
|
||||
landing, including the return journey; a closed door cannot be bypassed from below.
|
||||
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
|
||||
depth-tested aperture. The former eight-unit view cutoff has been replaced with
|
||||
a bound beyond the diagonal of the whole map: moving away no longer hides a
|
||||
doorway's continuation. 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.
|
||||
Destination pixel depth is retained so the swinging door can occlude the view
|
||||
through the opening. Descending risers meet the near edge of their treads.
|
||||
|
||||
## Infernal Portal
|
||||
|
||||
|
|
@ -52,12 +68,22 @@ 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.
|
||||
Both levers remain usable after activation. Interacting again toggles the selected
|
||||
lever back, disables walking/E entry immediately and shows the sealed portal.
|
||||
The handle returns through the same six poses over 600ms; toggling mid-motion
|
||||
reverses from its current pose. Both handles must finish in their enabled position
|
||||
to reopen the portal. Interaction prompts distinguish pulling from returning.
|
||||
|
||||
## 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
|
||||
names follow the selected appearance. Walking now uses eight distinct generated
|
||||
phases per appearance, plus a mirrored pack selected by camera-relative travel.
|
||||
The phase follows successful displacement (one cycle per 0.75 world units), not
|
||||
elapsed time: waiting or being blocked does not march in place. Supporting feet
|
||||
are normalized to the floor baseline, with the old standing/seated canvas padding
|
||||
compensated. 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.
|
||||
|
|
@ -83,6 +109,8 @@ performer sprites and audience applause recordings are not included in this pass
|
|||
Generated using the built-in image generator, with no API key:
|
||||
|
||||
- `assets/sprites/hall/guest-cast-v1.png`
|
||||
- `assets/sprites/hall/guest-walk-v2.png`
|
||||
- `assets/sprites/hall/basement-door-v2.png`
|
||||
- `assets/sprites/hall/cabaret-ensemble-v1.png`
|
||||
- `assets/sprites/hall/cabaret-musicians-v1.png`
|
||||
- `assets/sprites/hall/upper-materials-v1.png`
|
||||
|
|
@ -92,10 +120,11 @@ Generated using the built-in image generator, with no API key:
|
|||
- `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`.
|
||||
`assets/sprites/hall/prompts-v8.json`. The new door/walking prompts are in
|
||||
`assets/sprites/hall/prompts-v9.json` (built-in image_gen, no API key).
|
||||
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`
|
||||
`pack_upper_motion.py`, `pack_door_walk_v2.py`
|
||||
under `tools/generators`. Original source images are preserved.
|
||||
|
||||
## Verification
|
||||
|
|
@ -114,8 +143,19 @@ 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.
|
||||
The new tests include far doorway views, automatic opening before any E press,
|
||||
wheel-before-hinge sequencing, eight native gait captures, reverse-facing art,
|
||||
blocked movement and equal gait phase after the same travel at 16/32ms updates.
|
||||
An additional native return-path test starts below a closed door without E,
|
||||
checks that the body is stopped while the wheel turns, then walks back into the
|
||||
hall after automatic opening.
|
||||
All six visitors are also updated for two simulated minutes by the native movement
|
||||
procedures, checking walkable positions and bounded distance-driven gait phases.
|
||||
`--prove-distance-regression` restores just the old eight-unit cutoff and must
|
||||
fail the actual native distant-doorway assertion. The shipping loader probe
|
||||
also checks both 48-frame gait packs and the 18-texture door pack from root/TEMP.
|
||||
|
||||
Published on 2026-10-18 to `poker.exe` and `bin/poker.exe`, with the existing
|
||||
Previous release, 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
|
||||
|
|
@ -124,3 +164,171 @@ 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.
|
||||
|
||||
## 2026-10-19 Release
|
||||
|
||||
Published the distance/door/gait repair to root `poker.exe`, `bin/poker.exe` and
|
||||
`build/graphics/poker.exe`; updated the existing C# launcher's integrity hash.
|
||||
All three game copies have SHA-256
|
||||
`70D16BF2DB85AC23E098B9B31EB4CD9A6664D6E51FEDE470CC6116C3E9F94339`.
|
||||
Fresh native navigation/rendering/gait tests and shipping procedure parity pass,
|
||||
including reverse-side closed-door collision and automatic return traversal.
|
||||
Terminal/lantern parity, billiards rules/ownership parity, 208 card faces and
|
||||
shipping asset loading from root/TEMP also pass. Negative controls reproduce
|
||||
the old distant-wall cutoff and missing aperture near clipping. The game runtime
|
||||
is still assembly-only; C# is used only by the existing launcher packaging.
|
||||
No full interactive desktop playthrough or full legacy action suite is claimed.
|
||||
|
||||
### Portal Switching Update
|
||||
|
||||
The same day's follow-up adds reversible lever interaction and immediate portal
|
||||
shutdown. Root/bin/build game copies and launcher integrity metadata were updated
|
||||
to SHA-256 `36C1CF0734F3C1E0F30F52E76C236FE0261D76E9089BF5AB5B7FBEDEE4E58FF4`.
|
||||
Native tests cover both enabled levers remaining reachable, pull/return prompts,
|
||||
walking and E entry blocked immediately after shutdown, the visible sealed arch,
|
||||
mid-stroke reversal without a position jump, full release and reactivation.
|
||||
Tested/shipping procedure parity, billiards and terminal regressions pass.
|
||||
|
||||
### Automatic Door Closing Update
|
||||
|
||||
The latest build closes the basement door after the player leaves its safety
|
||||
zone and a 1200ms hold expires. The leaf shuts before the wheel locks; approaching
|
||||
again reverses either phase without a jump. The upper stair landing and the full
|
||||
leaf swing remain protected while occupied. A manual E request also expires.
|
||||
Native assertions cover both sides, the hold delay, one update per frame,
|
||||
closing/locking order, mid-motion reversal, full closed collision and reopening.
|
||||
Three native captures show closing, locking and the fully closed door.
|
||||
Published root/bin/build game copies and launcher metadata use SHA-256
|
||||
`ADF4CE0A7ECAED4D0293A3791FA8B1CA204C931E5F78FE70C1DAD8166BF7AD24`.
|
||||
|
||||
### Secret Fortune Teller Room
|
||||
|
||||
The west arcade now has a pull-down lantern beside the slots. E lowers the
|
||||
lantern, then slides a concealed timber panel aside. E again returns it; the
|
||||
panel pauses closing while the player occupies its threshold or remains anywhere
|
||||
inside the secret staircase/chamber, so a pending close cannot trap a visitor.
|
||||
The original eastern steel basement door and lottery stand have not moved.
|
||||
|
||||
Walking through the opening enters a separate, manually traversed west-facing
|
||||
stairwell (route 3), descending to a compact 5x4-metre cellar (hall level 3),
|
||||
reduced from the initial 8x9-metre layout. All camera
|
||||
height changes follow the player's position. Doorway apertures show both ends
|
||||
at near and far distances; returning uses the same physical staircase.
|
||||
|
||||
The chamber and its staircase use dedicated sapphire stone walls, a silver-star
|
||||
coffered ceiling, blue-grey floor tiles and a blue lunar rug, not the ordinary
|
||||
basement's worn concrete/green walls. The seated figure wears a blue mantle;
|
||||
both idle poses keep the entire face hidden inside the hood. The table has tarot
|
||||
cards, blue-white candles and a crystal ball. A matching curio bookcase, lunar
|
||||
tapestry, two floor candelabra, armillary sphere, scrying mirror and velvet bench
|
||||
with a grimoire side table furnish the room without blocking the walking route.
|
||||
A second potion cabinet, low book cupboard, relic display, chest, card vitrine,
|
||||
scroll rack and hanging herbs fill the wall/corner space. The fronts use fixed
|
||||
world-aligned planes rather than camera-facing billboards; the two largest
|
||||
cabinets and bench also have depth geometry. The room's central walking route
|
||||
and stair aperture remain clear, with furniture collision checked natively.
|
||||
|
||||
The ambient light is dim. Eight world-space point sources at the blue-white
|
||||
candles, crystal and lantern positions supply local cool illumination. Native
|
||||
lighting reconstructs each surface position from scene depth and its camera ray,
|
||||
including height, before applying distance falloff. Flame cores stay bright
|
||||
only near an actual light. This is not a camera-centred glow or a flat blue tint.
|
||||
It runs only in the tarot chamber, before the HUD and doorway composition.
|
||||
|
||||
Artwork was generated/edited with the built-in image tool. The five version-2
|
||||
source PNGs and exact prompts are in `assets/sprites/hall/fortune-*-v2.*`.
|
||||
`tools/generators/pack_fortune_room.py` packs sixteen transparent 256px sprites into
|
||||
`assets/animations/hall/fortune-room-v2.bin` and four independent surfaces into
|
||||
`fortune-materials-v2.bin`. Materials 132-135 are resolved separately, without
|
||||
overwriting any shared casino or basement texture. Fixed prop surfaces use
|
||||
virtual IDs 136-151 against the same sprite pack. Wall and plane renderers
|
||||
handle them at every distance, with bounded fallback when an art pack is absent.
|
||||
All game rendering, navigation and interaction remain native assembly.
|
||||
|
||||
E at the fortune teller opens the existing native "Not implemented yet" modal.
|
||||
Closing it preserves the room, camera and bankroll. There is deliberately no
|
||||
fake AI dialogue, tarot result, student form, networking or model dependency.
|
||||
Those features remain deferred until a real dialogue approach is selected.
|
||||
|
||||
`fortune_room_test.inc` runs within the fresh MASM hall harness: lantern targeting
|
||||
and toggle prompts, pull/open poses, closed collision, per-frame animation guard,
|
||||
near/far doorway views, full walking descent/return, continuous stair eye height,
|
||||
room furniture collision, two idle poses, modal open/close without spending,
|
||||
occupied-threshold safety and closing after return. Native captures are written
|
||||
to `build/hall-expansion/fortune-*.bmp` and `.png`. The shipping asset-path probe
|
||||
also checks both new art packs. Palette assertions cover the chamber, both side
|
||||
views, the close-up and the staircase; native assertions cover the extra furniture
|
||||
collisions, material offsets, point-light falloff and darker-versus-unlit output.
|
||||
Additional native captures cover oblique furniture and the eastern cabinets.
|
||||
A full interactive desktop playthrough is not
|
||||
claimed by these automated native tests.
|
||||
|
||||
### Blue Chamber Release (2026-10-20)
|
||||
|
||||
Published root/bin/build game copies and the launcher integrity metadata use
|
||||
SHA-256 `3C6E6B85A3B695B5DD52EB4F1E8BE0345E9DE4D4028D269B925B1F5AAF2C3BFA`.
|
||||
Fresh native navigation, lighting, furnished-room and dialogue checks pass.
|
||||
Shipping procedure parity, billiards rules/ownership, all 208 card faces,
|
||||
terminal regressions and asset loading from root/TEMP also pass.
|
||||
The parity reader now optionally normalizes branches to named procedure entries,
|
||||
as it already did calls, so the same tail branch remains comparable across
|
||||
different object layouts. It still compares the destination procedure name and
|
||||
all instruction opcodes; ordinary card/terminal comparisons keep their defaults.
|
||||
|
||||
### Lived-In Den And Centred Entrance
|
||||
|
||||
The room-side entrance is now centred on the east wall: Y=10..12 in a room
|
||||
spanning Y=9..13. Its centreline Y=11 also meets the fortune teller directly.
|
||||
The unchanged secret staircase remains at Y=9..11; the lower landing translates
|
||||
Y by +1 metre on entry and -1 on return. Both doorway views use the same inverse
|
||||
translation, so the visible continuation agrees with physical walking. The
|
||||
arcade lantern and the separate steel basement door/lottery stand stay put.
|
||||
|
||||
The blue palace-like panelling and ceiling are replaced by worn fabric wall
|
||||
hangings, a slack cloth canopy, dusty irregular flagstones and a faded rug.
|
||||
Gathered curtains frame the fortune teller. A stuffed, mismatched curio shelf,
|
||||
draped workbench, leaning book piles, loose papers, travel bag, open chest,
|
||||
cauldron, bottles/tray, melted floor candles and hanging herbs make the room
|
||||
less tidy without closing the central walking aisle. Furniture remains fixed
|
||||
in world space. The concealed face, local blue light and unavailable dialogue
|
||||
are retained. Additional low light sources correspond to the floor candles.
|
||||
|
||||
Version-3 sources: `assets/sprites/hall/fortune-den-v3.png`,
|
||||
`fortune-clutter-v3.png`, `fortune-materials-v3.png` and the exact built-in image
|
||||
generation/edit prompts in `fortune-den-v3.prompts.json`. The offline packer
|
||||
retains the original sixteen frames and adds eight new ones, yielding
|
||||
`assets/animations/hall/fortune-room-v3.bin` (256x256x24) and
|
||||
`fortune-materials-v3.bin` (256x256x4). Surface IDs 152-159 select the new frames.
|
||||
No external runtime, model, service or library is added.
|
||||
|
||||
Fresh native tests cover centred entry alignment, actual descent and return,
|
||||
the centred doorway mask, clutter collision, art bounds/headers, lighting and
|
||||
the dialogue placeholder. `fortune-doorway-centered.png` records the room-side
|
||||
opening; the usual room/side/oblique captures show the new furnishing pass.
|
||||
|
||||
Published root/bin/build executables and launcher metadata now use SHA-256
|
||||
`C65E1B1EE5CE8DDB79EADFED982619378780EE4E866BC3D2F3A17DA83E26446F`.
|
||||
Fresh native tests and shipping procedure parity pass, including off-centre
|
||||
crossings and the inverse landing translation. Billiards ownership/rules, card
|
||||
ranks, terminals and root/TEMP asset loading pass as well. This does not claim
|
||||
a full interactive desktop playthrough.
|
||||
|
||||
### Bar Camera Without Alcohol Sway
|
||||
|
||||
Removed alcohol strength, intoxication accumulation and its per-frame horizon
|
||||
offset. Drink prices, purchases, serving/drinking animations and both bar views
|
||||
are unchanged. Walking now restores the exact standing horizon and eye height,
|
||||
including recovery from a previously shifted camera. Staircase height remains
|
||||
route-controlled.
|
||||
|
||||
The native bar fixture exercises 24 visits across both bars, all three drinks,
|
||||
single-debit protection, animation boundaries, six actual bar renders and 6144
|
||||
walking updates with exact camera-state assertions. Profile saving is disabled
|
||||
through the existing arrival-confirmation guard. Removing horizon recovery in a
|
||||
test-only build is rejected by case 70. The fixture models the return-to-walking
|
||||
state; it does not automate the Win32 Escape key event loop.
|
||||
|
||||
Published game copies and launcher integrity metadata use SHA-256
|
||||
`EADBA9C8C9BD5B7E3CC4CE486ED647C1CE9383D1DCB6EBC94FE755EA1F4BF927`.
|
||||
Fresh native checks, shipping procedure parity, billiards, card ranks, terminals,
|
||||
Russian labels, audio bindings and root/TEMP asset loading pass.
|
||||
|
|
|
|||
BIN
poker.exe
BIN
poker.obj
|
|
@ -5,9 +5,16 @@ 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,132
|
||||
jb @F
|
||||
cmp eax,159
|
||||
ja @F
|
||||
invoke ResolveFortuneMaterial3D,material
|
||||
ret
|
||||
@@:
|
||||
cmp eax,122
|
||||
jb @F
|
||||
cmp eax,130
|
||||
cmp eax,131
|
||||
ja @F
|
||||
invoke ResolveUpperMaterial3D,material
|
||||
ret
|
||||
|
|
|
|||
539
src_graphics/fortune_room.inc
Normal file
|
|
@ -0,0 +1,539 @@
|
|||
LoadFortuneArt3D PROC
|
||||
invoke LoadAnimPack3D,OFFSET fortuneArt3D,OFFSET fortuneArtPath3D
|
||||
invoke ValidateCityPack3D,OFFSET fortuneArt3D,256,256,24
|
||||
invoke LoadAnimPack3D,OFFSET fortuneMaterialArt3D,OFFSET fortuneMaterialPath3D
|
||||
invoke ValidateCityPack3D,OFFSET fortuneMaterialArt3D,256,256,4
|
||||
ret
|
||||
LoadFortuneArt3D ENDP
|
||||
|
||||
FreeFortuneArt3D PROC
|
||||
invoke FreeAnimPack3D,OFFSET fortuneArt3D
|
||||
invoke FreeAnimPack3D,OFFSET fortuneMaterialArt3D
|
||||
ret
|
||||
FreeFortuneArt3D ENDP
|
||||
|
||||
ResolveFortuneMaterial3D PROC material:DWORD
|
||||
mov eax,material
|
||||
sub eax,132
|
||||
cmp eax,3
|
||||
ja fortune_material_sprite
|
||||
cmp fortuneMaterialArt3D.pixels,0
|
||||
je fortune_material_missing
|
||||
shl eax,18
|
||||
add eax,fortuneMaterialArt3D.pixels
|
||||
mov ecx,256
|
||||
mov edx,256
|
||||
ret
|
||||
fortune_material_sprite:
|
||||
sub eax,4
|
||||
cmp eax,23
|
||||
ja fortune_material_missing
|
||||
cmp fortuneArt3D.pixels,0
|
||||
je fortune_material_missing
|
||||
shl eax,18
|
||||
add eax,fortuneArt3D.pixels
|
||||
mov ecx,256
|
||||
mov edx,256
|
||||
ret
|
||||
fortune_material_missing:
|
||||
xor eax,eax
|
||||
ret
|
||||
ResolveFortuneMaterial3D ENDP
|
||||
|
||||
UpdateFortunePassage3D PROC
|
||||
mov eax,animationClock3D
|
||||
cmp eax,fortunePassageClock3D
|
||||
je fortune_update_done
|
||||
mov fortunePassageClock3D,eax
|
||||
mov eax,deltaTime3D
|
||||
cmp eax,100
|
||||
jbe @F
|
||||
mov eax,100
|
||||
@@:
|
||||
cmp fortunePassageTarget3D,0
|
||||
je fortune_update_close
|
||||
add eax,fortunePassageTime3D
|
||||
cmp eax,1400
|
||||
jbe fortune_update_store
|
||||
mov eax,1400
|
||||
jmp fortune_update_store
|
||||
fortune_update_close:
|
||||
; A pending close waits for the visitor to leave the entire secret branch.
|
||||
cmp hallLevel3D,3
|
||||
je fortune_update_done
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne fortune_update_hall_guard
|
||||
cmp stairRoute3D,3
|
||||
jne fortune_update_subtract
|
||||
jmp fortune_update_done
|
||||
fortune_update_hall_guard:
|
||||
cmp hallLevel3D,0
|
||||
jne fortune_update_subtract
|
||||
cmp playerX,2*FP_1
|
||||
ja fortune_update_subtract
|
||||
cmp playerY,FP_1*85/10
|
||||
jb fortune_update_subtract
|
||||
cmp playerY,FP_1*115/10
|
||||
jbe fortune_update_done
|
||||
fortune_update_subtract:
|
||||
mov edx,fortunePassageTime3D
|
||||
sub edx,eax
|
||||
jae @F
|
||||
xor edx,edx
|
||||
@@:
|
||||
mov eax,edx
|
||||
fortune_update_store:
|
||||
mov fortunePassageTime3D,eax
|
||||
fortune_update_done:
|
||||
ret
|
||||
UpdateFortunePassage3D ENDP
|
||||
|
||||
FortuneEntranceCanWalk3D PROC px:DWORD,py:DWORD
|
||||
cmp px,FP_1*17/10
|
||||
ja fortune_entrance_clear
|
||||
cmp py,9*FP_1
|
||||
jb fortune_entrance_clear
|
||||
cmp py,11*FP_1
|
||||
ja fortune_entrance_clear
|
||||
cmp fortunePassageTime3D,1400
|
||||
jb fortune_entrance_block
|
||||
cmp py,FP_1*92/10
|
||||
jb fortune_entrance_block
|
||||
cmp py,FP_1*108/10
|
||||
ja fortune_entrance_block
|
||||
fortune_entrance_clear:
|
||||
mov eax,1
|
||||
ret
|
||||
fortune_entrance_block:
|
||||
xor eax,eax
|
||||
ret
|
||||
FortuneEntranceCanWalk3D ENDP
|
||||
|
||||
TryFortuneInteraction3D PROC
|
||||
cmp interactTargetId,INTERACT_SECRET_LAMP
|
||||
jne fortune_try_seer
|
||||
cmp hallLevel3D,0
|
||||
jne fortune_interaction_none
|
||||
xor fortunePassageTarget3D,1
|
||||
call ClearKeys3D
|
||||
mov eax,1
|
||||
ret
|
||||
fortune_try_seer:
|
||||
cmp interactTargetId,INTERACT_FORTUNE
|
||||
jne fortune_interaction_none
|
||||
cmp hallLevel3D,3
|
||||
jne fortune_interaction_none
|
||||
call BeginUnavailable3D
|
||||
mov eax,1
|
||||
ret
|
||||
fortune_interaction_none:
|
||||
xor eax,eax
|
||||
ret
|
||||
TryFortuneInteraction3D ENDP
|
||||
|
||||
CheckFortuneInteractions3D PROC
|
||||
cmp hallLevel3D,3
|
||||
je fortune_check_room
|
||||
cmp fortunePassageTime3D,1400
|
||||
jne @F
|
||||
invoke HallTarget3D,FP_1*13/10,10*FP_1,0,OFFSET fortuneStairHint3D
|
||||
@@:
|
||||
cmp fortunePassageTarget3D,0
|
||||
jne fortune_check_release
|
||||
invoke HallTarget3D,FP_1*14/10,FP_1*87/10,INTERACT_SECRET_LAMP,OFFSET fortuneLampPull3D
|
||||
ret
|
||||
fortune_check_release:
|
||||
invoke HallTarget3D,FP_1*14/10,FP_1*87/10,INTERACT_SECRET_LAMP,OFFSET fortuneLampReturn3D
|
||||
ret
|
||||
fortune_check_room:
|
||||
mov interactTargetId,0
|
||||
mov interactPromptPtr,0
|
||||
invoke HallTarget3D,FP_1*173/10,11*FP_1,0,OFFSET fortuneExitHint3D
|
||||
invoke HallTarget3D,FP_1*141/10,11*FP_1,INTERACT_FORTUNE,OFFSET fortuneTalk3D
|
||||
fortune_check_done:
|
||||
ret
|
||||
CheckFortuneInteractions3D ENDP
|
||||
|
||||
RenderFortuneEntrance3D PROC uses ebx esi
|
||||
local panelLo:DWORD,panelHi:DWORD,bodyLow:DWORD,bodyHigh:DWORD
|
||||
; The real wall remains behind this aperture; the portal compositor clips it.
|
||||
invoke DrawCityBoxSides3D,FP_1*128/100,FP_1*138/100,FP_1*88/10,9*FP_1,0,FP_1*5/2,102
|
||||
invoke DrawCityBoxSides3D,FP_1*128/100,FP_1*138/100,11*FP_1,FP_1*112/10,0,FP_1*5/2,102
|
||||
invoke DrawCityBoxSides3D,FP_1*128/100,FP_1*138/100,9*FP_1,11*FP_1,FP_1*23/10,FP_1*5/2,102
|
||||
mov eax,fortunePassageTime3D
|
||||
sub eax,280
|
||||
jns @F
|
||||
xor eax,eax
|
||||
@@:
|
||||
imul eax,2*FP_1
|
||||
xor edx,edx
|
||||
mov ecx,1120
|
||||
div ecx
|
||||
add eax,9*FP_1
|
||||
mov panelLo,eax
|
||||
add eax,2*FP_1
|
||||
mov panelHi,eax
|
||||
invoke DrawCityVertical3D,0,FP_1*135/100,panelLo,panelHi,0,FP_1*23/10,1
|
||||
; A rigid brass arm stays fixed while the lantern itself is pulled down.
|
||||
mov eax,fortunePassageTime3D
|
||||
cmp eax,280
|
||||
jbe @F
|
||||
mov eax,280
|
||||
@@:
|
||||
imul eax,FP_1/5
|
||||
xor edx,edx
|
||||
mov ecx,280
|
||||
div ecx
|
||||
mov ebx,FP_1*14/10
|
||||
sub ebx,eax
|
||||
mov bodyLow,ebx
|
||||
add ebx,FP_1/2
|
||||
mov bodyHigh,ebx
|
||||
invoke DrawCityBoxSides3D,FP_1*131/100,FP_1*155/100,FP_1*857/100,FP_1*883/100,bodyLow,bodyHigh,120
|
||||
invoke DrawCityHorizontal3D,FP_1*131/100,FP_1*155/100,FP_1*857/100,FP_1*883/100,bodyHigh,110
|
||||
invoke DrawCityHorizontal3D,FP_1*131/100,FP_1*155/100,FP_1*857/100,FP_1*883/100,bodyLow,110
|
||||
invoke DrawCityBoxSides3D,FP_1*102/100,FP_1*143/100,FP_1*866/100,FP_1*874/100,FP_1*194/100,2*FP_1,110
|
||||
invoke DrawCityBoxSides3D,FP_1*140/100,FP_1*146/100,FP_1*866/100,FP_1*874/100,bodyHigh,2*FP_1,110
|
||||
ret
|
||||
RenderFortuneEntrance3D ENDP
|
||||
|
||||
DrawFortuneFixedProp3D PROC uses esi prop:DWORD,axis:DWORD
|
||||
local coordinate:DWORD,lo:DWORD,hi:DWORD,zTop:DWORD,material:DWORD
|
||||
mov esi,prop
|
||||
mov eax,[esi].CityProp3D.posX
|
||||
mov ecx,[esi].CityProp3D.posY
|
||||
cmp axis,0
|
||||
je @F
|
||||
xchg eax,ecx
|
||||
@@:
|
||||
mov coordinate,eax
|
||||
mov eax,[esi].CityProp3D.worldW
|
||||
sar eax,1
|
||||
mov edx,ecx
|
||||
sub edx,eax
|
||||
mov lo,edx
|
||||
add ecx,eax
|
||||
mov hi,ecx
|
||||
mov eax,[esi].CityProp3D.groundZ
|
||||
add eax,[esi].CityProp3D.worldH
|
||||
mov zTop,eax
|
||||
mov eax,[esi].CityProp3D.frame
|
||||
add eax,136
|
||||
mov material,eax
|
||||
invoke DrawCityVertical3D,axis,coordinate,lo,hi,[esi].CityProp3D.groundZ,zTop,material
|
||||
ret
|
||||
DrawFortuneFixedProp3D ENDP
|
||||
|
||||
RenderFortuneFurnishings3D PROC uses ebx
|
||||
; Layered cloth, a worn rug and irregular floor clutter make a working den.
|
||||
invoke DrawCityHorizontal3D,FP_1*135/10,FP_1*176/10,FP_1*97/10,FP_1*124/10,FP_1/100,135
|
||||
; Furniture fronts have fixed wall axes and solid depth, never billboards.
|
||||
invoke DrawCityBoxSides3D,FP_1*134/10,FP_1*148/10,FP_1*902/100,FP_1*931/100,0,FP_1*21/10,132
|
||||
invoke DrawCityHorizontal3D,FP_1*134/10,FP_1*148/10,FP_1*902/100,FP_1*931/100,FP_1*21/10,134
|
||||
invoke DrawCityBoxSides3D,FP_1*154/10,FP_1*176/10,FP_1*1266/100,FP_1*129/10,FP_1*2/10,FP_1*6/10,135
|
||||
invoke DrawCityBoxSides3D,FP_1*1771/100,FP_1*1798/100,FP_1*1225/100,FP_1*1285/100,0,FP_1*19/10,132
|
||||
invoke DrawCityBoxSides3D,FP_1*14,FP_1*152/10,FP_1*1271/100,FP_1*129/10,0,FP_1*6/10,132
|
||||
; Both poses keep the face hidden; the hands and mantle change instead.
|
||||
mov eax,animationClock3D
|
||||
xor edx,edx
|
||||
mov ecx,6000
|
||||
div ecx
|
||||
xor eax,eax
|
||||
cmp edx,4800
|
||||
jb @F
|
||||
inc eax
|
||||
@@:
|
||||
mov fortuneSeer3D.frame,eax
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneTapestry3D,0
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneShelf3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneBench3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneMirror3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneCandleA3D,0
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneCandleB3D,0
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneScrolls3D,0
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneHerbs3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneHerbsB3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneHerbsC3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneHanging3D,0
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneHanging3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortunePotions3D,0
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneBooks3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneChest3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneBookPileA3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneBookPileB3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneCauldron3D,0
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneTray3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneFloorCandlesA3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneFloorCandlesB3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneNorthLamp3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneSouthLamp3D,1
|
||||
invoke DrawFortuneFixedProp3D,OFFSET fortuneSeer3D,0
|
||||
ret
|
||||
RenderFortuneFurnishings3D ENDP
|
||||
|
||||
FortuneLightAt3D PROC uses ebx esi edi wx:DWORD,wy:DWORD,wz:DWORD
|
||||
local best:DWORD
|
||||
mov best,0
|
||||
xor esi,esi
|
||||
fortune_light_source:
|
||||
mov eax,wx
|
||||
sub eax,fortuneLights3D[esi]
|
||||
imul eax,eax
|
||||
mov ebx,eax
|
||||
mov eax,wy
|
||||
sub eax,fortuneLights3D[esi+4]
|
||||
imul eax,eax
|
||||
add ebx,eax
|
||||
mov eax,wz
|
||||
sub eax,fortuneLights3D[esi+8]
|
||||
imul eax,eax
|
||||
add eax,ebx
|
||||
cmp eax,900
|
||||
jae fortune_light_source_next
|
||||
neg eax
|
||||
add eax,900
|
||||
imul eax,fortuneLights3D[esi+12]
|
||||
xor edx,edx
|
||||
mov ecx,900
|
||||
div ecx
|
||||
cmp eax,best
|
||||
jbe fortune_light_source_next
|
||||
mov best,eax
|
||||
fortune_light_source_next:
|
||||
add esi,16
|
||||
cmp esi,FORTUNE_LIGHT_COUNT*16
|
||||
jb fortune_light_source
|
||||
mov eax,best
|
||||
ret
|
||||
FortuneLightAt3D ENDP
|
||||
|
||||
ApplyFortuneLighting3D PROC uses ebx esi edi
|
||||
local row:DWORD,column:DWORD,planeDepth:DWORD,depth:DWORD
|
||||
local wx:DWORD,wy:DWORD,wz:DWORD,light:DWORD
|
||||
local gainR:DWORD,gainG:DWORD,gainB:DWORD,subRow:DWORD,subColumn:DWORD
|
||||
mov row,0
|
||||
fortune_light_row:
|
||||
mov eax,row
|
||||
cmp eax,viewHorizon3D
|
||||
jge fortune_light_floor
|
||||
mov ecx,viewHorizon3D
|
||||
sub ecx,eax
|
||||
mov eax,viewCeiling3D
|
||||
sub eax,viewEye3D
|
||||
jmp fortune_light_plane
|
||||
fortune_light_floor:
|
||||
sub eax,viewHorizon3D
|
||||
inc eax
|
||||
mov ecx,eax
|
||||
mov eax,viewEye3D
|
||||
fortune_light_plane:
|
||||
imul eax,SCREEN_H
|
||||
cdq
|
||||
idiv ecx
|
||||
mov planeDepth,eax
|
||||
mov column,0
|
||||
fortune_light_column:
|
||||
imul edi,row,SCREEN_W
|
||||
add edi,column
|
||||
mov eax,cityDepthBuffer3D[edi*4]
|
||||
cmp eax,planeDepth
|
||||
jle @F
|
||||
mov eax,planeDepth
|
||||
@@:
|
||||
; Limit the reconstructed sample at the horizon before squaring distances.
|
||||
cmp eax,16*FP_1
|
||||
jle @F
|
||||
mov eax,16*FP_1
|
||||
@@:
|
||||
mov depth,eax
|
||||
mov ebx,column
|
||||
imul cityRayX3D[ebx*4]
|
||||
shrd eax,edx,16
|
||||
add eax,playerX
|
||||
sar eax,12
|
||||
mov wx,eax
|
||||
mov eax,depth
|
||||
imul cityRayY3D[ebx*4]
|
||||
shrd eax,edx,16
|
||||
add eax,playerY
|
||||
sar eax,12
|
||||
mov wy,eax
|
||||
mov eax,viewHorizon3D
|
||||
sub eax,row
|
||||
imul eax,depth
|
||||
cdq
|
||||
mov ecx,SCREEN_H
|
||||
idiv ecx
|
||||
add eax,viewEye3D
|
||||
sar eax,12
|
||||
mov wz,eax
|
||||
invoke FortuneLightAt3D,wx,wy,wz
|
||||
mov light,eax
|
||||
mov ebx,eax
|
||||
xor edx,edx
|
||||
mov ecx,3
|
||||
div ecx
|
||||
add eax,48
|
||||
mov gainR,eax
|
||||
imul eax,ebx,3
|
||||
shr eax,2
|
||||
add eax,64
|
||||
mov gainG,eax
|
||||
add ebx,82
|
||||
mov gainB,ebx
|
||||
mov subRow,0
|
||||
fortune_light_block_row:
|
||||
mov subColumn,0
|
||||
fortune_light_pixel:
|
||||
imul esi,subRow,SCREEN_W
|
||||
add esi,subColumn
|
||||
add esi,edi
|
||||
mov ebx,screenBuffer[esi*4]
|
||||
; Preserve the small blue-white flame cores only beside an actual light.
|
||||
cmp light,100
|
||||
jb fortune_light_shade
|
||||
cmp bl,210
|
||||
jb fortune_light_shade
|
||||
cmp bh,180
|
||||
jae fortune_light_pixel_next
|
||||
fortune_light_shade:
|
||||
movzx eax,bl
|
||||
imul eax,gainB
|
||||
shr eax,8
|
||||
mov ecx,eax
|
||||
movzx eax,bh
|
||||
imul eax,gainG
|
||||
shr eax,8
|
||||
shl eax,8
|
||||
or ecx,eax
|
||||
mov eax,ebx
|
||||
shr eax,16
|
||||
and eax,255
|
||||
imul eax,gainR
|
||||
shr eax,8
|
||||
shl eax,16
|
||||
or eax,ecx
|
||||
mov screenBuffer[esi*4],eax
|
||||
fortune_light_pixel_next:
|
||||
inc subColumn
|
||||
cmp subColumn,2
|
||||
jb fortune_light_pixel
|
||||
inc subRow
|
||||
cmp subRow,2
|
||||
jb fortune_light_block_row
|
||||
add column,2
|
||||
cmp column,SCREEN_W
|
||||
jb fortune_light_column
|
||||
add row,2
|
||||
cmp row,SCREEN_H
|
||||
jb fortune_light_row
|
||||
ret
|
||||
ApplyFortuneLighting3D ENDP
|
||||
|
||||
RenderFortuneRoom3D PROC
|
||||
call RenderFortuneFurnishings3D
|
||||
call ApplyFortuneLighting3D
|
||||
ret
|
||||
RenderFortuneRoom3D ENDP
|
||||
|
||||
FortuneCanWalk3D PROC px:DWORD,py:DWORD
|
||||
cmp px,FP_1*1485/100
|
||||
ja fortune_walk_north
|
||||
cmp py,FP_1*98/10
|
||||
jb fortune_walk_north
|
||||
cmp py,FP_1*118/10
|
||||
jbe fortune_walk_block
|
||||
fortune_walk_north:
|
||||
cmp py,FP_1*1005/100
|
||||
ja @F
|
||||
cmp px,FP_1*146/10
|
||||
jb @F
|
||||
cmp px,FP_1*1565/100
|
||||
jbe fortune_walk_block
|
||||
@@:
|
||||
cmp py,FP_1*965/100
|
||||
ja fortune_walk_south
|
||||
cmp px,15*FP_1
|
||||
jb fortune_walk_block
|
||||
cmp px,FP_1*155/10
|
||||
jb fortune_walk_south
|
||||
cmp px,FP_1*171/10
|
||||
jbe fortune_walk_block
|
||||
fortune_walk_south:
|
||||
cmp px,FP_1*172/10
|
||||
jb @F
|
||||
cmp py,FP_1*122/10
|
||||
jae fortune_walk_block
|
||||
@@:
|
||||
cmp py,FP_1*1175/100
|
||||
jb fortune_walk_clear
|
||||
cmp px,FP_1*146/10
|
||||
jb fortune_walk_block
|
||||
cmp px,15*FP_1
|
||||
jb @F
|
||||
cmp px,FP_1*158/10
|
||||
jbe fortune_walk_block
|
||||
@@:
|
||||
cmp py,FP_1*121/10
|
||||
jae fortune_walk_block
|
||||
fortune_walk_clear:
|
||||
mov eax,1
|
||||
ret
|
||||
fortune_walk_block:
|
||||
xor eax,eax
|
||||
ret
|
||||
FortuneCanWalk3D ENDP
|
||||
|
||||
RenderFortuneStairs3D PROC uses ebx esi
|
||||
local x0:DWORD,x1:DWORD,z0:DWORD,z1:DWORD
|
||||
; A west-facing descent: the player controls every step, with level landings.
|
||||
xor esi,esi
|
||||
fortune_stair_step:
|
||||
imul eax,esi,FP_1/3
|
||||
add eax,19*FP_1
|
||||
mov x0,eax
|
||||
add eax,FP_1/3
|
||||
mov x1,eax
|
||||
imul eax,esi,FP_1/16
|
||||
mov z0,eax
|
||||
add eax,FP_1/16
|
||||
mov z1,eax
|
||||
invoke DrawCityHorizontal3D,x0,x1,9*FP_1,11*FP_1,z0,134
|
||||
invoke DrawCityVertical3D,0,x1,9*FP_1,11*FP_1,z0,z1,132
|
||||
inc esi
|
||||
cmp esi,24
|
||||
jb fortune_stair_step
|
||||
invoke DrawCityHorizontal3D,27*FP_1,31*FP_1,9*FP_1,11*FP_1,FP_1*3/2,134
|
||||
invoke DrawCityHorizontal3D,16*FP_1,19*FP_1,9*FP_1,11*FP_1,0,134
|
||||
ret
|
||||
RenderFortuneStairs3D ENDP
|
||||
|
||||
RenderFortuneViews3D PROC
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je fortune_views_stairs
|
||||
cmp hallLevel3D,3
|
||||
je fortune_views_room
|
||||
cmp hallLevel3D,0
|
||||
jne fortune_views_done
|
||||
cmp fortunePassageTime3D,280
|
||||
jbe fortune_views_done
|
||||
cmp playerX,FP_1*14/10
|
||||
jbe fortune_views_done
|
||||
invoke DrawInteriorView3D,0,FP_1*138/100,9*FP_1,11*FP_1,0,FP_1*23/10,28*FP_1,0,FP_1*3/2,OFFSET fortuneStairMap3D,MODE_STAIRS,0,134,3
|
||||
ret
|
||||
fortune_views_room:
|
||||
cmp playerX,FP_1*175/10
|
||||
jae fortune_views_done
|
||||
invoke DrawInteriorView3D,0,FP_1*174/10,10*FP_1,12*FP_1,0,FP_1*5/2,0,-FP_1,0,OFFSET fortuneStairMap3D,MODE_STAIRS,3,134,3
|
||||
ret
|
||||
fortune_views_stairs:
|
||||
cmp playerX,FP_1*2955/100
|
||||
jae @F
|
||||
invoke DrawInteriorView3D,0,FP_1*295/10,9*FP_1,11*FP_1,FP_1*3/2,4*FP_1,-28*FP_1,0,-FP_1*3/2,OFFSET casinoMap,MODE_WALK,0,100,0
|
||||
@@:
|
||||
cmp playerX,FP_1*172/10
|
||||
jbe fortune_views_done
|
||||
invoke DrawInteriorView3D,0,FP_1*173/10,9*FP_1,11*FP_1,0,FP_1*5/2,0,FP_1,0,OFFSET fortuneRoomMap3D,MODE_WALK,3,134,0
|
||||
fortune_views_done:
|
||||
ret
|
||||
RenderFortuneViews3D ENDP
|
||||
67
src_graphics/fortune_room_data.inc
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
; Secret tarot chamber. Dialogue deliberately uses the existing unavailable modal.
|
||||
UpdateFortunePassage3D PROTO
|
||||
RenderFortuneEntrance3D PROTO
|
||||
RenderFortuneRoom3D PROTO
|
||||
RenderFortuneStairs3D PROTO
|
||||
RenderFortuneViews3D PROTO
|
||||
CheckFortuneInteractions3D PROTO
|
||||
TryFortuneInteraction3D PROTO
|
||||
FortuneCanWalk3D PROTO :DWORD,:DWORD
|
||||
FortuneEntranceCanWalk3D PROTO :DWORD,:DWORD
|
||||
LoadFortuneArt3D PROTO
|
||||
FreeFortuneArt3D PROTO
|
||||
ResolveFortuneMaterial3D PROTO :DWORD
|
||||
INTERACT_SECRET_LAMP equ 45
|
||||
INTERACT_FORTUNE equ 46
|
||||
.data
|
||||
fortunePassageTarget3D dd 0
|
||||
fortunePassageTime3D dd 0
|
||||
fortunePassageClock3D dd -1
|
||||
fortuneArt3D AnimPack3D <>
|
||||
fortuneMaterialArt3D AnimPack3D <>
|
||||
fortuneArtPath3D db "assets\animations\hall\fortune-room-v3.bin",0
|
||||
fortuneMaterialPath3D db "assets\animations\hall\fortune-materials-v3.bin",0
|
||||
fortuneLampPull3D db 69,58,32,207,206,210,223,205,211,210,220,32,212,206,205,192,208,220,0
|
||||
fortuneLampReturn3D db 69,58,32,194,197,208,205,211,210,220,32,212,206,205,192,208,220,0
|
||||
fortuneTalk3D db 69,58,32,207,206,195,206,194,206,208,200,210,220,32,209,32,195,192,196,192,203,202,206,201,0
|
||||
fortuneStairHint3D db 210,192,201,205,192,223,32,203,197,209,210,205,200,214,192,0
|
||||
fortuneExitHint3D db 203,197,209,210,205,200,214,192,32,194,32,202,192,199,200,205,206,0
|
||||
fortuneSeer3D CityProp3D <FP_1*141/10,11*FP_1,2*FP_1,FP_1*19/10,0,OFFSET fortuneArt3D>
|
||||
fortuneShelf3D CityProp3D <FP_1*141/10,FP_1*932/100,FP_1*15/10,FP_1*23/10,17,OFFSET fortuneArt3D>
|
||||
fortuneTapestry3D CityProp3D <FP_1*1308/100,11*FP_1,4*FP_1,FP_1*28/10,16,OFFSET fortuneArt3D>
|
||||
fortuneOrb3D CityProp3D <FP_1*146/10,FP_1*126/10,FP_1*6/10,FP_1,5,OFFSET fortuneArt3D,FP_1*6/10>
|
||||
fortuneBench3D CityProp3D <FP_1*165/10,FP_1*1265/100,FP_1*22/10,FP_1*12/10,6,OFFSET fortuneArt3D>
|
||||
fortuneMirror3D CityProp3D <FP_1*163/10,FP_1*925/100,FP_1*11/10,2*FP_1,7,OFFSET fortuneArt3D>
|
||||
fortuneCandleA3D CityProp3D <FP_1*135/10,FP_1*951/100,FP_1*6/10,FP_1*17/10,4,OFFSET fortuneArt3D>
|
||||
fortuneCandleB3D CityProp3D <FP_1*135/10,FP_1*122/10,FP_1*6/10,FP_1*17/10,4,OFFSET fortuneArt3D>
|
||||
fortuneCards3D CityProp3D <FP_1*173/10,FP_1*904/100,FP_1*8/10,FP_1*8/10,8,OFFSET fortuneArt3D,FP_1*13/10>
|
||||
fortuneScrolls3D CityProp3D <FP_1*1768/100,FP_1*1255/100,FP_1*7/10,FP_1*4/10,9,OFFSET fortuneArt3D,FP_1*205/100>
|
||||
fortuneHerbs3D CityProp3D <FP_1*138/10,FP_1*1296/100,FP_1*5/10,FP_1,10,OFFSET fortuneArt3D,FP_1*12/10>
|
||||
fortuneHerbsB3D CityProp3D <FP_1*165/10,FP_1*1296/100,FP_1*5/10,FP_1,10,OFFSET fortuneArt3D,FP_1*14/10>
|
||||
fortuneHerbsC3D CityProp3D <FP_1*156/10,FP_1*1296/100,FP_1*4/10,FP_1*7/10,10,OFFSET fortuneArt3D,FP_1*17/10>
|
||||
fortuneHanging3D CityProp3D <FP_1*154/10,FP_1*117/10,FP_1*4/10,FP_1*6/10,11,OFFSET fortuneArt3D,FP_1*19/10>
|
||||
fortunePotions3D CityProp3D <FP_1*177/10,FP_1*1255/100,FP_1*11/10,FP_1*19/10,12,OFFSET fortuneArt3D>
|
||||
fortuneBooks3D CityProp3D <FP_1*147/10,FP_1*127/10,FP_1*18/10,FP_1*13/10,18,OFFSET fortuneArt3D>
|
||||
fortuneRelics3D CityProp3D <FP_1*1304/100,FP_1*935/100,FP_1*4/10,FP_1*7/10,14,OFFSET fortuneArt3D,FP_1*16/10>
|
||||
fortuneChest3D CityProp3D <FP_1*166/10,FP_1*124/10,FP_1*11/10,FP_1,21,OFFSET fortuneArt3D>
|
||||
fortuneBookPileA3D CityProp3D <FP_1*151/10,FP_1*98/10,FP_1,FP_1*7/10,19,OFFSET fortuneArt3D>
|
||||
fortuneBookPileB3D CityProp3D <FP_1*158/10,FP_1*122/10,FP_1*9/10,FP_1*6/10,19,OFFSET fortuneArt3D>
|
||||
fortuneCauldron3D CityProp3D <FP_1*138/10,FP_1*121/10,FP_1*12/10,FP_1*11/10,20,OFFSET fortuneArt3D>
|
||||
fortuneTray3D CityProp3D <FP_1*171/10,FP_1*123/10,FP_1,FP_1*5/10,22,OFFSET fortuneArt3D>
|
||||
fortuneFloorCandlesA3D CityProp3D <FP_1*154/10,FP_1*12,FP_1*7/10,FP_1*7/10,23,OFFSET fortuneArt3D>
|
||||
fortuneFloorCandlesB3D CityProp3D <FP_1*166/10,FP_1*96/10,FP_1*6/10,FP_1*6/10,23,OFFSET fortuneArt3D>
|
||||
fortuneNorthLamp3D CityProp3D <FP_1*153/10,FP_1*914/100,FP_1*4/10,FP_1*8/10,11,OFFSET fortuneArt3D,FP_1*15/10>
|
||||
fortuneSouthLamp3D CityProp3D <FP_1*146/10,FP_1*1286/100,FP_1*4/10,FP_1*8/10,11,OFFSET fortuneArt3D,FP_1*15/10>
|
||||
; Point lights use sixteenth-metre coordinates: X,Y,Z,power. No screen-space halos.
|
||||
fortuneLights3D dd 216,152,26,174
|
||||
dd 216,195,26,174
|
||||
dd 226,187,20,174
|
||||
dd 226,167,18,145
|
||||
dd 246,187,34,174
|
||||
dd 283,201,23,125
|
||||
dd 245,148,29,160
|
||||
dd 234,203,29,155
|
||||
dd 246,192,6,155
|
||||
dd 266,154,5,155
|
||||
FORTUNE_LIGHT_COUNT equ ($-fortuneLights3D)/16
|
||||
.code
|
||||
|
|
@ -159,8 +159,6 @@ barTime3D dd 0
|
|||
barPhase3D dd 0
|
||||
barSelection3D dd 0
|
||||
barPrice3D dd 100,200,0
|
||||
barStrength3D dd 20000,35000,0
|
||||
barIntoxication3D dd 0
|
||||
stairsTime3D dd 0
|
||||
stairsReturnX3D dd 0
|
||||
stairsReturnY3D dd 0
|
||||
|
|
@ -168,5 +166,6 @@ 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\fortune_room_data.inc
|
||||
include src_graphics\cabaret_data.inc
|
||||
.code
|
||||
|
|
|
|||
|
|
@ -1,6 +1,16 @@
|
|||
; 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,131
|
||||
jne @F
|
||||
mov eax,basementDoorArt3D.pixels
|
||||
test eax,eax
|
||||
jz upper_material_missing
|
||||
add eax,17*256*512*4
|
||||
mov ecx,256
|
||||
mov edx,512
|
||||
ret
|
||||
@@:
|
||||
cmp material,129
|
||||
jne @F
|
||||
mov eax,upperPortal3D.frame
|
||||
|
|
@ -20,15 +30,23 @@ ResolveUpperMaterial3D PROC material:DWORD
|
|||
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
|
||||
jne upper_material_not_door
|
||||
cmp basementDoorArt3D.pixels,0
|
||||
je upper_material_missing
|
||||
imul eax,basementWheelTime3D,16
|
||||
xor edx,edx
|
||||
mov ecx,700
|
||||
div ecx
|
||||
cmp eax,16
|
||||
jbe @F
|
||||
mov eax,16
|
||||
@@:
|
||||
imul eax,256*512*4
|
||||
add eax,basementDoorArt3D.pixels
|
||||
mov ecx,256
|
||||
mov edx,512
|
||||
ret
|
||||
upper_material_not_door:
|
||||
cmp material,126
|
||||
jne @F
|
||||
mov eax,OFFSET upperFeltPixels3D
|
||||
|
|
@ -177,22 +195,8 @@ 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
|
||||
call UpdateBasementDoor3D
|
||||
call UpdateFortunePassage3D
|
||||
interior_travel_check:
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je interior_travel_stairs
|
||||
|
|
@ -202,6 +206,24 @@ interior_travel_check:
|
|||
je interior_travel_bunker
|
||||
cmp hallLevel3D,2
|
||||
je interior_travel_upper
|
||||
cmp hallLevel3D,3
|
||||
je interior_travel_fortune_room
|
||||
cmp fortunePassageTime3D,1400
|
||||
jne interior_travel_east
|
||||
cmp playerX,FP_1*14/10
|
||||
ja interior_travel_east
|
||||
cmp playerY,FP_1*92/10
|
||||
jb interior_travel_east
|
||||
cmp playerY,FP_1*108/10
|
||||
ja interior_travel_east
|
||||
add playerX,28*FP_1
|
||||
mov stairRoute3D,3
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov worldMap3D,OFFSET fortuneStairMap3D
|
||||
mov floorTexture3D,134
|
||||
call UpdateStairHeight3D
|
||||
ret
|
||||
interior_travel_east:
|
||||
cmp playerY,20*FP_1
|
||||
jb interior_travel_door
|
||||
cmp playerY,22*FP_1
|
||||
|
|
@ -213,7 +235,7 @@ interior_travel_check:
|
|||
interior_travel_door:
|
||||
cmp basementDoorOpen3D,FP_1*9/10
|
||||
jb interior_travel_done
|
||||
cmp playerX,FP_1*286/10
|
||||
cmp playerX,29*FP_1
|
||||
jb interior_travel_done
|
||||
cmp playerY,FP_1*99/10
|
||||
jb interior_travel_done
|
||||
|
|
@ -250,7 +272,23 @@ interior_travel_upper:
|
|||
mov floorTexture3D,85
|
||||
call UpdateStairHeight3D
|
||||
ret
|
||||
interior_travel_fortune_room:
|
||||
cmp playerX,FP_1*175/10
|
||||
jb interior_travel_done
|
||||
cmp playerY,10*FP_1
|
||||
jb interior_travel_done
|
||||
cmp playerY,12*FP_1
|
||||
jae interior_travel_done
|
||||
mov stairRoute3D,3
|
||||
sub playerY,FP_1
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov worldMap3D,OFFSET fortuneStairMap3D
|
||||
mov floorTexture3D,134
|
||||
call UpdateStairHeight3D
|
||||
ret
|
||||
interior_travel_stairs:
|
||||
cmp stairRoute3D,3
|
||||
je interior_fortune_thresholds
|
||||
cmp stairRoute3D,1
|
||||
jne interior_upper_thresholds
|
||||
cmp playerX,FP_1*31/10
|
||||
|
|
@ -284,6 +322,22 @@ interior_exit_upper_bottom:
|
|||
mov worldMap3D,OFFSET casinoMap
|
||||
add playerX,27*FP_1
|
||||
mov floorTexture3D,100
|
||||
jmp interior_finish
|
||||
interior_fortune_thresholds:
|
||||
cmp playerX,FP_1*2955/100
|
||||
jae interior_fortune_top
|
||||
cmp playerX,FP_1*172/10
|
||||
ja interior_travel_done
|
||||
mov hallLevel3D,3
|
||||
add playerY,FP_1
|
||||
mov worldMap3D,OFFSET fortuneRoomMap3D
|
||||
mov floorTexture3D,134
|
||||
jmp interior_finish
|
||||
interior_fortune_top:
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
sub playerX,28*FP_1
|
||||
mov floorTexture3D,100
|
||||
interior_finish:
|
||||
mov engineMode,MODE_WALK
|
||||
mov interactTargetId,0
|
||||
|
|
@ -298,6 +352,22 @@ UpdateStairHeight3D PROC
|
|||
jne interior_height_done
|
||||
mov viewHorizon3D,HALF_H
|
||||
mov viewCeiling3D,FP_1*11/2
|
||||
cmp stairRoute3D,3
|
||||
jne @F
|
||||
mov viewCeiling3D,4*FP_1
|
||||
mov eax,playerX
|
||||
sub eax,19*FP_1
|
||||
jle interior_height_zero
|
||||
cmp eax,8*FP_1
|
||||
jle interior_height_fortune
|
||||
mov eax,8*FP_1
|
||||
interior_height_fortune:
|
||||
imul eax,3
|
||||
cdq
|
||||
mov ecx,16
|
||||
idiv ecx
|
||||
jmp interior_height_store
|
||||
@@:
|
||||
cmp stairRoute3D,1
|
||||
jne interior_height_upper
|
||||
mov viewCeiling3D,4*FP_1
|
||||
|
|
@ -340,6 +410,7 @@ UpdateInteriorEye3D PROC
|
|||
je UpdateStairHeight3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne interior_eye_done
|
||||
mov viewHorizon3D,HALF_H
|
||||
mov viewEye3D,HALL_STANDING_EYE
|
||||
mov viewCeiling3D,FP_1*5/2
|
||||
interior_eye_done:
|
||||
|
|
@ -362,7 +433,7 @@ interior_check_steel:
|
|||
jb interior_door_clear
|
||||
cmp py,12*FP_1
|
||||
jae interior_door_clear
|
||||
cmp px,FP_1*2835/100
|
||||
cmp px,FP_1*2875/100
|
||||
jb interior_door_clear
|
||||
cmp py,FP_1*96/10
|
||||
jb interior_portal_blocked
|
||||
|
|
@ -379,15 +450,11 @@ interior_door_clear:
|
|||
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
|
||||
; Same east alcove, with a deeper reveal and a stationary iron frame.
|
||||
invoke DrawCityBoxSides3D,FP_1*284/10,29*FP_1,9*FP_1,FP_1*975/100,0,FP_1*11/5,102
|
||||
invoke DrawCityBoxSides3D,FP_1*284/10,29*FP_1,FP_1*1125/100,12*FP_1,0,FP_1*11/5,102
|
||||
invoke DrawCityBoxSides3D,FP_1*284/10,29*FP_1,FP_1*975/100,FP_1*1125/100,FP_1*19/10,FP_1*11/5,102
|
||||
invoke DrawCityVertical3D,0,FP_1*2838/100,9*FP_1,12*FP_1,-FP_1/10,FP_1*21/10,131
|
||||
; 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
|
||||
|
|
@ -398,6 +465,11 @@ RenderHallPortals3D ENDP
|
|||
|
||||
RenderWalkableStairs3D PROC uses ebx esi edi
|
||||
local treadStart:DWORD,treadEnd:DWORD,z0:DWORD,z1:DWORD
|
||||
cmp stairRoute3D,3
|
||||
jne @F
|
||||
call RenderFortuneStairs3D
|
||||
ret
|
||||
@@:
|
||||
cmp stairRoute3D,1
|
||||
jne interior_render_upper_steps
|
||||
; East-facing descent, then a level northward turn into the old bunker.
|
||||
|
|
@ -416,7 +488,7 @@ interior_concrete_step:
|
|||
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
|
||||
invoke DrawCityVertical3D,0,treadStart,20*FP_1,23*FP_1,z1,z0,92
|
||||
inc esi
|
||||
cmp esi,24
|
||||
jb interior_concrete_step
|
||||
|
|
@ -539,17 +611,22 @@ CheckUpperInteractions3D PROC
|
|||
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
|
||||
jnz upper_check_left_release
|
||||
invoke HallTarget3D,FP_1*91/10,FP_1*226/10,INTERACT_UPPER_LEFT,OFFSET upperLeftPrompt3D
|
||||
@@:
|
||||
jmp upper_check_right
|
||||
upper_check_left_release:
|
||||
invoke HallTarget3D,FP_1*91/10,FP_1*226/10,INTERACT_UPPER_LEFT,OFFSET upperLeftRelease3D
|
||||
upper_check_right:
|
||||
test upperLeverMask3D,2
|
||||
jnz upper_check_bar
|
||||
jnz upper_check_right_release
|
||||
invoke HallTarget3D,FP_1*119/10,FP_1*226/10,INTERACT_UPPER_RIGHT,OFFSET upperRightPrompt3D
|
||||
jmp upper_check_bar
|
||||
jmp upper_check_portal
|
||||
upper_check_right_release:
|
||||
invoke HallTarget3D,FP_1*119/10,FP_1*226/10,INTERACT_UPPER_RIGHT,OFFSET upperRightRelease3D
|
||||
upper_check_portal:
|
||||
cmp upperPortalReady3D,1
|
||||
jne upper_check_bar
|
||||
invoke HallTarget3D,FP_1*105/10,FP_1*237/10,INTERACT_HELL_TOWER,OFFSET upperPortalPrompt3D
|
||||
upper_check_bar:
|
||||
cmp playerX,10*FP_1
|
||||
|
|
@ -718,13 +795,13 @@ TryUpperInteraction3D PROC
|
|||
jne upper_interaction_none
|
||||
cmp interactTargetId,INTERACT_UPPER_LEFT
|
||||
jne @F
|
||||
or upperLeverMask3D,1
|
||||
jmp upper_interaction_handled
|
||||
xor upperLeverMask3D,1
|
||||
jmp upper_interaction_lever
|
||||
@@:
|
||||
cmp interactTargetId,INTERACT_UPPER_RIGHT
|
||||
jne @F
|
||||
or upperLeverMask3D,2
|
||||
jmp upper_interaction_handled
|
||||
xor upperLeverMask3D,2
|
||||
jmp upper_interaction_lever
|
||||
@@:
|
||||
cmp interactTargetId,INTERACT_HELL_TOWER
|
||||
jne upper_interaction_none
|
||||
|
|
@ -733,6 +810,10 @@ TryUpperInteraction3D PROC
|
|||
mov upperPortalEntered3D,1
|
||||
call InitHellTower3D
|
||||
jmp upper_interaction_handled
|
||||
upper_interaction_lever:
|
||||
; Do not wait for the next animation tick to disable both entry paths.
|
||||
mov upperPortalReady3D,0
|
||||
jmp upper_interaction_handled
|
||||
upper_interaction_locked:
|
||||
invoke CopyString3D,OFFSET upperPortalLocked3D,OFFSET flashMessage
|
||||
mov flashTimer,120
|
||||
|
|
@ -775,24 +856,36 @@ UpdateUpperEffects3D PROC uses ebx esi
|
|||
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
|
||||
@@:
|
||||
test upperLeverMask3D,ebx
|
||||
jz upper_effect_release
|
||||
add eax,upperLeverTime3D[esi*4]
|
||||
cmp eax,600
|
||||
jbe @F
|
||||
mov eax,600
|
||||
@@:
|
||||
jmp upper_effect_store
|
||||
upper_effect_release:
|
||||
mov ecx,upperLeverTime3D[esi*4]
|
||||
sub ecx,eax
|
||||
jae @F
|
||||
xor ecx,ecx
|
||||
@@:
|
||||
mov eax,ecx
|
||||
upper_effect_store:
|
||||
mov upperLeverTime3D[esi*4],eax
|
||||
upper_effect_next:
|
||||
shl ebx,1
|
||||
inc esi
|
||||
cmp esi,2
|
||||
jb upper_effect_lever
|
||||
mov upperPortalReady3D,0
|
||||
cmp upperLeverMask3D,3
|
||||
jne upper_effects_done
|
||||
cmp upperLeverTime3D,600
|
||||
jne upper_effects_done
|
||||
cmp upperLeverTime3D[4],600
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ UpdateStairHeight3D PROTO
|
|||
RenderWalkableStairs3D PROTO
|
||||
RenderUpperLounge3D PROTO
|
||||
RenderHallPortals3D PROTO
|
||||
UpdateBasementDoor3D PROTO
|
||||
DrawBasementLeaf3D PROTO
|
||||
RenderStairDoor3D PROTO
|
||||
StairDoorCanWalk3D PROTO :DWORD,:DWORD
|
||||
CheckUpperInteractions3D PROTO
|
||||
InteriorDoorCanWalk3D PROTO :DWORD,:DWORD
|
||||
UpperCanWalk3D PROTO :DWORD,:DWORD
|
||||
|
|
@ -24,6 +28,10 @@ INTERACT_UPPER_RIGHT equ 44
|
|||
stairRoute3D dd 0
|
||||
basementDoorTarget3D dd 0
|
||||
basementDoorOpen3D dd 0
|
||||
basementWheelTime3D dd 0
|
||||
basementDoorHold3D dd 0
|
||||
basementDoorArt3D AnimPack3D <>
|
||||
basementDoorPath3D db "assets\animations\hall\basement-door-v2.bin",0
|
||||
interiorDoorClock3D dd -1
|
||||
portalRendering3D dd 0
|
||||
portalPixelsWritten3D dd 0
|
||||
|
|
@ -32,6 +40,8 @@ upperPortalReady3D dd 0
|
|||
upperPortalEntered3D dd 0
|
||||
upperEffectClock3D dd -1
|
||||
upperLeverTime3D dd 0,0
|
||||
upperLeftRelease3D db 69,58,32,194,197,208,205,211,210,220,32,203,197,194,219,201,32,208,219,215,192,195,0
|
||||
upperRightRelease3D db 69,58,32,194,197,208,205,211,210,220,32,207,208,192,194,219,201,32,208,219,215,192,195,0
|
||||
upperGamesArt3D AnimPack3D <>
|
||||
upperPortalArt3D AnimPack3D <>
|
||||
upperLeverArt3D AnimPack3D <>
|
||||
|
|
|
|||
|
|
@ -160,4 +160,68 @@ basementStairMap3D db 92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,92,9
|
|||
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
|
||||
fortuneRoomMap3D db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,0,0,0,0,0,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,0,0,0,0,0,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,0,0,0,0,0,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,0,0,0,0,0,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
fortuneStairMap3D db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
db 132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132
|
||||
.code
|
||||
|
|
|
|||
|
|
@ -21,7 +21,15 @@ hall_plane_row:
|
|||
cmp hallLevel3D, 1
|
||||
jne @F
|
||||
mov material, 93
|
||||
@@:
|
||||
@@:
|
||||
cmp hallLevel3D,3
|
||||
je hall_plane_fortune_ceiling
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne hall_plane_distance
|
||||
cmp stairRoute3D,3
|
||||
jne hall_plane_distance
|
||||
hall_plane_fortune_ceiling:
|
||||
mov material,133
|
||||
jmp hall_plane_distance
|
||||
hall_plane_floor:
|
||||
sub eax, viewHorizon3D
|
||||
|
|
@ -122,6 +130,21 @@ hall_plane_sample:
|
|||
hall_plane_wood:
|
||||
mov ebx, 85
|
||||
hall_plane_choose_hd:
|
||||
cmp ebx,132
|
||||
jb hall_plane_other_hd
|
||||
cmp ebx,135
|
||||
ja hall_plane_other_hd
|
||||
mov ecx,fortuneMaterialArt3D.pixels
|
||||
test ecx,ecx
|
||||
jz hall_plane_fortune_fallback
|
||||
sub ebx,132
|
||||
shl ebx,18
|
||||
add ecx,ebx
|
||||
jmp hall_plane_hd_coordinates
|
||||
hall_plane_fortune_fallback:
|
||||
mov ebx,94
|
||||
mov material,ebx
|
||||
hall_plane_other_hd:
|
||||
cmp ebx,122
|
||||
jb hall_plane_existing_hd
|
||||
cmp ebx,125
|
||||
|
|
@ -313,6 +336,9 @@ InitHall3D PROC uses esi edi
|
|||
mov ecx, 256
|
||||
rep movsd
|
||||
mov hallLevel3D, 0
|
||||
mov fortunePassageTarget3D,0
|
||||
mov fortunePassageTime3D,0
|
||||
mov fortunePassageClock3D,-1
|
||||
mov worldMap3D, OFFSET casinoMap
|
||||
mov viewEye3D, HALL_STANDING_EYE
|
||||
mov viewCeiling3D, FP_1*5/2
|
||||
|
|
@ -346,6 +372,7 @@ InitHall3D ENDP
|
|||
|
||||
LoadHallAssets3D PROC
|
||||
call InitUpperSurfaces3D
|
||||
call LoadFortuneArt3D
|
||||
invoke LoadAnimPack3D, OFFSET upperMaterialArt3D, OFFSET upperMaterialPath3D
|
||||
invoke ValidateCityPack3D, OFFSET upperMaterialArt3D,256,256,4
|
||||
invoke LoadAnimPack3D,OFFSET upperGamesArt3D,OFFSET upperGamesPath3D
|
||||
|
|
@ -366,6 +393,12 @@ LoadHallAssets3D PROC
|
|||
invoke ValidateCityPack3D, OFFSET cabaretGuitarArt3D,160,256,4
|
||||
invoke LoadAnimPack3D, OFFSET npcGuestArt3D, OFFSET npcGuestPath3D
|
||||
invoke ValidateCityPack3D, OFFSET npcGuestArt3D,128,224,36
|
||||
invoke LoadAnimPack3D, OFFSET npcWalkArt3D, OFFSET npcWalkPath3D
|
||||
invoke ValidateCityPack3D, OFFSET npcWalkArt3D,128,224,48
|
||||
invoke LoadAnimPack3D, OFFSET npcWalkLeftArt3D, OFFSET npcWalkLeftPath3D
|
||||
invoke ValidateCityPack3D, OFFSET npcWalkLeftArt3D,128,224,48
|
||||
invoke LoadAnimPack3D, OFFSET basementDoorArt3D, OFFSET basementDoorPath3D
|
||||
invoke ValidateCityPack3D, OFFSET basementDoorArt3D,256,512,18
|
||||
call LoadWorldDressing3D
|
||||
call LoadScratchArt3D
|
||||
invoke LoadAnimPack3D, OFFSET hallCardFurniture3D, OFFSET hallCardFurniturePath3D
|
||||
|
|
@ -437,6 +470,7 @@ hall_load_sign:
|
|||
LoadHallAssets3D ENDP
|
||||
|
||||
FreeHallAssets3D PROC
|
||||
call FreeFortuneArt3D
|
||||
invoke FreeAnimPack3D, OFFSET upperMaterialArt3D
|
||||
invoke FreeAnimPack3D,OFFSET upperGamesArt3D
|
||||
invoke FreeAnimPack3D,OFFSET upperPortalArt3D
|
||||
|
|
@ -447,6 +481,9 @@ FreeHallAssets3D PROC
|
|||
invoke FreeAnimPack3D, OFFSET cabaretPianoArt3D
|
||||
invoke FreeAnimPack3D, OFFSET cabaretGuitarArt3D
|
||||
invoke FreeAnimPack3D, OFFSET npcGuestArt3D
|
||||
invoke FreeAnimPack3D, OFFSET npcWalkArt3D
|
||||
invoke FreeAnimPack3D, OFFSET npcWalkLeftArt3D
|
||||
invoke FreeAnimPack3D, OFFSET basementDoorArt3D
|
||||
call FreeWorldDressing3D
|
||||
call FreeNativeWheelArt3D
|
||||
call FreeNativeCoinArt3D
|
||||
|
|
@ -536,11 +573,21 @@ surface_missing:
|
|||
ret
|
||||
ResolveWorldSurface3D ENDP
|
||||
|
||||
RenderHall3D PROC uses ebx esi
|
||||
RenderHall3D PROC uses ebx esi
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je fortune_hall_stairs
|
||||
cmp hallLevel3D,3
|
||||
jne @F
|
||||
call RenderFortuneRoom3D
|
||||
call RenderInteriorViews3D
|
||||
ret
|
||||
@@:
|
||||
fortune_hall_stairs:
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne @F
|
||||
call RenderWalkableStairs3D
|
||||
call RenderInteriorViews3D
|
||||
call RenderStairDoor3D
|
||||
ret
|
||||
@@:
|
||||
cmp hallLevel3D,2
|
||||
|
|
@ -646,8 +693,10 @@ hall_render_lounge:
|
|||
invoke DrawCityObject3D,OFFSET scrStation3D,0
|
||||
call RenderHallDressing3D
|
||||
call RenderHallPortals3D
|
||||
call RenderFortuneEntrance3D
|
||||
call ApplyHallLighting3D
|
||||
call RenderInteriorViews3D
|
||||
call DrawBasementLeaf3D
|
||||
ret
|
||||
hall_render_bunker:
|
||||
invoke DrawCityBoxSides3D, 14*FP_1,20*FP_1,14*FP_1,15*FP_1,0,FP_1/2,96
|
||||
|
|
@ -711,6 +760,11 @@ hall_target_done:
|
|||
HallTarget3D ENDP
|
||||
|
||||
CheckHallInteractions3D PROC
|
||||
cmp hallLevel3D,3
|
||||
jne @F
|
||||
call CheckFortuneInteractions3D
|
||||
ret
|
||||
@@:
|
||||
cmp hallLevel3D,2
|
||||
jne @F
|
||||
call CheckUpperInteractions3D
|
||||
|
|
@ -733,6 +787,7 @@ hall_check_other:
|
|||
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
|
||||
call CheckFortuneInteractions3D
|
||||
ret
|
||||
hall_check_bunker:
|
||||
mov interactTargetId, 0
|
||||
|
|
@ -742,6 +797,9 @@ hall_check_bunker:
|
|||
CheckHallInteractions3D ENDP
|
||||
|
||||
TriggerHallInteraction3D PROC
|
||||
call TryFortuneInteraction3D
|
||||
test eax,eax
|
||||
jnz hall_trigger_handled
|
||||
call TryUpperInteraction3D
|
||||
test eax,eax
|
||||
jnz hall_trigger_handled
|
||||
|
|
@ -878,17 +936,6 @@ bar_animate:
|
|||
bar_after_drink:
|
||||
cmp barTime3D, 2400
|
||||
jb bar_handle_done
|
||||
mov ecx, barSelection3D
|
||||
mov eax, barStrength3D[ecx*4]
|
||||
test eax, eax
|
||||
jnz @F
|
||||
mov barIntoxication3D, 0
|
||||
@@:
|
||||
add barIntoxication3D, eax
|
||||
cmp barIntoxication3D, 60000
|
||||
jbe @F
|
||||
mov barIntoxication3D, 60000
|
||||
@@:
|
||||
mov barPhase3D, 0
|
||||
mov barTime3D, 0
|
||||
bar_handle_done:
|
||||
|
|
@ -1034,9 +1081,16 @@ city_world_guard_right:
|
|||
RenderCityGuards3D ENDP
|
||||
|
||||
CanWalkHall3D PROC uses esi ebx px:DWORD,py:DWORD
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je fortune_collision_stairs
|
||||
cmp hallLevel3D,3
|
||||
jne fortune_collision_stairs
|
||||
invoke FortuneCanWalk3D,px,py
|
||||
ret
|
||||
fortune_collision_stairs:
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne @F
|
||||
mov eax,1
|
||||
invoke StairDoorCanWalk3D,px,py
|
||||
ret
|
||||
@@:
|
||||
cmp hallLevel3D,2
|
||||
|
|
@ -1046,6 +1100,9 @@ CanWalkHall3D PROC uses esi ebx px:DWORD,py:DWORD
|
|||
@@:
|
||||
cmp hallLevel3D, 0
|
||||
jne hall_collision_bunker
|
||||
invoke FortuneEntranceCanWalk3D,px,py
|
||||
test eax,eax
|
||||
jz hall_collision_blocked
|
||||
invoke ScratchCanWalk3D,px,py
|
||||
test eax,eax
|
||||
jz hall_collision_blocked
|
||||
|
|
@ -1232,23 +1289,6 @@ UpdateHall3D PROC
|
|||
call UpdateInteriorTravel3D
|
||||
call UpdatePatrons3D
|
||||
call UpdateInteriorEye3D
|
||||
cmp barIntoxication3D, 0
|
||||
je hall_update_done
|
||||
mov eax, deltaTime3D
|
||||
imul eax, 2
|
||||
sub barIntoxication3D, eax
|
||||
jnc @F
|
||||
mov barIntoxication3D, 0
|
||||
@@:
|
||||
mov eax, animationClock3D
|
||||
shr eax, 3
|
||||
and eax, 511
|
||||
mov eax, tableSin[eax*4]
|
||||
imul barIntoxication3D
|
||||
shrd eax, edx, 16
|
||||
sar eax, 14
|
||||
add viewHorizon3D, eax
|
||||
hall_update_done:
|
||||
ret
|
||||
UpdateHall3D ENDP
|
||||
|
||||
|
|
@ -1347,6 +1387,8 @@ RenderPatrons3D ENDP
|
|||
include src_graphics\world_dressing.inc
|
||||
include src_graphics\terminal.inc
|
||||
include src_graphics\hall_expansion.inc
|
||||
include src_graphics\steel_door.inc
|
||||
include src_graphics\interior_portals.inc
|
||||
include src_graphics\fortune_room.inc
|
||||
include src_graphics\cabaret.inc
|
||||
include src_graphics\hall_lighting.inc
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ portal_mask_y:
|
|||
portal_mask_depth:
|
||||
cmp eax,2048
|
||||
jle portal_mask_next
|
||||
cmp eax,8*FP_1
|
||||
; The aperture is visible throughout the room, not just within eight metres.
|
||||
cmp eax,64*FP_1
|
||||
ja portal_mask_next
|
||||
cmp eax,zBuffer[ebx*4]
|
||||
ja portal_mask_next
|
||||
|
|
@ -101,6 +102,9 @@ portal_mask_next:
|
|||
cmp targetMode,MODE_STAIRS
|
||||
jne @F
|
||||
mov viewCeiling3D,FP_1*11/2
|
||||
cmp targetRoute,3
|
||||
jne @F
|
||||
mov viewCeiling3D,4*FP_1
|
||||
@@:
|
||||
mov eax,worldMap3D
|
||||
mov oldMap,eax
|
||||
|
|
@ -164,7 +168,7 @@ portal_compose_pixel:
|
|||
cmp eax,portalSourceDepth3D[edi]
|
||||
ja portal_restore_pixel
|
||||
; The rendered destination is already in screenBuffer at this exact ray.
|
||||
mov cityDepthBuffer3D[edi],eax
|
||||
; Keep destination depth, so the hinged leaf can occlude the continuation.
|
||||
inc portalPixelsWritten3D
|
||||
jmp portal_compose_next
|
||||
portal_restore_pixel:
|
||||
|
|
@ -194,21 +198,29 @@ RenderInteriorViews3D PROC
|
|||
cmp portalRendering3D,0
|
||||
jne interior_views_done
|
||||
mov portalPixelsWritten3D,0
|
||||
cmp hallLevel3D,3
|
||||
je RenderFortuneViews3D
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne @F
|
||||
cmp stairRoute3D,3
|
||||
je RenderFortuneViews3D
|
||||
@@:
|
||||
cmp engineMode,MODE_STAIRS
|
||||
je interior_views_stairs
|
||||
cmp hallLevel3D,2
|
||||
je interior_views_lounge
|
||||
cmp hallLevel3D,1
|
||||
je interior_views_bunker
|
||||
call RenderFortuneViews3D
|
||||
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
|
||||
cmp basementDoorOpen3D,0
|
||||
je interior_views_done
|
||||
cmp playerX,29*FP_1
|
||||
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
|
||||
invoke DrawInteriorView3D,0,FP_1*2895/100,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
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ npc_life_init:
|
|||
mov npcLife3D[ebx].pathCount,0
|
||||
mov npcLife3D[ebx].remainder,0
|
||||
mov npcLife3D[ebx].poseClock,0
|
||||
mov npcWalkDistance3D[esi*4],0
|
||||
mov npcWalkVelocityX3D[esi*4],0
|
||||
mov npcWalkVelocityY3D[esi*4],0
|
||||
mov eax,npcStartDest3D[esi*4]
|
||||
mov npcLife3D[ebx].destination,eax
|
||||
imul edi,esi,SIZEOF NpcMindEntity
|
||||
|
|
@ -215,10 +218,15 @@ NpcCanStep3D ENDP
|
|||
; Millisecond movement, carrying the remainder and consuming whole route cells.
|
||||
NpcMove3D PROC uses ebx esi edi slot:DWORD,milliseconds:DWORD
|
||||
local budget:DWORD,wx:DWORD,wy:DWORD,mind:DWORD,pathBase:DWORD
|
||||
local oldX:DWORD,oldY:DWORD
|
||||
imul ebx,slot,SIZEOF NpcLife3D
|
||||
imul eax,slot,SIZEOF NpcMindEntity
|
||||
lea esi,casinoNpcs3D[eax]
|
||||
mov mind,esi
|
||||
mov eax,[esi].NpcMindEntity.posX
|
||||
mov oldX,eax
|
||||
mov eax,[esi].NpcMindEntity.posY
|
||||
mov oldY,eax
|
||||
mov eax,slot
|
||||
imul eax,NPC_PATH_CAP*4
|
||||
add eax,OFFSET npcPaths3D
|
||||
|
|
@ -350,6 +358,35 @@ npc_move_blocked:
|
|||
mov npcLife3D[ebx].remaining,1000
|
||||
mov npcLife3D[ebx].pathCount,0
|
||||
npc_move_done:
|
||||
; Advance gait by successful displacement only, including partial blocked steps.
|
||||
mov esi,mind
|
||||
mov ebx,slot
|
||||
mov eax,[esi].NpcMindEntity.posX
|
||||
sub eax,oldX
|
||||
mov ecx,eax
|
||||
mov eax,[esi].NpcMindEntity.posY
|
||||
sub eax,oldY
|
||||
mov edi,eax
|
||||
or eax,ecx
|
||||
jz npc_move_no_stride
|
||||
mov npcWalkVelocityX3D[ebx*4],ecx
|
||||
mov npcWalkVelocityY3D[ebx*4],edi
|
||||
mov eax,ecx
|
||||
cdq
|
||||
xor eax,edx
|
||||
sub eax,edx
|
||||
mov ecx,eax
|
||||
mov eax,edi
|
||||
cdq
|
||||
xor eax,edx
|
||||
sub eax,edx
|
||||
add eax,ecx
|
||||
add eax,npcWalkDistance3D[ebx*4]
|
||||
xor edx,edx
|
||||
mov ecx,FP_1*3/4
|
||||
div ecx
|
||||
mov npcWalkDistance3D[ebx*4],edx
|
||||
npc_move_no_stride:
|
||||
ret
|
||||
NpcMove3D ENDP
|
||||
|
||||
|
|
@ -466,6 +503,7 @@ npc_draw_person:
|
|||
mov npcSprite3D.posY,eax
|
||||
mov npcSprite3D.worldW,FP_1*3/5
|
||||
mov npcSprite3D.worldH,FP_1*6/5
|
||||
mov npcSprite3D.groundZ,-FP_1*6/5*4/224
|
||||
mov npcSprite3D.asset,OFFSET npcGuestArt3D
|
||||
cmp npcGuestArt3D.pixels,0
|
||||
je npc_draw_next
|
||||
|
|
@ -483,13 +521,34 @@ npc_draw_person:
|
|||
npc_draw_walk:
|
||||
cmp npcLife3D[ebx].state,NPC_WALK
|
||||
jne npc_draw_sprite
|
||||
mov eax,npcLife3D[ebx].poseClock
|
||||
cmp npcWalkArt3D.pixels,0
|
||||
je npc_draw_sprite
|
||||
mov npcSprite3D.asset,OFFSET npcWalkArt3D
|
||||
mov npcSprite3D.groundZ,0
|
||||
mov eax,npcLife3D[ebx].identity
|
||||
mov eax,npcGuestAppearance3D[eax*4]
|
||||
shl eax,3
|
||||
mov npcSprite3D.frame,eax
|
||||
mov ebx,npcDrawOrder3D[esi*4]
|
||||
mov eax,npcWalkDistance3D[ebx*4]
|
||||
xor edx,edx
|
||||
mov ecx,180
|
||||
mov ecx,FP_1*3/32
|
||||
div ecx
|
||||
and eax,3
|
||||
inc eax
|
||||
and eax,7
|
||||
add npcSprite3D.frame,eax
|
||||
; Match the drawn facing to the actual camera-relative direction of travel.
|
||||
mov eax,npcWalkVelocityY3D[ebx*4]
|
||||
imul playerDirX
|
||||
shrd eax,edx,16
|
||||
mov edi,eax
|
||||
mov eax,npcWalkVelocityX3D[ebx*4]
|
||||
imul playerDirY
|
||||
shrd eax,edx,16
|
||||
sub edi,eax
|
||||
jns npc_draw_sprite
|
||||
cmp npcWalkLeftArt3D.pixels,0
|
||||
je npc_draw_sprite
|
||||
mov npcSprite3D.asset,OFFSET npcWalkLeftArt3D
|
||||
npc_draw_sprite:
|
||||
invoke DrawCityObject3D,OFFSET npcSprite3D,0
|
||||
npc_draw_next:
|
||||
|
|
|
|||
|
|
@ -46,6 +46,13 @@ npcLife3D NpcLife3D NPC_LIFE_COUNT dup(<>)
|
|||
npcLifeReady3D dd 0
|
||||
npcGuestArt3D AnimPack3D <>
|
||||
npcGuestPath3D db "assets\animations\hall\guest-cast-v1.bin",0
|
||||
npcWalkArt3D AnimPack3D <>
|
||||
npcWalkPath3D db "assets\animations\hall\guest-walk-v2.bin",0
|
||||
npcWalkLeftArt3D AnimPack3D <>
|
||||
npcWalkLeftPath3D db "assets\animations\hall\guest-walk-left-v2.bin",0
|
||||
npcWalkDistance3D dd NPC_LIFE_COUNT dup(0)
|
||||
npcWalkVelocityX3D dd NPC_LIFE_COUNT dup(0)
|
||||
npcWalkVelocityY3D dd NPC_LIFE_COUNT dup(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
|
||||
|
|
|
|||
359
src_graphics/steel_door.inc
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
; Hysteresis and a hold timer keep the shared doorway safe on both sides.
|
||||
UpdateBasementDoor3D PROC uses ebx
|
||||
local nearDoor:DWORD,rangeX:DWORD,rangeY:DWORD
|
||||
mov nearDoor,0
|
||||
mov rangeX,FP_1*12/5
|
||||
mov rangeY,FP_1*7/5
|
||||
mov eax,basementDoorTarget3D
|
||||
or eax,basementDoorOpen3D
|
||||
or eax,basementWheelTime3D
|
||||
jz @F
|
||||
mov rangeX,3*FP_1
|
||||
mov rangeY,FP_1*9/5
|
||||
@@:
|
||||
cmp engineMode,MODE_WALK
|
||||
jne steel_approach_stairs
|
||||
cmp hallLevel3D,0
|
||||
jne steel_door_clock
|
||||
mov eax,playerX
|
||||
sub eax,FP_1*289/10
|
||||
cdq
|
||||
xor eax,edx
|
||||
sub eax,edx
|
||||
cmp eax,rangeX
|
||||
ja steel_door_clock
|
||||
mov eax,playerY
|
||||
sub eax,FP_1*105/10
|
||||
cdq
|
||||
xor eax,edx
|
||||
sub eax,edx
|
||||
cmp eax,rangeY
|
||||
ja steel_door_clock
|
||||
jmp steel_request_open
|
||||
steel_approach_stairs:
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne steel_door_clock
|
||||
cmp stairRoute3D,1
|
||||
jne steel_door_clock
|
||||
mov eax,5*FP_1
|
||||
cmp rangeX,3*FP_1
|
||||
jne @F
|
||||
mov eax,FP_1*13/2
|
||||
@@:
|
||||
cmp playerX,eax
|
||||
ja steel_door_clock
|
||||
cmp playerY,FP_1*195/10
|
||||
jb steel_door_clock
|
||||
cmp playerY,FP_1*235/10
|
||||
ja steel_door_clock
|
||||
steel_request_open:
|
||||
mov nearDoor,1
|
||||
mov basementDoorTarget3D,FP_1
|
||||
mov basementDoorHold3D,1200
|
||||
steel_door_clock:
|
||||
mov eax,animationClock3D
|
||||
cmp eax,interiorDoorClock3D
|
||||
je steel_update_done
|
||||
mov interiorDoorClock3D,eax
|
||||
mov ebx,deltaTime3D
|
||||
cmp ebx,100
|
||||
jbe @F
|
||||
mov ebx,100
|
||||
@@:
|
||||
cmp nearDoor,1
|
||||
je steel_animate
|
||||
; A manual E request outside the automatic opening radius still gets time.
|
||||
cmp basementDoorTarget3D,FP_1
|
||||
jne steel_far_hold
|
||||
cmp basementDoorHold3D,0
|
||||
jne steel_far_hold
|
||||
mov eax,basementDoorOpen3D
|
||||
or eax,basementWheelTime3D
|
||||
jnz steel_far_hold
|
||||
mov basementDoorHold3D,2000
|
||||
steel_far_hold:
|
||||
cmp basementDoorHold3D,0
|
||||
je steel_request_close
|
||||
mov eax,basementDoorHold3D
|
||||
sub eax,ebx
|
||||
jae @F
|
||||
xor eax,eax
|
||||
@@:
|
||||
mov basementDoorHold3D,eax
|
||||
jmp steel_animate
|
||||
steel_request_close:
|
||||
mov basementDoorTarget3D,0
|
||||
steel_animate:
|
||||
cmp basementDoorTarget3D,0
|
||||
je steel_close_leaf
|
||||
cmp basementWheelTime3D,700
|
||||
jae steel_swing
|
||||
mov eax,700
|
||||
sub eax,basementWheelTime3D
|
||||
cmp ebx,eax
|
||||
jb steel_wheel_partial
|
||||
sub ebx,eax
|
||||
mov basementWheelTime3D,700
|
||||
jmp steel_swing
|
||||
steel_wheel_partial:
|
||||
add basementWheelTime3D,ebx
|
||||
jmp steel_update_done
|
||||
steel_swing:
|
||||
; Opening: unlock first, then swing around the fixed hinge over 1000ms.
|
||||
imul eax,ebx,FP_1
|
||||
xor edx,edx
|
||||
mov ecx,1000
|
||||
div ecx
|
||||
add eax,basementDoorOpen3D
|
||||
cmp eax,FP_1
|
||||
jbe @F
|
||||
mov eax,FP_1
|
||||
@@:
|
||||
mov basementDoorOpen3D,eax
|
||||
jmp steel_update_done
|
||||
steel_close_leaf:
|
||||
cmp basementDoorOpen3D,0
|
||||
je steel_lock_wheel
|
||||
; Carry unused milliseconds into locking only after the leaf is fully shut.
|
||||
imul eax,basementDoorOpen3D,1000
|
||||
add eax,FP_1-1
|
||||
shr eax,16
|
||||
cmp ebx,eax
|
||||
jb steel_close_partial
|
||||
sub ebx,eax
|
||||
mov basementDoorOpen3D,0
|
||||
jmp steel_lock_wheel
|
||||
steel_close_partial:
|
||||
imul eax,ebx,FP_1
|
||||
xor edx,edx
|
||||
mov ecx,1000
|
||||
div ecx
|
||||
sub basementDoorOpen3D,eax
|
||||
jmp steel_update_done
|
||||
steel_lock_wheel:
|
||||
mov eax,basementWheelTime3D
|
||||
sub eax,ebx
|
||||
jae @F
|
||||
xor eax,eax
|
||||
@@:
|
||||
mov basementWheelTime3D,eax
|
||||
steel_update_done:
|
||||
ret
|
||||
UpdateBasementDoor3D ENDP
|
||||
|
||||
; The shared doorway occupies the overlapping landing in both local maps.
|
||||
StairDoorCanWalk3D PROC px:DWORD,py:DWORD
|
||||
cmp stairRoute3D,1
|
||||
jne stair_steel_clear
|
||||
cmp px,FP_1*35/10
|
||||
jb stair_steel_clear
|
||||
cmp px,FP_1*42/10
|
||||
ja stair_steel_clear
|
||||
cmp py,FP_1*2025/100
|
||||
jb stair_steel_blocked
|
||||
cmp py,FP_1*2175/100
|
||||
jae stair_steel_blocked
|
||||
mov eax,px
|
||||
add eax,25*FP_1
|
||||
mov edx,py
|
||||
sub edx,FP_1*21/2
|
||||
invoke InteriorDoorCanWalk3D,eax,edx
|
||||
ret
|
||||
stair_steel_blocked:
|
||||
xor eax,eax
|
||||
ret
|
||||
stair_steel_clear:
|
||||
mov eax,1
|
||||
ret
|
||||
StairDoorCanWalk3D ENDP
|
||||
|
||||
RenderStairDoor3D PROC
|
||||
local oldX:DWORD,oldY:DWORD,oldEye:DWORD
|
||||
cmp stairRoute3D,1
|
||||
jne stair_steel_done
|
||||
; Fill the wider landing around the actual narrow doorway on the stair side.
|
||||
invoke DrawCityBoxSides3D,FP_1*375/100,4*FP_1,20*FP_1,FP_1*2025/100,FP_1*3/2,FP_1*37/10,92
|
||||
invoke DrawCityBoxSides3D,FP_1*375/100,4*FP_1,FP_1*2175/100,23*FP_1,FP_1*3/2,FP_1*37/10,92
|
||||
mov eax,playerX
|
||||
mov oldX,eax
|
||||
add playerX,25*FP_1
|
||||
mov eax,playerY
|
||||
mov oldY,eax
|
||||
sub playerY,FP_1*21/2
|
||||
mov eax,viewEye3D
|
||||
mov oldEye,eax
|
||||
sub viewEye3D,FP_1*3/2
|
||||
invoke DrawCityVertical3D,0,FP_1*2838/100,9*FP_1,12*FP_1,-FP_1/10,FP_1*21/10,131
|
||||
call DrawBasementLeaf3D
|
||||
mov eax,oldX
|
||||
mov playerX,eax
|
||||
mov eax,oldY
|
||||
mov playerY,eax
|
||||
mov eax,oldEye
|
||||
mov viewEye3D,eax
|
||||
stair_steel_done:
|
||||
ret
|
||||
RenderStairDoor3D ENDP
|
||||
|
||||
; Perspective-correct vertical plane with a rotating horizontal basis. The wheel
|
||||
; is baked into offline texture phases, not rotated in screen space at runtime.
|
||||
DrawBasementLeaf3D PROC uses ebx esi edi
|
||||
local ux:DWORD,uy:DWORD,nx:DWORD,ny:DWORD,numerator:DWORD
|
||||
local depth:DWORD,texture:DWORD,sourceX:DWORD,top:DWORD,bottom:DWORD
|
||||
local stepY:DWORD,sourceY:DWORD,row:DWORD,column:DWORD,destination:DWORD
|
||||
invoke ResolveUpperMaterial3D,128
|
||||
test eax,eax
|
||||
jz steel_leaf_done
|
||||
mov texture,eax
|
||||
mov eax,basementDoorOpen3D
|
||||
imul eax,128
|
||||
shr eax,16
|
||||
and eax,511
|
||||
mov ecx,tableSin[eax*4]
|
||||
mov ux,ecx
|
||||
neg ecx
|
||||
mov ny,ecx
|
||||
mov ecx,tableCos[eax*4]
|
||||
mov uy,ecx
|
||||
mov nx,ecx
|
||||
mov eax,FP_1*289/10
|
||||
sub eax,playerX
|
||||
imul nx
|
||||
shrd eax,edx,16
|
||||
mov ebx,eax
|
||||
mov eax,FP_1*975/100
|
||||
sub eax,playerY
|
||||
imul ny
|
||||
shrd eax,edx,16
|
||||
add eax,ebx
|
||||
mov numerator,eax
|
||||
mov column,0
|
||||
steel_leaf_column:
|
||||
mov ebx,column
|
||||
mov eax,cityRayX3D[ebx*4]
|
||||
imul nx
|
||||
shrd eax,edx,16
|
||||
mov esi,eax
|
||||
mov eax,cityRayY3D[ebx*4]
|
||||
imul ny
|
||||
shrd eax,edx,16
|
||||
add eax,esi
|
||||
mov ecx,eax
|
||||
cdq
|
||||
xor eax,edx
|
||||
sub eax,edx
|
||||
cmp eax,256
|
||||
jb steel_leaf_next_column
|
||||
mov eax,numerator
|
||||
cdq
|
||||
shld edx,eax,16
|
||||
shl eax,16
|
||||
idiv ecx
|
||||
cmp eax,4096
|
||||
jle steel_leaf_next_column
|
||||
cmp eax,zBuffer[ebx*4]
|
||||
jae steel_leaf_next_column
|
||||
cmp portalRendering3D,0
|
||||
je @F
|
||||
cmp eax,portalClipDepth3D[ebx*4]
|
||||
jb steel_leaf_next_column
|
||||
@@:
|
||||
mov depth,eax
|
||||
imul cityRayX3D[ebx*4]
|
||||
shrd eax,edx,16
|
||||
add eax,playerX
|
||||
sub eax,FP_1*289/10
|
||||
imul ux
|
||||
shrd eax,edx,16
|
||||
mov esi,eax
|
||||
mov eax,depth
|
||||
imul cityRayY3D[ebx*4]
|
||||
shrd eax,edx,16
|
||||
add eax,playerY
|
||||
sub eax,FP_1*975/100
|
||||
imul uy
|
||||
shrd eax,edx,16
|
||||
add eax,esi
|
||||
test eax,eax
|
||||
js steel_leaf_next_column
|
||||
cmp eax,FP_1*3/2
|
||||
jae steel_leaf_next_column
|
||||
imul eax,256
|
||||
xor edx,edx
|
||||
mov ecx,FP_1*3/2
|
||||
div ecx
|
||||
mov sourceX,eax
|
||||
mov eax,viewEye3D
|
||||
sub eax,FP_1*19/10
|
||||
imul eax,SCREEN_H
|
||||
cdq
|
||||
idiv depth
|
||||
add eax,viewHorizon3D
|
||||
mov top,eax
|
||||
mov eax,viewEye3D
|
||||
imul eax,SCREEN_H
|
||||
cdq
|
||||
idiv depth
|
||||
add eax,viewHorizon3D
|
||||
mov bottom,eax
|
||||
sub eax,top
|
||||
jle steel_leaf_next_column
|
||||
mov ecx,eax
|
||||
mov eax,512*FP_1
|
||||
xor edx,edx
|
||||
div ecx
|
||||
mov stepY,eax
|
||||
mov eax,top
|
||||
test eax,eax
|
||||
jns @F
|
||||
xor eax,eax
|
||||
@@:
|
||||
mov row,eax
|
||||
sub eax,top
|
||||
imul eax,stepY
|
||||
mov sourceY,eax
|
||||
cmp bottom,SCREEN_H
|
||||
jle @F
|
||||
mov bottom,SCREEN_H
|
||||
@@:
|
||||
mov eax,row
|
||||
cmp eax,bottom
|
||||
jge steel_leaf_next_column
|
||||
imul eax,SCREEN_W
|
||||
add eax,column
|
||||
shl eax,2
|
||||
mov destination,eax
|
||||
steel_leaf_pixel:
|
||||
mov edi,destination
|
||||
mov eax,depth
|
||||
cmp eax,cityDepthBuffer3D[edi]
|
||||
ja steel_leaf_next_pixel
|
||||
mov eax,sourceY
|
||||
shr eax,16
|
||||
cmp eax,512
|
||||
jae steel_leaf_next_pixel
|
||||
shl eax,8
|
||||
add eax,sourceX
|
||||
mov esi,texture
|
||||
mov eax,[esi+eax*4]
|
||||
cmp eax,80000000h
|
||||
jb steel_leaf_next_pixel
|
||||
and eax,00FFFFFFh
|
||||
mov screenBuffer[edi],eax
|
||||
mov eax,depth
|
||||
mov cityDepthBuffer3D[edi],eax
|
||||
steel_leaf_next_pixel:
|
||||
mov eax,stepY
|
||||
add sourceY,eax
|
||||
add destination,SCREEN_W*4
|
||||
inc row
|
||||
mov eax,row
|
||||
cmp eax,bottom
|
||||
jb steel_leaf_pixel
|
||||
steel_leaf_next_column:
|
||||
inc column
|
||||
cmp column,SCREEN_W
|
||||
jb steel_leaf_column
|
||||
steel_leaf_done:
|
||||
ret
|
||||
DrawBasementLeaf3D ENDP
|
||||
|
|
@ -124,7 +124,10 @@ hall_wall_painting:
|
|||
jnz hall_wall_painting
|
||||
xor esi,esi
|
||||
hall_wall_sconce:
|
||||
cmp esi,1
|
||||
je hall_wall_secret_lamp
|
||||
invoke DrawHallLantern3D,FP_1*102/100,hallSconceY3D[esi*4],1
|
||||
hall_wall_secret_lamp:
|
||||
invoke DrawHallLantern3D,FP_1*3098/100,hallSconceY3D[esi*4],-1
|
||||
inc esi
|
||||
cmp esi,4
|
||||
|
|
|
|||
|
|
@ -62,14 +62,25 @@ def main():
|
|||
for y in range(13,23):
|
||||
for x in range(15,18):
|
||||
down_stairs[y][x] = 0
|
||||
fortune = [[132]*32 for _ in range(32)]
|
||||
for y in range(9,13):
|
||||
for x in range(13,18):
|
||||
fortune[y][x] = 0
|
||||
fortune_stairs = [[132]*32 for _ in range(32)]
|
||||
for y in range(9,11):
|
||||
for x in range(16,31):
|
||||
fortune_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)])):
|
||||
(down_stairs,(3,21),[(16,21),(16,14)]),
|
||||
(fortune,(17,10),[(14,10),(16,12),(17,10)]),
|
||||
(fortune_stairs,(29,10),[(17,10)])):
|
||||
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),
|
||||
("upperLoungeMap3D",upper), ("upperStairMap3D",up_stairs),
|
||||
("basementStairMap3D",down_stairs)):
|
||||
("basementStairMap3D",down_stairs), ("fortuneRoomMap3D",fortune),
|
||||
("fortuneStairMap3D",fortune_stairs)):
|
||||
for index, row in enumerate(grid):
|
||||
output.append((name if index == 0 else " " * len(name)) + " db " + ",".join(map(str, row)))
|
||||
output.append(".code")
|
||||
|
|
|
|||
74
tools/generators/pack_door_walk_v2.py
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
"""Offline normalization of generated door components and distance-driven gait art."""
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from PIL import Image
|
||||
from pack_guest_cast import isolate
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
ART = ROOT / "assets/sprites/hall"
|
||||
OUT = ROOT / "assets/animations/hall"
|
||||
PREVIEWS = ROOT / "build/hall-expansion"
|
||||
|
||||
|
||||
def pack(name, frames):
|
||||
w, h = frames[0].size
|
||||
assert all(frame.size == (w, h) for frame in frames)
|
||||
payload = b"".join(frame.tobytes("raw", "BGRA") for frame in frames)
|
||||
(OUT / name).write_bytes(struct.pack("<4s4I", b"ANM1", w, h, len(frames), len(payload)) + payload)
|
||||
|
||||
|
||||
def guests():
|
||||
sheet = Image.open(ART / "guest-walk-v2.png").convert("RGBA")
|
||||
# The generated strip has variable row heights; these gutters separate feet/heads.
|
||||
rows = (0, 190, 376, 560, 746, 916, sheet.height)
|
||||
frames = []
|
||||
for appearance in range(6):
|
||||
cells = [isolate(sheet.crop((col*sheet.width//8, rows[appearance],
|
||||
(col+1)*sheet.width//8, rows[appearance+1]))) for col in range(8)]
|
||||
scale = min(126/max(cell.width for cell in cells), 204/max(cell.height for cell in cells))
|
||||
for cell in cells:
|
||||
cell = cell.resize((round(cell.width*scale), round(cell.height*scale)), Image.Resampling.LANCZOS)
|
||||
frame = Image.new("RGBA", (128,224))
|
||||
frame.alpha_composite(cell, ((128-cell.width)//2, 224-cell.height))
|
||||
frames.append(frame)
|
||||
pack("guest-walk-v2.bin", frames)
|
||||
pack("guest-walk-left-v2.bin", [frame.transpose(Image.Transpose.FLIP_LEFT_RIGHT) for frame in frames])
|
||||
preview = Image.new("RGBA", (8*128,6*224), (27,30,29,255))
|
||||
for i, frame in enumerate(frames[:48]):
|
||||
preview.alpha_composite(frame, ((i%8)*128, (i//8)*224))
|
||||
preview.save(PREVIEWS / "guest-walk-v2-preview.png")
|
||||
|
||||
|
||||
def door():
|
||||
sheet = Image.open(ART / "basement-door-v2.png").convert("RGBA")
|
||||
leaf = isolate(sheet.crop((0,0,640,sheet.height))).resize((256,512), Image.Resampling.LANCZOS)
|
||||
source_frame = isolate(sheet.crop((640,0,1420,sheet.height)))
|
||||
wheel = isolate(sheet.crop((1420,0,sheet.width,sheet.height))).resize((92,92), Image.Resampling.LANCZOS)
|
||||
frames = []
|
||||
for phase in range(17):
|
||||
texture = leaf.copy()
|
||||
rotated = wheel.rotate(-270*phase/16, Image.Resampling.BICUBIC, expand=False)
|
||||
# UV texels are not square in world space on the 1.5 by 1.9 metre leaf.
|
||||
rotated = rotated.resize((58,92), Image.Resampling.LANCZOS)
|
||||
texture.alpha_composite(rotated, (99,244))
|
||||
frames.append(texture)
|
||||
# Map the rigid jambs around a known rectangular aperture, without a baked leaf.
|
||||
w,h = source_frame.size
|
||||
left, right, lintel = round(w*.24), round(w*.72), round(h*.14)
|
||||
frame = Image.new("RGBA", (256,512))
|
||||
frame.alpha_composite(source_frame.crop((0,0,left,h)).resize((64,512)), (0,0))
|
||||
frame.alpha_composite(source_frame.crop((right,0,w,h)).resize((64,512)), (192,0))
|
||||
frame.alpha_composite(source_frame.crop((left,0,right,lintel)).resize((128,32)), (64,0))
|
||||
frames.append(frame)
|
||||
pack("basement-door-v2.bin", frames)
|
||||
preview = Image.new("RGBA", (3*256,512), (27,30,29,255))
|
||||
for i, texture in enumerate((frames[0],frames[8],frame)):
|
||||
preview.alpha_composite(texture,(i*256,0))
|
||||
preview.save(PREVIEWS / "basement-door-v2-preview.png")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
PREVIEWS.mkdir(parents=True, exist_ok=True)
|
||||
guests()
|
||||
door()
|
||||
print("Packed 96 gait frames and 18 door textures; runtime remains native assembly.")
|
||||
63
tools/generators/pack_fortune_room.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
"""Pack generated tarot-room sprites. Offline tooling, never a game runtime."""
|
||||
from pathlib import Path
|
||||
import struct
|
||||
from PIL import Image
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
|
||||
|
||||
def sprites(name, row_split, bottom_split=.5, top_split=.5):
|
||||
source = ROOT / "assets/sprites/hall" / name
|
||||
sheet = Image.open(source).convert("RGBA")
|
||||
w, h = sheet.size
|
||||
frames = []
|
||||
# The source has tightly spaced silhouettes. Bounds are checked on alpha.
|
||||
# Each cell is independently fitted, but the two idle poses share one scale.
|
||||
# The generated first row ends above the bookcase's crescent crest.
|
||||
split = round(h * row_split)
|
||||
top = round(w * top_split)
|
||||
cells = [sheet.crop((0,0,top,split)), sheet.crop((top,0,w,split)),
|
||||
sheet.crop((0,split,round(w*bottom_split),h)),
|
||||
sheet.crop((round(w*bottom_split),split,w,h))]
|
||||
bounds = [cell.getchannel("A").point(lambda a: 255 if a > 32 else 0).getbbox() for cell in cells]
|
||||
assert all(bounds), "missing transparent sprite"
|
||||
cropped = [cell.crop(box) for cell, box in zip(cells, bounds)]
|
||||
scale = min(242/max(c.width for c in cropped[:2]), 252/max(c.height for c in cropped[:2]))
|
||||
for i, cell in enumerate(cropped):
|
||||
factor = scale if i < 2 else min(242/cell.width, 252/cell.height)
|
||||
cell = cell.resize((round(cell.width*factor), round(cell.height*factor)), Image.Resampling.LANCZOS)
|
||||
frame = Image.new("RGBA", (256,256))
|
||||
frame.alpha_composite(cell, ((256-cell.width)//2, 256-cell.height))
|
||||
frames.append(frame)
|
||||
return frames
|
||||
|
||||
|
||||
def pack(name, frames):
|
||||
payload = b"".join(frame.tobytes("raw", "BGRA") for frame in frames)
|
||||
target = ROOT / "assets/animations/hall" / name
|
||||
target.write_bytes(struct.pack("<4s4I",b"ANM1",256,256,len(frames),len(payload))+payload)
|
||||
|
||||
|
||||
def main():
|
||||
frames = sprites("fortune-room-v2.png", .47)
|
||||
frames += sprites("fortune-decor-v2.png", .52, .60)
|
||||
frames += sprites("fortune-wall-v2.png", .46, top_split=.55)
|
||||
frames += sprites("fortune-cabinets-v2.png", .58, top_split=.47)
|
||||
frames += sprites("fortune-den-v3.png", .575, .61, top_split=.64)
|
||||
frames += sprites("fortune-clutter-v3.png", .53, .55, top_split=.515)
|
||||
pack("fortune-room-v3.bin", frames)
|
||||
sheet = Image.open(ROOT / "assets/sprites/hall/fortune-materials-v3.png").convert("RGBA")
|
||||
w, h = sheet.size
|
||||
materials = [sheet.crop(box).resize((256,256), Image.Resampling.LANCZOS)
|
||||
for box in ((0,0,w//2,h//2), (w//2,0,w,h//2),
|
||||
(0,h//2,w//2,h), (w//2,h//2,w,h))]
|
||||
pack("fortune-materials-v3.bin", materials)
|
||||
preview = Image.new("RGBA", (1024,1536), (22,30,48,255))
|
||||
for i, frame in enumerate(frames):
|
||||
preview.alpha_composite(frame,((i%4)*256,(i//4)*256))
|
||||
preview.save(ROOT / "build/hall-expansion/fortune-art-preview.png")
|
||||
print("Packed twenty-four den sprites and four worn cloth/stone materials.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
139
tools/tests/bar_camera_test.inc
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
.data
|
||||
barTestMain db "build\hall-expansion\bar-camera-main.bmp",0
|
||||
barTestUpper db "build\hall-expansion\bar-camera-upper.bmp",0
|
||||
.code
|
||||
BarCameraAssert PROC
|
||||
cmp viewHorizon3D,HALF_H
|
||||
jne bar_camera_assert_fail
|
||||
cmp viewEye3D,HALL_STANDING_EYE
|
||||
jne bar_camera_assert_fail
|
||||
cmp playerX,12*FP_1
|
||||
jne bar_camera_assert_fail
|
||||
cmp playerY,14*FP_1
|
||||
jne bar_camera_assert_fail
|
||||
cmp playerAngle,384
|
||||
jne bar_camera_assert_fail
|
||||
ret
|
||||
bar_camera_assert_fail:
|
||||
invoke ExitProcess,expansionStage
|
||||
ret
|
||||
BarCameraAssert ENDP
|
||||
|
||||
BarCameraNativeTests PROC uses ebx esi edi
|
||||
local expectedBank:DWORD, oldArrival:DWORD
|
||||
; Exercise real purchases without writing the player's profile.
|
||||
mov eax,arrivalConfirmed3D
|
||||
mov oldArrival,eax
|
||||
mov arrivalConfirmed3D,0
|
||||
mov expansionStage,70
|
||||
mov engineMode,MODE_WALK
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
mov playerX,12*FP_1
|
||||
mov playerY,14*FP_1
|
||||
mov playerAngle,384
|
||||
mov viewHorizon3D,-300
|
||||
mov viewEye3D,-FP_1
|
||||
mov deltaTime3D,16
|
||||
call UpdateHall3D
|
||||
call BarCameraAssert
|
||||
xor ebx,ebx
|
||||
bar_camera_level:
|
||||
mov hallLevel3D,ebx
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
test ebx,ebx
|
||||
jz @F
|
||||
mov worldMap3D,OFFSET upperLoungeMap3D
|
||||
@@:
|
||||
mov bankroll,10000
|
||||
mov expectedBank,10000
|
||||
mov esi,12
|
||||
bar_camera_visit:
|
||||
mov expansionStage,71
|
||||
mov interactTargetId,INTERACT_BAR
|
||||
call TriggerHallInteraction3D
|
||||
cmp engineMode,MODE_BAR
|
||||
jne bar_camera_fail
|
||||
xor edi,edi
|
||||
bar_camera_drink:
|
||||
mov eax,barPrice3D[edi*4]
|
||||
sub expectedBank,eax
|
||||
invoke BuyBarDrink3D,edi
|
||||
mov eax,expectedBank
|
||||
cmp bankroll,eax
|
||||
jne bar_camera_fail
|
||||
cmp barPhase3D,1
|
||||
jne bar_camera_fail
|
||||
cmp barTime3D,0
|
||||
jne bar_camera_fail
|
||||
; A second click while serving must not buy another drink.
|
||||
invoke BuyBarDrink3D,edi
|
||||
mov eax,expectedBank
|
||||
cmp bankroll,eax
|
||||
jne bar_camera_fail
|
||||
mov deltaTime3D,2999
|
||||
call HandleBar3D
|
||||
cmp barPhase3D,1
|
||||
jne bar_camera_fail
|
||||
mov deltaTime3D,1
|
||||
call HandleBar3D
|
||||
cmp barPhase3D,2
|
||||
jne bar_camera_fail
|
||||
cmp barTime3D,0
|
||||
jne bar_camera_fail
|
||||
mov deltaTime3D,2399
|
||||
call HandleBar3D
|
||||
cmp barPhase3D,2
|
||||
jne bar_camera_fail
|
||||
; Render every drink in each bar once, checking camera restoration.
|
||||
cmp esi,12
|
||||
jne @F
|
||||
call RenderBar3D
|
||||
call BarCameraAssert
|
||||
cmp engineMode,MODE_BAR
|
||||
jne bar_camera_fail
|
||||
@@:
|
||||
mov deltaTime3D,1
|
||||
call HandleBar3D
|
||||
cmp barPhase3D,0
|
||||
jne bar_camera_fail
|
||||
cmp barTime3D,0
|
||||
jne bar_camera_fail
|
||||
call BarCameraAssert
|
||||
inc edi
|
||||
cmp edi,3
|
||||
jb bar_camera_drink
|
||||
; Same return-to-walking state as the menu exit, then real hall updates.
|
||||
mov engineMode,MODE_WALK
|
||||
mov deltaTime3D,16
|
||||
mov edi,256
|
||||
bar_camera_walk:
|
||||
add animationClock3D,16
|
||||
call UpdateHall3D
|
||||
call UpdateInteriorEye3D
|
||||
call BarCameraAssert
|
||||
cmp engineMode,MODE_WALK
|
||||
jne bar_camera_fail
|
||||
dec edi
|
||||
jnz bar_camera_walk
|
||||
dec esi
|
||||
jnz bar_camera_visit
|
||||
mov expansionStage,72
|
||||
mov eax,expectedBank
|
||||
cmp bankroll,eax
|
||||
jne bar_camera_fail
|
||||
call ExpansionFrame
|
||||
test ebx,ebx
|
||||
jnz bar_camera_upper_snapshot
|
||||
invoke ExpansionSnapshot,OFFSET barTestMain
|
||||
mov ebx,2
|
||||
jmp bar_camera_level
|
||||
bar_camera_upper_snapshot:
|
||||
invoke ExpansionSnapshot,OFFSET barTestUpper
|
||||
mov eax,oldArrival
|
||||
mov arrivalConfirmed3D,eax
|
||||
ret
|
||||
bar_camera_fail:
|
||||
invoke ExitProcess,expansionStage
|
||||
ret
|
||||
BarCameraNativeTests ENDP
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
import struct
|
||||
from pathlib import Path
|
||||
|
||||
from capstone import Cs, CS_ARCH_X86, CS_MODE_32
|
||||
from capstone import Cs, CS_ARCH_X86, CS_MODE_32, CS_GRP_JUMP
|
||||
from capstone.x86_const import X86_OP_IMM
|
||||
|
||||
|
||||
|
|
@ -35,7 +35,7 @@ class Coff:
|
|||
for i in range(reloc_count)]
|
||||
self.sections[section] = (raw, size, entries)
|
||||
|
||||
def signature(self, name):
|
||||
def signature(self, name, tail_branches=False):
|
||||
section, start = self.functions[name]
|
||||
raw, size, entries = self.sections[section]
|
||||
end = min([value for sec, value in self.functions.values()
|
||||
|
|
@ -59,6 +59,12 @@ class Coff:
|
|||
if ins.mnemonic == "call" and not refs and ins.operands[0].type == X86_OP_IMM:
|
||||
target = targets[ins.operands[0].imm]
|
||||
code[ins.imm_offset:ins.imm_offset + ins.imm_size] = b"\0" * ins.imm_size
|
||||
elif (tail_branches and ins.group(CS_GRP_JUMP) and not refs
|
||||
and ins.operands[0].type == X86_OP_IMM
|
||||
and ins.operands[0].imm in targets):
|
||||
# A tail branch relocates with its callee, just like a direct call.
|
||||
target = targets[ins.operands[0].imm]
|
||||
code[ins.imm_offset:ins.imm_offset + ins.imm_size] = b"\0" * ins.imm_size
|
||||
result.append((bytes(code), tuple(refs), target))
|
||||
assert sum(len(code) for code, _, _ in result) == end - start, name
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import argparse
|
|||
import re
|
||||
import struct
|
||||
import subprocess
|
||||
from PIL import Image
|
||||
from PIL import Image, ImageStat
|
||||
from check_card_exit_build import Coff
|
||||
from check_terminals import signature
|
||||
|
||||
|
|
@ -28,16 +28,22 @@ 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")
|
||||
code += "\n" + read("src_graphics/steel_door.inc")
|
||||
code += "\n" + read("src_graphics/fortune_room.inc")
|
||||
code += "\n" + proc(read("src_graphics/unavailable.inc"),"BeginUnavailable3D")
|
||||
code += "\n" + proc(read("src_graphics/world_dressing.inc"),"RenderHallDressing3D")
|
||||
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"]),
|
||||
"CanWalkHall3D","UpdateHall3D","UpdatePatrons3D","RenderPatrons3D","RenderBar3D",
|
||||
"BuyBarDrink3D","HandleBar3D"]),
|
||||
("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/npc_life.inc",["InitNpcLife3D","NpcTryCell3D","NpcPlanPath3D",
|
||||
"NpcCanStep3D","NpcMove3D","UpdateNpcLife3D","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)
|
||||
|
|
@ -45,7 +51,8 @@ def prepare():
|
|||
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
|
||||
"src_graphics/cabaret_data.inc","src_graphics/hall_layout.inc",
|
||||
"src_graphics/fortune_room_data.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)
|
||||
|
|
@ -125,6 +132,8 @@ def main():
|
|||
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")
|
||||
parser.add_argument("--prove-distance-regression",action="store_true")
|
||||
parser.add_argument("--prove-bar-camera-regression",action="store_true")
|
||||
args = parser.parse_args()
|
||||
if args.parity:
|
||||
native,shipping = Coff(OUT/"module.obj"),Coff(ROOT/"build/graphics/poker.obj")
|
||||
|
|
@ -132,11 +141,27 @@ def main():
|
|||
"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
|
||||
assert signature(native,symbol,tail_branches=True) == signature(shipping,symbol,tail_branches=True),name
|
||||
print("PASS: all tested navigation, collision, hall/upper rendering, guests and cabaret procedures match the shipping assembly")
|
||||
return
|
||||
prepare()
|
||||
room = proc(read("src_graphics/fortune_room.inc"), "RenderFortuneFurnishings3D")
|
||||
assert "DrawCityObject3D" not in room, "tarot furniture must not face the camera"
|
||||
assert room.count("DrawFortuneFixedProp3D") == 24
|
||||
stem = "module"
|
||||
if args.prove_bar_camera_regression:
|
||||
stem = "module-camera-regression"
|
||||
source = (OUT/"module.asm").read_text(encoding="cp1251")
|
||||
before = proc(source,"UpdateInteriorEye3D")
|
||||
after = before.replace(" mov viewHorizon3D,HALF_H\n", "")
|
||||
assert before != after
|
||||
(OUT/(stem+".asm")).write_text(source.replace(before,after),encoding="cp1251")
|
||||
if args.prove_distance_regression:
|
||||
stem = "module-old-distance"
|
||||
source = (OUT/"module.asm").read_text(encoding="cp1251")
|
||||
assert source.count("cmp eax,64*FP_1") == 1
|
||||
source = source.replace("cmp eax,64*FP_1", "cmp eax,8*FP_1")
|
||||
(OUT/(stem+".asm")).write_text(source,encoding="cp1251")
|
||||
if args.prove_alpha_regression:
|
||||
stem = "module-alpha-regression"
|
||||
source = (OUT/"module.asm").read_text(encoding="cp1251")
|
||||
|
|
@ -165,11 +190,34 @@ def main():
|
|||
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_bar_camera_regression:
|
||||
assert result.returncode == 70,result.returncode
|
||||
print("PASS: removing camera recovery fails the native bar regression")
|
||||
return
|
||||
if args.prove_alpha_regression:
|
||||
assert result.returncode == 1,result.returncode
|
||||
print("PASS: transparent felt/keyboard regression rejected by actual native assertions")
|
||||
return
|
||||
if args.prove_distance_regression:
|
||||
assert result.returncode == 28,result.returncode
|
||||
print("PASS: restoring eight-metre cutoff fails the native far-doorway assertion")
|
||||
return
|
||||
assert result.returncode == 0,f"native case {result.returncode}"
|
||||
closed = Image.open(OUT/"fortune-closed.bmp").tobytes()
|
||||
opened = Image.open(OUT/"fortune-open.bmp").tobytes()
|
||||
assert closed != opened, "secret panel must visibly reveal the stairwell"
|
||||
seer = Image.open(OUT/"fortune-seer.bmp").tobytes()
|
||||
idle = Image.open(OUT/"fortune-seer-idle.bmp").tobytes()
|
||||
assert seer != idle, "fortune teller must have two distinct idle poses"
|
||||
dark = ImageStat.Stat(Image.open(OUT/"fortune-seer.bmp").convert("RGB")).mean
|
||||
full = ImageStat.Stat(Image.open(OUT/"fortune-full-light.bmp").convert("RGB")).mean
|
||||
assert .25 < sum(dark)/sum(full) < .8, "point lighting must darken the room without blacking it out"
|
||||
for name in ("fortune-room", "fortune-seer", "fortune-stairs", "fortune-north", "fortune-south"):
|
||||
pixels = Image.open(OUT/(name+".bmp")).convert("RGB").crop((0,40,640,440))
|
||||
blue = sum(b > r+12 and b > g for r,g,b in pixels.get_flattened_data())
|
||||
assert blue > 45000, (name, "chamber must use its own blue palette", blue)
|
||||
modal = Image.open(OUT/"fortune-unavailable.bmp").convert("RGB").crop((104,176,536,336))
|
||||
assert len(modal.getcolors(432*160)) > 3, "native unavailable message must be drawn"
|
||||
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
|
||||
|
|
@ -182,6 +230,15 @@ def main():
|
|||
image = Image.open(path).convert("RGB")
|
||||
assert image.size == (640,480) and len(image.getcolors(640*480)) > 100
|
||||
image.save(path.with_suffix(".png"))
|
||||
gait = [Image.open(OUT/f"gait-{i}.bmp").convert("RGB") for i in range(8)]
|
||||
assert len({frame.crop((245,360,410,480)).tobytes() for frame in gait}) == 8, "legs must visibly change in native renders"
|
||||
gait[0].save(OUT/"gait-native.gif",save_all=True,append_images=gait[1:],duration=110,loop=0)
|
||||
door = [Image.open(OUT/name).convert("RGB") for name in
|
||||
("old-basement-closed.bmp","door-wheel-turn.bmp","door-swing-half.bmp","basement-door.bmp")]
|
||||
assert len({frame.tobytes() for frame in door}) == 4, "unlocking, hinge motion and open passage must look different"
|
||||
closing = [Image.open(OUT/name).convert("RGB") for name in
|
||||
("door-closing.bmp","door-locking.bmp","door-closed-again.bmp")]
|
||||
assert len({frame.tobytes() for frame in closing}) == 3, "closing, wheel locking and fully shut poses must differ"
|
||||
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)]
|
||||
|
|
@ -191,7 +248,15 @@ def main():
|
|||
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"
|
||||
off = Image.open(OUT/"portal-disabled.bmp").crop((245,145,395,320)).tobytes()
|
||||
assert off not in vortex, "disabled portal must visibly show the sealed arch"
|
||||
pull = Image.open(OUT/"lever-mid-pull.bmp").crop((200,330,250,480)).tobytes()
|
||||
release = Image.open(OUT/"lever-mid-release.bmp").crop((200,330,250,480)).tobytes()
|
||||
assert pull != release, "left handle must visibly return while right remains enabled"
|
||||
for name,expected in (("guest-cast-v1.bin",(128,224,36)),
|
||||
("guest-walk-v2.bin",(128,224,48)),
|
||||
("guest-walk-left-v2.bin",(128,224,48)),
|
||||
("basement-door-v2.bin",(256,512,18)),
|
||||
("cabaret-ensemble-v1.bin",(160,256,12)),
|
||||
("cabaret-piano-v1.bin",(384,256,4)),
|
||||
("cabaret-guitar-v1.bin",(160,256,4)),
|
||||
|
|
@ -200,14 +265,28 @@ def main():
|
|||
("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))):
|
||||
("upper-braziers-v3.bin",(128,256,4)),
|
||||
("fortune-room-v3.bin",(256,256,24)),
|
||||
("fortune-materials-v3.bin",(256,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"):
|
||||
if name == "guest-cast-v1.bin":
|
||||
assert len(set(frames[::6])) == 6, "all six guest appearances must differ"
|
||||
if name == "guest-walk-v2.bin":
|
||||
for appearance in range(6):
|
||||
poses = [Image.frombytes("RGBA",(w,h),frame,"raw","BGRA")
|
||||
for frame in frames[appearance*8:appearance*8+8]]
|
||||
assert len({pose.crop((0,120,128,224)).tobytes() for pose in poses}) == 8
|
||||
assert all(pose.getbbox()[3] == 224 for pose in poses), "supporting foot must touch baseline"
|
||||
mirrored = (ROOT / "assets/animations/hall/guest-walk-left-v2.bin").read_bytes()[20:]
|
||||
assert all(Image.frombytes("RGBA",(w,h),frames[i],"raw","BGRA").transpose(
|
||||
Image.Transpose.FLIP_LEFT_RIGHT).tobytes("raw","BGRA") == mirrored[i*w*h*4:(i+1)*w*h*4] for i in range(48))
|
||||
if name == "basement-door-v2.bin":
|
||||
assert len(set(frames[:17])) == 17, "separate rotating handwheel phases"
|
||||
assert all(frame[:200*256*4] == frames[0][:200*256*4] for frame in frames[:17]), "leaf stays rigid while unlocking"
|
||||
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"):
|
||||
|
|
@ -222,7 +301,7 @@ def main():
|
|||
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")
|
||||
print("PASS: native stairs and doorway views, automatic door, original ticket position, distance-driven gait, reversible portal, secret lantern passage, manual cellar stairs and fortune teller unavailable dialogue")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ def proc(text, name):
|
|||
return re.search(rf"^{name} PROC\b.*?^{name} ENDP", text, re.M | re.S).group()
|
||||
|
||||
|
||||
def signature(coff, name):
|
||||
def signature(coff, name, tail_branches=False):
|
||||
result = []
|
||||
for code, refs, target in coff.signature(name):
|
||||
for code, refs, target in coff.signature(name, tail_branches=tail_branches):
|
||||
if code[:1] == b"\xe8" and len(refs) == 1 and refs[0][1][1] == 20:
|
||||
target = refs[0][1][0]
|
||||
refs = ()
|
||||
|
|
@ -38,6 +38,7 @@ def prepare():
|
|||
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")
|
||||
code += "\n" + proc(read("src_graphics/fortune_room.inc"), "ResolveFortuneMaterial3D")
|
||||
trigger = proc(read("casino3d.inc"), "Trigger3DInteraction")
|
||||
code += "\n" + trigger
|
||||
if "_InitHellTower3D@0" not in Coff(ROOT / "build/actions/actions-test.obj").functions:
|
||||
|
|
@ -47,9 +48,11 @@ def prepare():
|
|||
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(("fortuneMaterialArt3D", "fortuneArt3D"))
|
||||
own_data.update(("upperMaterialArt3D","upperFeltPixels3D","upperKeyPixels3D",
|
||||
"upperGlowPixels3D","upperPortalArt3D","upperPortal3D",
|
||||
"portalRendering3D","portalClipDepth3D"))
|
||||
"portalRendering3D","portalClipDepth3D","basementDoorArt3D",
|
||||
"basementWheelTime3D"))
|
||||
coff = Coff(ROOT / "build/actions/actions-test.obj")
|
||||
data = bytearray(coff.data)
|
||||
at = struct.unpack_from("<I", data, 8)[0]
|
||||
|
|
@ -142,7 +145,8 @@ NpcWaypoint ENDS
|
|||
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 += "\nResolveUpperMaterial3D PROTO :DWORD\ninclude src_graphics/terminal_data.inc\n.data\nupperMaterialArt3D AnimPack3D <>\nupperPortalArt3D AnimPack3D <>\nupperPortal3D CityProp3D <>\nbasementDoorArt3D AnimPack3D <>\nbasementWheelTime3D dd 0\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 += "\nResolveFortuneMaterial3D PROTO :DWORD\n.data\nfortuneMaterialArt3D AnimPack3D <>\nfortuneArt3D AnimPack3D <>\n.code\n"
|
||||
text += code + "\ninclude tools/tests/terminal_test.inc\nend TerminalTestStart\n"
|
||||
(OUT / "module.asm").write_text(text, encoding="ascii")
|
||||
|
||||
|
|
|
|||
375
tools/tests/fortune_room_test.inc
Normal file
|
|
@ -0,0 +1,375 @@
|
|||
.data
|
||||
fortuneTestClosed db "build\hall-expansion\fortune-closed.bmp",0
|
||||
fortuneTestPull db "build\hall-expansion\fortune-lamp-pulled.bmp",0
|
||||
fortuneTestOpen db "build\hall-expansion\fortune-open.bmp",0
|
||||
fortuneTestFar db "build\hall-expansion\fortune-open-far.bmp",0
|
||||
fortuneTestStairs db "build\hall-expansion\fortune-stairs.bmp",0
|
||||
fortuneTestTopReturn db "build\hall-expansion\fortune-stair-return.bmp",0
|
||||
fortuneTestRoom db "build\hall-expansion\fortune-room.bmp",0
|
||||
fortuneTestNear db "build\hall-expansion\fortune-seer.bmp",0
|
||||
fortuneTestIdle db "build\hall-expansion\fortune-seer-idle.bmp",0
|
||||
fortuneTestDialog db "build\hall-expansion\fortune-unavailable.bmp",0
|
||||
fortuneTestReturn db "build\hall-expansion\fortune-room-return.bmp",0
|
||||
fortuneTestNorth db "build\hall-expansion\fortune-north.bmp",0
|
||||
fortuneTestSouth db "build\hall-expansion\fortune-south.bmp",0
|
||||
fortuneTestMissing db "build\hall-expansion\fortune-missing-art.bmp",0
|
||||
fortuneTestOblique db "build\hall-expansion\fortune-oblique.bmp",0
|
||||
fortuneTestFullLight db "build\hall-expansion\fortune-full-light.bmp",0
|
||||
fortuneTestEast db "build\hall-expansion\fortune-east.bmp",0
|
||||
fortuneTestCentered db "build\hall-expansion\fortune-doorway-centered.bmp",0
|
||||
fortuneTestBank dd 0
|
||||
fortuneTestView dd 0
|
||||
.code
|
||||
FortuneNativeTests PROC uses esi edi
|
||||
mov expansionStage,60
|
||||
cmp fortuneArt3D.frames,24
|
||||
jne fortune_test_fail
|
||||
cmp fortuneArt3D.imageWidth,256
|
||||
jne fortune_test_fail
|
||||
cmp fortuneMaterialArt3D.frames,4
|
||||
jne fortune_test_fail
|
||||
invoke ResolveCitySurface3D,132
|
||||
cmp eax,fortuneMaterialArt3D.pixels
|
||||
jne fortune_test_fail
|
||||
invoke ResolveCitySurface3D,135
|
||||
mov edx,fortuneMaterialArt3D.pixels
|
||||
add edx,3*256*256*4
|
||||
cmp eax,edx
|
||||
jne fortune_test_fail
|
||||
invoke ResolveCitySurface3D,159
|
||||
mov edx,fortuneArt3D.pixels
|
||||
add edx,23*256*256*4
|
||||
cmp eax,edx
|
||||
jne fortune_test_fail
|
||||
invoke FortuneLightAt3D,216,152,26
|
||||
cmp eax,174
|
||||
jb fortune_test_fail
|
||||
invoke FortuneLightAt3D,0,0,0
|
||||
test eax,eax
|
||||
jnz fortune_test_fail
|
||||
mov engineMode,MODE_WALK
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
mov floorTexture3D,100
|
||||
mov fortunePassageTime3D,0
|
||||
mov fortunePassageTarget3D,0
|
||||
mov deltaTime3D,100
|
||||
mov playerX,3*FP_1
|
||||
mov playerY,10*FP_1
|
||||
mov playerAngle,256
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestClosed
|
||||
invoke CanWalk3D,FP_1*15/10,10*FP_1
|
||||
test eax,eax
|
||||
jnz fortune_test_fail
|
||||
mov playerX,FP_1*26/10
|
||||
mov playerY,FP_1*87/10
|
||||
call UpdateCamera3D
|
||||
call CheckInteractions
|
||||
cmp interactTargetId,INTERACT_SECRET_LAMP
|
||||
jne fortune_test_fail
|
||||
call Trigger3DInteraction
|
||||
cmp fortunePassageTarget3D,1
|
||||
jne fortune_test_fail
|
||||
add animationClock3D,100
|
||||
call UpdateInteriorTravel3D
|
||||
cmp fortunePassageTime3D,100
|
||||
jne fortune_test_fail
|
||||
call UpdateFortunePassage3D
|
||||
cmp fortunePassageTime3D,100
|
||||
jne fortune_test_fail
|
||||
add animationClock3D,100
|
||||
call UpdateInteriorTravel3D
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestPull
|
||||
mov esi,12
|
||||
fortune_test_opening:
|
||||
add animationClock3D,100
|
||||
call UpdateInteriorTravel3D
|
||||
dec esi
|
||||
jnz fortune_test_opening
|
||||
cmp fortunePassageTime3D,1400
|
||||
jne fortune_test_fail
|
||||
invoke CanWalk3D,FP_1*15/10,10*FP_1
|
||||
cmp eax,1
|
||||
jne fortune_test_fail
|
||||
call CheckInteractions
|
||||
cmp interactTargetId,INTERACT_SECRET_LAMP
|
||||
jne fortune_test_fail
|
||||
cmp interactPromptPtr,OFFSET fortuneLampReturn3D
|
||||
jne fortune_test_fail
|
||||
mov playerX,3*FP_1
|
||||
mov playerY,10*FP_1
|
||||
call ExpansionFrame
|
||||
cmp portalPixelsWritten3D,1000
|
||||
jb fortune_test_fail
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestOpen
|
||||
mov playerX,FP_1*85/10
|
||||
call ExpansionFrame
|
||||
cmp portalPixelsWritten3D,1000
|
||||
jb fortune_test_fail
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestFar
|
||||
mov expansionStage,61
|
||||
mov deltaTime3D,16
|
||||
mov playerX,3*FP_1
|
||||
mov playerY,10*FP_1
|
||||
mov keys3D[57h],1
|
||||
xor esi,esi
|
||||
fortune_test_walk_down:
|
||||
call ExpansionWalkFrame
|
||||
cmp engineMode,MODE_WALK
|
||||
jne @F
|
||||
cmp hallLevel3D,3
|
||||
je fortune_test_arrived
|
||||
@@:
|
||||
inc esi
|
||||
cmp esi,1000
|
||||
jb fortune_test_walk_down
|
||||
jmp fortune_test_fail
|
||||
fortune_test_arrived:
|
||||
mov keys3D[57h],0
|
||||
cmp worldMap3D,OFFSET fortuneRoomMap3D
|
||||
jne fortune_test_fail
|
||||
cmp floorTexture3D,134
|
||||
jne fortune_test_fail
|
||||
cmp playerY,11*FP_1
|
||||
jne fortune_test_fail
|
||||
mov eax,fortuneSeer3D.posY
|
||||
cmp eax,playerY
|
||||
jne fortune_test_fail
|
||||
invoke CanWalk3D,FP_1*173/10,FP_1*103/10
|
||||
cmp eax,1
|
||||
jne fortune_test_fail
|
||||
invoke CanWalk3D,FP_1*173/10,FP_1*117/10
|
||||
cmp eax,1
|
||||
jne fortune_test_fail
|
||||
; Off-centre crossing preserves lateral position in both local maps.
|
||||
mov playerX,FP_1*176/10
|
||||
mov playerY,FP_1*115/10
|
||||
call UpdateInteriorTravel3D
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne fortune_test_fail
|
||||
cmp playerY,FP_1*105/10
|
||||
jne fortune_test_fail
|
||||
mov playerX,17*FP_1
|
||||
call UpdateInteriorTravel3D
|
||||
cmp hallLevel3D,3
|
||||
jne fortune_test_fail
|
||||
cmp playerY,FP_1*115/10
|
||||
jne fortune_test_fail
|
||||
mov playerY,11*FP_1
|
||||
cmp playerAngle,256
|
||||
jne fortune_test_fail
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestRoom
|
||||
mov playerX,16*FP_1
|
||||
mov playerY,FP_1*116/10
|
||||
mov playerAngle,384
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestNorth
|
||||
mov playerX,16*FP_1
|
||||
mov playerY,FP_1*107/10
|
||||
mov playerAngle,128
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestSouth
|
||||
mov playerX,FP_1*155/10
|
||||
mov playerY,FP_1*116/10
|
||||
mov playerAngle,0
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestEast
|
||||
invoke CanWalk3D,FP_1*146/10,FP_1*126/10
|
||||
test eax,eax
|
||||
jnz fortune_test_fail
|
||||
invoke CanWalk3D,FP_1*163/10,FP_1*925/100
|
||||
test eax,eax
|
||||
jnz fortune_test_fail
|
||||
mov expansionStage,62
|
||||
; A mid-flight camera shows physical treads and continuous eye height.
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov stairRoute3D,3
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET fortuneStairMap3D
|
||||
mov playerX,24*FP_1
|
||||
mov playerY,10*FP_1
|
||||
mov playerAngle,256
|
||||
call UpdateStairHeight3D
|
||||
cmp viewEye3D,HALL_STANDING_EYE+FP_1*15/16
|
||||
jne fortune_test_fail
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestStairs
|
||||
mov playerX,28*FP_1
|
||||
mov playerAngle,0
|
||||
call UpdateStairHeight3D
|
||||
call ExpansionFrame
|
||||
cmp portalPixelsWritten3D,1000
|
||||
jb fortune_test_fail
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestTopReturn
|
||||
; The panel cannot close over somebody still at its upper landing.
|
||||
mov fortunePassageTarget3D,0
|
||||
mov playerX,29*FP_1
|
||||
add animationClock3D,16
|
||||
call UpdateFortunePassage3D
|
||||
cmp fortunePassageTime3D,1400
|
||||
jne fortune_test_fail
|
||||
mov playerX,24*FP_1
|
||||
add animationClock3D,16
|
||||
call UpdateFortunePassage3D
|
||||
cmp fortunePassageTime3D,1400
|
||||
jne fortune_test_fail
|
||||
mov engineMode,MODE_WALK
|
||||
mov hallLevel3D,3
|
||||
add animationClock3D,16
|
||||
call UpdateFortunePassage3D
|
||||
cmp fortunePassageTime3D,1400
|
||||
jne fortune_test_fail
|
||||
mov fortunePassageTarget3D,1
|
||||
mov engineMode,MODE_WALK
|
||||
mov hallLevel3D,3
|
||||
mov worldMap3D,OFFSET fortuneRoomMap3D
|
||||
mov playerX,FP_1*158/10
|
||||
mov playerY,11*FP_1
|
||||
mov playerAngle,256
|
||||
call UpdateInteriorEye3D
|
||||
mov animationClock3D,6000
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestNear
|
||||
call UpdateCamera3D
|
||||
call RenderCeilingAndFloor
|
||||
call RenderWalls
|
||||
call PrepareCityDepth3D
|
||||
call RenderFortuneFurnishings3D
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestFullLight
|
||||
call ApplyFortuneLighting3D
|
||||
mov playerX,FP_1*164/10
|
||||
mov playerY,FP_1*116/10
|
||||
mov playerAngle,288
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestOblique
|
||||
mov playerX,FP_1*158/10
|
||||
mov playerY,11*FP_1
|
||||
mov playerAngle,256
|
||||
mov esi,fortuneMaterialArt3D.pixels
|
||||
mov edi,hallArchitectureHD3D.pixels
|
||||
mov fortuneMaterialArt3D.pixels,0
|
||||
mov hallArchitectureHD3D.pixels,0
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestMissing
|
||||
mov fortuneMaterialArt3D.pixels,esi
|
||||
mov hallArchitectureHD3D.pixels,edi
|
||||
call ExpansionFrame
|
||||
call CheckInteractions
|
||||
cmp interactTargetId,INTERACT_FORTUNE
|
||||
jne fortune_test_fail
|
||||
invoke CanWalk3D,FP_1*141/10,11*FP_1
|
||||
test eax,eax
|
||||
jnz fortune_test_fail
|
||||
mov animationClock3D,5000
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestIdle
|
||||
mov eax,bankroll
|
||||
mov fortuneTestBank,eax
|
||||
mov eax,playerX
|
||||
mov fortuneTestView,eax
|
||||
call Trigger3DInteraction
|
||||
cmp engineMode,MODE_UNAVAILABLE
|
||||
jne fortune_test_fail
|
||||
call RenderUnavailable3D
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestDialog
|
||||
mov pressedKeys3D[0Dh],1
|
||||
call HandleUnavailable3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne fortune_test_fail
|
||||
cmp hallLevel3D,3
|
||||
jne fortune_test_fail
|
||||
mov eax,bankroll
|
||||
cmp eax,fortuneTestBank
|
||||
jne fortune_test_fail
|
||||
mov eax,playerX
|
||||
cmp eax,fortuneTestView
|
||||
jne fortune_test_fail
|
||||
mov playerX,FP_1*1495/100
|
||||
mov playerY,11*FP_1
|
||||
mov playerAngle,0
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestCentered
|
||||
xor esi,esi
|
||||
fortune_test_first_column:
|
||||
cmp portalClipDepth3D[esi*4],0
|
||||
jne fortune_test_find_last
|
||||
inc esi
|
||||
cmp esi,SCREEN_W
|
||||
jb fortune_test_first_column
|
||||
jmp fortune_test_fail
|
||||
fortune_test_find_last:
|
||||
mov edi,SCREEN_W-1
|
||||
fortune_test_last_column:
|
||||
cmp portalClipDepth3D[edi*4],0
|
||||
jne fortune_test_center_mask
|
||||
dec edi
|
||||
jns fortune_test_last_column
|
||||
jmp fortune_test_fail
|
||||
fortune_test_center_mask:
|
||||
add esi,edi
|
||||
cmp esi,SCREEN_W-2
|
||||
jb fortune_test_fail
|
||||
cmp esi,SCREEN_W+2
|
||||
ja fortune_test_fail
|
||||
mov playerX,16*FP_1
|
||||
call ExpansionFrame
|
||||
cmp portalPixelsWritten3D,1000
|
||||
jb fortune_test_fail
|
||||
invoke ExpansionSnapshot,OFFSET fortuneTestReturn
|
||||
mov expansionStage,63
|
||||
mov keys3D[57h],1
|
||||
xor esi,esi
|
||||
fortune_test_walk_up:
|
||||
call ExpansionWalkFrame
|
||||
cmp engineMode,MODE_WALK
|
||||
jne @F
|
||||
cmp hallLevel3D,0
|
||||
je fortune_test_back_hall
|
||||
@@:
|
||||
inc esi
|
||||
cmp esi,1000
|
||||
jb fortune_test_walk_up
|
||||
jmp fortune_test_fail
|
||||
fortune_test_back_hall:
|
||||
mov keys3D[57h],0
|
||||
cmp worldMap3D,OFFSET casinoMap
|
||||
jne fortune_test_fail
|
||||
cmp playerAngle,0
|
||||
jne fortune_test_fail
|
||||
cmp playerX,2*FP_1
|
||||
ja fortune_test_fail
|
||||
mov playerX,FP_1*26/10
|
||||
mov playerY,FP_1*87/10
|
||||
mov playerAngle,256
|
||||
call UpdateCamera3D
|
||||
call CheckInteractions
|
||||
cmp interactTargetId,INTERACT_SECRET_LAMP
|
||||
jne fortune_test_fail
|
||||
call Trigger3DInteraction
|
||||
cmp fortunePassageTarget3D,0
|
||||
jne fortune_test_fail
|
||||
mov deltaTime3D,100
|
||||
mov esi,14
|
||||
fortune_test_closing:
|
||||
add animationClock3D,100
|
||||
call UpdateFortunePassage3D
|
||||
dec esi
|
||||
jnz fortune_test_closing
|
||||
cmp fortunePassageTime3D,0
|
||||
jne fortune_test_fail
|
||||
invoke CanWalk3D,FP_1*15/10,10*FP_1
|
||||
test eax,eax
|
||||
jnz fortune_test_fail
|
||||
cmp scrStation3D.posX,29*FP_1
|
||||
jne fortune_test_fail
|
||||
cmp scrStation3D.posY,FP_1*15/2
|
||||
jne fortune_test_fail
|
||||
ret
|
||||
fortune_test_fail:
|
||||
invoke ExitProcess,expansionStage
|
||||
ret
|
||||
FortuneNativeTests ENDP
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
.data
|
||||
expansionStage dd 1
|
||||
expansionOcclusionPixel dd 0
|
||||
expansionGaitX dd 0
|
||||
expansionGaitY dd 0
|
||||
expansionGaitPhase dd 0
|
||||
expansionDoorState dd 0
|
||||
expansionBmpHeader db 42h,4Dh
|
||||
dd 1228854
|
||||
dw 0,0
|
||||
|
|
@ -8,6 +12,25 @@ expansionBmpHeader db 42h,4Dh
|
|||
dw 1,32
|
||||
dd 0,1228800,0,0,0,0
|
||||
expansionDoor db "build\hall-expansion\basement-door.bmp",0
|
||||
expansionDoorWheel db "build\hall-expansion\door-wheel-turn.bmp",0
|
||||
expansionDoorSwing db "build\hall-expansion\door-swing-half.bmp",0
|
||||
expansionDoorClosing db "build\hall-expansion\door-closing.bmp",0
|
||||
expansionDoorLocking db "build\hall-expansion\door-locking.bmp",0
|
||||
expansionDoorShut db "build\hall-expansion\door-closed-again.bmp",0
|
||||
expansionDoorBack db "build\hall-expansion\door-from-stairs.bmp",0
|
||||
expansionFarUpper db "build\hall-expansion\upper-return-far.bmp",0
|
||||
expansionFarEntry db "build\hall-expansion\upper-entry-far.bmp",0
|
||||
expansionFarBasement db "build\hall-expansion\basement-return-far.bmp",0
|
||||
expansionGait0 db "build\hall-expansion\gait-0.bmp",0
|
||||
expansionGait1 db "build\hall-expansion\gait-1.bmp",0
|
||||
expansionGait2 db "build\hall-expansion\gait-2.bmp",0
|
||||
expansionGait3 db "build\hall-expansion\gait-3.bmp",0
|
||||
expansionGait4 db "build\hall-expansion\gait-4.bmp",0
|
||||
expansionGait5 db "build\hall-expansion\gait-5.bmp",0
|
||||
expansionGait6 db "build\hall-expansion\gait-6.bmp",0
|
||||
expansionGait7 db "build\hall-expansion\gait-7.bmp",0
|
||||
expansionGaitPaths dd OFFSET expansionGait0,OFFSET expansionGait1,OFFSET expansionGait2,OFFSET expansionGait3
|
||||
dd OFFSET expansionGait4,OFFSET expansionGait5,OFFSET expansionGait6,OFFSET expansionGait7
|
||||
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
|
||||
|
|
@ -32,6 +55,8 @@ 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
|
||||
expansionPortalOff db "build\hall-expansion\portal-disabled.bmp",0
|
||||
expansionLeverRelease db "build\hall-expansion\lever-mid-release.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
|
||||
|
|
@ -94,6 +119,16 @@ ExpansionPortalSnapshot PROC path:DWORD
|
|||
ret
|
||||
ExpansionPortalSnapshot ENDP
|
||||
|
||||
ExpansionDoorTicks PROC uses esi count:DWORD
|
||||
mov esi,count
|
||||
expansion_door_tick:
|
||||
add animationClock3D,16
|
||||
call UpdateBasementDoor3D
|
||||
dec esi
|
||||
jnz expansion_door_tick
|
||||
ret
|
||||
ExpansionDoorTicks ENDP
|
||||
|
||||
ExpansionTestStart PROC
|
||||
call InitPaths3D
|
||||
call LoadTextures3D
|
||||
|
|
@ -102,6 +137,12 @@ ExpansionTestStart PROC
|
|||
call LoadHallAssets3D
|
||||
cmp npcGuestArt3D.pixels,0
|
||||
je expansion_fail
|
||||
cmp npcWalkArt3D.frames,48
|
||||
jne expansion_fail
|
||||
cmp npcWalkLeftArt3D.frames,48
|
||||
jne expansion_fail
|
||||
cmp basementDoorArt3D.frames,18
|
||||
jne expansion_fail
|
||||
cmp cabaretArt3D.pixels,0
|
||||
je expansion_fail
|
||||
cmp cabaretPianoArt3D.pixels,0
|
||||
|
|
@ -126,25 +167,57 @@ ExpansionTestStart PROC
|
|||
mov engineMode,MODE_WALK
|
||||
mov hallLevel3D,0
|
||||
mov expansionStage,2
|
||||
invoke CanWalk3D,FP_1*285/10,FP_1*105/10
|
||||
invoke CanWalk3D,FP_1*289/10,FP_1*105/10
|
||||
test eax,eax
|
||||
jnz expansion_fail
|
||||
mov interactTargetId,INTERACT_BASEMENT
|
||||
call TriggerHallInteraction3D
|
||||
mov playerX,27*FP_1
|
||||
mov playerY,FP_1*105/10
|
||||
mov playerAngle,0
|
||||
add animationClock3D,16
|
||||
call UpdateInteriorTravel3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne expansion_fail
|
||||
cmp basementDoorTarget3D,FP_1
|
||||
jne expansion_fail
|
||||
cmp basementDoorOpen3D,0
|
||||
jne expansion_fail
|
||||
xor esi,esi
|
||||
expansion_open_door:
|
||||
add animationClock3D,16
|
||||
call UpdateInteriorTravel3D
|
||||
inc esi
|
||||
cmp esi,60
|
||||
cmp esi,20
|
||||
jb expansion_open_door
|
||||
cmp basementWheelTime3D,0
|
||||
je expansion_fail
|
||||
cmp basementDoorOpen3D,0
|
||||
jne expansion_fail
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionDoorWheel
|
||||
mov esi,50
|
||||
expansion_door_swing:
|
||||
add animationClock3D,16
|
||||
call UpdateInteriorTravel3D
|
||||
dec esi
|
||||
jnz expansion_door_swing
|
||||
cmp basementWheelTime3D,700
|
||||
jne expansion_fail
|
||||
cmp basementDoorOpen3D,0
|
||||
je expansion_fail
|
||||
cmp basementDoorOpen3D,FP_1
|
||||
jae expansion_fail
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionDoorSwing
|
||||
mov esi,50
|
||||
expansion_finish_door:
|
||||
add animationClock3D,16
|
||||
call UpdateInteriorTravel3D
|
||||
dec esi
|
||||
jnz expansion_finish_door
|
||||
cmp basementDoorOpen3D,FP_1
|
||||
jne expansion_fail
|
||||
invoke CanWalk3D,FP_1*285/10,FP_1*105/10
|
||||
invoke CanWalk3D,FP_1*289/10,FP_1*105/10
|
||||
cmp eax,1
|
||||
jne expansion_fail
|
||||
mov playerX,27*FP_1
|
||||
|
|
@ -477,11 +550,48 @@ expansion_cast_positions:
|
|||
mov playerAngle,128
|
||||
invoke ExpansionPortalSnapshot,OFFSET expansionBunkerReturn
|
||||
mov expansionStage,28
|
||||
; These viewpoints used to lose their continuation at the eight-metre cutoff.
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov stairRoute3D,2
|
||||
mov worldMap3D,OFFSET upperStairMap3D
|
||||
mov floorTexture3D,85
|
||||
mov playerX,12*FP_1
|
||||
mov playerY,21*FP_1
|
||||
mov playerAngle,256
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
cmp portalPixelsWritten3D,100
|
||||
jb expansion_fail
|
||||
invoke ExpansionSnapshot,OFFSET expansionFarUpper
|
||||
mov engineMode,MODE_WALK
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
mov floorTexture3D,100
|
||||
mov playerX,21*FP_1
|
||||
mov playerAngle,0
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
cmp portalPixelsWritten3D,100
|
||||
jb expansion_fail
|
||||
invoke ExpansionSnapshot,OFFSET expansionFarEntry
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov stairRoute3D,1
|
||||
mov worldMap3D,OFFSET basementStairMap3D
|
||||
mov floorTexture3D,94
|
||||
mov playerX,12*FP_1
|
||||
mov playerAngle,256
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
cmp portalPixelsWritten3D,100
|
||||
jb expansion_fail
|
||||
invoke ExpansionSnapshot,OFFSET expansionFarBasement
|
||||
mov engineMode,MODE_WALK
|
||||
mov hallLevel3D,2
|
||||
mov worldMap3D,OFFSET upperLoungeMap3D
|
||||
mov floorTexture3D,124
|
||||
mov playerX,FP_1*105/10
|
||||
mov playerY,FP_1*193/10
|
||||
mov playerAngle,128
|
||||
mov upperLeverMask3D,0
|
||||
mov upperPortalReady3D,0
|
||||
mov upperLeverTime3D,0
|
||||
|
|
@ -543,6 +653,119 @@ expansion_portal_motion:
|
|||
inc esi
|
||||
cmp esi,12
|
||||
jb expansion_portal_motion
|
||||
mov expansionStage,29
|
||||
mov playerX,FP_1*91/10
|
||||
mov playerY,FP_1*214/10
|
||||
mov playerAngle,128
|
||||
call UpdateCamera3D
|
||||
call CheckUpperInteractions3D
|
||||
cmp interactTargetId,INTERACT_UPPER_LEFT
|
||||
jne expansion_fail
|
||||
cmp interactPromptPtr,OFFSET upperLeftRelease3D
|
||||
jne expansion_fail
|
||||
call TriggerHallInteraction3D
|
||||
cmp upperLeverMask3D,2
|
||||
jne expansion_fail
|
||||
cmp upperPortalReady3D,0
|
||||
jne expansion_fail
|
||||
mov playerX,FP_1*105/10
|
||||
mov playerY,FP_1*244/10
|
||||
call TryWalkUpperPortal3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne expansion_fail
|
||||
mov interactTargetId,INTERACT_HELL_TOWER
|
||||
call TriggerHallInteraction3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne expansion_fail
|
||||
mov playerY,FP_1*193/10
|
||||
call ExpansionFrame
|
||||
cmp upperPortal3D.frame,0
|
||||
jne expansion_fail
|
||||
invoke ExpansionSnapshot,OFFSET expansionPortalOff
|
||||
mov esi,20
|
||||
expansion_release_half:
|
||||
add animationClock3D,16
|
||||
call UpdateUpperEffects3D
|
||||
dec esi
|
||||
jnz expansion_release_half
|
||||
cmp upperLeverTime3D,280
|
||||
jne expansion_fail
|
||||
cmp upperLeverTime3D[4],600
|
||||
jne expansion_fail
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionLeverRelease
|
||||
; Reverse the handle halfway through release, without snapping to an endpoint.
|
||||
mov interactTargetId,INTERACT_UPPER_LEFT
|
||||
call TriggerHallInteraction3D
|
||||
cmp upperLeverTime3D,280
|
||||
jne expansion_fail
|
||||
cmp upperPortalReady3D,0
|
||||
jne expansion_fail
|
||||
mov esi,20
|
||||
expansion_reenable_half:
|
||||
add animationClock3D,16
|
||||
call UpdateUpperEffects3D
|
||||
dec esi
|
||||
jnz expansion_reenable_half
|
||||
cmp upperPortalReady3D,1
|
||||
jne expansion_fail
|
||||
; The right handle is also available after activation, and disables immediately.
|
||||
mov playerX,FP_1*119/10
|
||||
mov playerY,FP_1*214/10
|
||||
call CheckUpperInteractions3D
|
||||
cmp interactTargetId,INTERACT_UPPER_RIGHT
|
||||
jne expansion_fail
|
||||
cmp interactPromptPtr,OFFSET upperRightRelease3D
|
||||
jne expansion_fail
|
||||
call TriggerHallInteraction3D
|
||||
cmp upperLeverMask3D,1
|
||||
jne expansion_fail
|
||||
cmp upperPortalReady3D,0
|
||||
jne expansion_fail
|
||||
mov esi,40
|
||||
expansion_release_right:
|
||||
add animationClock3D,16
|
||||
call UpdateUpperEffects3D
|
||||
dec esi
|
||||
jnz expansion_release_right
|
||||
cmp upperLeverTime3D[4],0
|
||||
jne expansion_fail
|
||||
cmp upperLeverTime3D,600
|
||||
jne expansion_fail
|
||||
mov interactTargetId,INTERACT_UPPER_LEFT
|
||||
call TriggerHallInteraction3D
|
||||
mov esi,40
|
||||
expansion_release_both:
|
||||
add animationClock3D,16
|
||||
call UpdateUpperEffects3D
|
||||
dec esi
|
||||
jnz expansion_release_both
|
||||
cmp upperLeverMask3D,0
|
||||
jne expansion_fail
|
||||
cmp upperLeverTime3D,0
|
||||
jne expansion_fail
|
||||
cmp upperLeverTime3D[4],0
|
||||
jne expansion_fail
|
||||
cmp upperPortalReady3D,0
|
||||
jne expansion_fail
|
||||
call CheckUpperInteractions3D
|
||||
cmp interactTargetId,INTERACT_UPPER_RIGHT
|
||||
jne expansion_fail
|
||||
cmp interactPromptPtr,OFFSET upperRightPrompt3D
|
||||
jne expansion_fail
|
||||
mov interactTargetId,INTERACT_UPPER_LEFT
|
||||
call TriggerHallInteraction3D
|
||||
mov interactTargetId,INTERACT_UPPER_RIGHT
|
||||
call TriggerHallInteraction3D
|
||||
mov esi,40
|
||||
expansion_reopen_both:
|
||||
add animationClock3D,16
|
||||
call UpdateUpperEffects3D
|
||||
dec esi
|
||||
jnz expansion_reopen_both
|
||||
cmp upperPortalReady3D,1
|
||||
jne expansion_fail
|
||||
mov playerX,FP_1*105/10
|
||||
mov playerY,FP_1*244/10
|
||||
call TryWalkUpperPortal3D
|
||||
cmp engineMode,MODE_HELL_TOWER
|
||||
|
|
@ -591,8 +814,358 @@ expansion_portal_motion:
|
|||
mov hRaycastThread,0
|
||||
mov hRaycastStartEvent,0
|
||||
mov hRaycastDoneEvent,0
|
||||
mov expansionStage,31
|
||||
mov hallLevel3D,0
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
; Equal walked distance at two update rates must select the same gait phase.
|
||||
call InitNpcLife3D
|
||||
invoke NpcPlanPath3D,0,5
|
||||
test eax,eax
|
||||
jz expansion_fail
|
||||
mov npcLife3D.state,NPC_WALK
|
||||
mov esi,10
|
||||
expansion_gait_rate16:
|
||||
invoke NpcMove3D,0,16
|
||||
dec esi
|
||||
jnz expansion_gait_rate16
|
||||
mov eax,casinoNpcs3D.posX
|
||||
mov expansionGaitX,eax
|
||||
mov eax,casinoNpcs3D.posY
|
||||
mov expansionGaitY,eax
|
||||
mov eax,npcWalkDistance3D
|
||||
mov expansionGaitPhase,eax
|
||||
call InitNpcLife3D
|
||||
invoke NpcPlanPath3D,0,5
|
||||
test eax,eax
|
||||
jz expansion_fail
|
||||
mov npcLife3D.state,NPC_WALK
|
||||
mov esi,5
|
||||
expansion_gait_rate32:
|
||||
invoke NpcMove3D,0,32
|
||||
dec esi
|
||||
jnz expansion_gait_rate32
|
||||
mov eax,casinoNpcs3D.posX
|
||||
cmp eax,expansionGaitX
|
||||
jne expansion_fail
|
||||
mov playerX,eax
|
||||
mov eax,casinoNpcs3D.posY
|
||||
cmp eax,expansionGaitY
|
||||
jne expansion_fail
|
||||
mov playerY,eax
|
||||
mov eax,npcWalkDistance3D
|
||||
cmp eax,expansionGaitPhase
|
||||
jne expansion_fail
|
||||
invoke NpcMove3D,0,16
|
||||
mov eax,npcWalkDistance3D
|
||||
cmp eax,expansionGaitPhase
|
||||
jne expansion_fail
|
||||
cmp npcLife3D.state,NPC_STAND
|
||||
jne expansion_fail
|
||||
mov playerX,FP_1*285/10
|
||||
mov playerY,21*FP_1
|
||||
call InitNpcLife3D
|
||||
; A blocked move must not tick the gait. A successful one consumes distance.
|
||||
mov npcLife3D.state,NPC_WALK
|
||||
mov npcLife3D.pathCount,0
|
||||
invoke NpcMove3D,0,16
|
||||
cmp npcWalkDistance3D,0
|
||||
jne expansion_fail
|
||||
invoke NpcPlanPath3D,0,5
|
||||
test eax,eax
|
||||
jz expansion_fail
|
||||
mov npcLife3D.state,NPC_WALK
|
||||
invoke NpcMove3D,0,16
|
||||
cmp npcWalkDistance3D,0
|
||||
je expansion_fail
|
||||
mov eax,npcWalkDistance3D
|
||||
mov expansionOcclusionPixel,eax
|
||||
invoke NpcMove3D,0,0
|
||||
mov eax,npcWalkDistance3D
|
||||
cmp eax,expansionOcclusionPixel
|
||||
jne expansion_fail
|
||||
; Native renders of each gait phase, not just hashes of offline source art.
|
||||
mov esi,1
|
||||
expansion_gait_hide:
|
||||
imul ebx,esi,SIZEOF NpcLife3D
|
||||
mov npcLife3D[ebx].state,NPC_AWAY
|
||||
inc esi
|
||||
cmp esi,NPC_LIFE_COUNT
|
||||
jb expansion_gait_hide
|
||||
mov npcLife3D.state,NPC_WALK
|
||||
mov npcLife3D.identity,0
|
||||
mov casinoNpcs3D.posX,FP_1*165/10
|
||||
mov casinoNpcs3D.posY,FP_1*175/10
|
||||
mov npcWalkVelocityX3D,FP_1
|
||||
mov npcWalkVelocityY3D,0
|
||||
mov playerX,FP_1*165/10
|
||||
mov playerY,FP_1*195/10
|
||||
mov playerAngle,384
|
||||
call UpdateInteriorEye3D
|
||||
xor esi,esi
|
||||
expansion_gait_frame:
|
||||
imul eax,esi,FP_1*3/32
|
||||
mov npcWalkDistance3D,eax
|
||||
call UpdateCamera3D
|
||||
call RenderCeilingAndFloor
|
||||
call RenderWalls
|
||||
call PrepareCityDepth3D
|
||||
call RenderNpcLife3D
|
||||
mov eax,npcSprite3D.frame
|
||||
cmp eax,esi
|
||||
jne expansion_fail
|
||||
invoke ExpansionSnapshot,expansionGaitPaths[esi*4]
|
||||
inc esi
|
||||
cmp esi,8
|
||||
jb expansion_gait_frame
|
||||
mov npcWalkVelocityX3D,-FP_1
|
||||
call PrepareCityDepth3D
|
||||
call RenderNpcLife3D
|
||||
cmp npcSprite3D.asset,OFFSET npcWalkLeftArt3D
|
||||
jne expansion_fail
|
||||
mov expansionStage,32
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov stairRoute3D,1
|
||||
mov worldMap3D,OFFSET basementStairMap3D
|
||||
mov floorTexture3D,94
|
||||
mov playerX,FP_1*46/10
|
||||
mov playerY,21*FP_1
|
||||
mov playerAngle,256
|
||||
mov basementDoorTarget3D,0
|
||||
mov basementDoorOpen3D,0
|
||||
mov basementWheelTime3D,0
|
||||
invoke CanWalk3D,FP_1*39/10,FP_1*227/10
|
||||
test eax,eax
|
||||
jnz expansion_fail
|
||||
mov keys3D[57h],1
|
||||
mov esi,10
|
||||
expansion_return_closed:
|
||||
call ExpansionWalkFrame
|
||||
dec esi
|
||||
jnz expansion_return_closed
|
||||
cmp engineMode,MODE_STAIRS
|
||||
jne expansion_fail
|
||||
cmp playerX,FP_1*405/100
|
||||
jb expansion_fail
|
||||
cmp basementDoorTarget3D,FP_1
|
||||
jne expansion_fail
|
||||
cmp basementDoorOpen3D,0
|
||||
jne expansion_fail
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionDoorBack
|
||||
mov esi,150
|
||||
expansion_return_auto_open:
|
||||
call ExpansionWalkFrame
|
||||
cmp engineMode,MODE_WALK
|
||||
je expansion_return_opened
|
||||
dec esi
|
||||
jnz expansion_return_auto_open
|
||||
jmp expansion_fail
|
||||
expansion_return_opened:
|
||||
mov keys3D[57h],0
|
||||
cmp hallLevel3D,0
|
||||
jne expansion_fail
|
||||
cmp basementWheelTime3D,700
|
||||
jne expansion_fail
|
||||
cmp basementDoorOpen3D,FP_1*9/10
|
||||
jb expansion_fail
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov stairRoute3D,1
|
||||
mov worldMap3D,OFFSET basementStairMap3D
|
||||
invoke CanWalk3D,FP_1*39/10,FP_1*227/10
|
||||
test eax,eax
|
||||
jnz expansion_fail
|
||||
invoke CanWalk3D,FP_1*39/10,21*FP_1
|
||||
cmp eax,1
|
||||
jne expansion_fail
|
||||
mov engineMode,MODE_WALK
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
mov expansionStage,33
|
||||
mov playerX,FP_1*305/10
|
||||
mov playerY,FP_1*305/10
|
||||
call InitNpcLife3D
|
||||
mov deltaTime3D,50
|
||||
mov esi,2400
|
||||
expansion_long_gait:
|
||||
call UpdateNpcLife3D
|
||||
xor edi,edi
|
||||
expansion_gait_bounds:
|
||||
cmp npcWalkDistance3D[edi*4],FP_1*3/4
|
||||
jae expansion_fail
|
||||
imul ebx,edi,SIZEOF NpcMindEntity
|
||||
invoke CanWalk3D,casinoNpcs3D[ebx].posX,casinoNpcs3D[ebx].posY
|
||||
cmp eax,1
|
||||
jne expansion_fail
|
||||
inc edi
|
||||
cmp edi,NPC_LIFE_COUNT
|
||||
jb expansion_gait_bounds
|
||||
dec esi
|
||||
jnz expansion_long_gait
|
||||
cmp npcLifeClock3D,120000
|
||||
jne expansion_fail
|
||||
mov expansionStage,34
|
||||
mov deltaTime3D,16
|
||||
mov floorTexture3D,100
|
||||
mov playerX,FP_1*288/10
|
||||
mov playerY,FP_1*105/10
|
||||
mov playerAngle,0
|
||||
mov basementDoorTarget3D,FP_1
|
||||
mov basementDoorOpen3D,FP_1
|
||||
mov basementWheelTime3D,700
|
||||
mov basementDoorHold3D,0
|
||||
invoke ExpansionDoorTicks,200
|
||||
cmp basementDoorOpen3D,FP_1
|
||||
jne expansion_fail
|
||||
cmp basementDoorHold3D,1200
|
||||
jne expansion_fail
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov stairRoute3D,1
|
||||
mov worldMap3D,OFFSET basementStairMap3D
|
||||
mov playerX,FP_1*43/10
|
||||
mov playerY,21*FP_1
|
||||
invoke ExpansionDoorTicks,200
|
||||
cmp basementDoorOpen3D,FP_1
|
||||
jne expansion_fail
|
||||
cmp basementWheelTime3D,700
|
||||
jne expansion_fail
|
||||
mov engineMode,MODE_WALK
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
mov playerX,25*FP_1
|
||||
mov playerY,FP_1*105/10
|
||||
add animationClock3D,16
|
||||
call UpdateBasementDoor3D
|
||||
cmp basementDoorHold3D,1184
|
||||
jne expansion_fail
|
||||
call UpdateBasementDoor3D
|
||||
cmp basementDoorHold3D,1184
|
||||
jne expansion_fail
|
||||
invoke ExpansionDoorTicks,74
|
||||
cmp basementDoorHold3D,0
|
||||
jne expansion_fail
|
||||
cmp basementDoorOpen3D,FP_1
|
||||
jne expansion_fail
|
||||
invoke ExpansionDoorTicks,20
|
||||
cmp basementDoorOpen3D,0
|
||||
je expansion_fail
|
||||
cmp basementDoorOpen3D,FP_1
|
||||
jae expansion_fail
|
||||
cmp basementWheelTime3D,700
|
||||
jne expansion_fail
|
||||
mov eax,basementDoorOpen3D
|
||||
mov expansionDoorState,eax
|
||||
mov playerX,27*FP_1
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionDoorClosing
|
||||
; Returning during the swing reverses smoothly and keeps the wheel unlocked.
|
||||
invoke ExpansionDoorTicks,1
|
||||
mov eax,basementDoorOpen3D
|
||||
cmp eax,expansionDoorState
|
||||
jbe expansion_fail
|
||||
cmp eax,FP_1
|
||||
jae expansion_fail
|
||||
cmp basementWheelTime3D,700
|
||||
jne expansion_fail
|
||||
invoke ExpansionDoorTicks,75
|
||||
cmp basementDoorOpen3D,FP_1
|
||||
jne expansion_fail
|
||||
mov playerX,25*FP_1
|
||||
invoke ExpansionDoorTicks,75
|
||||
mov esi,70
|
||||
expansion_wait_closed_leaf:
|
||||
invoke ExpansionDoorTicks,1
|
||||
cmp basementDoorOpen3D,0
|
||||
je expansion_leaf_shut
|
||||
cmp basementWheelTime3D,700
|
||||
jne expansion_fail
|
||||
dec esi
|
||||
jnz expansion_wait_closed_leaf
|
||||
jmp expansion_fail
|
||||
expansion_leaf_shut:
|
||||
cmp basementWheelTime3D,600
|
||||
jbe expansion_fail
|
||||
invoke ExpansionDoorTicks,20
|
||||
cmp basementDoorOpen3D,0
|
||||
jne expansion_fail
|
||||
cmp basementWheelTime3D,0
|
||||
je expansion_fail
|
||||
cmp basementWheelTime3D,700
|
||||
jae expansion_fail
|
||||
mov eax,basementWheelTime3D
|
||||
mov expansionDoorState,eax
|
||||
mov playerX,27*FP_1
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET expansionDoorLocking
|
||||
; Returning during locking unlocks from that pose before moving the leaf.
|
||||
invoke ExpansionDoorTicks,1
|
||||
cmp basementDoorOpen3D,0
|
||||
jne expansion_fail
|
||||
mov eax,basementWheelTime3D
|
||||
cmp eax,expansionDoorState
|
||||
jbe expansion_fail
|
||||
invoke ExpansionDoorTicks,100
|
||||
cmp basementDoorOpen3D,FP_1
|
||||
jne expansion_fail
|
||||
mov playerX,25*FP_1
|
||||
invoke ExpansionDoorTicks,200
|
||||
cmp basementDoorTarget3D,0
|
||||
jne expansion_fail
|
||||
cmp basementDoorOpen3D,0
|
||||
jne expansion_fail
|
||||
cmp basementWheelTime3D,0
|
||||
jne expansion_fail
|
||||
invoke CanWalk3D,FP_1*289/10,FP_1*105/10
|
||||
test eax,eax
|
||||
jnz expansion_fail
|
||||
mov playerX,27*FP_1
|
||||
call ExpansionFrame
|
||||
cmp portalPixelsWritten3D,0
|
||||
jne expansion_fail
|
||||
invoke ExpansionSnapshot,OFFSET expansionDoorShut
|
||||
mov engineMode,MODE_STAIRS
|
||||
mov stairRoute3D,1
|
||||
mov worldMap3D,OFFSET basementStairMap3D
|
||||
mov playerX,FP_1*43/10
|
||||
mov playerY,21*FP_1
|
||||
invoke ExpansionDoorTicks,120
|
||||
cmp basementDoorOpen3D,FP_1
|
||||
jne expansion_fail
|
||||
mov playerX,8*FP_1
|
||||
invoke ExpansionDoorTicks,200
|
||||
cmp basementDoorOpen3D,0
|
||||
jne expansion_fail
|
||||
cmp basementWheelTime3D,0
|
||||
jne expansion_fail
|
||||
invoke CanWalk3D,FP_1*39/10,21*FP_1
|
||||
test eax,eax
|
||||
jnz expansion_fail
|
||||
mov playerX,FP_1*43/10
|
||||
invoke ExpansionDoorTicks,120
|
||||
cmp basementDoorOpen3D,FP_1
|
||||
jne expansion_fail
|
||||
; Optional E also works outside the automatic opening radius, then expires.
|
||||
mov engineMode,MODE_WALK
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
mov playerX,25*FP_1
|
||||
mov playerY,FP_1*105/10
|
||||
mov basementDoorOpen3D,0
|
||||
mov basementWheelTime3D,0
|
||||
mov basementDoorHold3D,0
|
||||
mov interactTargetId,INTERACT_BASEMENT
|
||||
call TriggerHallInteraction3D
|
||||
invoke ExpansionDoorTicks,110
|
||||
cmp basementDoorOpen3D,FP_1
|
||||
jne expansion_fail
|
||||
invoke ExpansionDoorTicks,200
|
||||
cmp basementDoorOpen3D,0
|
||||
jne expansion_fail
|
||||
cmp basementWheelTime3D,0
|
||||
jne expansion_fail
|
||||
call FortuneNativeTests
|
||||
call BarCameraNativeTests
|
||||
invoke ExitProcess,0
|
||||
expansion_fail:
|
||||
invoke ExitProcess,expansionStage
|
||||
ret
|
||||
ExpansionTestStart ENDP
|
||||
include tools/tests/fortune_room_test.inc
|
||||
include tools/tests/bar_camera_test.inc
|
||||
|
|
|
|||
|
|
@ -295,11 +295,12 @@ hall_test_back_in_hall:
|
|||
call RenderBar3D
|
||||
invoke WriteActionSnapshot3D,OFFSET hallTestDrink3D
|
||||
mov barSelection3D, 2
|
||||
mov barIntoxication3D, 60000
|
||||
mov barTime3D, 2390
|
||||
mov deltaTime3D, 16
|
||||
call HandleBar3D
|
||||
cmp barIntoxication3D, 0
|
||||
cmp barPhase3D, 0
|
||||
jne hall_test_fail
|
||||
cmp barTime3D, 0
|
||||
jne hall_test_fail
|
||||
mov hallTestStage3D, 73
|
||||
call InitNativeWheel3D
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ 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
|
||||
guestWalkProbePath db "assets\animations\hall\guest-walk-v2.bin",0
|
||||
guestWalkLeftProbePath db "assets\animations\hall\guest-walk-left-v2.bin",0
|
||||
basementDoorProbePath db "assets\animations\hall\basement-door-v2.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
|
||||
|
|
@ -56,6 +59,8 @@ 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
|
||||
fortuneRoomProbePath db "assets\animations\hall\fortune-room-v3.bin",0
|
||||
fortuneMaterialsProbePath db "assets\animations\hall\fortune-materials-v3.bin",0
|
||||
furniturePaths dd OFFSET cardFurniturePath,320,208,1
|
||||
dd OFFSET wheelFurniturePath,288,268,1
|
||||
dd OFFSET backFurniturePath,304,236,1
|
||||
|
|
@ -76,6 +81,9 @@ furniturePaths dd OFFSET cardFurniturePath,320,208,1
|
|||
dd OFFSET terminalWorldProbePath,256,512,4
|
||||
dd OFFSET terminalScreenProbePath,640,480,1
|
||||
dd OFFSET guestCastProbePath,128,224,36
|
||||
dd OFFSET guestWalkProbePath,128,224,48
|
||||
dd OFFSET guestWalkLeftProbePath,128,224,48
|
||||
dd OFFSET basementDoorProbePath,256,512,18
|
||||
dd OFFSET cabaretProbePath,160,256,12
|
||||
dd OFFSET cabaretPianoProbePath,384,256,4
|
||||
dd OFFSET cabaretGuitarProbePath,160,256,4
|
||||
|
|
@ -85,6 +93,8 @@ furniturePaths dd OFFSET cardFurniturePath,320,208,1
|
|||
dd OFFSET upperPortalProbePath,256,512,13
|
||||
dd OFFSET upperLeversProbePath,128,224,6
|
||||
dd OFFSET upperBraziersProbePath,128,256,4
|
||||
dd OFFSET fortuneRoomProbePath,256,256,24
|
||||
dd OFFSET fortuneMaterialsProbePath,256,256,4
|
||||
furniturePathCount equ ($-furniturePaths)/16
|
||||
.code
|
||||
RunStreetAssetProbe PROC
|
||||
|
|
|
|||