Merge branch 'main' of https://git.silverstone.crazedns.ru/Billi_Bob/KAZIK
# Conflicts: # build/graphics/poker.exe # poker.exe # poker.obj
2
.gitignore
vendored
|
|
@ -30,6 +30,8 @@ __pycache__/
|
|||
/build/buckshot/
|
||||
/build/coin/
|
||||
/build/dice/
|
||||
/build/darts/
|
||||
/build/observer/
|
||||
/build/hall/
|
||||
/build/icon_work/
|
||||
/build/scratch/
|
||||
|
|
|
|||
BIN
assets/animations/darts/arm-throw-v3.bin
Normal file
BIN
assets/animations/darts/board-v1.bin
Normal file
BIN
assets/animations/darts/dart-views-v1.bin
Normal file
BIN
assets/animations/darts/lounge-wall-v1.bin
Normal file
BIN
assets/animations/fortune/cards-v1.bin
Normal file
BIN
assets/animations/fortune/deal-v1.bin
Normal file
BIN
assets/animations/fortune/gesture-v1.bin
Normal file
BIN
assets/animations/fortune/orb-hands-v1.bin
Normal file
BIN
assets/animations/fortune/orb-mist-v1.bin
Normal file
BIN
assets/animations/fortune/room-v1.bin
Normal file
BIN
assets/animations/fortune/shuffle-v1.bin
Normal file
BIN
assets/sprites/darts/arm-throw-v3.png
Normal file
|
After Width: | Height: | Size: 1.2 MiB |
BIN
assets/sprites/darts/board-v1.png
Normal file
|
After Width: | Height: | Size: 194 KiB |
BIN
assets/sprites/darts/dart-views-v1.png
Normal file
|
After Width: | Height: | Size: 900 KiB |
BIN
assets/sprites/darts/lounge-wall-v1.png
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
80
assets/sprites/darts/prompts-v1.md
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# Darts Artwork
|
||||
|
||||
Generated with the built-in image-generation tool. Runtime assets live in
|
||||
`assets/animations/darts/`; original selected PNGs are in this directory.
|
||||
All grid cells are extracted at one common scale. No separately generated limb
|
||||
pieces or stretched forearms are used.
|
||||
|
||||
## arm-throw-v3.png
|
||||
|
||||
Sports game animation sprites, harmless pub darts. Create a 4x4 equal-grid
|
||||
transparent sprite atlas, 16 frames, square 1536x1536. FIRST PERSON from the
|
||||
thrower's eyes looking at their OWN RIGHT forearm and hand aiming a small
|
||||
regulation sports dart FORWARD INTO THE SCREEN toward a distant dartboard,
|
||||
NOT horizontally sideways across the picture. Strong perspective foreshortening:
|
||||
the blue tail flights are closest to viewer; steel point points AWAY FROM
|
||||
VIEWER, deeper into scene, above and slightly left of flights. Entire forearm
|
||||
and visible bent elbow, dark shirt rolled ABOVE elbow. Right upper arm extends
|
||||
toward lower right, elbow is near bottom right, forearm rises toward upper
|
||||
middle. Natural darts throwing posture seen from own eyes, not another person's
|
||||
side-on arm. Detailed realistic digitized retro game sprite art, natural skin,
|
||||
silver knurled barrel, blue nylon shaft and blue cross flights. Row1 four similar
|
||||
forward aiming poses. Row2 four gradual drawback poses bending elbow toward
|
||||
viewer. Row3 four gradual FORWARD THROW poses straightening elbow and opening
|
||||
fingertips; dart disappears after frame10, frame11 and12 are empty hand. Row4
|
||||
four empty-handed follow-through/recovery poses, whole forearm still visible.
|
||||
Same anatomy, elbow anchor, scale and lighting across frames. EACH WHOLE arm
|
||||
stays inside its exact grid cell, 10 percent clear transparent margin all sides,
|
||||
no overflow into neighboring cells. At most one coherent arm per cell. No scene,
|
||||
no board, no faces, no borders, no text, no artifacts, no wrist-only framing, no
|
||||
floating dart separated from fingers. Actual alpha transparent background.
|
||||
|
||||
## dart-views-v1.png
|
||||
|
||||
Asset sheet for a pub DARTS sports video game: ONLY a small realistic regulation
|
||||
steel-tip SPORTS DART, no people, hands or other objects. 4 by 4 equal square
|
||||
grid, 16 different coherent views on 1536x1536 transparent canvas. Same dart
|
||||
every cell: short sharp silver steel point, cylindrical metallic silver tungsten
|
||||
barrel with many machined knurled grooves and dark narrow grip rings, slim dark
|
||||
blue nylon shaft, translucent rich royal-blue four-cross tail flights with
|
||||
realistic curved edges and a small white stripe. Detailed realistic digitized
|
||||
game rendering, NOT low-poly, not paper triangles. The sporting dart is about
|
||||
14cm long and delicate, not a weapon. Rotate camera around the dart: row1 rear
|
||||
three-quarter views angle 0,6,12,18 degrees; row2 24,30,36,42 degrees; row3
|
||||
48,54,60,66 degrees; row4 72,78,84,90 degrees. Angle0: tail blue flights closest
|
||||
to camera, point recedes straight away and is mostly hidden. Angle90: complete
|
||||
clean SIDE VIEW, steel point to LEFT, blue tail to RIGHT. All intermediate angles
|
||||
smoothly reveal the left-pointing silver barrel. Horizontal projected axis, no
|
||||
vertical roll. Each entire dart centered in its own cell, same scale, 15 percent
|
||||
transparent margin all sides. Rear views are naturally narrow in width, side
|
||||
views longer, same physical scale. Studio highlights for clear metal detail,
|
||||
sharp blue tail. NO background, checkerboard, grid lines, captions, text or
|
||||
shadows outside the isolated dart. Actual alpha transparency.
|
||||
|
||||
## lounge-wall-v1.png
|
||||
|
||||
A production game environment texture: straight-on FLAT FRONT ELEVATION of the
|
||||
wide darts-lounge wall inside a richly furnished vintage casino. Square
|
||||
1024x1024 raster asset, realistic digitized 1990s adventure game art, crisp small
|
||||
details. NOT a room perspective photo, no sidewalls, no floor or ceiling. Wall
|
||||
occupies full image. Deep muted emerald painted plaster above waist-high dark
|
||||
walnut wainscoting, brass trim, cream plaster details, burgundy accents, varied
|
||||
restrained colors. Central wall area around x50%,y42% must be clean emerald
|
||||
plaster with space for a separate dartboard, no board drawn. Left around
|
||||
x26%,y42%: substantial black chalkboard in an aged brass frame with subtle
|
||||
hand-written darts scores and tally marks. Right around x76%,y42%: illuminated
|
||||
glass trophy cabinet with silver cups, darts memorabilia, folded burgundy cloth,
|
||||
a framed old tournament photo. Warm-shaded brass wall lamps around x33% and
|
||||
x67% at y23%, realistic pools of warm light on plaster, a small analog clock
|
||||
above centre. Lower quarter: wainscot, two low cabinets and a narrow shelf for
|
||||
chalk, coasters and a folded towel; leave centre lower wall clear. The composition
|
||||
should read as an established pub-sports corner of an elegant casino, not an
|
||||
elevator, not a small wooden box, not sci-fi, not orange all over. All objects in
|
||||
true front elevation, fixed to wall, coherent scale and consistent lighting.
|
||||
No people, hands, sports dart closeup, text labels, logos, watermark or frame
|
||||
outside the image.
|
||||
|
||||
## board-v1.png
|
||||
|
||||
Deterministically generated exact 20-sector sports geometry, not AI artwork.
|
||||
The sector order and scoring ring radii are shared with native scoring tests.
|
||||
BIN
assets/sprites/fortune/crystal-mist-v1.png
Normal file
|
After Width: | Height: | Size: 2.3 MiB |
44
assets/sprites/fortune/packed-v1.json
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
[
|
||||
{
|
||||
"file": "room-v1.bin",
|
||||
"width": 640,
|
||||
"height": 480,
|
||||
"frames": 1
|
||||
},
|
||||
{
|
||||
"file": "shuffle-v1.bin",
|
||||
"width": 256,
|
||||
"height": 256,
|
||||
"frames": 64
|
||||
},
|
||||
{
|
||||
"file": "deal-v1.bin",
|
||||
"width": 256,
|
||||
"height": 256,
|
||||
"frames": 32
|
||||
},
|
||||
{
|
||||
"file": "gesture-v1.bin",
|
||||
"width": 256,
|
||||
"height": 256,
|
||||
"frames": 32
|
||||
},
|
||||
{
|
||||
"file": "orb-hands-v1.bin",
|
||||
"width": 256,
|
||||
"height": 256,
|
||||
"frames": 32
|
||||
},
|
||||
{
|
||||
"file": "orb-mist-v1.bin",
|
||||
"width": 192,
|
||||
"height": 192,
|
||||
"frames": 16
|
||||
},
|
||||
{
|
||||
"file": "cards-v1.bin",
|
||||
"width": 96,
|
||||
"height": 128,
|
||||
"frames": 45
|
||||
}
|
||||
]
|
||||
60
assets/sprites/fortune/prompts-v1.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Fortune Session Production Prompts
|
||||
|
||||
Generated with the built-in image generation tool, not an API CLI. Original
|
||||
outputs were copied into this project. Asset compilation is deterministic and
|
||||
does not make image-generation calls. These are the production specifications.
|
||||
|
||||
## teller-actions-v1.png
|
||||
|
||||
Reference: `../hall/fortune-room-v2.png`, top-row faceless hooded teller only.
|
||||
Transparent production sprite sheet, exactly eight columns by five rows, forty
|
||||
equal square cells. Front-facing seated upper body, navy embroidered robe,
|
||||
silver crescents, dark gloves, absolutely no face under the hood. Same silhouette,
|
||||
size, head and shoulder position in every cell. No table, chair, candles, scenery,
|
||||
labels or borders. Detailed painted retro pixel art. Row one: eight successive
|
||||
poses taking and splitting a deck and beginning overhand shuffle. Row two: eight
|
||||
successive packet-transfer shuffle poses ending with the deck centered. Row
|
||||
three: eight dealing poses, left hand holds the deck, right hand reaches forward,
|
||||
releases a card and retracts. Row four: eight card-turning and explanatory wrist
|
||||
and open-palm gestures. Row five: eight poses lifting and circling both hands
|
||||
around empty space for a separately rendered crystal ball. Articulate fingers,
|
||||
wrists, forearms and sleeves, no camera changes or torso bouncing. Render the
|
||||
whole sheet together for consistent character identity and palette.
|
||||
|
||||
## seated-room-v1.png
|
||||
|
||||
Full-screen 4:3 retro pixel-art background. First-person seated across an intimate
|
||||
round tarot table in a cramped cluttered midnight-blue occult cellar. Empty
|
||||
carved high-backed chair centered around 43 percent width, curtains behind it,
|
||||
no person. Dark blue velvet tabletop covers the lower 42 percent, silver edge
|
||||
trim and blank central area for separate cards. Teller will be composited with
|
||||
head at 43 percent width, 17 percent height and hands near 54 percent height.
|
||||
Left cabinets crowded with books, scrolls, herbs, skull and trinkets; right shelves
|
||||
with glass bottles, blue crystal and a hanging blue lantern. Deep shadow with
|
||||
cool candlelight, muted burgundy fabric and aged silver/brass accents. Believable
|
||||
untidiness, draped curtains above and at the sides. No table cards, deck, ball,
|
||||
lettering, numbers, UI or screen border. Keep the upper-right speech area clear.
|
||||
|
||||
## tarot-symbols-v1.png
|
||||
|
||||
Symbolic tarot atlas, exactly six columns by four rows, 24 equal portrait cells,
|
||||
no gaps, letters or labels. Objects rather than human figures; detailed retro
|
||||
pixel art, thin ivory borders, navy, silver, burgundy, gold and cyan. Row-major
|
||||
symbols: traveller's satchel/staff/cliff; wand/tools; pillars/moon; crown/wheat;
|
||||
stone throne; ceremonial hat/crossed keys; entwined roses/heart; chariot; lion/
|
||||
infinity; lantern/mountain; ornate wheel; scales/sword; inverted watch/tree;
|
||||
scythe/rose/hourglass; pouring cups; horned stone mask/chains; lightning/tower;
|
||||
star/water; moon/two towers; sun/sunflowers; trumpet/banner; globe/laurel. Last
|
||||
two cells: symmetrical silver crescent and stars on midnight-blue card backs.
|
||||
Keep each recognizable illustration completely within its consistent frame.
|
||||
|
||||
## crystal-mist-v1.png
|
||||
|
||||
Transparent VFX sprite sheet, exactly four columns by four rows, sixteen equal
|
||||
square cells. Identical crystal ball on a small antique silver stand, front view,
|
||||
fixed silhouette, scale and bottom-center anchor. No hands, person or table.
|
||||
Pale-blue smoke spirals clockwise through one full cycle across sixteen frames.
|
||||
Subtle blue sparks and lighting inside the glass, no text, faces, numbers or
|
||||
symbols. Keep stand stationary; vary the internal smoke, not object placement.
|
||||
Detailed painted retro pixel art, dark glass and luminous cyan-blue mist, clean
|
||||
transparent corners, no matte or background.
|
||||
BIN
assets/sprites/fortune/seated-room-v1.png
Normal file
|
After Width: | Height: | Size: 2.6 MiB |
BIN
assets/sprites/fortune/tarot-symbols-v1.png
Normal file
|
After Width: | Height: | Size: 3.6 MiB |
BIN
assets/sprites/fortune/teller-actions-v1.png
Normal file
|
After Width: | Height: | Size: 2.7 MiB |
BIN
bin/poker.exe
|
|
@ -8,7 +8,7 @@ namespace AssemblerCasinoLauncher
|
|||
{
|
||||
static class Program
|
||||
{
|
||||
const string ExpectedPokerSha256 = "C1714FEA01C6AA594A1895F56CCC287E5D1F35CDC829F5C2658FF05A4CBB6ECF";
|
||||
const string ExpectedPokerSha256 = "C3D857FDBF7597C976B16C5021D7F07951628E54F86FF2B7BB53F8A83FDE11CA";
|
||||
|
||||
[STAThread]
|
||||
static void Main()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using System;
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
|
|
@ -27,7 +27,7 @@ namespace PokerGameSetup
|
|||
sealed class InstallerForm : Form
|
||||
{
|
||||
const string Marker = "<<POKER_GAME_GUI_PAYLOAD_ZIP>>";
|
||||
const string ExpectedPayloadSha256 = "718663039D0CCB6EE83C62D23C8F8F7A6D4C596135E75F757C101C568E8473E9";
|
||||
const string ExpectedPayloadSha256 = "1A2AC9A543AAF354EAD5BFA4716A8677143E27D994D55120AAF4ACF8296AAA6B";
|
||||
readonly Color bg = Color.FromArgb(15, 12, 10);
|
||||
readonly Color panel = Color.FromArgb(28, 22, 18);
|
||||
readonly Color gold = Color.FromArgb(236, 186, 75);
|
||||
|
|
@ -247,19 +247,44 @@ namespace PokerGameSetup
|
|||
Directory.CreateDirectory(Path.Combine(target, "runtime"));
|
||||
|
||||
var exe = Assembly.GetExecutingAssembly().Location;
|
||||
var bytes = File.ReadAllBytes(exe);
|
||||
var marker = System.Text.Encoding.UTF8.GetBytes(Marker);
|
||||
var index = FindMarker(bytes, marker);
|
||||
if (index < 0) throw new InvalidOperationException("В установщике не найден архив игры.");
|
||||
|
||||
var payloadBytes = bytes.Skip(index + marker.Length).ToArray();
|
||||
VerifyPayloadIntegrity(payloadBytes);
|
||||
SetProgress(0, "Поиск архива игры...");
|
||||
long payloadStart = -1;
|
||||
using (var fs = File.OpenRead(exe))
|
||||
{
|
||||
payloadStart = FindMarkerInStream(fs, marker);
|
||||
}
|
||||
if (payloadStart < 0) throw new InvalidOperationException("В установщике не найден архив игры.");
|
||||
|
||||
var tempZip = Path.Combine(Path.GetTempPath(), "assembler_casino_payload_" + Guid.NewGuid().ToString("N") + ".zip");
|
||||
File.WriteAllBytes(tempZip, payloadBytes);
|
||||
|
||||
try
|
||||
{
|
||||
SetProgress(5, "Проверка целостности архива...");
|
||||
using (var fs = File.OpenRead(exe))
|
||||
{
|
||||
fs.Position = payloadStart;
|
||||
using (var outFs = File.Create(tempZip, 65536))
|
||||
using (var sha = SHA256.Create())
|
||||
{
|
||||
var buf = new byte[65536];
|
||||
int r;
|
||||
while ((r = fs.Read(buf, 0, buf.Length)) > 0)
|
||||
{
|
||||
sha.TransformBlock(buf, 0, r, null, 0);
|
||||
outFs.Write(buf, 0, r);
|
||||
}
|
||||
sha.TransformFinalBlock(buf, 0, 0);
|
||||
var actual = BitConverter.ToString(sha.Hash).Replace("-", "");
|
||||
if (!string.IsNullOrWhiteSpace(ExpectedPayloadSha256) &&
|
||||
ExpectedPayloadSha256 != "TO_BE_FILLED_BY_RELEASE_BUILD" &&
|
||||
!actual.Equals(ExpectedPayloadSha256, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new InvalidOperationException("Архив игры внутри установщика был изменен. Установка остановлена.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
using (var zip = ZipFile.OpenRead(tempZip))
|
||||
{
|
||||
var files = zip.Entries.Where(e => !string.IsNullOrEmpty(e.Name)).ToList();
|
||||
|
|
@ -285,23 +310,10 @@ namespace PokerGameSetup
|
|||
}
|
||||
finally
|
||||
{
|
||||
try { File.Delete(tempZip); } catch { }
|
||||
try { if (File.Exists(tempZip)) File.Delete(tempZip); } catch { }
|
||||
}
|
||||
}
|
||||
|
||||
void VerifyPayloadIntegrity(byte[] payloadBytes)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(ExpectedPayloadSha256) || ExpectedPayloadSha256 == "TO_BE_FILLED_BY_RELEASE_BUILD")
|
||||
return;
|
||||
|
||||
using (var sha = SHA256.Create())
|
||||
{
|
||||
var hash = sha.ComputeHash(payloadBytes);
|
||||
var actual = BitConverter.ToString(hash).Replace("-", "");
|
||||
if (!actual.Equals(ExpectedPayloadSha256, StringComparison.OrdinalIgnoreCase))
|
||||
throw new InvalidOperationException("Архив игры внутри установщика был изменен. Установка остановлена.");
|
||||
}
|
||||
}
|
||||
|
||||
void CreateDesktopShortcut(string target)
|
||||
{
|
||||
|
|
@ -347,12 +359,48 @@ namespace PokerGameSetup
|
|||
catch { }
|
||||
}
|
||||
|
||||
long FindMarkerInStream(Stream stream, byte[] marker)
|
||||
{
|
||||
var buffer = new byte[1024 * 1024];
|
||||
long streamPos = 0;
|
||||
int bytesRead;
|
||||
int overlap = marker.Length - 1;
|
||||
int carry = 0;
|
||||
|
||||
while ((bytesRead = stream.Read(buffer, carry, buffer.Length - carry)) > 0)
|
||||
{
|
||||
int total = carry + bytesRead;
|
||||
int idx = FindMarker(buffer, marker, total);
|
||||
if (idx >= 0)
|
||||
{
|
||||
return streamPos + idx + marker.Length;
|
||||
}
|
||||
if (total >= overlap)
|
||||
{
|
||||
Buffer.BlockCopy(buffer, total - overlap, buffer, 0, overlap);
|
||||
carry = overlap;
|
||||
streamPos += (total - overlap);
|
||||
}
|
||||
else
|
||||
{
|
||||
carry = total;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int FindMarker(byte[] bytes, byte[] marker)
|
||||
{
|
||||
for (int i = 0; i <= bytes.Length - marker.Length; i++)
|
||||
return FindMarker(bytes, marker, bytes.Length);
|
||||
}
|
||||
|
||||
int FindMarker(byte[] bytes, byte[] marker, int length)
|
||||
{
|
||||
for (int i = 0; i <= length - marker.Length; i++)
|
||||
{
|
||||
if (bytes[i] != marker[0]) continue;
|
||||
bool ok = true;
|
||||
for (int j = 0; j < marker.Length; j++)
|
||||
for (int j = 1; j < marker.Length; j++)
|
||||
{
|
||||
if (bytes[i + j] != marker[j]) { ok = false; break; }
|
||||
}
|
||||
|
|
|
|||
22
casino3d.inc
|
|
@ -171,6 +171,8 @@ include src_graphics\native_dice_data.inc
|
|||
include src_graphics\scratch_data.inc
|
||||
include src_graphics\hell_tower_data.inc
|
||||
include src_graphics\charms_data.inc
|
||||
include src_graphics\observer_data.inc
|
||||
include src_graphics\darts_data.inc
|
||||
|
||||
.data
|
||||
ALIGN 4
|
||||
|
|
@ -4856,7 +4858,7 @@ check_target_terminal:
|
|||
check_target_observer:
|
||||
cmp eax, INTERACT_OBSERVER
|
||||
jne target_unported
|
||||
call BeginUnavailable3D
|
||||
call BeginObserver3D
|
||||
ret
|
||||
|
||||
target_unported:
|
||||
|
|
@ -5194,6 +5196,8 @@ include src_graphics\unavailable.inc
|
|||
include src_graphics\fortune_dialog.inc
|
||||
include src_graphics\hell_tower.inc
|
||||
include src_graphics\charms.inc
|
||||
include src_graphics\observer.inc
|
||||
include src_graphics\darts.inc
|
||||
|
||||
; --- Main Loop of the 3D Casino Engine ---
|
||||
StartHellTower3D PROC uses ebx esi edi
|
||||
|
|
@ -5245,6 +5249,15 @@ dispatch_frame:
|
|||
jz @F
|
||||
call ToggleFullscreen3D
|
||||
@@:
|
||||
cmp obsActive3D,0
|
||||
jne engine_observer_frame
|
||||
cmp engineMode,MODE_OBSERVER
|
||||
jne engine_observer_skip
|
||||
engine_observer_frame:
|
||||
call UpdateMouseLockState
|
||||
call ObserverFrame3D
|
||||
jmp present_frame
|
||||
engine_observer_skip:
|
||||
invoke Pressed3D, 1Bh
|
||||
test eax, eax
|
||||
jz engine_tick
|
||||
|
|
@ -5343,6 +5356,12 @@ check_walk_mode:
|
|||
call RenderMinimap
|
||||
jmp present_frame
|
||||
check_slots_mode:
|
||||
cmp engineMode,MODE_DARTS
|
||||
jne check_darts_done
|
||||
call HandleDarts3D
|
||||
call RenderDarts3D
|
||||
jmp present_frame
|
||||
check_darts_done:
|
||||
cmp engineMode,MODE_FORTUNE
|
||||
jne @F
|
||||
call HandleFortuneDialog3D
|
||||
|
|
@ -5620,6 +5639,7 @@ engine_shutdown:
|
|||
call SaveProgress3D
|
||||
invoke PlayCasinoSound3D, 0, 0, 0
|
||||
call FreeRussianAssets3D
|
||||
call FreeDarts3D
|
||||
call FreeStartupAssets3D
|
||||
call FreeCardTableAssets3D
|
||||
call FreeBuckshotAssets3D
|
||||
|
|
|
|||
BIN
dist/Assembler Casino.exe
vendored
BIN
dist/Uninstall Assembler Casino.exe
vendored
71
docs/DARTS.md
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Darts
|
||||
|
||||
The board is on the first-floor wall near the billiards area. It opens a native
|
||||
MASM darts match against Max. No Python, browser, external engine or network is
|
||||
required by the game.
|
||||
|
||||
## Match
|
||||
|
||||
- Both players start at 500. First to reach exactly zero wins.
|
||||
- Three darts per visit. Visits alternate between the player and Max.
|
||||
- Any scoring ring can finish. This is a custom 500-point straight-out format,
|
||||
not tournament 501 double-out.
|
||||
- A negative remainder is a bust: the entire visit is cancelled and play passes
|
||||
to the other player. A remainder of one is valid.
|
||||
- The casino bankroll and saved profile are not changed by this game.
|
||||
|
||||
## Controls
|
||||
|
||||
- Move the mouse to aim; the outlined cross marks the current hit point.
|
||||
- Hold the left mouse button to draw back, release to throw.
|
||||
- The hand and cross have the same smooth, mixed-phase sway. Holding the aim
|
||||
longer than 1.2 seconds gradually increases fatigue, capped after 4 seconds.
|
||||
Precise mouse motion is retained independently of the hall's 320px UI grid.
|
||||
There is no extra
|
||||
random scatter after releasing the button.
|
||||
- The target sector appears beside the turn indicator (S, D or T).
|
||||
- Tab opens/closes the live score sheet and pauses the current action.
|
||||
- R or the lower-right button starts a new match. Escape returns to the hall.
|
||||
|
||||
## Visuals
|
||||
|
||||
The throwing line is 2700 board millimetres from the target. The initial lens
|
||||
is adjusted so the board is only about 7 percent smaller on screen than the
|
||||
previous 2370mm/900px view. Its original 512px texture and scoring geometry are
|
||||
unchanged. Max watches the same moving aim point for a short release window;
|
||||
he does not reroll a hit or bypass the sway.
|
||||
|
||||
The independent
|
||||
scene camera follows one immutable parabolic trajectory, passes beside the
|
||||
dart, returns behind it, pauses at the hit, then returns to the throwing line.
|
||||
The small fading trail samples the same trajectory.
|
||||
|
||||
Whole-arm raster animation includes the elbow, draw-back, release and empty
|
||||
follow-through. A single rigid canvas transform aligns the held dart with the
|
||||
target; individual limb pieces are never stretched or assembled.
|
||||
|
||||
The flying dart uses sixteen detailed camera-relative raster views. Its needle
|
||||
anchor is registered per view, so the tip meets the scored board coordinate.
|
||||
It is not drawn from flat-colored geometric triangles.
|
||||
|
||||
The lounge has actual ray-projected front/side/back walls, floor and ceiling.
|
||||
Chairs and the lamp table occupy fixed world planes, rather than turning to
|
||||
face the camera. The wall chalkboard is rewritten with real scores and recent
|
||||
visit history, including when seen from the moving camera.
|
||||
|
||||
## Assets And Checks
|
||||
|
||||
`tools/generators/pack_darts.py` is an offline asset packer. It builds the exact
|
||||
20-sector board and packs the generated arm, dart views and lounge artwork.
|
||||
Artwork provenance and prompts are in `assets/sprites/darts/prompts-v1.md`.
|
||||
|
||||
`C:/Python314/python.exe tools/tests/check_darts.py` compiles the actual MASM
|
||||
scene into a native fixture and checks 67 scoring vectors, 20 complete bot
|
||||
matches, busts, per-player score isolation, immutable hits, real mouse-release
|
||||
events, fine high-resolution mouse input, sway/fatigue, board footprint, Tab
|
||||
behavior, camera motion and profile isolation. It renders native
|
||||
640x480 framebuffers, a contact sheet and a GIF under `build/darts/`.
|
||||
|
||||
`--parity` compares tested procedure instruction/relocation signatures to the
|
||||
full shipping assembly object. The hall fixture additionally covers interaction
|
||||
dispatch and the unchanged stairs, doors, bar and fortune room.
|
||||
|
|
@ -1,5 +1,17 @@
|
|||
# Offline Fortune Teller
|
||||
|
||||
## Currently Shelved (2026-10-22)
|
||||
|
||||
The seated visual session no longer starts the worker or generates readings.
|
||||
The native scene deals and reveals cards, then explicitly says that explanation
|
||||
is not implemented. The crystal-ball scene is visual only, without academic
|
||||
grade selection or collection of student identity details. Existing inference
|
||||
sources and local model files are retained for future work, not required by the
|
||||
current game. The older AI archive is not an updated visual-session release.
|
||||
See `FORTUNE_SESSION.md` for the current runtime and verification.
|
||||
|
||||
The following documents the previous experimental neural integration.
|
||||
|
||||
The approved C++ exception is limited to the neural worker. MASM still owns the
|
||||
room, interaction, question input, card selection, dialogue rendering, process
|
||||
lifetime and all gameplay. No OpenAI API, Ollama, Python runtime, listening port,
|
||||
|
|
|
|||
58
docs/FORTUNE_SESSION.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Seated Fortune Session
|
||||
|
||||
The room interaction opens a fixed first-person view across the table. No player
|
||||
position, camera or bankroll state is modified. Escape or Exit returns to walking.
|
||||
All runtime input, random card selection, state transitions and rendering are MASM
|
||||
in `src_graphics/fortune_dialog.inc`; Python only compiles offline image assets.
|
||||
|
||||
## Tarot
|
||||
|
||||
Enter a question (up to 240 CP1251 characters), then Ask or Enter. The question
|
||||
stays visible under the table for the entire reading and cannot change midway.
|
||||
Three distinct major arcana are selected in assembly, each with an orientation.
|
||||
The 22 symbolic illustrations have separately packed 180-degree reversed faces.
|
||||
|
||||
Sequence: shuffle 3.2 s; deal cards individually 1 s each; turn a card over 0.8 s;
|
||||
show its name, orientation and honest unavailable explanation 3.6 s; repeat for
|
||||
the next two cards; show the overall-answer placeholder; finish. Continue can
|
||||
advance a completed speech, never skip a deal or a flip. Again clears the question
|
||||
only after completion; Selection can return to the choice of scenes at any time.
|
||||
|
||||
Speech is composited beside the teller, not an OS window. Its border grows with
|
||||
the visible text. Characters reveal every 28 ms with a short upward pop. Rendering
|
||||
does not advance timers: the session updates from the game clock, with bounded
|
||||
steps after stalls. No neural worker or algorithmic reading generator is called.
|
||||
|
||||
## Crystal Ball
|
||||
|
||||
The second selection raises a crystal ball and animates gloved hands with swirling
|
||||
blue mist. It ends with an explicit unavailable-result line. There is no real
|
||||
student grading, inference of merit, identity form or automated academic decision.
|
||||
Again replays the visual sequence. This limitation is deliberate, not a hidden
|
||||
random grade presented as a judgement.
|
||||
|
||||
## Assets
|
||||
|
||||
Generated source art and production prompts: `assets/sprites/fortune/`.
|
||||
Offline packer: `tools/generators/pack_fortune_session.py`.
|
||||
Runtime packs: `assets/animations/fortune/` (ANM1 format).
|
||||
|
||||
Forty authored poses are arranged on one shared anchored 8x5 sheet. Shared-canvas
|
||||
in-betweens yield 64 shuffle, 32 deal, 32 gesture and 32 orb-hand frames. Sixteen
|
||||
mist frames, 44 oriented card faces, a back and a 640x480 room complete the scene.
|
||||
Art loads only when seated and all seven packs are freed on exit/shutdown. The
|
||||
seated view is independent of the walking room's existing asset packs and map.
|
||||
|
||||
## Verification
|
||||
|
||||
Run `C:/Python314/python.exe tools/tests/check_hall_expansion.py` after packing.
|
||||
The fresh native x86 harness exercises real WM_CHAR input, length/backspace,
|
||||
empty/double submission, all stage transitions, card uniqueness, speech growth,
|
||||
question persistence, crystal-ball stages, exit during animation, asset cleanup
|
||||
and unchanged player position/bank. It writes actual framebuffer BMP/PNG captures
|
||||
and a 64-frame shuffle GIF under `build/hall-expansion/`. Python additionally checks
|
||||
native-frame differences, question pixels, pack integrity and reversed artwork.
|
||||
`--parity` checks these tested procedures against the shipping game object.
|
||||
|
||||
The old multi-gigabyte neural ZIP is not rebuilt by this feature. Distributions
|
||||
must include the new `assets/animations/fortune/` directory with the updated game.
|
||||
49
docs/OBSERVER.md
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
# Native AI Observer
|
||||
|
||||
The observer lounge opens a game selector rather than the old unavailable modal.
|
||||
All gameplay, decisions, rendering and input remain MASM32. No neural model or
|
||||
network connection is used.
|
||||
|
||||
## Games
|
||||
|
||||
- Heads-up Hold'em: the existing bot decision is shared by both seats.
|
||||
- Blackjack: an autonomous first seat plays against the normal dealer.
|
||||
- Durak: both throwing-in and transfer variants use the same legal-move bot.
|
||||
- Billiards: both pyramid and eight-ball seats use the existing shot planner.
|
||||
|
||||
These are real rounds using the normal shuffled decks, settlement and billiard
|
||||
physics. The observer does not generate scripted winners. Both poker and Durak
|
||||
hands are visible to the spectator, but each bot still sees only its own cards
|
||||
and public information. Blackjack retains the dealer's normal hidden hole card.
|
||||
|
||||
## Controls
|
||||
|
||||
Select a game with the mouse, keys 1-6, or arrows and Enter. During a match:
|
||||
|
||||
- Space or the pause button pauses/resumes decisions and animation.
|
||||
- Tab or the speed button cycles 1x, 2x and 4x.
|
||||
- N or restart starts a fresh round without adding an abandoned round to scores.
|
||||
- Escape or Games returns to the selector. Escape again returns to the hall.
|
||||
|
||||
Completed rounds remain visible for three simulated seconds before another
|
||||
round starts. Each round uses a fresh virtual stack. Scores reset when choosing
|
||||
a different watching session. Human gameplay commands are discarded while watching.
|
||||
|
||||
## Profile Isolation
|
||||
|
||||
The observer snapshots the player's balance, RNG state, tournament flag, bets,
|
||||
game variants and blackjack/billiard opponent settings. These are restored before
|
||||
leaving. Native save, NPC-contract progress and loss-protection charms have an
|
||||
observer barrier. Watching does not spend personal funds, charms, grant balance
|
||||
achievements, progress contracts or write the profile.
|
||||
|
||||
## Verification
|
||||
|
||||
`C:/Python314/python.exe tools/tests/check_observer.py` compiles fresh native game
|
||||
and spectator procedures against existing rendering helpers. It runs 80 complete
|
||||
card rounds, checks both billiard seats, pause and profile restoration, compares
|
||||
runtime files before/after, and captures actual native framebuffers.
|
||||
|
||||
After rebuilding the full game, `--parity` checks that the shipping procedures
|
||||
match the native-tested object. `test_billiards_module.ps1` additionally exercises
|
||||
ordinary human/AI billiards with observer mode disabled.
|
||||
BIN
poker.exe
BIN
poker.obj
|
|
@ -40,6 +40,10 @@ DrawTableHeader3D PROC hdrLabel:DWORD,hdrPot:DWORD
|
|||
DrawTableHeader3D ENDP
|
||||
|
||||
DrawStakeControls3D PROC uses ebx enabled:DWORD
|
||||
cmp obsActive3D,0
|
||||
je @F
|
||||
ret
|
||||
@@:
|
||||
mov ebx, 006D7470h
|
||||
cmp enabled, 0
|
||||
je @F
|
||||
|
|
@ -84,6 +88,8 @@ poker_render_stake_enabled:
|
|||
invoke DrawStakeControls3D, eax
|
||||
mov eax, pkStreet3D
|
||||
invoke DrawString8x8, 180, 6, pkStreetNames3D[eax*4], 00C4B8A0h
|
||||
cmp obsActive3D,0
|
||||
jne poker_observer_chips
|
||||
mov eax, pkRaise3D
|
||||
mov esi, OFFSET tableRaiseText3D
|
||||
cmp pkState3D, PK_IDLE
|
||||
|
|
@ -99,6 +105,7 @@ poker_render_blind:
|
|||
pop eax
|
||||
invoke FormatNumber, eax, OFFSET hudNumBuffer
|
||||
invoke DrawString8x8, 12, 187, OFFSET hudNumBuffer, 00F6F4DDh
|
||||
poker_observer_chips:
|
||||
invoke DrawNativeChips3D, 278, 196, pkPot3D, 00B14A49h
|
||||
cmp pkChipTimer3D, 320
|
||||
jae @F
|
||||
|
|
@ -144,6 +151,8 @@ poker_render_chip_path:
|
|||
xor ebx, ebx
|
||||
poker_render_hole:
|
||||
mov hidden, 0
|
||||
cmp obsActive3D,0
|
||||
jne poker_render_hole_state
|
||||
cmp ebx, 2
|
||||
jb poker_render_hole_state
|
||||
mov hidden, 1
|
||||
|
|
@ -237,7 +246,13 @@ poker_render_status:
|
|||
cmp pkState3D, PK_RESULT
|
||||
jne poker_render_ai_status
|
||||
mov eax, pkOutcome3D
|
||||
cmp obsActive3D,0
|
||||
je poker_personal_result
|
||||
invoke DrawString8x8,108,103,obsResultNames3D[eax*4],00F4DF9Eh
|
||||
jmp poker_result_class
|
||||
poker_personal_result:
|
||||
invoke DrawString8x8, 108, 103, pkResults3D[eax*4], 00F4DF9Eh
|
||||
poker_result_class:
|
||||
cmp pkResult3D, 1
|
||||
jne poker_render_buttons
|
||||
invoke NativePokerClass3D, pkScore3D
|
||||
|
|
@ -315,9 +330,12 @@ bj_render_stake_enabled:
|
|||
inc eax
|
||||
@@:
|
||||
invoke DrawStakeControls3D, eax
|
||||
cmp obsActive3D,0
|
||||
jne bj_observer_cards
|
||||
invoke DrawString8x8, 8, 36, OFFSET textBet3D, 00DDCFB1h
|
||||
invoke FormatNumber, cbBet3D, OFFSET hudNumBuffer
|
||||
invoke DrawString8x8, 80, 36, OFFSET hudNumBuffer, 00F6F4DDh
|
||||
bj_observer_cards:
|
||||
cmp cbState3D, CB_IDLE
|
||||
je bj_render_buttons
|
||||
xor edi, edi
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
; Output: eax = 1 if saved (bet refunded to bankroll), 0 if not saved
|
||||
; ------------------------------------------------------------------------------
|
||||
TriggerCharmLossProtection PROC uses ebx ecx edx, betAmount:DWORD
|
||||
cmp obsActive3D,0
|
||||
jne protection_none
|
||||
; 1. Check Devil's Contract (50% chance to refund full bet)
|
||||
cmp charmDevilUses, 0
|
||||
jle check_clover
|
||||
|
|
|
|||
|
|
@ -5,6 +5,11 @@ 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,160
|
||||
jne @F
|
||||
call ResolveDartsSurface3D
|
||||
ret
|
||||
@@:
|
||||
cmp eax,132
|
||||
jb @F
|
||||
cmp eax,159
|
||||
|
|
|
|||
890
src_graphics/darts.inc
Normal file
|
|
@ -0,0 +1,890 @@
|
|||
; One immutable hit point drives scoring, dart geometry and the follow camera.
|
||||
.code
|
||||
LoadDarts3D PROC
|
||||
invoke LoadAnimPack3D,OFFSET dtBoard3D,OFFSET dtBoardPath
|
||||
test eax,eax
|
||||
jz dt_load_fail
|
||||
cmp dtBoard3D.imageWidth,512
|
||||
jne dt_load_fail
|
||||
cmp dtBoard3D.height,512
|
||||
jne dt_load_fail
|
||||
cmp dtBoard3D.frames,1
|
||||
jne dt_load_fail
|
||||
invoke LoadAnimPack3D,OFFSET dtHands3D,OFFSET dtHandsPath
|
||||
test eax,eax
|
||||
jz dt_load_fail
|
||||
cmp dtHands3D.imageWidth,256
|
||||
jne dt_load_fail
|
||||
cmp dtHands3D.height,256
|
||||
jne dt_load_fail
|
||||
cmp dtHands3D.frames,16
|
||||
jne dt_load_fail
|
||||
invoke LoadAnimPack3D,OFFSET dtDart3D,OFFSET dtDartPath
|
||||
test eax,eax
|
||||
jz dt_load_fail
|
||||
cmp dtDart3D.imageWidth,256
|
||||
jne dt_load_fail
|
||||
cmp dtDart3D.height,256
|
||||
jne dt_load_fail
|
||||
cmp dtDart3D.frames,16
|
||||
jne dt_load_fail
|
||||
invoke LoadAnimPack3D,OFFSET dtWall3D,OFFSET dtWallPath
|
||||
test eax,eax
|
||||
jz dt_load_fail
|
||||
cmp dtWall3D.imageWidth,512
|
||||
jne dt_load_fail
|
||||
cmp dtWall3D.height,512
|
||||
jne dt_load_fail
|
||||
cmp dtWall3D.frames,1
|
||||
jne dt_load_fail
|
||||
invoke LoadAnimPack3D,OFFSET dtChair3D,OFFSET dtChairPath
|
||||
test eax,eax
|
||||
jz dt_load_fail
|
||||
invoke LoadAnimPack3D,OFFSET dtTable3D,OFFSET dtTablePath
|
||||
test eax,eax
|
||||
jz dt_load_fail
|
||||
invoke LoadAnimPack3D,OFFSET dtRoom3D,OFFSET dtRoomPath
|
||||
test eax,eax
|
||||
jz dt_load_fail
|
||||
cmp dtRoom3D.imageWidth,256
|
||||
jne dt_load_fail
|
||||
cmp dtRoom3D.height,256
|
||||
jne dt_load_fail
|
||||
cmp dtRoom3D.frames,12
|
||||
jne dt_load_fail
|
||||
mov eax,1
|
||||
ret
|
||||
dt_load_fail:
|
||||
call FreeDarts3D
|
||||
xor eax,eax
|
||||
ret
|
||||
LoadDarts3D ENDP
|
||||
|
||||
FreeDarts3D PROC
|
||||
invoke FreeAnimPack3D,OFFSET dtBoard3D
|
||||
invoke FreeAnimPack3D,OFFSET dtHands3D
|
||||
invoke FreeAnimPack3D,OFFSET dtDart3D
|
||||
invoke FreeAnimPack3D,OFFSET dtWall3D
|
||||
invoke FreeAnimPack3D,OFFSET dtChair3D
|
||||
invoke FreeAnimPack3D,OFFSET dtTable3D
|
||||
invoke FreeAnimPack3D,OFFSET dtRoom3D
|
||||
ret
|
||||
FreeDarts3D ENDP
|
||||
|
||||
ResetDarts3D PROC
|
||||
mov dtState3D,DT_AIM
|
||||
mov dtTimer3D,0
|
||||
mov dtRemaining3D,500
|
||||
mov dtVisitStart3D,500
|
||||
mov dtScores3D,500
|
||||
mov dtScores3D[4],500
|
||||
mov dtActor3D,0
|
||||
mov dtWinner3D,0
|
||||
mov dtBotTimer3D,0
|
||||
mov dtBotPrepared3D,0
|
||||
mov dtScoreOpen3D,0
|
||||
mov dtHistoryRows3D,1
|
||||
mov dtHistory3D,500
|
||||
mov dtHistory3D[4],500
|
||||
mov dtWallDirty3D,1
|
||||
mov dtCount3D,0
|
||||
mov dtThrows3D,0
|
||||
mov dtVisit3D,1
|
||||
mov dtLast3D,0
|
||||
mov dtBust3D,0
|
||||
mov dtHolding3D,0
|
||||
mov dtMouseReleased3D,0
|
||||
mov dtHoldTime3D,0
|
||||
mov dtCamX3D,0
|
||||
mov dtCamY3D,0
|
||||
mov dtCamZ3D,0
|
||||
mov dtLens3D,DT_FOCAL
|
||||
mov dtLookX3D,0
|
||||
mov dtLookY3D,0
|
||||
ret
|
||||
ResetDarts3D ENDP
|
||||
|
||||
BeginDarts3D PROC
|
||||
call LoadDarts3D
|
||||
xor eax,1
|
||||
mov dtError3D,eax
|
||||
call ResetDarts3D
|
||||
mov engineMode,MODE_DARTS
|
||||
mov paused3D,0
|
||||
mov mouseX3D,160
|
||||
mov mouseY3D,107
|
||||
mov dtCursorX3D,320*256
|
||||
mov dtCursorY3D,214*256
|
||||
call ClearKeys3D
|
||||
mov mouseClick3D,0
|
||||
ret
|
||||
BeginDarts3D ENDP
|
||||
|
||||
DartsAim3D PROC uses ebx
|
||||
local amplitude:DWORD,phase:DWORD,secondary:DWORD
|
||||
; Smooth mixed phases and gradual hold fatigue; never scatter after release.
|
||||
mov amplitude,10
|
||||
cmp dtHolding3D,0
|
||||
je dt_aim_phase
|
||||
mov eax,dtHoldTime3D
|
||||
sub eax,1200
|
||||
jle dt_aim_phase
|
||||
xor edx,edx
|
||||
mov ecx,350
|
||||
div ecx
|
||||
cmp eax,8
|
||||
jbe @F
|
||||
mov eax,8
|
||||
@@:
|
||||
add amplitude,eax
|
||||
dt_aim_phase:
|
||||
mov eax,dtClock3D
|
||||
xor edx,edx
|
||||
mov ecx,3
|
||||
div ecx
|
||||
and eax,511
|
||||
mov phase,eax
|
||||
mov ebx,tableSin[eax*4]
|
||||
imul ebx,amplitude
|
||||
sar ebx,16
|
||||
mov eax,dtClock3D
|
||||
xor edx,edx
|
||||
mov ecx,5
|
||||
div ecx
|
||||
add eax,91
|
||||
and eax,511
|
||||
mov secondary,eax
|
||||
mov eax,tableSin[eax*4]
|
||||
imul eax,3
|
||||
sar eax,16
|
||||
add ebx,eax
|
||||
mov eax,dtCursorX3D
|
||||
sub eax,320*256
|
||||
imul eax,DT_WALL
|
||||
cdq
|
||||
mov ecx,DT_FOCAL*256
|
||||
idiv ecx
|
||||
cmp dtActor3D,0
|
||||
je @F
|
||||
mov eax,dtBotAimX3D
|
||||
@@:
|
||||
add eax,ebx
|
||||
cmp eax,-290
|
||||
jge @F
|
||||
mov eax,-290
|
||||
@@:
|
||||
cmp eax,290
|
||||
jle @F
|
||||
mov eax,290
|
||||
@@:
|
||||
mov dtAimX3D,eax
|
||||
mov eax,phase
|
||||
mov ebx,tableCos[eax*4]
|
||||
mov ecx,amplitude
|
||||
sub ecx,2
|
||||
imul ebx,ecx
|
||||
sar ebx,16
|
||||
mov eax,secondary
|
||||
sub eax,58
|
||||
and eax,511
|
||||
mov eax,tableSin[eax*4]
|
||||
imul eax,3
|
||||
sar eax,16
|
||||
add ebx,eax
|
||||
mov eax,dtCursorY3D
|
||||
mov edx,214*256
|
||||
sub edx,eax
|
||||
imul eax,edx,DT_WALL
|
||||
cdq
|
||||
mov ecx,DT_FOCAL*256
|
||||
idiv ecx
|
||||
cmp dtActor3D,0
|
||||
je @F
|
||||
mov eax,dtBotAimY3D
|
||||
@@:
|
||||
add eax,ebx
|
||||
cmp eax,-245
|
||||
jge @F
|
||||
mov eax,-245
|
||||
@@:
|
||||
cmp eax,245
|
||||
jle @F
|
||||
mov eax,245
|
||||
@@:
|
||||
mov dtAimY3D,eax
|
||||
ret
|
||||
DartsAim3D ENDP
|
||||
|
||||
; Select a target from the visible score, then commit imperfect aim before release.
|
||||
DartsBotAim3D PROC uses ebx esi
|
||||
local ring:DWORD,target:DWORD
|
||||
mov target,20
|
||||
mov ring,103
|
||||
mov eax,dtRemaining3D
|
||||
cmp eax,20
|
||||
ja dt_bot_double
|
||||
mov target,eax
|
||||
mov ring,53
|
||||
jmp dt_bot_sector
|
||||
dt_bot_double:
|
||||
cmp eax,40
|
||||
ja dt_bot_special
|
||||
test eax,1
|
||||
jnz dt_bot_special
|
||||
shr eax,1
|
||||
mov target,eax
|
||||
mov ring,166
|
||||
jmp dt_bot_sector
|
||||
dt_bot_special:
|
||||
cmp eax,50
|
||||
jne @F
|
||||
mov dtBotDesired3D,50
|
||||
mov dtBotAimX3D,0
|
||||
mov dtBotAimY3D,0
|
||||
jmp dt_bot_scatter
|
||||
@@:
|
||||
cmp eax,25
|
||||
jne @F
|
||||
mov dtBotDesired3D,25
|
||||
mov dtBotAimX3D,0
|
||||
mov dtBotAimY3D,11
|
||||
jmp dt_bot_scatter
|
||||
@@:
|
||||
cmp eax,60
|
||||
ja dt_bot_sector
|
||||
xor edx,edx
|
||||
mov ecx,3
|
||||
div ecx
|
||||
test edx,edx
|
||||
jnz dt_bot_single20
|
||||
mov target,eax
|
||||
jmp dt_bot_sector
|
||||
dt_bot_single20:
|
||||
mov ring,53
|
||||
dt_bot_sector:
|
||||
mov eax,target
|
||||
cmp ring,103
|
||||
jne @F
|
||||
imul eax,3
|
||||
@@:
|
||||
cmp ring,166
|
||||
jne @F
|
||||
shl eax,1
|
||||
@@:
|
||||
mov dtBotDesired3D,eax
|
||||
xor ebx,ebx
|
||||
@@:
|
||||
mov eax,dtSector3D[ebx*4]
|
||||
cmp eax,target
|
||||
je dt_bot_direction
|
||||
inc ebx
|
||||
cmp ebx,20
|
||||
jb @B
|
||||
xor ebx,ebx
|
||||
dt_bot_direction:
|
||||
imul eax,ebx,512
|
||||
xor edx,edx
|
||||
mov ecx,20
|
||||
div ecx
|
||||
mov esi,eax
|
||||
mov eax,tableSin[esi*4]
|
||||
imul eax,ring
|
||||
sar eax,16
|
||||
mov dtBotAimX3D,eax
|
||||
mov eax,tableCos[esi*4]
|
||||
imul eax,ring
|
||||
sar eax,16
|
||||
mov dtBotAimY3D,eax
|
||||
dt_bot_scatter:
|
||||
invoke RandomRange3D,17
|
||||
sub eax,8
|
||||
add dtBotAimX3D,eax
|
||||
invoke RandomRange3D,17
|
||||
sub eax,8
|
||||
add dtBotAimY3D,eax
|
||||
mov dtBotPrepared3D,1
|
||||
ret
|
||||
DartsBotAim3D ENDP
|
||||
|
||||
ThrowDarts3D PROC
|
||||
cmp dtState3D,DT_AIM
|
||||
jne dt_throw_done
|
||||
cmp dtError3D,0
|
||||
jne dt_throw_done
|
||||
mov eax,dtAimX3D
|
||||
mov dtHitX3D,eax
|
||||
mov eax,dtAimY3D
|
||||
mov dtHitY3D,eax
|
||||
mov dtState3D,DT_RELEASE
|
||||
mov dtTimer3D,0
|
||||
mov dtHolding3D,0
|
||||
mov dtMouseReleased3D,0
|
||||
inc dtThrows3D
|
||||
dt_throw_done:
|
||||
ret
|
||||
ThrowDarts3D ENDP
|
||||
|
||||
; Board units are millimetres: 6.35/15.9 bull, 99..107 triple, 162..170 double.
|
||||
DartsScore3D PROC uses ebx x:SDWORD,y:SDWORD
|
||||
local angle:DWORD,oldControl:WORD,truncate:WORD
|
||||
cmp x,-170
|
||||
jl dt_score_miss
|
||||
cmp x,170
|
||||
jg dt_score_miss
|
||||
cmp y,-170
|
||||
jl dt_score_miss
|
||||
cmp y,170
|
||||
jg dt_score_miss
|
||||
mov eax,x
|
||||
imul eax,eax
|
||||
mov ebx,y
|
||||
imul ebx,ebx
|
||||
add ebx,eax
|
||||
cmp ebx,170*170
|
||||
ja dt_score_miss
|
||||
cmp ebx,40
|
||||
jbe dt_score_bull
|
||||
cmp ebx,253
|
||||
jbe dt_score_outer_bull
|
||||
fnstcw oldControl
|
||||
mov ax,oldControl
|
||||
or ax,0C00h
|
||||
mov truncate,ax
|
||||
fldcw truncate
|
||||
fild x
|
||||
fild y
|
||||
fpatan
|
||||
fmul dtAngleFactor3D
|
||||
fistp angle
|
||||
fldcw oldControl
|
||||
mov eax,angle
|
||||
add eax,65536+1638
|
||||
and eax,65535
|
||||
imul eax,20
|
||||
shr eax,16
|
||||
mov eax,dtSector3D[eax*4]
|
||||
mov edx,1
|
||||
cmp ebx,162*162
|
||||
jae dt_score_double
|
||||
cmp ebx,99*99
|
||||
jb dt_score_done
|
||||
cmp ebx,107*107
|
||||
ja dt_score_done
|
||||
mov edx,3
|
||||
imul eax,3
|
||||
ret
|
||||
dt_score_double:
|
||||
mov edx,2
|
||||
shl eax,1
|
||||
ret
|
||||
dt_score_bull:
|
||||
mov eax,50
|
||||
mov edx,2
|
||||
ret
|
||||
dt_score_outer_bull:
|
||||
mov eax,25
|
||||
mov edx,1
|
||||
ret
|
||||
dt_score_miss:
|
||||
xor eax,eax
|
||||
xor edx,edx
|
||||
dt_score_done:
|
||||
ret
|
||||
DartsScore3D ENDP
|
||||
|
||||
SettleDarts3D PROC uses ebx
|
||||
cmp dtState3D,DT_FLIGHT
|
||||
jne dt_settle_done
|
||||
invoke DartsScore3D,dtHitX3D,dtHitY3D
|
||||
mov dtLast3D,eax
|
||||
mov dtMultiplier3D,edx
|
||||
mov ebx,dtCount3D
|
||||
cmp ebx,3
|
||||
jae dt_settle_done
|
||||
mov dtMarkScore3D[ebx*4],eax
|
||||
mov ecx,dtHitX3D
|
||||
mov dtMarkX3D[ebx*4],ecx
|
||||
mov ecx,dtHitY3D
|
||||
mov dtMarkY3D[ebx*4],ecx
|
||||
inc dtCount3D
|
||||
mov ecx,dtRemaining3D
|
||||
sub ecx,eax
|
||||
js dt_bust
|
||||
dt_score_accept:
|
||||
mov dtRemaining3D,ecx
|
||||
jmp dt_begin_impact
|
||||
dt_bust:
|
||||
mov eax,dtVisitStart3D
|
||||
mov dtRemaining3D,eax
|
||||
mov dtBust3D,1
|
||||
dt_begin_impact:
|
||||
mov ebx,dtActor3D
|
||||
mov eax,dtRemaining3D
|
||||
mov dtScores3D[ebx*4],eax
|
||||
mov dtWallDirty3D,1
|
||||
test eax,eax
|
||||
jnz @F
|
||||
mov dtWinner3D,ebx
|
||||
@@:
|
||||
cmp dtRemaining3D,0
|
||||
je dt_record_visit
|
||||
cmp dtCount3D,3
|
||||
je dt_record_visit
|
||||
cmp dtBust3D,0
|
||||
je dt_no_record
|
||||
dt_record_visit:
|
||||
call DartsRecordScore3D
|
||||
dt_no_record:
|
||||
mov dtState3D,DT_IMPACT
|
||||
mov dtTimer3D,0
|
||||
invoke PlayCasinoSound3D,OFFSET soundClick,0,1
|
||||
dt_settle_done:
|
||||
ret
|
||||
SettleDarts3D ENDP
|
||||
|
||||
DartsRecordScore3D PROC uses ebx esi edi
|
||||
mov ebx,dtHistoryRows3D
|
||||
cmp ebx,32
|
||||
jb @F
|
||||
lea esi,dtHistory3D[8]
|
||||
lea edi,dtHistory3D
|
||||
mov ecx,62
|
||||
cld
|
||||
rep movsd
|
||||
mov ebx,31
|
||||
@@:
|
||||
mov eax,dtScores3D
|
||||
mov dtHistory3D[ebx*8],eax
|
||||
mov eax,dtScores3D[4]
|
||||
mov dtHistory3D[ebx*8+4],eax
|
||||
inc ebx
|
||||
mov dtHistoryRows3D,ebx
|
||||
mov dtWallDirty3D,1
|
||||
ret
|
||||
DartsRecordScore3D ENDP
|
||||
|
||||
HandleDarts3D PROC
|
||||
invoke Pressed3D,9
|
||||
test eax,eax
|
||||
jz @F
|
||||
xor dtScoreOpen3D,1
|
||||
mov dtHolding3D,0
|
||||
mov dtMouseReleased3D,0
|
||||
@@:
|
||||
cmp dtScoreOpen3D,0
|
||||
jne dt_handle_done
|
||||
invoke Pressed3D,52h
|
||||
test eax,eax
|
||||
jz @F
|
||||
call ResetDarts3D
|
||||
@@:
|
||||
cmp mouseClick3D,0
|
||||
je dt_handle_game
|
||||
cmp mouseY3D,218
|
||||
jb dt_handle_game
|
||||
cmp mouseX3D,230
|
||||
jb dt_handle_game
|
||||
call ResetDarts3D
|
||||
mov mouseClick3D,0
|
||||
dt_handle_game:
|
||||
cmp dtError3D,0
|
||||
jne dt_handle_done
|
||||
mov eax,deltaTime3D
|
||||
add dtClock3D,eax
|
||||
cmp dtState3D,DT_AIM
|
||||
jne dt_animate
|
||||
cmp dtActor3D,0
|
||||
je dt_player_aim
|
||||
cmp dtBotPrepared3D,0
|
||||
jne @F
|
||||
call DartsBotAim3D
|
||||
@@:
|
||||
call DartsAim3D
|
||||
mov eax,deltaTime3D
|
||||
add dtBotTimer3D,eax
|
||||
cmp dtBotTimer3D,900
|
||||
jb dt_handle_done
|
||||
mov dtHolding3D,1
|
||||
mov eax,dtBotTimer3D
|
||||
sub eax,900
|
||||
cmp eax,280
|
||||
jbe @F
|
||||
mov eax,280
|
||||
@@:
|
||||
mov dtHoldTime3D,eax
|
||||
cmp dtBotTimer3D,1220
|
||||
jb dt_handle_done
|
||||
invoke DartsScore3D,dtAimX3D,dtAimY3D
|
||||
cmp eax,dtBotDesired3D
|
||||
je dt_bot_commit
|
||||
cmp dtBotTimer3D,1600
|
||||
jb dt_handle_done
|
||||
dt_bot_commit:
|
||||
call ThrowDarts3D
|
||||
jmp dt_handle_done
|
||||
dt_player_aim:
|
||||
cmp mouseClick3D,0
|
||||
je @F
|
||||
mov dtHolding3D,1
|
||||
mov dtHoldTime3D,0
|
||||
@@:
|
||||
cmp dtHolding3D,0
|
||||
je dt_player_hover
|
||||
mov eax,deltaTime3D
|
||||
add dtHoldTime3D,eax
|
||||
cmp dtHoldTime3D,4000
|
||||
jbe @F
|
||||
mov dtHoldTime3D,4000
|
||||
@@:
|
||||
dt_player_hover:
|
||||
call DartsAim3D
|
||||
cmp dtHolding3D,0
|
||||
jne dt_player_release
|
||||
mov dtMouseReleased3D,0
|
||||
jmp dt_handle_done
|
||||
dt_player_release:
|
||||
cmp dtMouseReleased3D,0
|
||||
je dt_handle_done
|
||||
call ThrowDarts3D
|
||||
jmp dt_handle_done
|
||||
dt_animate:
|
||||
mov eax,deltaTime3D
|
||||
add dtTimer3D,eax
|
||||
cmp dtState3D,DT_RELEASE
|
||||
jne dt_animate_flight
|
||||
cmp dtTimer3D,200
|
||||
jb dt_handle_done
|
||||
sub dtTimer3D,200
|
||||
mov dtState3D,DT_FLIGHT
|
||||
jmp dt_handle_done
|
||||
dt_animate_flight:
|
||||
cmp dtState3D,DT_FLIGHT
|
||||
jne dt_animate_impact
|
||||
cmp dtTimer3D,DT_THROW_MS
|
||||
jb dt_handle_done
|
||||
call SettleDarts3D
|
||||
jmp dt_handle_done
|
||||
dt_animate_impact:
|
||||
cmp dtState3D,DT_IMPACT
|
||||
jne dt_animate_return
|
||||
cmp dtTimer3D,1300
|
||||
jb dt_handle_done
|
||||
mov dtState3D,DT_WIN
|
||||
cmp dtRemaining3D,0
|
||||
je dt_handle_done
|
||||
mov dtState3D,DT_RETURN
|
||||
mov dtTimer3D,0
|
||||
jmp dt_handle_done
|
||||
dt_animate_return:
|
||||
cmp dtState3D,DT_RETURN
|
||||
jne dt_handle_done
|
||||
cmp dtTimer3D,750
|
||||
jb dt_handle_done
|
||||
cmp dtBust3D,0
|
||||
jne dt_next_visit
|
||||
cmp dtCount3D,3
|
||||
jb dt_return_aim
|
||||
dt_next_visit:
|
||||
mov dtBust3D,0
|
||||
mov dtCount3D,0
|
||||
inc dtVisit3D
|
||||
xor dtActor3D,1
|
||||
mov eax,dtActor3D
|
||||
mov eax,dtScores3D[eax*4]
|
||||
mov dtRemaining3D,eax
|
||||
mov dtVisitStart3D,eax
|
||||
dt_return_aim:
|
||||
mov dtState3D,DT_AIM
|
||||
mov dtTimer3D,0
|
||||
mov dtMouseReleased3D,0
|
||||
mov dtHolding3D,0
|
||||
mov dtHoldTime3D,0
|
||||
mov dtBotPrepared3D,0
|
||||
mov dtBotTimer3D,0
|
||||
dt_handle_done:
|
||||
mov mouseClick3D,0
|
||||
ret
|
||||
HandleDarts3D ENDP
|
||||
|
||||
DartsPath3D PROC uses ebx esi timeMs:DWORD
|
||||
local progress:DWORD,pathX:DWORD,pathY:DWORD,pathZ:DWORD
|
||||
mov eax,timeMs
|
||||
cmp eax,DT_THROW_MS
|
||||
jbe @F
|
||||
mov eax,DT_THROW_MS
|
||||
@@:
|
||||
shl eax,16
|
||||
xor edx,edx
|
||||
mov ecx,DT_THROW_MS
|
||||
div ecx
|
||||
mov progress,eax
|
||||
mov ebx,eax
|
||||
imul eax,DT_WALL
|
||||
sar eax,16
|
||||
mov pathZ,eax
|
||||
mov eax,dtHitX3D
|
||||
sub eax,70
|
||||
imul eax,ebx
|
||||
sar eax,16
|
||||
add eax,70
|
||||
mov pathX,eax
|
||||
mov eax,65536
|
||||
sub eax,ebx
|
||||
imul ebx
|
||||
shrd eax,edx,16
|
||||
imul eax,280
|
||||
sar eax,16
|
||||
mov esi,eax
|
||||
mov eax,dtHitY3D
|
||||
add eax,35
|
||||
imul eax,ebx
|
||||
sar eax,16
|
||||
sub eax,35
|
||||
add eax,esi
|
||||
mov edx,eax
|
||||
mov eax,pathX
|
||||
mov ecx,pathZ
|
||||
ret
|
||||
DartsPath3D ENDP
|
||||
|
||||
; Real orbit: behind -> alongside the dart -> behind the impact, with one camera basis.
|
||||
DartsCamera3D PROC uses ebx esi
|
||||
local progress:DWORD,weight:DWORD,timeMs:DWORD,factor:DWORD
|
||||
local length2:DWORD,span:DWORD,targetZ:DWORD,deltaX:DWORD,deltaZ:DWORD
|
||||
mov timeMs,0
|
||||
cmp dtState3D,DT_FLIGHT
|
||||
jne @F
|
||||
mov eax,dtTimer3D
|
||||
mov timeMs,eax
|
||||
jmp dt_camera_path
|
||||
@@:
|
||||
cmp dtState3D,DT_IMPACT
|
||||
je dt_camera_end
|
||||
cmp dtState3D,DT_WIN
|
||||
je dt_camera_end
|
||||
cmp dtState3D,DT_RETURN
|
||||
jne dt_camera_path
|
||||
dt_camera_end:
|
||||
mov timeMs,DT_THROW_MS
|
||||
dt_camera_path:
|
||||
invoke DartsPath3D,timeMs
|
||||
mov dtShotX3D,eax
|
||||
mov dtShotY3D,edx
|
||||
mov dtShotZ3D,ecx
|
||||
mov eax,timeMs
|
||||
cmp eax,DT_THROW_MS
|
||||
jbe @F
|
||||
mov eax,DT_THROW_MS
|
||||
@@:
|
||||
shl eax,16
|
||||
xor edx,edx
|
||||
mov ecx,DT_THROW_MS
|
||||
div ecx
|
||||
mov progress,eax
|
||||
mov ebx,eax
|
||||
shr eax,8
|
||||
mov eax,tableSin[eax*4]
|
||||
imul eax
|
||||
shrd eax,edx,16
|
||||
mov weight,eax
|
||||
imul eax,260
|
||||
sar eax,16
|
||||
mov ecx,420
|
||||
sub ecx,eax
|
||||
mov eax,dtShotZ3D
|
||||
sub eax,ecx
|
||||
jns @F
|
||||
xor eax,eax
|
||||
@@:
|
||||
mov dtCamZ3D,eax
|
||||
mov eax,weight
|
||||
imul eax,300
|
||||
sar eax,16
|
||||
mov ecx,dtShotX3D
|
||||
sub ecx,70
|
||||
sub ecx,eax
|
||||
mov dtCamX3D,ecx
|
||||
mov eax,weight
|
||||
imul eax,80
|
||||
sar eax,16
|
||||
add eax,dtShotY3D
|
||||
add eax,35
|
||||
mov dtCamY3D,eax
|
||||
mov eax,dtHitX3D
|
||||
imul eax,ebx
|
||||
sar eax,17
|
||||
mov esi,eax
|
||||
mov ecx,dtShotX3D
|
||||
sub ecx,eax
|
||||
imul ecx,weight
|
||||
sar ecx,16
|
||||
add ecx,esi
|
||||
mov dtLookX3D,ecx
|
||||
mov eax,dtHitY3D
|
||||
imul eax,ebx
|
||||
sar eax,17
|
||||
mov esi,eax
|
||||
mov ecx,dtShotY3D
|
||||
sub ecx,eax
|
||||
imul ecx,weight
|
||||
sar ecx,16
|
||||
add ecx,esi
|
||||
mov dtLookY3D,ecx
|
||||
mov eax,dtShotZ3D
|
||||
sub eax,DT_WALL
|
||||
imul eax,weight
|
||||
sar eax,16
|
||||
add eax,DT_WALL
|
||||
mov targetZ,eax
|
||||
cmp dtState3D,DT_RETURN
|
||||
jne dt_camera_basis
|
||||
mov eax,dtTimer3D
|
||||
cmp eax,750
|
||||
jbe @F
|
||||
mov eax,750
|
||||
@@:
|
||||
mov ecx,750
|
||||
sub ecx,eax
|
||||
imul eax,ecx,65536
|
||||
xor edx,edx
|
||||
mov ecx,750
|
||||
div ecx
|
||||
mov ebx,eax
|
||||
mov eax,dtCamX3D
|
||||
imul eax,ebx
|
||||
sar eax,16
|
||||
mov dtCamX3D,eax
|
||||
mov eax,dtCamY3D
|
||||
imul eax,ebx
|
||||
sar eax,16
|
||||
mov dtCamY3D,eax
|
||||
mov eax,dtCamZ3D
|
||||
imul eax,ebx
|
||||
sar eax,16
|
||||
mov dtCamZ3D,eax
|
||||
mov eax,dtLookX3D
|
||||
imul eax,ebx
|
||||
sar eax,16
|
||||
mov dtLookX3D,eax
|
||||
mov eax,dtLookY3D
|
||||
imul eax,ebx
|
||||
sar eax,16
|
||||
mov dtLookY3D,eax
|
||||
dt_camera_basis:
|
||||
mov eax,dtCamZ3D
|
||||
imul eax,550
|
||||
xor edx,edx
|
||||
mov ecx,DT_WALL-420
|
||||
div ecx
|
||||
mov ecx,DT_FOCAL
|
||||
sub ecx,eax
|
||||
mov eax,weight
|
||||
imul eax,160
|
||||
sar eax,16
|
||||
sub ecx,eax
|
||||
cmp ecx,350
|
||||
jge @F
|
||||
mov ecx,350
|
||||
@@:
|
||||
mov dtLens3D,ecx
|
||||
mov eax,dtLookX3D
|
||||
sub eax,dtCamX3D
|
||||
mov deltaX,eax
|
||||
imul eax,eax
|
||||
mov ebx,targetZ
|
||||
sub ebx,dtCamZ3D
|
||||
mov deltaZ,ebx
|
||||
imul ebx,ebx
|
||||
add eax,ebx
|
||||
mov length2,eax
|
||||
fild length2
|
||||
fsqrt
|
||||
fistp span
|
||||
cmp span,1
|
||||
jge @F
|
||||
mov span,1
|
||||
@@:
|
||||
mov eax,deltaX
|
||||
shl eax,16
|
||||
cdq
|
||||
idiv span
|
||||
mov dtYawSin3D,eax
|
||||
mov eax,deltaZ
|
||||
shl eax,16
|
||||
cdq
|
||||
idiv span
|
||||
mov dtYawCos3D,eax
|
||||
mov eax,dtLookY3D
|
||||
sub eax,dtCamY3D
|
||||
imul eax,dtLens3D
|
||||
cdq
|
||||
idiv span
|
||||
add eax,214
|
||||
mov dtScreenY3D,eax
|
||||
mov dtScreenX3D,320
|
||||
ret
|
||||
DartsCamera3D ENDP
|
||||
|
||||
DartsProject3D PROC uses ebx x:SDWORD,y:SDWORD,z:SDWORD
|
||||
local deltaX:DWORD,deltaZ:DWORD,depth:DWORD,side:DWORD
|
||||
mov eax,x
|
||||
sub eax,dtCamX3D
|
||||
mov deltaX,eax
|
||||
mov ebx,z
|
||||
sub ebx,dtCamZ3D
|
||||
mov deltaZ,ebx
|
||||
imul eax,dtYawCos3D
|
||||
imul ebx,dtYawSin3D
|
||||
sub eax,ebx
|
||||
sar eax,16
|
||||
mov side,eax
|
||||
mov eax,deltaX
|
||||
imul eax,dtYawSin3D
|
||||
mov ebx,deltaZ
|
||||
imul ebx,dtYawCos3D
|
||||
add eax,ebx
|
||||
sar eax,16
|
||||
mov depth,eax
|
||||
cmp eax,16
|
||||
jg @F
|
||||
mov eax,16
|
||||
@@:
|
||||
mov ecx,eax
|
||||
mov eax,side
|
||||
imul eax,dtLens3D
|
||||
cdq
|
||||
idiv ecx
|
||||
add eax,320
|
||||
push eax
|
||||
mov eax,y
|
||||
sub eax,dtCamY3D
|
||||
imul eax,dtLens3D
|
||||
cdq
|
||||
idiv ecx
|
||||
mov edx,dtScreenY3D
|
||||
sub edx,eax
|
||||
pop eax
|
||||
mov ecx,depth
|
||||
ret
|
||||
DartsProject3D ENDP
|
||||
|
||||
ResolveDartsSurface3D PROC
|
||||
cmp dtBoard3D.pixels,0
|
||||
jne @F
|
||||
invoke LoadAnimPack3D,OFFSET dtBoard3D,OFFSET dtBoardPath
|
||||
@@:
|
||||
mov eax,dtBoard3D.pixels
|
||||
mov ecx,512
|
||||
mov edx,512
|
||||
ret
|
||||
ResolveDartsSurface3D ENDP
|
||||
|
||||
DrawHallDarts3D PROC
|
||||
cmp hallLevel3D,0
|
||||
jne @F
|
||||
invoke DrawCityVertical3D,0,FP_1*3098/100,FP_1*275/10,FP_1*285/10,FP_1*115/100,FP_1*215/100,160
|
||||
@@:
|
||||
ret
|
||||
DrawHallDarts3D ENDP
|
||||
|
||||
include src_graphics\darts_render.inc
|
||||
3
src_graphics/darts_art.inc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
; Generated per-view needle anchors in the 256px source canvas.
|
||||
dtDartTipX3D dd 94,70,41,24,28,21,11,3,15,9,8,11,17,10,9,7
|
||||
dtDartTipY3D dd 142,139,139,139,135,135,134,133,132,131,130,130,116,118,123,128
|
||||
145
src_graphics/darts_data.inc
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
; All game and camera logic is native MASM; offline tooling only packs artwork.
|
||||
MODE_DARTS equ 27
|
||||
INTERACT_DARTS equ 47
|
||||
DT_AIM equ 0
|
||||
DT_RELEASE equ 1
|
||||
DT_FLIGHT equ 2
|
||||
DT_IMPACT equ 3
|
||||
DT_RETURN equ 4
|
||||
DT_WIN equ 5
|
||||
DT_WALL equ 2700
|
||||
DT_FOCAL equ 950
|
||||
DT_THROW_MS equ 1200
|
||||
BeginDarts3D PROTO
|
||||
HandleDarts3D PROTO
|
||||
RenderDarts3D PROTO
|
||||
LoadDarts3D PROTO
|
||||
FreeDarts3D PROTO
|
||||
ResetDarts3D PROTO
|
||||
DartsAim3D PROTO
|
||||
ThrowDarts3D PROTO
|
||||
DartsCamera3D PROTO
|
||||
DartsPath3D PROTO :DWORD
|
||||
PrepareDartsRays3D PROTO
|
||||
DrawDartsTrail3D PROTO
|
||||
DrawDartsLine3D PROTO :SDWORD,:SDWORD,:SDWORD,:SDWORD,:DWORD
|
||||
DartsBotAim3D PROTO
|
||||
DartsRecordScore3D PROTO
|
||||
UpdateDartsWall3D PROTO
|
||||
DartsWallText3D PROTO :DWORD,:DWORD,:DWORD
|
||||
DrawDartsScoreboard3D PROTO
|
||||
DrawDartsProps3D PROTO
|
||||
DrawDartsArm3D PROTO :DWORD,:SDWORD,:SDWORD
|
||||
DartsScore3D PROTO :SDWORD,:SDWORD
|
||||
SettleDarts3D PROTO
|
||||
DrawDartsRoom3D PROTO
|
||||
DrawDartsModel3D PROTO :SDWORD,:SDWORD,:SDWORD
|
||||
DartsProject3D PROTO :SDWORD,:SDWORD,:SDWORD
|
||||
DrawHallDarts3D PROTO
|
||||
ResolveDartsSurface3D PROTO
|
||||
.data
|
||||
dtBoard3D AnimPack3D <>
|
||||
dtHands3D AnimPack3D <>
|
||||
dtDart3D AnimPack3D <>
|
||||
dtWall3D AnimPack3D <>
|
||||
dtChair3D AnimPack3D <>
|
||||
dtTable3D AnimPack3D <>
|
||||
dtRoom3D AnimPack3D <>
|
||||
dtBoardPath db "assets\animations\darts\board-v1.bin",0
|
||||
dtHandsPath db "assets\animations\darts\arm-throw-v3.bin",0
|
||||
dtDartPath db "assets\animations\darts\dart-views-v1.bin",0
|
||||
dtWallPath db "assets\animations\darts\lounge-wall-v1.bin",0
|
||||
dtChairPath db "assets\animations\hall\lounge-chair-v5.bin",0
|
||||
dtTablePath db "assets\animations\hall\lamp-table-v5.bin",0
|
||||
dtRoomPath db "assets\animations\hall\room-materials-hd.bin",0
|
||||
dtState3D dd DT_AIM
|
||||
dtTimer3D dd 0
|
||||
dtClock3D dd 0
|
||||
dtRemaining3D dd 500
|
||||
dtVisitStart3D dd 500
|
||||
dtScores3D dd 500,500
|
||||
dtActor3D dd 0
|
||||
dtWinner3D dd 0
|
||||
dtBotTimer3D dd 0
|
||||
dtBotPrepared3D dd 0
|
||||
dtBotAimX3D dd 0
|
||||
dtBotAimY3D dd 0
|
||||
dtBotDesired3D dd 60
|
||||
dtCursorX3D dd 320*256
|
||||
dtCursorY3D dd 214*256
|
||||
dtScoreOpen3D dd 0
|
||||
dtHistoryRows3D dd 1
|
||||
dtHistory3D dd 64 dup(0)
|
||||
dtWallDirty3D dd 1
|
||||
dtVisit3D dd 1
|
||||
dtCount3D dd 0
|
||||
dtThrows3D dd 0
|
||||
dtLast3D dd 0
|
||||
dtMultiplier3D dd 0
|
||||
dtBust3D dd 0
|
||||
dtError3D dd 0
|
||||
dtHolding3D dd 0
|
||||
dtHoldTime3D dd 0
|
||||
dtMouseReleased3D dd 0
|
||||
dtAimX3D dd 0
|
||||
dtAimY3D dd 0
|
||||
dtHitX3D dd 0
|
||||
dtHitY3D dd 0
|
||||
dtMarkX3D dd 3 dup(0)
|
||||
dtMarkY3D dd 3 dup(0)
|
||||
dtMarkScore3D dd 3 dup(0)
|
||||
dtShotX3D dd 0
|
||||
dtShotY3D dd 0
|
||||
dtShotZ3D dd 0
|
||||
dtCamX3D dd 0
|
||||
dtCamY3D dd 0
|
||||
dtCamZ3D dd 0
|
||||
dtLens3D dd DT_FOCAL
|
||||
dtYawSin3D dd 0
|
||||
dtYawCos3D dd 65536
|
||||
dtLookX3D dd 0
|
||||
dtLookY3D dd 0
|
||||
dtScreenX3D dd 320
|
||||
dtScreenY3D dd 214
|
||||
include src_graphics\darts_art.inc
|
||||
dtSector3D dd 20,1,18,4,13,6,10,15,2,17,3,19,7,16,8,11,14,9,12,5
|
||||
dtAngleFactor3D REAL8 10430.37835047045
|
||||
dtTitle db 196,192,208,210,209,32,53,48,48,0
|
||||
dtYou db 194,219,0
|
||||
dtBot db 204,192,202,209,0
|
||||
dtYourTurn db 194,192,216,32,213,206,196,0
|
||||
dtBotTurn db 213,206,196,32,204,192,202,209,192,0
|
||||
dtYouWin db 194,219,32,207,206,193,197,196,200,203,200,0
|
||||
dtBotWin db 204,192,202,209,32,207,206,193,197,196,200,203,0
|
||||
dtScoreTitle db 209,215,197,210,32,204,192,210,215,192,0
|
||||
; Fixed wall-axis furniture planes: axis, fixed coordinate, range, bottom/top, pack.
|
||||
dtProps3D dd 0,2150,-1800,-1000,-1730,-380,OFFSET dtChair3D
|
||||
dd 1,1950,1050,1850,-1730,-380,OFFSET dtChair3D
|
||||
dd 1,1950,100,900,-1730,-380,OFFSET dtChair3D
|
||||
dd 1,1780,850,1200,-1730,-680,OFFSET dtTable3D
|
||||
dtThrow db 193,208,206,209,206,202,0
|
||||
dtLeft db 206,209,210,192,203,206,209,220,0
|
||||
dtVisit db 207,206,196,213,206,196,0
|
||||
dtWin db 195,206,210,206,194,206,33,32,208,206,194,205,206,32,205,206,203,220,0
|
||||
dtBust db 207,197,208,197,193,206,208,0
|
||||
dtMiss db 204,200,204,206,0
|
||||
dtNext db 196,192,203,220,216,197,0
|
||||
dtAgain db 209,205,206,194,192,0
|
||||
dtExit db 194,32,213,206,203,203,0
|
||||
dtPrompt db 69,58,32,196,192,208,210,209,0
|
||||
dtLoading db 205,197,32,211,196,192,203,206,209,220,32,199,192,195,208,211,199,200,210,220,32,196,192,208,210,209,0
|
||||
dtHold db 203,202,204,0
|
||||
dtBull db 66,85,76,76,0
|
||||
dtDouble db 68,0
|
||||
dtTriple db 84,0
|
||||
dtSingle db 83,0
|
||||
dtTarget db 214,197,203,220,0
|
||||
.data?
|
||||
dtRayX3D dd 640 dup(?)
|
||||
dtRayZ3D dd 640 dup(?)
|
||||
dtRayDepth3D dd 640 dup(?)
|
||||
dtRayU3D dd 640 dup(?)
|
||||
dtRayFront3D dd 640 dup(?)
|
||||
dtRayHitX3D dd 640 dup(?)
|
||||
.code
|
||||
|
||||
330
src_graphics/darts_environment.inc
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
; A single rigid sprite transform aligns the dart axis with the live target.
|
||||
DrawDartsArm3D PROC uses ebx esi edi pose:DWORD,targetX:SDWORD,targetY:SDWORD
|
||||
local originX:DWORD,originY:DWORD,left:DWORD,right:DWORD,bottom:DWORD,row:DWORD,column:DWORD
|
||||
local deltaX:DWORD,deltaY:DWORD,sourceX:DWORD,framePtr:DWORD
|
||||
mov eax,pose
|
||||
shl eax,18
|
||||
add eax,dtHands3D.pixels
|
||||
mov framePtr,eax
|
||||
mov eax,targetX
|
||||
sub eax,10
|
||||
mov originX,eax
|
||||
mov left,eax
|
||||
add eax,363
|
||||
mov right,eax
|
||||
mov eax,targetY
|
||||
add eax,49
|
||||
mov originY,eax
|
||||
add eax,182
|
||||
cmp eax,400
|
||||
jle @F
|
||||
mov eax,400
|
||||
@@:
|
||||
mov bottom,eax
|
||||
mov eax,originY
|
||||
sub eax,182
|
||||
cmp eax,32
|
||||
jge @F
|
||||
mov eax,32
|
||||
@@:
|
||||
mov row,eax
|
||||
cmp left,0
|
||||
jge @F
|
||||
mov left,0
|
||||
@@:
|
||||
cmp right,640
|
||||
jle dt_arm_row
|
||||
mov right,640
|
||||
dt_arm_row:
|
||||
mov eax,row
|
||||
cmp eax,bottom
|
||||
jge dt_arm_done
|
||||
sub eax,originY
|
||||
mov deltaY,eax
|
||||
mov eax,left
|
||||
mov column,eax
|
||||
dt_arm_column:
|
||||
mov eax,column
|
||||
cmp eax,right
|
||||
jge dt_arm_next_row
|
||||
sub eax,originX
|
||||
mov deltaX,eax
|
||||
sub eax,deltaY
|
||||
imul eax,46341
|
||||
sar eax,16
|
||||
cmp eax,256
|
||||
jae dt_arm_skip
|
||||
mov sourceX,eax
|
||||
mov eax,deltaX
|
||||
add eax,deltaY
|
||||
imul eax,46341
|
||||
sar eax,16
|
||||
cmp eax,256
|
||||
jae dt_arm_skip
|
||||
shl eax,8
|
||||
add eax,sourceX
|
||||
mov esi,framePtr
|
||||
mov eax,[esi+eax*4]
|
||||
test eax,80000000h
|
||||
jz dt_arm_skip
|
||||
and eax,00FFFFFFh
|
||||
mov edx,row
|
||||
imul edx,640
|
||||
add edx,column
|
||||
mov screenBuffer[edx*4],eax
|
||||
dt_arm_skip:
|
||||
inc column
|
||||
jmp dt_arm_column
|
||||
dt_arm_next_row:
|
||||
inc row
|
||||
jmp dt_arm_row
|
||||
dt_arm_done:
|
||||
ret
|
||||
DrawDartsArm3D ENDP
|
||||
|
||||
; Furniture is attached to fixed world planes, never camera-facing billboards.
|
||||
DrawDartsProps3D PROC uses ebx esi edi
|
||||
local item:DWORD,column:DWORD,depth:DWORD,sourceU:DWORD,packPtr:DWORD
|
||||
local firstY:DWORD,lastY:DWORD,row:DWORD,sourceY:DWORD,stepY:DWORD,span:DWORD
|
||||
mov item,0
|
||||
dt_prop_item:
|
||||
imul eax,item,28
|
||||
lea esi,dtProps3D[eax]
|
||||
mov eax,[esi+24]
|
||||
mov packPtr,eax
|
||||
mov column,0
|
||||
dt_prop_column:
|
||||
mov ebx,column
|
||||
cmp dword ptr [esi],0
|
||||
jne dt_prop_side
|
||||
mov ecx,dtRayZ3D[ebx*4]
|
||||
mov eax,[esi+4]
|
||||
sub eax,dtCamZ3D
|
||||
jmp dt_prop_distance
|
||||
dt_prop_side:
|
||||
mov ecx,dtRayX3D[ebx*4]
|
||||
mov eax,[esi+4]
|
||||
sub eax,dtCamX3D
|
||||
dt_prop_distance:
|
||||
test ecx,ecx
|
||||
jz dt_prop_next_column
|
||||
shl eax,16
|
||||
cdq
|
||||
idiv ecx
|
||||
cmp eax,40
|
||||
jl dt_prop_next_column
|
||||
cmp eax,dtRayDepth3D[ebx*4]
|
||||
jge dt_prop_next_column
|
||||
mov depth,eax
|
||||
mov eax,dtRayX3D[ebx*4]
|
||||
mov edi,dtCamX3D
|
||||
cmp dword ptr [esi],0
|
||||
je @F
|
||||
mov eax,dtRayZ3D[ebx*4]
|
||||
mov edi,dtCamZ3D
|
||||
@@:
|
||||
imul depth
|
||||
shrd eax,edx,16
|
||||
add eax,edi
|
||||
sub eax,[esi+8]
|
||||
js dt_prop_next_column
|
||||
mov ecx,[esi+12]
|
||||
sub ecx,[esi+8]
|
||||
cmp eax,ecx
|
||||
jge dt_prop_next_column
|
||||
mov edi,packPtr
|
||||
imul eax,[edi].AnimPack3D.imageWidth
|
||||
cdq
|
||||
idiv ecx
|
||||
mov sourceU,eax
|
||||
mov eax,[esi+20]
|
||||
sub eax,dtCamY3D
|
||||
imul eax,dtLens3D
|
||||
cdq
|
||||
idiv depth
|
||||
mov ecx,dtScreenY3D
|
||||
sub ecx,eax
|
||||
mov firstY,ecx
|
||||
mov eax,[esi+16]
|
||||
sub eax,dtCamY3D
|
||||
imul eax,dtLens3D
|
||||
cdq
|
||||
idiv depth
|
||||
mov ecx,dtScreenY3D
|
||||
sub ecx,eax
|
||||
mov lastY,ecx
|
||||
sub ecx,firstY
|
||||
jle dt_prop_next_column
|
||||
mov span,ecx
|
||||
mov eax,[edi].AnimPack3D.height
|
||||
shl eax,16
|
||||
xor edx,edx
|
||||
div span
|
||||
mov stepY,eax
|
||||
mov eax,firstY
|
||||
cmp eax,32
|
||||
jge @F
|
||||
mov eax,32
|
||||
@@:
|
||||
mov row,eax
|
||||
sub eax,firstY
|
||||
imul eax,stepY
|
||||
mov sourceY,eax
|
||||
cmp lastY,400
|
||||
jle dt_prop_row
|
||||
mov lastY,400
|
||||
dt_prop_row:
|
||||
mov eax,row
|
||||
cmp eax,lastY
|
||||
jge dt_prop_next_column
|
||||
mov eax,sourceY
|
||||
shr eax,16
|
||||
cmp eax,[edi].AnimPack3D.height
|
||||
jae dt_prop_next_column
|
||||
imul eax,[edi].AnimPack3D.imageWidth
|
||||
add eax,sourceU
|
||||
mov edx,[edi].AnimPack3D.pixels
|
||||
mov eax,[edx+eax*4]
|
||||
test eax,80000000h
|
||||
jz @F
|
||||
mov edx,row
|
||||
imul edx,640
|
||||
add edx,column
|
||||
and eax,00FFFFFFh
|
||||
mov screenBuffer[edx*4],eax
|
||||
@@:
|
||||
mov eax,stepY
|
||||
add sourceY,eax
|
||||
inc row
|
||||
jmp dt_prop_row
|
||||
dt_prop_next_column:
|
||||
inc column
|
||||
cmp column,640
|
||||
jb dt_prop_column
|
||||
inc item
|
||||
cmp item,4
|
||||
jb dt_prop_item
|
||||
ret
|
||||
DrawDartsProps3D ENDP
|
||||
|
||||
; Write chalk glyphs directly into the wall texture, so the score follows perspective.
|
||||
DartsWallText3D PROC uses ebx esi edi px:DWORD,py:DWORD,textPtr:DWORD
|
||||
local row:DWORD,column:DWORD,pen:DWORD,glyph:DWORD
|
||||
mov esi,textPtr
|
||||
mov eax,px
|
||||
mov pen,eax
|
||||
dt_chalk_char:
|
||||
movzx eax,byte ptr [esi]
|
||||
test eax,eax
|
||||
jz dt_chalk_done
|
||||
lea eax,font8x8[eax*8]
|
||||
mov glyph,eax
|
||||
mov row,0
|
||||
dt_chalk_row:
|
||||
mov eax,glyph
|
||||
mov edx,row
|
||||
movzx ebx,byte ptr [eax+edx]
|
||||
mov column,0
|
||||
dt_chalk_pixel:
|
||||
test ebx,80h
|
||||
jz @F
|
||||
mov eax,row
|
||||
add eax,py
|
||||
shl eax,9
|
||||
add eax,pen
|
||||
add eax,column
|
||||
mov edi,dtWall3D.pixels
|
||||
mov dword ptr [edi+eax*4],0FFD8D9C2h
|
||||
@@:
|
||||
shl ebx,1
|
||||
inc column
|
||||
cmp column,8
|
||||
jb dt_chalk_pixel
|
||||
inc row
|
||||
cmp row,8
|
||||
jb dt_chalk_row
|
||||
add pen,8
|
||||
inc esi
|
||||
jmp dt_chalk_char
|
||||
dt_chalk_done:
|
||||
ret
|
||||
DartsWallText3D ENDP
|
||||
|
||||
UpdateDartsWall3D PROC uses ebx esi edi
|
||||
cmp dtWallDirty3D,0
|
||||
je dt_wall_score_done
|
||||
cmp dtWall3D.pixels,0
|
||||
je dt_wall_score_done
|
||||
mov ebx,104
|
||||
dt_chalk_clear:
|
||||
mov eax,ebx
|
||||
shl eax,9
|
||||
add eax,32
|
||||
mov edi,dtWall3D.pixels
|
||||
lea edi,[edi+eax*4]
|
||||
mov ecx,102
|
||||
mov eax,0FF202623h
|
||||
rep stosd
|
||||
inc ebx
|
||||
cmp ebx,282
|
||||
jb dt_chalk_clear
|
||||
invoke DartsWallText3D,40,108,OFFSET dtYou
|
||||
invoke DartsWallText3D,86,108,OFFSET dtBot
|
||||
invoke FormatNumber,dtScores3D,OFFSET hudNumBuffer
|
||||
invoke DartsWallText3D,40,126,OFFSET hudNumBuffer
|
||||
invoke FormatNumber,dtScores3D[4],OFFSET hudNumBuffer
|
||||
invoke DartsWallText3D,86,126,OFFSET hudNumBuffer
|
||||
mov ebx,dtHistoryRows3D
|
||||
sub ebx,7
|
||||
jns @F
|
||||
xor ebx,ebx
|
||||
@@:
|
||||
mov esi,150
|
||||
dt_chalk_history:
|
||||
cmp ebx,dtHistoryRows3D
|
||||
jae dt_chalk_clean
|
||||
invoke FormatNumber,dtHistory3D[ebx*8],OFFSET hudNumBuffer
|
||||
invoke DartsWallText3D,40,esi,OFFSET hudNumBuffer
|
||||
invoke FormatNumber,dtHistory3D[ebx*8+4],OFFSET hudNumBuffer
|
||||
invoke DartsWallText3D,86,esi,OFFSET hudNumBuffer
|
||||
add esi,17
|
||||
inc ebx
|
||||
jmp dt_chalk_history
|
||||
dt_chalk_clean:
|
||||
mov dtWallDirty3D,0
|
||||
dt_wall_score_done:
|
||||
ret
|
||||
UpdateDartsWall3D ENDP
|
||||
|
||||
DrawDartsScoreboard3D PROC uses ebx esi edi
|
||||
invoke DrawBoxBorder,54,23,212,174,001A2420h,00B0A16Eh,2
|
||||
invoke DrawString8x8,70,32,OFFSET dtScoreTitle,00E9DEBAh
|
||||
invoke DrawString8x8,118,49,OFFSET dtYou,0090CDB9h
|
||||
invoke DrawString8x8,198,49,OFFSET dtBot,00E8D7ADh
|
||||
mov ebx,dtHistoryRows3D
|
||||
sub ebx,10
|
||||
jns @F
|
||||
xor ebx,ebx
|
||||
@@:
|
||||
mov esi,65
|
||||
dt_scoreboard_row:
|
||||
cmp ebx,dtHistoryRows3D
|
||||
jae dt_scoreboard_current
|
||||
lea eax,[ebx+1]
|
||||
invoke FormatNumber,eax,OFFSET hudNumBuffer
|
||||
invoke DrawString8x8,70,esi,OFFSET hudNumBuffer,008A9C91h
|
||||
invoke FormatNumber,dtHistory3D[ebx*8],OFFSET hudNumBuffer
|
||||
invoke DrawString8x8,118,esi,OFFSET hudNumBuffer,00E2E2CFh
|
||||
invoke FormatNumber,dtHistory3D[ebx*8+4],OFFSET hudNumBuffer
|
||||
invoke DrawString8x8,198,esi,OFFSET hudNumBuffer,00E2E2CFh
|
||||
add esi,10
|
||||
inc ebx
|
||||
jmp dt_scoreboard_row
|
||||
dt_scoreboard_current:
|
||||
invoke DrawBox,66,170,188,1,00758772h
|
||||
invoke FormatNumber,dtScores3D,OFFSET hudNumBuffer
|
||||
invoke DrawString8x8,118,180,OFFSET hudNumBuffer,00FFFFFFh
|
||||
invoke FormatNumber,dtScores3D[4],OFFSET hudNumBuffer
|
||||
invoke DrawString8x8,198,180,OFFSET hudNumBuffer,00FFFFFFh
|
||||
ret
|
||||
DrawDartsScoreboard3D ENDP
|
||||
657
src_graphics/darts_render.inc
Normal file
|
|
@ -0,0 +1,657 @@
|
|||
include src_graphics\darts_environment.inc
|
||||
|
||||
; One-pixel, alpha-blended trajectory, clipped to the 3D playfield.
|
||||
DrawDartsLine3D PROC uses ebx esi edi x0:SDWORD,y0:SDWORD,x1:SDWORD,y1:SDWORD,opacity:DWORD
|
||||
local stepX:DWORD,stepY:DWORD,steps:DWORD,px:DWORD,py:DWORD,paint:DWORD
|
||||
mov eax,x1
|
||||
sub eax,x0
|
||||
cdq
|
||||
xor eax,edx
|
||||
sub eax,edx
|
||||
mov ebx,eax
|
||||
mov eax,y1
|
||||
sub eax,y0
|
||||
cdq
|
||||
xor eax,edx
|
||||
sub eax,edx
|
||||
cmp eax,ebx
|
||||
jbe @F
|
||||
mov ebx,eax
|
||||
@@:
|
||||
cmp ebx,2048
|
||||
ja dt_line_done
|
||||
test ebx,ebx
|
||||
jnz @F
|
||||
inc ebx
|
||||
@@:
|
||||
mov steps,ebx
|
||||
mov eax,x1
|
||||
sub eax,x0
|
||||
shl eax,16
|
||||
cdq
|
||||
idiv ebx
|
||||
mov stepX,eax
|
||||
mov eax,y1
|
||||
sub eax,y0
|
||||
shl eax,16
|
||||
cdq
|
||||
idiv ebx
|
||||
mov stepY,eax
|
||||
mov eax,x0
|
||||
shl eax,16
|
||||
mov px,eax
|
||||
mov eax,y0
|
||||
shl eax,16
|
||||
mov py,eax
|
||||
inc steps
|
||||
dt_line_pixel:
|
||||
mov eax,px
|
||||
sar eax,16
|
||||
cmp eax,640
|
||||
jae dt_line_skip
|
||||
mov edx,py
|
||||
sar edx,16
|
||||
cmp edx,32
|
||||
jl dt_line_skip
|
||||
cmp edx,400
|
||||
jge dt_line_skip
|
||||
imul edx,640
|
||||
add edx,eax
|
||||
lea edi,screenBuffer[edx*4]
|
||||
mov esi,[edi]
|
||||
movzx eax,byte ptr [edi]
|
||||
mov ecx,219
|
||||
sub ecx,eax
|
||||
imul ecx,opacity
|
||||
sar ecx,8
|
||||
add eax,ecx
|
||||
mov paint,eax
|
||||
mov eax,esi
|
||||
shr eax,8
|
||||
and eax,255
|
||||
mov ecx,210
|
||||
sub ecx,eax
|
||||
imul ecx,opacity
|
||||
sar ecx,8
|
||||
add eax,ecx
|
||||
shl eax,8
|
||||
or paint,eax
|
||||
mov eax,esi
|
||||
shr eax,16
|
||||
and eax,255
|
||||
mov ecx,180
|
||||
sub ecx,eax
|
||||
imul ecx,opacity
|
||||
sar ecx,8
|
||||
add eax,ecx
|
||||
shl eax,16
|
||||
or eax,paint
|
||||
mov [edi],eax
|
||||
dt_line_skip:
|
||||
mov eax,stepX
|
||||
add px,eax
|
||||
mov eax,stepY
|
||||
add py,eax
|
||||
dec steps
|
||||
jnz dt_line_pixel
|
||||
dt_line_done:
|
||||
ret
|
||||
DrawDartsLine3D ENDP
|
||||
|
||||
DrawDartsTrail3D PROC uses ebx esi edi
|
||||
local timeMs:DWORD,lastX:DWORD,lastY:DWORD,valid:DWORD,fade:DWORD,alpha:DWORD
|
||||
mov eax,dtTimer3D
|
||||
mov fade,256
|
||||
cmp dtState3D,DT_FLIGHT
|
||||
je dt_trail_start
|
||||
cmp dtState3D,DT_IMPACT
|
||||
jne dt_trail_done
|
||||
cmp eax,300
|
||||
jae dt_trail_done
|
||||
imul eax,256
|
||||
xor edx,edx
|
||||
mov ecx,300
|
||||
div ecx
|
||||
mov ecx,256
|
||||
sub ecx,eax
|
||||
mov fade,ecx
|
||||
mov eax,DT_THROW_MS
|
||||
dt_trail_start:
|
||||
mov timeMs,eax
|
||||
mov valid,0
|
||||
mov ebx,12
|
||||
dt_trail_segment:
|
||||
imul eax,ebx,20
|
||||
mov ecx,timeMs
|
||||
sub ecx,eax
|
||||
js dt_trail_next
|
||||
invoke DartsPath3D,ecx
|
||||
invoke DartsProject3D,eax,edx,ecx
|
||||
cmp ecx,60
|
||||
jl dt_trail_invalid
|
||||
mov esi,eax
|
||||
mov edi,edx
|
||||
cmp valid,0
|
||||
je dt_trail_remember
|
||||
mov eax,13
|
||||
sub eax,ebx
|
||||
imul eax,9
|
||||
imul eax,fade
|
||||
shr eax,8
|
||||
mov alpha,eax
|
||||
invoke DrawDartsLine3D,lastX,lastY,esi,edi,alpha
|
||||
dt_trail_remember:
|
||||
mov lastX,esi
|
||||
mov lastY,edi
|
||||
mov valid,1
|
||||
jmp dt_trail_next
|
||||
dt_trail_invalid:
|
||||
mov valid,0
|
||||
dt_trail_next:
|
||||
dec ebx
|
||||
jns dt_trail_segment
|
||||
dt_trail_done:
|
||||
ret
|
||||
DrawDartsTrail3D ENDP
|
||||
|
||||
; Independent scene camera. Hall player coordinates are never borrowed or modified.
|
||||
PrepareDartsRays3D PROC uses ebx esi edi
|
||||
local cameraRay:DWORD,wallType:DWORD,wallT:DWORD,sideT:DWORD
|
||||
xor ebx,ebx
|
||||
dt_ray_column:
|
||||
mov eax,ebx
|
||||
sub eax,320
|
||||
shl eax,16
|
||||
cdq
|
||||
idiv dtLens3D
|
||||
mov cameraRay,eax
|
||||
imul dtYawCos3D
|
||||
shrd eax,edx,16
|
||||
add eax,dtYawSin3D
|
||||
mov dtRayX3D[ebx*4],eax
|
||||
mov eax,cameraRay
|
||||
imul dtYawSin3D
|
||||
shrd eax,edx,16
|
||||
neg eax
|
||||
add eax,dtYawCos3D
|
||||
mov dtRayZ3D[ebx*4],eax
|
||||
mov wallT,7FFFFFFFh
|
||||
mov wallType,0
|
||||
test eax,eax
|
||||
jz dt_ray_side
|
||||
mov ecx,eax
|
||||
mov eax,DT_WALL
|
||||
test ecx,ecx
|
||||
jns @F
|
||||
mov eax,-2000
|
||||
mov wallType,2
|
||||
@@:
|
||||
sub eax,dtCamZ3D
|
||||
shl eax,16
|
||||
cdq
|
||||
idiv ecx
|
||||
test eax,eax
|
||||
jle dt_ray_side
|
||||
mov wallT,eax
|
||||
dt_ray_side:
|
||||
mov ecx,dtRayX3D[ebx*4]
|
||||
test ecx,ecx
|
||||
jz dt_ray_hit
|
||||
mov eax,2600
|
||||
test ecx,ecx
|
||||
jns @F
|
||||
mov eax,-2600
|
||||
@@:
|
||||
sub eax,dtCamX3D
|
||||
shl eax,16
|
||||
cdq
|
||||
idiv ecx
|
||||
test eax,eax
|
||||
jle dt_ray_hit
|
||||
cmp eax,wallT
|
||||
jge dt_ray_hit
|
||||
mov wallT,eax
|
||||
mov wallType,1
|
||||
dt_ray_hit:
|
||||
mov eax,wallT
|
||||
cmp eax,1
|
||||
jge @F
|
||||
mov eax,1
|
||||
@@:
|
||||
mov dtRayDepth3D[ebx*4],eax
|
||||
mov esi,eax
|
||||
mov eax,dtRayX3D[ebx*4]
|
||||
imul esi
|
||||
shrd eax,edx,16
|
||||
add eax,dtCamX3D
|
||||
mov dtRayHitX3D[ebx*4],eax
|
||||
cmp wallType,1
|
||||
jne @F
|
||||
mov eax,dtRayZ3D[ebx*4]
|
||||
imul esi
|
||||
shrd eax,edx,16
|
||||
add eax,dtCamZ3D
|
||||
@@:
|
||||
imul eax,256
|
||||
cdq
|
||||
mov ecx,500
|
||||
idiv ecx
|
||||
and eax,255
|
||||
mov dtRayU3D[ebx*4],eax
|
||||
mov eax,wallType
|
||||
mov dtRayFront3D[ebx*4],eax
|
||||
inc ebx
|
||||
cmp ebx,640
|
||||
jb dt_ray_column
|
||||
ret
|
||||
PrepareDartsRays3D ENDP
|
||||
|
||||
; True inverse projection onto front/side/back walls, floor and ceiling.
|
||||
DrawDartsRoom3D PROC uses ebx esi edi
|
||||
local row:DWORD,rayY:DWORD,worldY:DWORD,depth:DWORD,sourceU:DWORD,sourceV:DWORD
|
||||
call PrepareDartsRays3D
|
||||
mov row,32
|
||||
dt_room_row:
|
||||
mov eax,dtScreenY3D
|
||||
sub eax,row
|
||||
shl eax,16
|
||||
cdq
|
||||
idiv dtLens3D
|
||||
mov rayY,eax
|
||||
mov eax,row
|
||||
imul eax,640*4
|
||||
lea edi,screenBuffer[eax]
|
||||
xor ebx,ebx
|
||||
dt_room_pixel:
|
||||
mov eax,rayY
|
||||
imul dtRayDepth3D[ebx*4]
|
||||
shrd eax,edx,16
|
||||
add eax,dtCamY3D
|
||||
mov worldY,eax
|
||||
cmp eax,-1730
|
||||
jl dt_room_floor
|
||||
cmp eax,1300
|
||||
jg dt_room_ceiling
|
||||
cmp dtRayFront3D[ebx*4],0
|
||||
jne dt_room_wall
|
||||
mov eax,dtRayHitX3D[ebx*4]
|
||||
add eax,256
|
||||
cmp eax,512
|
||||
jae dt_room_wall
|
||||
mov sourceU,eax
|
||||
mov eax,256
|
||||
sub eax,worldY
|
||||
cmp eax,512
|
||||
jae dt_room_wall
|
||||
shl eax,9
|
||||
add eax,sourceU
|
||||
mov esi,dtBoard3D.pixels
|
||||
mov eax,[esi+eax*4]
|
||||
test eax,80000000h
|
||||
jnz dt_room_store
|
||||
dt_room_wall:
|
||||
mov eax,570
|
||||
sub eax,worldY
|
||||
imul eax,512
|
||||
cdq
|
||||
mov ecx,1800
|
||||
idiv ecx
|
||||
test eax,eax
|
||||
jns @F
|
||||
xor eax,eax
|
||||
@@:
|
||||
cmp eax,511
|
||||
jbe @F
|
||||
mov eax,511
|
||||
@@:
|
||||
shl eax,9
|
||||
mov sourceV,eax
|
||||
mov eax,dtRayU3D[ebx*4]
|
||||
and eax,15
|
||||
add eax,190
|
||||
cmp dtRayFront3D[ebx*4],0
|
||||
jne dt_room_wall_sample
|
||||
mov ecx,dtRayHitX3D[ebx*4]
|
||||
add ecx,850
|
||||
cmp ecx,1700
|
||||
jae dt_room_wall_sample
|
||||
imul eax,ecx,512
|
||||
cdq
|
||||
mov ecx,1700
|
||||
idiv ecx
|
||||
dt_room_wall_sample:
|
||||
add eax,sourceV
|
||||
mov esi,dtWall3D.pixels
|
||||
mov eax,[esi+eax*4]
|
||||
jmp dt_room_store
|
||||
dt_room_floor:
|
||||
mov eax,-1730
|
||||
jmp dt_room_horizontal
|
||||
dt_room_ceiling:
|
||||
mov eax,1300
|
||||
dt_room_horizontal:
|
||||
sub eax,dtCamY3D
|
||||
shl eax,16
|
||||
cdq
|
||||
idiv rayY
|
||||
mov depth,eax
|
||||
mov eax,dtRayX3D[ebx*4]
|
||||
imul depth
|
||||
shrd eax,edx,16
|
||||
add eax,dtCamX3D
|
||||
imul eax,256
|
||||
cdq
|
||||
mov ecx,500
|
||||
idiv ecx
|
||||
and eax,255
|
||||
mov sourceU,eax
|
||||
mov eax,dtRayZ3D[ebx*4]
|
||||
imul depth
|
||||
shrd eax,edx,16
|
||||
add eax,dtCamZ3D
|
||||
imul eax,256
|
||||
cdq
|
||||
mov ecx,500
|
||||
idiv ecx
|
||||
and eax,255
|
||||
shl eax,8
|
||||
add eax,sourceU
|
||||
mov esi,dtRoom3D.pixels
|
||||
add esi,5*256*256*4
|
||||
mov eax,[esi+eax*4]
|
||||
cmp rayY,0
|
||||
jle dt_room_store
|
||||
mov eax,00444943h
|
||||
dt_room_store:
|
||||
and eax,00FFFFFFh
|
||||
stosd
|
||||
inc ebx
|
||||
cmp ebx,640
|
||||
jb dt_room_pixel
|
||||
inc row
|
||||
cmp row,400
|
||||
jb dt_room_row
|
||||
ret
|
||||
DrawDartsRoom3D ENDP
|
||||
|
||||
; Camera-relative raster views retain the generated metal and flight detail.
|
||||
DrawDartsModel3D PROC uses ebx esi edi x:SDWORD,y:SDWORD,z:SDWORD
|
||||
local deltaX:DWORD,deltaZ:DWORD,angle:DWORD,pose:DWORD
|
||||
local centerX:DWORD,centerY:DWORD,depth:DWORD,span:DWORD,worldX:DWORD,worldY:DWORD
|
||||
mov eax,x
|
||||
sub eax,dtCamX3D
|
||||
mov deltaX,eax
|
||||
mov eax,z
|
||||
sub eax,dtCamZ3D
|
||||
mov deltaZ,eax
|
||||
fild deltaX
|
||||
fild deltaZ
|
||||
fpatan
|
||||
fabs
|
||||
fmul dtAngleFactor3D
|
||||
fistp angle
|
||||
imul eax,angle,60
|
||||
add eax,32768
|
||||
shr eax,16
|
||||
cmp eax,15
|
||||
jbe @F
|
||||
mov eax,15
|
||||
@@:
|
||||
mov pose,eax
|
||||
mov eax,x
|
||||
sub eax,70
|
||||
imul eax,64
|
||||
cdq
|
||||
mov ecx,DT_WALL
|
||||
idiv ecx
|
||||
mov edx,x
|
||||
sub edx,eax
|
||||
mov worldX,edx
|
||||
mov eax,y
|
||||
sub eax,280
|
||||
cmp z,DT_WALL-8
|
||||
jae @F
|
||||
mov eax,dtTimer3D
|
||||
imul eax,560
|
||||
xor edx,edx
|
||||
mov ecx,DT_THROW_MS
|
||||
div ecx
|
||||
mov edx,y
|
||||
add edx,280
|
||||
sub edx,eax
|
||||
mov eax,edx
|
||||
@@:
|
||||
imul eax,64
|
||||
cdq
|
||||
mov ecx,DT_WALL
|
||||
idiv ecx
|
||||
mov edx,y
|
||||
sub edx,eax
|
||||
mov worldY,edx
|
||||
mov eax,z
|
||||
sub eax,64
|
||||
invoke DartsProject3D,worldX,worldY,eax
|
||||
cmp ecx,80
|
||||
jl dt_model_done
|
||||
mov centerX,eax
|
||||
mov centerY,edx
|
||||
mov depth,ecx
|
||||
imul eax,dtLens3D,160
|
||||
xor edx,edx
|
||||
div depth
|
||||
cmp eax,240
|
||||
jbe @F
|
||||
mov eax,240
|
||||
@@:
|
||||
cmp eax,12
|
||||
jae @F
|
||||
mov eax,12
|
||||
@@:
|
||||
mov span,eax
|
||||
invoke DartsProject3D,x,y,z
|
||||
mov centerX,eax
|
||||
mov centerY,edx
|
||||
mov ebx,pose
|
||||
mov eax,dtDartTipX3D[ebx*4]
|
||||
imul eax,span
|
||||
shr eax,8
|
||||
sub centerX,eax
|
||||
mov eax,dtDartTipY3D[ebx*4]
|
||||
imul eax,span
|
||||
shr eax,8
|
||||
sub centerY,eax
|
||||
invoke DrawAnimSprite3D,OFFSET dtDart3D,pose,centerX,centerY,span,span
|
||||
dt_model_done:
|
||||
ret
|
||||
DrawDartsModel3D ENDP
|
||||
|
||||
RenderDarts3D PROC uses ebx esi edi
|
||||
local bx0:DWORD,by0:DWORD,boardSpan:DWORD,handX:DWORD,handY:DWORD,pose:DWORD
|
||||
cmp dtError3D,0
|
||||
jne dt_render_error
|
||||
call DartsCamera3D
|
||||
call UpdateDartsWall3D
|
||||
call DrawDartsRoom3D
|
||||
call DrawDartsProps3D
|
||||
call DrawDartsTrail3D
|
||||
xor ebx,ebx
|
||||
dt_render_marks:
|
||||
cmp ebx,dtCount3D
|
||||
jae dt_render_flight
|
||||
invoke DrawDartsModel3D,dtMarkX3D[ebx*4],dtMarkY3D[ebx*4],DT_WALL-4
|
||||
inc ebx
|
||||
jmp dt_render_marks
|
||||
dt_render_flight:
|
||||
cmp dtState3D,DT_FLIGHT
|
||||
jne dt_render_hand
|
||||
invoke DartsProject3D,dtShotX3D,dtShotY3D,dtShotZ3D
|
||||
cmp ecx,140
|
||||
jl dt_render_hand
|
||||
invoke DrawDartsModel3D,dtShotX3D,dtShotY3D,dtShotZ3D
|
||||
dt_render_hand:
|
||||
mov pose,0
|
||||
cmp dtState3D,DT_AIM
|
||||
je dt_render_aim_hand
|
||||
cmp dtState3D,DT_RELEASE
|
||||
je dt_render_release_hand
|
||||
cmp dtState3D,DT_FLIGHT
|
||||
jne dt_render_hud
|
||||
cmp dtTimer3D,250
|
||||
jae dt_render_hud
|
||||
mov eax,dtTimer3D
|
||||
xor edx,edx
|
||||
mov ecx,50
|
||||
div ecx
|
||||
add eax,12
|
||||
cmp eax,15
|
||||
jbe dt_render_pose
|
||||
mov eax,15
|
||||
jmp dt_render_pose
|
||||
dt_render_release_hand:
|
||||
mov eax,dtTimer3D
|
||||
imul eax,3
|
||||
xor edx,edx
|
||||
mov ecx,200
|
||||
div ecx
|
||||
add eax,8
|
||||
cmp eax,10
|
||||
jne dt_render_pose
|
||||
mov eax,11
|
||||
jmp dt_render_pose
|
||||
dt_render_aim_hand:
|
||||
mov eax,0
|
||||
cmp dtHolding3D,0
|
||||
je dt_render_pose
|
||||
mov eax,dtHoldTime3D
|
||||
imul eax,4
|
||||
xor edx,edx
|
||||
mov ecx,280
|
||||
div ecx
|
||||
cmp eax,3
|
||||
jbe @F
|
||||
mov eax,3
|
||||
@@:
|
||||
add eax,4
|
||||
dt_render_pose:
|
||||
mov pose,eax
|
||||
invoke DartsProject3D,dtAimX3D,dtAimY3D,DT_WALL
|
||||
mov bx0,eax
|
||||
mov by0,edx
|
||||
add eax,16
|
||||
mov handX,eax
|
||||
mov eax,edx
|
||||
sub eax,110
|
||||
mov handY,eax
|
||||
invoke DrawDartsArm3D,pose,bx0,by0
|
||||
cmp dtState3D,DT_AIM
|
||||
jne dt_render_hud
|
||||
mov eax,bx0
|
||||
sub eax,7
|
||||
mov edx,by0
|
||||
sub edx,2
|
||||
invoke DrawBuckshotPanel3D,eax,edx,15,5,00101112h
|
||||
mov eax,by0
|
||||
sub eax,7
|
||||
mov edx,bx0
|
||||
sub edx,2
|
||||
invoke DrawBuckshotPanel3D,edx,eax,5,15,00101112h
|
||||
mov eax,bx0
|
||||
sub eax,6
|
||||
invoke DrawBuckshotPanel3D,eax,by0,13,2,00F6E6ADh
|
||||
mov eax,by0
|
||||
sub eax,6
|
||||
invoke DrawBuckshotPanel3D,bx0,eax,2,13,00F6E6ADh
|
||||
dt_render_hud:
|
||||
invoke DrawBox,0,0,320,16,00151D20h
|
||||
invoke DrawString8x8,8,4,OFFSET dtTitle,00E8D7ADh
|
||||
invoke DrawString8x8,112,4,OFFSET dtYou,0090CDB9h
|
||||
invoke FormatNumber,dtScores3D,OFFSET hudNumBuffer
|
||||
invoke DrawString8x8,140,4,OFFSET hudNumBuffer,00FFFFFFh
|
||||
invoke DrawString8x8,204,4,OFFSET dtBot,00E8D7ADh
|
||||
invoke FormatNumber,dtScores3D[4],OFFSET hudNumBuffer
|
||||
invoke DrawString8x8,248,4,OFFSET hudNumBuffer,00FFFFFFh
|
||||
invoke DrawBox,0,200,320,40,00151D20h
|
||||
mov ebx,OFFSET dtYourTurn
|
||||
cmp dtActor3D,0
|
||||
je @F
|
||||
mov ebx,OFFSET dtBotTurn
|
||||
@@:
|
||||
cmp dtState3D,DT_WIN
|
||||
jne dt_hud_actor
|
||||
mov ebx,OFFSET dtYouWin
|
||||
cmp dtWinner3D,0
|
||||
je dt_hud_actor
|
||||
mov ebx,OFFSET dtBotWin
|
||||
dt_hud_actor:
|
||||
invoke DrawString8x8,8,206,ebx,00E8D7ADh
|
||||
xor ebx,ebx
|
||||
dt_render_scores:
|
||||
imul esi,ebx,52
|
||||
add esi,8
|
||||
invoke DrawBoxBorder,esi,220,46,16,00293436h,00728689h,1
|
||||
cmp ebx,dtCount3D
|
||||
jae @F
|
||||
invoke FormatNumber,dtMarkScore3D[ebx*4],OFFSET hudNumBuffer
|
||||
add esi,7
|
||||
invoke DrawString8x8,esi,224,OFFSET hudNumBuffer,00FFFFFFh
|
||||
@@:
|
||||
inc ebx
|
||||
cmp ebx,3
|
||||
jb dt_render_scores
|
||||
invoke DrawBoxBorder,230,220,82,16,00293436h,00728689h,1
|
||||
invoke DrawString8x8,248,224,OFFSET dtAgain,00FFFFFFh
|
||||
cmp dtState3D,DT_AIM
|
||||
jne @F
|
||||
invoke DartsScore3D,dtAimX3D,dtAimY3D
|
||||
mov esi,OFFSET dtSingle
|
||||
cmp edx,3
|
||||
jne dt_preview_double
|
||||
mov esi,OFFSET dtTriple
|
||||
jmp dt_preview_sector
|
||||
dt_preview_double:
|
||||
cmp edx,2
|
||||
jne dt_preview_sector
|
||||
mov esi,OFFSET dtDouble
|
||||
dt_preview_sector:
|
||||
test edx,edx
|
||||
jz dt_preview_value
|
||||
mov ecx,edx
|
||||
xor edx,edx
|
||||
div ecx
|
||||
dt_preview_value:
|
||||
mov edi,eax
|
||||
invoke DrawString8x8,176,206,OFFSET dtTarget,00AABDB6h
|
||||
invoke DrawString8x8,224,206,esi,00F6E6ADh
|
||||
invoke FormatNumber,edi,OFFSET hudNumBuffer
|
||||
invoke DrawString8x8,236,206,OFFSET hudNumBuffer,00FFFFFFh
|
||||
jmp dt_render_done
|
||||
@@:
|
||||
cmp dtState3D,DT_RELEASE
|
||||
je dt_render_done
|
||||
cmp dtState3D,DT_FLIGHT
|
||||
je dt_render_done
|
||||
cmp dtState3D,DT_RETURN
|
||||
je dt_render_done
|
||||
mov ebx,OFFSET dtWin
|
||||
cmp dtRemaining3D,0
|
||||
je dt_render_done
|
||||
mov ebx,OFFSET dtBust
|
||||
cmp dtBust3D,0
|
||||
jne dt_render_banner
|
||||
mov ebx,OFFSET dtMiss
|
||||
cmp dtLast3D,0
|
||||
je dt_render_banner
|
||||
mov ebx,OFFSET dtThrow
|
||||
dt_render_banner:
|
||||
invoke DrawString8x8,192,206,ebx,00E8D7ADh
|
||||
dt_render_done:
|
||||
cmp dtScoreOpen3D,0
|
||||
je @F
|
||||
call DrawDartsScoreboard3D
|
||||
@@:
|
||||
ret
|
||||
dt_render_error:
|
||||
invoke DrawBox,0,0,320,240,00151D20h
|
||||
invoke DrawString8x8,12,104,OFFSET dtLoading,00E8D7ADh
|
||||
ret
|
||||
RenderDarts3D ENDP
|
||||
|
|
@ -85,6 +85,11 @@ dk_render_ai_card:
|
|||
shr edx, 1
|
||||
add eax, edx
|
||||
mov posX, eax
|
||||
mov hidden,1
|
||||
cmp obsActive3D,0
|
||||
je @F
|
||||
mov hidden,0
|
||||
@@:
|
||||
cmp dkState3D, DK_DEAL
|
||||
jne @F
|
||||
mov eax, ebx
|
||||
|
|
@ -94,10 +99,10 @@ dk_render_ai_card:
|
|||
mov edx, dkTimer3D
|
||||
sub edx, eax
|
||||
js dk_render_next_ai
|
||||
invoke DrawSlidingCard3D, posX, 48, durakAIHand[ebx*4], 1, edx
|
||||
invoke DrawSlidingCard3D, posX, 48, durakAIHand[ebx*4], hidden, edx
|
||||
jmp dk_render_next_ai
|
||||
@@:
|
||||
invoke DrawNativeCard3D, posX, 48, durakAIHand[ebx*4], 1, 34
|
||||
invoke DrawNativeCard3D, posX, 48, durakAIHand[ebx*4], hidden, 34
|
||||
dk_render_next_ai:
|
||||
inc ebx
|
||||
jmp dk_render_ai_card
|
||||
|
|
|
|||
|
|
@ -300,10 +300,13 @@ eight_finish_done:
|
|||
EightFinish3D ENDP
|
||||
|
||||
EightHand3D PROC uses ebx esi
|
||||
cmp obsActive3D,0
|
||||
jne eight_hand_bot
|
||||
cmp poolPlayer3D,1
|
||||
jne eight_hand_human
|
||||
cmp poolAI3D,0
|
||||
je eight_hand_human
|
||||
eight_hand_bot:
|
||||
mov esi,55*FP_1
|
||||
eight_hand_row:
|
||||
mov ebx,40*FP_1
|
||||
|
|
|
|||
|
|
@ -1,4 +1,7 @@
|
|||
MODE_FORTUNE equ 25
|
||||
FORTUNE_SUBMODE_MENU equ 0
|
||||
FORTUNE_SUBMODE_TAROT equ 1
|
||||
FORTUNE_SUBMODE_ORB equ 2
|
||||
BeginFortuneDialog3D PROTO
|
||||
HandleFortuneDialog3D PROTO
|
||||
RenderFortuneDialog3D PROTO
|
||||
|
|
@ -7,61 +10,116 @@ StopFortuneWorker3D PROTO
|
|||
PollFortuneWorker3D PROTO
|
||||
SubmitFortuneQuestion3D PROTO
|
||||
FortuneWrap3D PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD
|
||||
InitFortuneMindState3D PROTO
|
||||
GenerateTarotReadingMind3D PROTO
|
||||
GenerateStudentGradeMind3D PROTO
|
||||
DetectTopic3D PROTO :DWORD
|
||||
FortuneStrLen PROTO :DWORD
|
||||
FortuneStrCat PROTO :DWORD,:DWORD
|
||||
SubmitStudentGrade3D PROTO
|
||||
DrawFortuneCardBox3D PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
|
||||
CreatePipe PROTO :DWORD,:DWORD,:DWORD,:DWORD
|
||||
SetHandleInformation PROTO :DWORD,:DWORD,:DWORD
|
||||
CreateProcessA PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
|
||||
PeekNamedPipe PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD
|
||||
TerminateProcess PROTO :DWORD,:DWORD
|
||||
GetExitCodeProcess PROTO :DWORD,:DWORD
|
||||
include src_graphics\fortune_mind_data.inc
|
||||
UpdateFortuneSession3D PROTO
|
||||
SetFortuneSpeech3D PROTO :DWORD
|
||||
FortuneCardSpeech3D PROTO :DWORD
|
||||
DrawFortuneSpeech3D PROTO
|
||||
DrawFortuneSessionCard3D PROTO :DWORD
|
||||
.data
|
||||
fortuneWorkerPath3D db "ai\fortune-engine.exe",0
|
||||
fortuneWorkerCommand3D db "ai\fortune-engine.exe",0
|
||||
fortuneNullName3D db "NUL",0
|
||||
fortunePipeSecurity3D dd 12,0,1
|
||||
fortuneStartup3D dd 17 dup(0)
|
||||
fortuneSessionPacks3D LABEL BYTE
|
||||
fortuneSessionRoom3D AnimPack3D <>
|
||||
fortuneSessionShuffle3D AnimPack3D <>
|
||||
fortuneSessionDeal3D AnimPack3D <>
|
||||
fortuneSessionGesture3D AnimPack3D <>
|
||||
fortuneSessionOrbHands3D AnimPack3D <>
|
||||
fortuneSessionMist3D AnimPack3D <>
|
||||
fortuneSessionCards3D AnimPack3D <>
|
||||
fortuneSessionPath0 db "assets\animations\fortune\room-v1.bin",0
|
||||
fortuneSessionPath1 db "assets\animations\fortune\shuffle-v1.bin",0
|
||||
fortuneSessionPath2 db "assets\animations\fortune\deal-v1.bin",0
|
||||
fortuneSessionPath3 db "assets\animations\fortune\gesture-v1.bin",0
|
||||
fortuneSessionPath4 db "assets\animations\fortune\orb-hands-v1.bin",0
|
||||
fortuneSessionPath5 db "assets\animations\fortune\orb-mist-v1.bin",0
|
||||
fortuneSessionPath6 db "assets\animations\fortune\cards-v1.bin",0
|
||||
fortuneSessionPaths3D dd OFFSET fortuneSessionPath0,OFFSET fortuneSessionPath1,OFFSET fortuneSessionPath2,OFFSET fortuneSessionPath3
|
||||
dd OFFSET fortuneSessionPath4,OFFSET fortuneSessionPath5,OFFSET fortuneSessionPath6
|
||||
fortuneSubMode3D dd 0
|
||||
fortunePhase3D dd 0
|
||||
fortunePhaseTime3D dd 0
|
||||
fortuneSessionClock3D dd 0
|
||||
fortuneSpeechTime3D dd 0
|
||||
fortuneSpeechLength3D dd 0
|
||||
fortuneSpeechVisible3D dd 0
|
||||
fortuneSpeechWidth3D dd 0
|
||||
fortuneSpeechHeight3D dd 0
|
||||
fortuneQuestionLength3D dd 0
|
||||
fortuneQuestion3D db 241 dup(0)
|
||||
fortuneDrawCards3D dd 3 dup(0)
|
||||
fortuneCardsDrawn3D dd 0
|
||||
fortuneDealt3D dd 0
|
||||
fortuneRevealed3D dd 0
|
||||
fortuneWorkerState3D dd 0
|
||||
fortuneProcess3D dd 4 dup(0)
|
||||
fortuneRead3D dd 0
|
||||
fortuneWrite3D dd 0
|
||||
fortuneChildRead3D dd 0
|
||||
fortuneChildWrite3D dd 0
|
||||
fortuneNull3D dd 0
|
||||
fortuneWorkerState3D dd 0 ; 0=off, 1=loading, 2=ready, 3=generating, 4=error
|
||||
fortuneWorkerTick3D dd 0
|
||||
fortuneReceiveLength3D dd -1
|
||||
fortuneReceiveHeader3D dd 3 dup(0)
|
||||
fortuneRequest3D dd 31514346h,0,0,0,0
|
||||
fortuneIo3D dd 0
|
||||
fortuneAvailable3D dd 0
|
||||
fortuneQuestionLength3D dd 0
|
||||
fortuneQuestion3D db 241 dup(0)
|
||||
fortuneAnswer3D db 8193 dup(0)
|
||||
fortuneBackground3D dd SCREEN_SIZE dup(0)
|
||||
fortuneSpeech3D db 256 dup(0)
|
||||
fortuneLine3D db 73 dup(0)
|
||||
fortuneGlyph3D db 0,0
|
||||
fortuneCaret3D db "_",0
|
||||
fortuneCaretX3D dd 24
|
||||
fortuneCaretY3D dd 402
|
||||
fortunePage3D dd 0
|
||||
fortuneMore3D dd 0
|
||||
fortuneShort3D dd 0
|
||||
fortuneLine3D db 73 dup(0)
|
||||
fortuneCaretX3D dd 24
|
||||
fortuneCaretY3D dd 366
|
||||
fortuneCaret3D db "_",0
|
||||
fortuneDurations3D dd 0,3200,1000,1000,1000,800,3600,800,3600,800,3600,3600,0
|
||||
fortuneRoleTexts3D dd OFFSET fortuneFirst3D,OFFSET fortuneSecond3D,OFFSET fortuneThird3D
|
||||
fortuneTitle3D db 195,192,196,192,203,202,192,0
|
||||
fortuneLoading3D db 207,240,238,225,243,230,228,224,254,32,234,224,240,242,251,46,46,46,0
|
||||
fortuneThinking3D db 202,224,240,242,251,32,238,242,226,229,247,224,254,242,46,46,46,0
|
||||
fortuneReady3D db 202,224,234,238,233,32,226,238,239,240,238,241,32,242,229,225,255,32,239,240,232,226,184,235,63,0
|
||||
fortuneError3D db 205,229,32,243,228,224,235,238,241,252,32,231,224,239,243,241,242,232,242,252,32,237,229,233,240,238,241,229,242,252,46,32
|
||||
db 207,240,238,226,229,240,252,32,234,238,236,239,235,229,234,242,32,232,227,240,251,46,0
|
||||
fortuneAsk3D db 209,207,208,206,209,200,210,220,0
|
||||
fortuneExit3D db 194,219,201,210,200,0
|
||||
fortuneDisclaimer3D db 208,224,241,234,235,224,228,32,228,235,255,32,240,224,231,236,251,248,235,229,237,232,255,44,32
|
||||
db 237,229,32,228,238,241,242,238,226,229,240,237,238,229,32,239,240,229,228,241,234,224,231,224,237,232,229,46,0
|
||||
fortunePartial3D db 206,242,226,229,242,32,241,238,234,240,224,249,184,237,0
|
||||
fortuneLeft3D db "<",0
|
||||
fortuneRight3D db ">",0
|
||||
fortuneAsk3D db 209,207,208,206,209,200,210,220,0
|
||||
fortuneAgain3D db 197,217,168,32,208,192,199,0
|
||||
fortuneMenu3D db 194,219,193,206,208,0
|
||||
fortuneTarot3D db 195,192,196,192,205,200,197,0
|
||||
fortuneFate3D db 208,197,216,197,205,200,197,32,209,211,196,220,193,219,0
|
||||
fortuneWelcome3D db 215,242,238,32,242,229,225,255,32,239,240,232,226,229,235,238,63,0
|
||||
fortuneReady3D db 202,224,234,238,233,32,226,238,239,240,238,241,10,242,229,225,255,32,239,240,232,226,184,235
|
||||
db 63,0
|
||||
fortuneShuffle3D db 210,192,209,206,194,192,205,200,197,0
|
||||
fortuneDeal3D db 208,192,209,202,203,192,196,0
|
||||
fortuneReveal3D db 202,192,208,210,219,32,206,210,202,208,219,194,192,222,210,209,223,0
|
||||
fortuneSummary3D db 194,241,229,32,242,240,232,32,234,224,240,242,251,32,238,242,234,240,251,242,251,46,10,206
|
||||
db 225,249,232,233,32,238,242,226,229,242,32,237,224,32,226,238,239,240,238,241,10,239,238,234
|
||||
db 224,32,237,229,32,240,229,224,235,232,231,238,226,224,237,46,0
|
||||
fortuneUnavailable3D db 207,238,234,224,32,237,229,32,236,238,227,243,32,238,225,250,255,241,237,232,242,252,10,231
|
||||
db 237,224,247,229,237,232,229,32,253,242,238,233,32,234,224,240,242,251,46,0
|
||||
fortuneOrbText3D db 216,224,240,32,236,238,235,247,232,242,46,10,208,229,248,229,237,232,229,32,239,238,234,224
|
||||
db 32,237,229,32,240,229,224,235,232,231,238,226,224,237,238,46,0
|
||||
fortuneOrbStatus3D db 213,208,211,209,210,192,203,220,205,219,201,32,216,192,208,0
|
||||
fortuneMissing3D db 205,229,32,243,228,224,235,238,241,252,32,231,224,227,240,243,231,232,242,252,32,232,231,238,225,240,224,230,229,237,232,255,46,0
|
||||
fortuneContinue3D db 196,192,203,220,216,197,0
|
||||
fortuneQuestionLabel3D db 210,194,206,201,32,194,206,207,208,206,209,0
|
||||
fortuneFirst3D db 207,197,208,194,192,223,32,202,192,208,210,192,0
|
||||
fortuneSecond3D db 194,210,206,208,192,223,32,202,192,208,210,192,0
|
||||
fortuneThird3D db 210,208,197,210,220,223,32,202,192,208,210,192,0
|
||||
fortuneUpright3D db 207,240,255,236,238,229,32,239,238,235,238,230,229,237,232,229,0
|
||||
fortuneReversed3D db 207,229,240,229,226,184,240,237,243,242,224,255,32,234,224,240,242,224,0
|
||||
fortuneCardName0 db 216,243,242,0
|
||||
fortuneCardName1 db 204,224,227,0
|
||||
fortuneCardName2 db 194,229,240,245,238,226,237,224,255,32,230,240,232,246,224,0
|
||||
fortuneCardName3 db 200,236,239,229,240,224,242,240,232,246,224,0
|
||||
fortuneCardName4 db 200,236,239,229,240,224,242,238,240,0
|
||||
fortuneCardName5 db 200,229,240,238,244,224,237,242,0
|
||||
fortuneCardName6 db 194,235,254,225,235,184,237,237,251,229,0
|
||||
fortuneCardName7 db 202,238,235,229,241,237,232,246,224,0
|
||||
fortuneCardName8 db 209,232,235,224,0
|
||||
fortuneCardName9 db 206,242,248,229,235,252,237,232,234,0
|
||||
fortuneCardName10 db 202,238,235,229,241,238,32,244,238,240,242,243,237,251,0
|
||||
fortuneCardName11 db 209,239,240,224,226,229,228,235,232,226,238,241,242,252,0
|
||||
fortuneCardName12 db 207,238,226,229,248,229,237,237,251,233,0
|
||||
fortuneCardName13 db 209,236,229,240,242,252,0
|
||||
fortuneCardName14 db 211,236,229,240,229,237,237,238,241,242,252,0
|
||||
fortuneCardName15 db 196,252,255,226,238,235,0
|
||||
fortuneCardName16 db 193,224,248,237,255,0
|
||||
fortuneCardName17 db 199,226,229,231,228,224,0
|
||||
fortuneCardName18 db 203,243,237,224,0
|
||||
fortuneCardName19 db 209,238,235,237,246,229,0
|
||||
fortuneCardName20 db 209,243,228,0
|
||||
fortuneCardName21 db 204,232,240,0
|
||||
fortuneCardNames3D dd OFFSET fortuneCardName0,OFFSET fortuneCardName1,OFFSET fortuneCardName2,OFFSET fortuneCardName3
|
||||
dd OFFSET fortuneCardName4,OFFSET fortuneCardName5,OFFSET fortuneCardName6,OFFSET fortuneCardName7
|
||||
dd OFFSET fortuneCardName8,OFFSET fortuneCardName9,OFFSET fortuneCardName10,OFFSET fortuneCardName11
|
||||
dd OFFSET fortuneCardName12,OFFSET fortuneCardName13,OFFSET fortuneCardName14,OFFSET fortuneCardName15
|
||||
dd OFFSET fortuneCardName16,OFFSET fortuneCardName17,OFFSET fortuneCardName18,OFFSET fortuneCardName19
|
||||
dd OFFSET fortuneCardName20,OFFSET fortuneCardName21
|
||||
.data?
|
||||
fortuneBackground3D dd SCREEN_SIZE dup(?)
|
||||
.data
|
||||
|
|
|
|||
|
|
@ -781,6 +781,7 @@ CheckHallInteractions3D PROC
|
|||
; A point on the serving edge remains in front even when standing close.
|
||||
invoke HallTarget3D, playerX,23*FP_1+FP_HALF,INTERACT_BAR,OFFSET hallBarPrompt3D
|
||||
hall_check_other:
|
||||
invoke HallTarget3D,FP_1*305/10,28*FP_1,INTERACT_DARTS,OFFSET dtPrompt
|
||||
invoke HallTarget3D,16*FP_1,FP_1*73/10,INTERACT_CABARET_SEAT,OFFSET cabaretSeat3D
|
||||
invoke HallTarget3D,scrStation3D.posX,scrStation3D.posY,INTERACT_SCRATCH,OFFSET scratchHall3D
|
||||
invoke HallTarget3D,FP_1*285/10,FP_1*105/10,INTERACT_BASEMENT,OFFSET hallBunkerPrompt3D
|
||||
|
|
@ -797,6 +798,12 @@ hall_check_bunker:
|
|||
CheckHallInteractions3D ENDP
|
||||
|
||||
TriggerHallInteraction3D PROC
|
||||
cmp interactTargetId,INTERACT_DARTS
|
||||
jne hall_not_darts
|
||||
call BeginDarts3D
|
||||
mov eax,1
|
||||
ret
|
||||
hall_not_darts:
|
||||
call TryFortuneInteraction3D
|
||||
test eax,eax
|
||||
jnz hall_trigger_handled
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
DrawBJPartySelector3D PROC uses ebx esi edi
|
||||
cmp obsActive3D,0
|
||||
je @F
|
||||
ret
|
||||
@@:
|
||||
xor edi,edi
|
||||
cmp cbState3D,CB_IDLE
|
||||
je @F
|
||||
|
|
|
|||
|
|
@ -573,33 +573,39 @@ dk_find_done:
|
|||
DurakFindMove3D ENDP
|
||||
|
||||
DurakAITurn3D PROC
|
||||
cmp dkAttacker3D, 1
|
||||
je dk_ai_attack
|
||||
invoke DurakFindMove3D, 1, DK_TRANSFER
|
||||
cmp eax, 0FFFFFFFFh
|
||||
je @F
|
||||
invoke DurakPlay3D, 1, eax, DK_TRANSFER
|
||||
ret
|
||||
@@:
|
||||
invoke DurakFindMove3D, 1, DK_DEFEND
|
||||
cmp eax, 0FFFFFFFFh
|
||||
je dk_ai_take
|
||||
invoke DurakPlay3D, 1, eax, DK_DEFEND
|
||||
ret
|
||||
dk_ai_take:
|
||||
invoke DurakTake3D, 1
|
||||
ret
|
||||
dk_ai_attack:
|
||||
invoke DurakFindMove3D, 1, DK_ATTACK
|
||||
cmp eax, 0FFFFFFFFh
|
||||
je dk_ai_finish
|
||||
invoke DurakPlay3D, 1, eax, DK_ATTACK
|
||||
ret
|
||||
dk_ai_finish:
|
||||
invoke DurakFinish3D, 1
|
||||
invoke DurakBotTurn3D,1
|
||||
ret
|
||||
DurakAITurn3D ENDP
|
||||
|
||||
DurakBotTurn3D PROC actor:DWORD
|
||||
mov eax,actor
|
||||
cmp dkAttacker3D,eax
|
||||
je dk_ai_attack
|
||||
invoke DurakFindMove3D, actor, DK_TRANSFER
|
||||
cmp eax, 0FFFFFFFFh
|
||||
je @F
|
||||
invoke DurakPlay3D, actor, eax, DK_TRANSFER
|
||||
ret
|
||||
@@:
|
||||
invoke DurakFindMove3D, actor, DK_DEFEND
|
||||
cmp eax, 0FFFFFFFFh
|
||||
je dk_ai_take
|
||||
invoke DurakPlay3D, actor, eax, DK_DEFEND
|
||||
ret
|
||||
dk_ai_take:
|
||||
invoke DurakTake3D, actor
|
||||
ret
|
||||
dk_ai_attack:
|
||||
invoke DurakFindMove3D, actor, DK_ATTACK
|
||||
cmp eax, 0FFFFFFFFh
|
||||
je dk_ai_finish
|
||||
invoke DurakPlay3D, actor, eax, DK_ATTACK
|
||||
ret
|
||||
dk_ai_finish:
|
||||
invoke DurakFinish3D, actor
|
||||
ret
|
||||
DurakBotTurn3D ENDP
|
||||
|
||||
DurakMouse3D PROC uses ebx
|
||||
cmp mouseClick3D, 0
|
||||
je dk_mouse_done
|
||||
|
|
|
|||
|
|
@ -382,19 +382,27 @@ poker_showdown_done:
|
|||
PokerShowdown3D ENDP
|
||||
|
||||
; The AI sees only its two cards and the already exposed board. No player cards/future deck.
|
||||
PokerAIDecide3D PROC uses ebx esi edi
|
||||
PokerAIDecide3D PROC
|
||||
invoke PokerBotAct3D,1
|
||||
ret
|
||||
PokerAIDecide3D ENDP
|
||||
|
||||
PokerBotAct3D PROC uses ebx esi edi actor:DWORD
|
||||
local strength:DWORD,owed:DWORD
|
||||
mov eax, pkCurrent3D
|
||||
sub eax, pkPaid3D[4]
|
||||
mov ecx,actor
|
||||
sub eax, pkPaid3D[ecx*4]
|
||||
mov owed, eax
|
||||
cmp pkBoardCount3D, 3
|
||||
jae poker_ai_postflop
|
||||
mov eax, pkHole3D[8]
|
||||
mov ebx,actor
|
||||
shl ebx,3
|
||||
mov eax, pkHole3D[ebx]
|
||||
xor edx, edx
|
||||
mov ecx, 13
|
||||
div ecx
|
||||
mov esi, edx
|
||||
mov eax, pkHole3D[12]
|
||||
mov eax, pkHole3D[ebx+4]
|
||||
xor edx, edx
|
||||
div ecx
|
||||
mov strength, 0
|
||||
|
|
@ -416,7 +424,8 @@ poker_ai_pair:
|
|||
jmp poker_ai_odds
|
||||
poker_ai_postflop:
|
||||
lea edi, pkEvalHand3D
|
||||
lea esi, pkHole3D[8]
|
||||
mov eax,actor
|
||||
lea esi, pkHole3D[eax*8]
|
||||
movsd
|
||||
movsd
|
||||
lea esi, pkBoard3D
|
||||
|
|
@ -452,7 +461,12 @@ poker_ai_consider_raise:
|
|||
je poker_ai_call
|
||||
cmp pkRaisedCount3D, 2
|
||||
jae poker_ai_call
|
||||
cmp bankroll, 0
|
||||
mov eax,bankroll
|
||||
cmp actor,1
|
||||
je @F
|
||||
mov eax,pkStack3D
|
||||
@@:
|
||||
cmp eax, 0
|
||||
je poker_ai_call
|
||||
cmp strength, 3
|
||||
jae poker_ai_strong_raise
|
||||
|
|
@ -464,11 +478,11 @@ poker_ai_strong_raise:
|
|||
jae poker_ai_call
|
||||
poker_ai_raise:
|
||||
mov pkAiMessage3D, 3
|
||||
invoke PokerRaise3D, 1, pkMinRaise3D
|
||||
invoke PokerRaise3D, actor, pkMinRaise3D
|
||||
ret
|
||||
poker_ai_fold:
|
||||
mov pkAiMessage3D, 4
|
||||
invoke PokerFold3D, 1
|
||||
invoke PokerFold3D, actor
|
||||
ret
|
||||
poker_ai_call:
|
||||
mov pkAiMessage3D, 1
|
||||
|
|
@ -476,9 +490,9 @@ poker_ai_call:
|
|||
je @F
|
||||
mov pkAiMessage3D, 2
|
||||
@@:
|
||||
invoke PokerCall3D, 1
|
||||
invoke PokerCall3D, actor
|
||||
ret
|
||||
PokerAIDecide3D ENDP
|
||||
PokerBotAct3D ENDP
|
||||
|
||||
HandleNativePoker3D PROC uses ebx
|
||||
call HandleCardExit3D
|
||||
|
|
|
|||
|
|
@ -190,6 +190,8 @@ npc_accept_done:
|
|||
NpcAcceptContract3D ENDP
|
||||
|
||||
NpcRecordRound3D PROC uses ebx esi edi game:DWORD,won:DWORD
|
||||
cmp obsActive3D,0
|
||||
jne npc_event_done
|
||||
call NpcEnsureJournal3D
|
||||
test eax,eax
|
||||
jz npc_event_done
|
||||
|
|
|
|||
494
src_graphics/observer.inc
Normal file
|
|
@ -0,0 +1,494 @@
|
|||
; Spectator control supplies decisions to the real native games, never simulated results.
|
||||
.code
|
||||
BeginObserver3D PROC uses esi edi
|
||||
lea esi,screenBuffer
|
||||
lea edi,obsBackground3D
|
||||
mov ecx,SCREEN_SIZE
|
||||
rep movsd
|
||||
mov engineMode,MODE_OBSERVER
|
||||
mov obsError3D,0
|
||||
mov paused3D,0
|
||||
call ClearKeys3D
|
||||
mov mouseClick3D,0
|
||||
ret
|
||||
BeginObserver3D ENDP
|
||||
|
||||
StartObserver3D PROC uses ebx esi
|
||||
cmp obsActive3D,0
|
||||
jne obs_start_done
|
||||
cmp obsSelection3D,OBS_GAME_COUNT
|
||||
jae obs_start_done
|
||||
xor esi,esi
|
||||
obs_save_profile:
|
||||
mov ebx,obsProfilePointers3D[esi*4]
|
||||
mov eax,[ebx]
|
||||
mov obsSavedProfile3D[esi*4],eax
|
||||
inc esi
|
||||
cmp esi,LENGTHOF obsProfilePointers3D
|
||||
jb obs_save_profile
|
||||
mov obsActive3D,1
|
||||
mov obsPaused3D,0
|
||||
mov obsRound3D,0
|
||||
mov obsWins3D[0],0
|
||||
mov obsWins3D[4],0
|
||||
mov obsWins3D[8],0
|
||||
mov tournamentMode,0
|
||||
mov cardExitActive3D,0
|
||||
mov obsError3D,0
|
||||
mov eax,obsSelection3D
|
||||
mov eax,obsModes3D[eax*4]
|
||||
mov engineMode,eax
|
||||
cmp eax,MODE_POOL
|
||||
je obs_init_pool
|
||||
cmp eax,MODE_DURAK
|
||||
je obs_init_durak
|
||||
cmp eax,MODE_BLACKJACK
|
||||
je obs_init_blackjack
|
||||
call InitNativePoker3D
|
||||
jmp obs_check_assets
|
||||
obs_init_durak:
|
||||
call InitNativeDurak3D
|
||||
mov eax,obsSelection3D
|
||||
sub eax,2
|
||||
mov dkVariant3D,eax
|
||||
jmp obs_check_assets
|
||||
obs_init_blackjack:
|
||||
call InitNativeBlackjack3D
|
||||
mov cbBotCount3D,0
|
||||
obs_check_assets:
|
||||
cmp cardTablePixels3D,0
|
||||
jne obs_start_round
|
||||
call StopObserver3D
|
||||
mov obsError3D,1
|
||||
ret
|
||||
obs_init_pool:
|
||||
mov poolAI3D,1
|
||||
mov eax,obsSelection3D
|
||||
sub eax,4
|
||||
invoke StartBilliards3D,eax
|
||||
obs_start_round:
|
||||
call ObserverNewRound3D
|
||||
obs_start_done:
|
||||
ret
|
||||
StartObserver3D ENDP
|
||||
|
||||
StopObserver3D PROC uses esi ebx
|
||||
cmp obsActive3D,0
|
||||
je obs_stop_menu
|
||||
; The save barrier remains active until every personal value has been restored.
|
||||
xor esi,esi
|
||||
obs_restore_profile:
|
||||
mov ebx,obsProfilePointers3D[esi*4]
|
||||
mov eax,obsSavedProfile3D[esi*4]
|
||||
mov [ebx],eax
|
||||
inc esi
|
||||
cmp esi,LENGTHOF obsProfilePointers3D
|
||||
jb obs_restore_profile
|
||||
mov obsActive3D,0
|
||||
obs_stop_menu:
|
||||
mov engineMode,MODE_OBSERVER
|
||||
mov paused3D,0
|
||||
mov cardExitActive3D,0
|
||||
call ClearKeys3D
|
||||
mov mouseClick3D,0
|
||||
invoke PlayCasinoSound3D,0,0,0
|
||||
ret
|
||||
StopObserver3D ENDP
|
||||
|
||||
ObserverNewRound3D PROC
|
||||
mov obsTimer3D,0
|
||||
mov obsResultTimer3D,0
|
||||
mov obsOutcome3D,0
|
||||
inc obsRound3D
|
||||
mov bankroll,10000
|
||||
cmp engineMode,MODE_POOL
|
||||
jne @F
|
||||
call ResetPool3D
|
||||
ret
|
||||
@@:
|
||||
cmp engineMode,MODE_DURAK
|
||||
jne @F
|
||||
mov dkBet3D,100
|
||||
mov dkState3D,DK_IDLE
|
||||
call StartNativeDurak3D
|
||||
ret
|
||||
@@:
|
||||
cmp engineMode,MODE_BLACKJACK
|
||||
jne @F
|
||||
mov cbBet3D,100
|
||||
mov cbState3D,CB_IDLE
|
||||
call StartNativeBlackjack3D
|
||||
ret
|
||||
@@:
|
||||
mov pkBlind3D,50
|
||||
mov pkState3D,PK_IDLE
|
||||
call StartNativePoker3D
|
||||
ret
|
||||
ObserverNewRound3D ENDP
|
||||
|
||||
ObserverOutcome3D PROC
|
||||
cmp engineMode,MODE_POOL
|
||||
jne @F
|
||||
cmp poolState3D,POOL_RESULT
|
||||
jne obs_no_outcome
|
||||
mov eax,poolWinner3D
|
||||
inc eax
|
||||
ret
|
||||
@@:
|
||||
cmp engineMode,MODE_DURAK
|
||||
jne @F
|
||||
cmp dkState3D,DK_RESULT
|
||||
jne obs_no_outcome
|
||||
mov eax,dkOutcome3D
|
||||
inc eax
|
||||
ret
|
||||
@@:
|
||||
cmp engineMode,MODE_BLACKJACK
|
||||
jne @F
|
||||
cmp cbState3D,CB_RESULT
|
||||
jne obs_no_outcome
|
||||
mov eax,cbOutcome3D
|
||||
cmp eax,3
|
||||
jne obs_number_outcome
|
||||
mov eax,1
|
||||
ret
|
||||
@@:
|
||||
cmp pkState3D,PK_RESULT
|
||||
jne obs_no_outcome
|
||||
mov eax,pkOutcome3D
|
||||
obs_number_outcome:
|
||||
inc eax
|
||||
ret
|
||||
obs_no_outcome:
|
||||
xor eax,eax
|
||||
ret
|
||||
ObserverOutcome3D ENDP
|
||||
|
||||
ObserverBlackjack3D PROC
|
||||
cmp cbState3D,CB_PLAYER
|
||||
jne obs_bj_update
|
||||
cmp obsTimer3D,700
|
||||
jb obs_bj_update
|
||||
mov obsTimer3D,0
|
||||
; Only our hand and the dealer's exposed up-card enter the decision.
|
||||
cmp cbPlayerScore3D,17
|
||||
jae obs_bj_stand
|
||||
cmp cbPlayerScore3D,12
|
||||
jb obs_bj_hit
|
||||
mov eax,cbDealer3D[0]
|
||||
xor edx,edx
|
||||
mov ecx,13
|
||||
div ecx
|
||||
cmp edx,5
|
||||
jae obs_bj_hit
|
||||
obs_bj_stand:
|
||||
invoke FinishBJPlayer3D,-1
|
||||
jmp obs_bj_update
|
||||
obs_bj_hit:
|
||||
call NativeBJHit3D
|
||||
obs_bj_update:
|
||||
call HandleNativeBlackjack3D
|
||||
ret
|
||||
ObserverBlackjack3D ENDP
|
||||
|
||||
ObserverDurak3D PROC
|
||||
cmp dkState3D,DK_TURN
|
||||
jne obs_durak_update
|
||||
cmp dkActor3D,0
|
||||
jne obs_durak_update
|
||||
cmp obsTimer3D,700
|
||||
jb obs_durak_update
|
||||
mov obsTimer3D,0
|
||||
invoke DurakBotTurn3D,0
|
||||
obs_durak_update:
|
||||
call HandleNativeDurak3D
|
||||
ret
|
||||
ObserverDurak3D ENDP
|
||||
|
||||
ObserverTick3D PROC uses ebx
|
||||
cmp obsActive3D,0
|
||||
je obs_tick_done
|
||||
call ObserverOutcome3D
|
||||
test eax,eax
|
||||
jz obs_tick_game
|
||||
cmp obsOutcome3D,0
|
||||
jne obs_result_wait
|
||||
mov obsOutcome3D,eax
|
||||
inc obsWins3D[eax*4-4]
|
||||
mov obsResultTimer3D,0
|
||||
obs_result_wait:
|
||||
mov eax,deltaTime3D
|
||||
add obsResultTimer3D,eax
|
||||
cmp obsResultTimer3D,3000
|
||||
jb obs_tick_done
|
||||
call ObserverNewRound3D
|
||||
ret
|
||||
obs_tick_game:
|
||||
mov eax,deltaTime3D
|
||||
add obsTimer3D,eax
|
||||
cmp engineMode,MODE_POOL
|
||||
jne @F
|
||||
call HandlePool3D
|
||||
ret
|
||||
@@:
|
||||
cmp engineMode,MODE_DURAK
|
||||
jne @F
|
||||
call ObserverDurak3D
|
||||
ret
|
||||
@@:
|
||||
cmp engineMode,MODE_BLACKJACK
|
||||
jne @F
|
||||
call ObserverBlackjack3D
|
||||
ret
|
||||
@@:
|
||||
cmp pkState3D,PK_PLAYER
|
||||
jne obs_poker_update
|
||||
cmp obsTimer3D,700
|
||||
jb obs_poker_update
|
||||
mov obsTimer3D,0
|
||||
invoke PokerBotAct3D,0
|
||||
obs_poker_update:
|
||||
call HandleNativePoker3D
|
||||
obs_tick_done:
|
||||
ret
|
||||
ObserverTick3D ENDP
|
||||
|
||||
ObserverFrame3D PROC uses ebx esi
|
||||
invoke Pressed3D,1Bh
|
||||
test eax,eax
|
||||
jz obs_frame_input
|
||||
cmp obsActive3D,0
|
||||
je obs_leave_hall
|
||||
call StopObserver3D
|
||||
jmp obs_frame_draw
|
||||
obs_leave_hall:
|
||||
mov engineMode,MODE_WALK
|
||||
call ClearKeys3D
|
||||
mov mouseClick3D,0
|
||||
jmp obs_frame_draw
|
||||
obs_frame_input:
|
||||
cmp obsActive3D,0
|
||||
jne obs_watch_input
|
||||
xor esi,esi
|
||||
obs_menu_digit:
|
||||
lea eax,[esi+31h]
|
||||
invoke Pressed3D,eax
|
||||
test eax,eax
|
||||
jnz obs_menu_pick
|
||||
inc esi
|
||||
cmp esi,OBS_GAME_COUNT
|
||||
jb obs_menu_digit
|
||||
invoke Pressed3D,26h
|
||||
test eax,eax
|
||||
jz @F
|
||||
cmp obsSelection3D,0
|
||||
jne obs_menu_up
|
||||
mov obsSelection3D,OBS_GAME_COUNT
|
||||
obs_menu_up:
|
||||
dec obsSelection3D
|
||||
@@:
|
||||
invoke Pressed3D,28h
|
||||
test eax,eax
|
||||
jz @F
|
||||
inc obsSelection3D
|
||||
cmp obsSelection3D,OBS_GAME_COUNT
|
||||
jb @F
|
||||
mov obsSelection3D,0
|
||||
@@:
|
||||
invoke Pressed3D,0Dh
|
||||
test eax,eax
|
||||
jnz obs_menu_start
|
||||
cmp mouseClick3D,0
|
||||
je obs_frame_draw
|
||||
cmp mouseX3D,16
|
||||
jb obs_frame_draw
|
||||
cmp mouseX3D,304
|
||||
jae obs_frame_draw
|
||||
mov eax,mouseY3D
|
||||
sub eax,52
|
||||
js obs_frame_draw
|
||||
xor edx,edx
|
||||
mov ecx,23
|
||||
div ecx
|
||||
cmp eax,OBS_GAME_COUNT
|
||||
jae obs_menu_close_click
|
||||
cmp edx,20
|
||||
jae obs_frame_draw
|
||||
mov esi,eax
|
||||
obs_menu_pick:
|
||||
mov obsSelection3D,esi
|
||||
obs_menu_start:
|
||||
call StartObserver3D
|
||||
jmp obs_frame_draw
|
||||
obs_menu_close_click:
|
||||
cmp mouseY3D,212
|
||||
jb obs_frame_draw
|
||||
jmp obs_leave_hall
|
||||
obs_watch_input:
|
||||
invoke Pressed3D,20h
|
||||
test eax,eax
|
||||
jnz obs_watch_pause
|
||||
invoke Pressed3D,9
|
||||
test eax,eax
|
||||
jnz obs_watch_speed
|
||||
invoke Pressed3D,4Eh
|
||||
test eax,eax
|
||||
jnz obs_watch_restart
|
||||
cmp mouseClick3D,0
|
||||
je obs_watch_tick
|
||||
cmp mouseY3D,222
|
||||
jb obs_watch_tick
|
||||
cmp mouseY3D,239
|
||||
jae obs_watch_tick
|
||||
mov eax,mouseX3D
|
||||
cmp eax,74
|
||||
jb obs_watch_pause
|
||||
cmp eax,134
|
||||
jb obs_watch_speed
|
||||
cmp eax,226
|
||||
jb obs_watch_restart
|
||||
call StopObserver3D
|
||||
jmp obs_frame_draw
|
||||
obs_watch_pause:
|
||||
xor obsPaused3D,1
|
||||
jmp obs_watch_tick
|
||||
obs_watch_speed:
|
||||
shl obsSpeed3D,1
|
||||
cmp obsSpeed3D,4
|
||||
jbe obs_watch_tick
|
||||
mov obsSpeed3D,1
|
||||
jmp obs_watch_tick
|
||||
obs_watch_restart:
|
||||
call ObserverNewRound3D
|
||||
obs_watch_tick:
|
||||
; Human game commands never reach the autonomous seats.
|
||||
call ClearKeys3D
|
||||
mov mouseClick3D,0
|
||||
cmp obsPaused3D,0
|
||||
jne obs_frame_draw
|
||||
mov ebx,deltaTime3D
|
||||
mov eax,ebx
|
||||
imul eax,obsSpeed3D
|
||||
mov deltaTime3D,eax
|
||||
add animationClock3D,eax
|
||||
call ObserverTick3D
|
||||
mov deltaTime3D,ebx
|
||||
obs_frame_draw:
|
||||
call RenderObserver3D
|
||||
mov mouseClick3D,0
|
||||
ret
|
||||
ObserverFrame3D ENDP
|
||||
|
||||
RenderObserver3D PROC uses ebx esi edi
|
||||
cmp obsActive3D,0
|
||||
jne obs_render_game
|
||||
lea esi,obsBackground3D
|
||||
lea edi,screenBuffer
|
||||
mov ecx,SCREEN_SIZE
|
||||
rep movsd
|
||||
invoke DrawBox,0,0,320,240,00151D20h
|
||||
invoke DrawString8x8,16,18,OFFSET obsTitle,00E8D7ADh
|
||||
xor ebx,ebx
|
||||
obs_render_row:
|
||||
imul esi,ebx,23
|
||||
add esi,52
|
||||
mov edi,00728689h
|
||||
cmp ebx,obsSelection3D
|
||||
jne @F
|
||||
mov edi,00E8D7ADh
|
||||
@@:
|
||||
invoke DrawBoxBorder,16,esi,288,20,00293436h,edi,1
|
||||
add esi,6
|
||||
lea eax,[ebx+1]
|
||||
invoke FormatNumber,eax,OFFSET hudNumBuffer
|
||||
invoke DrawString8x8,24,esi,OFFSET hudNumBuffer,edi
|
||||
invoke DrawString8x8,48,esi,obsNames3D[ebx*4],edi
|
||||
inc ebx
|
||||
cmp ebx,OBS_GAME_COUNT
|
||||
jb obs_render_row
|
||||
invoke DrawBoxBorder,16,212,288,20,00293436h,00728689h,1
|
||||
invoke DrawString8x8,136,218,OFFSET obsClose,00E8D7ADh
|
||||
cmp obsError3D,0
|
||||
je obs_render_done
|
||||
invoke DrawString8x8,16,198,OFFSET obsFailed,00DB8B83h
|
||||
ret
|
||||
obs_render_game:
|
||||
cmp engineMode,MODE_POOL
|
||||
jne @F
|
||||
call RenderPool3D
|
||||
jmp obs_render_hud
|
||||
@@:
|
||||
cmp engineMode,MODE_DURAK
|
||||
jne @F
|
||||
call RenderNativeDurak3D
|
||||
jmp obs_render_hud
|
||||
@@:
|
||||
cmp engineMode,MODE_BLACKJACK
|
||||
jne @F
|
||||
call RenderNativeBlackjack3D
|
||||
jmp obs_render_hud
|
||||
@@:
|
||||
call RenderNativePoker3D
|
||||
obs_render_hud:
|
||||
invoke DrawBox,0,0,320,32,00151D20h
|
||||
mov ebx,obsSelection3D
|
||||
invoke DrawString8x8,8,5,obsNames3D[ebx*4],00E8D7ADh
|
||||
invoke DrawString8x8,8,20,OFFSET obsFirst,0090CDB2h
|
||||
invoke FormatNumber,obsWins3D[0],OFFSET hudNumBuffer
|
||||
invoke DrawString8x8,48,20,OFFSET hudNumBuffer,0090CDB2h
|
||||
invoke DrawString8x8,88,20,OFFSET obsSecond,00DEA0A0h
|
||||
invoke FormatNumber,obsWins3D[4],OFFSET hudNumBuffer
|
||||
invoke DrawString8x8,128,20,OFFSET hudNumBuffer,00DEA0A0h
|
||||
invoke DrawString8x8,190,20,OFFSET obsRound,00E8D7ADh
|
||||
invoke FormatNumber,obsRound3D,OFFSET hudNumBuffer
|
||||
invoke DrawString8x8,254,20,OFFSET hudNumBuffer,00FFFFFFh
|
||||
invoke DrawBox,0,215,320,25,00151D20h
|
||||
cmp obsOutcome3D,0
|
||||
je obs_render_turn
|
||||
mov ebx,OFFSET obsWin1
|
||||
cmp obsOutcome3D,1
|
||||
je @F
|
||||
mov ebx,OFFSET obsWin2
|
||||
cmp obsOutcome3D,2
|
||||
je @F
|
||||
mov ebx,OFFSET obsDraw
|
||||
@@:
|
||||
invoke DrawString8x8,200,5,ebx,00E8D7ADh
|
||||
jmp obs_render_controls
|
||||
obs_render_turn:
|
||||
mov eax,pkTurn3D
|
||||
cmp engineMode,MODE_POKER
|
||||
je obs_render_actor
|
||||
mov eax,dkActor3D
|
||||
cmp engineMode,MODE_DURAK
|
||||
je obs_render_actor
|
||||
mov eax,poolPlayer3D
|
||||
cmp engineMode,MODE_POOL
|
||||
je obs_render_actor
|
||||
xor eax,eax
|
||||
cmp cbState3D,CB_DEALER
|
||||
jne obs_render_actor
|
||||
inc eax
|
||||
obs_render_actor:
|
||||
and eax,1
|
||||
invoke DrawString8x8,232,5,obsTurnNames3D[eax*4],00A7C7C5h
|
||||
obs_render_controls:
|
||||
mov ebx,OFFSET obsPause
|
||||
cmp obsPaused3D,0
|
||||
je @F
|
||||
mov ebx,OFFSET obsPlay
|
||||
@@:
|
||||
invoke DrawBoxBorder,4,222,66,16,00293436h,00728689h,1
|
||||
invoke DrawString8x8,14,226,ebx,00FFFFFFh
|
||||
mov eax,obsSpeed3D
|
||||
shr eax,1
|
||||
mov ebx,obsSpeedNames3D[eax*4]
|
||||
invoke DrawBoxBorder,78,222,48,16,00293436h,00728689h,1
|
||||
invoke DrawString8x8,94,226,ebx,00FFFFFFh
|
||||
invoke DrawBoxBorder,138,222,80,16,00293436h,00728689h,1
|
||||
invoke DrawString8x8,154,226,OFFSET obsRestart,00FFFFFFh
|
||||
invoke DrawBoxBorder,230,222,86,16,00293436h,00728689h,1
|
||||
invoke DrawString8x8,254,226,OFFSET obsGames,00FFFFFFh
|
||||
obs_render_done:
|
||||
ret
|
||||
RenderObserver3D ENDP
|
||||
64
src_graphics/observer_data.inc
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
; Native observer uses the existing games; no alternate rules engine.
|
||||
MODE_OBSERVER equ 26
|
||||
OBS_GAME_COUNT equ 6
|
||||
BeginObserver3D PROTO
|
||||
ObserverFrame3D PROTO
|
||||
StartObserver3D PROTO
|
||||
StopObserver3D PROTO
|
||||
ObserverNewRound3D PROTO
|
||||
ObserverTick3D PROTO
|
||||
ObserverOutcome3D PROTO
|
||||
RenderObserver3D PROTO
|
||||
ObserverBlackjack3D PROTO
|
||||
ObserverDurak3D PROTO
|
||||
PokerBotAct3D PROTO :DWORD
|
||||
DurakBotTurn3D PROTO :DWORD
|
||||
.data
|
||||
obsActive3D dd 0
|
||||
obsSelection3D dd 0
|
||||
obsPaused3D dd 0
|
||||
obsSpeed3D dd 1
|
||||
obsTimer3D dd 0
|
||||
obsResultTimer3D dd 0
|
||||
obsOutcome3D dd 0
|
||||
obsRound3D dd 0
|
||||
obsWins3D dd 0,0,0
|
||||
obsError3D dd 0
|
||||
obsModes3D dd MODE_POKER,MODE_BLACKJACK,MODE_DURAK,MODE_DURAK,MODE_POOL,MODE_POOL
|
||||
obsNames3D dd OFFSET obsPoker,OFFSET obsBlackjack,OFFSET obsDurak,OFFSET obsTransfer,OFFSET obsPyramid,OFFSET obsEight
|
||||
obsProfilePointers3D dd OFFSET bankroll,OFFSET rngSeed3D,OFFSET tournamentMode
|
||||
dd OFFSET pkBlind3D,OFFSET cbBet3D,OFFSET dkBet3D,OFFSET dkVariant3D
|
||||
dd OFFSET poolVariant3D,OFFSET poolAI3D,OFFSET cbBotCount3D
|
||||
obsSavedProfile3D dd 10 dup(0)
|
||||
obsSpeedNames3D dd OFFSET obsSpeed1,OFFSET obsSpeed2,OFFSET obsSpeed4
|
||||
obsSpeed1 db "1X",0
|
||||
obsSpeed2 db "2X",0
|
||||
obsSpeed4 db "4X",0
|
||||
obsSeparator db ":",0
|
||||
obsResultNames3D dd OFFSET obsWin1,OFFSET obsWin2,OFFSET obsDraw
|
||||
obsTurnNames3D dd OFFSET obsTurn1,OFFSET obsTurn2
|
||||
obsTurn1 db 213,206,196,32,200,200,32,49,0
|
||||
obsTurn2 db 213,206,196,32,200,200,32,50,0
|
||||
obsTitle db 205,192,193,203,222,196,197,205,200,197,0
|
||||
obsPoker db 207,206,202,197,208,0
|
||||
obsBlackjack db 50,49,0
|
||||
obsDurak db 196,211,208,192,202,58,32,207,206,196,202,200,196,205,206,201,0
|
||||
obsTransfer db 196,211,208,192,202,58,32,207,197,208,197,194,206,196,205,206,201,0
|
||||
obsPyramid db 193,200,203,220,223,208,196,58,32,207,200,208,192,204,200,196,192,0
|
||||
obsEight db 193,200,203,220,223,208,196,58,32,194,206,209,220,204,197,208,202,192,0
|
||||
obsFirst db 200,200,32,49,0
|
||||
obsSecond db 200,200,32,50,0
|
||||
obsDraw db 205,200,215,220,223,0
|
||||
obsWin1 db 207,206,193,197,196,192,32,200,200,32,49,0
|
||||
obsWin2 db 207,206,193,197,196,192,32,200,200,32,50,0
|
||||
obsPause db 207,192,211,199,192,0
|
||||
obsPlay db 62,0
|
||||
obsRestart db 199,192,205,206,194,206,0
|
||||
obsGames db 200,195,208,219,0
|
||||
obsRound db 207,192,208,210,200,223,0
|
||||
obsClose db 194,32,213,206,203,203,0
|
||||
obsFailed db 205,197,32,211,196,192,203,206,209,220,32,206,210,202,208,219,210,220,32,209,210,206,203,0
|
||||
.data?
|
||||
obsBackground3D dd SCREEN_SIZE dup(?)
|
||||
.code
|
||||
|
||||
|
|
@ -13,6 +13,13 @@ WindowEvents3D PROC uses ebx esi edi hwnd:DWORD, uMsg:DWORD, wp:DWORD, lp:DWORD
|
|||
invoke FortuneChar3D, wp
|
||||
xor eax, eax
|
||||
ret
|
||||
@@:
|
||||
cmp uMsg,202h
|
||||
jne @F
|
||||
cmp engineMode,MODE_DARTS
|
||||
jne event_default
|
||||
mov dtMouseReleased3D,1
|
||||
jmp event_mouse_down
|
||||
@@:
|
||||
cmp uMsg, 201h
|
||||
je event_mouse_down
|
||||
|
|
@ -24,6 +31,8 @@ WindowEvents3D PROC uses ebx esi edi hwnd:DWORD, uMsg:DWORD, wp:DWORD, lp:DWORD
|
|||
@@:
|
||||
cmp uMsg, 200h
|
||||
jne @F
|
||||
cmp engineMode,MODE_DARTS
|
||||
je event_mouse_down
|
||||
cmp engineMode, MODE_POOL
|
||||
jne event_default
|
||||
test wp, 1
|
||||
|
|
@ -61,6 +70,15 @@ event_mouse_down:
|
|||
sub eax, viewLeft3D
|
||||
cmp eax, viewWidth3D
|
||||
jae event_done
|
||||
cmp engineMode,MODE_DARTS
|
||||
jne event_coarse_x
|
||||
push eax
|
||||
imul eax,640*256
|
||||
xor edx,edx
|
||||
div viewWidth3D
|
||||
mov dtCursorX3D,eax
|
||||
pop eax
|
||||
event_coarse_x:
|
||||
imul eax, 320
|
||||
xor edx, edx
|
||||
div viewWidth3D
|
||||
|
|
@ -69,6 +87,15 @@ event_mouse_down:
|
|||
sub eax, viewTop3D
|
||||
cmp eax, viewHeight3D
|
||||
jae event_done
|
||||
cmp engineMode,MODE_DARTS
|
||||
jne event_coarse_y
|
||||
push eax
|
||||
imul eax,480*256
|
||||
xor edx,edx
|
||||
div viewHeight3D
|
||||
mov dtCursorY3D,eax
|
||||
pop eax
|
||||
event_coarse_y:
|
||||
imul eax, 240
|
||||
xor edx, edx
|
||||
div viewHeight3D
|
||||
|
|
@ -87,6 +114,13 @@ event_mouse_down:
|
|||
pyr_event_pick:
|
||||
mov pyrPickClick3D,1
|
||||
pyr_event_pick_done:
|
||||
cmp uMsg,202h
|
||||
je event_done
|
||||
cmp uMsg,200h
|
||||
jne @F
|
||||
cmp engineMode,MODE_DARTS
|
||||
je event_done
|
||||
@@:
|
||||
mov mouseClick3D, 1
|
||||
jmp event_done
|
||||
event_key_down:
|
||||
|
|
@ -110,6 +144,8 @@ event_fullscreen:
|
|||
call ToggleFullscreen3D
|
||||
jmp event_done
|
||||
event_focus_lost:
|
||||
mov dtHolding3D,0
|
||||
mov dtMouseReleased3D,0
|
||||
call ClearKeys3D
|
||||
jmp event_done
|
||||
event_erase:
|
||||
|
|
@ -572,6 +608,8 @@ progress_loaded:
|
|||
LoadProgress3D ENDP
|
||||
|
||||
SaveProgress3D PROC uses ebx
|
||||
cmp obsActive3D,0
|
||||
jne progress_saved
|
||||
IFDEF CASINO_ACTION_TEST
|
||||
ret
|
||||
ENDIF
|
||||
|
|
|
|||
|
|
@ -537,7 +537,11 @@ pyr_actor:
|
|||
ret
|
||||
@@:
|
||||
cmp poolPlayer3D,1
|
||||
jne pyr_human
|
||||
je pyr_actor_ai_check
|
||||
cmp obsActive3D,0
|
||||
jne pyr_actor_ai_check
|
||||
jmp pyr_human
|
||||
pyr_actor_ai_check:
|
||||
cmp poolAI3D,0
|
||||
je pyr_human
|
||||
cmp poolTime3D,1000
|
||||
|
|
|
|||
|
|
@ -115,6 +115,7 @@ RenderHallDressing3D PROC uses ebx esi edi
|
|||
cmp hallLevel3D,0
|
||||
jne hall_dressing_done
|
||||
call RenderTerminalCabinets3D
|
||||
call DrawHallDarts3D
|
||||
lea esi,hallWallArt3D
|
||||
mov edi,WORLD_WALL_ART_COUNT
|
||||
hall_wall_painting:
|
||||
|
|
|
|||
203
tools/build_setup.py
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Build and package the complete modern Assembler Casino into PokerGameSetup.exe."""
|
||||
|
||||
import hashlib
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
MARKER = b"<<POKER_GAME_GUI_PAYLOAD_ZIP>>"
|
||||
|
||||
|
||||
def get_sha256(data_or_path):
|
||||
if isinstance(data_or_path, (str, Path)):
|
||||
h = hashlib.sha256()
|
||||
with open(data_or_path, "rb") as f:
|
||||
while chunk := f.read(65536):
|
||||
h.update(chunk)
|
||||
return h.hexdigest().upper()
|
||||
return hashlib.sha256(data_or_path).hexdigest().upper()
|
||||
|
||||
|
||||
def collect_payload_files():
|
||||
entries = []
|
||||
|
||||
# Executables
|
||||
for src, dst in [
|
||||
(ROOT / "dist/Assembler Casino.exe", "Assembler Casino.exe"),
|
||||
(ROOT / "dist/Uninstall Assembler Casino.exe", "Uninstall Assembler Casino.exe"),
|
||||
(ROOT / "bin/poker.exe", "bin/poker.exe"),
|
||||
(ROOT / "poker.exe", "poker.exe"),
|
||||
]:
|
||||
if not src.exists():
|
||||
raise FileNotFoundError(f"Required binary not found: {src}")
|
||||
entries.append((src, dst))
|
||||
|
||||
# Assets (excluding raw sprites used only for asset generation)
|
||||
assets_dir = ROOT / "assets"
|
||||
for r, dirs, files in os.walk(assets_dir):
|
||||
if "sprites" in Path(r).parts:
|
||||
continue
|
||||
for f in sorted(files):
|
||||
fp = Path(r) / f
|
||||
rel = fp.relative_to(ROOT).as_posix()
|
||||
entries.append((fp, rel))
|
||||
|
||||
# Docs
|
||||
docs_dir = ROOT / "docs"
|
||||
for r, dirs, files in os.walk(docs_dir):
|
||||
for f in sorted(files):
|
||||
if f.endswith((".txt", ".md")):
|
||||
fp = Path(r) / f
|
||||
rel = fp.relative_to(ROOT).as_posix()
|
||||
entries.append((fp, rel))
|
||||
|
||||
# Tools
|
||||
tool_files = [
|
||||
"tools/previews/preview_dbd_hook.bat",
|
||||
"tools/previews/preview_hakari.bat",
|
||||
"tools/previews/preview_intro.bat",
|
||||
"tools/previews/preview_casino3d.bat",
|
||||
"tools/tests/achievement_popup.bat",
|
||||
"tools/tests/release_smoke_check.ps1",
|
||||
]
|
||||
for rel in tool_files:
|
||||
fp = ROOT / rel
|
||||
if fp.exists():
|
||||
entries.append((fp, rel))
|
||||
|
||||
# Backwards compatibility paths for tests / older scripts
|
||||
if (ROOT / "tools/previews/preview_dbd_hook.bat").exists():
|
||||
entries.append((ROOT / "tools/previews/preview_dbd_hook.bat", "tools/preview_dbd_hook.bat"))
|
||||
if (ROOT / "tools/tests/release_smoke_check.ps1").exists():
|
||||
entries.append((ROOT / "tools/tests/release_smoke_check.ps1", "tools/release_smoke_check.ps1"))
|
||||
|
||||
# Runtime configuration and state
|
||||
for rel in ["runtime/casino3d-progress.dat", "runtime/display.cfg"]:
|
||||
fp = ROOT / rel
|
||||
if fp.exists():
|
||||
entries.append((fp, rel))
|
||||
|
||||
# AI worker & metadata (without the 2.5 GB model per docs/FORTUNE_AI.md)
|
||||
ai_dir = ROOT / "ai"
|
||||
if ai_dir.exists():
|
||||
for r, dirs, files in os.walk(ai_dir):
|
||||
if "models" in Path(r).parts:
|
||||
continue
|
||||
for f in sorted(files):
|
||||
fp = Path(r) / f
|
||||
rel = fp.relative_to(ROOT).as_posix()
|
||||
entries.append((fp, rel))
|
||||
|
||||
# Root documentation
|
||||
for rel in ["AGENTS.md", "README.md"]:
|
||||
fp = ROOT / rel
|
||||
if fp.exists():
|
||||
entries.append((fp, rel))
|
||||
|
||||
return entries
|
||||
|
||||
|
||||
def build_payload_zip(zip_path, entries):
|
||||
print(f"Creating payload archive: {zip_path} ({len(entries)} files)...")
|
||||
zip_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=6) as zf:
|
||||
for src, dst in entries:
|
||||
zf.write(src, dst)
|
||||
size_mb = zip_path.stat().st_size / (1024 * 1024)
|
||||
print(f"Payload archive created: {size_mb:.2f} MB")
|
||||
|
||||
|
||||
def compile_setup_base(source_path, base_exe_path):
|
||||
csc = Path(os.environ.get("WINDIR", r"C:\Windows")) / r"Microsoft.NET\Framework\v4.0.30319\csc.exe"
|
||||
if not csc.exists():
|
||||
raise RuntimeError(f"csc.exe not found at {csc}")
|
||||
|
||||
icon = ROOT / "assets/icons/casino.ico"
|
||||
cmd = [
|
||||
str(csc),
|
||||
"/nologo",
|
||||
"/target:winexe",
|
||||
"/platform:x86",
|
||||
"/optimize+",
|
||||
"/reference:System.Windows.Forms.dll",
|
||||
"/reference:System.Drawing.dll",
|
||||
"/reference:System.IO.Compression.dll",
|
||||
"/reference:System.IO.Compression.FileSystem.dll",
|
||||
"/reference:System.Core.dll",
|
||||
f"/win32icon:{icon}",
|
||||
f"/out:{base_exe_path}",
|
||||
str(source_path),
|
||||
]
|
||||
print(f"Compiling installer base: {' '.join(cmd)}")
|
||||
res = subprocess.run(cmd, capture_output=True, text=True)
|
||||
if res.returncode != 0:
|
||||
raise RuntimeError(f"Compilation failed:\n{res.stdout}\n{res.stderr}")
|
||||
print(f"Compiled: {base_exe_path} ({base_exe_path.stat().st_size} bytes)")
|
||||
|
||||
|
||||
def main():
|
||||
print("=== Building Assembler Casino PokerGameSetup ===")
|
||||
|
||||
# 1. Collect files
|
||||
entries = collect_payload_files()
|
||||
payload_zip = ROOT / "build/payload_release_final.zip"
|
||||
build_payload_zip(payload_zip, entries)
|
||||
|
||||
# 2. Hash payload
|
||||
payload_hash = get_sha256(payload_zip)
|
||||
print(f"Payload SHA-256: {payload_hash}")
|
||||
|
||||
# 3. Update PokerGameSetupGui.cs with new payload hash
|
||||
setup_cs = ROOT / "build/PokerGameSetupGui.cs"
|
||||
cs_content = setup_cs.read_text(encoding="utf-8")
|
||||
hash_pattern = r'const string ExpectedPayloadSha256 = "([A-Fa-f0-9]{64}|TO_BE_FILLED_BY_RELEASE_BUILD)";'
|
||||
if not re.search(hash_pattern, cs_content):
|
||||
raise ValueError("Could not find ExpectedPayloadSha256 constant in PokerGameSetupGui.cs")
|
||||
new_cs_content = re.sub(
|
||||
hash_pattern,
|
||||
f'const string ExpectedPayloadSha256 = "{payload_hash}";',
|
||||
cs_content,
|
||||
count=1
|
||||
)
|
||||
setup_cs.write_text(new_cs_content, encoding="utf-8")
|
||||
print("Updated ExpectedPayloadSha256 in PokerGameSetupGui.cs")
|
||||
|
||||
# 4. Compile base setup executable
|
||||
base_exe = ROOT / "build/PokerGameSetup_base.exe"
|
||||
compile_setup_base(setup_cs, base_exe)
|
||||
|
||||
# 5. Assemble final PokerGameSetup.exe
|
||||
setup_exe = ROOT / "PokerGameSetup.exe"
|
||||
print(f"Assembling {setup_exe}...")
|
||||
with open(setup_exe, "wb") as out_f:
|
||||
with open(base_exe, "rb") as in_base:
|
||||
out_f.write(in_base.read())
|
||||
out_f.write(MARKER)
|
||||
with open(payload_zip, "rb") as in_zip:
|
||||
while chunk := in_zip.read(65536):
|
||||
out_f.write(chunk)
|
||||
|
||||
total_mb = setup_exe.stat().st_size / (1024 * 1024)
|
||||
print(f"=== Successfully built {setup_exe} ({total_mb:.2f} MB) ===")
|
||||
|
||||
# 6. Verify built setup
|
||||
with open(setup_exe, "rb") as f:
|
||||
head = f.read(4 * 1024 * 1024)
|
||||
idx = head.find(MARKER)
|
||||
assert idx != -1, "Marker not found in generated setup!"
|
||||
f.seek(idx + len(MARKER))
|
||||
h = hashlib.sha256()
|
||||
while chunk := f.read(65536):
|
||||
h.update(chunk)
|
||||
actual_hash = h.hexdigest().upper()
|
||||
assert actual_hash == payload_hash, f"Hash mismatch: {actual_hash} != {payload_hash}"
|
||||
print(f"[VERIFIED] Marker and SHA-256 match perfectly ({actual_hash})")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
21
tools/generators/darts_cp1251.patch
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
diff --git a/casino3d.inc b/casino3d.inc
|
||||
--- a/casino3d.inc
|
||||
+++ b/casino3d.inc
|
||||
@@ -174,1 +174,2 @@
|
||||
include src_graphics\observer_data.inc
|
||||
+include src_graphics\darts_data.inc
|
||||
@@ -5198,1 +5199,2 @@
|
||||
include src_graphics\observer.inc
|
||||
+include src_graphics\darts.inc
|
||||
@@ -5357,1 +5359,9 @@
|
||||
check_slots_mode:
|
||||
+ cmp engineMode,MODE_DARTS
|
||||
+ jne check_darts_done
|
||||
+ call HandleDarts3D
|
||||
+ call RenderDarts3D
|
||||
+ jmp present_frame
|
||||
+check_darts_done:
|
||||
cmp engineMode,MODE_FORTUNE
|
||||
@@ -5642,1 +5652,2 @@
|
||||
call FreeRussianAssets3D
|
||||
+ call FreeDarts3D
|
||||
15
tools/generators/darts_hall.patch
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
diff --git a/src_graphics/hall_scene.inc b/src_graphics/hall_scene.inc
|
||||
--- a/src_graphics/hall_scene.inc
|
||||
+++ b/src_graphics/hall_scene.inc
|
||||
@@ -783,1 +783,2 @@
|
||||
hall_check_other:
|
||||
+ invoke HallTarget3D,FP_1*305/10,28*FP_1,INTERACT_DARTS,OFFSET dtPrompt
|
||||
@@ -799,1 +800,9 @@
|
||||
TriggerHallInteraction3D PROC
|
||||
+ cmp interactTargetId,INTERACT_DARTS
|
||||
+ jne hall_not_darts
|
||||
+ call BeginDarts3D
|
||||
+ mov eax,1
|
||||
+ ret
|
||||
+hall_not_darts:
|
||||
call TryFortuneInteraction3D
|
||||
29
tools/generators/observer_cp1251.patch
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
diff --git a/casino3d.inc b/casino3d.inc
|
||||
--- a/casino3d.inc
|
||||
+++ b/casino3d.inc
|
||||
@@ -173,1 +173,2 @@
|
||||
include src_graphics\charms_data.inc
|
||||
+include src_graphics\observer_data.inc
|
||||
@@ -4856,5 +4857,5 @@
|
||||
check_target_observer:
|
||||
cmp eax, INTERACT_OBSERVER
|
||||
jne target_unported
|
||||
- call BeginUnavailable3D
|
||||
+ call BeginObserver3D
|
||||
ret
|
||||
@@ -5196,1 +5197,2 @@
|
||||
include src_graphics\charms.inc
|
||||
+include src_graphics\observer.inc
|
||||
@@ -5245,2 +5247,11 @@
|
||||
call ToggleFullscreen3D
|
||||
@@:
|
||||
+ cmp obsActive3D,0
|
||||
+ jne engine_observer_frame
|
||||
+ cmp engineMode,MODE_OBSERVER
|
||||
+ jne engine_observer_skip
|
||||
+engine_observer_frame:
|
||||
+ call UpdateMouseLockState
|
||||
+ call ObserverFrame3D
|
||||
+ jmp present_frame
|
||||
+engine_observer_skip:
|
||||
invoke Pressed3D, 1Bh
|
||||
103
tools/generators/pack_darts.py
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
"""Exact clock-board geometry plus fixed-grid full-arm animation packing."""
|
||||
from pathlib import Path
|
||||
import math
|
||||
import random
|
||||
import struct
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
ART = ROOT / "assets/sprites/darts"
|
||||
OUT = ROOT / "assets/animations/darts"
|
||||
SECTORS = [20, 1, 18, 4, 13, 6, 10, 15, 2, 17, 3, 19, 7, 16, 8, 11, 14, 9, 12, 5]
|
||||
|
||||
|
||||
def pack(name, frames):
|
||||
width, height = frames[0].size
|
||||
payload = b"".join(frame.tobytes("raw", "BGRA") for frame in frames)
|
||||
(OUT / name).write_bytes(struct.pack("<4s4I", b"ANM1", width, height, len(frames), len(payload)) + payload)
|
||||
|
||||
|
||||
def board():
|
||||
rng = random.Random(301)
|
||||
image = Image.new("RGBA", (512, 512))
|
||||
pixels = image.load()
|
||||
for y in range(512):
|
||||
for x in range(512):
|
||||
dx, dy = x-256, 256-y
|
||||
radius = math.hypot(dx, dy)
|
||||
angle = math.atan2(dx, dy)
|
||||
sector = int(((angle + math.pi/20) % math.tau) / (math.tau/20))
|
||||
red = sector % 2 == 0
|
||||
color = (30, 36, 39)
|
||||
if radius < 170:
|
||||
color = (35, 38, 37) if red else (214, 206, 178)
|
||||
if 99 <= radius <= 107 or 162 <= radius:
|
||||
color = (174, 37, 47) if red else (28, 112, 87)
|
||||
if radius <= 15.9:
|
||||
color = (28, 112, 87)
|
||||
if radius <= 6.35:
|
||||
color = (187, 37, 47)
|
||||
elif radius < 210:
|
||||
color = (20, 24, 25)
|
||||
elif radius < 240:
|
||||
color = (72, 29, 35)
|
||||
elif radius < 248:
|
||||
color = (31, 37, 41)
|
||||
else:
|
||||
continue
|
||||
grain = rng.randrange(-11, 12) if radius < 170 else rng.randrange(-3, 4)
|
||||
pixels[x, y] = tuple(max(0, min(255, c+grain)) for c in color) + (255,)
|
||||
draw = ImageDraw.Draw(image)
|
||||
wire = (171, 178, 173, 255)
|
||||
for r in (6.35, 15.9, 99, 107, 162, 170):
|
||||
draw.ellipse((256-r, 256-r, 256+r, 256+r), outline=wire, width=1)
|
||||
for index in range(20):
|
||||
theta = (index*18-9)*math.pi/180
|
||||
draw.line([(256+math.sin(theta)*16, 256-math.cos(theta)*16),
|
||||
(256+math.sin(theta)*170, 256-math.cos(theta)*170)], fill=wire)
|
||||
font = ImageFont.truetype("C:/Windows/Fonts/arialbd.ttf", 23)
|
||||
for index, value in enumerate(SECTORS):
|
||||
theta = index*math.tau/20
|
||||
draw.text((256+math.sin(theta)*190, 256-math.cos(theta)*190), str(value),
|
||||
font=font, fill=(227, 230, 220), anchor="mm", stroke_width=1)
|
||||
draw.ellipse((12, 12, 500, 500), outline=(104, 114, 120), width=3)
|
||||
image.save(ART / "board-v1.png")
|
||||
return image
|
||||
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
ART.mkdir(parents=True, exist_ok=True)
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
pack("board-v1.bin", [board()])
|
||||
wall = Image.open(ART / "lounge-wall-v1.png").convert("RGBA")
|
||||
pack("lounge-wall-v1.bin", [wall.resize((512,512), Image.Resampling.LANCZOS)])
|
||||
anchors = []
|
||||
for name in ("arm-throw-v3", "dart-views-v1"):
|
||||
source = ART / (name + ".png")
|
||||
sheet = Image.open(source).convert("RGBA")
|
||||
assert sheet.width == sheet.height
|
||||
cell = sheet.width / 4
|
||||
frames = [sheet.crop(tuple(round(v*cell) for v in (x, y, x+1, y+1)))
|
||||
.resize((256, 256), Image.Resampling.LANCZOS) for y in range(4) for x in range(4)]
|
||||
assert all(f.getchannel("A").getextrema()[0] == 0 for f in frames)
|
||||
assert len({f.tobytes() for f in frames}) == 16
|
||||
pack(name + ".bin", frames)
|
||||
if name == "dart-views-v1":
|
||||
for frame in frames:
|
||||
metal = [(x,y) for y in range(256) for x in range(256)
|
||||
if (lambda p: p[3] > 220 and min(p[:3]) > 50
|
||||
and max(p[:3])-min(p[:3]) < 60)(frame.getpixel((x,y)))]
|
||||
tip_x = min(x for x,y in metal)
|
||||
ys = sorted(y for x,y in metal if x <= tip_x+2)
|
||||
anchors.append((tip_x, ys[len(ys)//2]))
|
||||
(ROOT / "src_graphics/darts_art.inc").write_text(
|
||||
"; Generated per-view needle anchors in the 256px source canvas.\n"
|
||||
+ "dtDartTipX3D dd " + ",".join(str(p[0]) for p in anchors) + "\n"
|
||||
+ "dtDartTipY3D dd " + ",".join(str(p[1]) for p in anchors) + "\n", encoding="ascii")
|
||||
print("Packed exact 20-sector board; whole-arm animation uses one fixed canvas and scale.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
63
tools/generators/pack_fortune_session.py
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
"""Offline sprite-sheet compiler; runtime animation stays in MASM."""
|
||||
from pathlib import Path
|
||||
import json
|
||||
import struct
|
||||
from PIL import Image
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
ART = ROOT / "assets/sprites/fortune"
|
||||
OUT = ROOT / "assets/animations/fortune"
|
||||
|
||||
|
||||
def grid(name, columns, rows, size):
|
||||
source = Image.open(ART / name).convert("RGBA")
|
||||
return [source.crop((round(x*source.width/columns), round(y*source.height/rows),
|
||||
round((x+1)*source.width/columns), round((y+1)*source.height/rows)))
|
||||
.resize(size, Image.Resampling.LANCZOS)
|
||||
for y in range(rows) for x in range(columns)]
|
||||
|
||||
|
||||
def tween(frames, loop=False):
|
||||
# One shared canvas/scale, never fit moving hands independently per frame.
|
||||
output = []
|
||||
for i, frame in enumerate(frames):
|
||||
following = frames[(i+1) % len(frames)] if loop or i+1 < len(frames) else frame
|
||||
output.extend(Image.blend(frame, following, step/4) for step in range(4))
|
||||
return output
|
||||
|
||||
|
||||
def pack(name, frames):
|
||||
width, height = frames[0].size
|
||||
assert len(frames) <= 64
|
||||
assert all(frame.size == (width, height) for frame in frames)
|
||||
payload = b"".join(frame.tobytes("raw", "BGRA") for frame in frames)
|
||||
(OUT / (name + ".bin")).write_bytes(
|
||||
struct.pack("<4s4I", b"ANM1", width, height, len(frames), len(payload)) + payload)
|
||||
return {"file": name + ".bin", "width": width, "height": height, "frames": len(frames)}
|
||||
|
||||
|
||||
def main():
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
body = grid("teller-actions-v1.png", 8, 5, (256, 256))
|
||||
cards = grid("tarot-symbols-v1.png", 6, 4, (96, 128))
|
||||
# Orientations are real upside-down artwork, not merely a different label.
|
||||
faces = cards[:22] + [c.transpose(Image.Transpose.ROTATE_180) for c in cards[:22]] + [cards[22]]
|
||||
room = Image.open(ART / "seated-room-v1.png").convert("RGBA").resize((640, 480), Image.Resampling.LANCZOS)
|
||||
manifest = [pack("room-v1", [room]),
|
||||
pack("shuffle-v1", tween(body[:16], loop=True)),
|
||||
pack("deal-v1", tween(body[16:24])),
|
||||
pack("gesture-v1", tween(body[24:32], loop=True)),
|
||||
pack("orb-hands-v1", tween(body[32:40], loop=True)),
|
||||
pack("orb-mist-v1", grid("crystal-mist-v1.png", 4, 4, (192, 192))),
|
||||
pack("cards-v1", faces)]
|
||||
(ART / "packed-v1.json").write_text(json.dumps(manifest, indent=2) + "\n", encoding="ascii")
|
||||
preview = room.copy()
|
||||
preview.alpha_composite(body[0].resize((280, 250)), (134, 32))
|
||||
for index in range(3):
|
||||
preview.alpha_composite(cards[22].resize((72, 96)), (198+index*100, 302))
|
||||
preview.save(ROOT / "build/hall-expansion/fortune-session-art.png")
|
||||
print("Packed 160 hand poses, 16 mist frames, 44 oriented faces, back and seated room.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -33,6 +33,8 @@ InitPaths3D PROTO
|
|||
ExitProcess PROTO :DWORD
|
||||
WriteActionSnapshot3D PROTO :DWORD
|
||||
include src_graphics/billiards_data.inc
|
||||
.data
|
||||
obsActive3D dd 0
|
||||
.code
|
||||
include src_graphics/billiards.inc
|
||||
include tools/tests/pyramid_test.inc
|
||||
|
|
|
|||
175
tools/tests/check_darts.py
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
"""Fresh MASM dart scoring, mouse aiming, full-arm animation and 3D follow-camera proofs."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import hashlib
|
||||
import re
|
||||
import struct
|
||||
import subprocess
|
||||
from PIL import Image
|
||||
from check_card_exit_build import Coff
|
||||
from check_terminals import signature
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
OUT = ROOT / "build/darts"
|
||||
|
||||
|
||||
def read(path):
|
||||
raw = (ROOT / path).read_bytes()
|
||||
try:
|
||||
return raw.decode("utf8").replace("\r\n", "\n")
|
||||
except UnicodeDecodeError:
|
||||
return raw.decode("cp1251").replace("\r\n", "\n")
|
||||
|
||||
|
||||
def proc(text, name):
|
||||
return re.search(rf"^{name} PROC\b.*?^{name} ENDP", text, re.M | re.S).group()
|
||||
|
||||
|
||||
def expand(path):
|
||||
return re.sub(r"^include ([^\n]+)$", lambda m: expand(m[1].strip().replace("\\", "/")),
|
||||
read(path), flags=re.M)
|
||||
|
||||
|
||||
def prepare():
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
code = expand("src_graphics/darts.inc")
|
||||
code += "\n" + proc(read("src_graphics/platform_code.inc"), "WindowEvents3D")
|
||||
for name in ("ResolveCitySurface3D", "DrawCityVertical3D"):
|
||||
code += "\n" + proc(read("src_graphics/city_bins.inc"), name)
|
||||
code += "\n" + proc(read("tools/tests/casino_actions_test.inc"), "WriteActionSnapshot3D")
|
||||
code += "\nPlayCasinoSound3D PROC arg0:DWORD,arg1:DWORD,arg2:DWORD\n ret\nPlayCasinoSound3D ENDP\n"
|
||||
code += "\n" + read("tools/tests/darts_test.inc")
|
||||
code += "\n" + read("tools/tests/darts_ui.inc")
|
||||
data = expand("src_graphics/darts_data.inc")
|
||||
stages = [(0, t) for t in range(0, 320, 40)] + [(1, t) for t in range(0, 200, 40)]
|
||||
stages += [(2, t) for t in range(0, 1200, 40)]
|
||||
stages += [(3, t) for t in range(0, 1300, 80)] + [(4, t) for t in range(0, 751, 50)]
|
||||
code += "\nDartsTestFrames PROC\n call ResetDarts3D\n mov dtAimX3D,0\n mov dtAimY3D,103\n mov dtHitX3D,0\n mov dtHitY3D,103\n"
|
||||
for index, (state, time) in enumerate(stages):
|
||||
data += f'\n.data\ndtFrame{index} db "build\\darts\\frame-{index:03}.bmp",0\n'
|
||||
if state == 3 and stages[index-1][0] != 3:
|
||||
code += " mov dtState3D,DT_FLIGHT\n call SettleDarts3D\n"
|
||||
code += f" mov dtState3D,{state}\n mov dtTimer3D,{time}\n mov dtClock3D,{time}\n"
|
||||
if state == 0:
|
||||
code += f" mov dtHolding3D,1\n mov dtHoldTime3D,{time}\n"
|
||||
code += f" call RenderDarts3D\n invoke WriteActionSnapshot3D,OFFSET dtFrame{index}\n"
|
||||
data += '\n.data\ndtScoreFrame db "build\\darts\\scoreboard.bmp",0\n'
|
||||
code += " mov dtScoreOpen3D,1\n call RenderDarts3D\n invoke WriteActionSnapshot3D,OFFSET dtScoreFrame\n"
|
||||
code += " call ResetDarts3D\n mov dtHolding3D,1\n mov dtCursorX3D,320*256\n mov dtCursorY3D,214*256-103*DT_FOCAL*256/DT_WALL\n"
|
||||
for index in range(80):
|
||||
data += f'\n.data\ndtSwayFrame{index} db "build\\darts\\sway-{index:03}.bmp",0\n'
|
||||
code += f" mov dtClock3D,{index*40}\n mov dtHoldTime3D,{index*40}\n call DartsAim3D\n call RenderDarts3D\n invoke WriteActionSnapshot3D,OFFSET dtSwayFrame{index}\n"
|
||||
code += " ret\nDartsTestFrames ENDP\n"
|
||||
code = code.replace(":SDWORD", ":DWORD")
|
||||
data += "\n.data\nactionBmpHeader db 42h,4Dh\n dd 1228854\n dw 0,0\n dd 54,40,640,-480\n dw 1,32\n dd 0,1228800,0,0,0,0\n"
|
||||
own_funcs = set(re.findall(r"^(\w+) PROC", code, re.M))
|
||||
own_data = set(re.findall(r"^(\w+)\s+(?:db|dw|dd|REAL4|REAL8|AnimPack3D|LABEL)\b", data + "\n" + code, re.M))
|
||||
source = "\n".join(read(str(p.relative_to(ROOT))) for p in
|
||||
[ROOT / "poker.asm", ROOT / "casino3d.inc", ROOT / "casino3d_data.inc",
|
||||
*sorted(p for p in (ROOT / "src_graphics").glob("*.inc") if p.name != "renderer.inc")])
|
||||
structures = [re.search(rf"^{name} STRUCT\b.*?^{name} ENDS", source, re.M | re.S).group()
|
||||
for name in ("AnimPack3D", "BJBot3D")]
|
||||
types = {name: kind for name, kind in re.findall(r"^(\w+)\s+(AnimPack3D|BJBot3D)\b", source, re.M)}
|
||||
types.update({name: {"db": "BYTE", "dw": "WORD", "dd": "DWORD"}[kind.lower()]
|
||||
for name, kind in re.findall(r"^(\w+)\s+(db|dw|dd)\b", source, re.M | re.I)})
|
||||
types.update({name: kind.upper() for name, kind in
|
||||
re.findall(r"^(\w+)\s+(REAL4|REAL8)\b", source, re.M | re.I)})
|
||||
coff = Coff(ROOT / "build/graphics/poker.obj")
|
||||
helper = bytearray(coff.data)
|
||||
symbol_at = struct.unpack_from("<I", helper, 8)[0]
|
||||
externs, protos = [], {}
|
||||
for index, name in coff.symbols.items():
|
||||
pos = symbol_at + index * 18
|
||||
_, section, kind, storage, _ = struct.unpack_from("<IhHBB", helper, pos + 8)
|
||||
if section > 0 and re.fullmatch(r"[A-Za-z_]\w*", name) and storage in (2, 3):
|
||||
secname = helper[20+(section-1)*40:28+(section-1)*40].rstrip(b"\0")
|
||||
if secname in (b".data", b".bss", b".const"):
|
||||
helper[pos+16] = 3 if name in own_data else 2
|
||||
if name not in own_data:
|
||||
externs.append(f"EXTERN {name}:{types.get(name, 'DWORD')}")
|
||||
match = re.fullmatch(r"_(\w+)@(\d+)", name)
|
||||
if match and kind == 0x20 and section > 0:
|
||||
helper[pos+16] = 3 if match[1] in own_funcs else 2
|
||||
if match[1] not in own_funcs:
|
||||
protos[match[1]] = int(match[2]) // 4
|
||||
for name, params in re.findall(r"^(\w+)\s+PROTO([^\n]*)", source, re.M):
|
||||
protos.setdefault(name, params.count(":DWORD"))
|
||||
for name in own_funcs:
|
||||
protos[name] = len(re.findall(r":S?DWORD", proc(code, name).splitlines()[0]))
|
||||
constants = dict(re.findall(r"^(\w+)\s+equ\s+([^;\n]+)", source, re.M))
|
||||
used = set(re.findall(r"^(\w+) equ", data, re.M))
|
||||
ordered = []
|
||||
def constant(name):
|
||||
if name not in constants or name in used:
|
||||
return
|
||||
used.add(name)
|
||||
for dep in re.findall(r"\b[A-Za-z_]\w*", constants[name]):
|
||||
constant(dep)
|
||||
ordered.append(f"{name} equ {constants[name]}")
|
||||
for name in re.findall(r"\b[A-Za-z_]\w*", code + data):
|
||||
constant(name)
|
||||
header = ".386\n.model flat\noption casemap:none\n" + "\n".join(structures + ordered + externs)
|
||||
header += "\noption language:stdcall\n" + "\n".join(
|
||||
name + " PROTO" + (" " + ",".join([":DWORD"]*count) if count else "") for name, count in protos.items())
|
||||
data = re.sub(r"^\w+ PROTO[^\n]*\n", "", data, flags=re.M)
|
||||
(OUT / "helpers.obj").write_bytes(helper)
|
||||
(OUT / "module.asm").write_text(header + "\n" + data + "\n.code\n" + code + "\nend DartsTestStart\n", encoding="cp1251")
|
||||
return {name for name in own_funcs if not name.startswith(("DartsTest", "DartsUi"))} - {
|
||||
"PlayCasinoSound3D", "WriteActionSnapshot3D"}
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--parity", action="store_true")
|
||||
parser.add_argument("--interactive", action="store_true")
|
||||
args = parser.parse_args()
|
||||
functions = prepare()
|
||||
if args.parity:
|
||||
tested, shipping = Coff(OUT / "module.obj"), Coff(ROOT / "build/graphics/poker.obj")
|
||||
for name in functions:
|
||||
symbol = next(key for key in tested.functions if key.startswith("_" + name + "@"))
|
||||
assert signature(tested, symbol, tail_branches=True) == signature(shipping, symbol, tail_branches=True), name
|
||||
print("PASS: dart scene, scoring, input and camera match the shipping object")
|
||||
return
|
||||
masm = Path("F:/MASM32/masm32")
|
||||
subprocess.run([str(masm / "bin/ml.exe"), "/c", "/coff", "/Cp", "/nologo", "/I.",
|
||||
"/Fobuild/darts/module.obj", "build/darts/module.asm"], cwd=ROOT, check=True)
|
||||
sdk = sorted(Path("C:/Program Files (x86)/Windows Kits/10/Lib").glob("*/um/x86"))[-1]
|
||||
target = "ui" if args.interactive else "module"
|
||||
entry = "DartsUiStart" if args.interactive else "DartsTestStart"
|
||||
subprocess.run([str(masm / "bin/link.exe"), "/nologo", "/SUBSYSTEM:WINDOWS", f"/ENTRY:{entry}@0",
|
||||
f"/LIBPATH:{sdk}", f"/LIBPATH:{masm/'lib'}", f"/OUT:build/darts/{target}.exe",
|
||||
"build/darts/module.obj", "build/darts/helpers.obj", "kernel32.lib", "user32.lib",
|
||||
"gdi32.lib", "winmm.lib", "ws2_32.lib"], cwd=ROOT, check=True)
|
||||
def profile():
|
||||
return {str(p): hashlib.sha256(p.read_bytes()).digest() for p in (ROOT / "runtime").rglob("*")
|
||||
if p.is_file()}
|
||||
before = profile()
|
||||
run = subprocess.run([str(OUT / (target + ".exe"))], cwd=ROOT, timeout=900 if args.interactive else 180)
|
||||
assert run.returncode == 0, f"Native darts assertion: {run.returncode}"
|
||||
assert profile() == before, "Darts changed persistent profile files"
|
||||
if args.interactive:
|
||||
print("PASS: interactive native scene closed without changing the profile")
|
||||
return
|
||||
frames = [Image.open(p).convert("RGB") for p in sorted(OUT.glob("frame-*.bmp"))]
|
||||
assert len(frames) >= 60 and len({f.tobytes() for f in frames}) >= 40
|
||||
assert all(len(f.getcolors(640*480)) > 80 for f in frames)
|
||||
frames[0].save(OUT / "throw.gif", save_all=True, append_images=frames[1:], duration=40, loop=0)
|
||||
sheet = Image.new("RGB", (640*3, 480*2))
|
||||
for slot, index in enumerate((0, 10, 22, 36, 48, len(frames)-1)):
|
||||
sheet.paste(frames[index], ((slot % 3)*640, (slot // 3)*480))
|
||||
sheet.save(OUT / "contact.png")
|
||||
sway = [Image.open(p).convert("RGB") for p in sorted(OUT.glob("sway-*.bmp"))]
|
||||
assert len(sway) == 80 and len({f.tobytes() for f in sway}) > 24
|
||||
sway[0].save(OUT / "sway.gif", save_all=True, append_images=sway[1:], duration=40, loop=0)
|
||||
triples, = struct.unpack("<I", (OUT / "aim-metrics.bin").read_bytes())
|
||||
print(f"PASS: stationary centre-of-T20 aim scored triples in {triples}/100 native samples")
|
||||
print("PASS: 67 scoring vectors, 20 complete bot matches, bust/turn isolation, fine mouse/release, sway/fatigue, board footprint, Tab, orbit camera and native moving frames")
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
|
@ -24,6 +24,11 @@ def proc(source, name):
|
|||
return re.search(rf"^{name} PROC\b.*?^{name} ENDP",source,re.M|re.S).group()
|
||||
|
||||
|
||||
def expand(path):
|
||||
return re.sub(r"^include ([^\n]+)$", lambda m: expand(m[1].strip().replace("\\", "/")),
|
||||
read(path), flags=re.M)
|
||||
|
||||
|
||||
def prepare():
|
||||
OUT.mkdir(exist_ok=True)
|
||||
code = read("src_graphics/hall_expansion.inc") + "\n" + read("src_graphics/cabaret.inc")
|
||||
|
|
@ -32,6 +37,10 @@ def prepare():
|
|||
code += "\n" + read("src_graphics/fortune_room.inc")
|
||||
code += "\n" + read("src_graphics/fortune_dialog.inc")
|
||||
code += "\n" + proc(read("src_graphics/unavailable.inc"),"BeginUnavailable3D")
|
||||
code += "\n" + read("src_graphics/observer.inc")
|
||||
code += "\n" + expand("src_graphics/darts.inc").replace(":SDWORD", ":DWORD")
|
||||
code += "\n" + proc(read("src_graphics/native_poker.inc"),"PokerBotAct3D")
|
||||
code += "\n" + proc(read("src_graphics/native_durak.inc"),"DurakBotTurn3D")
|
||||
code += "\n" + proc(read("src_graphics/world_dressing.inc"),"RenderHallDressing3D")
|
||||
for path, names in (
|
||||
("src_graphics/hall_scene.inc",["RenderCeilingAndFloor","RenderSprites","InitHall3D",
|
||||
|
|
@ -53,8 +62,13 @@ def prepare():
|
|||
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",
|
||||
"src_graphics/fortune_room_data.inc","src_graphics/fortune_dialog_data.inc")) + "\n.data\n" + npc
|
||||
own_data = set(re.findall(r"^(\w+) (?:dd|db|AnimPack3D|CityProp3D)\b",data,re.M))
|
||||
"src_graphics/fortune_room_data.inc","src_graphics/fortune_dialog_data.inc",
|
||||
"src_graphics/observer_data.inc")) + "\n.data\n" + npc
|
||||
darts = read("src_graphics/darts_data.inc").replace(
|
||||
"include src_graphics\\darts_art.inc", read("src_graphics/darts_art.inc"))
|
||||
data += "\n" + re.sub(r"^\w+ PROTO[^\n]*\n", "", darts, flags=re.M)
|
||||
own_data = set(re.findall(r"^(\w+) (?:dd|db|dw|REAL4|REAL8|AnimPack3D|CityProp3D)\b",data,re.M))
|
||||
own_data.update(re.findall(r"^(\w+) LABEL\b",data,re.M))
|
||||
coff = Coff(ROOT / "build/graphics/poker.obj")
|
||||
helper = bytearray(coff.data)
|
||||
at = struct.unpack_from("<I",helper,8)[0]
|
||||
|
|
@ -80,6 +94,8 @@ def prepare():
|
|||
types[name] = "CitySpriteCrop3D"
|
||||
for name,kind in re.findall(r"^(\w+)\s+(db|dw|dd)\b",all_source,re.M|re.I):
|
||||
types.setdefault(name,{"db":"BYTE","dw":"WORD","dd":"DWORD"}[kind.lower()])
|
||||
for name,kind in re.findall(r"^(\w+)\s+(REAL4|REAL8)\b",all_source,re.M|re.I):
|
||||
types[name] = kind.upper()
|
||||
for index,name in coff.symbols.items():
|
||||
pos = at+index*18
|
||||
_,sec,kind,storage,_ = struct.unpack_from("<IhHBB",helper,pos+8)
|
||||
|
|
@ -135,7 +151,6 @@ def main():
|
|||
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")
|
||||
parser.add_argument("--with-fortune-ai",action="store_true")
|
||||
args = parser.parse_args()
|
||||
if args.parity:
|
||||
native,shipping = Coff(OUT/"module.obj"),Coff(ROOT/"build/graphics/poker.obj")
|
||||
|
|
@ -151,10 +166,6 @@ def main():
|
|||
assert "DrawCityObject3D" not in room, "tarot furniture must not face the camera"
|
||||
assert room.count("DrawFortuneFixedProp3D") == 24
|
||||
stem = "module"
|
||||
if args.with_fortune_ai:
|
||||
stem = "module-live-ai"
|
||||
source = (OUT/"module.asm").read_text(encoding="cp1251")
|
||||
(OUT/(stem+".asm")).write_text("CASINO_FORTUNE_AI_LIVE equ 1\n" + source,encoding="cp1251")
|
||||
if args.prove_bar_camera_regression:
|
||||
stem = "module-camera-regression"
|
||||
source = (OUT/"module.asm").read_text(encoding="cp1251")
|
||||
|
|
@ -224,6 +235,31 @@ def main():
|
|||
assert blue > 45000, (name, "chamber must use its own blue palette", blue)
|
||||
modal = Image.open(OUT/"fortune-unavailable.bmp").convert("RGB").crop((20,65,620,100))
|
||||
assert len(modal.getcolors(600*35)) >= 2, "native fortune dialogue status must be drawn"
|
||||
# These are frames rendered by the actual x86 procedures, not a Python scene simulation.
|
||||
motion = [Image.open(OUT/f"fortune-session-motion-{i:02}.bmp").convert("RGB") for i in range(64)]
|
||||
assert len({im.crop((134,32,414,282)).tobytes() for im in motion}) >= 48
|
||||
motion[0].save(OUT/"fortune-session-shuffle.gif",save_all=True,
|
||||
append_images=motion[1:],duration=25,loop=0)
|
||||
stages = ("deal-one","deal-two","deal-three","flip-one","explain-one",
|
||||
"flip-two","explain-two","flip-three","explain-three","summary")
|
||||
frames = [Image.open(OUT/f"fortune-session-{s}.bmp").convert("RGB") for s in stages]
|
||||
assert len({im.crop((190,285,475,398)).tobytes() for im in frames}) >= 7
|
||||
question = frames[0].crop((18,400,620,444)).tobytes()
|
||||
assert all(im.crop((18,400,620,444)).tobytes() == question for im in frames)
|
||||
for name in ("room","shuffle","deal","gesture","orb-hands","orb-mist","cards"):
|
||||
path = ROOT/f"assets/animations/fortune/{name}-v1.bin"
|
||||
data = path.read_bytes()
|
||||
magic,w,h,count,size = struct.unpack_from("<4s4I",data)
|
||||
assert magic == b"ANM1" and len(data) == size+20 and size == w*h*4*count
|
||||
assert count <= 64 and size <= 32*1024*1024
|
||||
if count > 1:
|
||||
pixels = [data[20+i*w*h*4:20+(i+1)*w*h*4] for i in range(count)]
|
||||
assert len(set(pixels)) >= count-3, (name,"animation frames must differ")
|
||||
if name == "cards":
|
||||
for i in range(22):
|
||||
upright = Image.frombytes("RGBA",(w,h),pixels[i],"raw","BGRA")
|
||||
reversed_card = Image.frombytes("RGBA",(w,h),pixels[i+22],"raw","BGRA")
|
||||
assert upright.transpose(Image.Transpose.ROTATE_180).tobytes() == reversed_card.tobytes()
|
||||
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
|
||||
|
|
@ -308,8 +344,6 @@ def main():
|
|||
x0,y0,x1,y1 = sprite.getbbox()
|
||||
assert 0 < x0 < x1 < w and 0 < y0 < y1 < h, "uncropped musician cutout"
|
||||
print("PASS: native stairs/doorways, automatic door, tickets, gait, portal, secret passage, bar camera and fortune dialogue")
|
||||
if args.with_fortune_ai:
|
||||
print("PASS: actual x86 game input/IPC/rendering with the x64 local model, response and child cleanup")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
|
|
|||
148
tools/tests/check_observer.py
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
"""Compile fresh native spectator/game code and run autonomous matches, not mock results."""
|
||||
from pathlib import Path
|
||||
import argparse
|
||||
import hashlib
|
||||
import re
|
||||
import struct
|
||||
import subprocess
|
||||
from PIL import Image
|
||||
from check_card_exit_build import Coff
|
||||
from check_terminals import signature
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
OUT = ROOT / "build/observer"
|
||||
|
||||
|
||||
def read(path):
|
||||
raw = (ROOT / path).read_bytes()
|
||||
try:
|
||||
return raw.decode("utf8").replace("\r\n", "\n")
|
||||
except UnicodeDecodeError:
|
||||
return raw.decode("cp1251").replace("\r\n", "\n")
|
||||
|
||||
|
||||
def proc(text, name):
|
||||
return re.search(rf"^{name} PROC\b.*?^{name} ENDP", text, re.M | re.S).group()
|
||||
|
||||
|
||||
def expand(path):
|
||||
return re.sub(r"^include ([^\n]+)$", lambda m: expand(m[1].strip().replace("\\", "/")),
|
||||
read(path), flags=re.M)
|
||||
|
||||
|
||||
def prepare():
|
||||
OUT.mkdir(parents=True, exist_ok=True)
|
||||
code = "\n".join(expand("src_graphics/" + name + ".inc") for name in
|
||||
("observer", "native_poker", "native_durak", "native_blackjack", "pyramid_play", "eightball",
|
||||
"card_tables_render", "durak_render"))
|
||||
for path, names in (("platform_code", ("SaveProgress3D",)),
|
||||
("npc_contracts", ("NpcRecordRound3D",)),
|
||||
("charms", ("TriggerCharmLossProtection",)),
|
||||
("card_table_input", ("HandleCardExit3D", "RequestCardExit3D", "LeaveCardTable3D", "TableBusy3D"))):
|
||||
code += "\n" + "\n".join(proc(read("src_graphics/" + path + ".inc"), name) for name in names)
|
||||
code += "\n" + proc(read("tools/tests/casino_actions_test.inc"), "WriteActionSnapshot3D")
|
||||
code += "\n" + read("tools/tests/durak_test.inc")
|
||||
code += "\nPlayCasinoSound3D PROC arg0:DWORD,arg1:DWORD,arg2:DWORD\n ret\nPlayCasinoSound3D ENDP\n"
|
||||
# Stop before any persistence side effect even if a tested observer barrier regresses.
|
||||
for name in ("RefreshTerminalAchievements3D", "NpcEnsureJournal3D"):
|
||||
code += f"\n{name} PROC\n invoke ExitProcess,8402\n ret\n{name} ENDP\n"
|
||||
code += "\n" + read("tools/tests/observer_test.inc")
|
||||
code = code.replace(":SDWORD", ":DWORD")
|
||||
data = read("src_graphics/observer_data.inc")
|
||||
data += "\n.data\nactionBmpHeader db 42h,4Dh\n dd 1228854\n dw 0,0\n dd 54,40,640,-480\n dw 1,32\n dd 0,1228800,0,0,0,0\n"
|
||||
own_funcs = set(re.findall(r"^(\w+) PROC", code, re.M))
|
||||
own_data = set(re.findall(r"^(\w+)\s+(?:db|dd)\b", data + "\n" + code, re.M))
|
||||
source = "\n".join(read(str(p.relative_to(ROOT))) for p in
|
||||
[ROOT / "poker.asm", ROOT / "casino3d.inc", ROOT / "casino3d_data.inc",
|
||||
*sorted(p for p in (ROOT / "src_graphics").glob("*.inc") if p.name != "renderer.inc")])
|
||||
structures = [re.search(rf"^{name} STRUCT\b.*?^{name} ENDS", source, re.M | re.S).group()
|
||||
for name in ("AnimPack3D", "BJBot3D")]
|
||||
types = {name: kind for name, kind in re.findall(r"^(\w+)\s+(AnimPack3D|BJBot3D)\b", source, re.M)}
|
||||
types.update({name: {"db": "BYTE", "dw": "WORD", "dd": "DWORD"}[kind.lower()]
|
||||
for name, kind in re.findall(r"^(\w+)\s+(db|dw|dd)\b", source, re.M | re.I)})
|
||||
types.update({name: kind.upper() for name, kind in
|
||||
re.findall(r"^(\w+)\s+(REAL4|REAL8)\b", source, re.M | re.I)})
|
||||
coff = Coff(ROOT / "build/graphics/poker.obj")
|
||||
helper = bytearray(coff.data)
|
||||
symbol_at = struct.unpack_from("<I", helper, 8)[0]
|
||||
externs, protos = [], {}
|
||||
for index, name in coff.symbols.items():
|
||||
pos = symbol_at + index * 18
|
||||
_, section, kind, storage, _ = struct.unpack_from("<IhHBB", helper, pos + 8)
|
||||
if section > 0 and re.fullmatch(r"[A-Za-z_]\w*", name) and storage in (2, 3):
|
||||
secname = helper[20+(section-1)*40:28+(section-1)*40].rstrip(b"\0")
|
||||
if secname in (b".data", b".bss", b".const"):
|
||||
helper[pos+16] = 3 if name in own_data else 2
|
||||
if name not in own_data:
|
||||
externs.append(f"EXTERN {name}:{types.get(name, 'DWORD')}")
|
||||
match = re.fullmatch(r"_(\w+)@(\d+)", name)
|
||||
if match and kind == 0x20 and section > 0:
|
||||
helper[pos+16] = 3 if match[1] in own_funcs else 2
|
||||
if match[1] not in own_funcs:
|
||||
protos[match[1]] = int(match[2]) // 4
|
||||
for name, params in re.findall(r"^(\w+)\s+PROTO([^\n]*)", source, re.M):
|
||||
protos.setdefault(name, params.count(":DWORD"))
|
||||
for name in own_funcs:
|
||||
protos[name] = len(re.findall(r":S?DWORD", proc(code, name).splitlines()[0]))
|
||||
constants = dict(re.findall(r"^(\w+)\s+equ\s+([^;\n]+)", source, re.M))
|
||||
used = set(re.findall(r"^(\w+) equ", data, re.M))
|
||||
ordered = []
|
||||
def constant(name):
|
||||
if name not in constants or name in used:
|
||||
return
|
||||
used.add(name)
|
||||
for dep in re.findall(r"\b[A-Za-z_]\w*", constants[name]):
|
||||
constant(dep)
|
||||
ordered.append(f"{name} equ {constants[name]}")
|
||||
for name in re.findall(r"\b[A-Za-z_]\w*", code + data):
|
||||
constant(name)
|
||||
header = ".386\n.model flat\noption casemap:none\n" + "\n".join(structures + ordered + externs)
|
||||
header += "\noption language:stdcall\n" + "\n".join(
|
||||
name + " PROTO" + (" " + ",".join([":DWORD"]*count) if count else "") for name, count in protos.items())
|
||||
data = re.sub(r"^\w+ PROTO[^\n]*\n", "", data, flags=re.M)
|
||||
(OUT / "helpers.obj").write_bytes(helper)
|
||||
(OUT / "module.asm").write_text(header + "\n" + data + "\n.code\n" + code + "\nend ObserverTestStart\n", encoding="cp1251")
|
||||
return {name for name in own_funcs if not name.startswith("DurakTest") and name != "RunDurakTest3D"} - {
|
||||
"PlayCasinoSound3D", "WriteActionSnapshot3D", "ObserverTestStart",
|
||||
"RefreshTerminalAchievements3D", "NpcEnsureJournal3D"}
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--parity", action="store_true")
|
||||
args = parser.parse_args()
|
||||
functions = prepare()
|
||||
if args.parity:
|
||||
tested, shipping = Coff(OUT / "module.obj"), Coff(ROOT / "build/graphics/poker.obj")
|
||||
for name in functions:
|
||||
symbol = next(key for key in tested.functions if key.startswith("_" + name + "@"))
|
||||
assert signature(tested, symbol, tail_branches=True) == signature(shipping, symbol, tail_branches=True), name
|
||||
print("PASS: native observer and game code match the shipping object")
|
||||
return
|
||||
masm = Path("F:/MASM32/masm32")
|
||||
subprocess.run([str(masm / "bin/ml.exe"), "/c", "/coff", "/Cp", "/nologo", "/I.",
|
||||
"/Fobuild/observer/module.obj", "build/observer/module.asm"], cwd=ROOT, check=True)
|
||||
sdk = sorted(Path("C:/Program Files (x86)/Windows Kits/10/Lib").glob("*/um/x86"))[-1]
|
||||
subprocess.run([str(masm / "bin/link.exe"), "/nologo", "/SUBSYSTEM:CONSOLE", "/ENTRY:ObserverTestStart@0",
|
||||
f"/LIBPATH:{sdk}", f"/LIBPATH:{masm/'lib'}", "/OUT:build/observer/module.exe",
|
||||
"build/observer/module.obj", "build/observer/helpers.obj", "kernel32.lib", "user32.lib",
|
||||
"gdi32.lib", "winmm.lib", "ws2_32.lib"], cwd=ROOT, check=True)
|
||||
def profile():
|
||||
return {str(p): hashlib.sha256(p.read_bytes()).digest() for p in (ROOT / "runtime").rglob("*")
|
||||
if p.is_file()}
|
||||
before = profile()
|
||||
run = subprocess.run([str(OUT / "module.exe")], cwd=ROOT, timeout=180)
|
||||
assert run.returncode == 0, f"Native observer assertion: {run.returncode}"
|
||||
assert profile() == before, "Observation changed persistent profile files"
|
||||
sheet = Image.new("RGB", (1280, 480*4))
|
||||
for i, name in enumerate(("menu", "poker", "blackjack", "durak", "transfer", "pyramid", "eight")):
|
||||
frame = Image.open(OUT / (name + ".bmp")).convert("RGB")
|
||||
assert len(frame.getcolors(640*480)) >= (4 if name == "menu" else 500), name
|
||||
frame.save(OUT / (name + ".png"))
|
||||
sheet.paste(frame, ((i % 2)*640, (i // 2)*480))
|
||||
sheet.save(OUT / "contact.png")
|
||||
print("PASS: 80 autonomous card matches, both billiard seats, pause, profile restore and no persistent writes")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
42
tools/tests/darts_hall_test.inc
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
.data
|
||||
dtHallSnapshot db "build\hall-expansion\darts-hall.bmp",0
|
||||
.code
|
||||
ExpansionDartsTests PROC
|
||||
mov expansionStage,8510
|
||||
mov hallLevel3D,0
|
||||
mov engineMode,MODE_WALK
|
||||
mov worldMap3D,OFFSET casinoMap
|
||||
mov playerX,29*FP_1
|
||||
mov playerY,28*FP_1
|
||||
mov playerAngle,0
|
||||
invoke CanWalk3D,playerX,playerY
|
||||
test eax,eax
|
||||
jz dt_hall_fail
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
invoke ExpansionSnapshot,OFFSET dtHallSnapshot
|
||||
call CheckHallInteractions3D
|
||||
cmp interactTargetId,INTERACT_DARTS
|
||||
jne dt_hall_fail
|
||||
call TriggerHallInteraction3D
|
||||
test eax,eax
|
||||
jz dt_hall_fail
|
||||
cmp engineMode,MODE_DARTS
|
||||
jne dt_hall_fail
|
||||
cmp dtError3D,0
|
||||
jne dt_hall_fail
|
||||
cmp dtScores3D,500
|
||||
jne dt_hall_fail
|
||||
cmp dtScores3D[4],500
|
||||
jne dt_hall_fail
|
||||
cmp playerX,29*FP_1
|
||||
jne dt_hall_fail
|
||||
cmp playerY,28*FP_1
|
||||
jne dt_hall_fail
|
||||
call FreeDarts3D
|
||||
mov engineMode,MODE_WALK
|
||||
ret
|
||||
dt_hall_fail:
|
||||
invoke ExitProcess,8510
|
||||
ret
|
||||
ExpansionDartsTests ENDP
|
||||
374
tools/tests/darts_test.inc
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
.data
|
||||
dtVectors3D LABEL DWORD
|
||||
dd 0,53,20,1
|
||||
dd 0,103,60,3
|
||||
dd 0,166,40,2
|
||||
dd 16,50,1,1
|
||||
dd 32,98,3,3
|
||||
dd 51,158,2,2
|
||||
dd 31,43,18,1
|
||||
dd 61,83,54,3
|
||||
dd 98,134,36,2
|
||||
dd 43,31,4,1
|
||||
dd 83,61,12,3
|
||||
dd 134,98,8,2
|
||||
dd 50,16,13,1
|
||||
dd 98,32,39,3
|
||||
dd 158,51,26,2
|
||||
dd 53,0,6,1
|
||||
dd 103,0,18,3
|
||||
dd 166,0,12,2
|
||||
dd 50,-16,10,1
|
||||
dd 98,-32,30,3
|
||||
dd 158,-51,20,2
|
||||
dd 43,-31,15,1
|
||||
dd 83,-61,45,3
|
||||
dd 134,-98,30,2
|
||||
dd 31,-43,2,1
|
||||
dd 61,-83,6,3
|
||||
dd 98,-134,4,2
|
||||
dd 16,-50,17,1
|
||||
dd 32,-98,51,3
|
||||
dd 51,-158,34,2
|
||||
dd 0,-53,3,1
|
||||
dd 0,-103,9,3
|
||||
dd 0,-166,6,2
|
||||
dd -16,-50,19,1
|
||||
dd -32,-98,57,3
|
||||
dd -51,-158,38,2
|
||||
dd -31,-43,7,1
|
||||
dd -61,-83,21,3
|
||||
dd -98,-134,14,2
|
||||
dd -43,-31,16,1
|
||||
dd -83,-61,48,3
|
||||
dd -134,-98,32,2
|
||||
dd -50,-16,8,1
|
||||
dd -98,-32,24,3
|
||||
dd -158,-51,16,2
|
||||
dd -53,0,11,1
|
||||
dd -103,0,33,3
|
||||
dd -166,0,22,2
|
||||
dd -50,16,14,1
|
||||
dd -98,32,42,3
|
||||
dd -158,51,28,2
|
||||
dd -43,31,9,1
|
||||
dd -83,61,27,3
|
||||
dd -134,98,18,2
|
||||
dd -31,43,12,1
|
||||
dd -61,83,36,3
|
||||
dd -98,134,24,2
|
||||
dd -16,50,5,1
|
||||
dd -32,98,15,3
|
||||
dd -51,158,10,2
|
||||
dd 0,0,50,2
|
||||
dd 0,6,50,2
|
||||
dd 0,10,25,1
|
||||
dd 0,16,20,1
|
||||
dd 0,190,0,0
|
||||
dd 2147483647,0,0,0
|
||||
dd -2147483648,0,0,0
|
||||
dtVectorCount3D equ 67
|
||||
dtTestTriples3D dd 0
|
||||
dtTestMetricPath db "build\darts\aim-metrics.bin",0
|
||||
.code
|
||||
DartsTestWriteMetrics PROC uses ebx
|
||||
local written:DWORD
|
||||
invoke CreateFileA,OFFSET dtTestMetricPath,40000000h,0,0,2,80h,0
|
||||
cmp eax,-1
|
||||
je dt_test_metric_fail
|
||||
mov ebx,eax
|
||||
invoke WriteFile,ebx,OFFSET dtTestTriples3D,4,ADDR written,0
|
||||
invoke CloseHandle,ebx
|
||||
ret
|
||||
dt_test_metric_fail:
|
||||
invoke ExitProcess,8505
|
||||
ret
|
||||
DartsTestWriteMetrics ENDP
|
||||
|
||||
DartsTestStart PROC uses ebx esi edi
|
||||
local legs:DWORD
|
||||
call InitPaths3D
|
||||
lea esi,dtVectors3D
|
||||
mov ebx,dtVectorCount3D
|
||||
dt_test_vector:
|
||||
invoke DartsScore3D,[esi],[esi+4]
|
||||
cmp eax,[esi+8]
|
||||
jne dt_test_fail
|
||||
cmp edx,[esi+12]
|
||||
jne dt_test_fail
|
||||
add esi,16
|
||||
dec ebx
|
||||
jnz dt_test_vector
|
||||
mov playerX,123456
|
||||
mov playerY,789012
|
||||
mov bankroll,12345
|
||||
call ResetDarts3D
|
||||
mov dtCursorX3D,320*256
|
||||
mov dtCursorY3D,214*256
|
||||
mov dtClock3D,384
|
||||
mov dtHolding3D,1
|
||||
mov dtHoldTime3D,0
|
||||
call DartsAim3D
|
||||
cmp dtAimX3D,10
|
||||
jl dt_test_fail
|
||||
cmp dtAimX3D,13
|
||||
jg dt_test_fail
|
||||
mov ebx,dtAimX3D
|
||||
mov dtHoldTime3D,4000
|
||||
call DartsAim3D
|
||||
mov eax,dtAimX3D
|
||||
sub eax,ebx
|
||||
cmp eax,8
|
||||
jne dt_test_fail
|
||||
; Fine mouse motion must survive the hall's coarser 320px UI coordinates.
|
||||
mov engineMode,MODE_DARTS
|
||||
mov viewWidth3D,2560
|
||||
mov viewHeight3D,1440
|
||||
mov viewLeft3D,0
|
||||
mov viewTop3D,0
|
||||
invoke WindowEvents3D,0,200h,0,02820500h
|
||||
call DartsAim3D
|
||||
mov ebx,dtAimX3D
|
||||
invoke WindowEvents3D,0,200h,0,02820502h
|
||||
call DartsAim3D
|
||||
cmp dtAimX3D,ebx
|
||||
jle dt_test_fail
|
||||
cmp mouseX3D,160
|
||||
jne dt_test_fail
|
||||
mov ebx,dtAimY3D
|
||||
invoke WindowEvents3D,0,200h,0,02840502h
|
||||
call DartsAim3D
|
||||
cmp dtAimY3D,ebx
|
||||
jge dt_test_fail
|
||||
cmp mouseY3D,107
|
||||
jne dt_test_fail
|
||||
call ResetDarts3D
|
||||
call DartsCamera3D
|
||||
invoke DartsProject3D,-248,0,DT_WALL
|
||||
mov ebx,eax
|
||||
invoke DartsProject3D,248,0,DT_WALL
|
||||
sub eax,ebx
|
||||
cmp eax,170
|
||||
jl dt_test_fail
|
||||
cmp eax,180
|
||||
jg dt_test_fail
|
||||
; Camping at the centre of T20 no longer guarantees a triple every time.
|
||||
mov dtCursorX3D,320*256
|
||||
mov dtCursorY3D,214*256-103*DT_FOCAL*256/DT_WALL
|
||||
mov dtClock3D,0
|
||||
mov dtTestTriples3D,0
|
||||
mov edi,100
|
||||
dt_test_sway_samples:
|
||||
call DartsAim3D
|
||||
invoke DartsScore3D,dtAimX3D,dtAimY3D
|
||||
cmp eax,60
|
||||
jne @F
|
||||
inc dtTestTriples3D
|
||||
@@:
|
||||
add dtClock3D,16
|
||||
dec edi
|
||||
jnz dt_test_sway_samples
|
||||
cmp dtTestTriples3D,10
|
||||
jb dt_test_fail
|
||||
cmp dtTestTriples3D,60
|
||||
ja dt_test_fail
|
||||
call DartsTestWriteMetrics
|
||||
mov dtState3D,DT_FLIGHT
|
||||
mov dtTimer3D,400
|
||||
mov deltaTime3D,16
|
||||
mov pressedKeys3D[9],1
|
||||
call HandleDarts3D
|
||||
cmp dtScoreOpen3D,1
|
||||
jne dt_test_fail
|
||||
cmp dtTimer3D,400
|
||||
jne dt_test_fail
|
||||
mov pressedKeys3D[9],1
|
||||
call HandleDarts3D
|
||||
cmp dtScoreOpen3D,0
|
||||
jne dt_test_fail
|
||||
cmp dtTimer3D,416
|
||||
jne dt_test_fail
|
||||
call ResetDarts3D
|
||||
mov dtHitX3D,0
|
||||
mov dtHitY3D,103
|
||||
mov dtRemaining3D,59
|
||||
mov dtVisitStart3D,100
|
||||
mov dtState3D,DT_FLIGHT
|
||||
call SettleDarts3D
|
||||
cmp dtRemaining3D,100
|
||||
jne dt_test_fail
|
||||
cmp dtBust3D,1
|
||||
jne dt_test_fail
|
||||
cmp dtCount3D,1
|
||||
jne dt_test_fail
|
||||
call SettleDarts3D
|
||||
cmp dtCount3D,1
|
||||
jne dt_test_fail
|
||||
call ResetDarts3D
|
||||
mov dtHitX3D,0
|
||||
mov dtHitY3D,166
|
||||
mov dtRemaining3D,40
|
||||
mov dtVisitStart3D,40
|
||||
mov dtState3D,DT_FLIGHT
|
||||
call SettleDarts3D
|
||||
cmp dtRemaining3D,0
|
||||
jne dt_test_fail
|
||||
mov dtTimer3D,1300
|
||||
mov deltaTime3D,16
|
||||
call HandleDarts3D
|
||||
cmp dtState3D,DT_WIN
|
||||
jne dt_test_fail
|
||||
call ResetDarts3D
|
||||
mov dtHitY3D,166
|
||||
mov dtRemaining3D,41
|
||||
mov dtVisitStart3D,41
|
||||
mov dtState3D,DT_FLIGHT
|
||||
call SettleDarts3D
|
||||
cmp dtRemaining3D,1
|
||||
jne dt_test_fail
|
||||
cmp dtBust3D,0
|
||||
jne dt_test_fail
|
||||
mov dtCount3D,3
|
||||
mov dtState3D,DT_RETURN
|
||||
mov dtTimer3D,750
|
||||
call HandleDarts3D
|
||||
cmp dtCount3D,0
|
||||
jne dt_test_fail
|
||||
cmp dtVisit3D,2
|
||||
jne dt_test_fail
|
||||
cmp dtActor3D,1
|
||||
jne dt_test_fail
|
||||
cmp dtRemaining3D,500
|
||||
jne dt_test_fail
|
||||
cmp dtScores3D,1
|
||||
jne dt_test_fail
|
||||
cmp dtScores3D[4],500
|
||||
jne dt_test_fail
|
||||
call ResetDarts3D
|
||||
mov engineMode,MODE_DARTS
|
||||
mov viewWidth3D,640
|
||||
mov viewHeight3D,480
|
||||
mov viewLeft3D,0
|
||||
mov viewTop3D,0
|
||||
mov mouseClick3D,0
|
||||
invoke WindowEvents3D,0,200h,0,00D60140h
|
||||
cmp mouseClick3D,0
|
||||
jne dt_test_fail
|
||||
invoke WindowEvents3D,0,201h,1,00D60140h
|
||||
call HandleDarts3D
|
||||
cmp dtState3D,DT_AIM
|
||||
jne dt_test_fail
|
||||
cmp dtHolding3D,1
|
||||
jne dt_test_fail
|
||||
invoke WindowEvents3D,0,202h,0,00D60140h
|
||||
call HandleDarts3D
|
||||
cmp dtState3D,DT_RELEASE
|
||||
jne dt_test_fail
|
||||
cmp dtThrows3D,1
|
||||
jne dt_test_fail
|
||||
mov ebx,dtHitX3D
|
||||
mov esi,dtHitY3D
|
||||
invoke WindowEvents3D,0,200h,0,00640064h
|
||||
call HandleDarts3D
|
||||
cmp dtHitX3D,ebx
|
||||
jne dt_test_fail
|
||||
cmp dtHitY3D,esi
|
||||
jne dt_test_fail
|
||||
call ResetDarts3D
|
||||
mov dtHitX3D,80
|
||||
mov dtHitY3D,103
|
||||
mov dtState3D,DT_FLIGHT
|
||||
mov dtTimer3D,0
|
||||
call DartsCamera3D
|
||||
cmp dtCamZ3D,0
|
||||
jne dt_test_fail
|
||||
mov dtTimer3D,600
|
||||
call DartsCamera3D
|
||||
cmp dtCamZ3D,DT_WALL/2-160
|
||||
jne dt_test_fail
|
||||
cmp dtYawSin3D,50000
|
||||
jl dt_test_fail
|
||||
mov dtTimer3D,1200
|
||||
call DartsCamera3D
|
||||
cmp dtCamZ3D,DT_WALL-420
|
||||
jne dt_test_fail
|
||||
invoke DartsProject3D,80,103,DT_WALL
|
||||
cmp eax,320
|
||||
jl dt_test_fail
|
||||
cmp eax,420
|
||||
jg dt_test_fail
|
||||
cmp edx,120
|
||||
jl dt_test_fail
|
||||
cmp edx,214
|
||||
jg dt_test_fail
|
||||
cmp dtYawSin3D,12000
|
||||
jg dt_test_fail
|
||||
; Actual bot input ignores the cursor and automatically releases a dart.
|
||||
call ResetDarts3D
|
||||
mov dtActor3D,1
|
||||
mov mouseX3D,0
|
||||
mov mouseY3D,0
|
||||
mov deltaTime3D,1600
|
||||
call HandleDarts3D
|
||||
cmp dtState3D,DT_RELEASE
|
||||
jne dt_test_fail
|
||||
cmp dtThrows3D,1
|
||||
jne dt_test_fail
|
||||
cmp dtScores3D,500
|
||||
jne dt_test_fail
|
||||
; Complete a real match through normal phase/visit transitions.
|
||||
mov legs,20
|
||||
dt_test_another_match:
|
||||
call ResetDarts3D
|
||||
mov edi,5000
|
||||
dt_test_match:
|
||||
cmp dtState3D,DT_WIN
|
||||
je dt_test_match_done
|
||||
cmp dtState3D,DT_AIM
|
||||
jne dt_test_match_tick
|
||||
cmp dtActor3D,0
|
||||
jne dt_test_match_tick
|
||||
call DartsBotAim3D
|
||||
mov eax,dtBotAimX3D
|
||||
mov dtAimX3D,eax
|
||||
mov eax,dtBotAimY3D
|
||||
mov dtAimY3D,eax
|
||||
call ThrowDarts3D
|
||||
dt_test_match_tick:
|
||||
mov deltaTime3D,1400
|
||||
call HandleDarts3D
|
||||
cmp dtScores3D,500
|
||||
ja dt_test_fail
|
||||
cmp dtScores3D[4],500
|
||||
ja dt_test_fail
|
||||
dec edi
|
||||
jnz dt_test_match
|
||||
jmp dt_test_fail
|
||||
dt_test_match_done:
|
||||
mov eax,dtWinner3D
|
||||
cmp dtScores3D[eax*4],0
|
||||
jne dt_test_fail
|
||||
dec legs
|
||||
jnz dt_test_another_match
|
||||
cmp playerX,123456
|
||||
jne dt_test_fail
|
||||
cmp playerY,789012
|
||||
jne dt_test_fail
|
||||
cmp bankroll,12345
|
||||
jne dt_test_fail
|
||||
call LoadDarts3D
|
||||
test eax,eax
|
||||
jz dt_test_assets
|
||||
call DartsTestFrames
|
||||
call FreeDarts3D
|
||||
xor eax,eax
|
||||
jmp dt_test_exit
|
||||
dt_test_assets:
|
||||
mov eax,8502
|
||||
jmp dt_test_exit
|
||||
dt_test_fail:
|
||||
mov eax,8501
|
||||
dt_test_exit:
|
||||
invoke ExitProcess,eax
|
||||
DartsTestStart ENDP
|
||||
|
||||
64
tools/tests/darts_ui.inc
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
; Interactive fixture reuses the actual native scene, window events and presenter.
|
||||
.code
|
||||
DartsUiStart PROC
|
||||
call InitPaths3D
|
||||
call Create3DWindow
|
||||
test eax,eax
|
||||
jz dt_ui_fail
|
||||
invoke SetWindowLongA,h3DWnd,-4,OFFSET WindowEvents3D
|
||||
call ToggleFullscreen3D
|
||||
call BeginDarts3D
|
||||
call RenderDarts3D
|
||||
invoke ShowWindow,h3DWnd,1
|
||||
call Present3D
|
||||
invoke SetForegroundWindow,h3DWnd
|
||||
invoke GetTickCount
|
||||
mov previousTick3D,eax
|
||||
dt_ui_loop:
|
||||
invoke PeekMessageA,OFFSET msg3D,0,0,0,1
|
||||
test eax,eax
|
||||
jz dt_ui_frame
|
||||
cmp dword ptr msg3D[4],12h
|
||||
je dt_ui_close
|
||||
invoke TranslateMessage,OFFSET msg3D
|
||||
invoke DispatchMessageA,OFFSET msg3D
|
||||
jmp dt_ui_loop
|
||||
dt_ui_frame:
|
||||
invoke GetTickCount
|
||||
mov frameStart3D,eax
|
||||
sub eax,previousTick3D
|
||||
cmp eax,50
|
||||
jbe @F
|
||||
mov eax,50
|
||||
@@:
|
||||
mov deltaTime3D,eax
|
||||
mov eax,frameStart3D
|
||||
mov previousTick3D,eax
|
||||
invoke GetForegroundWindow
|
||||
cmp eax,h3DWnd
|
||||
jne dt_ui_idle
|
||||
invoke Pressed3D,1Bh
|
||||
test eax,eax
|
||||
jnz dt_ui_close
|
||||
invoke Pressed3D,7Ah
|
||||
test eax,eax
|
||||
jz @F
|
||||
call ToggleFullscreen3D
|
||||
@@:
|
||||
call HandleDarts3D
|
||||
call RenderDarts3D
|
||||
call Present3D
|
||||
dt_ui_idle:
|
||||
invoke Sleep,16
|
||||
jmp dt_ui_loop
|
||||
dt_ui_close:
|
||||
call FreeDarts3D
|
||||
invoke ReleaseDC,h3DWnd,h3DDC
|
||||
invoke DestroyWindow,h3DWnd
|
||||
dt_ui_exit:
|
||||
invoke ExitProcess,0
|
||||
ret
|
||||
dt_ui_fail:
|
||||
invoke ExitProcess,8503
|
||||
ret
|
||||
DartsUiStart ENDP
|
||||
|
|
@ -1,112 +1,243 @@
|
|||
.data
|
||||
fortuneLiveQuestion db 209,242,238,232,242,32,235,232,32,237,224,247,224,242,252,32,243,247,232,242,252,241,255,32,240,232,241,238,226,224,237,232,254,63,0
|
||||
fortuneLiveShot db "build\hall-expansion\fortune-dialog-answer.bmp",0
|
||||
fortuneLiveBank dd 0
|
||||
fortuneSessionBank dd 0
|
||||
fortuneSessionPlayer dd 0
|
||||
fortuneSessionMotion db "build\hall-expansion\fortune-session-motion-"
|
||||
fortuneSessionDigits db "00.bmp",0
|
||||
fortuneSessionQuestion db 193,243,228,229,242,32,235,232,32,231,224,226,242,240,224,32,228,238,230,228,252,63,0
|
||||
fortuneSessionShot0 db "build\hall-expansion\fortune-session-input.bmp",0
|
||||
fortuneSessionShot1 db "build\hall-expansion\fortune-session-shuffle.bmp",0
|
||||
fortuneSessionShot2 db "build\hall-expansion\fortune-session-deal-one.bmp",0
|
||||
fortuneSessionShot3 db "build\hall-expansion\fortune-session-deal-two.bmp",0
|
||||
fortuneSessionShot4 db "build\hall-expansion\fortune-session-deal-three.bmp",0
|
||||
fortuneSessionShot5 db "build\hall-expansion\fortune-session-flip-one.bmp",0
|
||||
fortuneSessionShot6 db "build\hall-expansion\fortune-session-explain-one.bmp",0
|
||||
fortuneSessionShot7 db "build\hall-expansion\fortune-session-flip-two.bmp",0
|
||||
fortuneSessionShot8 db "build\hall-expansion\fortune-session-explain-two.bmp",0
|
||||
fortuneSessionShot9 db "build\hall-expansion\fortune-session-flip-three.bmp",0
|
||||
fortuneSessionShot10 db "build\hall-expansion\fortune-session-explain-three.bmp",0
|
||||
fortuneSessionShot11 db "build\hall-expansion\fortune-session-summary.bmp",0
|
||||
fortuneSessionShot12 db "build\hall-expansion\fortune-session-done.bmp",0
|
||||
fortuneSessionShot13 db "build\hall-expansion\fortune-session-orb-rise.bmp",0
|
||||
fortuneSessionShot14 db "build\hall-expansion\fortune-session-orb-mist.bmp",0
|
||||
fortuneSessionShot15 db "build\hall-expansion\fortune-session-orb-final.bmp",0
|
||||
fortuneSessionShots dd OFFSET fortuneSessionShot0,OFFSET fortuneSessionShot1,OFFSET fortuneSessionShot2,OFFSET fortuneSessionShot3
|
||||
dd OFFSET fortuneSessionShot4,OFFSET fortuneSessionShot5,OFFSET fortuneSessionShot6,OFFSET fortuneSessionShot7
|
||||
dd OFFSET fortuneSessionShot8,OFFSET fortuneSessionShot9,OFFSET fortuneSessionShot10,OFFSET fortuneSessionShot11
|
||||
dd OFFSET fortuneSessionShot12
|
||||
.code
|
||||
FortuneDialogNativeTests PROC uses esi edi
|
||||
FortuneSessionStepTest PROC
|
||||
add animationClock3D,100
|
||||
call UpdateFortuneSession3D
|
||||
ret
|
||||
FortuneSessionStepTest ENDP
|
||||
FortuneDialogNativeTests PROC uses ebx esi edi
|
||||
mov expansionStage,80
|
||||
mov hallLevel3D,3
|
||||
mov engineMode,MODE_WALK
|
||||
mov worldMap3D,OFFSET fortuneRoomMap3D
|
||||
mov floorTexture3D,134
|
||||
mov playerX,FP_1*155/10
|
||||
mov playerY,11*FP_1
|
||||
mov playerAngle,256
|
||||
call UpdateInteriorEye3D
|
||||
call ExpansionFrame
|
||||
mov eax,bankroll
|
||||
mov fortuneLiveBank,eax
|
||||
mov fortuneSessionBank,eax
|
||||
mov eax,playerX
|
||||
mov fortuneSessionPlayer,eax
|
||||
call BeginFortuneDialog3D
|
||||
cmp fortuneSessionShuffle3D.frames,64
|
||||
jne fortune_session_fail
|
||||
cmp fortuneSessionDeal3D.frames,32
|
||||
jne fortune_session_fail
|
||||
cmp fortuneSessionCards3D.frames,45
|
||||
jne fortune_session_fail
|
||||
cmp fortuneProcess3D,0
|
||||
jne fortune_session_fail
|
||||
cmp fortuneRead3D,0
|
||||
jne fortune_session_fail
|
||||
invoke WindowEvents3D,0,102h,'1',0
|
||||
cmp fortuneSubMode3D,FORTUNE_SUBMODE_TAROT
|
||||
jne fortune_session_fail
|
||||
; Lower edge of the question box is not an Exit-button hit target.
|
||||
mov mouseX3D,260
|
||||
mov mouseY3D,222
|
||||
mov mouseClick3D,1
|
||||
call HandleFortuneDialog3D
|
||||
cmp engineMode,MODE_FORTUNE
|
||||
jne fortune_live_fail
|
||||
cmp fortuneWorkerState3D,1
|
||||
jne fortune_live_fail
|
||||
jne fortune_session_fail
|
||||
call SubmitFortuneQuestion3D
|
||||
cmp fortunePhase3D,0
|
||||
jne fortune_session_fail
|
||||
mov esi,250
|
||||
fortune_live_full_input:
|
||||
fortune_session_input_limit:
|
||||
invoke WindowEvents3D,0,102h,'a',0
|
||||
dec esi
|
||||
jnz fortune_live_full_input
|
||||
jnz fortune_session_input_limit
|
||||
cmp fortuneQuestionLength3D,240
|
||||
jne fortune_live_fail
|
||||
jne fortune_session_fail
|
||||
cmp fortuneQuestion3D[240],0
|
||||
jne fortune_live_fail
|
||||
jne fortune_session_fail
|
||||
invoke WindowEvents3D,0,102h,8,0
|
||||
cmp fortuneQuestionLength3D,239
|
||||
jne fortune_live_fail
|
||||
jne fortune_session_fail
|
||||
mov fortuneQuestionLength3D,0
|
||||
mov fortuneQuestion3D,0
|
||||
mov esi,OFFSET fortuneLiveQuestion
|
||||
fortune_live_type:
|
||||
mov esi,OFFSET fortuneSessionQuestion
|
||||
fortune_session_input:
|
||||
movzx eax,BYTE PTR [esi]
|
||||
test eax,eax
|
||||
jz fortune_live_ready
|
||||
jz fortune_session_input_done
|
||||
invoke WindowEvents3D,0,102h,eax,0
|
||||
inc esi
|
||||
jmp fortune_live_type
|
||||
fortune_live_ready:
|
||||
mov edi,9000
|
||||
fortune_live_wait_ready:
|
||||
call PollFortuneWorker3D
|
||||
cmp fortuneWorkerState3D,2
|
||||
je fortune_live_submit
|
||||
cmp fortuneWorkerState3D,4
|
||||
je fortune_live_fail
|
||||
invoke Sleep,20
|
||||
dec edi
|
||||
jnz fortune_live_wait_ready
|
||||
jmp fortune_live_fail
|
||||
fortune_live_submit:
|
||||
mov expansionStage,81
|
||||
call SubmitFortuneQuestion3D
|
||||
cmp fortuneWorkerState3D,3
|
||||
jne fortune_live_fail
|
||||
mov esi,fortuneQuestionLength3D
|
||||
invoke WindowEvents3D,0,102h,'X',0
|
||||
cmp fortuneQuestionLength3D,esi
|
||||
jne fortune_live_fail
|
||||
call SubmitFortuneQuestion3D
|
||||
mov edi,9000
|
||||
fortune_live_wait_answer:
|
||||
call PollFortuneWorker3D
|
||||
cmp fortuneWorkerState3D,2
|
||||
je fortune_live_answer
|
||||
cmp fortuneWorkerState3D,4
|
||||
je fortune_live_fail
|
||||
invoke Sleep,20
|
||||
dec edi
|
||||
jnz fortune_live_wait_answer
|
||||
jmp fortune_live_fail
|
||||
fortune_live_answer:
|
||||
cmp fortuneAnswer3D,0
|
||||
je fortune_live_fail
|
||||
cmp fortuneQuestionLength3D,0
|
||||
jne fortune_live_fail
|
||||
jmp fortune_session_input
|
||||
fortune_session_input_done:
|
||||
mov fortuneSpeechTime3D,3000
|
||||
call RenderFortuneDialog3D
|
||||
invoke ExpansionSnapshot,OFFSET fortuneLiveShot
|
||||
invoke ExpansionSnapshot,OFFSET fortuneSessionShot0
|
||||
call SubmitFortuneQuestion3D
|
||||
cmp fortunePhase3D,1
|
||||
jne fortune_session_fail
|
||||
cmp fortuneDealt3D,0
|
||||
jne fortune_session_fail
|
||||
invoke WindowEvents3D,0,102h,'X',0
|
||||
cmp fortuneQuestionLength3D,22
|
||||
jne fortune_session_fail
|
||||
call SubmitFortuneQuestion3D
|
||||
cmp fortunePhase3D,1
|
||||
jne fortune_session_fail
|
||||
mov fortunePhaseTime3D,700
|
||||
mov fortuneSpeechTime3D,1000
|
||||
call RenderFortuneDialog3D
|
||||
invoke ExpansionSnapshot,OFFSET fortuneSessionShot1
|
||||
cmp fortunePhaseTime3D,700
|
||||
jne fortune_session_fail
|
||||
xor edi,edi
|
||||
fortune_session_motion:
|
||||
mov eax,edi
|
||||
xor edx,edx
|
||||
mov ecx,10
|
||||
div ecx
|
||||
add al,'0'
|
||||
add dl,'0'
|
||||
mov fortuneSessionDigits[0],al
|
||||
mov fortuneSessionDigits[1],dl
|
||||
mov eax,edi
|
||||
imul eax,25
|
||||
mov fortunePhaseTime3D,eax
|
||||
call RenderFortuneDialog3D
|
||||
invoke ExpansionSnapshot,OFFSET fortuneSessionMotion
|
||||
inc edi
|
||||
cmp edi,64
|
||||
jb fortune_session_motion
|
||||
mov ebx,1
|
||||
fortune_session_phases:
|
||||
mov eax,fortuneDurations3D[ebx*4]
|
||||
mov fortunePhaseTime3D,eax
|
||||
call FortuneSessionStepTest
|
||||
inc ebx
|
||||
cmp fortunePhase3D,ebx
|
||||
jne fortune_session_fail
|
||||
mov fortunePhaseTime3D,600
|
||||
mov fortuneSpeechTime3D,3000
|
||||
call RenderFortuneDialog3D
|
||||
invoke ExpansionSnapshot,fortuneSessionShots[ebx*4]
|
||||
cmp ebx,12
|
||||
jb fortune_session_phases
|
||||
cmp fortuneDealt3D,3
|
||||
jne fortune_session_fail
|
||||
cmp fortuneRevealed3D,3
|
||||
jne fortune_session_fail
|
||||
cmp fortuneQuestionLength3D,22
|
||||
jne fortune_session_fail
|
||||
mov eax,fortuneSpeechLength3D
|
||||
cmp fortuneSpeechVisible3D,eax
|
||||
jne fortune_session_fail
|
||||
; Distinct underlying cards even when orientation differs.
|
||||
xor esi,esi
|
||||
fortune_session_unique_outer:
|
||||
mov eax,fortuneDrawCards3D[esi*4]
|
||||
xor edx,edx
|
||||
mov ecx,22
|
||||
div ecx
|
||||
mov edi,edx
|
||||
lea ebx,[esi+1]
|
||||
fortune_session_unique_inner:
|
||||
cmp ebx,3
|
||||
jae fortune_session_unique_next
|
||||
mov eax,fortuneDrawCards3D[ebx*4]
|
||||
xor edx,edx
|
||||
div ecx
|
||||
cmp edx,edi
|
||||
je fortune_session_fail
|
||||
inc ebx
|
||||
jmp fortune_session_unique_inner
|
||||
fortune_session_unique_next:
|
||||
inc esi
|
||||
cmp esi,2
|
||||
jb fortune_session_unique_outer
|
||||
mov expansionStage,81
|
||||
; Bubble grows as glyphs appear and cannot exceed the scene edge.
|
||||
invoke SetFortuneSpeech3D,OFFSET fortuneSummary3D
|
||||
mov fortuneSpeechTime3D,280
|
||||
call RenderFortuneDialog3D
|
||||
mov esi,fortuneSpeechWidth3D
|
||||
mov fortuneSpeechTime3D,3000
|
||||
call RenderFortuneDialog3D
|
||||
cmp fortuneSpeechWidth3D,esi
|
||||
jbe fortune_session_fail
|
||||
cmp fortuneSpeechWidth3D,204
|
||||
ja fortune_session_fail
|
||||
cmp fortuneSpeechHeight3D,140
|
||||
ja fortune_session_fail
|
||||
; Return to selection, then display the neutral, non-grading orb sequence.
|
||||
mov mouseX3D,200
|
||||
mov mouseY3D,232
|
||||
mov mouseClick3D,1
|
||||
call HandleFortuneDialog3D
|
||||
cmp fortuneSubMode3D,0
|
||||
jne fortune_session_fail
|
||||
invoke WindowEvents3D,0,102h,'2',0
|
||||
cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB
|
||||
jne fortune_session_fail
|
||||
mov fortunePhaseTime3D,700
|
||||
call RenderFortuneDialog3D
|
||||
invoke ExpansionSnapshot,OFFSET fortuneSessionShot13
|
||||
mov fortunePhaseTime3D,1400
|
||||
call FortuneSessionStepTest
|
||||
cmp fortunePhase3D,2
|
||||
jne fortune_session_fail
|
||||
mov fortunePhaseTime3D,1500
|
||||
mov fortuneSpeechTime3D,3000
|
||||
call RenderFortuneDialog3D
|
||||
invoke ExpansionSnapshot,OFFSET fortuneSessionShot14
|
||||
mov fortunePhaseTime3D,5000
|
||||
call FortuneSessionStepTest
|
||||
cmp fortunePhase3D,3
|
||||
jne fortune_session_fail
|
||||
mov fortuneSpeechTime3D,3000
|
||||
call RenderFortuneDialog3D
|
||||
invoke ExpansionSnapshot,OFFSET fortuneSessionShot15
|
||||
; Exit in the middle of an animation as well as after completion.
|
||||
mov mouseX3D,260
|
||||
mov mouseY3D,226
|
||||
mov mouseY3D,232
|
||||
mov mouseClick3D,1
|
||||
call HandleFortuneDialog3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne fortune_live_fail
|
||||
cmp fortuneProcess3D,0
|
||||
jne fortune_live_fail
|
||||
cmp fortuneRead3D,0
|
||||
jne fortune_live_fail
|
||||
cmp fortuneWrite3D,0
|
||||
jne fortune_live_fail
|
||||
jne fortune_session_fail
|
||||
cmp fortuneSessionRoom3D.pixels,0
|
||||
jne fortune_session_fail
|
||||
call BeginFortuneDialog3D
|
||||
invoke FortuneChar3D,'1'
|
||||
invoke FortuneChar3D,'a'
|
||||
call SubmitFortuneQuestion3D
|
||||
mov mouseX3D,260
|
||||
mov mouseY3D,232
|
||||
mov mouseClick3D,1
|
||||
call HandleFortuneDialog3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne fortune_session_fail
|
||||
cmp fortuneSessionShuffle3D.pixels,0
|
||||
jne fortune_session_fail
|
||||
mov eax,bankroll
|
||||
cmp eax,fortuneLiveBank
|
||||
jne fortune_live_fail
|
||||
cmp playerX,FP_1*155/10
|
||||
jne fortune_live_fail
|
||||
cmp playerY,11*FP_1
|
||||
jne fortune_live_fail
|
||||
cmp playerAngle,256
|
||||
jne fortune_live_fail
|
||||
cmp eax,fortuneSessionBank
|
||||
jne fortune_session_fail
|
||||
mov eax,playerX
|
||||
cmp eax,fortuneSessionPlayer
|
||||
jne fortune_session_fail
|
||||
ret
|
||||
fortune_live_fail:
|
||||
call StopFortuneWorker3D
|
||||
fortune_session_fail:
|
||||
invoke ExitProcess,expansionStage
|
||||
ret
|
||||
FortuneDialogNativeTests ENDP
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ fortune_test_arrived:
|
|||
invoke ExpansionSnapshot,OFFSET fortuneTestDialog
|
||||
mov mouseClick3D,1
|
||||
mov mouseX3D,260
|
||||
mov mouseY3D,226
|
||||
mov mouseY3D,232
|
||||
call HandleFortuneDialog3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne fortune_test_fail
|
||||
|
|
|
|||
|
|
@ -1162,9 +1162,8 @@ expansion_leaf_shut:
|
|||
jne expansion_fail
|
||||
call FortuneNativeTests
|
||||
call BarCameraNativeTests
|
||||
IFDEF CASINO_FORTUNE_AI_LIVE
|
||||
call FortuneDialogNativeTests
|
||||
ENDIF
|
||||
call ExpansionDartsTests
|
||||
invoke ExitProcess,0
|
||||
expansion_fail:
|
||||
invoke ExitProcess,expansionStage
|
||||
|
|
@ -1173,3 +1172,4 @@ ExpansionTestStart ENDP
|
|||
include tools/tests/fortune_room_test.inc
|
||||
include tools/tests/bar_camera_test.inc
|
||||
include tools/tests/fortune_dialog_test.inc
|
||||
include tools/tests/darts_hall_test.inc
|
||||
|
|
|
|||
161
tools/tests/observer_test.inc
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
.data
|
||||
obsTestGame dd 0
|
||||
obsTestTicks dd 0
|
||||
obsTestTurns dd 0
|
||||
obsTestMenu db "build\observer\menu.bmp",0
|
||||
obsTestPaths dd OFFSET obsTestPoker,OFFSET obsTestBlackjack,OFFSET obsTestDurak
|
||||
dd OFFSET obsTestTransfer,OFFSET obsTestPyramid,OFFSET obsTestEight
|
||||
obsTestPoker db "build\observer\poker.bmp",0
|
||||
obsTestBlackjack db "build\observer\blackjack.bmp",0
|
||||
obsTestDurak db "build\observer\durak.bmp",0
|
||||
obsTestTransfer db "build\observer\transfer.bmp",0
|
||||
obsTestPyramid db "build\observer\pyramid.bmp",0
|
||||
obsTestEight db "build\observer\eight.bmp",0
|
||||
.code
|
||||
ObserverTestStart PROC
|
||||
call InitPaths3D
|
||||
mov arrivalConfirmed3D,1
|
||||
mov bankroll,12345
|
||||
mov charmCloverUses,17
|
||||
mov charmDevilUses,18
|
||||
mov obsActive3D,1
|
||||
invoke TriggerCharmLossProtection,100
|
||||
test eax,eax
|
||||
jnz obs_test_fail
|
||||
cmp bankroll,12345
|
||||
jne obs_test_fail
|
||||
mov obsActive3D,0
|
||||
call BeginObserver3D
|
||||
call RenderObserver3D
|
||||
invoke WriteActionSnapshot3D,OFFSET obsTestMenu
|
||||
obs_test_game:
|
||||
mov eax,obsTestGame
|
||||
mov obsSelection3D,eax
|
||||
call StartObserver3D
|
||||
cmp obsActive3D,1
|
||||
jne obs_test_fail
|
||||
mov obsTestTicks,0
|
||||
mov obsTestTurns,0
|
||||
obs_test_tick:
|
||||
mov deltaTime3D,50
|
||||
call ObserverTick3D
|
||||
cmp obsTestGame,4
|
||||
jb obs_test_cards
|
||||
cmp poolState3D,POOL_STRIKE
|
||||
jne @F
|
||||
mov eax,poolPlayer3D
|
||||
bts obsTestTurns,eax
|
||||
@@:
|
||||
cmp obsTestTicks,1500
|
||||
jb obs_test_next_tick
|
||||
cmp obsTestTurns,3
|
||||
jne obs_test_fail
|
||||
jmp obs_test_snapshot
|
||||
obs_test_cards:
|
||||
cmp obsRound3D,21
|
||||
jb obs_test_next_tick
|
||||
mov eax,obsWins3D[0]
|
||||
add eax,obsWins3D[4]
|
||||
add eax,obsWins3D[8]
|
||||
cmp eax,20
|
||||
jne obs_test_fail
|
||||
jmp obs_test_snapshot
|
||||
obs_test_next_tick:
|
||||
inc obsTestTicks
|
||||
cmp obsTestTicks,150000
|
||||
jb obs_test_tick
|
||||
jmp obs_test_fail
|
||||
obs_test_snapshot:
|
||||
; Advance into a real deal/turn before capturing the watching surface.
|
||||
mov ecx,40
|
||||
obs_test_render_advance:
|
||||
push ecx
|
||||
mov deltaTime3D,50
|
||||
call ObserverTick3D
|
||||
pop ecx
|
||||
loop obs_test_render_advance
|
||||
call RenderObserver3D
|
||||
mov eax,obsTestGame
|
||||
invoke WriteActionSnapshot3D,obsTestPaths[eax*4]
|
||||
mov obsPaused3D,1
|
||||
mov eax,obsRound3D
|
||||
push eax
|
||||
mov eax,obsTimer3D
|
||||
push eax
|
||||
mov deltaTime3D,50
|
||||
call ObserverFrame3D
|
||||
pop eax
|
||||
cmp eax,obsTimer3D
|
||||
jne obs_test_fail
|
||||
pop eax
|
||||
cmp eax,obsRound3D
|
||||
jne obs_test_fail
|
||||
mov pressedKeys3D[9],1
|
||||
call ObserverFrame3D
|
||||
cmp obsSpeed3D,2
|
||||
jne obs_test_fail
|
||||
mov pressedKeys3D[9],1
|
||||
call ObserverFrame3D
|
||||
cmp obsSpeed3D,4
|
||||
jne obs_test_fail
|
||||
mov pressedKeys3D[9],1
|
||||
call ObserverFrame3D
|
||||
cmp obsSpeed3D,1
|
||||
jne obs_test_fail
|
||||
call StopObserver3D
|
||||
cmp bankroll,12345
|
||||
jne obs_test_fail
|
||||
cmp charmCloverUses,17
|
||||
jne obs_test_fail
|
||||
cmp charmDevilUses,18
|
||||
jne obs_test_fail
|
||||
cmp obsActive3D,0
|
||||
jne obs_test_fail
|
||||
inc obsTestGame
|
||||
cmp obsTestGame,OBS_GAME_COUNT
|
||||
jb obs_test_game
|
||||
; The actual selector opens a table, and Escape unwinds watch -> menu -> hall.
|
||||
mov pressedKeys3D[31h],1
|
||||
call ObserverFrame3D
|
||||
cmp obsActive3D,1
|
||||
jne obs_test_fail
|
||||
mov pressedKeys3D[1Bh],1
|
||||
call ObserverFrame3D
|
||||
cmp engineMode,MODE_OBSERVER
|
||||
jne obs_test_fail
|
||||
cmp bankroll,12345
|
||||
jne obs_test_fail
|
||||
mov mouseX3D,100
|
||||
mov mouseY3D,174
|
||||
mov mouseClick3D,1
|
||||
call ObserverFrame3D
|
||||
cmp obsActive3D,1
|
||||
jne obs_test_fail
|
||||
cmp poolVariant3D,1
|
||||
jne obs_test_fail
|
||||
mov mouseX3D,20
|
||||
mov mouseY3D,230
|
||||
mov mouseClick3D,1
|
||||
call ObserverFrame3D
|
||||
cmp obsPaused3D,1
|
||||
jne obs_test_fail
|
||||
mov mouseX3D,250
|
||||
mov mouseClick3D,1
|
||||
call ObserverFrame3D
|
||||
cmp obsActive3D,0
|
||||
jne obs_test_fail
|
||||
mov pressedKeys3D[1Bh],1
|
||||
call ObserverFrame3D
|
||||
cmp engineMode,MODE_WALK
|
||||
jne obs_test_fail
|
||||
mov obsActive3D,1
|
||||
call RunDurakTest3D
|
||||
test eax,eax
|
||||
jnz obs_test_exit
|
||||
xor eax,eax
|
||||
jmp obs_test_exit
|
||||
obs_test_fail:
|
||||
mov eax,8401
|
||||
obs_test_exit:
|
||||
invoke ExitProcess,eax
|
||||
ObserverTestStart ENDP
|
||||
|
|
@ -70,27 +70,18 @@ try {
|
|||
$cp1251 = [Text.Encoding]::GetEncoding(1251)
|
||||
$asmText = $cp1251.GetString($asmBytes)
|
||||
foreach ($needle in @(
|
||||
'settingsCinematicText db "Cinematic -',
|
||||
'settingsAdaptiveText db "Adaptive -',
|
||||
'settingsStableText db "Stable -',
|
||||
'AnimShouldDrawFrame PROC',
|
||||
'AnimFrameDelay PROC',
|
||||
'WriteAnimProfileFile PROC',
|
||||
'SAVE_SIGN_SIZE equ 276',
|
||||
'SAVE_FILE_SIZE equ 280',
|
||||
'CalcSaveSignature PROC',
|
||||
'mov DWORD PTR [saveBuffer+SAVE_SIGN_SIZE], eax',
|
||||
'cmp charsRead, SAVE_FILE_SIZE',
|
||||
'includelib ws2_32.lib',
|
||||
'GameOnlineCasino PROC',
|
||||
'OnlineRRSessionHost PROC',
|
||||
'OnlineRRSessionClient PROC'
|
||||
'includelib ws2_32.lib'
|
||||
)) {
|
||||
if ($asmText.IndexOf($needle) -lt 0) {
|
||||
Fail "Missing source marker: $needle"
|
||||
}
|
||||
}
|
||||
Pass 'Animation profile and save signature markers exist in poker.asm'
|
||||
Pass 'Save signature and library markers exist in poker.asm'
|
||||
|
||||
if ($asmText -match 'MAMBO|mambo|PlayMambo') {
|
||||
Fail 'MAMBO markers still exist in poker.asm'
|
||||
|
|
|
|||