diff --git a/assets/animations/darts/board-v2.bin b/assets/animations/darts/board-v2.bin new file mode 100644 index 0000000..33e46ca Binary files /dev/null and b/assets/animations/darts/board-v2.bin differ diff --git a/assets/animations/fortune/orb-numerals-v1.bin b/assets/animations/fortune/orb-numerals-v1.bin new file mode 100644 index 0000000..c1f2000 Binary files /dev/null and b/assets/animations/fortune/orb-numerals-v1.bin differ diff --git a/assets/animations/rooftop/city-night-v1.bin b/assets/animations/rooftop/city-night-v1.bin new file mode 100644 index 0000000..6ded084 Binary files /dev/null and b/assets/animations/rooftop/city-night-v1.bin differ diff --git a/assets/animations/rooftop/city-v1.bin b/assets/animations/rooftop/city-v1.bin new file mode 100644 index 0000000..3398b12 Binary files /dev/null and b/assets/animations/rooftop/city-v1.bin differ diff --git a/assets/animations/rooftop/details-v1.bin b/assets/animations/rooftop/details-v1.bin new file mode 100644 index 0000000..9b88ec6 Binary files /dev/null and b/assets/animations/rooftop/details-v1.bin differ diff --git a/assets/animations/rooftop/lighting-v1.bin b/assets/animations/rooftop/lighting-v1.bin new file mode 100644 index 0000000..7fa159f Binary files /dev/null and b/assets/animations/rooftop/lighting-v1.bin differ diff --git a/assets/animations/rooftop/materials-v1.bin b/assets/animations/rooftop/materials-v1.bin new file mode 100644 index 0000000..740b1e0 Binary files /dev/null and b/assets/animations/rooftop/materials-v1.bin differ diff --git a/assets/animations/rooftop/mesh-boat-v1.bin b/assets/animations/rooftop/mesh-boat-v1.bin new file mode 100644 index 0000000..f6cb0fe Binary files /dev/null and b/assets/animations/rooftop/mesh-boat-v1.bin differ diff --git a/assets/animations/rooftop/mesh-car-v1.bin b/assets/animations/rooftop/mesh-car-v1.bin new file mode 100644 index 0000000..cf11fea Binary files /dev/null and b/assets/animations/rooftop/mesh-car-v1.bin differ diff --git a/assets/animations/rooftop/mesh-city-v1.bin b/assets/animations/rooftop/mesh-city-v1.bin new file mode 100644 index 0000000..712a09b Binary files /dev/null and b/assets/animations/rooftop/mesh-city-v1.bin differ diff --git a/assets/animations/rooftop/night-v1.bin b/assets/animations/rooftop/night-v1.bin new file mode 100644 index 0000000..4097e7a Binary files /dev/null and b/assets/animations/rooftop/night-v1.bin differ diff --git a/assets/animations/rooftop/panorama-v1.bin b/assets/animations/rooftop/panorama-v1.bin new file mode 100644 index 0000000..378398b Binary files /dev/null and b/assets/animations/rooftop/panorama-v1.bin differ diff --git a/assets/animations/rooftop/rail-hands-v1.bin b/assets/animations/rooftop/rail-hands-v1.bin new file mode 100644 index 0000000..18f4423 Binary files /dev/null and b/assets/animations/rooftop/rail-hands-v1.bin differ diff --git a/assets/sprites/darts/board-v2.png b/assets/sprites/darts/board-v2.png new file mode 100644 index 0000000..babdc44 Binary files /dev/null and b/assets/sprites/darts/board-v2.png differ diff --git a/assets/sprites/darts/prompts-v1.md b/assets/sprites/darts/prompts-v1.md index ac80888..b5556b4 100644 --- a/assets/sprites/darts/prompts-v1.md +++ b/assets/sprites/darts/prompts-v1.md @@ -78,3 +78,7 @@ outside the image. Deterministically generated exact 20-sector sports geometry, not AI artwork. The sector order and scoring ring radii are shared with native scoring tests. + +`board-v2.png` is the deterministic readability revision: larger, brighter +numerals and cleaner wires, drawn at 2x then reduced to the same 512px canvas. +It uses no image-generation model and does not change the board's world size. diff --git a/assets/sprites/fortune/orb-numerals-v1.png b/assets/sprites/fortune/orb-numerals-v1.png new file mode 100644 index 0000000..13d92fb Binary files /dev/null and b/assets/sprites/fortune/orb-numerals-v1.png differ diff --git a/assets/sprites/fortune/orb-tracking-v1.json b/assets/sprites/fortune/orb-tracking-v1.json new file mode 100644 index 0000000..f6c71fc --- /dev/null +++ b/assets/sprites/fortune/orb-tracking-v1.json @@ -0,0 +1,105 @@ +{ + "canvas": [ + 192, + 192 + ], + "font": "Cambria Regular, rasterized at 4x; no font needed at runtime", + "frames": [ + { + "cx": 107.0, + "cy": 85.60757294157031, + "rx": 72.68997989989911, + "ry": 74.69516621989159 + }, + { + "cx": 95.5, + "cy": 85.76519319889574, + "rx": 72.51196201438557, + "ry": 74.89806065214609 + }, + { + "cx": 95.5, + "cy": 85.71241583054908, + "rx": 72.69942242784161, + "ry": 74.83168393851604 + }, + { + "cx": 84.0, + "cy": 85.72005975738783, + "rx": 72.63601648302654, + "ry": 74.68751926061978 + }, + { + "cx": 106.5, + "cy": 82.20860810943547, + "rx": 72.87681495588859, + "ry": 74.48180234847793 + }, + { + "cx": 95.0, + "cy": 82.2684885845598, + "rx": 73.25393481375772, + "ry": 74.62519501114646 + }, + { + "cx": 95.0, + "cy": 82.35337687892542, + "rx": 73.02811219141157, + "ry": 74.66203440932597 + }, + { + "cx": 84.0, + "cy": 82.42039157417511, + "rx": 72.80858646484103, + "ry": 74.62208876727115 + }, + { + "cx": 106.5, + "cy": 81.72901899211435, + "rx": 74.34475334746573, + "ry": 73.78037632992753 + }, + { + "cx": 95.5, + "cy": 81.63668560751485, + "rx": 75.06807641776177, + "ry": 73.48706340594615 + }, + { + "cx": 95.5, + "cy": 81.67982777738753, + "rx": 75.12648691865994, + "ry": 73.49674962974022 + }, + { + "cx": 84.5, + "cy": 81.57452731727243, + "rx": 74.48750227516855, + "ry": 73.69652904057105 + }, + { + "cx": 106.0, + "cy": 72.23134647099609, + "rx": 75.09606781800167, + "ry": 75.52834001598418 + }, + { + "cx": 96.0, + "cy": 72.31864303902333, + "rx": 74.79155274675983, + "ry": 75.30939853900956 + }, + { + "cx": 94.5, + "cy": 72.10115208001082, + "rx": 74.92026723097523, + "ry": 75.46485386910284 + }, + { + "cx": 85.0, + "cy": 72.21051327527425, + "rx": 74.61593591942703, + "ry": 75.5076844695196 + } + ] +} diff --git a/assets/sprites/rooftop/chair-views-v1.png b/assets/sprites/rooftop/chair-views-v1.png new file mode 100644 index 0000000..6c40bbe Binary files /dev/null and b/assets/sprites/rooftop/chair-views-v1.png differ diff --git a/assets/sprites/rooftop/city-facades-v1.png b/assets/sprites/rooftop/city-facades-v1.png new file mode 100644 index 0000000..eef0b7e Binary files /dev/null and b/assets/sprites/rooftop/city-facades-v1.png differ diff --git a/assets/sprites/rooftop/cutouts-v1.png b/assets/sprites/rooftop/cutouts-v1.png new file mode 100644 index 0000000..2db8bde Binary files /dev/null and b/assets/sprites/rooftop/cutouts-v1.png differ diff --git a/assets/sprites/rooftop/district-map-v1.json b/assets/sprites/rooftop/district-map-v1.json new file mode 100644 index 0000000..936088e --- /dev/null +++ b/assets/sprites/rooftop/district-map-v1.json @@ -0,0 +1,6923 @@ +{ + "extent": [ + -520, + 560, + -530, + 550 + ], + "casino": [ + 10.9, + 29.1, + 0.9, + 21.1 + ], + "street_spacing": 36, + "street_width": 8, + "street_level": -60, + "canal": [ + -190, + 230, + 60, + 76 + ], + "bridges": [ + -34, + 38, + 110 + ], + "east_river": [ + 70, + 1800, + 29, + 76 + ], + "river_bridges": [ + 110, + 254, + 398 + ], + "lots": [ + { + "centre": [ + -160, + -169 + ], + "courtyard": [ + -167, + -153, + -176, + -162 + ], + "facade": 190, + "height": 15 + }, + { + "centre": [ + -124, + -169 + ], + "courtyard": [ + -131, + -117, + -176, + -162 + ], + "facade": 193, + "height": 16 + }, + { + "centre": [ + -88, + -169 + ], + "courtyard": [ + -95, + -81, + -176, + -162 + ], + "facade": 188, + "height": 12 + }, + { + "centre": [ + -16, + -169 + ], + "courtyard": [ + -23, + -9, + -176, + -162 + ], + "facade": 186, + "height": 25 + }, + { + "centre": [ + 20, + -169 + ], + "courtyard": [ + 13, + 27, + -176, + -162 + ], + "facade": 189, + "height": 26 + }, + { + "centre": [ + 56, + -169 + ], + "courtyard": [ + 49, + 63, + -176, + -162 + ], + "facade": 192, + "height": 22 + }, + { + "centre": [ + 92, + -169 + ], + "courtyard": [ + 85, + 99, + -176, + -162 + ], + "facade": 187, + "height": 23 + }, + { + "centre": [ + 128, + -169 + ], + "courtyard": [ + 121, + 135, + -176, + -162 + ], + "facade": 204, + "height": 31 + }, + { + "centre": [ + 164, + -169 + ], + "courtyard": [ + 157, + 171, + -176, + -162 + ], + "facade": 193, + "height": 20 + }, + { + "centre": [ + 200, + -169 + ], + "courtyard": [ + 193, + 207, + -176, + -162 + ], + "facade": 188, + "height": 16 + }, + { + "centre": [ + -160, + -133 + ], + "courtyard": [ + -167, + -153, + -140, + -126 + ], + "facade": 191, + "height": 25 + }, + { + "centre": [ + -124, + -133 + ], + "courtyard": [ + -131, + -117, + -140, + -126 + ], + "facade": 186, + "height": 26 + }, + { + "centre": [ + -88, + -133 + ], + "courtyard": [ + -95, + -81, + -140, + -126 + ], + "facade": 189, + "height": 22 + }, + { + "centre": [ + -52, + -133 + ], + "courtyard": [ + -59, + -45, + -140, + -126 + ], + "facade": 192, + "height": 23 + }, + { + "centre": [ + -16, + -133 + ], + "courtyard": [ + -23, + -9, + -140, + -126 + ], + "facade": 204, + "height": 31 + }, + { + "centre": [ + 20, + -133 + ], + "courtyard": [ + 13, + 27, + -140, + -126 + ], + "facade": 190, + "height": 20 + }, + { + "centre": [ + 56, + -133 + ], + "courtyard": [ + 49, + 63, + -140, + -126 + ], + "facade": 193, + "height": 16 + }, + { + "centre": [ + 92, + -133 + ], + "courtyard": [ + 85, + 99, + -140, + -126 + ], + "facade": 204, + "height": 28 + }, + { + "centre": [ + 128, + -133 + ], + "courtyard": [ + 121, + 135, + -140, + -126 + ], + "facade": 191, + "height": 13 + }, + { + "centre": [ + 164, + -133 + ], + "courtyard": [ + 157, + 171, + -140, + -126 + ], + "facade": 186, + "height": 14 + }, + { + "centre": [ + 200, + -133 + ], + "courtyard": [ + 193, + 207, + -140, + -126 + ], + "facade": 204, + "height": 25 + }, + { + "centre": [ + -160, + -97 + ], + "courtyard": [ + -167, + -153, + -104, + -90 + ], + "facade": 204, + "height": 31 + }, + { + "centre": [ + -124, + -97 + ], + "courtyard": [ + -131, + -117, + -104, + -90 + ], + "facade": 187, + "height": 20 + }, + { + "centre": [ + -88, + -97 + ], + "courtyard": [ + -95, + -81, + -104, + -90 + ], + "facade": 190, + "height": 16 + }, + { + "centre": [ + -52, + -97 + ], + "courtyard": [ + -59, + -45, + -104, + -90 + ], + "facade": 204, + "height": 28 + }, + { + "centre": [ + -16, + -97 + ], + "courtyard": [ + -23, + -9, + -104, + -90 + ], + "facade": 188, + "height": 13 + }, + { + "centre": [ + 20, + -97 + ], + "courtyard": [ + 13, + 27, + -104, + -90 + ], + "facade": 191, + "height": 14 + }, + { + "centre": [ + 56, + -97 + ], + "courtyard": [ + 49, + 63, + -104, + -90 + ], + "facade": 204, + "height": 25 + }, + { + "centre": [ + 92, + -97 + ], + "courtyard": [ + 85, + 99, + -104, + -90 + ], + "facade": 189, + "height": 27 + }, + { + "centre": [ + 128, + -97 + ], + "courtyard": [ + 121, + 135, + -104, + -90 + ], + "facade": 192, + "height": 12 + }, + { + "centre": [ + 164, + -97 + ], + "courtyard": [ + 157, + 171, + -104, + -90 + ], + "facade": 187, + "height": 24 + }, + { + "centre": [ + 200, + -97 + ], + "courtyard": [ + 193, + 207, + -104, + -90 + ], + "facade": 190, + "height": 25 + }, + { + "centre": [ + -160, + -61 + ], + "courtyard": [ + -167, + -153, + -68, + -54 + ], + "facade": 193, + "height": 13 + }, + { + "centre": [ + -124, + -61 + ], + "courtyard": [ + -131, + -117, + -68, + -54 + ], + "facade": 188, + "height": 14 + }, + { + "centre": [ + -88, + -61 + ], + "courtyard": [ + -95, + -81, + -68, + -54 + ], + "facade": 204, + "height": 25 + }, + { + "centre": [ + -52, + -61 + ], + "courtyard": [ + -59, + -45, + -68, + -54 + ], + "facade": 186, + "height": 27 + }, + { + "centre": [ + -16, + -61 + ], + "courtyard": [ + -23, + -9, + -68, + -54 + ], + "facade": 189, + "height": 12 + }, + { + "centre": [ + 20, + -61 + ], + "courtyard": [ + 13, + 27, + -68, + -54 + ], + "facade": 192, + "height": 24 + }, + { + "centre": [ + 56, + -61 + ], + "courtyard": [ + 49, + 63, + -68, + -54 + ], + "facade": 187, + "height": 25 + }, + { + "centre": [ + 92, + -61 + ], + "courtyard": [ + 85, + 99, + -68, + -54 + ], + "facade": 190, + "height": 21 + }, + { + "centre": [ + 128, + -61 + ], + "courtyard": [ + 121, + 135, + -68, + -54 + ], + "facade": 193, + "height": 22 + }, + { + "centre": [ + 164, + -61 + ], + "courtyard": [ + 157, + 171, + -68, + -54 + ], + "facade": 188, + "height": 18 + }, + { + "centre": [ + 200, + -61 + ], + "courtyard": [ + 193, + 207, + -68, + -54 + ], + "facade": 191, + "height": 19 + }, + { + "centre": [ + -160, + -25 + ], + "courtyard": [ + -167, + -153, + -32, + -18 + ], + "facade": 186, + "height": 12 + }, + { + "centre": [ + -124, + -25 + ], + "courtyard": [ + -131, + -117, + -32, + -18 + ], + "facade": 189, + "height": 24 + }, + { + "centre": [ + -88, + -25 + ], + "courtyard": [ + -95, + -81, + -32, + -18 + ], + "facade": 192, + "height": 25 + }, + { + "centre": [ + -52, + -25 + ], + "courtyard": [ + -59, + -45, + -32, + -18 + ], + "facade": 187, + "height": 21 + }, + { + "centre": [ + -16, + -25 + ], + "courtyard": [ + -23, + -9, + -32, + -18 + ], + "facade": 203, + "height": 8 + }, + { + "centre": [ + 20, + -25 + ], + "courtyard": [ + 13, + 27, + -32, + -18 + ], + "facade": 203, + "height": 6 + }, + { + "centre": [ + 56, + -25 + ], + "courtyard": [ + 49, + 63, + -32, + -18 + ], + "facade": 203, + "height": 11 + }, + { + "centre": [ + 92, + -25 + ], + "courtyard": [ + 85, + 99, + -32, + -18 + ], + "facade": 191, + "height": 15 + }, + { + "centre": [ + 128, + -25 + ], + "courtyard": [ + 121, + 135, + -32, + -18 + ], + "facade": 186, + "height": 16 + }, + { + "centre": [ + 200, + -25 + ], + "courtyard": [ + 193, + 207, + -32, + -18 + ], + "facade": 192, + "height": 13 + }, + { + "centre": [ + -160, + 11 + ], + "courtyard": [ + -167, + -153, + 4, + 18 + ], + "facade": 187, + "height": 22 + }, + { + "centre": [ + -124, + 11 + ], + "courtyard": [ + -131, + -117, + 4, + 18 + ], + "facade": 190, + "height": 18 + }, + { + "centre": [ + -88, + 11 + ], + "courtyard": [ + -95, + -81, + 4, + 18 + ], + "facade": 193, + "height": 19 + }, + { + "centre": [ + -52, + 11 + ], + "courtyard": [ + -59, + -45, + 4, + 18 + ], + "facade": 188, + "height": 15 + }, + { + "centre": [ + 92, + 11 + ], + "courtyard": [ + 85, + 99, + 4, + 18 + ], + "facade": 192, + "height": 14 + }, + { + "centre": [ + 128, + 11 + ], + "courtyard": [ + 121, + 135, + 4, + 18 + ], + "facade": 199, + "height": 38 + }, + { + "centre": [ + -160, + 119 + ], + "courtyard": [ + -167, + -153, + 112, + 126 + ], + "facade": 190, + "height": 20 + }, + { + "centre": [ + -124, + 119 + ], + "courtyard": [ + -131, + -117, + 112, + 126 + ], + "facade": 193, + "height": 21 + }, + { + "centre": [ + -88, + 119 + ], + "courtyard": [ + -95, + -81, + 112, + 126 + ], + "facade": 188, + "height": 17 + }, + { + "centre": [ + -16, + 119 + ], + "courtyard": [ + -23, + -9, + 112, + 126 + ], + "facade": 186, + "height": 14 + }, + { + "centre": [ + 56, + 119 + ], + "courtyard": [ + 49, + 63, + 112, + 126 + ], + "facade": 192, + "height": 16 + }, + { + "centre": [ + 92, + 119 + ], + "courtyard": [ + 85, + 99, + 112, + 126 + ], + "facade": 187, + "height": 12 + }, + { + "centre": [ + 164, + 119 + ], + "courtyard": [ + 157, + 171, + 112, + 126 + ], + "facade": 193, + "height": 25 + }, + { + "centre": [ + 200, + 119 + ], + "courtyard": [ + 193, + 207, + 112, + 126 + ], + "facade": 188, + "height": 26 + }, + { + "centre": [ + -160, + 155 + ], + "courtyard": [ + -167, + -153, + 148, + 162 + ], + "facade": 191, + "height": 14 + }, + { + "centre": [ + -124, + 155 + ], + "courtyard": [ + -131, + -117, + 148, + 162 + ], + "facade": 186, + "height": 15 + }, + { + "centre": [ + -88, + 155 + ], + "courtyard": [ + -95, + -81, + 148, + 162 + ], + "facade": 189, + "height": 16 + }, + { + "centre": [ + -52, + 155 + ], + "courtyard": [ + -59, + -45, + 148, + 162 + ], + "facade": 192, + "height": 12 + }, + { + "centre": [ + 20, + 155 + ], + "courtyard": [ + 13, + 27, + 148, + 162 + ], + "facade": 190, + "height": 25 + }, + { + "centre": [ + 56, + 155 + ], + "courtyard": [ + 49, + 63, + 148, + 162 + ], + "facade": 193, + "height": 26 + }, + { + "centre": [ + 92, + 155 + ], + "courtyard": [ + 85, + 99, + 148, + 162 + ], + "facade": 188, + "height": 22 + }, + { + "centre": [ + 128, + 155 + ], + "courtyard": [ + 121, + 135, + 148, + 162 + ], + "facade": 191, + "height": 23 + }, + { + "centre": [ + 164, + 155 + ], + "courtyard": [ + 157, + 171, + 148, + 162 + ], + "facade": 204, + "height": 31 + }, + { + "centre": [ + 200, + 155 + ], + "courtyard": [ + 193, + 207, + 148, + 162 + ], + "facade": 189, + "height": 20 + }, + { + "centre": [ + -124, + 191 + ], + "courtyard": [ + -131, + -117, + 184, + 198 + ], + "facade": 187, + "height": 25 + }, + { + "centre": [ + -88, + 191 + ], + "courtyard": [ + -95, + -81, + 184, + 198 + ], + "facade": 190, + "height": 26 + }, + { + "centre": [ + -52, + 191 + ], + "courtyard": [ + -59, + -45, + 184, + 198 + ], + "facade": 193, + "height": 22 + }, + { + "centre": [ + -16, + 191 + ], + "courtyard": [ + -23, + -9, + 184, + 198 + ], + "facade": 188, + "height": 23 + }, + { + "centre": [ + 20, + 191 + ], + "courtyard": [ + 13, + 27, + 184, + 198 + ], + "facade": 204, + "height": 31 + }, + { + "centre": [ + 56, + 191 + ], + "courtyard": [ + 49, + 63, + 184, + 198 + ], + "facade": 186, + "height": 20 + }, + { + "centre": [ + 92, + 191 + ], + "courtyard": [ + 85, + 99, + 184, + 198 + ], + "facade": 189, + "height": 16 + }, + { + "centre": [ + 128, + 191 + ], + "courtyard": [ + 121, + 135, + 184, + 198 + ], + "facade": 204, + "height": 28 + }, + { + "centre": [ + 164, + 191 + ], + "courtyard": [ + 157, + 171, + 184, + 198 + ], + "facade": 187, + "height": 13 + }, + { + "centre": [ + 200, + 191 + ], + "courtyard": [ + 193, + 207, + 184, + 198 + ], + "facade": 190, + "height": 14 + } + ], + "parks": [ + [ + -52, + -169 + ], + [ + 164, + -25 + ], + [ + -16, + 11 + ], + [ + 56, + 11 + ], + [ + 200, + 11 + ], + [ + -52, + 119 + ], + [ + 128, + 119 + ], + [ + -16, + 155 + ], + [ + -160, + 191 + ] + ], + "lamps": [ + [ + -145.2, + -169 + ], + [ + -145.2, + -139 + ], + [ + -145.2, + -109 + ], + [ + -145.2, + -79 + ], + [ + -145.2, + -49 + ], + [ + -145.2, + -19 + ], + [ + -145.2, + 11 + ], + [ + -145.2, + 41 + ], + [ + -145.2, + 101 + ], + [ + -145.2, + 131 + ], + [ + -145.2, + 161 + ], + [ + -145.2, + 191 + ], + [ + -109.2, + -169 + ], + [ + -109.2, + -139 + ], + [ + -109.2, + -109 + ], + [ + -109.2, + -79 + ], + [ + -109.2, + -49 + ], + [ + -109.2, + -19 + ], + [ + -109.2, + 11 + ], + [ + -109.2, + 41 + ], + [ + -109.2, + 101 + ], + [ + -109.2, + 131 + ], + [ + -109.2, + 161 + ], + [ + -109.2, + 191 + ], + [ + -73.2, + -169 + ], + [ + -73.2, + -139 + ], + [ + -73.2, + -109 + ], + [ + -73.2, + -79 + ], + [ + -73.2, + -49 + ], + [ + -73.2, + -19 + ], + [ + -73.2, + 11 + ], + [ + -73.2, + 41 + ], + [ + -73.2, + 101 + ], + [ + -73.2, + 131 + ], + [ + -73.2, + 161 + ], + [ + -73.2, + 191 + ], + [ + -37.2, + -169 + ], + [ + -37.2, + -139 + ], + [ + -37.2, + -109 + ], + [ + -37.2, + -79 + ], + [ + -37.2, + -49 + ], + [ + -37.2, + -19 + ], + [ + -37.2, + 11 + ], + [ + -37.2, + 41 + ], + [ + -37.2, + 101 + ], + [ + -37.2, + 131 + ], + [ + -37.2, + 161 + ], + [ + -37.2, + 191 + ], + [ + -1.2000000000000028, + -169 + ], + [ + -1.2000000000000028, + -139 + ], + [ + -1.2000000000000028, + -109 + ], + [ + -1.2000000000000028, + -79 + ], + [ + -1.2000000000000028, + -49 + ], + [ + -1.2000000000000028, + -19 + ], + [ + -1.2000000000000028, + 11 + ], + [ + -1.2000000000000028, + 41 + ], + [ + -1.2000000000000028, + 101 + ], + [ + -1.2000000000000028, + 131 + ], + [ + -1.2000000000000028, + 161 + ], + [ + -1.2000000000000028, + 191 + ], + [ + 34.8, + -169 + ], + [ + 34.8, + -139 + ], + [ + 34.8, + -109 + ], + [ + 34.8, + -79 + ], + [ + 34.8, + -49 + ], + [ + 34.8, + -19 + ], + [ + 34.8, + 11 + ], + [ + 34.8, + 41 + ], + [ + 34.8, + 101 + ], + [ + 34.8, + 131 + ], + [ + 34.8, + 161 + ], + [ + 34.8, + 191 + ], + [ + 70.8, + -169 + ], + [ + 70.8, + -139 + ], + [ + 70.8, + -109 + ], + [ + 70.8, + -79 + ], + [ + 70.8, + -49 + ], + [ + 70.8, + -19 + ], + [ + 70.8, + 11 + ], + [ + 70.8, + 101 + ], + [ + 70.8, + 131 + ], + [ + 70.8, + 161 + ], + [ + 70.8, + 191 + ], + [ + 106.8, + -169 + ], + [ + 106.8, + -139 + ], + [ + 106.8, + -109 + ], + [ + 106.8, + -79 + ], + [ + 106.8, + -49 + ], + [ + 106.8, + -19 + ], + [ + 106.8, + 11 + ], + [ + 106.8, + 101 + ], + [ + 106.8, + 131 + ], + [ + 106.8, + 161 + ], + [ + 106.8, + 191 + ], + [ + 142.8, + -169 + ], + [ + 142.8, + -139 + ], + [ + 142.8, + -109 + ], + [ + 142.8, + -79 + ], + [ + 142.8, + -49 + ], + [ + 142.8, + -19 + ], + [ + 142.8, + 11 + ], + [ + 142.8, + 101 + ], + [ + 142.8, + 131 + ], + [ + 142.8, + 161 + ], + [ + 142.8, + 191 + ], + [ + 178.8, + -169 + ], + [ + 178.8, + -139 + ], + [ + 178.8, + -109 + ], + [ + 178.8, + -79 + ], + [ + 178.8, + -49 + ], + [ + 178.8, + -19 + ], + [ + 178.8, + 11 + ], + [ + 178.8, + 101 + ], + [ + 178.8, + 131 + ], + [ + 178.8, + 161 + ], + [ + 178.8, + 191 + ], + [ + 214.8, + -169 + ], + [ + 214.8, + -139 + ], + [ + 214.8, + -109 + ], + [ + 214.8, + -79 + ], + [ + 214.8, + -49 + ], + [ + 214.8, + -19 + ], + [ + 214.8, + 11 + ], + [ + 214.8, + 101 + ], + [ + 214.8, + 131 + ], + [ + 214.8, + 161 + ], + [ + 214.8, + 191 + ], + [ + -170, + 58.5 + ], + [ + -170, + 77.5 + ], + [ + -152, + 58.5 + ], + [ + -152, + 77.5 + ], + [ + -134, + 58.5 + ], + [ + -134, + 77.5 + ], + [ + -116, + 58.5 + ], + [ + -116, + 77.5 + ], + [ + -98, + 58.5 + ], + [ + -98, + 77.5 + ], + [ + -80, + 58.5 + ], + [ + -80, + 77.5 + ], + [ + -62, + 58.5 + ], + [ + -62, + 77.5 + ], + [ + -44, + 58.5 + ], + [ + -44, + 77.5 + ], + [ + -26, + 58.5 + ], + [ + -26, + 77.5 + ], + [ + -8, + 58.5 + ], + [ + -8, + 77.5 + ], + [ + 10, + 58.5 + ], + [ + 10, + 77.5 + ], + [ + 28, + 58.5 + ], + [ + 28, + 77.5 + ], + [ + 46, + 58.5 + ], + [ + 46, + 77.5 + ], + [ + 64, + 58.5 + ], + [ + 64, + 77.5 + ], + [ + 82, + 77.5 + ], + [ + 100, + 77.5 + ], + [ + 118, + 77.5 + ], + [ + 136, + 77.5 + ], + [ + 154, + 77.5 + ], + [ + 172, + 77.5 + ], + [ + 190, + 77.5 + ], + [ + 208, + 77.5 + ], + [ + 86, + 27 + ], + [ + 86, + 78 + ], + [ + 104, + 27 + ], + [ + 104, + 78 + ], + [ + 122, + 27 + ], + [ + 122, + 78 + ], + [ + 140, + 27 + ], + [ + 140, + 78 + ], + [ + 158, + 27 + ], + [ + 158, + 78 + ], + [ + 176, + 27 + ], + [ + 176, + 78 + ], + [ + 194, + 27 + ], + [ + 194, + 78 + ], + [ + 212, + 27 + ], + [ + 212, + 78 + ], + [ + 230, + 27 + ], + [ + 230, + 78 + ], + [ + 248, + 27 + ], + [ + 248, + 78 + ], + [ + 266, + 27 + ], + [ + 266, + 78 + ], + [ + 284, + 27 + ], + [ + 284, + 78 + ], + [ + 302, + 27 + ], + [ + 302, + 78 + ], + [ + 320, + 27 + ], + [ + 320, + 78 + ], + [ + 338, + 27 + ], + [ + 338, + 78 + ], + [ + 356, + 27 + ], + [ + 356, + 78 + ], + [ + 374, + 27 + ], + [ + 374, + 78 + ], + [ + 392, + 27 + ], + [ + 392, + 78 + ], + [ + 410, + 27 + ], + [ + 410, + 78 + ], + [ + 428, + 27 + ], + [ + 428, + 78 + ], + [ + 446, + 27 + ], + [ + 446, + 78 + ], + [ + 464, + 27 + ], + [ + 464, + 78 + ], + [ + 482, + 27 + ], + [ + 482, + 78 + ], + [ + 500, + 27 + ], + [ + 500, + 78 + ], + [ + 518, + 27 + ], + [ + 518, + 78 + ], + [ + 536, + 27 + ], + [ + 536, + 78 + ], + [ + 554, + 27 + ], + [ + 554, + 78 + ], + [ + 572, + 27 + ], + [ + 572, + 78 + ], + [ + 590, + 27 + ], + [ + 590, + 78 + ], + [ + 608, + 27 + ], + [ + 608, + 78 + ], + [ + 626, + 27 + ], + [ + 626, + 78 + ], + [ + 644, + 27 + ], + [ + 644, + 78 + ] + ], + "towers": [ + [ + 320, + -130, + 90 + ], + [ + 355, + -165, + 126 + ], + [ + 390, + -125, + 108 + ], + [ + 425, + -195, + 155 + ], + [ + 460, + -145, + 102 + ], + [ + 345, + -225, + 115 + ], + [ + 405, + -260, + 132 + ], + [ + 475, + -240, + 88 + ] + ], + "buildings": [ + [ + 14, + 25, + -30, + -20, + -54.0, + 203, + 0 + ], + [ + 50, + 61, + -30, + -20, + -49.0, + 203, + 0 + ], + [ + -22, + -11, + -30, + -20, + -52.0, + 203, + 0 + ], + [ + 6, + 34, + -54, + -47, + -35.7, + 192, + 1 + ], + [ + 78, + 85, + 4, + 18, + -46.0, + 192, + 2 + ], + [ + -30, + -2, + -54, + -47, + -47.7, + 189, + 1 + ], + [ + 42, + 70, + -54, + -47, + -34.7, + 187, + 1 + ], + [ + 78, + 85, + -32, + -18, + -45.0, + 191, + 2 + ], + [ + 6, + 13, + -68, + -54, + -36.0, + 192, + 2 + ], + [ + -66, + -38, + -3, + 4, + -45.0, + 188, + 0 + ], + [ + -66, + -38, + 18, + 25, + -44.7, + 188, + 1 + ], + [ + 78, + 106, + -3, + 4, + -46.0, + 192, + 0 + ], + [ + 78, + 106, + 18, + 25, + -45.7, + 192, + 1 + ], + [ + -9, + -2, + -68, + -54, + -47.7, + 189, + 3 + ], + [ + 42, + 49, + -68, + -54, + -35.0, + 187, + 2 + ], + [ + -66, + -38, + -18, + -11, + -38.7, + 187, + 1 + ], + [ + 78, + 106, + -18, + -11, + -44.7, + 191, + 1 + ], + [ + 6, + 34, + -75, + -68, + -36.0, + 192, + 0 + ], + [ + -66, + -59, + 4, + 18, + -45.0, + 188, + 2 + ], + [ + 99, + 106, + 4, + 18, + -45.7, + 192, + 3 + ], + [ + -30, + -23, + -68, + -54, + -48.0, + 189, + 2 + ], + [ + 63, + 70, + -68, + -54, + -34.7, + 187, + 3 + ], + [ + -66, + -38, + -39, + -32, + -39.0, + 187, + 0 + ], + [ + 78, + 106, + -39, + -32, + -45.0, + 191, + 0 + ], + [ + -30, + -2, + -75, + -68, + -48.0, + 189, + 0 + ], + [ + 42, + 70, + -75, + -68, + -35.0, + 187, + 0 + ], + [ + -66, + -59, + -32, + -18, + -39.0, + 187, + 2 + ], + [ + -66, + -38, + -54, + -47, + -32.7, + 186, + 1 + ], + [ + 78, + 106, + -54, + -47, + -38.7, + 190, + 1 + ], + [ + 78, + 85, + -68, + -54, + -39.0, + 190, + 2 + ], + [ + 6, + 34, + -90, + -83, + -45.7, + 191, + 1 + ], + [ + -81, + -74, + 4, + 18, + -40.7, + 193, + 3 + ], + [ + -30, + -2, + -90, + -83, + -46.7, + 188, + 1 + ], + [ + -81, + -74, + -32, + -18, + -34.7, + 192, + 3 + ], + [ + 114, + 121, + -32, + -18, + -43.7, + 186, + 1 + ], + [ + -30, + -2, + 105, + 112, + -46.0, + 186, + 0 + ], + [ + 42, + 70, + 105, + 112, + -44.0, + 192, + 0 + ], + [ + 120, + 136, + 1, + 21, + -22.0, + 199, + 0 + ], + [ + 6, + 13, + -104, + -90, + -46.0, + 191, + 2 + ], + [ + -102, + -74, + -3, + 4, + -41.0, + 193, + 0 + ], + [ + -102, + -74, + 18, + 25, + -40.7, + 193, + 1 + ], + [ + -66, + -38, + -75, + -68, + -33.0, + 186, + 0 + ], + [ + -66, + -59, + -68, + -54, + -33.0, + 186, + 2 + ], + [ + 78, + 106, + -75, + -68, + -39.0, + 190, + 0 + ], + [ + -9, + -2, + -104, + -90, + -46.7, + 188, + 3 + ], + [ + -102, + -74, + -18, + -11, + -34.7, + 192, + 1 + ], + [ + 114, + 142, + -18, + -11, + -44.0, + 186, + 0 + ], + [ + -9, + -2, + 112, + 126, + -45.7, + 186, + 3 + ], + [ + 42, + 49, + 112, + 126, + -44.0, + 192, + 2 + ], + [ + 48, + 64, + -107, + -87, + -35.0, + 204, + 0 + ], + [ + -30, + -23, + -104, + -90, + -47.0, + 188, + 2 + ], + [ + -102, + -74, + -39, + -32, + -35.0, + 192, + 0 + ], + [ + -30, + -23, + 112, + 126, + -46.0, + 186, + 2 + ], + [ + 6, + 34, + -111, + -104, + -46.0, + 191, + 0 + ], + [ + -102, + -95, + 4, + 18, + -41.0, + 193, + 2 + ], + [ + 78, + 106, + -90, + -83, + -32.7, + 189, + 1 + ], + [ + 114, + 121, + -68, + -54, + -38.0, + 193, + 2 + ], + [ + 78, + 106, + 105, + 112, + -48.0, + 187, + 0 + ], + [ + -30, + -2, + -111, + -104, + -47.0, + 188, + 0 + ], + [ + -102, + -95, + -32, + -18, + -35.0, + 192, + 2 + ], + [ + -30, + -2, + 126, + 133, + -45.7, + 186, + 1 + ], + [ + 42, + 70, + 126, + 133, + -43.7, + 192, + 1 + ], + [ + 78, + 85, + -104, + -90, + -33.0, + 189, + 2 + ], + [ + 114, + 142, + -54, + -47, + -37.7, + 193, + 1 + ], + [ + 78, + 85, + 112, + 126, + -48.0, + 187, + 2 + ], + [ + -60, + -44, + -107, + -87, + -32.0, + 204, + 0 + ], + [ + -96, + -80, + -71, + -51, + -35.0, + 204, + 0 + ], + [ + 6, + 34, + -126, + -119, + -39.7, + 190, + 1 + ], + [ + 6, + 34, + 141, + 148, + -35.0, + 190, + 0 + ], + [ + 114, + 142, + -75, + -68, + -38.0, + 193, + 0 + ], + [ + 99, + 106, + 112, + 126, + -47.7, + 187, + 3 + ], + [ + 42, + 70, + -126, + -119, + -43.7, + 193, + 1 + ], + [ + 42, + 70, + 141, + 148, + -34.0, + 193, + 0 + ], + [ + 78, + 106, + -111, + -104, + -33.0, + 189, + 0 + ], + [ + 135, + 142, + -68, + -54, + -37.7, + 193, + 3 + ], + [ + 78, + 106, + 126, + 133, + -47.7, + 187, + 1 + ], + [ + 6, + 13, + -140, + -126, + -40.0, + 190, + 2 + ], + [ + -138, + -110, + -3, + 4, + -42.0, + 190, + 0 + ], + [ + -138, + -110, + 18, + 25, + -41.7, + 190, + 1 + ], + [ + 6, + 13, + 148, + 162, + -35.0, + 190, + 2 + ], + [ + 27, + 34, + 148, + 162, + -34.7, + 190, + 3 + ], + [ + -102, + -74, + -90, + -83, + -43.7, + 190, + 1 + ], + [ + -81, + -74, + -104, + -90, + -43.7, + 190, + 3 + ], + [ + 114, + 142, + -90, + -83, + -47.7, + 192, + 1 + ], + [ + 114, + 121, + -104, + -90, + -48.0, + 192, + 2 + ], + [ + 42, + 49, + -140, + -126, + -44.0, + 193, + 2 + ], + [ + -138, + -110, + -18, + -11, + -35.7, + 189, + 1 + ], + [ + 42, + 49, + 148, + 162, + -34.0, + 193, + 2 + ], + [ + -24, + -8, + -143, + -123, + -29.0, + 204, + 0 + ], + [ + 63, + 70, + -140, + -126, + -43.7, + 193, + 3 + ], + [ + -138, + -110, + -39, + -32, + -36.0, + 189, + 0 + ], + [ + -66, + -38, + -126, + -119, + -36.7, + 192, + 1 + ], + [ + 150, + 157, + -68, + -54, + -42.0, + 188, + 2 + ], + [ + -66, + -38, + 141, + 148, + -48.0, + 192, + 0 + ], + [ + 6, + 34, + -147, + -140, + -40.0, + 190, + 0 + ], + [ + -138, + -131, + 4, + 18, + -42.0, + 190, + 2 + ], + [ + 6, + 34, + 162, + 169, + -34.7, + 190, + 1 + ], + [ + -138, + -110, + -54, + -47, + -45.7, + 188, + 1 + ], + [ + 150, + 178, + -54, + -47, + -41.7, + 188, + 1 + ], + [ + -45, + -38, + 148, + 162, + -47.7, + 192, + 3 + ], + [ + 78, + 85, + 148, + 162, + -37.7, + 188, + 1 + ], + [ + 42, + 70, + -147, + -140, + -44.0, + 193, + 0 + ], + [ + -138, + -131, + -32, + -18, + -36.0, + 189, + 2 + ], + [ + 42, + 70, + 162, + 169, + -33.7, + 193, + 1 + ], + [ + -102, + -74, + -111, + -104, + -44.0, + 190, + 0 + ], + [ + -102, + -95, + -104, + -90, + -44.0, + 190, + 2 + ], + [ + 114, + 142, + -111, + -104, + -48.0, + 192, + 0 + ], + [ + 135, + 142, + -104, + -90, + -47.7, + 192, + 3 + ], + [ + -102, + -74, + 126, + 133, + -43.0, + 188, + 0 + ], + [ + -102, + -95, + 112, + 126, + -42.7, + 188, + 1 + ], + [ + 84, + 100, + -143, + -123, + -32.0, + 204, + 0 + ], + [ + -66, + -59, + -140, + -126, + -37.0, + 192, + 2 + ], + [ + -138, + -110, + -75, + -68, + -46.0, + 188, + 0 + ], + [ + 150, + 178, + -75, + -68, + -42.0, + 188, + 0 + ], + [ + -66, + -59, + 148, + 162, + -48.0, + 192, + 2 + ], + [ + 6, + 34, + -162, + -155, + -33.7, + 189, + 1 + ], + [ + -153, + -146, + 4, + 18, + -37.7, + 187, + 3 + ], + [ + -66, + -38, + -147, + -140, + -37.0, + 192, + 0 + ], + [ + -138, + -131, + -68, + -54, + -46.0, + 188, + 2 + ], + [ + -66, + -38, + 162, + 169, + -47.7, + 192, + 1 + ], + [ + 78, + 106, + 162, + 169, + -38.0, + 188, + 0 + ], + [ + -102, + -74, + -126, + -119, + -38.0, + 189, + 0 + ], + [ + 114, + 142, + -126, + -119, + -46.7, + 191, + 1 + ], + [ + 150, + 157, + -104, + -90, + -36.0, + 187, + 2 + ], + [ + 150, + 157, + 112, + 126, + -35.0, + 193, + 2 + ], + [ + -102, + -74, + 141, + 148, + -44.0, + 189, + 0 + ], + [ + 114, + 142, + 141, + 148, + -37.0, + 191, + 0 + ], + [ + -30, + -2, + -162, + -155, + -34.7, + 186, + 1 + ], + [ + 42, + 70, + -162, + -155, + -38.0, + 192, + 0 + ], + [ + -153, + -146, + -32, + -18, + -47.7, + 186, + 3 + ], + [ + 186, + 193, + -32, + -18, + -47.0, + 192, + 2 + ], + [ + -30, + -2, + 177, + 184, + -37.0, + 188, + 0 + ], + [ + 42, + 70, + 177, + 184, + -40.0, + 186, + 0 + ], + [ + 114, + 121, + -140, + -126, + -47.0, + 191, + 2 + ], + [ + -138, + -110, + -90, + -83, + -39.7, + 187, + 1 + ], + [ + 150, + 178, + -90, + -83, + -35.7, + 187, + 1 + ], + [ + 150, + 178, + 105, + 112, + -35.0, + 193, + 0 + ], + [ + 114, + 121, + 148, + 162, + -37.0, + 191, + 2 + ], + [ + 12, + 28, + 181, + 201, + -29.0, + 204, + 0 + ], + [ + 6, + 13, + -176, + -162, + -34.0, + 189, + 2 + ], + [ + -174, + -146, + -3, + 4, + -38.0, + 187, + 0 + ], + [ + -174, + -146, + 18, + 25, + -37.7, + 187, + 1 + ], + [ + -9, + -2, + -176, + -162, + -34.7, + 186, + 3 + ], + [ + 42, + 49, + -176, + -162, + -37.7, + 192, + 1 + ], + [ + -174, + -146, + -18, + -11, + -47.7, + 186, + 1 + ], + [ + 186, + 214, + -18, + -11, + -46.7, + 192, + 1 + ], + [ + 42, + 49, + 184, + 198, + -40.0, + 186, + 2 + ], + [ + 78, + 106, + -162, + -155, + -36.7, + 187, + 1 + ], + [ + -153, + -146, + -68, + -54, + -46.7, + 193, + 3 + ], + [ + 186, + 193, + -68, + -54, + -41.0, + 191, + 2 + ], + [ + 78, + 106, + 177, + 184, + -44.0, + 189, + 0 + ], + [ + -30, + -23, + -176, + -162, + -35.0, + 186, + 2 + ], + [ + -174, + -146, + -39, + -32, + -48.0, + 186, + 0 + ], + [ + 186, + 214, + -39, + -32, + -47.0, + 192, + 0 + ], + [ + -30, + -23, + 184, + 198, + -37.0, + 188, + 2 + ], + [ + -102, + -95, + -140, + -126, + -37.7, + 189, + 1 + ], + [ + 135, + 142, + -140, + -126, + -46.7, + 191, + 3 + ], + [ + -138, + -110, + -111, + -104, + -40.0, + 187, + 0 + ], + [ + 150, + 178, + -111, + -104, + -36.0, + 187, + 0 + ], + [ + -138, + -110, + 126, + 133, + -39.0, + 193, + 0 + ], + [ + 150, + 178, + 126, + 133, + -34.7, + 193, + 1 + ], + [ + -102, + -95, + 148, + 162, + -44.0, + 189, + 2 + ], + [ + 114, + 142, + -147, + -140, + -47.0, + 191, + 0 + ], + [ + -138, + -131, + -104, + -90, + -40.0, + 187, + 2 + ], + [ + -138, + -131, + 112, + 126, + -38.7, + 193, + 1 + ], + [ + 171, + 178, + 112, + 126, + -34.7, + 193, + 3 + ], + [ + -102, + -74, + 162, + 169, + -43.7, + 189, + 1 + ], + [ + 114, + 142, + 162, + 169, + -36.7, + 191, + 1 + ], + [ + 78, + 85, + -176, + -162, + -37.0, + 187, + 2 + ], + [ + -174, + -146, + -54, + -47, + -46.7, + 193, + 1 + ], + [ + 186, + 214, + -54, + -47, + -40.7, + 191, + 1 + ], + [ + 78, + 85, + 184, + 198, + -44.0, + 189, + 2 + ], + [ + 6, + 34, + -183, + -176, + -34.0, + 189, + 0 + ], + [ + -174, + -167, + 4, + 18, + -38.0, + 187, + 2 + ], + [ + -30, + -2, + -183, + -176, + -35.0, + 186, + 0 + ], + [ + -174, + -167, + -32, + -18, + -48.0, + 186, + 2 + ], + [ + 207, + 214, + -32, + -18, + -46.7, + 192, + 3 + ], + [ + -30, + -2, + 198, + 205, + -36.7, + 188, + 1 + ], + [ + 42, + 70, + 198, + 205, + -39.7, + 186, + 1 + ], + [ + -138, + -110, + -126, + -119, + -33.7, + 186, + 1 + ], + [ + 150, + 178, + -126, + -119, + -45.7, + 186, + 1 + ], + [ + 150, + 157, + -140, + -126, + -46.0, + 186, + 2 + ], + [ + -138, + -110, + 141, + 148, + -45.0, + 186, + 0 + ], + [ + -117, + -110, + 148, + 162, + -44.7, + 186, + 3 + ], + [ + -174, + -146, + -75, + -68, + -47.0, + 193, + 0 + ], + [ + 186, + 214, + -75, + -68, + -41.0, + 191, + 0 + ], + [ + -66, + -59, + 184, + 198, + -37.7, + 193, + 1 + ], + [ + 99, + 106, + 184, + 198, + -43.7, + 189, + 3 + ], + [ + -102, + -74, + -162, + -155, + -47.7, + 188, + 1 + ], + [ + 186, + 193, + -104, + -90, + -35.0, + 190, + 2 + ], + [ + -153, + -146, + 112, + 126, + -39.7, + 190, + 3 + ], + [ + 186, + 193, + 112, + 126, + -34.0, + 188, + 2 + ], + [ + -102, + -74, + 177, + 184, + -34.0, + 190, + 0 + ], + [ + 156, + 172, + 145, + 165, + -29.0, + 204, + 0 + ], + [ + 78, + 106, + -183, + -176, + -37.0, + 187, + 0 + ], + [ + -174, + -167, + -68, + -54, + -47.0, + 193, + 2 + ], + [ + 207, + 214, + -68, + -54, + -40.7, + 191, + 3 + ], + [ + -66, + -38, + 198, + 205, + -38.0, + 193, + 0 + ], + [ + 78, + 106, + 198, + 205, + -43.7, + 189, + 1 + ], + [ + -81, + -74, + -176, + -162, + -47.7, + 188, + 3 + ], + [ + 186, + 214, + -90, + -83, + -34.7, + 190, + 1 + ], + [ + -174, + -146, + 105, + 112, + -40.0, + 190, + 0 + ], + [ + 186, + 214, + 105, + 112, + -34.0, + 188, + 0 + ], + [ + 120, + 136, + -179, + -159, + -29.0, + 204, + 0 + ], + [ + -168, + -152, + -107, + -87, + -29.0, + 204, + 0 + ], + [ + 120, + 136, + 181, + 201, + -32.0, + 204, + 0 + ], + [ + -138, + -110, + -147, + -140, + -34.0, + 186, + 0 + ], + [ + -138, + -131, + -140, + -126, + -34.0, + 186, + 2 + ], + [ + 150, + 178, + -147, + -140, + -46.0, + 186, + 0 + ], + [ + -138, + -110, + 162, + 169, + -44.7, + 186, + 1 + ], + [ + -138, + -131, + 148, + 162, + -45.0, + 186, + 2 + ], + [ + -102, + -95, + -176, + -162, + -48.0, + 188, + 2 + ], + [ + 186, + 214, + -111, + -104, + -35.0, + 190, + 0 + ], + [ + -174, + -146, + 126, + 133, + -39.7, + 190, + 1 + ], + [ + 186, + 214, + 126, + 133, + -33.7, + 188, + 1 + ], + [ + -102, + -95, + 184, + 198, + -34.0, + 190, + 2 + ], + [ + -102, + -74, + -183, + -176, + -48.0, + 188, + 0 + ], + [ + 207, + 214, + -104, + -90, + -34.7, + 190, + 3 + ], + [ + -174, + -167, + 112, + 126, + -40.0, + 190, + 2 + ], + [ + -102, + -74, + 198, + 205, + -33.7, + 190, + 1 + ], + [ + -138, + -110, + -162, + -155, + -43.7, + 193, + 1 + ], + [ + 150, + 178, + -162, + -155, + -39.7, + 193, + 1 + ], + [ + -153, + -146, + -140, + -126, + -34.7, + 191, + 3 + ], + [ + -153, + -146, + 148, + 162, + -45.7, + 191, + 3 + ], + [ + 186, + 193, + 148, + 162, + -40.0, + 189, + 2 + ], + [ + -138, + -110, + 177, + 184, + -35.0, + 187, + 0 + ], + [ + 150, + 178, + 177, + 184, + -47.0, + 187, + 0 + ], + [ + 150, + 157, + -176, + -162, + -40.0, + 193, + 2 + ], + [ + -174, + -146, + -126, + -119, + -34.7, + 191, + 1 + ], + [ + -174, + -146, + 141, + 148, + -46.0, + 191, + 0 + ], + [ + 186, + 214, + 141, + 148, + -40.0, + 189, + 0 + ], + [ + -117, + -110, + 184, + 198, + -34.7, + 187, + 3 + ], + [ + 150, + 157, + 184, + 198, + -47.0, + 187, + 2 + ], + [ + 192, + 208, + -143, + -123, + -35.0, + 204, + 0 + ], + [ + -138, + -131, + -176, + -162, + -44.0, + 193, + 2 + ], + [ + -174, + -146, + -147, + -140, + -35.0, + 191, + 0 + ], + [ + -174, + -146, + 162, + 169, + -45.7, + 191, + 1 + ], + [ + 186, + 214, + 162, + 169, + -39.7, + 189, + 1 + ], + [ + -138, + -131, + 184, + 198, + -35.0, + 187, + 2 + ], + [ + 171, + 178, + 184, + 198, + -46.7, + 187, + 3 + ], + [ + -138, + -110, + -183, + -176, + -44.0, + 193, + 0 + ], + [ + 150, + 178, + -183, + -176, + -40.0, + 193, + 0 + ], + [ + -174, + -167, + -140, + -126, + -35.0, + 191, + 2 + ], + [ + -174, + -167, + 148, + 162, + -46.0, + 191, + 2 + ], + [ + -138, + -110, + 198, + 205, + -34.7, + 187, + 1 + ], + [ + 150, + 178, + 198, + 205, + -46.7, + 187, + 1 + ], + [ + -174, + -146, + -162, + -155, + -44.7, + 190, + 1 + ], + [ + -153, + -146, + -176, + -162, + -44.7, + 190, + 3 + ], + [ + 186, + 214, + -162, + -155, + -43.7, + 188, + 1 + ], + [ + 186, + 193, + -176, + -162, + -44.0, + 188, + 2 + ], + [ + 186, + 214, + 177, + 184, + -46.0, + 190, + 0 + ], + [ + 186, + 193, + 184, + 198, + -46.0, + 190, + 2 + ], + [ + -1, + 41, + -269, + -221, + -36, + 190, + 4 + ], + [ + -262, + -210, + -13, + 35, + -41, + 190, + 4 + ], + [ + -2, + 42, + 243, + 291, + -35, + 190, + 4 + ], + [ + -174, + -146, + -183, + -176, + -45.0, + 190, + 0 + ], + [ + -174, + -167, + -176, + -162, + -45.0, + 190, + 2 + ], + [ + 186, + 214, + -183, + -176, + -44.0, + 188, + 0 + ], + [ + 207, + 214, + -176, + -162, + -43.7, + 188, + 3 + ], + [ + 186, + 214, + 198, + 205, + -45.7, + 190, + 1 + ], + [ + -70, + -18, + -269, + -221, + -41, + 189, + 4 + ], + [ + 58, + 110, + -269, + -221, + -31, + 191, + 4 + ], + [ + -260, + -212, + -77, + -29, + -23, + 189, + 4 + ], + [ + 250, + 302, + -77, + -29, + -41, + 189, + 4 + ], + [ + -61, + -27, + 243, + 291, + -40, + 189, + 4 + ], + [ + 67, + 101, + 243, + 291, + -30, + 191, + 4 + ], + [ + -129, + -87, + -269, + -221, + -46, + 188, + 4 + ], + [ + 127, + 169, + -269, + -221, + -26, + 192, + 4 + ], + [ + -259, + -213, + -141, + -93, + -34, + 188, + 4 + ], + [ + 252, + 300, + -141, + -93, + -23, + 188, + 4 + ], + [ + -255, + -217, + 115, + 163, + -48, + 192, + 4 + ], + [ + 256, + 296, + 115, + 163, + -37, + 192, + 4 + ], + [ + -130, + -86, + 243, + 291, + -45, + 188, + 4 + ], + [ + 126, + 170, + 243, + 291, + -25, + 192, + 4 + ], + [ + 0, + 40, + -333, + -285, + -47, + 189, + 4 + ], + [ + -197, + -147, + -269, + -221, + -22, + 187, + 4 + ], + [ + 195, + 229, + -269, + -221, + -50, + 193, + 4 + ], + [ + -258, + -214, + -205, + -157, + -45, + 187, + 4 + ], + [ + 253, + 299, + -205, + -157, + -34, + 187, + 4 + ], + [ + -321, + -279, + -13, + 35, + -46, + 189, + 4 + ], + [ + -256, + -216, + 179, + 227, + -37, + 193, + 4 + ], + [ + 255, + 297, + 179, + 227, + -26, + 193, + 4 + ], + [ + -189, + -155, + 243, + 291, + -50, + 187, + 4 + ], + [ + 194, + 230, + 243, + 291, + -49, + 193, + 4 + ], + [ + -3, + 43, + 307, + 355, + -24, + 191, + 4 + ], + [ + -68, + -20, + -333, + -285, + -23, + 188, + 4 + ], + [ + 59, + 109, + -333, + -285, + -42, + 190, + 4 + ], + [ + -319, + -281, + -77, + -29, + -28, + 188, + 4 + ], + [ + 319, + 361, + -77, + -29, + -36, + 190, + 4 + ], + [ + -62, + -26, + 307, + 355, + -29, + 190, + 4 + ], + [ + 65, + 103, + 307, + 355, + -48, + 192, + 4 + ], + [ + 312, + 328, + -140, + -120, + 30, + 200, + 2 + ], + [ + 315, + 325, + -137, + -123, + 37, + 200, + 2 + ], + [ + -127, + -89, + -333, + -285, + -28, + 187, + 4 + ], + [ + 128, + 168, + -333, + -285, + -37, + 191, + 4 + ], + [ + -318, + -282, + -141, + -93, + -39, + 187, + 4 + ], + [ + 320, + 360, + -141, + -93, + -47, + 189, + 4 + ], + [ + -323, + -277, + 115, + 163, + -24, + 191, + 4 + ], + [ + 315, + 365, + 115, + 163, + -32, + 193, + 4 + ], + [ + -131, + -85, + 307, + 355, + -34, + 189, + 4 + ], + [ + 124, + 172, + 307, + 355, + -43, + 193, + 4 + ], + [ + -256, + -216, + -269, + -221, + -27, + 186, + 4 + ], + [ + 254, + 298, + -269, + -221, + -45, + 186, + 4 + ], + [ + -257, + -215, + 243, + 291, + -26, + 186, + 4 + ], + [ + 253, + 299, + 243, + 291, + -44, + 186, + 4 + ], + [ + -196, + -148, + -333, + -285, + -33, + 186, + 4 + ], + [ + 187, + 237, + -333, + -285, + -32, + 192, + 4 + ], + [ + -317, + -283, + -205, + -157, + -50, + 186, + 4 + ], + [ + 322, + 358, + -205, + -157, + -29, + 188, + 4 + ], + [ + -325, + -275, + 179, + 227, + -42, + 192, + 4 + ], + [ + 323, + 357, + 179, + 227, + -50, + 186, + 4 + ], + [ + -190, + -154, + 307, + 355, + -39, + 188, + 4 + ], + [ + 193, + 231, + 307, + 355, + -38, + 186, + 4 + ], + [ + 347, + 363, + -175, + -155, + 66, + 201, + 2 + ], + [ + 350, + 360, + -172, + -158, + 73, + 201, + 2 + ], + [ + 2, + 38, + -397, + -349, + -29, + 188, + 4 + ], + [ + -389, + -339, + -13, + 35, + -22, + 188, + 4 + ], + [ + -5, + 45, + 371, + 419, + -42, + 192, + 4 + ], + [ + -67, + -21, + -397, + -349, + -34, + 187, + 4 + ], + [ + 61, + 107, + -397, + -349, + -24, + 189, + 4 + ], + [ + -388, + -340, + -77, + -29, + -33, + 187, + 4 + ], + [ + 378, + 430, + -77, + -29, + -31, + 191, + 4 + ], + [ + -64, + -24, + 371, + 419, + -47, + 191, + 4 + ], + [ + 64, + 104, + 371, + 419, + -37, + 193, + 4 + ], + [ + 382, + 398, + -135, + -115, + 48, + 202, + 2 + ], + [ + 385, + 395, + -132, + -118, + 55, + 202, + 2 + ], + [ + 337, + 353, + -235, + -215, + 55, + 202, + 2 + ], + [ + 340, + 350, + -232, + -218, + 62, + 202, + 2 + ], + [ + -126, + -90, + -397, + -349, + -39, + 186, + 4 + ], + [ + 129, + 167, + -397, + -349, + -48, + 190, + 4 + ], + [ + -387, + -341, + -141, + -93, + -44, + 186, + 4 + ], + [ + 379, + 429, + -141, + -93, + -42, + 190, + 4 + ], + [ + -382, + -346, + 115, + 163, + -29, + 190, + 4 + ], + [ + 384, + 424, + 115, + 163, + -27, + 186, + 4 + ], + [ + -132, + -84, + 371, + 419, + -23, + 190, + 4 + ], + [ + 123, + 173, + 371, + 419, + -32, + 186, + 4 + ], + [ + -255, + -217, + -333, + -285, + -38, + 193, + 4 + ], + [ + 256, + 296, + -333, + -285, + -27, + 193, + 4 + ], + [ + -325, + -275, + -269, + -221, + -32, + 193, + 4 + ], + [ + 323, + 357, + -269, + -221, + -40, + 187, + 4 + ], + [ + -326, + -274, + 243, + 291, + -31, + 193, + 4 + ], + [ + 322, + 358, + 243, + 291, + -39, + 187, + 4 + ], + [ + -259, + -213, + 307, + 355, + -44, + 187, + 4 + ], + [ + 252, + 300, + 307, + 355, + -33, + 187, + 4 + ], + [ + -195, + -149, + -397, + -349, + -44, + 193, + 4 + ], + [ + 188, + 236, + -397, + -349, + -43, + 191, + 4 + ], + [ + -385, + -343, + -205, + -157, + -26, + 193, + 4 + ], + [ + 381, + 427, + -205, + -157, + -24, + 189, + 4 + ], + [ + -384, + -344, + 179, + 227, + -47, + 191, + 4 + ], + [ + 382, + 426, + 179, + 227, + -45, + 187, + 4 + ], + [ + -191, + -153, + 371, + 419, + -28, + 189, + 4 + ], + [ + 192, + 232, + 371, + 419, + -27, + 187, + 4 + ], + [ + 3, + 37, + -461, + -413, + -40, + 187, + 4 + ], + [ + -448, + -408, + -13, + 35, + -27, + 187, + 4 + ], + [ + -6, + 46, + 435, + 483, + -31, + 193, + 4 + ], + [ + -66, + -22, + -461, + -413, + -45, + 186, + 4 + ], + [ + 62, + 106, + -461, + -413, + -35, + 188, + 4 + ], + [ + -324, + -276, + -333, + -285, + -43, + 192, + 4 + ], + [ + 315, + 365, + -333, + -285, + -22, + 186, + 4 + ], + [ + -447, + -409, + -77, + -29, + -38, + 186, + 4 + ], + [ + 447, + 489, + -77, + -29, + -26, + 192, + 4 + ], + [ + -318, + -282, + 307, + 355, + -49, + 186, + 4 + ], + [ + 321, + 359, + 307, + 355, + -28, + 188, + 4 + ], + [ + -65, + -23, + 435, + 483, + -36, + 192, + 4 + ], + [ + 63, + 105, + 435, + 483, + -26, + 186, + 4 + ], + [ + 417, + 433, + -205, + -185, + 95, + 200, + 2 + ], + [ + 420, + 430, + -202, + -188, + 102, + 200, + 2 + ], + [ + -254, + -218, + -397, + -349, + -49, + 192, + 4 + ], + [ + 257, + 295, + -397, + -349, + -38, + 192, + 4 + ], + [ + -384, + -344, + -269, + -221, + -37, + 192, + 4 + ], + [ + 382, + 426, + -269, + -221, + -35, + 188, + 4 + ], + [ + -385, + -343, + 243, + 291, + -36, + 192, + 4 + ], + [ + 381, + 427, + 243, + 291, + -34, + 188, + 4 + ], + [ + -260, + -212, + 371, + 419, + -33, + 188, + 4 + ], + [ + 251, + 301, + 371, + 419, + -22, + 188, + 4 + ], + [ + -125, + -91, + -461, + -413, + -50, + 193, + 4 + ], + [ + 131, + 165, + -461, + -413, + -30, + 189, + 4 + ], + [ + -446, + -410, + -141, + -93, + -49, + 193, + 4 + ], + [ + 448, + 488, + -141, + -93, + -37, + 191, + 4 + ], + [ + -451, + -405, + 115, + 163, + -34, + 189, + 4 + ], + [ + 443, + 493, + 115, + 163, + -22, + 187, + 4 + ], + [ + -134, + -82, + 435, + 483, + -41, + 191, + 4 + ], + [ + 131, + 165, + 435, + 483, + -50, + 187, + 4 + ], + [ + 452, + 468, + -155, + -135, + 42, + 201, + 2 + ], + [ + 455, + 465, + -152, + -138, + 49, + 201, + 2 + ], + [ + 397, + 413, + -270, + -250, + 72, + 200, + 2 + ], + [ + 400, + 410, + -267, + -253, + 79, + 200, + 2 + ], + [ + -193, + -151, + -461, + -413, + -26, + 192, + 4 + ], + [ + 190, + 234, + -461, + -413, + -25, + 190, + 4 + ], + [ + -454, + -402, + -205, + -157, + -31, + 192, + 4 + ], + [ + 449, + 487, + -205, + -157, + -48, + 190, + 4 + ], + [ + -452, + -404, + 179, + 227, + -23, + 190, + 4 + ], + [ + 451, + 485, + 179, + 227, + -40, + 188, + 4 + ], + [ + -193, + -151, + 435, + 483, + -46, + 190, + 4 + ], + [ + 190, + 234, + 435, + 483, + -45, + 188, + 4 + ], + [ + -322, + -278, + -397, + -349, + -25, + 191, + 4 + ], + [ + 316, + 364, + -397, + -349, + -33, + 193, + 4 + ], + [ + -383, + -345, + -333, + -285, + -48, + 191, + 4 + ], + [ + 383, + 425, + -333, + -285, + -46, + 187, + 4 + ], + [ + -386, + -342, + 307, + 355, + -25, + 193, + 4 + ], + [ + 380, + 428, + 307, + 355, + -23, + 189, + 4 + ], + [ + -319, + -281, + 371, + 419, + -38, + 187, + 4 + ], + [ + 319, + 361, + 371, + 419, + -46, + 189, + 4 + ], + [ + -5, + 45, + -525, + -477, + -22, + 186, + 4 + ], + [ + -517, + -467, + -13, + 35, + -32, + 186, + 4 + ], + [ + 2, + 38, + 499, + 547, + -49, + 186, + 4 + ], + [ + -64, + -24, + -525, + -477, + -27, + 193, + 4 + ], + [ + 63, + 105, + -525, + -477, + -46, + 187, + 4 + ], + [ + -262, + -210, + -461, + -413, + -31, + 191, + 4 + ], + [ + 258, + 294, + -461, + -413, + -49, + 191, + 4 + ], + [ + -453, + -403, + -269, + -221, + -42, + 191, + 4 + ], + [ + 451, + 485, + -269, + -221, + -30, + 189, + 4 + ], + [ + -516, + -468, + -77, + -29, + -43, + 193, + 4 + ], + [ + 515, + 549, + -77, + -29, + -50, + 193, + 4 + ], + [ + -454, + -402, + 243, + 291, + -41, + 191, + 4 + ], + [ + 450, + 486, + 243, + 291, + -29, + 189, + 4 + ], + [ + -261, + -211, + 435, + 483, + -22, + 189, + 4 + ], + [ + 259, + 293, + 435, + 483, + -40, + 189, + 4 + ], + [ + -66, + -22, + 499, + 547, + -25, + 193, + 4 + ], + [ + 61, + 107, + 499, + 547, + -44, + 187, + 4 + ], + [ + 467, + 483, + -250, + -230, + 28, + 201, + 2 + ], + [ + 470, + 480, + -247, + -233, + 35, + 201, + 2 + ], + [ + -133, + -83, + -525, + -477, + -32, + 192, + 4 + ], + [ + 122, + 174, + -525, + -477, + -41, + 188, + 4 + ], + [ + -514, + -470, + -141, + -93, + -25, + 192, + 4 + ], + [ + 507, + 557, + -141, + -93, + -32, + 192, + 4 + ], + [ + -510, + -474, + 115, + 163, + -39, + 188, + 4 + ], + [ + 511, + 553, + 115, + 163, + -46, + 188, + 4 + ], + [ + -125, + -91, + 499, + 547, + -30, + 192, + 4 + ], + [ + 130, + 166, + 499, + 547, + -39, + 188, + 4 + ], + [ + -381, + -347, + -397, + -349, + -30, + 190, + 4 + ], + [ + 385, + 423, + -397, + -349, + -28, + 186, + 4 + ], + [ + -388, + -340, + 371, + 419, + -43, + 186, + 4 + ], + [ + 378, + 430, + 371, + 419, + -41, + 190, + 4 + ], + [ + -192, + -152, + -525, + -477, + -37, + 191, + 4 + ], + [ + 191, + 233, + -525, + -477, + -36, + 189, + 4 + ], + [ + -513, + -471, + -205, + -157, + -36, + 191, + 4 + ], + [ + 508, + 556, + -205, + -157, + -43, + 191, + 4 + ], + [ + -511, + -473, + 179, + 227, + -28, + 189, + 4 + ], + [ + 510, + 554, + 179, + 227, + -35, + 189, + 4 + ], + [ + -194, + -150, + 499, + 547, + -35, + 191, + 4 + ], + [ + 189, + 235, + 499, + 547, + -34, + 189, + 4 + ], + [ + 559, + 581, + -4, + 16, + -44, + 186, + 4 + ], + [ + -321, + -279, + -461, + -413, + -36, + 190, + 4 + ], + [ + 317, + 363, + -461, + -413, + -44, + 192, + 4 + ], + [ + -451, + -405, + -333, + -285, + -24, + 190, + 4 + ], + [ + 442, + 494, + -333, + -285, + -41, + 188, + 4 + ], + [ + -445, + -411, + 307, + 355, + -30, + 192, + 4 + ], + [ + 448, + 488, + 307, + 355, + -47, + 190, + 4 + ], + [ + -320, + -280, + 435, + 483, + -27, + 188, + 4 + ], + [ + 318, + 362, + 435, + 483, + -35, + 190, + 4 + ], + [ + 559, + 581, + 96, + 116, + -44, + 186, + 4 + ], + [ + -261, + -211, + -525, + -477, + -42, + 190, + 4 + ], + [ + 250, + 302, + -525, + -477, + -31, + 190, + 4 + ], + [ + -512, + -472, + -269, + -221, + -47, + 190, + 4 + ], + [ + 510, + 554, + -269, + -221, + -25, + 190, + 4 + ], + [ + -513, + -471, + 243, + 291, + -46, + 190, + 4 + ], + [ + 509, + 555, + 243, + 291, + -24, + 190, + 4 + ], + [ + -253, + -219, + 499, + 547, + -40, + 190, + 4 + ], + [ + 258, + 294, + 499, + 547, + -29, + 190, + 4 + ], + [ + 593, + 615, + -12, + 8, + -37, + 187, + 4 + ], + [ + -390, + -338, + -461, + -413, + -41, + 189, + 4 + ], + [ + 386, + 422, + -461, + -413, + -39, + 193, + 4 + ], + [ + -450, + -406, + -397, + -349, + -35, + 189, + 4 + ], + [ + 444, + 492, + -397, + -349, + -23, + 187, + 4 + ], + [ + -447, + -409, + 371, + 419, + -48, + 193, + 4 + ], + [ + 447, + 489, + 371, + 419, + -36, + 191, + 4 + ], + [ + -389, + -339, + 435, + 483, + -32, + 187, + 4 + ], + [ + 387, + 421, + 435, + 483, + -30, + 191, + 4 + ], + [ + 593, + 615, + 105, + 125, + -37, + 187, + 4 + ], + [ + -320, + -280, + -525, + -477, + -47, + 189, + 4 + ], + [ + 319, + 361, + -525, + -477, + -26, + 191, + 4 + ], + [ + -510, + -474, + -333, + -285, + -29, + 189, + 4 + ], + [ + 511, + 553, + -333, + -285, + -36, + 189, + 4 + ], + [ + -514, + -470, + 307, + 355, + -35, + 191, + 4 + ], + [ + 507, + 557, + 307, + 355, + -42, + 191, + 4 + ], + [ + -322, + -278, + 499, + 547, + -45, + 189, + 4 + ], + [ + 317, + 363, + 499, + 547, + -24, + 191, + 4 + ], + [ + 627, + 649, + -20, + 0, + -30, + 188, + 4 + ], + [ + 627, + 649, + 114, + 134, + -30, + 188, + 4 + ], + [ + -449, + -407, + -461, + -413, + -46, + 188, + 4 + ], + [ + 445, + 491, + -461, + -413, + -34, + 186, + 4 + ], + [ + -448, + -408, + 435, + 483, + -37, + 186, + 4 + ], + [ + 446, + 490, + 435, + 483, + -25, + 192, + 4 + ], + [ + -388, + -340, + -525, + -477, + -23, + 188, + 4 + ], + [ + 387, + 421, + -525, + -477, + -50, + 192, + 4 + ], + [ + -509, + -475, + -397, + -349, + -40, + 188, + 4 + ], + [ + 512, + 552, + -397, + -349, + -47, + 188, + 4 + ], + [ + -515, + -469, + 371, + 419, + -24, + 192, + 4 + ], + [ + 506, + 558, + 371, + 419, + -31, + 192, + 4 + ], + [ + -381, + -347, + 499, + 547, + -50, + 188, + 4 + ], + [ + 385, + 423, + 499, + 547, + -48, + 192, + 4 + ], + [ + 661, + 683, + -4, + 16, + -23, + 189, + 4 + ], + [ + 661, + 683, + 123, + 143, + -23, + 189, + 4 + ], + [ + -447, + -409, + -525, + -477, + -28, + 187, + 4 + ], + [ + 446, + 490, + -525, + -477, + -45, + 193, + 4 + ], + [ + -517, + -467, + -461, + -413, + -22, + 187, + 4 + ], + [ + 514, + 550, + -461, + -413, + -29, + 187, + 4 + ], + [ + -517, + -467, + 435, + 483, + -42, + 193, + 4 + ], + [ + 514, + 550, + 435, + 483, + -49, + 193, + 4 + ], + [ + -449, + -407, + 499, + 547, + -26, + 187, + 4 + ], + [ + 444, + 492, + 499, + 547, + -43, + 193, + 4 + ], + [ + 695, + 717, + -12, + 8, + -40, + 190, + 4 + ], + [ + 695, + 717, + 96, + 116, + -40, + 190, + 4 + ], + [ + 729, + 751, + -20, + 0, + -33, + 191, + 4 + ], + [ + -516, + -468, + -525, + -477, + -33, + 186, + 4 + ], + [ + 515, + 549, + -525, + -477, + -40, + 186, + 4 + ], + [ + -518, + -466, + 499, + 547, + -31, + 186, + 4 + ], + [ + 513, + 551, + 499, + 547, + -38, + 186, + 4 + ], + [ + 729, + 751, + 105, + 125, + -33, + 191, + 4 + ], + [ + 763, + 785, + -4, + 16, + -26, + 192, + 4 + ], + [ + 763, + 785, + 114, + 134, + -26, + 192, + 4 + ], + [ + 797, + 819, + -12, + 8, + -43, + 193, + 4 + ], + [ + 797, + 819, + 123, + 143, + -43, + 193, + 4 + ], + [ + 831, + 853, + -20, + 0, + -36, + 186, + 4 + ], + [ + 831, + 853, + 96, + 116, + -36, + 186, + 4 + ], + [ + 865, + 887, + -4, + 16, + -29, + 187, + 4 + ], + [ + 865, + 887, + 105, + 125, + -29, + 187, + 4 + ] + ], + "landmarks": { + "dome": [ + 20, + 119 + ], + "spire": [ + 164, + 11 + ] + } +} diff --git a/assets/sprites/rooftop/materials-v1.png b/assets/sprites/rooftop/materials-v1.png new file mode 100644 index 0000000..6a5ada0 Binary files /dev/null and b/assets/sprites/rooftop/materials-v1.png differ diff --git a/assets/sprites/rooftop/mesh-v1.json b/assets/sprites/rooftop/mesh-v1.json new file mode 100644 index 0000000..503dbef --- /dev/null +++ b/assets/sprites/rooftop/mesh-v1.json @@ -0,0 +1,6665 @@ +{ + "format": "RGM1: count/bytes + material/3*(position,normal,uv)", + "gl_axes": "X/Z/Y; normals explicitly outward; no runtime model engine", + "triangles": { + "scene": 35386, + "car": 360, + "boat": 170 + }, + "road_rectangles": [ + [ + -542, + -534, + -600, + -551 + ], + [ + -542, + -534, + -543, + -515 + ], + [ + -542, + -534, + -507, + -479 + ], + [ + -542, + -534, + -471, + -443 + ], + [ + -542, + -534, + -435, + -407 + ], + [ + -542, + -534, + -399, + -371 + ], + [ + -542, + -534, + -363, + -335 + ], + [ + -542, + -534, + -327, + -299 + ], + [ + -542, + -534, + -291, + -263 + ], + [ + -542, + -534, + -255, + -227 + ], + [ + -542, + -534, + -219, + -191 + ], + [ + -542, + -534, + -183, + -155 + ], + [ + -542, + -534, + -147, + -119 + ], + [ + -542, + -534, + -111, + -83 + ], + [ + -542, + -534, + -75, + -47 + ], + [ + -542, + -534, + -39, + -11 + ], + [ + -542, + -534, + -3, + 25 + ], + [ + -542, + -534, + 33, + 58 + ], + [ + -542, + -534, + 79, + 97 + ], + [ + -542, + -534, + 105, + 133 + ], + [ + -542, + -534, + 141, + 169 + ], + [ + -542, + -534, + 177, + 205 + ], + [ + -542, + -534, + 213, + 241 + ], + [ + -542, + -534, + 249, + 277 + ], + [ + -542, + -534, + 285, + 313 + ], + [ + -542, + -534, + 321, + 349 + ], + [ + -542, + -534, + 357, + 385 + ], + [ + -542, + -534, + 393, + 421 + ], + [ + -542, + -534, + 429, + 457 + ], + [ + -542, + -534, + 465, + 493 + ], + [ + -542, + -534, + 501, + 529 + ], + [ + -542, + -534, + 537, + 565 + ], + [ + -542, + -534, + 573, + 600 + ], + [ + -600, + 600, + -551, + -543 + ], + [ + -506, + -498, + -600, + -551 + ], + [ + -506, + -498, + -543, + -515 + ], + [ + -506, + -498, + -507, + -479 + ], + [ + -506, + -498, + -471, + -443 + ], + [ + -506, + -498, + -435, + -407 + ], + [ + -506, + -498, + -399, + -371 + ], + [ + -506, + -498, + -363, + -335 + ], + [ + -506, + -498, + -327, + -299 + ], + [ + -506, + -498, + -291, + -263 + ], + [ + -506, + -498, + -255, + -227 + ], + [ + -506, + -498, + -219, + -191 + ], + [ + -506, + -498, + -183, + -155 + ], + [ + -506, + -498, + -147, + -119 + ], + [ + -506, + -498, + -111, + -83 + ], + [ + -506, + -498, + -75, + -47 + ], + [ + -506, + -498, + -39, + -11 + ], + [ + -506, + -498, + -3, + 25 + ], + [ + -506, + -498, + 33, + 58 + ], + [ + -506, + -498, + 79, + 97 + ], + [ + -506, + -498, + 105, + 133 + ], + [ + -506, + -498, + 141, + 169 + ], + [ + -506, + -498, + 177, + 205 + ], + [ + -506, + -498, + 213, + 241 + ], + [ + -506, + -498, + 249, + 277 + ], + [ + -506, + -498, + 285, + 313 + ], + [ + -506, + -498, + 321, + 349 + ], + [ + -506, + -498, + 357, + 385 + ], + [ + -506, + -498, + 393, + 421 + ], + [ + -506, + -498, + 429, + 457 + ], + [ + -506, + -498, + 465, + 493 + ], + [ + -506, + -498, + 501, + 529 + ], + [ + -506, + -498, + 537, + 565 + ], + [ + -506, + -498, + 573, + 600 + ], + [ + -600, + 600, + -515, + -507 + ], + [ + -470, + -462, + -600, + -551 + ], + [ + -470, + -462, + -543, + -515 + ], + [ + -470, + -462, + -507, + -479 + ], + [ + -470, + -462, + -471, + -443 + ], + [ + -470, + -462, + -435, + -407 + ], + [ + -470, + -462, + -399, + -371 + ], + [ + -470, + -462, + -363, + -335 + ], + [ + -470, + -462, + -327, + -299 + ], + [ + -470, + -462, + -291, + -263 + ], + [ + -470, + -462, + -255, + -227 + ], + [ + -470, + -462, + -219, + -191 + ], + [ + -470, + -462, + -183, + -155 + ], + [ + -470, + -462, + -147, + -119 + ], + [ + -470, + -462, + -111, + -83 + ], + [ + -470, + -462, + -75, + -47 + ], + [ + -470, + -462, + -39, + -11 + ], + [ + -470, + -462, + -3, + 25 + ], + [ + -470, + -462, + 33, + 58 + ], + [ + -470, + -462, + 79, + 97 + ], + [ + -470, + -462, + 105, + 133 + ], + [ + -470, + -462, + 141, + 169 + ], + [ + -470, + -462, + 177, + 205 + ], + [ + -470, + -462, + 213, + 241 + ], + [ + -470, + -462, + 249, + 277 + ], + [ + -470, + -462, + 285, + 313 + ], + [ + -470, + -462, + 321, + 349 + ], + [ + -470, + -462, + 357, + 385 + ], + [ + -470, + -462, + 393, + 421 + ], + [ + -470, + -462, + 429, + 457 + ], + [ + -470, + -462, + 465, + 493 + ], + [ + -470, + -462, + 501, + 529 + ], + [ + -470, + -462, + 537, + 565 + ], + [ + -470, + -462, + 573, + 600 + ], + [ + -600, + 600, + -479, + -471 + ], + [ + -434, + -426, + -600, + -551 + ], + [ + -434, + -426, + -543, + -515 + ], + [ + -434, + -426, + -507, + -479 + ], + [ + -434, + -426, + -471, + -443 + ], + [ + -434, + -426, + -435, + -407 + ], + [ + -434, + -426, + -399, + -371 + ], + [ + -434, + -426, + -363, + -335 + ], + [ + -434, + -426, + -327, + -299 + ], + [ + -434, + -426, + -291, + -263 + ], + [ + -434, + -426, + -255, + -227 + ], + [ + -434, + -426, + -219, + -191 + ], + [ + -434, + -426, + -183, + -155 + ], + [ + -434, + -426, + -147, + -119 + ], + [ + -434, + -426, + -111, + -83 + ], + [ + -434, + -426, + -75, + -47 + ], + [ + -434, + -426, + -39, + -11 + ], + [ + -434, + -426, + -3, + 25 + ], + [ + -434, + -426, + 33, + 58 + ], + [ + -434, + -426, + 79, + 97 + ], + [ + -434, + -426, + 105, + 133 + ], + [ + -434, + -426, + 141, + 169 + ], + [ + -434, + -426, + 177, + 205 + ], + [ + -434, + -426, + 213, + 241 + ], + [ + -434, + -426, + 249, + 277 + ], + [ + -434, + -426, + 285, + 313 + ], + [ + -434, + -426, + 321, + 349 + ], + [ + -434, + -426, + 357, + 385 + ], + [ + -434, + -426, + 393, + 421 + ], + [ + -434, + -426, + 429, + 457 + ], + [ + -434, + -426, + 465, + 493 + ], + [ + -434, + -426, + 501, + 529 + ], + [ + -434, + -426, + 537, + 565 + ], + [ + -434, + -426, + 573, + 600 + ], + [ + -600, + 600, + -443, + -435 + ], + [ + -398, + -390, + -600, + -551 + ], + [ + -398, + -390, + -543, + -515 + ], + [ + -398, + -390, + -507, + -479 + ], + [ + -398, + -390, + -471, + -443 + ], + [ + -398, + -390, + -435, + -407 + ], + [ + -398, + -390, + -399, + -371 + ], + [ + -398, + -390, + -363, + -335 + ], + [ + -398, + -390, + -327, + -299 + ], + [ + -398, + -390, + -291, + -263 + ], + [ + -398, + -390, + -255, + -227 + ], + [ + -398, + -390, + -219, + -191 + ], + [ + -398, + -390, + -183, + -155 + ], + [ + -398, + -390, + -147, + -119 + ], + [ + -398, + -390, + -111, + -83 + ], + [ + -398, + -390, + -75, + -47 + ], + [ + -398, + -390, + -39, + -11 + ], + [ + -398, + -390, + -3, + 25 + ], + [ + -398, + -390, + 33, + 58 + ], + [ + -398, + -390, + 79, + 97 + ], + [ + -398, + -390, + 105, + 133 + ], + [ + -398, + -390, + 141, + 169 + ], + [ + -398, + -390, + 177, + 205 + ], + [ + -398, + -390, + 213, + 241 + ], + [ + -398, + -390, + 249, + 277 + ], + [ + -398, + -390, + 285, + 313 + ], + [ + -398, + -390, + 321, + 349 + ], + [ + -398, + -390, + 357, + 385 + ], + [ + -398, + -390, + 393, + 421 + ], + [ + -398, + -390, + 429, + 457 + ], + [ + -398, + -390, + 465, + 493 + ], + [ + -398, + -390, + 501, + 529 + ], + [ + -398, + -390, + 537, + 565 + ], + [ + -398, + -390, + 573, + 600 + ], + [ + -600, + 600, + -407, + -399 + ], + [ + -362, + -354, + -600, + -551 + ], + [ + -362, + -354, + -543, + -515 + ], + [ + -362, + -354, + -507, + -479 + ], + [ + -362, + -354, + -471, + -443 + ], + [ + -362, + -354, + -435, + -407 + ], + [ + -362, + -354, + -399, + -371 + ], + [ + -362, + -354, + -363, + -335 + ], + [ + -362, + -354, + -327, + -299 + ], + [ + -362, + -354, + -291, + -263 + ], + [ + -362, + -354, + -255, + -227 + ], + [ + -362, + -354, + -219, + -191 + ], + [ + -362, + -354, + -183, + -155 + ], + [ + -362, + -354, + -147, + -119 + ], + [ + -362, + -354, + -111, + -83 + ], + [ + -362, + -354, + -75, + -47 + ], + [ + -362, + -354, + -39, + -11 + ], + [ + -362, + -354, + -3, + 25 + ], + [ + -362, + -354, + 33, + 58 + ], + [ + -362, + -354, + 79, + 97 + ], + [ + -362, + -354, + 105, + 133 + ], + [ + -362, + -354, + 141, + 169 + ], + [ + -362, + -354, + 177, + 205 + ], + [ + -362, + -354, + 213, + 241 + ], + [ + -362, + -354, + 249, + 277 + ], + [ + -362, + -354, + 285, + 313 + ], + [ + -362, + -354, + 321, + 349 + ], + [ + -362, + -354, + 357, + 385 + ], + [ + -362, + -354, + 393, + 421 + ], + [ + -362, + -354, + 429, + 457 + ], + [ + -362, + -354, + 465, + 493 + ], + [ + -362, + -354, + 501, + 529 + ], + [ + -362, + -354, + 537, + 565 + ], + [ + -362, + -354, + 573, + 600 + ], + [ + -600, + 600, + -371, + -363 + ], + [ + -326, + -318, + -600, + -551 + ], + [ + -326, + -318, + -543, + -515 + ], + [ + -326, + -318, + -507, + -479 + ], + [ + -326, + -318, + -471, + -443 + ], + [ + -326, + -318, + -435, + -407 + ], + [ + -326, + -318, + -399, + -371 + ], + [ + -326, + -318, + -363, + -335 + ], + [ + -326, + -318, + -327, + -299 + ], + [ + -326, + -318, + -291, + -263 + ], + [ + -326, + -318, + -255, + -227 + ], + [ + -326, + -318, + -219, + -191 + ], + [ + -326, + -318, + -183, + -155 + ], + [ + -326, + -318, + -147, + -119 + ], + [ + -326, + -318, + -111, + -83 + ], + [ + -326, + -318, + -75, + -47 + ], + [ + -326, + -318, + -39, + -11 + ], + [ + -326, + -318, + -3, + 25 + ], + [ + -326, + -318, + 33, + 58 + ], + [ + -326, + -318, + 79, + 97 + ], + [ + -326, + -318, + 105, + 133 + ], + [ + -326, + -318, + 141, + 169 + ], + [ + -326, + -318, + 177, + 205 + ], + [ + -326, + -318, + 213, + 241 + ], + [ + -326, + -318, + 249, + 277 + ], + [ + -326, + -318, + 285, + 313 + ], + [ + -326, + -318, + 321, + 349 + ], + [ + -326, + -318, + 357, + 385 + ], + [ + -326, + -318, + 393, + 421 + ], + [ + -326, + -318, + 429, + 457 + ], + [ + -326, + -318, + 465, + 493 + ], + [ + -326, + -318, + 501, + 529 + ], + [ + -326, + -318, + 537, + 565 + ], + [ + -326, + -318, + 573, + 600 + ], + [ + -600, + 600, + -335, + -327 + ], + [ + -290, + -282, + -600, + -551 + ], + [ + -290, + -282, + -543, + -515 + ], + [ + -290, + -282, + -507, + -479 + ], + [ + -290, + -282, + -471, + -443 + ], + [ + -290, + -282, + -435, + -407 + ], + [ + -290, + -282, + -399, + -371 + ], + [ + -290, + -282, + -363, + -335 + ], + [ + -290, + -282, + -327, + -299 + ], + [ + -290, + -282, + -291, + -263 + ], + [ + -290, + -282, + -255, + -227 + ], + [ + -290, + -282, + -219, + -191 + ], + [ + -290, + -282, + -183, + -155 + ], + [ + -290, + -282, + -147, + -119 + ], + [ + -290, + -282, + -111, + -83 + ], + [ + -290, + -282, + -75, + -47 + ], + [ + -290, + -282, + -39, + -11 + ], + [ + -290, + -282, + -3, + 25 + ], + [ + -290, + -282, + 33, + 58 + ], + [ + -290, + -282, + 79, + 97 + ], + [ + -290, + -282, + 105, + 133 + ], + [ + -290, + -282, + 141, + 169 + ], + [ + -290, + -282, + 177, + 205 + ], + [ + -290, + -282, + 213, + 241 + ], + [ + -290, + -282, + 249, + 277 + ], + [ + -290, + -282, + 285, + 313 + ], + [ + -290, + -282, + 321, + 349 + ], + [ + -290, + -282, + 357, + 385 + ], + [ + -290, + -282, + 393, + 421 + ], + [ + -290, + -282, + 429, + 457 + ], + [ + -290, + -282, + 465, + 493 + ], + [ + -290, + -282, + 501, + 529 + ], + [ + -290, + -282, + 537, + 565 + ], + [ + -290, + -282, + 573, + 600 + ], + [ + -600, + 600, + -299, + -291 + ], + [ + -254, + -246, + -600, + -551 + ], + [ + -254, + -246, + -543, + -515 + ], + [ + -254, + -246, + -507, + -479 + ], + [ + -254, + -246, + -471, + -443 + ], + [ + -254, + -246, + -435, + -407 + ], + [ + -254, + -246, + -399, + -371 + ], + [ + -254, + -246, + -363, + -335 + ], + [ + -254, + -246, + -327, + -299 + ], + [ + -254, + -246, + -291, + -263 + ], + [ + -254, + -246, + -255, + -227 + ], + [ + -254, + -246, + -219, + -191 + ], + [ + -254, + -246, + -183, + -155 + ], + [ + -254, + -246, + -147, + -119 + ], + [ + -254, + -246, + -111, + -83 + ], + [ + -254, + -246, + -75, + -47 + ], + [ + -254, + -246, + -39, + -11 + ], + [ + -254, + -246, + -3, + 25 + ], + [ + -254, + -246, + 33, + 58 + ], + [ + -254, + -246, + 79, + 97 + ], + [ + -254, + -246, + 105, + 133 + ], + [ + -254, + -246, + 141, + 169 + ], + [ + -254, + -246, + 177, + 205 + ], + [ + -254, + -246, + 213, + 241 + ], + [ + -254, + -246, + 249, + 277 + ], + [ + -254, + -246, + 285, + 313 + ], + [ + -254, + -246, + 321, + 349 + ], + [ + -254, + -246, + 357, + 385 + ], + [ + -254, + -246, + 393, + 421 + ], + [ + -254, + -246, + 429, + 457 + ], + [ + -254, + -246, + 465, + 493 + ], + [ + -254, + -246, + 501, + 529 + ], + [ + -254, + -246, + 537, + 565 + ], + [ + -254, + -246, + 573, + 600 + ], + [ + -600, + 600, + -263, + -255 + ], + [ + -218, + -210, + -600, + -551 + ], + [ + -218, + -210, + -543, + -515 + ], + [ + -218, + -210, + -507, + -479 + ], + [ + -218, + -210, + -471, + -443 + ], + [ + -218, + -210, + -435, + -407 + ], + [ + -218, + -210, + -399, + -371 + ], + [ + -218, + -210, + -363, + -335 + ], + [ + -218, + -210, + -327, + -299 + ], + [ + -218, + -210, + -291, + -263 + ], + [ + -218, + -210, + -255, + -227 + ], + [ + -218, + -210, + -219, + -191 + ], + [ + -218, + -210, + -183, + -155 + ], + [ + -218, + -210, + -147, + -119 + ], + [ + -218, + -210, + -111, + -83 + ], + [ + -218, + -210, + -75, + -47 + ], + [ + -218, + -210, + -39, + -11 + ], + [ + -218, + -210, + -3, + 25 + ], + [ + -218, + -210, + 33, + 58 + ], + [ + -218, + -210, + 79, + 97 + ], + [ + -218, + -210, + 105, + 133 + ], + [ + -218, + -210, + 141, + 169 + ], + [ + -218, + -210, + 177, + 205 + ], + [ + -218, + -210, + 213, + 241 + ], + [ + -218, + -210, + 249, + 277 + ], + [ + -218, + -210, + 285, + 313 + ], + [ + -218, + -210, + 321, + 349 + ], + [ + -218, + -210, + 357, + 385 + ], + [ + -218, + -210, + 393, + 421 + ], + [ + -218, + -210, + 429, + 457 + ], + [ + -218, + -210, + 465, + 493 + ], + [ + -218, + -210, + 501, + 529 + ], + [ + -218, + -210, + 537, + 565 + ], + [ + -218, + -210, + 573, + 600 + ], + [ + -600, + 600, + -227, + -219 + ], + [ + -182, + -174, + -600, + -551 + ], + [ + -182, + -174, + -543, + -515 + ], + [ + -182, + -174, + -507, + -479 + ], + [ + -182, + -174, + -471, + -443 + ], + [ + -182, + -174, + -435, + -407 + ], + [ + -182, + -174, + -399, + -371 + ], + [ + -182, + -174, + -363, + -335 + ], + [ + -182, + -174, + -327, + -299 + ], + [ + -182, + -174, + -291, + -263 + ], + [ + -182, + -174, + -255, + -227 + ], + [ + -182, + -174, + -219, + -191 + ], + [ + -182, + -174, + -183, + -155 + ], + [ + -182, + -174, + -147, + -119 + ], + [ + -182, + -174, + -111, + -83 + ], + [ + -182, + -174, + -75, + -47 + ], + [ + -182, + -174, + -39, + -11 + ], + [ + -182, + -174, + -3, + 25 + ], + [ + -182, + -174, + 33, + 58 + ], + [ + -182, + -174, + 79, + 97 + ], + [ + -182, + -174, + 105, + 133 + ], + [ + -182, + -174, + 141, + 169 + ], + [ + -182, + -174, + 177, + 205 + ], + [ + -182, + -174, + 213, + 241 + ], + [ + -182, + -174, + 249, + 277 + ], + [ + -182, + -174, + 285, + 313 + ], + [ + -182, + -174, + 321, + 349 + ], + [ + -182, + -174, + 357, + 385 + ], + [ + -182, + -174, + 393, + 421 + ], + [ + -182, + -174, + 429, + 457 + ], + [ + -182, + -174, + 465, + 493 + ], + [ + -182, + -174, + 501, + 529 + ], + [ + -182, + -174, + 537, + 565 + ], + [ + -182, + -174, + 573, + 600 + ], + [ + -600, + 600, + -191, + -183 + ], + [ + -146, + -138, + -600, + -551 + ], + [ + -146, + -138, + -543, + -515 + ], + [ + -146, + -138, + -507, + -479 + ], + [ + -146, + -138, + -471, + -443 + ], + [ + -146, + -138, + -435, + -407 + ], + [ + -146, + -138, + -399, + -371 + ], + [ + -146, + -138, + -363, + -335 + ], + [ + -146, + -138, + -327, + -299 + ], + [ + -146, + -138, + -291, + -263 + ], + [ + -146, + -138, + -255, + -227 + ], + [ + -146, + -138, + -219, + -191 + ], + [ + -146, + -138, + -183, + -155 + ], + [ + -146, + -138, + -147, + -119 + ], + [ + -146, + -138, + -111, + -83 + ], + [ + -146, + -138, + -75, + -47 + ], + [ + -146, + -138, + -39, + -11 + ], + [ + -146, + -138, + -3, + 25 + ], + [ + -146, + -138, + 33, + 58 + ], + [ + -146, + -138, + 79, + 97 + ], + [ + -146, + -138, + 105, + 133 + ], + [ + -146, + -138, + 141, + 169 + ], + [ + -146, + -138, + 177, + 205 + ], + [ + -146, + -138, + 213, + 241 + ], + [ + -146, + -138, + 249, + 277 + ], + [ + -146, + -138, + 285, + 313 + ], + [ + -146, + -138, + 321, + 349 + ], + [ + -146, + -138, + 357, + 385 + ], + [ + -146, + -138, + 393, + 421 + ], + [ + -146, + -138, + 429, + 457 + ], + [ + -146, + -138, + 465, + 493 + ], + [ + -146, + -138, + 501, + 529 + ], + [ + -146, + -138, + 537, + 565 + ], + [ + -146, + -138, + 573, + 600 + ], + [ + -600, + 600, + -155, + -147 + ], + [ + -110, + -102, + -600, + -551 + ], + [ + -110, + -102, + -543, + -515 + ], + [ + -110, + -102, + -507, + -479 + ], + [ + -110, + -102, + -471, + -443 + ], + [ + -110, + -102, + -435, + -407 + ], + [ + -110, + -102, + -399, + -371 + ], + [ + -110, + -102, + -363, + -335 + ], + [ + -110, + -102, + -327, + -299 + ], + [ + -110, + -102, + -291, + -263 + ], + [ + -110, + -102, + -255, + -227 + ], + [ + -110, + -102, + -219, + -191 + ], + [ + -110, + -102, + -183, + -155 + ], + [ + -110, + -102, + -147, + -119 + ], + [ + -110, + -102, + -111, + -83 + ], + [ + -110, + -102, + -75, + -47 + ], + [ + -110, + -102, + -39, + -11 + ], + [ + -110, + -102, + -3, + 25 + ], + [ + -110, + -102, + 33, + 58 + ], + [ + -110, + -102, + 79, + 97 + ], + [ + -110, + -102, + 105, + 133 + ], + [ + -110, + -102, + 141, + 169 + ], + [ + -110, + -102, + 177, + 205 + ], + [ + -110, + -102, + 213, + 241 + ], + [ + -110, + -102, + 249, + 277 + ], + [ + -110, + -102, + 285, + 313 + ], + [ + -110, + -102, + 321, + 349 + ], + [ + -110, + -102, + 357, + 385 + ], + [ + -110, + -102, + 393, + 421 + ], + [ + -110, + -102, + 429, + 457 + ], + [ + -110, + -102, + 465, + 493 + ], + [ + -110, + -102, + 501, + 529 + ], + [ + -110, + -102, + 537, + 565 + ], + [ + -110, + -102, + 573, + 600 + ], + [ + -600, + 600, + -119, + -111 + ], + [ + -74, + -66, + -600, + -551 + ], + [ + -74, + -66, + -543, + -515 + ], + [ + -74, + -66, + -507, + -479 + ], + [ + -74, + -66, + -471, + -443 + ], + [ + -74, + -66, + -435, + -407 + ], + [ + -74, + -66, + -399, + -371 + ], + [ + -74, + -66, + -363, + -335 + ], + [ + -74, + -66, + -327, + -299 + ], + [ + -74, + -66, + -291, + -263 + ], + [ + -74, + -66, + -255, + -227 + ], + [ + -74, + -66, + -219, + -191 + ], + [ + -74, + -66, + -183, + -155 + ], + [ + -74, + -66, + -147, + -119 + ], + [ + -74, + -66, + -111, + -83 + ], + [ + -74, + -66, + -75, + -47 + ], + [ + -74, + -66, + -39, + -11 + ], + [ + -74, + -66, + -3, + 25 + ], + [ + -74, + -66, + 33, + 58 + ], + [ + -74, + -66, + 79, + 97 + ], + [ + -74, + -66, + 105, + 133 + ], + [ + -74, + -66, + 141, + 169 + ], + [ + -74, + -66, + 177, + 205 + ], + [ + -74, + -66, + 213, + 241 + ], + [ + -74, + -66, + 249, + 277 + ], + [ + -74, + -66, + 285, + 313 + ], + [ + -74, + -66, + 321, + 349 + ], + [ + -74, + -66, + 357, + 385 + ], + [ + -74, + -66, + 393, + 421 + ], + [ + -74, + -66, + 429, + 457 + ], + [ + -74, + -66, + 465, + 493 + ], + [ + -74, + -66, + 501, + 529 + ], + [ + -74, + -66, + 537, + 565 + ], + [ + -74, + -66, + 573, + 600 + ], + [ + -600, + 600, + -83, + -75 + ], + [ + -38, + -30, + -600, + -551 + ], + [ + -38, + -30, + -543, + -515 + ], + [ + -38, + -30, + -507, + -479 + ], + [ + -38, + -30, + -471, + -443 + ], + [ + -38, + -30, + -435, + -407 + ], + [ + -38, + -30, + -399, + -371 + ], + [ + -38, + -30, + -363, + -335 + ], + [ + -38, + -30, + -327, + -299 + ], + [ + -38, + -30, + -291, + -263 + ], + [ + -38, + -30, + -255, + -227 + ], + [ + -38, + -30, + -219, + -191 + ], + [ + -38, + -30, + -183, + -155 + ], + [ + -38, + -30, + -147, + -119 + ], + [ + -38, + -30, + -111, + -83 + ], + [ + -38, + -30, + -75, + -47 + ], + [ + -38, + -30, + -39, + -11 + ], + [ + -38, + -30, + -3, + 25 + ], + [ + -38, + -30, + 33, + 58 + ], + [ + -38, + -30, + 79, + 97 + ], + [ + -38, + -30, + 105, + 133 + ], + [ + -38, + -30, + 141, + 169 + ], + [ + -38, + -30, + 177, + 205 + ], + [ + -38, + -30, + 213, + 241 + ], + [ + -38, + -30, + 249, + 277 + ], + [ + -38, + -30, + 285, + 313 + ], + [ + -38, + -30, + 321, + 349 + ], + [ + -38, + -30, + 357, + 385 + ], + [ + -38, + -30, + 393, + 421 + ], + [ + -38, + -30, + 429, + 457 + ], + [ + -38, + -30, + 465, + 493 + ], + [ + -38, + -30, + 501, + 529 + ], + [ + -38, + -30, + 537, + 565 + ], + [ + -38, + -30, + 573, + 600 + ], + [ + -600, + 600, + -47, + -39 + ], + [ + -2, + 6, + -600, + -551 + ], + [ + -2, + 6, + -543, + -515 + ], + [ + -2, + 6, + -507, + -479 + ], + [ + -2, + 6, + -471, + -443 + ], + [ + -2, + 6, + -435, + -407 + ], + [ + -2, + 6, + -399, + -371 + ], + [ + -2, + 6, + -363, + -335 + ], + [ + -2, + 6, + -327, + -299 + ], + [ + -2, + 6, + -291, + -263 + ], + [ + -2, + 6, + -255, + -227 + ], + [ + -2, + 6, + -219, + -191 + ], + [ + -2, + 6, + -183, + -155 + ], + [ + -2, + 6, + -147, + -119 + ], + [ + -2, + 6, + -111, + -83 + ], + [ + -2, + 6, + -75, + -47 + ], + [ + -2, + 6, + -39, + -11 + ], + [ + -2, + 6, + -3, + 25 + ], + [ + -2, + 6, + 33, + 58 + ], + [ + -2, + 6, + 79, + 97 + ], + [ + -2, + 6, + 105, + 133 + ], + [ + -2, + 6, + 141, + 169 + ], + [ + -2, + 6, + 177, + 205 + ], + [ + -2, + 6, + 213, + 241 + ], + [ + -2, + 6, + 249, + 277 + ], + [ + -2, + 6, + 285, + 313 + ], + [ + -2, + 6, + 321, + 349 + ], + [ + -2, + 6, + 357, + 385 + ], + [ + -2, + 6, + 393, + 421 + ], + [ + -2, + 6, + 429, + 457 + ], + [ + -2, + 6, + 465, + 493 + ], + [ + -2, + 6, + 501, + 529 + ], + [ + -2, + 6, + 537, + 565 + ], + [ + -2, + 6, + 573, + 600 + ], + [ + -600, + 600, + -11, + -3 + ], + [ + 34, + 42, + -600, + -551 + ], + [ + 34, + 42, + -543, + -515 + ], + [ + 34, + 42, + -507, + -479 + ], + [ + 34, + 42, + -471, + -443 + ], + [ + 34, + 42, + -435, + -407 + ], + [ + 34, + 42, + -399, + -371 + ], + [ + 34, + 42, + -363, + -335 + ], + [ + 34, + 42, + -327, + -299 + ], + [ + 34, + 42, + -291, + -263 + ], + [ + 34, + 42, + -255, + -227 + ], + [ + 34, + 42, + -219, + -191 + ], + [ + 34, + 42, + -183, + -155 + ], + [ + 34, + 42, + -147, + -119 + ], + [ + 34, + 42, + -111, + -83 + ], + [ + 34, + 42, + -75, + -47 + ], + [ + 34, + 42, + -39, + -11 + ], + [ + 34, + 42, + -3, + 25 + ], + [ + 34, + 42, + 33, + 58 + ], + [ + 34, + 42, + 79, + 97 + ], + [ + 34, + 42, + 105, + 133 + ], + [ + 34, + 42, + 141, + 169 + ], + [ + 34, + 42, + 177, + 205 + ], + [ + 34, + 42, + 213, + 241 + ], + [ + 34, + 42, + 249, + 277 + ], + [ + 34, + 42, + 285, + 313 + ], + [ + 34, + 42, + 321, + 349 + ], + [ + 34, + 42, + 357, + 385 + ], + [ + 34, + 42, + 393, + 421 + ], + [ + 34, + 42, + 429, + 457 + ], + [ + 34, + 42, + 465, + 493 + ], + [ + 34, + 42, + 501, + 529 + ], + [ + 34, + 42, + 537, + 565 + ], + [ + 34, + 42, + 573, + 600 + ], + [ + -600, + 68, + 25, + 33 + ], + [ + 68, + 600, + 25, + 26 + ], + [ + 70, + 78, + -600, + -551 + ], + [ + 70, + 78, + -543, + -515 + ], + [ + 70, + 78, + -507, + -479 + ], + [ + 70, + 78, + -471, + -443 + ], + [ + 70, + 78, + -435, + -407 + ], + [ + 70, + 78, + -399, + -371 + ], + [ + 70, + 78, + -363, + -335 + ], + [ + 70, + 78, + -327, + -299 + ], + [ + 70, + 78, + -291, + -263 + ], + [ + 70, + 78, + -255, + -227 + ], + [ + 70, + 78, + -219, + -191 + ], + [ + 70, + 78, + -183, + -155 + ], + [ + 70, + 78, + -147, + -119 + ], + [ + 70, + 78, + -111, + -83 + ], + [ + 70, + 78, + -75, + -47 + ], + [ + 70, + 78, + -39, + -11 + ], + [ + 70, + 78, + -3, + 25 + ], + [ + 70, + 78, + 81, + 97 + ], + [ + 70, + 78, + 105, + 133 + ], + [ + 70, + 78, + 141, + 169 + ], + [ + 70, + 78, + 177, + 205 + ], + [ + 70, + 78, + 213, + 241 + ], + [ + 70, + 78, + 249, + 277 + ], + [ + 70, + 78, + 285, + 313 + ], + [ + 70, + 78, + 321, + 349 + ], + [ + 70, + 78, + 357, + 385 + ], + [ + 70, + 78, + 393, + 421 + ], + [ + 70, + 78, + 429, + 457 + ], + [ + 70, + 78, + 465, + 493 + ], + [ + 70, + 78, + 501, + 529 + ], + [ + 70, + 78, + 537, + 565 + ], + [ + 70, + 78, + 573, + 600 + ], + [ + 106, + 114, + -600, + -551 + ], + [ + 106, + 114, + -543, + -515 + ], + [ + 106, + 114, + -507, + -479 + ], + [ + 106, + 114, + -471, + -443 + ], + [ + 106, + 114, + -435, + -407 + ], + [ + 106, + 114, + -399, + -371 + ], + [ + 106, + 114, + -363, + -335 + ], + [ + 106, + 114, + -327, + -299 + ], + [ + 106, + 114, + -291, + -263 + ], + [ + 106, + 114, + -255, + -227 + ], + [ + 106, + 114, + -219, + -191 + ], + [ + 106, + 114, + -183, + -155 + ], + [ + 106, + 114, + -147, + -119 + ], + [ + 106, + 114, + -111, + -83 + ], + [ + 106, + 114, + -75, + -47 + ], + [ + 106, + 114, + -39, + -11 + ], + [ + 106, + 114, + -3, + 25 + ], + [ + 106, + 114, + 81, + 97 + ], + [ + 106, + 114, + 105, + 133 + ], + [ + 106, + 114, + 141, + 169 + ], + [ + 106, + 114, + 177, + 205 + ], + [ + 106, + 114, + 213, + 241 + ], + [ + 106, + 114, + 249, + 277 + ], + [ + 106, + 114, + 285, + 313 + ], + [ + 106, + 114, + 321, + 349 + ], + [ + 106, + 114, + 357, + 385 + ], + [ + 106, + 114, + 393, + 421 + ], + [ + 106, + 114, + 429, + 457 + ], + [ + 106, + 114, + 465, + 493 + ], + [ + 106, + 114, + 501, + 529 + ], + [ + 106, + 114, + 537, + 565 + ], + [ + 106, + 114, + 573, + 600 + ], + [ + -600, + 600, + 97, + 105 + ], + [ + 142, + 150, + -600, + -551 + ], + [ + 142, + 150, + -543, + -515 + ], + [ + 142, + 150, + -507, + -479 + ], + [ + 142, + 150, + -471, + -443 + ], + [ + 142, + 150, + -435, + -407 + ], + [ + 142, + 150, + -399, + -371 + ], + [ + 142, + 150, + -363, + -335 + ], + [ + 142, + 150, + -327, + -299 + ], + [ + 142, + 150, + -291, + -263 + ], + [ + 142, + 150, + -255, + -227 + ], + [ + 142, + 150, + -219, + -191 + ], + [ + 142, + 150, + -183, + -155 + ], + [ + 142, + 150, + -147, + -119 + ], + [ + 142, + 150, + -111, + -83 + ], + [ + 142, + 150, + -75, + -47 + ], + [ + 142, + 150, + -39, + -11 + ], + [ + 142, + 150, + -3, + 25 + ], + [ + 142, + 150, + 81, + 97 + ], + [ + 142, + 150, + 105, + 133 + ], + [ + 142, + 150, + 141, + 169 + ], + [ + 142, + 150, + 177, + 205 + ], + [ + 142, + 150, + 213, + 241 + ], + [ + 142, + 150, + 249, + 277 + ], + [ + 142, + 150, + 285, + 313 + ], + [ + 142, + 150, + 321, + 349 + ], + [ + 142, + 150, + 357, + 385 + ], + [ + 142, + 150, + 393, + 421 + ], + [ + 142, + 150, + 429, + 457 + ], + [ + 142, + 150, + 465, + 493 + ], + [ + 142, + 150, + 501, + 529 + ], + [ + 142, + 150, + 537, + 565 + ], + [ + 142, + 150, + 573, + 600 + ], + [ + -600, + 600, + 133, + 141 + ], + [ + 178, + 186, + -600, + -551 + ], + [ + 178, + 186, + -543, + -515 + ], + [ + 178, + 186, + -507, + -479 + ], + [ + 178, + 186, + -471, + -443 + ], + [ + 178, + 186, + -435, + -407 + ], + [ + 178, + 186, + -399, + -371 + ], + [ + 178, + 186, + -363, + -335 + ], + [ + 178, + 186, + -327, + -299 + ], + [ + 178, + 186, + -291, + -263 + ], + [ + 178, + 186, + -255, + -227 + ], + [ + 178, + 186, + -219, + -191 + ], + [ + 178, + 186, + -183, + -155 + ], + [ + 178, + 186, + -147, + -119 + ], + [ + 178, + 186, + -111, + -83 + ], + [ + 178, + 186, + -75, + -47 + ], + [ + 178, + 186, + -39, + -11 + ], + [ + 178, + 186, + -3, + 25 + ], + [ + 178, + 186, + 81, + 97 + ], + [ + 178, + 186, + 105, + 133 + ], + [ + 178, + 186, + 141, + 169 + ], + [ + 178, + 186, + 177, + 205 + ], + [ + 178, + 186, + 213, + 241 + ], + [ + 178, + 186, + 249, + 277 + ], + [ + 178, + 186, + 285, + 313 + ], + [ + 178, + 186, + 321, + 349 + ], + [ + 178, + 186, + 357, + 385 + ], + [ + 178, + 186, + 393, + 421 + ], + [ + 178, + 186, + 429, + 457 + ], + [ + 178, + 186, + 465, + 493 + ], + [ + 178, + 186, + 501, + 529 + ], + [ + 178, + 186, + 537, + 565 + ], + [ + 178, + 186, + 573, + 600 + ], + [ + -600, + 600, + 169, + 177 + ], + [ + 214, + 222, + -600, + -551 + ], + [ + 214, + 222, + -543, + -515 + ], + [ + 214, + 222, + -507, + -479 + ], + [ + 214, + 222, + -471, + -443 + ], + [ + 214, + 222, + -435, + -407 + ], + [ + 214, + 222, + -399, + -371 + ], + [ + 214, + 222, + -363, + -335 + ], + [ + 214, + 222, + -327, + -299 + ], + [ + 214, + 222, + -291, + -263 + ], + [ + 214, + 222, + -255, + -227 + ], + [ + 214, + 222, + -219, + -191 + ], + [ + 214, + 222, + -183, + -155 + ], + [ + 214, + 222, + -147, + -119 + ], + [ + 214, + 222, + -111, + -83 + ], + [ + 214, + 222, + -75, + -47 + ], + [ + 214, + 222, + -39, + -11 + ], + [ + 214, + 222, + -3, + 25 + ], + [ + 214, + 222, + 81, + 97 + ], + [ + 214, + 222, + 105, + 133 + ], + [ + 214, + 222, + 141, + 169 + ], + [ + 214, + 222, + 177, + 205 + ], + [ + 214, + 222, + 213, + 241 + ], + [ + 214, + 222, + 249, + 277 + ], + [ + 214, + 222, + 285, + 313 + ], + [ + 214, + 222, + 321, + 349 + ], + [ + 214, + 222, + 357, + 385 + ], + [ + 214, + 222, + 393, + 421 + ], + [ + 214, + 222, + 429, + 457 + ], + [ + 214, + 222, + 465, + 493 + ], + [ + 214, + 222, + 501, + 529 + ], + [ + 214, + 222, + 537, + 565 + ], + [ + 214, + 222, + 573, + 600 + ], + [ + -600, + 600, + 205, + 213 + ], + [ + 250, + 258, + -600, + -551 + ], + [ + 250, + 258, + -543, + -515 + ], + [ + 250, + 258, + -507, + -479 + ], + [ + 250, + 258, + -471, + -443 + ], + [ + 250, + 258, + -435, + -407 + ], + [ + 250, + 258, + -399, + -371 + ], + [ + 250, + 258, + -363, + -335 + ], + [ + 250, + 258, + -327, + -299 + ], + [ + 250, + 258, + -291, + -263 + ], + [ + 250, + 258, + -255, + -227 + ], + [ + 250, + 258, + -219, + -191 + ], + [ + 250, + 258, + -183, + -155 + ], + [ + 250, + 258, + -147, + -119 + ], + [ + 250, + 258, + -111, + -83 + ], + [ + 250, + 258, + -75, + -47 + ], + [ + 250, + 258, + -39, + -11 + ], + [ + 250, + 258, + -3, + 25 + ], + [ + 250, + 258, + 81, + 97 + ], + [ + 250, + 258, + 105, + 133 + ], + [ + 250, + 258, + 141, + 169 + ], + [ + 250, + 258, + 177, + 205 + ], + [ + 250, + 258, + 213, + 241 + ], + [ + 250, + 258, + 249, + 277 + ], + [ + 250, + 258, + 285, + 313 + ], + [ + 250, + 258, + 321, + 349 + ], + [ + 250, + 258, + 357, + 385 + ], + [ + 250, + 258, + 393, + 421 + ], + [ + 250, + 258, + 429, + 457 + ], + [ + 250, + 258, + 465, + 493 + ], + [ + 250, + 258, + 501, + 529 + ], + [ + 250, + 258, + 537, + 565 + ], + [ + 250, + 258, + 573, + 600 + ], + [ + -600, + 600, + 241, + 249 + ], + [ + 286, + 294, + -600, + -551 + ], + [ + 286, + 294, + -543, + -515 + ], + [ + 286, + 294, + -507, + -479 + ], + [ + 286, + 294, + -471, + -443 + ], + [ + 286, + 294, + -435, + -407 + ], + [ + 286, + 294, + -399, + -371 + ], + [ + 286, + 294, + -363, + -335 + ], + [ + 286, + 294, + -327, + -299 + ], + [ + 286, + 294, + -291, + -263 + ], + [ + 286, + 294, + -255, + -227 + ], + [ + 286, + 294, + -219, + -191 + ], + [ + 286, + 294, + -183, + -155 + ], + [ + 286, + 294, + -147, + -119 + ], + [ + 286, + 294, + -111, + -83 + ], + [ + 286, + 294, + -75, + -47 + ], + [ + 286, + 294, + -39, + -11 + ], + [ + 286, + 294, + -3, + 25 + ], + [ + 286, + 294, + 81, + 97 + ], + [ + 286, + 294, + 105, + 133 + ], + [ + 286, + 294, + 141, + 169 + ], + [ + 286, + 294, + 177, + 205 + ], + [ + 286, + 294, + 213, + 241 + ], + [ + 286, + 294, + 249, + 277 + ], + [ + 286, + 294, + 285, + 313 + ], + [ + 286, + 294, + 321, + 349 + ], + [ + 286, + 294, + 357, + 385 + ], + [ + 286, + 294, + 393, + 421 + ], + [ + 286, + 294, + 429, + 457 + ], + [ + 286, + 294, + 465, + 493 + ], + [ + 286, + 294, + 501, + 529 + ], + [ + 286, + 294, + 537, + 565 + ], + [ + 286, + 294, + 573, + 600 + ], + [ + -600, + 600, + 277, + 285 + ], + [ + 322, + 330, + -600, + -551 + ], + [ + 322, + 330, + -543, + -515 + ], + [ + 322, + 330, + -507, + -479 + ], + [ + 322, + 330, + -471, + -443 + ], + [ + 322, + 330, + -435, + -407 + ], + [ + 322, + 330, + -399, + -371 + ], + [ + 322, + 330, + -363, + -335 + ], + [ + 322, + 330, + -327, + -299 + ], + [ + 322, + 330, + -291, + -263 + ], + [ + 322, + 330, + -255, + -227 + ], + [ + 322, + 330, + -219, + -191 + ], + [ + 322, + 330, + -183, + -155 + ], + [ + 322, + 330, + -147, + -119 + ], + [ + 322, + 330, + -111, + -83 + ], + [ + 322, + 330, + -75, + -47 + ], + [ + 322, + 330, + -39, + -11 + ], + [ + 322, + 330, + -3, + 25 + ], + [ + 322, + 330, + 81, + 97 + ], + [ + 322, + 330, + 105, + 133 + ], + [ + 322, + 330, + 141, + 169 + ], + [ + 322, + 330, + 177, + 205 + ], + [ + 322, + 330, + 213, + 241 + ], + [ + 322, + 330, + 249, + 277 + ], + [ + 322, + 330, + 285, + 313 + ], + [ + 322, + 330, + 321, + 349 + ], + [ + 322, + 330, + 357, + 385 + ], + [ + 322, + 330, + 393, + 421 + ], + [ + 322, + 330, + 429, + 457 + ], + [ + 322, + 330, + 465, + 493 + ], + [ + 322, + 330, + 501, + 529 + ], + [ + 322, + 330, + 537, + 565 + ], + [ + 322, + 330, + 573, + 600 + ], + [ + -600, + 600, + 313, + 321 + ], + [ + 358, + 366, + -600, + -551 + ], + [ + 358, + 366, + -543, + -515 + ], + [ + 358, + 366, + -507, + -479 + ], + [ + 358, + 366, + -471, + -443 + ], + [ + 358, + 366, + -435, + -407 + ], + [ + 358, + 366, + -399, + -371 + ], + [ + 358, + 366, + -363, + -335 + ], + [ + 358, + 366, + -327, + -299 + ], + [ + 358, + 366, + -291, + -263 + ], + [ + 358, + 366, + -255, + -227 + ], + [ + 358, + 366, + -219, + -191 + ], + [ + 358, + 366, + -183, + -155 + ], + [ + 358, + 366, + -147, + -119 + ], + [ + 358, + 366, + -111, + -83 + ], + [ + 358, + 366, + -75, + -47 + ], + [ + 358, + 366, + -39, + -11 + ], + [ + 358, + 366, + -3, + 25 + ], + [ + 358, + 366, + 81, + 97 + ], + [ + 358, + 366, + 105, + 133 + ], + [ + 358, + 366, + 141, + 169 + ], + [ + 358, + 366, + 177, + 205 + ], + [ + 358, + 366, + 213, + 241 + ], + [ + 358, + 366, + 249, + 277 + ], + [ + 358, + 366, + 285, + 313 + ], + [ + 358, + 366, + 321, + 349 + ], + [ + 358, + 366, + 357, + 385 + ], + [ + 358, + 366, + 393, + 421 + ], + [ + 358, + 366, + 429, + 457 + ], + [ + 358, + 366, + 465, + 493 + ], + [ + 358, + 366, + 501, + 529 + ], + [ + 358, + 366, + 537, + 565 + ], + [ + 358, + 366, + 573, + 600 + ], + [ + -600, + 600, + 349, + 357 + ], + [ + 394, + 402, + -600, + -551 + ], + [ + 394, + 402, + -543, + -515 + ], + [ + 394, + 402, + -507, + -479 + ], + [ + 394, + 402, + -471, + -443 + ], + [ + 394, + 402, + -435, + -407 + ], + [ + 394, + 402, + -399, + -371 + ], + [ + 394, + 402, + -363, + -335 + ], + [ + 394, + 402, + -327, + -299 + ], + [ + 394, + 402, + -291, + -263 + ], + [ + 394, + 402, + -255, + -227 + ], + [ + 394, + 402, + -219, + -191 + ], + [ + 394, + 402, + -183, + -155 + ], + [ + 394, + 402, + -147, + -119 + ], + [ + 394, + 402, + -111, + -83 + ], + [ + 394, + 402, + -75, + -47 + ], + [ + 394, + 402, + -39, + -11 + ], + [ + 394, + 402, + -3, + 25 + ], + [ + 394, + 402, + 81, + 97 + ], + [ + 394, + 402, + 105, + 133 + ], + [ + 394, + 402, + 141, + 169 + ], + [ + 394, + 402, + 177, + 205 + ], + [ + 394, + 402, + 213, + 241 + ], + [ + 394, + 402, + 249, + 277 + ], + [ + 394, + 402, + 285, + 313 + ], + [ + 394, + 402, + 321, + 349 + ], + [ + 394, + 402, + 357, + 385 + ], + [ + 394, + 402, + 393, + 421 + ], + [ + 394, + 402, + 429, + 457 + ], + [ + 394, + 402, + 465, + 493 + ], + [ + 394, + 402, + 501, + 529 + ], + [ + 394, + 402, + 537, + 565 + ], + [ + 394, + 402, + 573, + 600 + ], + [ + -600, + 600, + 385, + 393 + ], + [ + 430, + 438, + -600, + -551 + ], + [ + 430, + 438, + -543, + -515 + ], + [ + 430, + 438, + -507, + -479 + ], + [ + 430, + 438, + -471, + -443 + ], + [ + 430, + 438, + -435, + -407 + ], + [ + 430, + 438, + -399, + -371 + ], + [ + 430, + 438, + -363, + -335 + ], + [ + 430, + 438, + -327, + -299 + ], + [ + 430, + 438, + -291, + -263 + ], + [ + 430, + 438, + -255, + -227 + ], + [ + 430, + 438, + -219, + -191 + ], + [ + 430, + 438, + -183, + -155 + ], + [ + 430, + 438, + -147, + -119 + ], + [ + 430, + 438, + -111, + -83 + ], + [ + 430, + 438, + -75, + -47 + ], + [ + 430, + 438, + -39, + -11 + ], + [ + 430, + 438, + -3, + 25 + ], + [ + 430, + 438, + 81, + 97 + ], + [ + 430, + 438, + 105, + 133 + ], + [ + 430, + 438, + 141, + 169 + ], + [ + 430, + 438, + 177, + 205 + ], + [ + 430, + 438, + 213, + 241 + ], + [ + 430, + 438, + 249, + 277 + ], + [ + 430, + 438, + 285, + 313 + ], + [ + 430, + 438, + 321, + 349 + ], + [ + 430, + 438, + 357, + 385 + ], + [ + 430, + 438, + 393, + 421 + ], + [ + 430, + 438, + 429, + 457 + ], + [ + 430, + 438, + 465, + 493 + ], + [ + 430, + 438, + 501, + 529 + ], + [ + 430, + 438, + 537, + 565 + ], + [ + 430, + 438, + 573, + 600 + ], + [ + -600, + 600, + 421, + 429 + ], + [ + 466, + 474, + -600, + -551 + ], + [ + 466, + 474, + -543, + -515 + ], + [ + 466, + 474, + -507, + -479 + ], + [ + 466, + 474, + -471, + -443 + ], + [ + 466, + 474, + -435, + -407 + ], + [ + 466, + 474, + -399, + -371 + ], + [ + 466, + 474, + -363, + -335 + ], + [ + 466, + 474, + -327, + -299 + ], + [ + 466, + 474, + -291, + -263 + ], + [ + 466, + 474, + -255, + -227 + ], + [ + 466, + 474, + -219, + -191 + ], + [ + 466, + 474, + -183, + -155 + ], + [ + 466, + 474, + -147, + -119 + ], + [ + 466, + 474, + -111, + -83 + ], + [ + 466, + 474, + -75, + -47 + ], + [ + 466, + 474, + -39, + -11 + ], + [ + 466, + 474, + -3, + 25 + ], + [ + 466, + 474, + 81, + 97 + ], + [ + 466, + 474, + 105, + 133 + ], + [ + 466, + 474, + 141, + 169 + ], + [ + 466, + 474, + 177, + 205 + ], + [ + 466, + 474, + 213, + 241 + ], + [ + 466, + 474, + 249, + 277 + ], + [ + 466, + 474, + 285, + 313 + ], + [ + 466, + 474, + 321, + 349 + ], + [ + 466, + 474, + 357, + 385 + ], + [ + 466, + 474, + 393, + 421 + ], + [ + 466, + 474, + 429, + 457 + ], + [ + 466, + 474, + 465, + 493 + ], + [ + 466, + 474, + 501, + 529 + ], + [ + 466, + 474, + 537, + 565 + ], + [ + 466, + 474, + 573, + 600 + ], + [ + -600, + 600, + 457, + 465 + ], + [ + 502, + 510, + -600, + -551 + ], + [ + 502, + 510, + -543, + -515 + ], + [ + 502, + 510, + -507, + -479 + ], + [ + 502, + 510, + -471, + -443 + ], + [ + 502, + 510, + -435, + -407 + ], + [ + 502, + 510, + -399, + -371 + ], + [ + 502, + 510, + -363, + -335 + ], + [ + 502, + 510, + -327, + -299 + ], + [ + 502, + 510, + -291, + -263 + ], + [ + 502, + 510, + -255, + -227 + ], + [ + 502, + 510, + -219, + -191 + ], + [ + 502, + 510, + -183, + -155 + ], + [ + 502, + 510, + -147, + -119 + ], + [ + 502, + 510, + -111, + -83 + ], + [ + 502, + 510, + -75, + -47 + ], + [ + 502, + 510, + -39, + -11 + ], + [ + 502, + 510, + -3, + 25 + ], + [ + 502, + 510, + 81, + 97 + ], + [ + 502, + 510, + 105, + 133 + ], + [ + 502, + 510, + 141, + 169 + ], + [ + 502, + 510, + 177, + 205 + ], + [ + 502, + 510, + 213, + 241 + ], + [ + 502, + 510, + 249, + 277 + ], + [ + 502, + 510, + 285, + 313 + ], + [ + 502, + 510, + 321, + 349 + ], + [ + 502, + 510, + 357, + 385 + ], + [ + 502, + 510, + 393, + 421 + ], + [ + 502, + 510, + 429, + 457 + ], + [ + 502, + 510, + 465, + 493 + ], + [ + 502, + 510, + 501, + 529 + ], + [ + 502, + 510, + 537, + 565 + ], + [ + 502, + 510, + 573, + 600 + ], + [ + -600, + 600, + 493, + 501 + ], + [ + 538, + 546, + -600, + -551 + ], + [ + 538, + 546, + -543, + -515 + ], + [ + 538, + 546, + -507, + -479 + ], + [ + 538, + 546, + -471, + -443 + ], + [ + 538, + 546, + -435, + -407 + ], + [ + 538, + 546, + -399, + -371 + ], + [ + 538, + 546, + -363, + -335 + ], + [ + 538, + 546, + -327, + -299 + ], + [ + 538, + 546, + -291, + -263 + ], + [ + 538, + 546, + -255, + -227 + ], + [ + 538, + 546, + -219, + -191 + ], + [ + 538, + 546, + -183, + -155 + ], + [ + 538, + 546, + -147, + -119 + ], + [ + 538, + 546, + -111, + -83 + ], + [ + 538, + 546, + -75, + -47 + ], + [ + 538, + 546, + -39, + -11 + ], + [ + 538, + 546, + -3, + 25 + ], + [ + 538, + 546, + 81, + 97 + ], + [ + 538, + 546, + 105, + 133 + ], + [ + 538, + 546, + 141, + 169 + ], + [ + 538, + 546, + 177, + 205 + ], + [ + 538, + 546, + 213, + 241 + ], + [ + 538, + 546, + 249, + 277 + ], + [ + 538, + 546, + 285, + 313 + ], + [ + 538, + 546, + 321, + 349 + ], + [ + 538, + 546, + 357, + 385 + ], + [ + 538, + 546, + 393, + 421 + ], + [ + 538, + 546, + 429, + 457 + ], + [ + 538, + 546, + 465, + 493 + ], + [ + 538, + 546, + 501, + 529 + ], + [ + 538, + 546, + 537, + 565 + ], + [ + 538, + 546, + 573, + 600 + ], + [ + -600, + 600, + 529, + 537 + ], + [ + 574, + 582, + -600, + -551 + ], + [ + 574, + 582, + -543, + -515 + ], + [ + 574, + 582, + -507, + -479 + ], + [ + 574, + 582, + -471, + -443 + ], + [ + 574, + 582, + -435, + -407 + ], + [ + 574, + 582, + -399, + -371 + ], + [ + 574, + 582, + -363, + -335 + ], + [ + 574, + 582, + -327, + -299 + ], + [ + 574, + 582, + -291, + -263 + ], + [ + 574, + 582, + -255, + -227 + ], + [ + 574, + 582, + -219, + -191 + ], + [ + 574, + 582, + -183, + -155 + ], + [ + 574, + 582, + -147, + -119 + ], + [ + 574, + 582, + -111, + -83 + ], + [ + 574, + 582, + -75, + -47 + ], + [ + 574, + 582, + -39, + -11 + ], + [ + 574, + 582, + -3, + 25 + ], + [ + 574, + 582, + 81, + 97 + ], + [ + 574, + 582, + 105, + 133 + ], + [ + 574, + 582, + 141, + 169 + ], + [ + 574, + 582, + 177, + 205 + ], + [ + 574, + 582, + 213, + 241 + ], + [ + 574, + 582, + 249, + 277 + ], + [ + 574, + 582, + 285, + 313 + ], + [ + 574, + 582, + 321, + 349 + ], + [ + 574, + 582, + 357, + 385 + ], + [ + 574, + 582, + 393, + 421 + ], + [ + 574, + 582, + 429, + 457 + ], + [ + 574, + 582, + 465, + 493 + ], + [ + 574, + 582, + 501, + 529 + ], + [ + 574, + 582, + 537, + 565 + ], + [ + 574, + 582, + 573, + 600 + ], + [ + -600, + 600, + 565, + 573 + ], + [ + 610, + 618, + -600, + -551 + ], + [ + 610, + 618, + -543, + -515 + ], + [ + 610, + 618, + -507, + -479 + ], + [ + 610, + 618, + -471, + -443 + ], + [ + 610, + 618, + -435, + -407 + ], + [ + 610, + 618, + -399, + -371 + ], + [ + 610, + 618, + -363, + -335 + ], + [ + 610, + 618, + -327, + -299 + ], + [ + 610, + 618, + -291, + -263 + ], + [ + 610, + 618, + -255, + -227 + ], + [ + 610, + 618, + -219, + -191 + ], + [ + 610, + 618, + -183, + -155 + ], + [ + 610, + 618, + -147, + -119 + ], + [ + 610, + 618, + -111, + -83 + ], + [ + 610, + 618, + -75, + -47 + ], + [ + 610, + 618, + -39, + -11 + ], + [ + 610, + 618, + -3, + 25 + ], + [ + 610, + 618, + 81, + 97 + ], + [ + 610, + 618, + 105, + 133 + ], + [ + 610, + 618, + 141, + 169 + ], + [ + 610, + 618, + 177, + 205 + ], + [ + 610, + 618, + 213, + 241 + ], + [ + 610, + 618, + 249, + 277 + ], + [ + 610, + 618, + 285, + 313 + ], + [ + 610, + 618, + 321, + 349 + ], + [ + 610, + 618, + 357, + 385 + ], + [ + 610, + 618, + 393, + 421 + ], + [ + 610, + 618, + 429, + 457 + ], + [ + 610, + 618, + 465, + 493 + ], + [ + 610, + 618, + 501, + 529 + ], + [ + 610, + 618, + 537, + 565 + ], + [ + 610, + 618, + 573, + 600 + ], + [ + -600, + 600, + 601, + 609 + ] + ], + "river_patches": [ + [ + -2000, + 2000, + 60, + 76, + true + ], + [ + 70, + 2000, + 29, + 60, + true + ] + ], + "removed_invisible_triangles": 18309, + "front_face": "CCW" +} diff --git a/assets/sprites/rooftop/modern-facades-v1.png b/assets/sprites/rooftop/modern-facades-v1.png new file mode 100644 index 0000000..71ce106 Binary files /dev/null and b/assets/sprites/rooftop/modern-facades-v1.png differ diff --git a/assets/sprites/rooftop/night-sky-v1.png b/assets/sprites/rooftop/night-sky-v1.png new file mode 100644 index 0000000..ddd4ffc Binary files /dev/null and b/assets/sprites/rooftop/night-sky-v1.png differ diff --git a/assets/sprites/rooftop/packed-v1.json b/assets/sprites/rooftop/packed-v1.json new file mode 100644 index 0000000..13f47ad --- /dev/null +++ b/assets/sprites/rooftop/packed-v1.json @@ -0,0 +1,45 @@ +{ + "panorama": [ + 512, + 512, + 4 + ], + "materials": [ + 256, + 256, + 8 + ], + "details": [ + 256, + 256, + 15 + ], + "city": [ + 512, + 512, + 22 + ], + "city-night": [ + 512, + 512, + 22 + ], + "night": [ + 512, + 512, + 4 + ], + "rail-hands": [ + 640, + 320, + 32 + ], + "lighting": [ + 512, + 512, + 3 + ], + "city_renderer": "world-space MASM geometry; sky only is raster", + "source": "Built-in image generation; runtime geometry and interaction are assembly-only", + "reference": "https://www.loftprojectetagi.ru/visitors/krysha/" +} diff --git a/assets/sprites/rooftop/panels-v1.png b/assets/sprites/rooftop/panels-v1.png new file mode 100644 index 0000000..c987b0e Binary files /dev/null and b/assets/sprites/rooftop/panels-v1.png differ diff --git a/assets/sprites/rooftop/panorama-v1.png b/assets/sprites/rooftop/panorama-v1.png new file mode 100644 index 0000000..c6c7ce0 Binary files /dev/null and b/assets/sprites/rooftop/panorama-v1.png differ diff --git a/assets/sprites/rooftop/panorama-v2.png b/assets/sprites/rooftop/panorama-v2.png new file mode 100644 index 0000000..e63cedd Binary files /dev/null and b/assets/sprites/rooftop/panorama-v2.png differ diff --git a/assets/sprites/rooftop/prompts-v2.md b/assets/sprites/rooftop/prompts-v2.md new file mode 100644 index 0000000..ad3de57 --- /dev/null +++ b/assets/sprites/rooftop/prompts-v2.md @@ -0,0 +1,27 @@ +# Rooftop City Source Art + +Generated with the built-in image generation tool. The selected PNG originals +are preserved in this directory; the offline packer prepares runtime atlases. +The city itself is MASM world-space geometry, not a generated city background. + +## Prompt Set + +- `rail-hands-v1.png`: transparent eight-pose sprite sheet, two anatomical hands + and complete forearms reaching forward and resting naturally on a low rooftop + handrail, first-person view, dark sleeves, consistent camera and anatomy. +- `city-facades-v1.png`: eight detailed, straight-on Petersburg-inspired + classical building facades, varied pastel masonry, carved window surrounds, + cornices and realistic windows; no perspective, sky or ground. +- `modern-facades-v1.png`: varied straight-on contemporary glass, brick, + townhouse and civic building facades, suitable for wrapping real geometry. +- `roof-zinc-v1.png`: detailed weathered blue-grey standing-seam zinc roof + surface, evenly lit, seamless material texture, no building silhouette. +- `sunset-sky-v1.png`: panoramic sunset sky with fine pink and warm cloud + streaks fading into cool blue; no buildings, landscape or city silhouette. +- `night-sky-v1.png`: panoramic deep night sky with subtle clouds and stars; + no buildings, landscape or city silhouette. + +These record the asset specifications. The generated images are originals, +not an exact reconstruction of any real venue or skyline. Alpha cleanup, +optical-flow in-betweens, window masks, light maps and mesh generation are +deterministic offline processing; no image service is used by the game. diff --git a/assets/sprites/rooftop/rail-hands-v1.png b/assets/sprites/rooftop/rail-hands-v1.png new file mode 100644 index 0000000..bce37f0 Binary files /dev/null and b/assets/sprites/rooftop/rail-hands-v1.png differ diff --git a/assets/sprites/rooftop/roof-zinc-v1.png b/assets/sprites/rooftop/roof-zinc-v1.png new file mode 100644 index 0000000..41eb187 Binary files /dev/null and b/assets/sprites/rooftop/roof-zinc-v1.png differ diff --git a/assets/sprites/rooftop/sofa-views-v1.png b/assets/sprites/rooftop/sofa-views-v1.png new file mode 100644 index 0000000..dbf710f Binary files /dev/null and b/assets/sprites/rooftop/sofa-views-v1.png differ diff --git a/assets/sprites/rooftop/sunset-sky-v1.png b/assets/sprites/rooftop/sunset-sky-v1.png new file mode 100644 index 0000000..d8e98a7 Binary files /dev/null and b/assets/sprites/rooftop/sunset-sky-v1.png differ diff --git a/bin/poker.exe b/bin/poker.exe index 33daddb..c5429fd 100644 Binary files a/bin/poker.exe and b/bin/poker.exe differ diff --git a/build/AssemblerCasinoLauncher.cs b/build/AssemblerCasinoLauncher.cs index 635046f..8f4070e 100644 --- a/build/AssemblerCasinoLauncher.cs +++ b/build/AssemblerCasinoLauncher.cs @@ -8,7 +8,7 @@ namespace AssemblerCasinoLauncher { static class Program { - const string ExpectedPokerSha256 = "07D474A0223390E7AC4F4F779818D77CC2DAE5123F4BDB0F72DD975473267310"; + const string ExpectedPokerSha256 = "63C04B59D74772621A7563EA70DFA6764C60684459EE7D046AF30B555A01FF15"; [STAThread] static void Main() diff --git a/build/graphics/poker.exe b/build/graphics/poker.exe index 6a31bf6..c5429fd 100644 Binary files a/build/graphics/poker.exe and b/build/graphics/poker.exe differ diff --git a/casino3d.inc b/casino3d.inc index f8b768f..f9aadf2 100644 --- a/casino3d.inc +++ b/casino3d.inc @@ -5350,10 +5350,14 @@ check_walk_mode: call UpdateInteriorTravel3D call UpdateInteriorEye3D call UpdateCamera3D + ; The roof owns all planes/depth; an indoor prepass would be discarded. + cmp hallLevel3D,HALL_ROOF + je walk_world_ready call RenderCeilingAndFloor call RenderWalls call PrepareCityDepth3D call RenderSprites +walk_world_ready: call RenderHall3D call RenderHUD call RenderMinimap diff --git a/dist/Assembler Casino.exe b/dist/Assembler Casino.exe index 33520b2..0b6e644 100644 Binary files a/dist/Assembler Casino.exe and b/dist/Assembler Casino.exe differ diff --git a/docs/DARTS.md b/docs/DARTS.md index ea83c54..a775541 100644 --- a/docs/DARTS.md +++ b/docs/DARTS.md @@ -31,8 +31,12 @@ required by the game. 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; +previous 2370mm/900px view. Texture resolution and scoring geometry are preserved. +The v2 board keeps the same physical bounds and 512px pack dimensions, +but uses larger high-contrast numerals, supersampled wires and lower grain. +The reticle is a 7x7 footprint containing only nine one-pixel marks, with no thick +outline. Individual marks adapt to bright/dark sectors for readability. +Max watches the same moving aim point for a short release window; he does not reroll a hit or bypass the sway. The independent diff --git a/docs/FORTUNE_SESSION.md b/docs/FORTUNE_SESSION.md index ceaac95..e0db183 100644 --- a/docs/FORTUNE_SESSION.md +++ b/docs/FORTUNE_SESSION.md @@ -25,22 +25,41 @@ 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. +The second selection is a fictional, theatrical student-themed lottery, separate +from tarot and not an assessment of actual academic merit. The form asks for a +first name, surname, birth date (DD.MM.YYYY), group and inclusive score bounds +from 0 to 100. Tab cycles fields, clicking focuses one, Backspace edits, and +Enter or the summon button submits. Empty fields, invalid calendar dates +(including leap years), reversed bounds and scores above 100 are rejected. + +Identity fields only dress the scene: they never seed or weight the draw. One +native random draw is committed on valid submission. The ball rises for 1.4 s, +hands circle through 32 poses with 16 mist frames for 5 s, then a glowing number +appears inside the ball. Short typewriter speech accompanies the stages. The +result fades into view and stays readable while the identity and chosen bounds +remain at the bottom. Renders, duplicate submits and clicks during the ritual +cannot reroll. After the reveal, Edit returns to the filled form for another +session. No network, inference worker, profile or bankroll changes are involved. +Fields are kept only in session memory and cleared on exit; nothing is saved. + +The number uses baked, antialiased Cambria numeral sprites with translucent blue +glow, not enlarged 3x5 block digits. Each of the 16 mist frames has its own measured +glass-centre anchor, excluding the pedestal. One assembly resolver chooses both +the ball frame and the matching number position, including the rising movement. +Numerals stay at a constant size and the fog remains visible through their glow. +The random draw and its probabilities are unchanged; identity never biases it. ## Assets Generated source art and production prompts: `assets/sprites/fortune/`. -Offline packer: `tools/generators/pack_fortune_session.py`. +Offline packers: `tools/generators/pack_fortune_session.py` and then +`tools/generators/pack_fortune_orb.py` (tracking metadata and numeral sprites). 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 +Art loads only when seated and all eight packs are freed on exit/shutdown. The seated view is independent of the walking room's existing asset packs and map. ## Verification @@ -48,11 +67,17 @@ seated view is independent of the walking room's existing asset packs and map. 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 +question persistence, crystal-ball form validation, inclusive draws, fixed bounds, +input locking, duplicate submission, 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 orb regression captures each of the 16 native frames twice, with and without +the number. Pixel differences must stay centred on that frame's glass sphere; +this also checks that opacity is restored and the numeral pack is freed on exit. +`--prove-fate-anchor-regression` deliberately restores the old fixed coordinates +in a test-only module and must fail the native tracking assertions. 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. diff --git a/docs/ROOFTOP.md b/docs/ROOFTOP.md new file mode 100644 index 0000000..90a81b3 --- /dev/null +++ b/docs/ROOFTOP.md @@ -0,0 +1,123 @@ +# Rooftop Lounge "Vysota" + +The terrace is an assembly-only walkable hall level (`HALL_ROOF = 4`), reached +by a real elevator cabin near the north wall to the right of the cabaret. +Existing games, basement stairs, scratch tickets and fortune room stay in place. + +## Travel + +Use E at the exterior call panel, then walk into the open cabin using the normal +movement keys. The inside panel offers the roof or the main hall depending on +the current landing. E starts a trip only when the player is fully inside. +Doors physically slide apart into projecting pockets. Movement is blocked during +closing/travel, while looking around remains possible. After one second of +closing and 3.2 seconds of travel the destination changes, doors open, and the +player walks out. Position, angle and standing height do not drift during travel. + +An idle open door closes after 2.8 seconds away from the elevator. It reverses +if the player re-enters its approach, rather than shutting through them. Arrival +keeps the cabin coordinates and opens the other landing; there is no forced +camera path or automatic walking. The illuminated physical indicator changes +during travel and shows `1` or `R` at the landings. + +## Terrace + +- A fully volumetric Petersburg-inspired city, seen from a sixty-metre tower. + Only the sky is a painted texture; buildings, streets, roofs, parks, canal, + bridges, monuments, cars and boats are real world-space geometry. +- Finite HD decking, low railings and an open sky, rather than an indoor ceiling + or a floor extending to infinity beyond the roof edge. +- Three three-seat sofas, four armchairs, three bar stools and three stools at + a rail-side standing counter: 13 selectable furniture positions, 19 seat places. +- Seven coffee tables, eight potted trees, flowering troughs, eight varied guests, + two supported strings of lanterns, and a slatted pergola over a working bar. +- Sofas and chairs use closed native volumes with separate padded cushions. + Planter boxes contain soil and multiple foliage clusters at different depths. + Whole-object photographs are not pasted onto every furniture face. +- E at a railing eases into a leaning pose, with thirty-two hand/forearm frames + anchored to the actual rail height. E or movement releases the rail. +- While leaning, mouse, arrows and A/D can turn up to 73 degrees either way. The + rail contact is projected in world space so the hands do not follow head yaw. +- Guests walk on the terrace with collision checks, pauses and walking frames. +- In the open first-floor elevator, 1 selects sunset and 2 selects night. +- E sits/stands; walking forward/backward also stands and restores the approach + position. Stool views use bar height and rail-side stools face the city. +- The bar reuses the existing mixing/drinking system and prices (100/200/free + water). Its camera is temporarily positioned at the new counter and completely + restored after rendering. Drinks are the only new-location actions that spend + or save bankroll, through the existing purchase routine. + +## Assets And Build + +Generated sources are under `assets/sprites/rooftop/`; runtime ANM1 packs are under +`assets/animations/rooftop/`. Run `tools/generators/pack_rooftop.py` offline to pack +the sky, materials, semantic night-window textures, ground lighting, hands, +district layout and collision map. Run `pack_rooftop_gl.py` afterwards to build +the static city and moving vehicle triangle meshes. Python, Pillow, NumPy and +OpenCV are offline developer tools, not game dependencies. + +The native MASM HD city renderer uses the Windows system OpenGL library, a +private hidden rendering context and an offscreen framebuffer up to 1280x960. It uses +textured mesh display lists, mipmaps, normals, directional dusk lighting, +distance fog and a separate emissive-window pass at night. The existing terrace, +hands and HUD are composited by direct native pixel copying into one DIB. Other games retain +their existing renderer. If the required GL capabilities are unavailable, the +assembly software city renderer remains the fallback. No C/C++ renderer, model +engine, browser runtime, cloud service or runtime downloader is introduced. + +The eastern avenue is now a broad river with raised embankments, promenades, +trees, piers, three full-span bridges and further riverfront quarters. Water +texture flow and six moving boats keep it from being a static blue plane. +Street rectangles are clipped into disjoint junctions rather than coplanar, +overlapping strips. The terrace deck uses mipmapped HD geometry rather than +the unfiltered software floor when HD rendering is available. City, foreground +and HUD are composed into one offscreen DIB and copied to the window once; +there is no intermediate bare-city frame. glReadPixels supplies the required +readback synchronisation, without a redundant preceding glFinish. + +## Rooftop Performance + +- F8 cycles automatic, eco (640x480) and detailed (1280x960) city rendering. + Automatic starts at 960x720 and drops to eco after sustained frames over 34 ms. + Switching back to automatic retries 960x720. The scene, view distance, collisions + and gameplay do not change with the render profile; other rooms are unaffected. +- The offline mesh packer discards only triangles that face away from every + possible rooftop eye position. Runtime back-face culling handles head turns. + The static mesh now contains 35,386 triangles instead of 53,695. +- CPU foreground surfaces use projected screen bounds and camera-facing box + sides. The final frame avoids an extra GDI scaling/alpha-blending pass. +- Mipmap generation is optional on older OpenGL drivers. If hardware rendering + cannot initialise, the software ground renderer still draws roads and water, + including solid-colour fallback surfaces when a texture pack is absent. +- `runtime/roof-renderer.txt` records the local GL vendor, renderer, version, + initialisation stage, city-pack availability and render width. It contains no + personal data and is not sent anywhere. Stage 0 means successful initialisation. + +Integrated Intel graphics use shared system memory; the small dedicated-memory +figure alone is not a useful rendering budget. Actual laptop performance must be +checked on that machine, not inferred from the desktop benchmark. + +The art direction takes atmospheric inspiration from the rooftop reference: +https://www.loftprojectetagi.ru/visitors/krysha/ . It is an original game backdrop, +not an exact survey or reproduction of that venue. Generated source images are +preserved alongside the packer metadata. Older city panoramas and furniture +reference photographs remain source art, not the active city/furniture renderer. + +Runtime sources: `src_graphics/rooftop*.inc` and the shared MASM city primitives. +The normal MASM build and root/bin publisher ship the feature. Rebuilding the +existing installer is separate from updating the ordinary game executable. + +## Verification + +`tools/tests/check_hall_expansion.py` compiles the real current MASM routines and +tests closed/open door collision, manual cabin entry/exit, both trips, timing, +camera/position/bankroll preservation, furniture/rail bounds, seating, automatic +closing, thirty repeated bar renders, asset loading/freeing and native screenshots. + +It exports a 70x78 walk grid from actual `CanWalk3D` calls. Reachability checks +verify a connected route from the cabin to the bar, both sides of the terrace, +and every seating approach after the denser layout. Framebuffer captures are +written under `build/hall-expansion/roof-*.png`. `--parity` verifies tested +procedures against the full shipping object before publishing. +The native harness also checks all three resolution profiles, automatic downgrade, +F8 switching, and software roads/water with both present and missing city textures. diff --git a/poker.exe b/poker.exe index 6a31bf6..c5429fd 100644 Binary files a/poker.exe and b/poker.exe differ diff --git a/src_graphics/city_bins.inc b/src_graphics/city_bins.inc index b7861ff..1f66d4b 100644 --- a/src_graphics/city_bins.inc +++ b/src_graphics/city_bins.inc @@ -5,12 +5,19 @@ 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,161 + cmp eax,MATERIAL_JACKBOX_FRONT jb @F - cmp eax,163 + cmp eax,MATERIAL_JACKBOX_BACK ja @F invoke ResolveJackboxSurface3D,material ret +@@: + cmp eax,ROOF_DECK + jb @F + cmp eax,ROOF_LAST_MATERIAL + ja @F + invoke ResolveRoofSurface3D,material + ret @@: cmp eax,160 jne @F @@ -116,6 +123,7 @@ city_prepare_ray: PrepareCityRays3D ENDP CityRayPlane3D PROC coordinate:DWORD,origin:DWORD,direction:DWORD + local farDelta:DWORD mov ecx, direction mov eax, ecx test eax, eax @@ -124,6 +132,17 @@ CityRayPlane3D PROC coordinate:DWORD,origin:DWORD,direction:DWORD @@: cmp eax, 256 jb city_ray_parallel + ; A long city ray can overflow IDIV before its depth is clipped. Reject + ; intersections beyond the exterior far plane before forming the dividend. + shl eax,10 + mov farDelta,eax + mov eax,coordinate + sub eax,origin + cdq + xor eax,edx + sub eax,edx + cmp eax,farDelta + jae city_ray_parallel mov eax, coordinate sub eax, origin cdq @@ -140,14 +159,78 @@ CityRayPlane3D ENDP DrawCityVertical3D PROC uses ebx esi edi axis:DWORD,coordinate:DWORD,loBound:DWORD,hiBound:DWORD,zLow:DWORD,zHigh:DWORD,material:DWORD local depth:DWORD,top:DWORD,bottom:DWORD,span:DWORD,column:DWORD local sourceX:DWORD,stepY:DWORD,sourceY:DWORD,destination:DWORD,row:DWORD,texture:DWORD - local textureW:DWORD,textureH:DWORD + local textureW:DWORD,textureH:DWORD,faceLight:DWORD + local endColumn:DWORD,firstX:DWORD,firstDepth:DWORD,clipX:DWORD,clipY:DWORD invoke ResolveCitySurface3D,material test eax, eax jz city_vertical_done mov texture, eax mov textureW, ecx mov textureH, edx + mov faceLight,230 + cmp roofCityPass3D,0 + je city_vertical_setup + cmp axis,0 + jne city_vertical_light_y + mov eax,playerX + cmp eax,coordinate + jl city_vertical_setup + mov faceLight,142 + jmp city_vertical_setup +city_vertical_light_y: + mov faceLight,165 + mov eax,playerY + cmp eax,coordinate + jl city_vertical_setup + mov faceLight,218 +city_vertical_setup: mov column, 0 + mov endColumn,SCREEN_W + cmp hallLevel3D,HALL_ROOF + jne city_vertical_column + mov eax,coordinate + mov edx,loBound + cmp axis,0 + je @F + xchg eax,edx +@@: + invoke ProjectCityPoint3D,eax,edx + mov firstX,eax + mov firstDepth,edx + mov eax,coordinate + mov edx,hiBound + cmp axis,0 + je @F + xchg eax,edx +@@: + invoke ProjectCityPoint3D,eax,edx + cmp edx,4096 + jg city_vertical_clip_second + cmp firstDepth,4096 + jle city_vertical_done + jmp city_vertical_column +city_vertical_clip_second: + cmp firstDepth,4096 + jle city_vertical_column + mov edx,firstX + cmp eax,edx + jge @F + xchg eax,edx +@@: + sub edx,2 + add eax,3 + test edx,edx + jns @F + xor edx,edx +@@: + cmp eax,SCREEN_W + jle @F + mov eax,SCREEN_W +@@: + cmp edx,eax + jge city_vertical_done + mov column,edx + mov endColumn,eax city_vertical_column: mov ebx, column cmp axis, 0 @@ -200,15 +283,15 @@ city_vertical_along: mov sourceX, eax mov eax, viewEye3D sub eax, zHigh - imul eax, SCREEN_H - cdq + mov ecx,SCREEN_H + imul ecx idiv depth add eax, viewHorizon3D mov top, eax mov eax, viewEye3D sub eax, zLow - imul eax, SCREEN_H - cdq + mov ecx,SCREEN_H + imul ecx idiv depth add eax, viewHorizon3D mov bottom, eax @@ -274,7 +357,10 @@ city_vertical_pixel: @@: mov edx, depth mov cityDepthBuffer3D[edi], edx + cmp roofCityPass3D,0 + jne city_vertical_scene_color and eax, 00FFFFFFh +city_vertical_scene_color: cmp cityBackdrop3D, 0 je @F push eax @@ -288,7 +374,21 @@ city_vertical_pixel: shr eax, 1 add eax, 00101828h @@: + cmp roofCityPass3D,0 + je city_vertical_color_ready + invoke ShadeRoofCityPixel3D,eax,depth,faceLight,material + mov ebx,edi + shr ebx,2 + mov ecx,roofPixelGlow3D + mov roofGlowMask3D[ebx],cl +city_vertical_color_ready: mov screenBuffer[edi], eax + cmp hallLevel3D,HALL_ROOF + jne city_vertical_next_pixel + mov ebx,edi + shr ebx,2 + mov ecx,roofCityPass3D + mov roofCityMask3D[ebx],cl city_vertical_next_pixel: mov eax, stepY add sourceY, eax @@ -299,7 +399,8 @@ city_vertical_next_pixel: jb city_vertical_pixel city_vertical_next_column: inc column - cmp column, SCREEN_W + mov eax,endColumn + cmp column,eax jb city_vertical_column city_vertical_done: ret @@ -308,7 +409,7 @@ DrawCityVertical3D ENDP DrawCityHorizontal3D PROC uses ebx esi edi xMin:DWORD,xMax:DWORD,yMin:DWORD,yMax:DWORD,z:DWORD,material:DWORD local left:DWORD,right:DWORD,top:DWORD,bottom:DWORD,corner:DWORD,pointX:DWORD,pointY:DWORD local depth:DWORD,row:DWORD,column:DWORD,stepX:DWORD,stepY:DWORD,scaleX:DWORD,scaleY:DWORD,texture:DWORD - local textureW:DWORD,textureH:DWORD,nearClipped:DWORD + local textureW:DWORD,textureH:DWORD,nearClipped:DWORD,visibleCorners:DWORD invoke ResolveCitySurface3D,material test eax, eax jz city_horizontal_done @@ -316,6 +417,7 @@ DrawCityHorizontal3D PROC uses ebx esi edi xMin:DWORD,xMax:DWORD,yMin:DWORD,yMax mov textureW, ecx mov textureH, edx mov nearClipped, 0 + mov visibleCorners,0 mov left, SCREEN_W mov right, 0 mov top, SCREEN_H @@ -338,6 +440,7 @@ city_horizontal_corner: mov nearClipped, 1 jmp city_horizontal_next_corner city_horizontal_visible_corner: + inc visibleCorners mov esi, edx cmp eax, left jge @F @@ -349,8 +452,8 @@ city_horizontal_visible_corner: @@: mov eax, viewEye3D sub eax, z - imul eax, SCREEN_H - cdq + mov ecx,SCREEN_H + imul ecx idiv esi add eax, viewHorizon3D cmp eax, top @@ -365,6 +468,11 @@ city_horizontal_next_corner: inc corner cmp corner, 4 jb city_horizontal_corner + cmp hallLevel3D,HALL_ROOF + jne @F + cmp visibleCorners,0 + je city_horizontal_done +@@: cmp nearClipped, 0 je @F ; Ray bounds/depth below clip the polygon; a behind-camera corner cannot discard it. @@ -414,8 +522,8 @@ city_horizontal_row: jz city_horizontal_next_row mov eax, viewEye3D sub eax, z - imul eax, SCREEN_H - cdq + mov ebx,SCREEN_H + imul ebx idiv ecx cmp eax, 4096 jle city_horizontal_next_row @@ -508,7 +616,25 @@ city_horizontal_alpha: mov ecx, depth mov cityDepthBuffer3D[edi], ecx and eax, 00FFFFFFh + cmp roofCityPass3D,0 + je city_horizontal_color_ready + cmp material,ROOF_CITY_ROAD + jne @F + invoke SampleRoofGroundLight3D,eax,pointX,pointY +@@: + invoke ShadeRoofCityPixel3D,eax,depth,244,material + mov ebx,edi + shr ebx,2 + mov ecx,roofPixelGlow3D + mov roofGlowMask3D[ebx],cl +city_horizontal_color_ready: mov screenBuffer[edi], eax + cmp hallLevel3D,HALL_ROOF + jne city_horizontal_advance + mov ebx,edi + shr ebx,2 + mov ecx,roofCityPass3D + mov roofCityMask3D[ebx],cl city_horizontal_advance: mov eax, stepX add pointX, eax @@ -529,6 +655,30 @@ city_horizontal_done: DrawCityHorizontal3D ENDP DrawCityBoxSides3D PROC xMin:DWORD,xMax:DWORD,yMin:DWORD,yMax:DWORD,zLow:DWORD,zHigh:DWORD,material:DWORD + cmp hallLevel3D,HALL_ROOF + jne city_box_all_sides + mov eax,playerX + cmp eax,xMin + jge @F + invoke DrawCityVertical3D,0,xMin,yMin,yMax,zLow,zHigh,material +@@: + mov eax,playerX + cmp eax,xMax + jle @F + invoke DrawCityVertical3D,0,xMax,yMin,yMax,zLow,zHigh,material +@@: + mov eax,playerY + cmp eax,yMin + jge @F + invoke DrawCityVertical3D,1,yMin,xMin,xMax,zLow,zHigh,material +@@: + mov eax,playerY + cmp eax,yMax + jle @F + invoke DrawCityVertical3D,1,yMax,xMin,xMax,zLow,zHigh,material +@@: + ret +city_box_all_sides: invoke DrawCityVertical3D, 0, xMin, yMin, yMax, zLow, zHigh, material invoke DrawCityVertical3D, 0, xMax, yMin, yMax, zLow, zHigh, material invoke DrawCityVertical3D, 1, yMin, xMin, xMax, zLow, zHigh, material diff --git a/src_graphics/darts_data.inc b/src_graphics/darts_data.inc index bd6a8dd..49a542d 100644 --- a/src_graphics/darts_data.inc +++ b/src_graphics/darts_data.inc @@ -30,6 +30,7 @@ DartsWallText3D PROTO :DWORD,:DWORD,:DWORD DrawDartsScoreboard3D PROTO DrawDartsProps3D PROTO DrawDartsArm3D PROTO :DWORD,:SDWORD,:SDWORD +DrawDartsReticle3D PROTO :SDWORD,:SDWORD DartsScore3D PROTO :SDWORD,:SDWORD SettleDarts3D PROTO DrawDartsRoom3D PROTO @@ -45,7 +46,7 @@ dtWall3D AnimPack3D <> dtChair3D AnimPack3D <> dtTable3D AnimPack3D <> dtRoom3D AnimPack3D <> -dtBoardPath db "assets\animations\darts\board-v1.bin",0 +dtBoardPath db "assets\animations\darts\board-v2.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 @@ -134,6 +135,7 @@ dtDouble db 68,0 dtTriple db 84,0 dtSingle db 83,0 dtTarget db 214,197,203,220,0 +dtReticleOffsets3D dd -3,0,-2,0,2,0,3,0,0,-3,0,-2,0,2,0,3,0,0 .data? dtRayX3D dd 640 dup(?) dtRayZ3D dd 640 dup(?) diff --git a/src_graphics/darts_environment.inc b/src_graphics/darts_environment.inc index 366d56b..cd78d48 100644 --- a/src_graphics/darts_environment.inc +++ b/src_graphics/darts_environment.inc @@ -1,3 +1,39 @@ +; Nine physical pixels, with contrast chosen from the surface beneath each one. +DrawDartsReticle3D PROC uses ebx esi edi targetX:SDWORD,targetY:SDWORD + lea esi,dtReticleOffsets3D + mov ebx,9 +dt_reticle_point: + mov eax,targetX + add eax,[esi] + cmp eax,640 + jae dt_reticle_next + mov edx,targetY + add edx,[esi+4] + cmp edx,32 + jl dt_reticle_next + cmp edx,400 + jge dt_reticle_next + imul edx,640 + add edx,eax + lea edi,screenBuffer[edx*4] + movzx eax,byte ptr [edi] + movzx ecx,byte ptr [edi+1] + lea eax,[eax+ecx*2] + movzx ecx,byte ptr [edi+2] + add eax,ecx + mov ecx,00F6E6ADh + cmp eax,560 + jb @F + mov ecx,00152021h +@@: + mov [edi],ecx +dt_reticle_next: + add esi,8 + dec ebx + jnz dt_reticle_point + ret +DrawDartsReticle3D ENDP + ; 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 diff --git a/src_graphics/darts_render.inc b/src_graphics/darts_render.inc index 5f115ff..646967e 100644 --- a/src_graphics/darts_render.inc +++ b/src_graphics/darts_render.inc @@ -545,22 +545,7 @@ dt_render_pose: 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 + invoke DrawDartsReticle3D,bx0,by0 dt_render_hud: invoke DrawBox,0,0,320,16,00151D20h invoke DrawString8x8,8,4,OFFSET dtTitle,00E8D7ADh diff --git a/src_graphics/fortune_dialog.inc b/src_graphics/fortune_dialog.inc index c5e2ca2..ebc0418 100644 --- a/src_graphics/fortune_dialog.inc +++ b/src_graphics/fortune_dialog.inc @@ -11,7 +11,7 @@ fortune_session_load: invoke LoadAnimPack3D,esi,fortuneSessionPaths3D[ebx*4] add esi,SIZEOF AnimPack3D inc ebx - cmp ebx,7 + cmp ebx,FORTUNE_SESSION_PACK_COUNT jb fortune_session_load mov engineMode,MODE_FORTUNE mov paused3D,0 @@ -24,6 +24,7 @@ fortune_session_load: mov fortuneQuestionLength3D,0 mov fortuneQuestion3D,0 mov fortuneWorkerState3D,0 + call ResetFortuneFate3D mov eax,animationClock3D mov fortuneSessionClock3D,eax invoke SetFortuneSpeech3D,OFFSET fortuneWelcome3D @@ -35,13 +36,14 @@ BeginFortuneDialog3D ENDP ; Kept as the public exit/shutdown hook. No inference process is launched. StopFortuneWorker3D PROC uses ebx esi lea esi,fortuneSessionPacks3D - mov ebx,7 + mov ebx,FORTUNE_SESSION_PACK_COUNT fortune_session_free: invoke FreeAnimPack3D,esi add esi,SIZEOF AnimPack3D dec ebx jnz fortune_session_free mov fortuneWorkerState3D,0 + call ResetFortuneFate3D ret StopFortuneWorker3D ENDP @@ -129,11 +131,17 @@ fortune_char_tarot: ret fortune_char_orb: mov fortuneSubMode3D,FORTUNE_SUBMODE_ORB - mov fortunePhase3D,1 + call ResetFortuneFate3D + mov fortunePhase3D,0 mov fortunePhaseTime3D,0 - invoke SetFortuneSpeech3D,OFFSET fortuneOrbStatus3D + invoke SetFortuneSpeech3D,OFFSET fortuneFateReady3D ret fortune_char_question: + cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB + jne @F + invoke FortuneFateChar3D,character + ret +@@: cmp fortuneSubMode3D,FORTUNE_SUBMODE_TAROT jne fortune_char_done cmp fortunePhase3D,0 @@ -277,6 +285,7 @@ fortune_update_orb: jb fortune_update_done mov fortunePhase3D,2 mov fortunePhaseTime3D,0 + invoke SetFortuneSpeech3D,OFFSET fortuneFateSwirl3D jmp fortune_update_done fortune_orb_swirl: cmp fortunePhase3D,2 @@ -285,7 +294,7 @@ fortune_orb_swirl: jb fortune_update_done mov fortunePhase3D,3 mov fortunePhaseTime3D,0 - invoke SetFortuneSpeech3D,OFFSET fortuneOrbText3D + invoke SetFortuneSpeech3D,OFFSET fortuneFateReveal3D fortune_update_done: ret UpdateFortuneSession3D ENDP @@ -315,6 +324,37 @@ HandleFortuneDialog3D PROC jae fortune_handle_reset jmp fortune_handle_action fortune_handle_menu_click: + cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB + jne fortune_handle_selection_click + cmp fortunePhase3D,0 + jne fortune_handle_done + ; Form uses physical pixels; mouse input here uses half-resolution units. + mov eax,mouseY3D + sub eax,162 + jb fortune_handle_done + xor edx,edx + mov ecx,21 + div ecx + cmp eax,2 + ja fortune_handle_done + cmp edx,11 + ja fortune_handle_done + shl eax,1 + cmp mouseX3D,156 + jb fortune_handle_form_left + cmp mouseX3D,168 + jb fortune_handle_done + cmp mouseX3D,312 + ja fortune_handle_done + inc eax + jmp fortune_handle_form_focus +fortune_handle_form_left: + cmp mouseX3D,8 + jb fortune_handle_done +fortune_handle_form_focus: + mov fortuneFateField3D,eax + ret +fortune_handle_selection_click: cmp fortuneSubMode3D,FORTUNE_SUBMODE_MENU jne fortune_handle_done cmp mouseY3D,196 @@ -332,6 +372,11 @@ fortune_handle_orb: fortune_handle_action: cmp fortuneSubMode3D,FORTUNE_SUBMODE_MENU je fortune_handle_tarot + cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB + jne @F + call SubmitFortuneFate3D + ret +@@: cmp fortuneSubMode3D,FORTUNE_SUBMODE_TAROT jne fortune_handle_done cmp fortunePhase3D,0 @@ -367,9 +412,14 @@ fortune_handle_menu: fortune_handle_reset: cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB jne @F - mov fortunePhase3D,1 + cmp fortunePhase3D,3 + jne fortune_handle_done + cmp fortunePhaseTime3D,1200 + jb fortune_handle_done + mov fortunePhase3D,0 mov fortunePhaseTime3D,0 - invoke SetFortuneSpeech3D,OFFSET fortuneOrbStatus3D + mov fortuneFateResult3D,-1 + invoke SetFortuneSpeech3D,OFFSET fortuneFateReady3D ret @@: cmp fortunePhase3D,12 @@ -692,6 +742,8 @@ fortune_render_hand_gesture: mov esi,eax jmp fortune_render_hands fortune_render_orb_hands: + cmp fortunePhase3D,0 + je fortune_render_hands mov ebx,OFFSET fortuneSessionOrbHands3D mov eax,fortunePhaseTime3D shr eax,5 @@ -737,28 +789,24 @@ fortune_render_menu: invoke DrawBuckshotText3D,364,404,OFFSET fortuneFate3D,00D2E8F2h jmp fortune_render_footer fortune_render_orb: - mov eax,fortunePhaseTime3D - shr eax,6 - and eax,15 - mov esi,eax - mov ebx,195 - cmp fortunePhase3D,1 + cmp fortunePhase3D,0 + jne fortune_render_orb_active + call DrawFortuneFateForm3D + jmp fortune_render_footer +fortune_render_orb_active: + call ResolveFortuneOrb3D + invoke DrawAnimSprite3D,OFFSET fortuneSessionMist3D,fortuneOrbFrame3D,fortuneOrbX3D,fortuneOrbY3D,128,128 + cmp fortunePhase3D,3 jne @F - mov eax,fortunePhaseTime3D - imul eax,135 - xor edx,edx - mov ecx,1400 - div ecx - mov ebx,330 - sub ebx,eax + call DrawFortuneFateResult3D @@: - invoke DrawAnimSprite3D,OFFSET fortuneSessionMist3D,esi,214,ebx,128,128 + call DrawFortuneFateIdentity3D fortune_render_footer: call DrawFortuneSpeech3D cmp fortuneSubMode3D,FORTUNE_SUBMODE_MENU je fortune_render_exit_button cmp fortuneSubMode3D,FORTUNE_SUBMODE_TAROT - jne fortune_render_again_button + jne fortune_render_fate_button invoke DrawBoxBorder,8,227,76,12,000B1E30h,00445F75h,1 mov esi,OFFSET fortuneAsk3D cmp fortunePhase3D,0 @@ -766,9 +814,20 @@ fortune_render_footer: mov esi,OFFSET fortuneContinue3D @@: invoke DrawBuckshotText3D,24,458,esi,00CCE4F4h + jmp fortune_render_again_button +fortune_render_fate_button: + cmp fortunePhase3D,0 + jne fortune_render_again_button + invoke DrawBoxBorder,8,227,76,12,000B1E30h,00445F75h,1 + invoke DrawBuckshotText3D,24,458,OFFSET fortuneFateStart3D,00CCE4F4h fortune_render_again_button: invoke DrawBoxBorder,88,227,76,12,000B1E30h,00445F75h,1 - invoke DrawBuckshotText3D,192,458,OFFSET fortuneAgain3D,0091A8BCh + mov esi,OFFSET fortuneAgain3D + cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB + jne @F + mov esi,OFFSET fortuneFateEdit3D +@@: + invoke DrawBuckshotText3D,192,458,esi,0091A8BCh invoke DrawBoxBorder,172,227,64,12,000B1E30h,00445F75h,1 invoke DrawBuckshotText3D,368,458,OFFSET fortuneMenu3D,00CCE4F4h fortune_render_exit_button: @@ -776,3 +835,425 @@ fortune_render_exit_button: invoke DrawBuckshotText3D,516,458,OFFSET fortuneExit3D,00CCE4F4h ret RenderFortuneDialog3D ENDP + +ResetFortuneFate3D PROC uses edi + lea edi,fortuneFateLengths3D + xor eax,eax + mov ecx,6 + rep stosd + lea edi,fortuneFateFields3D + mov ecx,96 + rep stosd + mov fortuneFateField3D,0 + mov fortuneFateResult3D,-1 + mov fortuneFateFields3D[256],'9' + mov fortuneFateFields3D[320],'1' + mov fortuneFateFields3D[321],'0' + mov fortuneFateLengths3D[16],1 + mov fortuneFateLengths3D[20],2 + ret +ResetFortuneFate3D ENDP + +FortuneFateChar3D PROC uses ebx esi edi character:DWORD + cmp fortunePhase3D,0 + jne fate_char_done + mov ebx,fortuneFateField3D + cmp ebx,6 + jae fate_char_done + mov eax,character + cmp eax,9 + je fate_char_next + mov esi,ebx + shl esi,6 + lea esi,fortuneFateFields3D[esi] + mov ecx,fortuneFateLengths3D[ebx*4] + cmp eax,8 + je fate_char_backspace + cmp eax,32 + jb fate_char_done + cmp eax,255 + ja fate_char_done + cmp eax,127 + je fate_char_done + cmp ecx,fortuneFateLimits3D[ebx*4] + jae fate_char_done + cmp ebx,2 + je fate_char_date + cmp ebx,4 + jae fate_char_digit + cmp eax,32 + jne fate_char_store + test ecx,ecx + jz fate_char_done + cmp BYTE PTR [esi+ecx-1],32 + je fate_char_done + jmp fate_char_store +fate_char_date: + cmp eax,'/' + jne @F + mov eax,'.' +@@: + cmp eax,'.' + jne fate_char_date_digit + cmp ecx,2 + je fate_char_store + cmp ecx,5 + je fate_char_store + jmp fate_char_done +fate_char_date_digit: + cmp eax,'0' + jb fate_char_done + cmp eax,'9' + ja fate_char_done + cmp ecx,2 + je fate_char_date_dot + cmp ecx,5 + jne fate_char_store +fate_char_date_dot: + mov BYTE PTR [esi+ecx],'.' + inc ecx + jmp fate_char_store +fate_char_digit: + cmp eax,'0' + jb fate_char_done + cmp eax,'9' + ja fate_char_done +fate_char_store: + mov [esi+ecx],al + inc ecx + mov fortuneFateLengths3D[ebx*4],ecx + mov BYTE PTR [esi+ecx],0 + ret +fate_char_backspace: + test ecx,ecx + jz fate_char_done + dec ecx + mov fortuneFateLengths3D[ebx*4],ecx + mov BYTE PTR [esi+ecx],0 + ret +fate_char_next: + inc ebx + cmp ebx,6 + jb @F + xor ebx,ebx +@@: + mov fortuneFateField3D,ebx +fate_char_done: + ret +FortuneFateChar3D ENDP + +; Validation is calendar-based, including leap years, not a birth-date heuristic. +ValidateFortuneBirth3D PROC uses ebx esi edi + local dayValue:DWORD,monthValue:DWORD + cmp fortuneFateLengths3D[8],10 + jne fate_birth_bad + lea esi,fortuneFateFields3D[128] + cmp BYTE PTR [esi+2],'.' + jne fate_birth_bad + cmp BYTE PTR [esi+5],'.' + jne fate_birth_bad + xor ebx,ebx + xor edi,edi +fate_birth_parse: + movzx eax,BYTE PTR [esi+edi] + cmp edi,2 + je fate_birth_day + cmp edi,5 + je fate_birth_month + sub eax,'0' + cmp eax,9 + ja fate_birth_bad + imul ebx,10 + add ebx,eax + jmp fate_birth_next +fate_birth_day: + mov dayValue,ebx + xor ebx,ebx + jmp fate_birth_next +fate_birth_month: + mov monthValue,ebx + xor ebx,ebx +fate_birth_next: + inc edi + cmp edi,10 + jb fate_birth_parse + cmp ebx,1900 + jb fate_birth_bad + cmp ebx,2099 + ja fate_birth_bad + mov eax,monthValue + dec eax + cmp eax,11 + ja fate_birth_bad + movzx ecx,fortuneFateMonthDays3D[eax] + cmp eax,1 + jne fate_birth_check_day + test ebx,3 + jnz fate_birth_check_day + cmp ebx,1900 + je fate_birth_check_day + inc ecx +fate_birth_check_day: + cmp dayValue,1 + jb fate_birth_bad + cmp dayValue,ecx + ja fate_birth_bad + mov eax,1 + ret +fate_birth_bad: + xor eax,eax + ret +ValidateFortuneBirth3D ENDP + +ParseFortuneFateNumber3D PROC uses ebx esi slot:DWORD + mov eax,slot + mov ecx,fortuneFateLengths3D[eax*4] + test ecx,ecx + jz fate_number_bad + cmp ecx,3 + ja fate_number_bad + shl eax,6 + lea esi,fortuneFateFields3D[eax] + xor eax,eax +fate_number_parse: + movzx ebx,BYTE PTR [esi] + sub ebx,'0' + cmp ebx,9 + ja fate_number_bad + imul eax,10 + add eax,ebx + inc esi + loop fate_number_parse + cmp eax,100 + ja fate_number_bad + ret +fate_number_bad: + mov eax,-1 + ret +ParseFortuneFateNumber3D ENDP + +SubmitFortuneFate3D PROC uses ebx + cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB + jne fate_submit_done + cmp fortunePhase3D,0 + jne fate_submit_done + xor ebx,ebx +fate_submit_required: + cmp fortuneFateLengths3D[ebx*4],0 + je fate_submit_missing + inc ebx + cmp ebx,6 + jb fate_submit_required + call ValidateFortuneBirth3D + test eax,eax + jz fate_submit_date + invoke ParseFortuneFateNumber3D,4 + cmp eax,-1 + je fate_submit_low + mov fortuneFateLowValue3D,eax + invoke ParseFortuneFateNumber3D,5 + cmp eax,-1 + je fate_submit_high + cmp eax,fortuneFateLowValue3D + jb fate_submit_high + mov fortuneFateHighValue3D,eax + sub eax,fortuneFateLowValue3D + inc eax + ; Exactly one draw, independent of identity. Renders and double clicks never reroll. + invoke RandomRange3D,eax + add eax,fortuneFateLowValue3D + mov fortuneFateResult3D,eax + mov fortunePhase3D,1 + mov fortunePhaseTime3D,0 + mov eax,animationClock3D + mov fortuneSessionClock3D,eax + invoke SetFortuneSpeech3D,OFFSET fortuneFateRising3D +fate_submit_done: + ret +fate_submit_missing: + mov fortuneFateField3D,ebx + invoke SetFortuneSpeech3D,OFFSET fortuneFateRequired3D + ret +fate_submit_date: + mov fortuneFateField3D,2 + invoke SetFortuneSpeech3D,OFFSET fortuneFateDateError3D + ret +fate_submit_low: + mov fortuneFateField3D,4 + jmp fate_submit_range +fate_submit_high: + mov fortuneFateField3D,5 +fate_submit_range: + invoke SetFortuneSpeech3D,OFFSET fortuneFateRangeError3D + ret +SubmitFortuneFate3D ENDP + +DrawFortuneFateForm3D PROC uses ebx esi edi + local px:DWORD,py:DWORD,edgeInk:DWORD + invoke DrawBoxBorder,8,149,304,74,00081322h,0046637Fh,1 + invoke DrawBuckshotText3D,24,304,OFFSET fortuneFatePlay3D,0086ABBCh + xor esi,esi +fate_form_field: + mov eax,fortuneFateXs3D[esi*4] + mov px,eax + shr eax,1 + mov ebx,fortuneFateYs3D[esi*4] + mov py,ebx + shr ebx,1 + mov edgeInk,00314D65h + cmp esi,fortuneFateField3D + jne @F + mov edgeInk,0088DDEAh +@@: + invoke DrawBoxBorder,eax,ebx,144,11,000B1D30h,edgeInk,1 + mov eax,px + add eax,8 + mov ebx,py + sub ebx,9 + invoke DrawBuckshotText3D,eax,ebx,fortuneFateLabels3D[esi*4],00A9C8D8h + mov edi,esi + shl edi,6 + add edi,OFFSET fortuneFateFields3D + mov eax,px + add eax,8 + mov ebx,py + add ebx,7 + cmp esi,2 + jne fate_form_value + cmp fortuneFateLengths3D[8],0 + jne fate_form_value + invoke DrawBuckshotText3D,eax,ebx,OFFSET fortuneFateHint3D,005A7A95h + jmp fate_form_caret +fate_form_value: + invoke DrawBuckshotText3D,eax,ebx,edi,00E8EFF4h +fate_form_caret: + cmp esi,fortuneFateField3D + jne fate_form_next + test animationClock3D,200h + jnz fate_form_next + mov eax,fortuneFateLengths3D[esi*4] + shl eax,3 + add eax,px + add eax,8 + mov ebx,py + add ebx,7 + invoke DrawBuckshotText3D,eax,ebx,OFFSET fortuneCaret3D,00B8F3FFh +fate_form_next: + inc esi + cmp esi,6 + jb fate_form_field + ret +DrawFortuneFateForm3D ENDP + +DrawFortuneFateIdentity3D PROC uses ebx esi edi + invoke DrawBoxBorder,8,190,304,33,00081322h,0046637Fh,1 + xor esi,esi +fate_identity_field: + mov eax,fortuneFateXs3D[esi*4] + add eax,8 + mov ebx,esi + shr ebx,1 + imul ebx,20 + add ebx,388 + mov edi,esi + shl edi,6 + add edi,OFFSET fortuneFateFields3D + cmp esi,4 + jb @F + invoke DrawBuckshotText3D,eax,ebx,fortuneFateLabels3D[esi*4],0086ABBCh + mov eax,fortuneFateXs3D[esi*4] + add eax,40 +@@: + invoke DrawBuckshotText3D,eax,ebx,edi,00E8EFF4h + inc esi + cmp esi,6 + jb fate_identity_field + ret +DrawFortuneFateIdentity3D ENDP + +; One resolver supplies the frame, sprite placement and its measured glass centre. +ResolveFortuneOrb3D PROC uses ebx + mov eax,fortunePhaseTime3D + shr eax,6 + and eax,15 + mov fortuneOrbFrame3D,eax + mov ebx,eax + mov fortuneOrbX3D,214 + mov fortuneOrbY3D,195 + cmp fortunePhase3D,1 + jne fate_orb_anchor + mov eax,fortunePhaseTime3D + cmp eax,1400 + jbe @F + mov eax,1400 +@@: + imul eax,135 + xor edx,edx + mov ecx,1400 + div ecx + mov ecx,330 + sub ecx,eax + mov fortuneOrbY3D,ecx +fate_orb_anchor: + mov eax,fortuneOrbAnchorX3D[ebx*4] + add eax,128 + shr eax,8 + add eax,fortuneOrbX3D + mov fortuneOrbCenterX3D,eax + mov eax,fortuneOrbAnchorY3D[ebx*4] + add eax,128 + shr eax,8 + add eax,fortuneOrbY3D + mov fortuneOrbCenterY3D,eax + ret +ResolveFortuneOrb3D ENDP + +DrawFortuneFateResult3D PROC uses ebx esi edi + local numberX:DWORD,numberY:DWORD + cmp fortuneFateResult3D,100 + ja fate_result_done + invoke FormatNumber,fortuneFateResult3D,OFFSET fortuneFateNumber3D + lea edi,fortuneFateNumber3D + xor ebx,ebx +fate_result_length: + cmp BYTE PTR [edi+ebx],0 + je fate_result_place + inc ebx + jmp fate_result_length +fate_result_place: + dec ebx + imul ebx,20 + add ebx,32 + shr ebx,1 + neg ebx + add ebx,fortuneOrbCenterX3D + mov numberX,ebx + mov eax,fortuneOrbCenterY3D + sub eax,24 + mov numberY,eax + ; Fade into the mist at a constant size; preserve opacity for later draws. + push animOpacity3D + mov eax,fortunePhaseTime3D + cmp eax,900 + jbe @F + mov eax,900 +@@: + imul eax,255 + xor edx,edx + mov ecx,900 + div ecx + mov animOpacity3D,eax +fate_result_digit: + movzx esi,BYTE PTR [edi] + test esi,esi + jz fate_result_restore + sub esi,'0' + invoke DrawAnimSprite3D,OFFSET fortuneSessionNumerals3D,esi,numberX,numberY,32,48 + add numberX,20 + inc edi + jmp fate_result_digit +fate_result_restore: + pop animOpacity3D +fate_result_done: + ret +DrawFortuneFateResult3D ENDP diff --git a/src_graphics/fortune_dialog_data.inc b/src_graphics/fortune_dialog_data.inc index b743d0c..099b76f 100644 --- a/src_graphics/fortune_dialog_data.inc +++ b/src_graphics/fortune_dialog_data.inc @@ -2,6 +2,7 @@ MODE_FORTUNE equ 25 FORTUNE_SUBMODE_MENU equ 0 FORTUNE_SUBMODE_TAROT equ 1 FORTUNE_SUBMODE_ORB equ 2 +FORTUNE_SESSION_PACK_COUNT equ 8 BeginFortuneDialog3D PROTO HandleFortuneDialog3D PROTO RenderFortuneDialog3D PROTO @@ -15,6 +16,15 @@ SetFortuneSpeech3D PROTO :DWORD FortuneCardSpeech3D PROTO :DWORD DrawFortuneSpeech3D PROTO DrawFortuneSessionCard3D PROTO :DWORD +ResetFortuneFate3D PROTO +FortuneFateChar3D PROTO :DWORD +SubmitFortuneFate3D PROTO +ValidateFortuneBirth3D PROTO +ParseFortuneFateNumber3D PROTO :DWORD +DrawFortuneFateForm3D PROTO +DrawFortuneFateIdentity3D PROTO +DrawFortuneFateResult3D PROTO +ResolveFortuneOrb3D PROTO .data fortuneSessionPacks3D LABEL BYTE fortuneSessionRoom3D AnimPack3D <> @@ -24,6 +34,7 @@ fortuneSessionGesture3D AnimPack3D <> fortuneSessionOrbHands3D AnimPack3D <> fortuneSessionMist3D AnimPack3D <> fortuneSessionCards3D AnimPack3D <> +fortuneSessionNumerals3D 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 @@ -31,8 +42,10 @@ 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 +fortuneSessionPath7 db "assets\animations\fortune\orb-numerals-v1.bin",0 fortuneSessionPaths3D dd OFFSET fortuneSessionPath0,OFFSET fortuneSessionPath1,OFFSET fortuneSessionPath2,OFFSET fortuneSessionPath3 dd OFFSET fortuneSessionPath4,OFFSET fortuneSessionPath5,OFFSET fortuneSessionPath6 + dd OFFSET fortuneSessionPath7 fortuneSubMode3D dd 0 fortunePhase3D dd 0 fortunePhaseTime3D dd 0 @@ -120,6 +133,46 @@ fortuneCardNames3D dd OFFSET fortuneCardName0,OFFSET fortuneCardName1,OFFSET for dd OFFSET fortuneCardName12,OFFSET fortuneCardName13,OFFSET fortuneCardName14,OFFSET fortuneCardName15 dd OFFSET fortuneCardName16,OFFSET fortuneCardName17,OFFSET fortuneCardName18,OFFSET fortuneCardName19 dd OFFSET fortuneCardName20,OFFSET fortuneCardName21 +; Local, ephemeral identity fields dress a fictional crystal-ball lottery. +fortuneFateField3D dd 0 +fortuneFateLengths3D dd 6 dup(0) +fortuneFateFields3D db 384 dup(0) +fortuneFateLowValue3D dd 9 +fortuneFateHighValue3D dd 10 +fortuneFateResult3D dd -1 +fortuneFateLimits3D dd 30,30,10,20,3,3 +fortuneFateXs3D dd 16,336,16,336,16,336 +fortuneFateYs3D dd 324,324,366,366,408,408 +fortuneFateLabels3D dd OFFSET fortuneFateName3D,OFFSET fortuneFateSurname3D,OFFSET fortuneFateBirth3D,OFFSET fortuneFateGroup3D + dd OFFSET fortuneFateLow3D,OFFSET fortuneFateHigh3D +fortuneFateMonthDays3D db 31,28,31,30,31,30,31,31,30,31,30,31 +fortuneFateNumber3D db 12 dup(0) +fortuneOrbFrame3D dd 0 +fortuneOrbX3D dd 214 +fortuneOrbY3D dd 195 +fortuneOrbCenterX3D dd 0 +fortuneOrbCenterY3D dd 0 +include src_graphics\fortune_orb_art.inc +fortuneFateName3D db 200,204,223,0 +fortuneFateSurname3D db 212,192,204,200,203,200,223,0 +fortuneFateBirth3D db 196,192,210,192,32,208,206,198,196,197,205,200,223,0 +fortuneFateGroup3D db 195,208,211,207,207,192,0 +fortuneFateLow3D db 206,210,0 +fortuneFateHigh3D db 196,206,0 +fortuneFateStart3D db 207,208,200,199,194,192,210,220,0 +fortuneFateEdit3D db 200,199,204,197,205,200,210,220,0 +fortuneFateHint3D db 196,196,46,204,204,46,195,195,195,195,0 +fortuneFateReady3D db 205,224,231,238,226,232,32,242,238,227,238,44,10,247,252,254,32,241,243,228,252,225,243,32,240,229,248,232,236,46,0 +fortuneFateRising3D db 210,232,248,229,46,10,216,224,240,32,239,240,238,225,243,230,228,224,229,242,241,255,46,0 +fortuneFateSwirl3D db 210,243,236,224,237,32,241,238,225,232,240,224,229,242,241,255,46,46,46,10,207,243,241,242,252,32,241,243,228,252,225,224,10,237,224,231,238,226,184,242,32,247,232,241,235,238,46,0 +fortuneFateReveal3D db 208,229,248,229,237,232,229,32,239,240,232,237,255,242,238,46,10,216,224,240,32,237,224,231,226,224,235,32,247,232,241,235,238,46,0 +fortuneFateRequired3D db 205,229,32,226,241,229,32,232,236,229,237,224,10,237,224,231,226,224,237,251,46,0 +fortuneFateDateError3D db 210,224,234,238,233,32,228,224,242,251,32,237,229,242,46,10,205,224,231,238,226,232,32,228,229,237,252,32,240,238,230,228,229,237,232,255,46,0 +fortuneFateRangeError3D db 205,224,231,238,226,232,32,227,240,224,237,232,246,251,58,10 + db 238,242,32,236,229,237,252,248,229,227,238,32,234,32,225,238,235,252,248,229,236,243,44,10 + db 238,242,32,48,32,228,238,32,49,48,48,46,0 +fortuneFateCaption3D db 208,197,216,197,205,200,197,32,209,211,196,220,193,219,0 +fortuneFatePlay3D db 200,195,208,206,194,206,201,32,208,200,210,211,192,203,0 .data? fortuneBackground3D dd SCREEN_SIZE dup(?) .data diff --git a/src_graphics/fortune_orb_art.inc b/src_graphics/fortune_orb_art.inc new file mode 100644 index 0000000..60723b6 --- /dev/null +++ b/src_graphics/fortune_orb_art.inc @@ -0,0 +1,3 @@ +; Generated by tools/generators/pack_fortune_orb.py; glass only, pedestal excluded. +fortuneOrbAnchorX3D dd 18261,16299,16299,14336,18176,16213,16213,14336,18176,16299,16299,14421,18091,16384,16128,14507 +fortuneOrbAnchorY3D dd 14610,14637,14628,14630,14030,14040,14055,14066,13948,13933,13940,13922,12327,12342,12305,12324 diff --git a/src_graphics/hall_data.inc b/src_graphics/hall_data.inc index 185b5ce..8c4f56b 100644 --- a/src_graphics/hall_data.inc +++ b/src_graphics/hall_data.inc @@ -169,4 +169,5 @@ include src_graphics\hall_expansion_data.inc include src_graphics\fortune_room_data.inc include src_graphics\fortune_dialog_data.inc include src_graphics\cabaret_data.inc +include src_graphics\rooftop_data.inc .code diff --git a/src_graphics/hall_expansion.inc b/src_graphics/hall_expansion.inc index 86948b6..9c82951 100644 --- a/src_graphics/hall_expansion.inc +++ b/src_graphics/hall_expansion.inc @@ -194,6 +194,9 @@ interior_begin_finish: BeginWalkableStairs3D ENDP UpdateInteriorTravel3D PROC + call UpdateRoof3D + cmp hallLevel3D,HALL_ROOF + je interior_travel_done call UpdateUpperEffects3D call UpdateBasementDoor3D call UpdateFortunePassage3D @@ -410,6 +413,11 @@ UpdateInteriorEye3D PROC je UpdateStairHeight3D cmp engineMode,MODE_WALK jne interior_eye_done + cmp hallLevel3D,HALL_ROOF + jne @F + call UpdateRoofEye3D + ret +@@: mov viewHorizon3D,HALF_H mov viewEye3D,HALL_STANDING_EYE mov viewCeiling3D,FP_1*5/2 diff --git a/src_graphics/hall_scene.inc b/src_graphics/hall_scene.inc index eaf0560..ffd5a1b 100644 --- a/src_graphics/hall_scene.inc +++ b/src_graphics/hall_scene.inc @@ -2,11 +2,17 @@ RenderCeilingAndFloor PROC uses ebx esi edi local row:DWORD,distance:DWORD,sx:DWORD,sy:DWORD,wx:DWORD,wy:DWORD local material:DWORD,shade:DWORD + cmp hallLevel3D,HALL_ROOF + jne @F + call DrawRoofSky3D +@@: mov row, 0 hall_plane_row: mov eax, row cmp eax, viewHorizon3D jge hall_plane_floor + cmp hallLevel3D,HALL_ROOF + je hall_plane_next_row cmp outdoorRender3D, 0 jne hall_plane_next_row mov ecx, viewHorizon3D @@ -96,7 +102,18 @@ hall_plane_distance: imul eax, SCREEN_W*4 lea edi, screenBuffer[eax] mov esi, SCREEN_W -hall_plane_pixel: +hall_plane_pixel: + cmp hallLevel3D,HALL_ROOF + jne roof_floor_inside + cmp wx,FP_1*109/10 + jl roof_floor_outside + cmp wx,FP_1*291/10 + jg roof_floor_outside + cmp wy,FP_1*9/10 + jl roof_floor_outside + cmp wy,FP_1*211/10 + jg roof_floor_outside +roof_floor_inside: mov ebx, material cmp outdoorRender3D, 0 je hall_plane_sample @@ -129,7 +146,13 @@ hall_plane_sample: jl hall_plane_choose_hd hall_plane_wood: mov ebx, 85 -hall_plane_choose_hd: +hall_plane_choose_hd: + cmp ebx,ROOF_DECK + jne hall_plane_not_roof + mov ecx,roofMaterialArt3D.pixels + test ecx,ecx + jnz hall_plane_hd_coordinates +hall_plane_not_roof: cmp ebx,132 jb hall_plane_other_hd cmp ebx,135 @@ -237,6 +260,9 @@ hall_plane_next_row: cmp row, SCREEN_H jb hall_plane_row ret +roof_floor_outside: + mov eax,[edi] + jmp hall_plane_store RenderCeilingAndFloor ENDP RenderSprites PROC uses ebx esi edi @@ -330,7 +356,8 @@ hall_sprites_done: ret RenderSprites ENDP -InitHall3D PROC uses esi edi +InitHall3D PROC uses esi edi + call InitRoof3D lea esi, hallLayout3D lea edi, casinoMap mov ecx, 256 @@ -370,7 +397,8 @@ init_plane_done: ret InitHall3D ENDP -LoadHallAssets3D PROC +LoadHallAssets3D PROC + call LoadRoofAssets3D call InitUpperSurfaces3D call LoadFortuneArt3D invoke LoadAnimPack3D, OFFSET upperMaterialArt3D, OFFSET upperMaterialPath3D @@ -469,7 +497,8 @@ hall_load_sign: ret LoadHallAssets3D ENDP -FreeHallAssets3D PROC +FreeHallAssets3D PROC + call FreeRoofAssets3D call FreeFortuneArt3D invoke FreeAnimPack3D, OFFSET upperMaterialArt3D invoke FreeAnimPack3D,OFFSET upperGamesArt3D @@ -574,6 +603,11 @@ surface_missing: ResolveWorldSurface3D ENDP RenderHall3D PROC uses ebx esi + cmp hallLevel3D,HALL_ROOF + jne @F + call RenderRoof3D + ret +@@: cmp engineMode,MODE_STAIRS je fortune_hall_stairs cmp hallLevel3D,3 @@ -694,6 +728,7 @@ hall_render_lounge: call RenderHallDressing3D call RenderHallPortals3D call RenderFortuneEntrance3D + call RenderLift3D call ApplyHallLighting3D call RenderInteriorViews3D call DrawBasementLeaf3D @@ -759,7 +794,12 @@ hall_target_done: ret HallTarget3D ENDP -CheckHallInteractions3D PROC +CheckHallInteractions3D PROC + cmp hallLevel3D,HALL_ROOF + jne @F + call CheckRoofInteractions3D + ret +@@: cmp hallLevel3D,3 jne @F call CheckFortuneInteractions3D @@ -790,6 +830,7 @@ hall_check_other: invoke HallTarget3D,FP_1*305/10,21*FP_1,0,OFFSET upperStairPrompt3D call CheckNpcInteractions3D call CheckFortuneInteractions3D + call CheckRoofInteractions3D ret hall_check_bunker: mov interactTargetId, 0 @@ -805,6 +846,9 @@ TriggerHallInteraction3D PROC mov eax,1 ret @@: + call TryRoofInteraction3D + test eax,eax + jnz hall_trigger_handled cmp interactTargetId,INTERACT_DARTS jne hall_not_darts call BeginDarts3D @@ -978,6 +1022,11 @@ RenderBar3D PROC uses ebx jne @F mov playerX,FP_1*25/2 mov playerY,16*FP_1+FP_1/3 +@@: + cmp hallLevel3D,HALL_ROOF + jne @F + mov playerX,15*FP_1 + mov playerY,FP_1*63/10 @@: mov viewHorizon3D, HALF_H mov viewEye3D, HALL_STANDING_EYE @@ -1007,6 +1056,10 @@ bar_draw_title: cmp hallLevel3D,2 jne @F mov eax,OFFSET upperBarName3D +@@: + cmp hallLevel3D,HALL_ROOF + jne @F + mov eax,OFFSET roofBarName3D @@: invoke DrawString8x8, 18,15,eax,00E7CA88h cmp barPhase3D, 2 @@ -1094,7 +1147,15 @@ city_world_guard_right: ret RenderCityGuards3D ENDP -CanWalkHall3D PROC uses esi ebx px:DWORD,py:DWORD +CanWalkHall3D PROC uses esi ebx px:DWORD,py:DWORD + cmp hallLevel3D,HALL_ROOF + jne @F + invoke RoofCanWalk3D,px,py + ret +@@: + invoke LiftCanWalk3D,px,py + test eax,eax + jz hall_collision_blocked cmp engineMode,MODE_STAIRS je fortune_collision_stairs cmp hallLevel3D,3 @@ -1404,6 +1465,7 @@ RenderPatrons3D ENDP include src_graphics\world_dressing.inc include src_graphics\terminal.inc include src_graphics\hall_expansion.inc +include src_graphics\rooftop.inc include src_graphics\steel_door.inc include src_graphics\interior_portals.inc include src_graphics\fortune_room.inc diff --git a/src_graphics/jackbox_data.inc b/src_graphics/jackbox_data.inc index d010247..2ff1ed2 100644 --- a/src_graphics/jackbox_data.inc +++ b/src_graphics/jackbox_data.inc @@ -3,15 +3,15 @@ ; ============================================================================== MODE_JACKBOX equ 28 -INTERACT_JACKBOX equ 48 +INTERACT_JACKBOX equ 52 JACKBOX_X equ FP_1*306/10 JACKBOX_Y equ FP_1*235/10 JACKBOX_TRACK_COUNT equ 3 -MATERIAL_JACKBOX_FRONT equ 161 +MATERIAL_JACKBOX_FRONT equ 211 MATERIAL_JACKBOX_SIDE equ 121 -MATERIAL_JACKBOX_BACK equ 163 +MATERIAL_JACKBOX_BACK equ 213 LoadJackboxArt3D PROTO FreeJackboxArt3D PROTO diff --git a/src_graphics/platform_code.inc b/src_graphics/platform_code.inc index da3fc0f..ad62451 100644 --- a/src_graphics/platform_code.inc +++ b/src_graphics/platform_code.inc @@ -431,6 +431,9 @@ present_geometry_ready: jz @F invoke PatBlt, h3DDC, eax, 0, ecx, edi, 42h @@: + invoke PresentRoofHD3D,h3DDC,offsetX,offsetY,viewW,viewH + test eax,eax + jnz present_done invoke StretchDIBits, h3DDC, offsetX, offsetY, viewW, viewH, 0, 0, SCREEN_W, SCREEN_H, OFFSET screenBuffer, OFFSET bmi3D, 0, SRCCOPY present_done: ret diff --git a/src_graphics/rooftop.inc b/src_graphics/rooftop.inc new file mode 100644 index 0000000..2d7bd5b --- /dev/null +++ b/src_graphics/rooftop.inc @@ -0,0 +1,1178 @@ +; Elevator phases: shut, opening, open, closing, travelling. Movement stays manual. +InitRoof3D PROC + mov liftPhase3D,0 + mov liftDoor3D,0 + mov liftTrip3D,0 + mov liftRideTime3D,0 + mov liftAwayTime3D,0 + mov roofSeat3D,0 + mov roofLean3D,0 + mov roofLeanTime3D,0 + mov roofNight3D,0 + mov roofAAValid3D,0 + mov roofUpdateClock3D,-1 + ret +InitRoof3D ENDP + +LoadRoofAssets3D PROC + invoke LoadAnimPack3D,OFFSET roofSkyArt3D,OFFSET roofSkyPath3D + invoke LoadAnimPack3D,OFFSET roofMaterialArt3D,OFFSET roofMaterialPath3D + invoke LoadAnimPack3D,OFFSET roofDetailArt3D,OFFSET roofDetailPath3D + invoke LoadAnimPack3D,OFFSET roofCityArt3D,OFFSET roofCityPath3D + invoke LoadAnimPack3D,OFFSET roofCityNightArt3D,OFFSET roofCityNightPath3D + invoke LoadAnimPack3D,OFFSET roofHandsArt3D,OFFSET roofHandsPath3D + invoke LoadAnimPack3D,OFFSET roofNightArt3D,OFFSET roofNightPath3D + invoke LoadAnimPack3D,OFFSET roofLightingArt3D,OFFSET roofLightingPath3D + ret +LoadRoofAssets3D ENDP + +FreeRoofAssets3D PROC + call FreeRoofHD3D + invoke FreeAnimPack3D,OFFSET roofSkyArt3D + invoke FreeAnimPack3D,OFFSET roofMaterialArt3D + invoke FreeAnimPack3D,OFFSET roofDetailArt3D + invoke FreeAnimPack3D,OFFSET roofCityArt3D + invoke FreeAnimPack3D,OFFSET roofCityNightArt3D + invoke FreeAnimPack3D,OFFSET roofHandsArt3D + invoke FreeAnimPack3D,OFFSET roofNightArt3D + invoke FreeAnimPack3D,OFFSET roofLightingArt3D + ret +FreeRoofAssets3D ENDP + +ResolveRoofSurface3D PROC material:DWORD + mov eax,material + sub eax,ROOF_FACADE + cmp eax,21 + ja roof_surface_furniture + cmp roofCityArt3D.pixels,0 + je roof_surface_missing + shl eax,20 + add eax,roofCityArt3D.pixels + cmp roofNight3D,0 + je roof_surface_city_ready + cmp roofCityNightArt3D.pixels,0 + je roof_surface_city_ready + sub eax,roofCityArt3D.pixels + add eax,roofCityNightArt3D.pixels +roof_surface_city_ready: + mov ecx,512 + mov edx,512 + ret +roof_surface_furniture: + mov eax,material + cmp eax,ROOF_SOFA_FRONT + jb @F + sub eax,ROOF_SOFA_FRONT + cmp eax,6 + ja roof_surface_missing + add eax,8 + cmp roofDetailArt3D.pixels,0 + je roof_surface_missing + shl eax,18 + add eax,roofDetailArt3D.pixels + jmp roof_surface_size +@@: + cmp eax,ROOF_LIGHT + jne @F + mov eax,OFFSET roofIndicatorPixel3D + mov ecx,1 + mov edx,1 + ret +@@: + cmp eax,ROOF_INDICATOR + jne @F + mov eax,OFFSET roofIndicatorPixels3D + mov ecx,64 + mov edx,64 + ret +@@: + sub eax,ROOF_DECK + cmp eax,7 + ja roof_surface_detail + cmp roofMaterialArt3D.pixels,0 + je roof_surface_missing + shl eax,18 + add eax,roofMaterialArt3D.pixels + jmp roof_surface_size +roof_surface_detail: + sub eax,8 + cmp eax,7 + ja roof_surface_missing + cmp roofDetailArt3D.pixels,0 + je roof_surface_missing + shl eax,18 + add eax,roofDetailArt3D.pixels +roof_surface_size: + mov ecx,256 + mov edx,256 + ret +roof_surface_missing: + xor eax,eax + ret +ResolveRoofSurface3D ENDP + +DrawRoofSky3D PROC uses ebx esi edi + local row:DWORD,sourceRow:DWORD,offsetX:DWORD,pixels:DWORD + cmp roofSkyArt3D.pixels,0 + jne roof_sky_ready + invoke DrawBox,0,0,320,240,00334C6Ah + ret +roof_sky_ready: + mov eax,roofSkyArt3D.pixels + cmp roofNight3D,0 + je @F + cmp roofNightArt3D.pixels,0 + je @F + mov eax,roofNightArt3D.pixels +@@: + mov pixels,eax + mov eax,playerAngle + shl eax,2 + mov offsetX,eax + mov row,0 + lea edi,screenBuffer +roof_sky_row: + mov eax,row + add eax,HALF_H + sub eax,viewHorizon3D + shl eax,9 + xor edx,edx + mov ecx,480 + div ecx + cmp eax,511 + jbe @F + mov eax,511 +@@: + shl eax,9 + mov sourceRow,eax + xor ebx,ebx +roof_sky_pixel: + mov eax,roofSkyColumns3D[ebx*4] + add eax,offsetX + and eax,2047 + mov ecx,eax + shr ecx,9 + shl ecx,18 + and eax,511 + add eax,ecx + add eax,sourceRow + mov esi,pixels + mov eax,[esi+eax*4] + and eax,00FFFFFFh + stosd + inc ebx + cmp ebx,640 + jb roof_sky_pixel + inc row + cmp row,480 + jb roof_sky_row + ret +DrawRoofSky3D ENDP + +StandFromRoofSeat3D PROC + cmp roofSeat3D,0 + je roof_stand_done + mov eax,roofReturnX3D + mov playerX,eax + mov eax,roofReturnY3D + mov playerY,eax + mov eax,roofReturnAngle3D + mov playerAngle,eax + mov roofSeat3D,0 +roof_stand_done: + ret +StandFromRoofSeat3D ENDP + +UpdateRoofEye3D PROC + mov viewHorizon3D,HALF_H + mov viewCeiling3D,FP_1*5/2 + mov viewEye3D,ROOF_STANDING_EYE + cmp roofLean3D,0 + je @F + mov eax,roofLeanTime3D + imul eax,FP_1*28/100 + xor edx,edx + mov ecx,1100 + div ecx + sub viewEye3D,eax + mov eax,roofLeanTime3D + imul eax,84 + xor edx,edx + div ecx + sub viewHorizon3D,eax + ret +@@: + cmp roofSeat3D,0 + je roof_eye_done + mov viewEye3D,ROOF_SEATED_EYE + mov eax,roofSeat3D + dec eax + cmp roofSeatKind3D[eax*4],2 + jb roof_eye_done + mov viewEye3D,ROOF_STANDING_EYE +roof_eye_done: + ret +UpdateRoofEye3D ENDP + +UpdateRoof3D PROC uses ebx + mov eax,animationClock3D + cmp eax,roofUpdateClock3D + je roof_update_done + mov roofUpdateClock3D,eax + cmp engineMode,MODE_WALK + jne roof_update_done + cmp hallLevel3D,0 + je roof_update_lift + cmp hallLevel3D,HALL_ROOF + jne roof_update_done + mov eax,deltaTime3D + cmp eax,100 + jbe @F + mov eax,100 +@@: + invoke UpdateRoofGuests3D,eax + cmp roofLean3D,0 + je roof_update_seat + call UpdateRoofLook3D + mov ebx,deltaTime3D + cmp ebx,100 + jbe @F + mov ebx,100 +@@: + cmp roofLean3D,3 + je roof_lean_retract + mov eax,ebx + shr eax,2 + inc eax + cmp keys3D[41h],0 + je @F + sub playerAngle,eax +@@: + cmp keys3D[44h],0 + je @F + add playerAngle,eax +@@: + call UpdateRoofLook3D + cmp keys3D[57h],0 + jne roof_lean_release + cmp keys3D[53h],0 + jne roof_lean_release + cmp keys3D[26h],0 + jne roof_lean_release + cmp keys3D[28h],0 + jne roof_lean_release + add roofLeanTime3D,ebx + cmp roofLeanTime3D,1100 + jb roof_update_lift + mov roofLeanTime3D,1100 + mov roofLean3D,2 + jmp roof_update_lift +roof_lean_release: + mov roofLean3D,3 +roof_lean_retract: + shl ebx,1 + sub roofLeanTime3D,ebx + ja roof_update_lift + mov roofLeanTime3D,0 + mov roofLean3D,0 + mov eax,roofReturnX3D + mov playerX,eax + mov eax,roofReturnY3D + mov playerY,eax + mov eax,roofReturnAngle3D + mov playerAngle,eax + call ClearKeys3D + jmp roof_update_lift +roof_update_seat: + cmp roofSeat3D,0 + je roof_update_lift + cmp keys3D[57h],0 + jne roof_update_stand + cmp keys3D[53h],0 + jne roof_update_stand + cmp keys3D[26h],0 + jne roof_update_stand + cmp keys3D[28h],0 + je roof_update_lift +roof_update_stand: + call StandFromRoofSeat3D +roof_update_lift: + cmp hallLevel3D,0 + jne roof_update_lift_step + cmp liftPhase3D,2 + jne roof_update_lift_step + cmp playerX,FP_1*236/10 + jb roof_update_lift_step + cmp playerX,FP_1*252/10 + ja roof_update_lift_step + cmp playerY,FP_1*31/10 + ja roof_update_lift_step + invoke Pressed3D,31h + test eax,eax + jz @F + mov roofNight3D,0 +@@: + invoke Pressed3D,32h + test eax,eax + jz roof_update_lift_step + mov roofNight3D,1 +roof_update_lift_step: + mov ebx,deltaTime3D + cmp ebx,100 + jbe @F + mov ebx,100 +@@: + cmp liftPhase3D,1 + je roof_lift_opening + cmp liftPhase3D,2 + je roof_lift_open + cmp liftPhase3D,3 + je roof_lift_closing + cmp liftPhase3D,4 + je roof_lift_ride + ret +roof_lift_opening: + add liftDoor3D,ebx + cmp liftDoor3D,1000 + jb roof_update_done + mov liftDoor3D,1000 + mov liftPhase3D,2 + mov liftAwayTime3D,0 + ret +roof_lift_open: + cmp playerX,22*FP_1 + jb roof_lift_away + cmp playerX,27*FP_1 + ja roof_lift_away + cmp playerY,6*FP_1 + ja roof_lift_away + mov liftAwayTime3D,0 + ret +roof_lift_away: + add liftAwayTime3D,ebx + cmp liftAwayTime3D,2800 + jb roof_update_done + mov liftTrip3D,0 + mov liftPhase3D,3 + ret +roof_lift_closing: + ; An idle close reverses if the player re-enters the doorway. + cmp liftTrip3D,0 + jne roof_lift_close_step + cmp playerX,23*FP_1 + jb roof_lift_close_step + cmp playerX,26*FP_1 + ja roof_lift_close_step + cmp playerY,5*FP_1 + ja roof_lift_close_step + mov liftPhase3D,1 + ret +roof_lift_close_step: + sub liftDoor3D,ebx + ja roof_update_done + mov liftDoor3D,0 + mov liftPhase3D,0 + cmp liftTrip3D,0 + je roof_update_done + mov liftPhase3D,4 + mov liftRideTime3D,0 + ret +roof_lift_ride: + add liftRideTime3D,ebx + cmp liftRideTime3D,3200 + jb roof_update_done + mov eax,liftDestination3D + mov hallLevel3D,eax + mov worldMap3D,OFFSET casinoMap + mov floorTexture3D,100 + test eax,eax + jz @F + mov worldMap3D,OFFSET roofMap3D + mov floorTexture3D,ROOF_DECK + invoke CopyString3D,OFFSET roofArrival3D,OFFSET flashMessage + mov flashTimer,150 +@@: + mov liftTrip3D,0 + mov liftPhase3D,1 + mov liftAwayTime3D,0 + mov interactTargetId,0 + mov interactPromptPtr,0 + call ClearKeys3D +roof_update_done: + ret +UpdateRoof3D ENDP + +CheckRoofInteractions3D PROC uses esi + cmp hallLevel3D,0 + je roof_check_lift + cmp hallLevel3D,HALL_ROOF + jne roof_check_done + mov interactTargetId,0 + mov interactPromptPtr,0 + cmp roofLean3D,0 + je @F + mov interactTargetId,INTERACT_ROOF_RAIL + mov interactPromptPtr,OFFSET roofLeanLeave3D + ret +@@: + cmp roofSeat3D,0 + je @F + mov interactTargetId,INTERACT_ROOF_SEAT + mov interactPromptPtr,OFFSET roofStandPrompt3D + ret +@@: + invoke HallTarget3D,15*FP_1,5*FP_1,INTERACT_BAR,OFFSET roofBarPrompt3D + cmp interactTargetId,INTERACT_BAR + je roof_check_lift + xor esi,esi +roof_check_seat: + invoke HallTarget3D,roofSeatX3D[esi*4],roofSeatY3D[esi*4],INTERACT_ROOF_SEAT,OFFSET roofSeatPrompt3D + cmp interactTargetId,INTERACT_ROOF_SEAT + jne @F + mov roofTargetSeat3D,esi + jmp roof_check_lift +@@: + inc esi + cmp esi,ROOF_SEAT_COUNT + jb roof_check_seat + call CheckRoofRail3D +roof_check_lift: + cmp liftPhase3D,3 + jae roof_check_busy + cmp playerX,FP_1*236/10 + jb roof_check_call + cmp playerX,FP_1*252/10 + ja roof_check_call + cmp playerY,FP_1*31/10 + ja roof_check_call + mov interactTargetId,INTERACT_LIFT_TRAVEL + mov interactPromptPtr,OFFSET roofLiftUp3D + cmp hallLevel3D,HALL_ROOF + jne roof_check_done + mov interactPromptPtr,OFFSET roofLiftDown3D + ret +roof_check_call: + invoke HallTarget3D,FP_1*2555/100,LIFT_FRONT_Y,INTERACT_LIFT_CALL,OFFSET roofLiftCall3D + ret +roof_check_busy: + cmp liftTrip3D,0 + je roof_check_done + mov interactTargetId,0 + mov interactPromptPtr,OFFSET roofLiftBusy3D + cmp liftDestination3D,0 + jne roof_check_done + mov interactPromptPtr,OFFSET roofLiftBusyDown3D +roof_check_done: + ret +CheckRoofInteractions3D ENDP + +TryRoofInteraction3D PROC uses ebx + cmp hallLevel3D,0 + je roof_try_lift + cmp hallLevel3D,HALL_ROOF + jne roof_try_none + cmp interactTargetId,INTERACT_ROOF_RAIL + jne @F + cmp roofLean3D,0 + je roof_try_lean + mov roofLean3D,3 + jmp roof_try_handled +roof_try_lean: + call StartRoofLean3D + jmp roof_try_handled +@@: + cmp interactTargetId,INTERACT_ROOF_SEAT + jne roof_try_lift + cmp roofSeat3D,0 + je roof_try_sit + call StandFromRoofSeat3D + jmp roof_try_handled +roof_try_sit: + mov ebx,roofTargetSeat3D + cmp ebx,ROOF_SEAT_COUNT + jae roof_try_none + mov eax,playerX + mov roofReturnX3D,eax + mov eax,playerY + mov roofReturnY3D,eax + mov eax,playerAngle + mov roofReturnAngle3D,eax + mov eax,roofSeatX3D[ebx*4] + mov playerX,eax + mov eax,roofSeatY3D[ebx*4] + mov playerY,eax + mov playerAngle,384 + cmp roofSeatKind3D[ebx*4],3 + jne @F + mov playerAngle,256 +@@: + inc ebx + mov roofSeat3D,ebx + jmp roof_try_handled +roof_try_lift: + cmp interactTargetId,INTERACT_LIFT_CALL + jne roof_try_travel + cmp liftPhase3D,3 + jae roof_try_none + mov liftPhase3D,1 + mov liftAwayTime3D,0 + jmp roof_try_handled +roof_try_travel: + cmp interactTargetId,INTERACT_LIFT_TRAVEL + jne roof_try_none + cmp liftPhase3D,2 + jne roof_try_none + cmp playerX,FP_1*236/10 + jb roof_try_none + cmp playerX,FP_1*252/10 + ja roof_try_none + cmp playerY,FP_1*31/10 + ja roof_try_none + cmp playerY,FP_1*15/10 + jb roof_try_none + mov liftDestination3D,HALL_ROOF + cmp hallLevel3D,0 + je @F + mov liftDestination3D,0 +@@: + mov liftTrip3D,1 + mov liftPhase3D,3 + mov liftRideTime3D,0 +roof_try_handled: + call ClearKeys3D + mov eax,1 + ret +roof_try_none: + xor eax,eax + ret +TryRoofInteraction3D ENDP + +LiftCanWalk3D PROC px:DWORD,py:DWORD + cmp hallLevel3D,0 + je @F + cmp hallLevel3D,HALL_ROOF + jne roof_lift_walk_clear +@@: + cmp py,FP_1*325/100 + jb roof_lift_walk_inner + cmp py,FP_1*375/100 + ja roof_lift_walk_clear + cmp px,FP_1*2225/100 + jb roof_lift_walk_clear + cmp px,FP_1*2655/100 + ja roof_lift_walk_clear + cmp px,FP_1*236/10 + jb roof_lift_walk_block + cmp px,FP_1*252/10 + ja roof_lift_walk_block +roof_lift_walk_inner: + cmp px,FP_1*231/10 + jb roof_lift_walk_clear + cmp px,FP_1*257/10 + ja roof_lift_walk_clear + cmp py,FP_1*37/10 + ja roof_lift_walk_clear + cmp liftTrip3D,0 + jne roof_lift_walk_block + cmp px,FP_1*236/10 + jb roof_lift_walk_block + cmp px,FP_1*252/10 + ja roof_lift_walk_block + cmp py,FP_1*15/10 + jb roof_lift_walk_block + cmp py,FP_1*33/10 + jb roof_lift_walk_clear + cmp liftDoor3D,950 + jb roof_lift_walk_block +roof_lift_walk_clear: + mov eax,1 + ret +roof_lift_walk_block: + xor eax,eax + ret +LiftCanWalk3D ENDP + +RoofCanWalk3D PROC uses ebx esi px:DWORD,py:DWORD + cmp roofLean3D,0 + jne roof_walk_block + cmp roofSeat3D,0 + jne roof_walk_block + invoke LiftCanWalk3D,px,py + test eax,eax + jz roof_walk_block + cmp px,FP_1*112/10 + jb roof_walk_block + cmp px,FP_1*288/10 + ja roof_walk_block + cmp py,FP_1*12/10 + jb roof_walk_block + cmp py,FP_1*208/10 + ja roof_walk_block + cmp px,FP_1*117/10 + jb roof_walk_furniture + cmp px,FP_1*183/10 + ja roof_walk_furniture + cmp py,FP_1*51/10 + jb roof_walk_block +roof_walk_furniture: + cmp px,FP_1*113/10 + jb roof_walk_posts_start + cmp px,FP_1*1255/100 + ja roof_walk_posts_start + cmp py,FP_1*113/10 + jb roof_walk_posts_start + cmp py,FP_1*167/10 + jbe roof_walk_block +roof_walk_posts_start: + xor esi,esi +roof_walk_post: + mov eax,px + sub eax,roofPostX3D[esi*4] + test eax,eax + jns @F + neg eax +@@: + cmp eax,FP_1/4 + ja roof_walk_post_next + mov eax,py + sub eax,roofPostY3D[esi*4] + test eax,eax + jns @F + neg eax +@@: + cmp eax,FP_1/4 + jbe roof_walk_block +roof_walk_post_next: + inc esi + cmp esi,8 + jb roof_walk_post + xor esi,esi +roof_walk_sofa: + mov eax,px + sub eax,roofSeatX3D[esi*4] + test eax,eax + jns @F + neg eax +@@: + mov ebx,FP_1*17/10 + cmp roofSeatKind3D[esi*4],0 + je @F + mov ebx,FP_1*7/10 + cmp roofSeatKind3D[esi*4],2 + jb @F + mov ebx,FP_1*45/100 +@@: + cmp eax,ebx + ja roof_walk_next + mov eax,py + sub eax,roofSeatY3D[esi*4] + cmp roofSeatKind3D[esi*4],2 + jb roof_walk_low_seat + cmp eax,-FP_1*45/100 + jl roof_walk_next + cmp eax,FP_1*65/100 + jle roof_walk_block + jmp roof_walk_next +roof_walk_low_seat: + cmp eax,-FP_1/2 + jl roof_walk_table + cmp eax,FP_1*12/10 + jle roof_walk_block +roof_walk_table: + mov ebx,FP_1*18/10 + cmp roofSeatKind3D[esi*4],1 + jne @F + mov ebx,FP_1*14/10 +@@: + add eax,ebx + cmp eax,-FP_1*6/10 + jl roof_walk_next + cmp eax,FP_1*6/10 + jg roof_walk_next + mov eax,px + sub eax,roofSeatX3D[esi*4] + test eax,eax + jns @F + neg eax +@@: + cmp eax,FP_1*8/10 + jbe roof_walk_block +roof_walk_next: + inc esi + cmp esi,ROOF_SEAT_COUNT + jb roof_walk_sofa + xor esi,esi +roof_walk_tree: + mov eax,px + sub eax,roofTreeX3D[esi*4] + test eax,eax + jns @F + neg eax +@@: + cmp eax,FP_1*5/10 + ja roof_walk_tree_next + mov eax,py + sub eax,roofTreeY3D[esi*4] + test eax,eax + jns @F + neg eax +@@: + cmp eax,FP_1*5/10 + jbe roof_walk_block +roof_walk_tree_next: + inc esi + cmp esi,ROOF_TREE_COUNT + jb roof_walk_tree + mov eax,1 + ret +roof_walk_block: + xor eax,eax + ret +RoofCanWalk3D ENDP + +RenderLift3D PROC uses ebx esi + local slide:DWORD + call UpdateRoofIndicator3D + ; The cabin is real geometry on both landings, never a screen transition. + invoke DrawCityBoxSides3D,FP_1*2325/100,FP_1*2345/100,FP_1*12/10,LIFT_FRONT_Y,0,FP_1*24/10,ROOF_PLASTER + invoke DrawCityBoxSides3D,FP_1*2535/100,FP_1*2555/100,FP_1*12/10,LIFT_FRONT_Y,0,FP_1*24/10,ROOF_PLASTER + invoke DrawCityBoxSides3D,FP_1*2345/100,FP_1*2535/100,FP_1*12/10,FP_1*13/10,0,FP_1*24/10,ROOF_METAL + invoke DrawCityHorizontal3D,FP_1*2345/100,FP_1*2535/100,FP_1*13/10,LIFT_FRONT_Y,FP_1*24/10,ROOF_PLASTER + invoke DrawCityHorizontal3D,24*FP_1,FP_1*248/10,FP_1*17/10,FP_1*29/10,FP_1*238/100,ROOF_LIGHT + invoke DrawCityHorizontal3D,FP_1*2345/100,FP_1*2535/100,FP_1*13/10,LIFT_FRONT_Y,FP_1/50,ROOF_STONE + invoke DrawCityVertical3D,1,FP_1*134/100,FP_1*236/10,FP_1*252/10,FP_1*85/100,FP_1*9/10,ROOF_BRASS + invoke DrawCityVertical3D,0,FP_1*235/10,FP_1*15/10,3*FP_1,FP_1*85/100,FP_1*9/10,ROOF_BRASS + invoke DrawCityVertical3D,0,FP_1*253/10,FP_1*15/10,3*FP_1,FP_1*85/100,FP_1*9/10,ROOF_BRASS + invoke DrawCityVertical3D,0,FP_1*2533/100,FP_1*20/10,FP_1*24/10,FP_1*9/10,FP_1*15/10,ROOF_BUTTON + invoke DrawCityVertical3D,0,FP_1*2533/100,FP_1*19/10,FP_1*24/10,FP_1*17/10,FP_1*21/10,ROOF_INDICATOR + ; Exterior call panel and a lit lintel. + invoke DrawCityVertical3D,1,FP_1*358/100,FP_1*254/10,FP_1*258/10,FP_1*9/10,FP_1*15/10,ROOF_BUTTON + invoke DrawCityBoxSides3D,FP_1*232/10,FP_1*256/10,FP_1*34/10,FP_1*35/10,FP_1*225/100,FP_1*24/10,ROOF_BRASS + invoke DrawCityVertical3D,1,FP_1*357/100,FP_1*241/10,FP_1*247/10,FP_1*226/100,FP_1*24/10,ROOF_INDICATOR + mov eax,liftDoor3D + imul eax,FP_1*95/100 + xor edx,edx + mov ecx,1000 + div ecx + mov slide,eax + mov ebx,LIFT_CENTRE_X-FP_1*95/100 + sub ebx,eax + mov esi,LIFT_CENTRE_X + sub esi,eax + invoke DrawCityVertical3D,1,LIFT_FRONT_Y,ebx,esi,0,FP_1*225/100,ROOF_DOOR + mov eax,slide + add eax,LIFT_CENTRE_X + mov ebx,eax + add eax,FP_1*95/100 + invoke DrawCityVertical3D,1,LIFT_FRONT_Y,ebx,eax,0,FP_1*225/100,ROOF_DOOR + ; These pockets occlude the translated leaves instead of squeezing their artwork. + invoke DrawCityBoxSides3D,FP_1*224/10,FP_1*2345/100,FP_1*344/100,FP_1*356/100,0,FP_1*24/10,ROOF_PLASTER + invoke DrawCityBoxSides3D,FP_1*2535/100,FP_1*264/10,FP_1*344/100,FP_1*356/100,0,FP_1*24/10,ROOF_PLASTER + ret +RenderLift3D ENDP + +UpdateRoofIndicator3D PROC uses ebx esi edi + lea edi,roofIndicatorPixels3D + mov eax,0FF101C24h + mov ecx,4096 + rep stosd + mov eax,'1' + cmp hallLevel3D,HALL_ROOF + jne @F + mov eax,'R' +@@: + cmp liftPhase3D,4 + jne roof_indicator_glyph + mov eax,liftRideTime3D + shr eax,10 + inc eax + cmp liftDestination3D,0 + jne @F + neg eax + add eax,5 +@@: + add eax,'0' +roof_indicator_glyph: + lea esi,font8x8[eax*8] + xor ebx,ebx +roof_indicator_row: + movzx edx,BYTE PTR [esi+ebx] + xor ecx,ecx +roof_indicator_column: + test edx,80h + jz roof_indicator_next + mov eax,ebx + shl eax,2 + add eax,16 + shl eax,6 + mov edi,ecx + shl edi,2 + add edi,16 + add edi,eax + lea edi,roofIndicatorPixels3D[edi*4] + push ecx + push edx + mov edx,4 +roof_indicator_block_row: + mov eax,0FFFFDEA0h + mov ecx,4 + rep stosd + add edi,(64-4)*4 + dec edx + jnz roof_indicator_block_row + pop edx + pop ecx +roof_indicator_next: + shl edx,1 + inc ecx + cmp ecx,8 + jb roof_indicator_column + inc ebx + cmp ebx,8 + jb roof_indicator_row + ret +UpdateRoofIndicator3D ENDP + +DrawRoofSofa3D PROC uses ebx esi centreX:DWORD,frontY:DWORD + invoke DrawRoofUpholstery3D,centreX,frontY,0 + ret +DrawRoofSofa3D ENDP + +DrawRoofChair3D PROC uses ebx esi centreX:DWORD,frontY:DWORD,kind:DWORD + invoke DrawRoofUpholstery3D,centreX,frontY,kind + ret +DrawRoofChair3D ENDP + +DrawRoofCoffee3D PROC uses ebx centreX:DWORD,centreY:DWORD + local x0:DWORD,x1:DWORD,y0:DWORD,y1:DWORD + mov eax,centreX + sub eax,FP_1*6/10 + mov x0,eax + add eax,FP_1*12/10 + mov x1,eax + mov eax,centreY + sub eax,FP_1*4/10 + mov y0,eax + add eax,FP_1*8/10 + mov y1,eax + invoke DrawCityBoxSides3D,x0,x1,y0,y1,FP_1*4/10,FP_1*45/100,ROOF_STONE + invoke DrawCityHorizontal3D,x0,x1,y0,y1,FP_1*45/100,ROOF_MARBLE + mov eax,centreX + sub eax,FP_1*75/100 + mov ebx,centreX + add ebx,FP_1*75/100 + invoke DrawCityVertical3D,1,centreY,eax,ebx,0,FP_1*9/10,ROOF_TABLE + ret +DrawRoofCoffee3D ENDP + +DrawRoofTree3D PROC uses ebx centreX:DWORD,centreY:DWORD,material:DWORD + local loX:DWORD,hiX:DWORD,loY:DWORD,hiY:DWORD + mov eax,centreX + sub eax,FP_1 + mov loX,eax + add eax,2*FP_1 + mov hiX,eax + mov eax,centreY + sub eax,FP_1 + mov loY,eax + add eax,2*FP_1 + mov hiY,eax + invoke DrawCityVertical3D,1,centreY,loX,hiX,0,FP_1*24/10,material + invoke DrawCityVertical3D,0,centreX,loY,hiY,0,FP_1*24/10,material + ret +DrawRoofTree3D ENDP + +DrawRoofBulb3D PROC uses ebx wx:DWORD,wy:DWORD,wz:DWORD + local endX:DWORD,endY:DWORD,endZ:DWORD + mov eax,wx + add eax,FP_1/12 + mov endX,eax + mov eax,wy + add eax,FP_1/12 + mov endY,eax + mov eax,wz + add eax,FP_1/10 + mov endZ,eax + mov eax,wx + sub eax,FP_1/12 + mov ebx,wx + add ebx,FP_1/12 + mov ecx,wz + add ecx,FP_1/4 + invoke DrawCityVertical3D,1,wy,eax,ebx,wz,ecx,ROOF_LANTERN + ret +DrawRoofBulb3D ENDP + +RenderRoof3D PROC uses ebx esi edi + call UpdateRoofQuality3D + call UpdateRoofLook3D + call UpdateRoofEye3D + ; Replace the prepass walls: roof boundaries are low physical rails, not DDA walls. + cmp roofHDReady3D,1 + jne roof_render_cpu_planes + cmp engineMode,MODE_WALK + jne roof_render_cpu_planes + lea edi,screenBuffer + xor eax,eax + mov ecx,SCREEN_SIZE + rep stosd + jmp roof_render_planes_ready +roof_render_cpu_planes: + call RenderCeilingAndFloor +roof_render_planes_ready: + lea edi,cityDepthBuffer3D + mov eax,ROOF_FAR_DEPTH + mov ecx,SCREEN_SIZE + rep stosd + lea edi,zBuffer + mov ecx,SCREEN_W + rep stosd + lea edi,roofCityMask3D + mov eax,02020202h + mov ecx,SCREEN_SIZE/4 + rep stosd + lea edi,roofGlowMask3D + xor eax,eax + mov ecx,SCREEN_SIZE/4 + rep stosd + call PrepareCityRays3D + call PrepareRoofFloorDepth3D + cmp roofHDReady3D,1 + jne roof_render_software_city + cmp engineMode,MODE_WALK + je roof_render_city_ready +roof_render_software_city: + call PrepareRoofCityAA3D + mov roofCityPass3D,1 + call RenderRoofCityGround3D + call RenderRoofDistrict3D + call RenderRoofLandmarks3D + call RenderRoofCityParks3D + call RenderRoofCityLamps3D + call RenderRoofTraffic3D + call RenderRoofCityBloom3D + call FinishRoofCityAA3D + mov roofCityPass3D,0 + call PrepareCityRays3D +roof_render_city_ready: + lea esi,screenBuffer + lea edi,roofHDBackdrop3D + mov ecx,SCREEN_SIZE + rep movsd + call RenderLift3D + ; Long working bar, lit backbar and a genuine slatted pergola. + invoke DrawCityBoxSides3D,12*FP_1,18*FP_1,4*FP_1,FP_1*48/10,0,FP_1*9/10,ROOF_STONE + invoke DrawCityVertical3D,1,FP_1*481/100,12*FP_1,15*FP_1,0,FP_1*88/100,ROOF_BAR_FRONT + invoke DrawCityVertical3D,1,FP_1*481/100,15*FP_1,18*FP_1,0,FP_1*88/100,ROOF_BAR_FRONT + invoke DrawCityHorizontal3D,12*FP_1,18*FP_1,4*FP_1,5*FP_1,FP_1*9/10,ROOF_MARBLE + invoke DrawCityBoxSides3D,12*FP_1,18*FP_1,FP_1*15/10,FP_1*17/10,0,FP_1*22/10,ROOF_STONE + invoke DrawCityVertical3D,1,FP_1*171/100,12*FP_1,15*FP_1,FP_1*4/10,FP_1*22/10,ROOF_BOTTLES + invoke DrawCityVertical3D,1,FP_1*171/100,15*FP_1,18*FP_1,FP_1*4/10,FP_1*22/10,ROOF_BOTTLES + xor esi,esi +roof_pergola_post: + mov ebx,roofPostX3D[esi*4] + mov ecx,roofPostY3D[esi*4] + mov eax,ebx + add eax,FP_1/8 + mov edx,ecx + add edx,FP_1/8 + invoke DrawCityBoxSides3D,ebx,eax,ecx,edx,0,FP_1*28/10,ROOF_STONE + inc esi + cmp esi,8 + jb roof_pergola_post + xor esi,esi +roof_pergola_slat: + mov eax,esi + imul eax,FP_1*6/10 + add eax,12*FP_1 + mov ebx,eax + add ebx,FP_1/10 + invoke DrawCityHorizontal3D,eax,ebx,FP_1*15/10,FP_1*63/10,FP_1*28/10,ROOF_STONE + inc esi + cmp esi,11 + jb roof_pergola_slat + mov eax,animationClock3D + shr eax,8 + and eax,1 + cmp engineMode,MODE_BAR + jne roof_tender_frame + mov eax,barTime3D + xor edx,edx + mov ecx,375 + div ecx + cmp eax,7 + jbe @F + mov eax,7 +@@: + mov edx,barSelection3D + shl edx,3 + add eax,edx +roof_tender_frame: + mov roofTender3D.frame,eax + invoke DrawCityObject3D,OFFSET roofTender3D,0 + xor esi,esi +roof_draw_sofas: + cmp roofSeatKind3D[esi*4],0 + jne roof_draw_chair + invoke DrawRoofSofa3D,roofSeatX3D[esi*4],roofSeatY3D[esi*4] + jmp roof_draw_seat_coffee +roof_draw_chair: + cmp engineMode,MODE_BAR + jne @F + cmp roofSeatKind3D[esi*4],2 + jae roof_draw_next_seat +@@: + invoke DrawRoofChair3D,roofSeatX3D[esi*4],roofSeatY3D[esi*4],roofSeatKind3D[esi*4] +roof_draw_seat_coffee: + cmp roofSeatKind3D[esi*4],2 + jae roof_draw_next_seat + mov eax,roofSeatY3D[esi*4] + sub eax,FP_1*18/10 + cmp roofSeatKind3D[esi*4],1 + jne @F + add eax,FP_1*4/10 +@@: + invoke DrawRoofCoffee3D,roofSeatX3D[esi*4],eax +roof_draw_next_seat: + inc esi + cmp esi,ROOF_SEAT_COUNT + jb roof_draw_sofas + ; Standing-height counter along the city rail, with three stools and lamps. + invoke DrawCityBoxSides3D,FP_1*115/10,FP_1*122/10,FP_1*115/10,FP_1*165/10,FP_1,FP_1*106/100,ROOF_STONE + invoke DrawCityHorizontal3D,FP_1*115/10,FP_1*122/10,FP_1*115/10,FP_1*165/10,FP_1*106/100,ROOF_MARBLE + xor esi,esi +roof_high_counter_leg: + mov eax,esi + imul eax,FP_1*23/10 + add eax,FP_1*116/10 + mov ebx,eax + add ebx,FP_1/10 + invoke DrawCityBoxSides3D,FP_1*117/10,FP_1*118/10,eax,ebx,0,FP_1,ROOF_METAL + inc esi + cmp esi,3 + jb roof_high_counter_leg + invoke DrawRoofBulb3D,FP_1*1185/100,FP_1*124/10,FP_1*107/100 + invoke DrawRoofBulb3D,FP_1*1185/100,FP_1*146/10,FP_1*107/100 + xor esi,esi +roof_draw_trees: + invoke DrawRoofTree3D,roofTreeX3D[esi*4],roofTreeY3D[esi*4],ROOF_TREE + inc esi + cmp esi,ROOF_TREE_COUNT + jb roof_draw_trees + invoke DrawRoofPlanter3D,12*FP_1,16*FP_1,FP_1*199/10,FP_1*205/10 + invoke DrawRoofPlanter3D,23*FP_1,27*FP_1,FP_1*199/10,FP_1*205/10 + xor esi,esi +roof_rail_post: + imul eax,esi,FP_1*25/10 + add eax,FP_1*112/10 + mov ebx,eax + add ebx,FP_1/14 + invoke DrawCityBoxSides3D,eax,ebx,FP_1*208/10,21*FP_1,0,ROOF_RAIL_HEIGHT,ROOF_METAL + mov eax,esi + imul eax,FP_1*25/10 + add eax,FP_1*112/10 + mov ebx,eax + add ebx,FP_1/14 + invoke DrawCityBoxSides3D,eax,ebx,FP_1,FP_1*12/10,0,ROOF_RAIL_HEIGHT,ROOF_METAL + inc esi + cmp esi,8 + jb roof_rail_post + invoke DrawRoofSolid3D,11*FP_1,29*FP_1,FP_1*2085/100,FP_1*2095/100,FP_1*98/100,ROOF_RAIL_HEIGHT,ROOF_METAL + invoke DrawRoofSolid3D,11*FP_1,29*FP_1,FP_1*105/100,FP_1*115/100,FP_1*98/100,ROOF_RAIL_HEIGHT,ROOF_METAL + invoke DrawRoofSolid3D,FP_1*1105/100,FP_1*1115/100,FP_1,21*FP_1,FP_1*98/100,ROOF_RAIL_HEIGHT,ROOF_METAL + invoke DrawRoofSolid3D,FP_1*2885/100,FP_1*2895/100,FP_1,21*FP_1,FP_1*98/100,ROOF_RAIL_HEIGHT,ROOF_METAL + xor esi,esi +roof_rail_side_post: + imul eax,esi,FP_1*25/10 + add eax,FP_1*12/10 + mov ebx,eax + add ebx,FP_1/14 + invoke DrawCityBoxSides3D,11*FP_1,FP_1*112/10,eax,ebx,0,ROOF_RAIL_HEIGHT,ROOF_METAL + mov eax,esi + imul eax,FP_1*25/10 + add eax,FP_1*12/10 + mov ebx,eax + add ebx,FP_1/14 + invoke DrawCityBoxSides3D,FP_1*288/10,29*FP_1,eax,ebx,0,ROOF_RAIL_HEIGHT,ROOF_METAL + inc esi + cmp esi,8 + jb roof_rail_side_post + ; Two suspended strings of small physical lamps, with edge supports. + xor esi,esi +roof_string_bulb: + imul eax,esi,FP_1*13/10 + add eax,12*FP_1 + invoke DrawRoofBulb3D,eax,7*FP_1,FP_1*235/100 + imul eax,esi,FP_1*13/10 + add eax,12*FP_1 + invoke DrawRoofBulb3D,eax,15*FP_1,FP_1*235/100 + inc esi + cmp esi,13 + jb roof_string_bulb + invoke DrawCityVertical3D,1,7*FP_1,12*FP_1,28*FP_1,FP_1*26/10,FP_1*261/100,ROOF_METAL + invoke DrawCityVertical3D,1,15*FP_1,12*FP_1,28*FP_1,FP_1*26/10,FP_1*261/100,ROOF_METAL + xor esi,esi +roof_guest_draw: + mov eax,roofGuestX3D[esi*4] + mov roofActor3D.posX,eax + mov eax,roofGuestY3D[esi*4] + mov roofActor3D.posY,eax + mov eax,roofGuestFrame3D[esi*4] + mov roofActor3D.frame,eax + mov roofActor3D.asset,OFFSET npcGuestArt3D + cmp roofGuestMoving3D[esi*4],0 + je roof_guest_idle + cmp npcWalkArt3D.pixels,0 + je roof_guest_idle + xor edx,edx + mov ecx,6 + div ecx + shl eax,3 + mov ebx,eax + mov eax,roofGuestDistance3D[esi*4] + xor edx,edx + mov ecx,FP_1*3/32 + div ecx + and eax,7 + add eax,ebx + mov roofActor3D.frame,eax + mov roofActor3D.asset,OFFSET npcWalkArt3D + mov eax,roofGuestDir3D[esi*4] + mov ecx,playerDirY + neg ecx + cmp eax,0 + je roof_guest_facing + neg ecx + cmp eax,1 + je roof_guest_facing + mov ecx,playerDirX + cmp eax,2 + je roof_guest_facing + neg ecx +roof_guest_facing: + test ecx,ecx + jns roof_guest_draw_sprite + mov roofActor3D.asset,OFFSET npcWalkLeftArt3D + jmp roof_guest_draw_sprite +roof_guest_idle: + mov eax,roofGuestClock3D + shr eax,10 + add eax,esi + and eax,1 + add roofActor3D.frame,eax +roof_guest_draw_sprite: + invoke DrawCityObject3D,OFFSET roofActor3D,0 + inc esi + cmp esi,ROOF_GUEST_COUNT + jb roof_guest_draw + call ShadeRoofNight3D + call RenderRoofLean3D + call RenderRoofQualityToast3D + ret +RenderRoof3D ENDP +include src_graphics\rooftop_details.inc +include src_graphics\rooftop_atmosphere.inc +include src_graphics\rooftop_surfaces.inc +include src_graphics\rooftop_city_life.inc +include src_graphics\rooftop_gl.inc +include src_graphics\rooftop_quality.inc diff --git a/src_graphics/rooftop_atmosphere.inc b/src_graphics/rooftop_atmosphere.inc new file mode 100644 index 0000000..56e7536 --- /dev/null +++ b/src_graphics/rooftop_atmosphere.inc @@ -0,0 +1,382 @@ +; Directional dusk lighting and aerial perspective are applied to actual geometry. +ShadeRoofCityPixel3D PROC uses ebx esi edi color:DWORD,depth:DWORD,light:DWORD,material:DWORD + local fog:DWORD,inverseFog:DWORD,red:DWORD,green:DWORD,blue:DWORD,level:DWORD + local targetR:DWORD,targetG:DWORD,targetB:DWORD + mov roofPixelGlow3D,0 + mov eax,depth + sar eax,16 + test eax,eax + jns @F + xor eax,eax +@@: + cmp eax,1023 + jbe @F + mov eax,1023 +@@: + movzx eax,roofFog3D[eax] + mov fog,eax + neg eax + add eax,256 + mov inverseFog,eax + mov eax,color + and eax,255 + mov blue,eax + mov eax,color + shr eax,8 + and eax,255 + mov green,eax + mov eax,color + shr eax,16 + and eax,255 + mov red,eax + mov targetR,171 + mov targetG,157 + mov targetB,177 + mov eax,light + mov level,eax + cmp roofNight3D,0 + je roof_lighting_day + mov targetR,13 + mov targetG,24 + mov targetB,44 + mov level,72 + cmp material,ROOF_CITY_ROAD + jne @F + mov level,256 +@@: + cmp material,ROOF_LIGHT + jne @F + mov level,256 + mov roofPixelGlow3D,96 +@@: + cmp roofLandmarkLight3D,0 + je @F + mov level,148 +@@: + cmp material,ROOF_BRASS + jne @F + mov level,228 + cmp roofLandmarkLight3D,0 + je @F + mov roofPixelGlow3D,12 +@@: + ; Emission is tagged in the actual window pixels, never inferred from gold trim. + cmp material,ROOF_FACADE + jb roof_lighting_apply + cmp material,ROOF_FACADE+7 + jbe roof_lighting_window + cmp material,ROOF_MODERN_FACADE + jb roof_lighting_apply + cmp material,ROOF_MODERN_FACADE+7 + ja roof_lighting_apply +roof_lighting_window: + mov eax,color + shr eax,24 + cmp eax,255 + jne roof_lighting_apply + mov level,310 + mov roofPixelGlow3D,64 + shr fog,1 + mov eax,fog + neg eax + add eax,256 + mov inverseFog,eax + jmp roof_lighting_apply +roof_lighting_day: + cmp material,ROOF_CITY_ROOF + jne @F + imul eax,red,180 + shr eax,8 + mov red,eax + imul eax,green,220 + shr eax,8 + mov green,eax +@@: + mov eax,red + imul eax,263 + shr eax,8 + cmp eax,255 + jbe @F + mov eax,255 +@@: + mov red,eax + imul eax,blue,235 + shr eax,8 + mov blue,eax +roof_lighting_apply: + mov eax,red + imul eax,level + shr eax,8 + cmp eax,255 + jbe @F + mov eax,255 +@@: + imul eax,inverseFog + mov ecx,targetR + imul ecx,fog + add eax,ecx + shr eax,8 + mov ebx,eax + shl ebx,16 + mov eax,green + imul eax,level + shr eax,8 + cmp eax,255 + jbe @F + mov eax,255 +@@: + imul eax,inverseFog + mov ecx,targetG + imul ecx,fog + add eax,ecx + shr eax,8 + shl eax,8 + or ebx,eax + mov eax,blue + imul eax,level + shr eax,8 + cmp eax,255 + jbe @F + mov eax,255 +@@: + imul eax,inverseFog + mov ecx,targetB + imul ecx,fog + add eax,ecx + shr eax,8 + or eax,ebx + ret +ShadeRoofCityPixel3D ENDP + +SampleRoofGroundLight3D PROC uses ebx esi edi color:DWORD,wx:DWORD,wy:DWORD + mov eax,color + cmp roofLightingArt3D.pixels,0 + je roof_ground_light_done + mov eax,wx + sar eax,17 + add eax,256 + cmp eax,511 + ja roof_ground_light_original + mov ebx,eax + mov eax,wy + sar eax,17 + add eax,256 + cmp eax,511 + ja roof_ground_light_original + shl eax,9 + add eax,ebx + mov esi,roofLightingArt3D.pixels + cmp roofNight3D,0 + je @F + add esi,512*512*4 +@@: + mov edx,[esi+eax*4] + mov edi,edx + and edx,255 + mov eax,color + and eax,255 + imul eax,edx + shr eax,8 + mov ebx,eax + mov edx,edi + shr edx,8 + and edx,255 + mov eax,color + shr eax,8 + and eax,255 + imul eax,edx + and eax,0FF00h + or ebx,eax + mov edx,edi + shr edx,16 + and edx,255 + mov eax,color + shr eax,16 + and eax,255 + imul eax,edx + and eax,0FF00h + shl eax,8 + or eax,ebx + ret +roof_ground_light_original: + mov eax,color +roof_ground_light_done: + ret +SampleRoofGroundLight3D ENDP + +PrepareRoofCityAA3D PROC uses ebx esi edi + mov roofAAStill3D,0 + cmp roofAAValid3D,0 + je roof_aa_moving + mov eax,playerX + cmp eax,roofAAX3D + jne roof_aa_moving + mov eax,playerY + cmp eax,roofAAY3D + jne roof_aa_moving + mov eax,playerAngle + cmp eax,roofAAAngle3D + jne roof_aa_moving + mov eax,viewEye3D + cmp eax,roofAAEye3D + jne roof_aa_moving + mov eax,viewHorizon3D + cmp eax,roofAAHorizon3D + jne roof_aa_moving + mov eax,roofNight3D + cmp eax,roofAATheme3D + jne roof_aa_moving + mov roofAAStill3D,1 + inc roofAAFrame3D + and roofAAFrame3D,3 + jmp roof_aa_save_view +roof_aa_moving: + mov roofAAFrame3D,0 +roof_aa_save_view: + mov eax,playerX + mov roofAAX3D,eax + mov eax,playerY + mov roofAAY3D,eax + mov eax,playerAngle + mov roofAAAngle3D,eax + mov eax,viewEye3D + mov roofAAEye3D,eax + mov eax,viewHorizon3D + mov roofAAHorizon3D,eax + mov eax,roofNight3D + mov roofAATheme3D,eax + mov eax,roofAAFrame3D + shl eax,1 + sub eax,3 + mov ebx,eax + mov eax,playerPlaneX + imul ebx + cdq + mov ecx,SCREEN_W*4 + idiv ecx + mov esi,eax + mov eax,playerPlaneY + imul ebx + cdq + idiv ecx + mov edi,eax + xor ebx,ebx +roof_aa_shift_ray: + add cityRayX3D[ebx*4],esi + add cityRayY3D[ebx*4],edi + inc ebx + cmp ebx,SCREEN_W + jb roof_aa_shift_ray + ret +PrepareRoofCityAA3D ENDP + +FinishRoofCityAA3D PROC uses ebx esi edi + xor edi,edi +roof_aa_pixel: + mov eax,screenBuffer[edi*4] + cmp roofAAStill3D,0 + je roof_aa_store + cmp roofCityMask3D[edi],1 + jne roof_aa_store + and eax,00FCFCFCh + shr eax,2 + mov edx,roofAAHistory3D[edi*4] + and edx,00FCFCFCh + shr edx,2 + imul edx,3 + add eax,edx + mov screenBuffer[edi*4],eax +roof_aa_store: + mov roofAAHistory3D[edi*4],eax + inc edi + cmp edi,SCREEN_SIZE + jb roof_aa_pixel + mov roofAAValid3D,1 + ret +FinishRoofCityAA3D ENDP + +RenderRoofCityBloom3D PROC uses ebx esi edi + local pixel:DWORD,color:DWORD,amount:DWORD,rowOffset:DWORD,columnOffset:DWORD + local target:DWORD,red:DWORD,green:DWORD,blue:DWORD + cmp roofNight3D,0 + je roof_bloom_done + mov pixel,SCREEN_W+1 +roof_bloom_source: + mov esi,pixel + movzx eax,roofGlowMask3D[esi] + test eax,eax + jz roof_bloom_next_source + shr eax,2 + mov amount,eax + mov eax,screenBuffer[esi*4] + mov color,eax + mov rowOffset,-1 +roof_bloom_row: + mov columnOffset,-1 +roof_bloom_column: + mov eax,rowOffset + imul eax,SCREEN_W + add eax,columnOffset + add eax,pixel + cmp eax,SCREEN_SIZE + jae roof_bloom_next_column + mov target,eax + lea edi,screenBuffer[eax*4] + mov ebx,[edi] + mov eax,color + and eax,255 + imul eax,amount + shr eax,8 + movzx ecx,bl + add eax,ecx + cmp eax,255 + jbe @F + mov eax,255 +@@: + mov blue,eax + mov eax,color + shr eax,8 + and eax,255 + imul eax,amount + shr eax,8 + mov ecx,ebx + shr ecx,8 + and ecx,255 + add eax,ecx + cmp eax,255 + jbe @F + mov eax,255 +@@: + mov green,eax + mov eax,color + shr eax,16 + and eax,255 + imul eax,amount + shr eax,8 + shr ebx,16 + and ebx,255 + add eax,ebx + cmp eax,255 + jbe @F + mov eax,255 +@@: + shl eax,16 + mov ecx,green + shl ecx,8 + or eax,ecx + or eax,blue + mov [edi],eax +roof_bloom_next_column: + inc columnOffset + cmp columnOffset,1 + jle roof_bloom_column + inc rowOffset + cmp rowOffset,1 + jle roof_bloom_row +roof_bloom_next_source: + inc pixel + cmp pixel,SCREEN_SIZE-SCREEN_W-1 + jb roof_bloom_source +roof_bloom_done: + ret +RenderRoofCityBloom3D ENDP diff --git a/src_graphics/rooftop_city_life.inc b/src_graphics/rooftop_city_life.inc new file mode 100644 index 0000000..87d3d58 --- /dev/null +++ b/src_graphics/rooftop_city_life.inc @@ -0,0 +1,156 @@ +RenderRoofCityLamps3D PROC uses ebx esi edi + local px:DWORD,py:DWORD,x1:DWORD,y1:DWORD + xor esi,esi +roof_city_lamp: + mov eax,roofCityLamps3D[esi*8] + mov px,eax + mov edx,roofCityLamps3D[esi*8+4] + mov py,edx + invoke ProjectCityPoint3D,eax,edx + cmp edx,FP_1 + jle roof_city_lamp_next + cmp edx,180*FP_1 + ja roof_city_lamp_next + cmp eax,-8 + jl roof_city_lamp_next + cmp eax,SCREEN_W+8 + jg roof_city_lamp_next + mov eax,px + add eax,FP_1/12 + mov x1,eax + mov eax,py + add eax,FP_1/12 + mov y1,eax + invoke DrawRoofSolid3D,px,x1,py,y1,ROOF_CITY_STREET_Z,ROOF_CITY_STREET_Z+5*FP_1,ROOF_METAL + mov eax,px + sub eax,FP_1/5 + mov edx,px + add edx,FP_1/5 + mov ecx,py + sub ecx,FP_1/5 + mov edi,py + add edi,FP_1/5 + invoke DrawRoofSolid3D,eax,edx,ecx,edi,ROOF_CITY_STREET_Z+FP_1*49/10,ROOF_CITY_STREET_Z+FP_1*51/10,ROOF_LIGHT +roof_city_lamp_next: + inc esi + cmp esi,ROOF_CITY_LAMP_COUNT + jb roof_city_lamp + ret +RenderRoofCityLamps3D ENDP + +DrawRoofCar3D PROC uses ebx esi edi centreX:DWORD,centreY:DWORD,axis:DWORD,style:DWORD + local x0:DWORD,x1:DWORD,y0:DWORD,y1:DWORD,bodyMat:DWORD,cabinX0:DWORD,cabinX1:DWORD + local cabinY0:DWORD,cabinY1:DWORD + invoke ProjectCityPoint3D,centreX,centreY + cmp edx,FP_1 + jle roof_car_done + cmp eax,-30 + jl roof_car_done + cmp eax,SCREEN_W+30 + jg roof_car_done + mov ebx,FP_1*19/10 + mov ecx,FP_1*8/10 + cmp axis,0 + je @F + xchg ebx,ecx +@@: + mov eax,centreX + sub eax,ebx + mov x0,eax + mov eax,centreX + add eax,ebx + mov x1,eax + mov eax,centreY + sub eax,ecx + mov y0,eax + mov eax,centreY + add eax,ecx + mov y1,eax + mov bodyMat,ROOF_CITY_ROOF + test style,1 + jz @F + mov bodyMat,ROOF_BRASS +@@: + invoke DrawRoofSolid3D,x0,x1,y0,y1,ROOF_CITY_STREET_Z+FP_1/4,ROOF_CITY_STREET_Z+FP_1*85/100,bodyMat + mov ebx,FP_1*9/10 + mov ecx,FP_1*65/100 + cmp axis,0 + je @F + xchg ebx,ecx +@@: + mov eax,centreX + sub eax,ebx + mov cabinX0,eax + mov eax,centreX + add eax,ebx + mov cabinX1,eax + mov eax,centreY + sub eax,ecx + mov cabinY0,eax + mov eax,centreY + add eax,ecx + mov cabinY1,eax + invoke DrawRoofSolid3D,cabinX0,cabinX1,cabinY0,cabinY1,ROOF_CITY_STREET_Z+FP_1*85/100,ROOF_CITY_STREET_Z+FP_1*135/100,ROOF_MODERN_FACADE+1 +roof_car_done: + ret +DrawRoofCar3D ENDP + +RenderRoofTraffic3D PROC uses ebx esi edi + local px:DWORD,py:DWORD,material:DWORD + xor esi,esi +roof_city_car: + mov eax,esi + imul eax,17000 + add eax,animationClock3D + mov ecx,393 + mul ecx + mov ecx,340*FP_1 + div ecx + sub edx,160*FP_1 + mov px,edx + mov py,-FP_1*59/10 + test esi,1 + jz @F + neg edx + mov px,edx + mov py,-FP_1*81/10 +@@: + cmp esi,4 + jb roof_city_car_x + mov eax,px + mov py,eax + mov px,FP_1*391/10 + test esi,1 + jz @F + mov px,FP_1*369/10 +@@: + invoke DrawRoofCar3D,px,py,1,esi + jmp roof_city_car_next +roof_city_car_x: + invoke DrawRoofCar3D,px,py,0,esi +roof_city_car_next: + inc esi + cmp esi,8 + jb roof_city_car + xor esi,esi +roof_city_boat: + imul eax,esi,47000 + add eax,animationClock3D + mov ecx,98 + mul ecx + mov ecx,340*FP_1 + div ecx + sub edx,160*FP_1 + mov px,edx + mov py,68*FP_1 + invoke DrawRoofEllipsoid3D,px,py,ROOF_CITY_STREET_Z-FP_1*16/10,3*FP_1,FP_1,FP_1/2,ROOF_CITY_ROOF + mov eax,px + sub eax,FP_1 + mov ebx,px + add ebx,FP_1 + invoke DrawRoofSolid3D,eax,ebx,FP_1*674/10,FP_1*686/10,ROOF_CITY_STREET_Z-FP_1*14/10,ROOF_CITY_STREET_Z-FP_1*7/10,ROOF_PLASTER + inc esi + cmp esi,3 + jb roof_city_boat + ret +RenderRoofTraffic3D ENDP diff --git a/src_graphics/rooftop_data.inc b/src_graphics/rooftop_data.inc new file mode 100644 index 0000000..ece6117 --- /dev/null +++ b/src_graphics/rooftop_data.inc @@ -0,0 +1,202 @@ +; Native walkable rooftop and elevator share the existing hall engine. +HALL_ROOF equ 4 +INTERACT_LIFT_CALL equ 48 +INTERACT_LIFT_TRAVEL equ 49 +INTERACT_ROOF_SEAT equ 50 +INTERACT_ROOF_RAIL equ 51 +ROOF_STANDING_EYE equ FP_1*165/100 +ROOF_SEATED_EYE equ FP_1*118/100 +ROOF_RAIL_HEIGHT equ FP_1*102/100 +ROOF_FAR_DEPTH equ FP_1*1024 +ROOF_FACADE equ 186 +ROOF_FOLIAGE equ 194 +ROOF_EARTH equ 195 +ROOF_CITY_ROOF equ 196 +ROOF_CITY_ROAD equ 197 +ROOF_CITY_WATER equ 198 +ROOF_MODERN_FACADE equ 199 +ROOF_CITY_LAWN equ 207 +ROOF_LAST_MATERIAL equ 207 +ROOF_CITY_STREET_Z equ -60*FP_1 +ROOF_DECK equ 161 +ROOF_STONE equ 162 +ROOF_TEAL equ 163 +ROOF_IVORY equ 164 +ROOF_METAL equ 165 +ROOF_BRASS equ 166 +ROOF_MARBLE equ 167 +ROOF_PLASTER equ 168 +ROOF_DOOR equ 169 +ROOF_BUTTON equ 170 +ROOF_BAR_FRONT equ 171 +ROOF_BOTTLES equ 172 +ROOF_TREE equ 173 +ROOF_GRASS equ 174 +ROOF_LANTERN equ 175 +ROOF_TABLE equ 176 +ROOF_INDICATOR equ 177 +ROOF_LIGHT equ 178 +ROOF_SOFA_FRONT equ 179 +ROOF_SOFA_SIDE equ 180 +ROOF_SOFA_BACK equ 181 +ROOF_CHAIR_FRONT equ 182 +ROOF_CHAIR_SIDE equ 183 +ROOF_CHAIR_BACK equ 184 +ROOF_BAR_STOOL equ 185 +ROOF_SEAT_COUNT equ 13 +ROOF_TREE_COUNT equ 8 +ROOF_GUEST_COUNT equ 8 +LIFT_CENTRE_X equ FP_1*244/10 +LIFT_FRONT_Y equ FP_1*345/100 +InitRoof3D PROTO +LoadRoofAssets3D PROTO +FreeRoofAssets3D PROTO +ResolveRoofSurface3D PROTO :DWORD +DrawRoofSky3D PROTO +RenderRoof3D PROTO +RenderLift3D PROTO +UpdateRoof3D PROTO +CheckRoofInteractions3D PROTO +TryRoofInteraction3D PROTO +LiftCanWalk3D PROTO :DWORD,:DWORD +RoofCanWalk3D PROTO :DWORD,:DWORD +UpdateRoofEye3D PROTO +UpdateRoofLook3D PROTO +StandFromRoofSeat3D PROTO +DrawRoofSofa3D PROTO :DWORD,:DWORD +DrawRoofChair3D PROTO :DWORD,:DWORD,:DWORD +DrawRoofCoffee3D PROTO :DWORD,:DWORD +DrawRoofTree3D PROTO :DWORD,:DWORD,:DWORD +DrawRoofBulb3D PROTO :DWORD,:DWORD,:DWORD +UpdateRoofIndicator3D PROTO +DrawRoofSolid3D PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD +DrawRoofUpholstery3D PROTO :DWORD,:DWORD,:DWORD +DrawRoofPlanter3D PROTO :DWORD,:DWORD,:DWORD,:DWORD +RenderRoofDistrict3D PROTO +UpdateRoofGuests3D PROTO :DWORD +CheckRoofRail3D PROTO +StartRoofLean3D PROTO +RenderRoofLean3D PROTO +DrawRoofHandsPlane3D PROTO :DWORD,:DWORD,:DWORD +ShadeRoofNight3D PROTO +PrepareRoofFloorDepth3D PROTO +DrawRoofEllipsoid3D PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD +DrawRoofPadding3D PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD +RenderRoofCityGround3D PROTO +RenderRoofLandmarks3D PROTO +RenderRoofCityParks3D PROTO +ShadeRoofCityPixel3D PROTO :DWORD,:DWORD,:DWORD,:DWORD +DrawRoofSlope3D PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD +DrawRoofPyramid3D PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD +SampleRoofGroundLight3D PROTO :DWORD,:DWORD,:DWORD +RenderRoofCityLamps3D PROTO +RenderRoofTraffic3D PROTO +DrawRoofCar3D PROTO :DWORD,:DWORD,:DWORD,:DWORD +PrepareRoofCityAA3D PROTO +FinishRoofCityAA3D PROTO +RenderRoofCityBloom3D PROTO +include src_graphics\rooftop_district_data.inc +include src_graphics\rooftop_map.inc +include src_graphics\rooftop_gl_data.inc +.data +roofSkyArt3D AnimPack3D <> +roofMaterialArt3D AnimPack3D <> +roofDetailArt3D AnimPack3D <> +roofCityArt3D AnimPack3D <> +roofCityNightArt3D AnimPack3D <> +roofHandsArt3D AnimPack3D <> +roofNightArt3D AnimPack3D <> +roofLightingArt3D AnimPack3D <> +roofLightingPath3D db "assets\animations\rooftop\lighting-v1.bin",0 +roofCityPath3D db "assets\animations\rooftop\city-v1.bin",0 +roofCityNightPath3D db "assets\animations\rooftop\city-night-v1.bin",0 +roofHandsPath3D db "assets\animations\rooftop\rail-hands-v1.bin",0 +roofNightPath3D db "assets\animations\rooftop\night-v1.bin",0 +roofNight3D dd 0 +roofCityPass3D dd 0 +roofLandmarkLight3D dd 0 +roofPixelGlow3D dd 0 +roofAAValid3D dd 0 +roofAAStill3D dd 0 +roofAAFrame3D dd 0 +roofAAX3D dd 0 +roofAAY3D dd 0 +roofAAAngle3D dd 0 +roofAAEye3D dd 0 +roofAAHorizon3D dd 0 +roofAATheme3D dd 0 +roofSlopeMask3D dd 0 +roofSlopeCentreX3D dd 0 +roofSlopeCentreY3D dd 0 +roofSlopeHalfX3D dd 0 +roofSlopeHalfY3D dd 0 +roofLean3D dd 0 +roofLeanTime3D dd 0 +roofLeanAngle3D dd 0 +roofLeanSide3D dd 0 +roofHandsContactX3D dd 320 +roofHandsWidth3D dd 640 +roofGuestClock3D dd 0 +roofGroundRoadPixels3D dd 0 +roofGroundWaterPixels3D dd 0 +roofGuestStep3D dd ROOF_GUEST_COUNT dup(0) +roofGuestWait3D dd 1200,2400,3600,1800,3000,4200,2000,3300 +roofGuestMoving3D dd ROOF_GUEST_COUNT dup(0) +roofGuestDistance3D dd ROOF_GUEST_COUNT dup(0) +roofGuestDir3D dd ROOF_GUEST_COUNT dup(0) +roofFloatFP3D REAL4 65536.0 +roofFloatHalfTex3D REAL4 127.5 +roofFloatShade3D REAL4 44.0 +roofFloatAmbient3D REAL4 205.0 +roofFloat4803D REAL4 480.0 +roofSkyPath3D db "assets\animations\rooftop\panorama-v1.bin",0 +roofMaterialPath3D db "assets\animations\rooftop\materials-v1.bin",0 +roofDetailPath3D db "assets\animations\rooftop\details-v1.bin",0 +liftPhase3D dd 0 +liftDoor3D dd 0 +liftTrip3D dd 0 +liftDestination3D dd 0 +liftRideTime3D dd 0 +liftAwayTime3D dd 0 +roofUpdateClock3D dd -1 +roofIndicatorPixel3D dd 0FFFFE9BDh +roofSeat3D dd 0 +roofReturnX3D dd 0 +roofReturnY3D dd 0 +roofReturnAngle3D dd 0 +roofSeatX3D dd FP_1*145/10,FP_1*245/10,FP_1*195/10,FP_1*177/10,FP_1*206/10,FP_1*145/10,FP_1*245/10 + dd 13*FP_1,15*FP_1,17*FP_1,13*FP_1,13*FP_1,13*FP_1 +roofSeatY3D dd FP_1*95/10,FP_1*95/10,18*FP_1,FP_1*95/10,FP_1*95/10,15*FP_1,15*FP_1 + dd FP_1*61/10,FP_1*61/10,FP_1*61/10,FP_1*124/10,FP_1*139/10,FP_1*154/10 +roofSeatKind3D dd 0,0,0,1,1,1,1,2,2,2,3,3,3 +roofTargetSeat3D dd 0 +roofTreeX3D dd 12*FP_1,28*FP_1,12*FP_1,28*FP_1,FP_1*194/10,FP_1*215/10,FP_1*175/10,FP_1*278/10 +roofTreeY3D dd FP_1*68/10,8*FP_1,FP_1*189/10,16*FP_1,FP_1*134/10,FP_1*195/10,FP_1*195/10,FP_1*45/10 +roofPostX3D dd 12*FP_1,18*FP_1,12*FP_1,18*FP_1,FP_1*117/10,FP_1*283/10,FP_1*117/10,FP_1*283/10 +roofPostY3D dd FP_1*15/10,FP_1*15/10,FP_1*62/10,FP_1*62/10,7*FP_1,7*FP_1,15*FP_1,15*FP_1 +roofGuestX3D dd FP_1*188/10,FP_1*165/10,FP_1*265/10,FP_1*163/10,FP_1*221/10,FP_1*263/10,FP_1*155/10,FP_1*205/10 +roofGuestY3D dd FP_1*65/10,FP_1*75/10,13*FP_1,FP_1*119/10,FP_1*122/10,FP_1*188/10,FP_1*188/10,FP_1*16/10+15*FP_1 +roofGuestFrame3D dd 0,24,12,6,18,30,0,24 +roofActor3D CityProp3D <0,0,FP_1*75/100,FP_1*175/100,0,OFFSET npcGuestArt3D> +roofTender3D CityProp3D <15*FP_1,FP_1*34/10,FP_1,FP_1,0,OFFSET hallBartender3D,FP_1*9/20> +roofBarName3D db 203,192,211,205,198,45,193,192,208,32,34,194,219,209,206,210,192,34,0 +roofLiftCall3D db 69,58,32,194,219,199,194,192,210,220,32,203,200,212,210,0 +roofLiftUp3D db 49,58,32,199,192,202,192,210,32,47,32,50,58,32,205,206,215,220,32,47,32,69,58,32,197,213,192,210,220,0 +roofLeanPrompt3D db 69,58,32,206,193,203,206,202,206,210,200,210,220,209,223,0 +roofLeanLeave3D db 69,58,32,206,210,206,201,210,200,0 +roofSunsetName3D db 199,192,202,192,210,0 +roofNightName3D db 205,206,215,220,0 +roofLiftDown3D db 69,58,32,194,32,195,203,192,194,205,219,201,32,199,192,203,0 +roofLiftBusy3D db 203,200,212,210,32,207,206,196,205,200,204,192,197,210,209,223,0 +roofLiftBusyDown3D db 203,200,212,210,32,209,207,211,209,202,192,197,210,209,223,0 +roofSeatPrompt3D db 69,58,32,207,208,200,209,197,209,210,220,0 +roofStandPrompt3D db 69,58,32,194,209,210,192,210,220,0 +roofBarPrompt3D db 69,58,32,203,192,211,205,198,45,193,192,208,32,34,194,219,209,206,210,192,34,0 +roofArrival3D db 202,208,219,216,192,32,47,32,210,197,208,208,192,209,192,32,34,194,219,209,206,210,192,34,0 +.data? +roofIndicatorPixels3D dd 4096 dup(?) +roofCityMask3D db SCREEN_SIZE dup(?) +roofGlowMask3D db SCREEN_SIZE dup(?) +roofSlopeRay3D dd SCREEN_W dup(?) +roofAAHistory3D dd SCREEN_SIZE dup(?) +.code diff --git a/src_graphics/rooftop_details.inc b/src_graphics/rooftop_details.inc new file mode 100644 index 0000000..cfc676d --- /dev/null +++ b/src_graphics/rooftop_details.inc @@ -0,0 +1,1496 @@ +; Native ray/ellipsoid intersection supplies rounded cushions and copper domes. +DrawRoofEllipsoid3D PROC uses ebx esi edi centreX:DWORD,centreY:DWORD,centreZ:DWORD,radiusX:DWORD,radiusY:DWORD,radiusZ:DWORD,material:DWORD + local leftPx:DWORD,rightPx:DWORD,topPx:DWORD,bottomPx:DWORD,column:DWORD,row:DWORD + local centrePx:DWORD,centreDepth:DWORD,pixelDepth:DWORD,temp:DWORD,texU:DWORD,texV:DWORD + local texture:DWORD,texWidth:DWORD,texHeight:DWORD,shade:DWORD,color:DWORD + local ox:REAL4,oy:REAL4,oz:REAL4,rayX:REAL4,rayY:REAL4,rayZ:REAL4 + local quadraticC:REAL4,quadraticA:REAL4,quadraticB:REAL4,discriminant:REAL4,hitTime:REAL4 + invoke ResolveCitySurface3D,material + test eax,eax + jz roof_round_done + mov texture,eax + mov texWidth,ecx + mov texHeight,edx + invoke ProjectCityPoint3D,centreX,centreY + cmp edx,FP_1/8 + jle roof_round_done + mov centrePx,eax + mov centreDepth,edx + mov eax,radiusX + add eax,radiusY + imul eax,SCREEN_H + cdq + idiv centreDepth + mov ecx,centrePx + sub ecx,eax + mov leftPx,ecx + add eax,centrePx + mov rightPx,eax + mov eax,viewEye3D + sub eax,centreZ + sub eax,radiusZ + imul eax,SCREEN_H + cdq + idiv centreDepth + add eax,viewHorizon3D + sub eax,4 + mov topPx,eax + mov eax,viewEye3D + sub eax,centreZ + add eax,radiusZ + imul eax,SCREEN_H + cdq + idiv centreDepth + add eax,viewHorizon3D + add eax,4 + mov bottomPx,eax + cmp leftPx,0 + jge @F + mov leftPx,0 +@@: + cmp rightPx,SCREEN_W-1 + jle @F + mov rightPx,SCREEN_W-1 +@@: + cmp topPx,0 + jge @F + mov topPx,0 +@@: + cmp bottomPx,SCREEN_H-1 + jle @F + mov bottomPx,SCREEN_H-1 +@@: + mov eax,leftPx + cmp eax,rightPx + jge roof_round_done + mov eax,topPx + cmp eax,bottomPx + jge roof_round_done + mov eax,playerX + sub eax,centreX + mov temp,eax + fild temp + fidiv radiusX + fstp ox + mov eax,playerY + sub eax,centreY + mov temp,eax + fild temp + fidiv radiusY + fstp oy + mov eax,viewEye3D + sub eax,centreZ + mov temp,eax + fild temp + fidiv radiusZ + fstp oz + fld ox + fmul st,st + fld oy + fmul st,st + faddp st(1),st + fld oz + fmul st,st + faddp st(1),st + fld1 + fsubp st(1),st + fstp quadraticC + mov eax,topPx + mov row,eax +roof_round_row: + mov eax,viewHorizon3D + sub eax,row + mov temp,eax + fild temp + fmul roofFloatFP3D + fdiv roofFloat4803D + fidiv radiusZ + fstp rayZ + mov eax,row + imul eax,SCREEN_W*4 + mov ebx,leftPx + lea edi,[eax+ebx*4] + mov column,ebx +roof_round_pixel: + mov ebx,column + fild cityRayX3D[ebx*4] + fidiv radiusX + fstp rayX + fild cityRayY3D[ebx*4] + fidiv radiusY + fstp rayY + fld rayX + fmul st,st + fld rayY + fmul st,st + faddp st(1),st + fld rayZ + fmul st,st + faddp st(1),st + fstp quadraticA + fld rayX + fmul ox + fld rayY + fmul oy + faddp st(1),st + fld rayZ + fmul oz + faddp st(1),st + fstp quadraticB + fld quadraticB + fmul st,st + fld quadraticA + fmul quadraticC + fsubp st(1),st + fst discriminant + ftst + fnstsw ax + sahf + fstp st(0) + jb roof_round_next + fld discriminant + fsqrt + fadd quadraticB + fchs + fdiv quadraticA + fst hitTime + fmul roofFloatFP3D + fistp pixelDepth + mov eax,pixelDepth + cmp eax,4096 + jle roof_round_next + cmp eax,cityDepthBuffer3D[edi] + jae roof_round_next + fld hitTime + fmul rayX + fadd ox + fld1 + faddp st(1),st + fmul roofFloatHalfTex3D + fistp texU + fld hitTime + fmul rayZ + fadd oz + fchs + fld1 + faddp st(1),st + fmul roofFloatHalfTex3D + fistp texV + fld hitTime + fmul rayZ + fadd oz + fmul roofFloatShade3D + fadd roofFloatAmbient3D + fistp shade + mov eax,texV + and eax,255 + mov ecx,texHeight + imul eax,ecx + shr eax,8 + imul eax,texWidth + mov edx,texU + and edx,255 + imul edx,texWidth + shr edx,8 + add eax,edx + mov esi,texture + mov eax,[esi+eax*4] + mov color,eax + movzx eax,al + imul eax,shade + shr eax,8 + mov ebx,eax + mov eax,color + shr eax,8 + and eax,255 + imul eax,shade + and eax,0FF00h + or ebx,eax + mov eax,color + shr eax,16 + and eax,255 + imul eax,shade + and eax,0FF00h + shl eax,8 + or eax,ebx + cmp roofCityPass3D,0 + je roof_round_color_ready + invoke ShadeRoofCityPixel3D,eax,pixelDepth,256,material + mov ecx,edi + shr ecx,2 + mov edx,roofPixelGlow3D + mov roofGlowMask3D[ecx],dl +roof_round_color_ready: + mov screenBuffer[edi],eax + cmp hallLevel3D,HALL_ROOF + jne @F + mov ecx,edi + shr ecx,2 + mov edx,roofCityPass3D + mov roofCityMask3D[ecx],dl +@@: + mov eax,pixelDepth + mov cityDepthBuffer3D[edi],eax +roof_round_next: + add edi,4 + inc column + mov eax,column + cmp eax,rightPx + jbe roof_round_pixel + inc row + mov eax,row + cmp eax,bottomPx + jbe roof_round_row +roof_round_done: + ret +DrawRoofEllipsoid3D ENDP + +; Closed furniture parts use material surfaces, never pictures of whole chairs. +DrawRoofSolid3D PROC uses ebx esi x0:DWORD,x1:DWORD,y0:DWORD,y1:DWORD,z0:DWORD,z1:DWORD,mat:DWORD + mov eax,x0 + cmp playerX,eax + jge @F + invoke DrawCityVertical3D,0,x0,y0,y1,z0,z1,mat +@@: + mov eax,x1 + cmp playerX,eax + jle @F + invoke DrawCityVertical3D,0,x1,y0,y1,z0,z1,mat +@@: + mov eax,y0 + cmp playerY,eax + jge @F + invoke DrawCityVertical3D,1,y0,x0,x1,z0,z1,mat +@@: + mov eax,y1 + cmp playerY,eax + jle @F + invoke DrawCityVertical3D,1,y1,x0,x1,z0,z1,mat +@@: + mov eax,z1 + cmp viewEye3D,eax + jle @F + invoke DrawCityHorizontal3D,x0,x1,y0,y1,z1,mat +@@: + ret +DrawRoofSolid3D ENDP + +DrawRoofPadding3D PROC uses ebx esi edi centreX:DWORD,centreY:DWORD,centreZ:DWORD,rx:DWORD,ry:DWORD,rz:DWORD,mat:DWORD + local x0:DWORD,x1:DWORD,y0:DWORD,y1:DWORD,z0:DWORD,z1:DWORD + local insetX0:DWORD,insetX1:DWORD,insetY0:DWORD,insetY1:DWORD,loZ:DWORD,hiZ:DWORD + mov eax,centreX + sub eax,rx + mov x0,eax + add eax,FP_1*35/1000 + mov insetX0,eax + mov eax,centreX + add eax,rx + mov x1,eax + sub eax,FP_1*35/1000 + mov insetX1,eax + mov eax,centreY + sub eax,ry + mov y0,eax + add eax,FP_1*35/1000 + mov insetY0,eax + mov eax,centreY + add eax,ry + mov y1,eax + sub eax,FP_1*35/1000 + mov insetY1,eax + mov eax,centreZ + sub eax,rz + mov z0,eax + add eax,FP_1*35/1000 + mov loZ,eax + mov eax,centreZ + add eax,rz + mov z1,eax + sub eax,FP_1*35/1000 + mov hiZ,eax + invoke DrawRoofSolid3D,insetX0,insetX1,insetY0,insetY1,z0,z1,mat + invoke DrawRoofSolid3D,x0,x1,insetY0,insetY1,loZ,hiZ,mat + invoke DrawRoofSolid3D,insetX0,insetX1,y0,y1,loZ,hiZ,mat + ret +DrawRoofPadding3D ENDP + +DrawRoofUpholstery3D PROC uses ebx esi edi centreX:DWORD,frontY:DWORD,kind:DWORD + local x0:DWORD,x1:DWORD,y1:DWORD,halfWidth:DWORD,count:DWORD,seatZ:DWORD,backZ:DWORD + local xa:DWORD,xb:DWORD,ya:DWORD,yb:DWORD,za:DWORD,zb:DWORD + mov halfWidth,FP_1*120/100 + mov count,3 + mov seatZ,FP_1*45/100 + mov backZ,FP_1*92/100 + mov ecx,FP_1*90/100 + cmp kind,0 + je @F + mov halfWidth,FP_1*46/100 + mov count,1 + mov ecx,FP_1*86/100 + cmp kind,2 + jb @F + mov halfWidth,FP_1*27/100 + mov seatZ,FP_1*78/100 + mov backZ,FP_1*108/100 + mov ecx,FP_1*48/100 +@@: + mov eax,centreX + sub eax,halfWidth + mov x0,eax + mov eax,centreX + add eax,halfWidth + mov x1,eax + mov eax,frontY + add eax,ecx + mov y1,eax + ; Four metal feet, solid armrests, a seat base and separate upholstered pads. + xor esi,esi +roof_furniture_foot: + mov eax,x0 + add eax,FP_1*8/100 + test esi,1 + jz @F + mov eax,x1 + sub eax,FP_1*12/100 +@@: + mov xa,eax + add eax,FP_1*4/100 + mov xb,eax + mov eax,frontY + add eax,FP_1*8/100 + test esi,2 + jz @F + mov eax,y1 + sub eax,FP_1*12/100 +@@: + mov ya,eax + add eax,FP_1*4/100 + mov yb,eax + mov eax,seatZ + sub eax,FP_1*12/100 + invoke DrawRoofSolid3D,xa,xb,ya,yb,0,eax,ROOF_METAL + inc esi + cmp esi,4 + jb roof_furniture_foot + mov eax,seatZ + sub eax,FP_1*14/100 + mov za,eax + add eax,FP_1*5/100 + invoke DrawRoofSolid3D,x0,x1,frontY,y1,za,eax,ROOF_TEAL + mov eax,y1 + sub eax,FP_1*14/100 + mov ya,eax + invoke DrawRoofSolid3D,x0,x1,ya,y1,seatZ,backZ,ROOF_TEAL + mov eax,x0 + add eax,FP_1*12/100 + mov xa,eax + mov eax,x1 + sub eax,FP_1*12/100 + mov xb,eax + ; Soft rectangular cushion edges are inset in three shallow bands. + xor esi,esi +roof_furniture_pad: + mov eax,xb + sub eax,xa + xor edx,edx + div count + mov edi,eax + imul eax,esi + add eax,xa + add eax,FP_1/100 + mov ebx,eax + add ebx,edi + sub ebx,FP_1*2/100 + mov ecx,frontY + add ecx,FP_1*5/100 + mov edx,y1 + sub edx,FP_1*16/100 + mov za,eax + mov zb,ebx + mov ya,ecx + mov yb,edx + mov eax,seatZ + sub eax,FP_1*9/100 + mov eax,za + add eax,zb + sar eax,1 + mov ebx,zb + sub ebx,za + sar ebx,1 + mov ecx,ya + add ecx,yb + sar ecx,1 + mov edx,yb + sub edx,ya + sar edx,1 + mov edi,seatZ + sub edi,FP_1*4/100 + invoke DrawRoofPadding3D,eax,ecx,edi,ebx,edx,FP_1*8/100,ROOF_IVORY + mov eax,za + add eax,FP_1*2/100 + mov ebx,zb + sub ebx,FP_1*2/100 + mov ecx,ya + add ecx,FP_1*2/100 + mov edx,yb + sub edx,FP_1*2/100 + mov ya,ecx + mov yb,edx + mov ecx,seatZ + add ecx,FP_1*2/100 + ; Back cushions are real thin volumes, not alpha views pasted on each side. + mov eax,y1 + sub eax,FP_1*22/100 + mov ya,eax + add eax,FP_1*9/100 + mov yb,eax + mov eax,seatZ + add eax,FP_1*2/100 + mov ecx,backZ + sub ecx,FP_1*3/100 + add eax,ecx + sar eax,1 + mov edi,eax + mov eax,za + add eax,zb + sar eax,1 + mov ebx,zb + sub ebx,za + sar ebx,1 + mov ecx,ya + add ecx,yb + sar ecx,1 + invoke DrawRoofPadding3D,eax,ecx,edi,ebx,FP_1*9/100,FP_1*23/100,ROOF_IVORY + inc esi + cmp esi,count + jb roof_furniture_pad + cmp kind,2 + jae roof_furniture_done + mov eax,seatZ + add eax,FP_1*17/100 + mov zb,eax + mov eax,x0 + add eax,FP_1*11/100 + invoke DrawRoofSolid3D,x0,eax,frontY,y1,seatZ,zb,ROOF_TEAL + mov eax,x1 + sub eax,FP_1*11/100 + invoke DrawRoofSolid3D,eax,x1,frontY,y1,seatZ,zb,ROOF_TEAL +roof_furniture_done: + ret +DrawRoofUpholstery3D ENDP + +DrawRoofPlanter3D PROC uses ebx esi x0:DWORD,x1:DWORD,y0:DWORD,y1:DWORD + local xa:DWORD,xb:DWORD,ya:DWORD,yb:DWORD + invoke DrawRoofSolid3D,x0,x1,y0,y1,0,FP_1*42/100,ROOF_STONE + mov eax,x0 + add eax,FP_1*4/100 + mov xa,eax + mov eax,x1 + sub eax,FP_1*4/100 + mov xb,eax + mov eax,y0 + add eax,FP_1*4/100 + mov ya,eax + mov eax,y1 + sub eax,FP_1*4/100 + mov yb,eax + invoke DrawCityHorizontal3D,xa,xb,ya,yb,FP_1*425/1000,ROOF_EARTH + ; Several separated foliage tufts give the bed depth from either end. + mov esi,xa +roof_planter_tuft: + mov ebx,esi + add ebx,FP_1*34/100 + cmp ebx,xb + jae roof_planter_done + mov eax,ya + add eax,FP_1*10/100 + invoke DrawCityVertical3D,1,eax,esi,ebx,FP_1*43/100,FP_1*85/100,ROOF_FOLIAGE + mov eax,yb + sub eax,FP_1*10/100 + invoke DrawCityVertical3D,1,eax,esi,ebx,FP_1*43/100,FP_1*78/100,ROOF_FOLIAGE + mov eax,esi + add eax,FP_1*17/100 + invoke DrawCityVertical3D,0,eax,ya,yb,FP_1*43/100,FP_1*85/100,ROOF_FOLIAGE + add esi,FP_1*38/100 + jmp roof_planter_tuft +roof_planter_done: + ret +DrawRoofPlanter3D ENDP + +UpdateRoofGuests3D PROC uses ebx esi edi milliseconds:DWORD + local px:DWORD,py:DWORD,stepSize:DWORD,savedSeat:DWORD,savedLean:DWORD + mov eax,milliseconds + add roofGuestClock3D,eax + imul eax,38 + mov stepSize,eax + mov eax,roofSeat3D + mov savedSeat,eax + mov eax,roofLean3D + mov savedLean,eax + mov roofSeat3D,0 + mov roofLean3D,0 + xor esi,esi +roof_person_update: + mov roofGuestMoving3D[esi*4],0 + mov eax,milliseconds + cmp roofGuestWait3D[esi*4],eax + jbe roof_person_step + sub roofGuestWait3D[esi*4],eax + jmp roof_person_next +roof_person_step: + mov roofGuestWait3D[esi*4],0 + mov eax,roofGuestX3D[esi*4] + mov px,eax + mov eax,roofGuestY3D[esi*4] + mov py,eax + mov eax,roofGuestDir3D[esi*4] + mov ebx,stepSize + cmp eax,0 + jne @F + add px,ebx + jmp roof_person_check +@@: + cmp eax,1 + jne @F + sub px,ebx + jmp roof_person_check +@@: + cmp eax,2 + jne @F + add py,ebx + jmp roof_person_check +@@: + sub py,ebx +roof_person_check: + invoke RoofCanWalk3D,px,py + test eax,eax + jz roof_person_turn + mov eax,px + sub eax,playerX + cdq + xor eax,edx + sub eax,edx + mov ebx,eax + mov eax,py + sub eax,playerY + cdq + xor eax,edx + sub eax,edx + add eax,ebx + cmp eax,FP_1*7/10 + jb roof_person_turn + mov eax,px + mov roofGuestX3D[esi*4],eax + mov eax,py + mov roofGuestY3D[esi*4],eax + mov roofGuestMoving3D[esi*4],1 + mov eax,stepSize + add roofGuestDistance3D[esi*4],eax + add roofGuestStep3D[esi*4],eax + cmp roofGuestStep3D[esi*4],FP_1*18/10 + jb roof_person_next +roof_person_turn: + mov roofGuestStep3D[esi*4],0 + mov roofGuestWait3D[esi*4],1700 + inc roofGuestDir3D[esi*4] + and roofGuestDir3D[esi*4],3 +roof_person_next: + inc esi + cmp esi,ROOF_GUEST_COUNT + jb roof_person_update + mov eax,savedSeat + mov roofSeat3D,eax + mov eax,savedLean + mov roofLean3D,eax + ret +UpdateRoofGuests3D ENDP + +CheckRoofRail3D PROC + mov roofLeanSide3D,-1 + cmp playerY,FP_1*191/10 + jb @F + cmp playerAngle,64 + jb @F + cmp playerAngle,192 + ja @F + mov roofLeanSide3D,0 +@@: + cmp playerX,FP_1*272/10 + jb @F + mov eax,playerAngle + add eax,64 + and eax,511 + cmp eax,128 + ja @F + mov roofLeanSide3D,1 +@@: + cmp playerX,FP_1*128/10 + ja @F + cmp playerAngle,192 + jb @F + cmp playerAngle,320 + ja @F + ; The western drinks counter is not a handrail approach. + cmp playerY,11*FP_1 + jb roof_check_west_rail + cmp playerY,17*FP_1 + jbe @F +roof_check_west_rail: + mov roofLeanSide3D,2 +@@: + cmp roofLeanSide3D,-1 + je @F + mov interactTargetId,INTERACT_ROOF_RAIL + mov interactPromptPtr,OFFSET roofLeanPrompt3D +@@: + ret +CheckRoofRail3D ENDP + +StartRoofLean3D PROC + mov eax,playerX + mov roofReturnX3D,eax + mov eax,playerY + mov roofReturnY3D,eax + mov eax,playerAngle + mov roofReturnAngle3D,eax + mov roofLeanAngle3D,128 + mov eax,playerX + mov edx,FP_1*1975/100 + cmp roofLeanSide3D,0 + je roof_start_lean_check + mov eax,FP_1*2775/100 + mov edx,playerY + mov roofLeanAngle3D,0 + cmp roofLeanSide3D,1 + je roof_start_lean_check + mov eax,FP_1*1225/100 + mov roofLeanAngle3D,256 +roof_start_lean_check: + push eax + push edx + invoke RoofCanWalk3D,eax,edx + mov ecx,eax + pop edx + pop eax + test ecx,ecx + jz roof_start_lean_done + mov playerX,eax + mov playerY,edx + mov eax,roofLeanAngle3D + mov playerAngle,eax + mov roofLean3D,1 + mov roofLeanTime3D,0 + call UpdateCamera3D +roof_start_lean_done: + ret +StartRoofLean3D ENDP + +UpdateRoofLook3D PROC + cmp roofLean3D,0 + je roof_look_done + mov eax,playerAngle + sub eax,roofLeanAngle3D + add eax,256 + and eax,511 + sub eax,256 + cmp eax,104 + jle @F + mov eax,104 +@@: + cmp eax,-104 + jge @F + mov eax,-104 +@@: + add eax,roofLeanAngle3D + and eax,511 + mov playerAngle,eax + call UpdateCamera3D +roof_look_done: + ret +UpdateRoofLook3D ENDP + +RenderRoofLean3D PROC uses ebx esi + local anchorX:DWORD,anchorY:DWORD,depth:DWORD,left:DWORD,widthPx:DWORD,heightPx:DWORD + cmp roofLean3D,0 + je roof_hands_done + cmp engineMode,MODE_WALK + jne roof_hands_done + mov eax,roofLeanTime3D + imul eax,31 + xor edx,edx + mov ecx,1100 + div ecx + mov esi,eax + ; Project the fixed rail contact instead of rotating the hands with the head. + mov ebx,roofLeanAngle3D + and ebx,511 + mov eax,tableCos[ebx*4] + mov ecx,FP_1*115/100 + imul ecx + shrd eax,edx,16 + add eax,playerX + mov anchorX,eax + mov eax,tableSin[ebx*4] + imul ecx + shrd eax,edx,16 + add eax,playerY + mov anchorY,eax + invoke ProjectCityPoint3D,anchorX,anchorY + cmp edx,FP_1/5 + jle roof_hands_done + mov depth,edx + mov left,eax + mov roofHandsContactX3D,eax + mov eax,640*FP_1*115/100 + cdq + idiv depth + mov widthPx,eax + mov roofHandsWidth3D,eax + shr eax,1 + sub left,eax + mov eax,widthPx + shr eax,1 + mov heightPx,eax + mov eax,viewEye3D + sub eax,ROOF_RAIL_HEIGHT + imul eax,480 + cdq + idiv depth + add eax,viewHorizon3D + mov ebx,eax + mov eax,widthPx + imul eax,166 + xor edx,edx + mov ecx,640 + div ecx + sub ebx,eax + invoke DrawRoofHandsPlane3D,esi,left,widthPx +roof_hands_done: + ret +RenderRoofLean3D ENDP + +; Keep the forearms in the first-person view; shear their contact line onto the rail. +DrawRoofHandsPlane3D PROC uses ebx esi edi frame:DWORD,left:DWORD,scaleW:DWORD + local source:DWORD,col:DWORD,normalX:DWORD,normalY:DWORD,denom:DWORD + local depth:DWORD,srcX:DWORD,srcY:DWORD,stepY:DWORD,row:DWORD,lastRow:DWORD + local extension:DWORD,shiftDown:DWORD + cmp roofHandsArt3D.pixels,0 + je roof_hands_plane_done + mov eax,frame + cmp eax,roofHandsArt3D.frames + jae roof_hands_plane_done + imul eax,640*320*4 + add eax,roofHandsArt3D.pixels + mov source,eax + mov eax,roofLeanAngle3D + and eax,511 + mov ecx,tableCos[eax*4] + mov normalX,ecx + mov ecx,tableSin[eax*4] + mov normalY,ecx + mov eax,FP_1*640 + cdq + idiv scaleW + mov stepY,eax + mov col,0 +roof_hands_plane_column: + cmp col,640 + jge roof_hands_plane_done + mov esi,col + mov eax,cityRayX3D[esi*4] + imul normalX + shrd eax,edx,16 + mov ebx,eax + mov eax,cityRayY3D[esi*4] + imul normalY + shrd eax,edx,16 + add eax,ebx + cmp eax,FP_1/12 + jle roof_hands_plane_next + mov denom,eax + mov eax,FP_1*115/100 + mov ecx,FP_1 + imul ecx + idiv denom + cmp eax,16*FP_1 + jge roof_hands_plane_next + mov depth,eax + mov eax,col + sub eax,left + imul eax,640 + cdq + idiv scaleW + mov extension,0 + mov shiftDown,0 + cmp eax,8 + jge roof_hands_check_right_edge + mov eax,scaleW + xor edx,edx + mov ecx,80 + div ecx + add eax,left + sub eax,col + mov shiftDown,eax + mov extension,1 + mov eax,8 + jmp roof_hands_source_column +roof_hands_check_right_edge: + cmp eax,632 + jl roof_hands_source_column + mov eax,scaleW + imul eax,632 + xor edx,edx + mov ecx,640 + div ecx + add eax,left + mov ecx,col + sub ecx,eax + mov shiftDown,ecx + mov extension,1 + mov eax,631 +roof_hands_source_column: + mov srcX,eax + mov eax,viewEye3D + sub eax,ROOF_RAIL_HEIGHT + imul eax,480 + cdq + idiv depth + add eax,viewHorizon3D + mov ebx,eax + mov eax,scaleW + imul eax,166 + xor edx,edx + mov ecx,640 + div ecx + sub ebx,eax + mov eax,shiftDown + add ebx,eax + mov denom,ebx + mov eax,ebx + test eax,eax + jns @F + xor eax,eax +@@: + cmp eax,480 + jge roof_hands_plane_next + mov row,eax + mov eax,scaleW + shr eax,1 + add eax,denom + cmp eax,480 + jle @F + mov eax,480 +@@: + mov lastRow,eax + cmp extension,0 + je @F + mov lastRow,480 +@@: + mov eax,row + sub eax,denom + imul eax,stepY + mov srcY,eax + mov eax,row + imul eax,640*4 + mov edi,col + lea edi,[eax+edi*4] + mov ebx,srcX + shl ebx,2 + add ebx,source +roof_hands_plane_pixel: + mov eax,row + cmp eax,lastRow + jge roof_hands_plane_next + mov edx,srcY + sar edx,16 + cmp extension,0 + je @F + cmp edx,314 + jl @F + mov edx,313 +@@: + cmp edx,320 + jae roof_hands_plane_advance + imul edx,640*4 + mov eax,[ebx+edx] + cmp eax,80000000h + jb roof_hands_plane_advance + and eax,00FFFFFFh + mov screenBuffer[edi],eax + mov eax,edi + shr eax,2 + mov roofCityMask3D[eax],0 +roof_hands_plane_advance: + mov eax,stepY + add srcY,eax + add edi,640*4 + inc row + jmp roof_hands_plane_pixel +roof_hands_plane_next: + inc col + cmp col,640 + jb roof_hands_plane_column +roof_hands_plane_done: + ret +DrawRoofHandsPlane3D ENDP + +ShadeRoofNight3D PROC uses ebx esi edi + cmp roofNight3D,0 + je roof_night_done + ; Sky is already night artwork. Only shade actual depth-tested world pixels. + xor edi,edi +roof_night_pixel: + mov eax,edi + shr eax,2 + cmp roofCityMask3D[eax],0 + jne roof_night_next + cmp cityDepthBuffer3D[edi],ROOF_FAR_DEPTH + jae roof_night_next + mov eax,screenBuffer[edi] + mov edx,eax + and edx,000000FFh + cmp edx,90 + ja @F + mov edx,eax + shr edx,16 + cmp edx,110 + ja roof_night_next +@@: + and eax,00FCFCFCh + shr eax,2 + add eax,00070D1Dh + mov screenBuffer[edi],eax +roof_night_next: + add edi,4 + cmp edi,SCREEN_SIZE*4 + jb roof_night_pixel +roof_night_done: + ret +ShadeRoofNight3D ENDP + +PrepareRoofFloorDepth3D PROC uses ebx esi edi + local row:DWORD,depth:DWORD,px:DWORD,py:DWORD,deltaX:DWORD,deltaY:DWORD + mov eax,viewHorizon3D + inc eax + mov row,eax +roof_floor_depth_row: + mov ecx,row + sub ecx,viewHorizon3D + mov eax,viewEye3D + imul eax,SCREEN_H + cdq + idiv ecx + mov depth,eax + imul cityRayX3D[0] + shrd eax,edx,16 + add eax,playerX + mov px,eax + mov eax,depth + imul cityRayY3D[0] + shrd eax,edx,16 + add eax,playerY + mov py,eax + mov eax,depth + imul playerPlaneX + shrd eax,edx,16 + shl eax,1 + cdq + mov ecx,SCREEN_W + idiv ecx + mov deltaX,eax + mov eax,depth + imul playerPlaneY + shrd eax,edx,16 + shl eax,1 + cdq + idiv ecx + mov deltaY,eax + mov eax,row + imul eax,SCREEN_W*4 + mov edi,eax + xor ebx,ebx +roof_floor_depth_pixel: + cmp px,FP_1*109/10 + jl roof_floor_depth_next + cmp px,FP_1*291/10 + jge roof_floor_depth_next + cmp py,FP_1*9/10 + jl roof_floor_depth_next + cmp py,FP_1*211/10 + jge roof_floor_depth_next + mov eax,depth + mov cityDepthBuffer3D[edi],eax + mov eax,edi + shr eax,2 + mov roofCityMask3D[eax],0 + cmp roofHDReady3D,1 + jne roof_floor_depth_next + cmp engineMode,MODE_WALK + jne roof_floor_depth_next + mov roofCityMask3D[eax],3 +roof_floor_depth_next: + mov eax,deltaX + add px,eax + mov eax,deltaY + add py,eax + add edi,4 + inc ebx + cmp ebx,SCREEN_W + jb roof_floor_depth_pixel + inc row + cmp row,SCREEN_H + jb roof_floor_depth_row + ret +PrepareRoofFloorDepth3D ENDP + +RenderRoofDistrict3D PROC uses ebx esi edi + local x0:DWORD,x1:DWORD,y0:DWORD,y1:DWORD,z:DWORD,mat:DWORD,roofZ:DWORD + local screenX:DWORD,centreDepth:DWORD,middle:DWORD,ridgeZ:DWORD + xor esi,esi +roof_district_building: + imul edi,esi,28 + mov eax,roofDistrict3D[edi] + mov x0,eax + mov eax,roofDistrict3D[edi+4] + mov x1,eax + add eax,x0 + sar eax,1 + mov ebx,roofDistrict3D[edi+8] + mov y0,ebx + mov ecx,roofDistrict3D[edi+12] + mov y1,ecx + add ebx,ecx + sar ebx,1 + invoke ProjectCityPoint3D,eax,ebx + cmp edx,-12*FP_1 + jle roof_district_next + mov centreDepth,edx + mov screenX,eax + cmp edx,12*FP_1 + jle roof_district_visible + mov eax,x1 + sub eax,x0 + add eax,y1 + sub eax,y0 + imul eax,SCREEN_H + cdq + idiv centreDepth + mov edx,screenX + add edx,eax + cmp edx,0 + jl roof_district_next + neg eax + add eax,screenX + cmp eax,SCREEN_W + jg roof_district_next +roof_district_visible: + mov eax,roofDistrict3D[edi+16] + mov z,eax + mov eax,roofDistrict3D[edi+20] + mov mat,eax + invoke DrawCityBoxSides3D,x0,x1,y0,y1,ROOF_CITY_STREET_Z,z,mat + mov eax,z + add eax,FP_1/6 + mov roofZ,eax + invoke DrawRoofSolid3D,x0,x1,y0,y1,z,eax,ROOF_PLASTER + cmp mat,ROOF_MODERN_FACADE + jae roof_city_flat_roof + cmp centreDepth,100*FP_1 + jle @F +roof_city_flat_roof: + mov eax,roofZ + add eax,FP_1/20 + invoke DrawCityHorizontal3D,x0,x1,y0,y1,eax,ROOF_CITY_ROOF + jmp roof_district_next +@@: + ; Stepped low mansards, dormers and chimneys give the skyline real parallax. + mov ebx,x0 + add ebx,FP_1*6/10 + mov ecx,x1 + sub ecx,FP_1*6/10 + mov edx,y0 + add edx,FP_1*6/10 + mov eax,y1 + sub eax,FP_1*6/10 + mov x0,ebx + mov x1,ecx + mov y0,edx + mov y1,eax + mov eax,roofZ + add eax,FP_1*24/10 + mov ridgeZ,eax + mov eax,x1 + sub eax,x0 + mov edx,y1 + sub edx,y0 + cmp eax,edx + ja roof_mansard_ridge_x + mov eax,x0 + add eax,x1 + sar eax,1 + mov middle,eax + invoke DrawRoofSlope3D,0,x0,middle,y0,y1,roofZ,FP_1*24/10,ROOF_CITY_ROOF + invoke DrawRoofSlope3D,0,middle,x1,y0,y1,ridgeZ,-FP_1*24/10,ROOF_CITY_ROOF + jmp roof_mansard_chimney +roof_mansard_ridge_x: + mov eax,y0 + add eax,y1 + sar eax,1 + mov middle,eax + invoke DrawRoofSlope3D,1,x0,x1,y0,middle,roofZ,FP_1*24/10,ROOF_CITY_ROOF + invoke DrawRoofSlope3D,1,x0,x1,middle,y1,ridgeZ,-FP_1*24/10,ROOF_CITY_ROOF +roof_mansard_chimney: + mov eax,ridgeZ + mov roofZ,eax + mov eax,x0 + add eax,FP_1/2 + mov ebx,y0 + add ebx,FP_1/2 + mov ecx,z + add ecx,FP_1*24/10 + invoke DrawRoofSolid3D,x0,eax,y0,ebx,roofZ,ecx,ROOF_STONE +roof_district_next: + inc esi + cmp esi,ROOF_BUILDING_COUNT + jb roof_district_building + ret +RenderRoofDistrict3D ENDP + +RenderRoofCityGround3D PROC uses ebx esi edi + local row:DWORD,depth:DWORD,px:DWORD,py:DWORD,deltaX:DWORD,deltaY:DWORD + local road:DWORD,stone:DWORD,water:DWORD,tex:DWORD,ix:DWORD,iy:DWORD + local sampleDepth:DWORD,planeHeight:DWORD,waterHeight:DWORD + mov eax,viewEye3D + sub eax,ROOF_CITY_STREET_Z + mov planeHeight,eax + add eax,2*FP_1 + mov waterHeight,eax + invoke ResolveRoofSurface3D,ROOF_CITY_ROAD + mov road,eax + invoke ResolveRoofSurface3D,ROOF_CITY_WATER + mov water,eax + invoke ResolveRoofSurface3D,ROOF_STONE + mov stone,eax + cmp road,0 + jne @F + mov road,0FFFFFFFFh +@@: + cmp water,0 + jne @F + mov water,0FFFFFFFDh +@@: + cmp stone,0 + jne @F + mov stone,0FFFFFFFEh +@@: + mov roofGroundRoadPixels3D,0 + mov roofGroundWaterPixels3D,0 + mov eax,viewHorizon3D + inc eax + mov row,eax +roof_city_ground_row: + mov ecx,row + sub ecx,viewHorizon3D + mov eax,viewEye3D + sub eax,ROOF_CITY_STREET_Z + imul eax,SCREEN_H + cdq + idiv ecx + cmp eax,ROOF_FAR_DEPTH + jae roof_city_ground_next_row + mov depth,eax + imul cityRayX3D[0] + shrd eax,edx,16 + add eax,playerX + mov px,eax + mov eax,depth + imul cityRayY3D[0] + shrd eax,edx,16 + add eax,playerY + mov py,eax + mov eax,depth + imul playerPlaneX + shrd eax,edx,16 + shl eax,1 + cdq + mov ecx,SCREEN_W + idiv ecx + mov deltaX,eax + mov eax,depth + imul playerPlaneY + shrd eax,edx,16 + shl eax,1 + cdq + idiv ecx + mov deltaY,eax + mov eax,row + imul eax,SCREEN_W*4 + mov edi,eax + xor ebx,ebx +roof_city_ground_pixel: + mov eax,depth + mov sampleDepth,eax + cmp px,-600*FP_1 + jl roof_city_ground_advance + cmp px,600*FP_1 + jg roof_city_ground_advance + cmp py,-600*FP_1 + jl roof_city_ground_advance + cmp py,600*FP_1 + jg roof_city_ground_advance + mov eax,depth + cmp eax,cityDepthBuffer3D[edi] + jae roof_city_ground_advance + mov eax,px + sar eax,16 + sub eax,20 + cdq + mov ecx,36 + idiv ecx + test edx,edx + jns @F + add edx,36 +@@: + mov ix,edx + mov eax,py + sar eax,16 + sub eax,11 + cdq + idiv ecx + test edx,edx + jns @F + add edx,36 +@@: + mov iy,edx + mov eax,road + mov tex,eax + ; Narrow stone pavements border the connected eight-metre carriageways. + cmp ix,13 + jb roof_city_ground_y_street + cmp ix,23 + ja roof_city_ground_y_street + cmp ix,14 + jb roof_city_ground_pavement + cmp ix,22 + ja roof_city_ground_pavement + jmp roof_city_ground_canal +roof_city_ground_y_street: + cmp iy,13 + jb roof_city_ground_pavement + cmp iy,23 + ja roof_city_ground_pavement + cmp iy,14 + jb roof_city_ground_pavement + cmp iy,22 + jbe roof_city_ground_canal +roof_city_ground_pavement: + mov eax,stone + mov tex,eax +roof_city_ground_canal: + cmp py,29*FP_1 + jl roof_city_ground_sample + cmp py,76*FP_1 + jg roof_city_ground_sample + cmp py,60*FP_1 + jge roof_city_old_channel + cmp px,70*FP_1 + jl roof_city_ground_sample + jmp roof_city_new_bridges +roof_city_old_channel: + mov eax,px + add eax,34*FP_1 + add eax,3*FP_1 + cmp eax,6*FP_1 + jbe roof_city_ground_sample + mov eax,px + sub eax,38*FP_1-3*FP_1 + cmp eax,6*FP_1 + jbe roof_city_ground_sample +roof_city_new_bridges: + mov eax,px + sub eax,110*FP_1-3*FP_1 + cmp eax,6*FP_1 + jbe roof_city_ground_sample + mov eax,px + sub eax,254*FP_1-3*FP_1 + cmp eax,6*FP_1 + jbe roof_city_ground_sample + mov eax,px + sub eax,398*FP_1-3*FP_1 + cmp eax,6*FP_1 + jbe roof_city_ground_sample +roof_city_ground_water: + mov eax,water + mov tex,eax + mov eax,depth + imul waterHeight + idiv planeHeight + mov sampleDepth,eax +roof_city_ground_sample: + mov eax,py + sar eax,9 + and eax,511 + shl eax,9 + mov edx,px + sar edx,9 + and edx,511 + add eax,edx + mov esi,tex + cmp esi,stone + jne @F + shr eax,2 + and eax,65535 +@@: + cmp esi,0FFFFFFFDh + jb roof_city_ground_texel + mov eax,00343B42h + cmp esi,0FFFFFFFFh + je roof_city_ground_texel_ready + mov eax,0063686Ah + cmp esi,0FFFFFFFEh + je roof_city_ground_texel_ready + mov eax,00183848h + jmp roof_city_ground_texel_ready +roof_city_ground_texel: + mov eax,[esi+eax*4] +roof_city_ground_texel_ready: + and eax,00FFFFFFh + cmp esi,road + jne @F + inc roofGroundRoadPixels3D +@@: + cmp esi,water + jne roof_city_ground_lighting + inc roofGroundWaterPixels3D + cmp roofNight3D,0 + je @F + mov eax,006E6E6Eh + jmp roof_city_ground_lighting +@@: + mov edx,px + sar edx,11 + add edx,animationClock3D + shr edx,4 + and edx,31 + cmp edx,3 + ja roof_city_ground_lighting + add eax,0020160Ch +roof_city_ground_lighting: + invoke SampleRoofGroundLight3D,eax,px,py + invoke ShadeRoofCityPixel3D,eax,sampleDepth,225,ROOF_CITY_ROAD + mov screenBuffer[edi],eax + mov eax,edi + shr eax,2 + mov roofCityMask3D[eax],1 + mov eax,sampleDepth + mov cityDepthBuffer3D[edi],eax +roof_city_ground_advance: + mov eax,deltaX + add px,eax + mov eax,deltaY + add py,eax + add edi,4 + inc ebx + cmp ebx,SCREEN_W + jb roof_city_ground_pixel +roof_city_ground_next_row: + inc row + cmp row,SCREEN_H + jb roof_city_ground_row + ; Continuous stone quays and three traversing bridge decks. + invoke DrawCityBoxSides3D,-190*FP_1,230*FP_1,59*FP_1,60*FP_1,ROOF_CITY_STREET_Z-2*FP_1,ROOF_CITY_STREET_Z,ROOF_STONE + invoke DrawCityBoxSides3D,-190*FP_1,230*FP_1,76*FP_1,77*FP_1,ROOF_CITY_STREET_Z-2*FP_1,ROOF_CITY_STREET_Z,ROOF_STONE + mov esi,-34*FP_1 + mov ebx,3 +roof_city_bridge: + mov eax,esi + sub eax,3*FP_1 + mov edx,esi + add edx,3*FP_1 + invoke DrawCityHorizontal3D,eax,edx,59*FP_1,77*FP_1,ROOF_CITY_STREET_Z,ROOF_CITY_ROAD + add esi,72*FP_1 + dec ebx + jnz roof_city_bridge +roof_city_ground_done: + ret +RenderRoofCityGround3D ENDP + +RenderRoofLandmarks3D PROC uses ebx esi edi + mov roofLandmarkLight3D,1 + lea esi,roofLandmarkParts3D + mov ebx,ROOF_LANDMARK_PARTS +roof_landmark_piece: + cmp DWORD PTR [esi],0 + jne roof_landmark_round + invoke DrawRoofSolid3D,[esi+4],[esi+8],[esi+12],[esi+16],[esi+20],[esi+24],[esi+28] + jmp roof_landmark_next +roof_landmark_round: + cmp DWORD PTR [esi],2 + jne @F + invoke DrawRoofPyramid3D,[esi+4],[esi+8],[esi+12],[esi+16],[esi+20],[esi+24],[esi+28] + jmp roof_landmark_next +@@: + invoke DrawRoofEllipsoid3D,[esi+4],[esi+8],[esi+12],[esi+16],[esi+20],[esi+24],[esi+28] +roof_landmark_next: + add esi,32 + dec ebx + jnz roof_landmark_piece + mov roofLandmarkLight3D,0 + ret +RenderRoofLandmarks3D ENDP + +RenderRoofCityParks3D PROC uses ebx esi edi + local px:DWORD,py:DWORD,x0:DWORD,x1:DWORD,y0:DWORD,y1:DWORD,treeX:DWORD,treeY:DWORD + xor esi,esi +roof_city_park: + mov eax,roofCityParks3D[esi*8] + mov px,eax + sub eax,12*FP_1 + mov x0,eax + add eax,24*FP_1 + mov x1,eax + mov eax,roofCityParks3D[esi*8+4] + mov py,eax + sub eax,12*FP_1 + mov y0,eax + add eax,24*FP_1 + mov y1,eax + invoke DrawCityHorizontal3D,x0,x1,y0,y1,ROOF_CITY_STREET_Z+FP_1/20,ROOF_CITY_LAWN + mov eax,px + sub eax,FP_1 + mov edx,px + add edx,FP_1 + invoke DrawCityHorizontal3D,eax,edx,y0,y1,ROOF_CITY_STREET_Z+FP_1/10,ROOF_STONE + xor ebx,ebx +roof_city_park_tree: + mov eax,px + sub eax,8*FP_1 + test ebx,1 + jz @F + add eax,16*FP_1 +@@: + mov treeX,eax + mov eax,ebx + shr eax,1 + imul eax,8*FP_1 + add eax,py + sub eax,8*FP_1 + mov treeY,eax + mov eax,treeX + sub eax,FP_1/8 + mov edx,treeX + add edx,FP_1/8 + mov ecx,treeY + sub ecx,FP_1/8 + mov edi,treeY + add edi,FP_1/8 + invoke DrawRoofSolid3D,eax,edx,ecx,edi,ROOF_CITY_STREET_Z,ROOF_CITY_STREET_Z+3*FP_1,ROOF_STONE + invoke DrawRoofEllipsoid3D,treeX,treeY,ROOF_CITY_STREET_Z+4*FP_1,FP_1*18/10,FP_1*18/10,FP_1*22/10,ROOF_CITY_LAWN + inc ebx + cmp ebx,6 + jb roof_city_park_tree + inc esi + cmp esi,ROOF_PARK_COUNT + jb roof_city_park + ret +RenderRoofCityParks3D ENDP diff --git a/src_graphics/rooftop_district_data.inc b/src_graphics/rooftop_district_data.inc new file mode 100644 index 0000000..70a1621 --- /dev/null +++ b/src_graphics/rooftop_district_data.inc @@ -0,0 +1,823 @@ +; Generated city block footprints and rooftop-relative heights. +ROOF_BUILDING_COUNT equ 518 +.data +roofDistrict3D LABEL DWORD + dd 917504,1638400,-1966080,-1310720,-3538944,203,0 + dd 3276800,3997696,-1966080,-1310720,-3211264,203,0 + dd -1441792,-720896,-1966080,-1310720,-3407872,203,0 + dd 393216,2228224,-3538944,-3080192,-2339635,192,1 + dd 5111808,5570560,262144,1179648,-3014656,192,2 + dd -1966080,-131072,-3538944,-3080192,-3126067,189,1 + dd 2752512,4587520,-3538944,-3080192,-2274099,187,1 + dd 5111808,5570560,-2097152,-1179648,-2949120,191,2 + dd 393216,851968,-4456448,-3538944,-2359296,192,2 + dd -4325376,-2490368,-196608,262144,-2949120,188,0 + dd -4325376,-2490368,1179648,1638400,-2929459,188,1 + dd 5111808,6946816,-196608,262144,-3014656,192,0 + dd 5111808,6946816,1179648,1638400,-2994995,192,1 + dd -589824,-131072,-4456448,-3538944,-3126067,189,3 + dd 2752512,3211264,-4456448,-3538944,-2293760,187,2 + dd -4325376,-2490368,-1179648,-720896,-2536243,187,1 + dd 5111808,6946816,-1179648,-720896,-2929459,191,1 + dd 393216,2228224,-4915200,-4456448,-2359296,192,0 + dd -4325376,-3866624,262144,1179648,-2949120,188,2 + dd 6488064,6946816,262144,1179648,-2994995,192,3 + dd -1966080,-1507328,-4456448,-3538944,-3145728,189,2 + dd 4128768,4587520,-4456448,-3538944,-2274099,187,3 + dd -4325376,-2490368,-2555904,-2097152,-2555904,187,0 + dd 5111808,6946816,-2555904,-2097152,-2949120,191,0 + dd -1966080,-131072,-4915200,-4456448,-3145728,189,0 + dd 2752512,4587520,-4915200,-4456448,-2293760,187,0 + dd -4325376,-3866624,-2097152,-1179648,-2555904,187,2 + dd -4325376,-2490368,-3538944,-3080192,-2143027,186,1 + dd 5111808,6946816,-3538944,-3080192,-2536243,190,1 + dd 5111808,5570560,-4456448,-3538944,-2555904,190,2 + dd 393216,2228224,-5898240,-5439488,-2994995,191,1 + dd -5308416,-4849664,262144,1179648,-2667315,193,3 + dd -1966080,-131072,-5898240,-5439488,-3060531,188,1 + dd -5308416,-4849664,-2097152,-1179648,-2274099,192,3 + dd 7471104,7929856,-2097152,-1179648,-2863923,186,1 + dd -1966080,-131072,6881280,7340032,-3014656,186,0 + dd 2752512,4587520,6881280,7340032,-2883584,192,0 + dd 7864320,8912896,65536,1376256,-1441792,199,0 + dd 393216,851968,-6815744,-5898240,-3014656,191,2 + dd -6684672,-4849664,-196608,262144,-2686976,193,0 + dd -6684672,-4849664,1179648,1638400,-2667315,193,1 + dd -4325376,-2490368,-4915200,-4456448,-2162688,186,0 + dd -4325376,-3866624,-4456448,-3538944,-2162688,186,2 + dd 5111808,6946816,-4915200,-4456448,-2555904,190,0 + dd -589824,-131072,-6815744,-5898240,-3060531,188,3 + dd -6684672,-4849664,-1179648,-720896,-2274099,192,1 + dd 7471104,9306112,-1179648,-720896,-2883584,186,0 + dd -589824,-131072,7340032,8257536,-2994995,186,3 + dd 2752512,3211264,7340032,8257536,-2883584,192,2 + dd 3145728,4194304,-7012352,-5701632,-2293760,204,0 + dd -1966080,-1507328,-6815744,-5898240,-3080192,188,2 + dd -6684672,-4849664,-2555904,-2097152,-2293760,192,0 + dd -1966080,-1507328,7340032,8257536,-3014656,186,2 + dd 393216,2228224,-7274496,-6815744,-3014656,191,0 + dd -6684672,-6225920,262144,1179648,-2686976,193,2 + dd 5111808,6946816,-5898240,-5439488,-2143027,189,1 + dd 7471104,7929856,-4456448,-3538944,-2490368,193,2 + dd 5111808,6946816,6881280,7340032,-3145728,187,0 + dd -1966080,-131072,-7274496,-6815744,-3080192,188,0 + dd -6684672,-6225920,-2097152,-1179648,-2293760,192,2 + dd -1966080,-131072,8257536,8716288,-2994995,186,1 + dd 2752512,4587520,8257536,8716288,-2863923,192,1 + dd 5111808,5570560,-6815744,-5898240,-2162688,189,2 + dd 7471104,9306112,-3538944,-3080192,-2470707,193,1 + dd 5111808,5570560,7340032,8257536,-3145728,187,2 + dd -3932160,-2883584,-7012352,-5701632,-2097152,204,0 + dd -6291456,-5242880,-4653056,-3342336,-2293760,204,0 + dd 393216,2228224,-8257536,-7798784,-2601779,190,1 + dd 393216,2228224,9240576,9699328,-2293760,190,0 + dd 7471104,9306112,-4915200,-4456448,-2490368,193,0 + dd 6488064,6946816,7340032,8257536,-3126067,187,3 + dd 2752512,4587520,-8257536,-7798784,-2863923,193,1 + dd 2752512,4587520,9240576,9699328,-2228224,193,0 + dd 5111808,6946816,-7274496,-6815744,-2162688,189,0 + dd 8847360,9306112,-4456448,-3538944,-2470707,193,3 + dd 5111808,6946816,8257536,8716288,-3126067,187,1 + dd 393216,851968,-9175040,-8257536,-2621440,190,2 + dd -9043968,-7208960,-196608,262144,-2752512,190,0 + dd -9043968,-7208960,1179648,1638400,-2732851,190,1 + dd 393216,851968,9699328,10616832,-2293760,190,2 + dd 1769472,2228224,9699328,10616832,-2274099,190,3 + dd -6684672,-4849664,-5898240,-5439488,-2863923,190,1 + dd -5308416,-4849664,-6815744,-5898240,-2863923,190,3 + dd 7471104,9306112,-5898240,-5439488,-3126067,192,1 + dd 7471104,7929856,-6815744,-5898240,-3145728,192,2 + dd 2752512,3211264,-9175040,-8257536,-2883584,193,2 + dd -9043968,-7208960,-1179648,-720896,-2339635,189,1 + dd 2752512,3211264,9699328,10616832,-2228224,193,2 + dd -1572864,-524288,-9371648,-8060928,-1900544,204,0 + dd 4128768,4587520,-9175040,-8257536,-2863923,193,3 + dd -9043968,-7208960,-2555904,-2097152,-2359296,189,0 + dd -4325376,-2490368,-8257536,-7798784,-2405171,192,1 + dd 9830400,10289152,-4456448,-3538944,-2752512,188,2 + dd -4325376,-2490368,9240576,9699328,-3145728,192,0 + dd 393216,2228224,-9633792,-9175040,-2621440,190,0 + dd -9043968,-8585216,262144,1179648,-2752512,190,2 + dd 393216,2228224,10616832,11075584,-2274099,190,1 + dd -9043968,-7208960,-3538944,-3080192,-2994995,188,1 + dd 9830400,11665408,-3538944,-3080192,-2732851,188,1 + dd -2949120,-2490368,9699328,10616832,-3126067,192,3 + dd 5111808,5570560,9699328,10616832,-2470707,188,1 + dd 2752512,4587520,-9633792,-9175040,-2883584,193,0 + dd -9043968,-8585216,-2097152,-1179648,-2359296,189,2 + dd 2752512,4587520,10616832,11075584,-2208563,193,1 + dd -6684672,-4849664,-7274496,-6815744,-2883584,190,0 + dd -6684672,-6225920,-6815744,-5898240,-2883584,190,2 + dd 7471104,9306112,-7274496,-6815744,-3145728,192,0 + dd 8847360,9306112,-6815744,-5898240,-3126067,192,3 + dd -6684672,-4849664,8257536,8716288,-2818048,188,0 + dd -6684672,-6225920,7340032,8257536,-2798387,188,1 + dd 5505024,6553600,-9371648,-8060928,-2097152,204,0 + dd -4325376,-3866624,-9175040,-8257536,-2424832,192,2 + dd -9043968,-7208960,-4915200,-4456448,-3014656,188,0 + dd 9830400,11665408,-4915200,-4456448,-2752512,188,0 + dd -4325376,-3866624,9699328,10616832,-3145728,192,2 + dd 393216,2228224,-10616832,-10158080,-2208563,189,1 + dd -10027008,-9568256,262144,1179648,-2470707,187,3 + dd -4325376,-2490368,-9633792,-9175040,-2424832,192,0 + dd -9043968,-8585216,-4456448,-3538944,-3014656,188,2 + dd -4325376,-2490368,10616832,11075584,-3126067,192,1 + dd 5111808,6946816,10616832,11075584,-2490368,188,0 + dd -6684672,-4849664,-8257536,-7798784,-2490368,189,0 + dd 7471104,9306112,-8257536,-7798784,-3060531,191,1 + dd 9830400,10289152,-6815744,-5898240,-2359296,187,2 + dd 9830400,10289152,7340032,8257536,-2293760,193,2 + dd -6684672,-4849664,9240576,9699328,-2883584,189,0 + dd 7471104,9306112,9240576,9699328,-2424832,191,0 + dd -1966080,-131072,-10616832,-10158080,-2274099,186,1 + dd 2752512,4587520,-10616832,-10158080,-2490368,192,0 + dd -10027008,-9568256,-2097152,-1179648,-3126067,186,3 + dd 12189696,12648448,-2097152,-1179648,-3080192,192,2 + dd -1966080,-131072,11599872,12058624,-2424832,188,0 + dd 2752512,4587520,11599872,12058624,-2621440,186,0 + dd 7471104,7929856,-9175040,-8257536,-3080192,191,2 + dd -9043968,-7208960,-5898240,-5439488,-2601779,187,1 + dd 9830400,11665408,-5898240,-5439488,-2339635,187,1 + dd 9830400,11665408,6881280,7340032,-2293760,193,0 + dd 7471104,7929856,9699328,10616832,-2424832,191,2 + dd 786432,1835008,11862016,13172736,-1900544,204,0 + dd 393216,851968,-11534336,-10616832,-2228224,189,2 + dd -11403264,-9568256,-196608,262144,-2490368,187,0 + dd -11403264,-9568256,1179648,1638400,-2470707,187,1 + dd -589824,-131072,-11534336,-10616832,-2274099,186,3 + dd 2752512,3211264,-11534336,-10616832,-2470707,192,1 + dd -11403264,-9568256,-1179648,-720896,-3126067,186,1 + dd 12189696,14024704,-1179648,-720896,-3060531,192,1 + dd 2752512,3211264,12058624,12976128,-2621440,186,2 + dd 5111808,6946816,-10616832,-10158080,-2405171,187,1 + dd -10027008,-9568256,-4456448,-3538944,-3060531,193,3 + dd 12189696,12648448,-4456448,-3538944,-2686976,191,2 + dd 5111808,6946816,11599872,12058624,-2883584,189,0 + dd -1966080,-1507328,-11534336,-10616832,-2293760,186,2 + dd -11403264,-9568256,-2555904,-2097152,-3145728,186,0 + dd 12189696,14024704,-2555904,-2097152,-3080192,192,0 + dd -1966080,-1507328,12058624,12976128,-2424832,188,2 + dd -6684672,-6225920,-9175040,-8257536,-2470707,189,1 + dd 8847360,9306112,-9175040,-8257536,-3060531,191,3 + dd -9043968,-7208960,-7274496,-6815744,-2621440,187,0 + dd 9830400,11665408,-7274496,-6815744,-2359296,187,0 + dd -9043968,-7208960,8257536,8716288,-2555904,193,0 + dd 9830400,11665408,8257536,8716288,-2274099,193,1 + dd -6684672,-6225920,9699328,10616832,-2883584,189,2 + dd 7471104,9306112,-9633792,-9175040,-3080192,191,0 + dd -9043968,-8585216,-6815744,-5898240,-2621440,187,2 + dd -9043968,-8585216,7340032,8257536,-2536243,193,1 + dd 11206656,11665408,7340032,8257536,-2274099,193,3 + dd -6684672,-4849664,10616832,11075584,-2863923,189,1 + dd 7471104,9306112,10616832,11075584,-2405171,191,1 + dd 5111808,5570560,-11534336,-10616832,-2424832,187,2 + dd -11403264,-9568256,-3538944,-3080192,-3060531,193,1 + dd 12189696,14024704,-3538944,-3080192,-2667315,191,1 + dd 5111808,5570560,12058624,12976128,-2883584,189,2 + dd 393216,2228224,-11993088,-11534336,-2228224,189,0 + dd -11403264,-10944512,262144,1179648,-2490368,187,2 + dd -1966080,-131072,-11993088,-11534336,-2293760,186,0 + dd -11403264,-10944512,-2097152,-1179648,-3145728,186,2 + dd 13565952,14024704,-2097152,-1179648,-3060531,192,3 + dd -1966080,-131072,12976128,13434880,-2405171,188,1 + dd 2752512,4587520,12976128,13434880,-2601779,186,1 + dd -9043968,-7208960,-8257536,-7798784,-2208563,186,1 + dd 9830400,11665408,-8257536,-7798784,-2994995,186,1 + dd 9830400,10289152,-9175040,-8257536,-3014656,186,2 + dd -9043968,-7208960,9240576,9699328,-2949120,186,0 + dd -7667712,-7208960,9699328,10616832,-2929459,186,3 + dd -11403264,-9568256,-4915200,-4456448,-3080192,193,0 + dd 12189696,14024704,-4915200,-4456448,-2686976,191,0 + dd -4325376,-3866624,12058624,12976128,-2470707,193,1 + dd 6488064,6946816,12058624,12976128,-2863923,189,3 + dd -6684672,-4849664,-10616832,-10158080,-3126067,188,1 + dd 12189696,12648448,-6815744,-5898240,-2293760,190,2 + dd -10027008,-9568256,7340032,8257536,-2601779,190,3 + dd 12189696,12648448,7340032,8257536,-2228224,188,2 + dd -6684672,-4849664,11599872,12058624,-2228224,190,0 + dd 10223616,11272192,9502720,10813440,-1900544,204,0 + dd 5111808,6946816,-11993088,-11534336,-2424832,187,0 + dd -11403264,-10944512,-4456448,-3538944,-3080192,193,2 + dd 13565952,14024704,-4456448,-3538944,-2667315,191,3 + dd -4325376,-2490368,12976128,13434880,-2490368,193,0 + dd 5111808,6946816,12976128,13434880,-2863923,189,1 + dd -5308416,-4849664,-11534336,-10616832,-3126067,188,3 + dd 12189696,14024704,-5898240,-5439488,-2274099,190,1 + dd -11403264,-9568256,6881280,7340032,-2621440,190,0 + dd 12189696,14024704,6881280,7340032,-2228224,188,0 + dd 7864320,8912896,-11730944,-10420224,-1900544,204,0 + dd -11010048,-9961472,-7012352,-5701632,-1900544,204,0 + dd 7864320,8912896,11862016,13172736,-2097152,204,0 + dd -9043968,-7208960,-9633792,-9175040,-2228224,186,0 + dd -9043968,-8585216,-9175040,-8257536,-2228224,186,2 + dd 9830400,11665408,-9633792,-9175040,-3014656,186,0 + dd -9043968,-7208960,10616832,11075584,-2929459,186,1 + dd -9043968,-8585216,9699328,10616832,-2949120,186,2 + dd -6684672,-6225920,-11534336,-10616832,-3145728,188,2 + dd 12189696,14024704,-7274496,-6815744,-2293760,190,0 + dd -11403264,-9568256,8257536,8716288,-2601779,190,1 + dd 12189696,14024704,8257536,8716288,-2208563,188,1 + dd -6684672,-6225920,12058624,12976128,-2228224,190,2 + dd -6684672,-4849664,-11993088,-11534336,-3145728,188,0 + dd 13565952,14024704,-6815744,-5898240,-2274099,190,3 + dd -11403264,-10944512,7340032,8257536,-2621440,190,2 + dd -6684672,-4849664,12976128,13434880,-2208563,190,1 + dd -9043968,-7208960,-10616832,-10158080,-2863923,193,1 + dd 9830400,11665408,-10616832,-10158080,-2601779,193,1 + dd -10027008,-9568256,-9175040,-8257536,-2274099,191,3 + dd -10027008,-9568256,9699328,10616832,-2994995,191,3 + dd 12189696,12648448,9699328,10616832,-2621440,189,2 + dd -9043968,-7208960,11599872,12058624,-2293760,187,0 + dd 9830400,11665408,11599872,12058624,-3080192,187,0 + dd 9830400,10289152,-11534336,-10616832,-2621440,193,2 + dd -11403264,-9568256,-8257536,-7798784,-2274099,191,1 + dd -11403264,-9568256,9240576,9699328,-3014656,191,0 + dd 12189696,14024704,9240576,9699328,-2621440,189,0 + dd -7667712,-7208960,12058624,12976128,-2274099,187,3 + dd 9830400,10289152,12058624,12976128,-3080192,187,2 + dd 12582912,13631488,-9371648,-8060928,-2293760,204,0 + dd -9043968,-8585216,-11534336,-10616832,-2883584,193,2 + dd -11403264,-9568256,-9633792,-9175040,-2293760,191,0 + dd -11403264,-9568256,10616832,11075584,-2994995,191,1 + dd 12189696,14024704,10616832,11075584,-2601779,189,1 + dd -9043968,-8585216,12058624,12976128,-2293760,187,2 + dd 11206656,11665408,12058624,12976128,-3060531,187,3 + dd -9043968,-7208960,-11993088,-11534336,-2883584,193,0 + dd 9830400,11665408,-11993088,-11534336,-2621440,193,0 + dd -11403264,-10944512,-9175040,-8257536,-2293760,191,2 + dd -11403264,-10944512,9699328,10616832,-3014656,191,2 + dd -9043968,-7208960,12976128,13434880,-2274099,187,1 + dd 9830400,11665408,12976128,13434880,-3060531,187,1 + dd -11403264,-9568256,-10616832,-10158080,-2929459,190,1 + dd -10027008,-9568256,-11534336,-10616832,-2929459,190,3 + dd 12189696,14024704,-10616832,-10158080,-2863923,188,1 + dd 12189696,12648448,-11534336,-10616832,-2883584,188,2 + dd 12189696,14024704,11599872,12058624,-3014656,190,0 + dd 12189696,12648448,12058624,12976128,-3014656,190,2 + dd -65536,2686976,-17629184,-14483456,-2359296,190,4 + dd -17170432,-13762560,-851968,2293760,-2686976,190,4 + dd -131072,2752512,15925248,19070976,-2293760,190,4 + dd -11403264,-9568256,-11993088,-11534336,-2949120,190,0 + dd -11403264,-10944512,-11534336,-10616832,-2949120,190,2 + dd 12189696,14024704,-11993088,-11534336,-2883584,188,0 + dd 13565952,14024704,-11534336,-10616832,-2863923,188,3 + dd 12189696,14024704,12976128,13434880,-2994995,190,1 + dd -4587520,-1179648,-17629184,-14483456,-2686976,189,4 + dd 3801088,7208960,-17629184,-14483456,-2031616,191,4 + dd -17039360,-13893632,-5046272,-1900544,-1507328,189,4 + dd 16384000,19791872,-5046272,-1900544,-2686976,189,4 + dd -3997696,-1769472,15925248,19070976,-2621440,189,4 + dd 4390912,6619136,15925248,19070976,-1966080,191,4 + dd -8454144,-5701632,-17629184,-14483456,-3014656,188,4 + dd 8323072,11075584,-17629184,-14483456,-1703936,192,4 + dd -16973824,-13959168,-9240576,-6094848,-2228224,188,4 + dd 16515072,19660800,-9240576,-6094848,-1507328,188,4 + dd -16711680,-14221312,7536640,10682368,-3145728,192,4 + dd 16777216,19398656,7536640,10682368,-2424832,192,4 + dd -8519680,-5636096,15925248,19070976,-2949120,188,4 + dd 8257536,11141120,15925248,19070976,-1638400,192,4 + dd 0,2621440,-21823488,-18677760,-3080192,189,4 + dd -12910592,-9633792,-17629184,-14483456,-1441792,187,4 + dd 12779520,15007744,-17629184,-14483456,-3276800,193,4 + dd -16908288,-14024704,-13434880,-10289152,-2949120,187,4 + dd 16580608,19595264,-13434880,-10289152,-2228224,187,4 + dd -21037056,-18284544,-851968,2293760,-3014656,189,4 + dd -16777216,-14155776,11730944,14876672,-2424832,193,4 + dd 16711680,19464192,11730944,14876672,-1703936,193,4 + dd -12386304,-10158080,15925248,19070976,-3276800,187,4 + dd 12713984,15073280,15925248,19070976,-3211264,193,4 + dd -196608,2818048,20119552,23265280,-1572864,191,4 + dd -4456448,-1310720,-21823488,-18677760,-1507328,188,4 + dd 3866624,7143424,-21823488,-18677760,-2752512,190,4 + dd -20905984,-18415616,-5046272,-1900544,-1835008,188,4 + dd 20905984,23658496,-5046272,-1900544,-2359296,190,4 + dd -4063232,-1703936,20119552,23265280,-1900544,190,4 + dd 4259840,6750208,20119552,23265280,-3145728,192,4 + dd 20447232,21495808,-9175040,-7864320,1966080,200,2 + dd 20643840,21299200,-8978432,-8060928,2424832,200,2 + dd -8323072,-5832704,-21823488,-18677760,-1835008,187,4 + dd 8388608,11010048,-21823488,-18677760,-2424832,191,4 + dd -20840448,-18481152,-9240576,-6094848,-2555904,187,4 + dd 20971520,23592960,-9240576,-6094848,-3080192,189,4 + dd -21168128,-18153472,7536640,10682368,-1572864,191,4 + dd 20643840,23920640,7536640,10682368,-2097152,193,4 + dd -8585216,-5570560,20119552,23265280,-2228224,189,4 + dd 8126464,11272192,20119552,23265280,-2818048,193,4 + dd -16777216,-14155776,-17629184,-14483456,-1769472,186,4 + dd 16646144,19529728,-17629184,-14483456,-2949120,186,4 + dd -16842752,-14090240,15925248,19070976,-1703936,186,4 + dd 16580608,19595264,15925248,19070976,-2883584,186,4 + dd -12845056,-9699328,-21823488,-18677760,-2162688,186,4 + dd 12255232,15532032,-21823488,-18677760,-2097152,192,4 + dd -20774912,-18546688,-13434880,-10289152,-3276800,186,4 + dd 21102592,23461888,-13434880,-10289152,-1900544,188,4 + dd -21299200,-18022400,11730944,14876672,-2752512,192,4 + dd 21168128,23396352,11730944,14876672,-3276800,186,4 + dd -12451840,-10092544,20119552,23265280,-2555904,188,4 + dd 12648448,15138816,20119552,23265280,-2490368,186,4 + dd 22740992,23789568,-11468800,-10158080,4325376,201,2 + dd 22937600,23592960,-11272192,-10354688,4784128,201,2 + dd 131072,2490368,-26017792,-22872064,-1900544,188,4 + dd -25493504,-22216704,-851968,2293760,-1441792,188,4 + dd -327680,2949120,24313856,27459584,-2752512,192,4 + dd -4390912,-1376256,-26017792,-22872064,-2228224,187,4 + dd 3997696,7012352,-26017792,-22872064,-1572864,189,4 + dd -25427968,-22282240,-5046272,-1900544,-2162688,187,4 + dd 24772608,28180480,-5046272,-1900544,-2031616,191,4 + dd -4194304,-1572864,24313856,27459584,-3080192,191,4 + dd 4194304,6815744,24313856,27459584,-2424832,193,4 + dd 25034752,26083328,-8847360,-7536640,3145728,202,2 + dd 25231360,25886720,-8650752,-7733248,3604480,202,2 + dd 22085632,23134208,-15400960,-14090240,3604480,202,2 + dd 22282240,22937600,-15204352,-14286848,4063232,202,2 + dd -8257536,-5898240,-26017792,-22872064,-2555904,186,4 + dd 8454144,10944512,-26017792,-22872064,-3145728,190,4 + dd -25362432,-22347776,-9240576,-6094848,-2883584,186,4 + dd 24838144,28114944,-9240576,-6094848,-2752512,190,4 + dd -25034752,-22675456,7536640,10682368,-1900544,190,4 + dd 25165824,27787264,7536640,10682368,-1769472,186,4 + dd -8650752,-5505024,24313856,27459584,-1507328,190,4 + dd 8060928,11337728,24313856,27459584,-2097152,186,4 + dd -16711680,-14221312,-21823488,-18677760,-2490368,193,4 + dd 16777216,19398656,-21823488,-18677760,-1769472,193,4 + dd -21299200,-18022400,-17629184,-14483456,-2097152,193,4 + dd 21168128,23396352,-17629184,-14483456,-2621440,187,4 + dd -21364736,-17956864,15925248,19070976,-2031616,193,4 + dd 21102592,23461888,15925248,19070976,-2555904,187,4 + dd -16973824,-13959168,20119552,23265280,-2883584,187,4 + dd 16515072,19660800,20119552,23265280,-2162688,187,4 + dd -12779520,-9764864,-26017792,-22872064,-2883584,193,4 + dd 12320768,15466496,-26017792,-22872064,-2818048,191,4 + dd -25231360,-22478848,-13434880,-10289152,-1703936,193,4 + dd 24969216,27983872,-13434880,-10289152,-1572864,189,4 + dd -25165824,-22544384,11730944,14876672,-3080192,191,4 + dd 25034752,27918336,11730944,14876672,-2949120,187,4 + dd -12517376,-10027008,24313856,27459584,-1835008,189,4 + dd 12582912,15204352,24313856,27459584,-1769472,187,4 + dd 196608,2424832,-30212096,-27066368,-2621440,187,4 + dd -29360128,-26738688,-851968,2293760,-1769472,187,4 + dd -393216,3014656,28508160,31653888,-2031616,193,4 + dd -4325376,-1441792,-30212096,-27066368,-2949120,186,4 + dd 4063232,6946816,-30212096,-27066368,-2293760,188,4 + dd -21233664,-18087936,-21823488,-18677760,-2818048,192,4 + dd 20643840,23920640,-21823488,-18677760,-1441792,186,4 + dd -29294592,-26804224,-5046272,-1900544,-2490368,186,4 + dd 29294592,32047104,-5046272,-1900544,-1703936,192,4 + dd -20840448,-18481152,20119552,23265280,-3211264,186,4 + dd 21037056,23527424,20119552,23265280,-1835008,188,4 + dd -4259840,-1507328,28508160,31653888,-2359296,192,4 + dd 4128768,6881280,28508160,31653888,-1703936,186,4 + dd 27328512,28377088,-13434880,-12124160,6225920,200,2 + dd 27525120,28180480,-13238272,-12320768,6684672,200,2 + dd -16646144,-14286848,-26017792,-22872064,-3211264,192,4 + dd 16842752,19333120,-26017792,-22872064,-2490368,192,4 + dd -25165824,-22544384,-17629184,-14483456,-2424832,192,4 + dd 25034752,27918336,-17629184,-14483456,-2293760,188,4 + dd -25231360,-22478848,15925248,19070976,-2359296,192,4 + dd 24969216,27983872,15925248,19070976,-2228224,188,4 + dd -17039360,-13893632,24313856,27459584,-2162688,188,4 + dd 16449536,19726336,24313856,27459584,-1441792,188,4 + dd -8192000,-5963776,-30212096,-27066368,-3276800,193,4 + dd 8585216,10813440,-30212096,-27066368,-1966080,189,4 + dd -29229056,-26869760,-9240576,-6094848,-3211264,193,4 + dd 29360128,31981568,-9240576,-6094848,-2424832,191,4 + dd -29556736,-26542080,7536640,10682368,-2228224,189,4 + dd 29032448,32309248,7536640,10682368,-1441792,187,4 + dd -8781824,-5373952,28508160,31653888,-2686976,191,4 + dd 8585216,10813440,28508160,31653888,-3276800,187,4 + dd 29622272,30670848,-10158080,-8847360,2752512,201,2 + dd 29818880,30474240,-9961472,-9043968,3211264,201,2 + dd 26017792,27066368,-17694720,-16384000,4718592,200,2 + dd 26214400,26869760,-17498112,-16580608,5177344,200,2 + dd -12648448,-9895936,-30212096,-27066368,-1703936,192,4 + dd 12451840,15335424,-30212096,-27066368,-1638400,190,4 + dd -29753344,-26345472,-13434880,-10289152,-2031616,192,4 + dd 29425664,31916032,-13434880,-10289152,-3145728,190,4 + dd -29622272,-26476544,11730944,14876672,-1507328,190,4 + dd 29556736,31784960,11730944,14876672,-2621440,188,4 + dd -12648448,-9895936,28508160,31653888,-3014656,190,4 + dd 12451840,15335424,28508160,31653888,-2949120,188,4 + dd -21102592,-18219008,-26017792,-22872064,-1638400,191,4 + dd 20709376,23855104,-26017792,-22872064,-2162688,193,4 + dd -25100288,-22609920,-21823488,-18677760,-3145728,191,4 + dd 25100288,27852800,-21823488,-18677760,-3014656,187,4 + dd -25296896,-22413312,20119552,23265280,-1638400,193,4 + dd 24903680,28049408,20119552,23265280,-1507328,189,4 + dd -20905984,-18415616,24313856,27459584,-2490368,187,4 + dd 20905984,23658496,24313856,27459584,-3014656,189,4 + dd -327680,2949120,-34406400,-31260672,-1441792,186,4 + dd -33882112,-30605312,-851968,2293760,-2097152,186,4 + dd 131072,2490368,32702464,35848192,-3211264,186,4 + dd -4194304,-1572864,-34406400,-31260672,-1769472,193,4 + dd 4128768,6881280,-34406400,-31260672,-3014656,187,4 + dd -17170432,-13762560,-30212096,-27066368,-2031616,191,4 + dd 16908288,19267584,-30212096,-27066368,-3211264,191,4 + dd -29687808,-26411008,-17629184,-14483456,-2752512,191,4 + dd 29556736,31784960,-17629184,-14483456,-1966080,189,4 + dd -33816576,-30670848,-5046272,-1900544,-2818048,193,4 + dd 33751040,35979264,-5046272,-1900544,-3276800,193,4 + dd -29753344,-26345472,15925248,19070976,-2686976,191,4 + dd 29491200,31850496,15925248,19070976,-1900544,189,4 + dd -17104896,-13828096,28508160,31653888,-1441792,189,4 + dd 16973824,19202048,28508160,31653888,-2621440,189,4 + dd -4325376,-1441792,32702464,35848192,-1638400,193,4 + dd 3997696,7012352,32702464,35848192,-2883584,187,4 + dd 30605312,31653888,-16384000,-15073280,1835008,201,2 + dd 30801920,31457280,-16187392,-15269888,2293760,201,2 + dd -8716288,-5439488,-34406400,-31260672,-2097152,192,4 + dd 7995392,11403264,-34406400,-31260672,-2686976,188,4 + dd -33685504,-30801920,-9240576,-6094848,-1638400,192,4 + dd 33226752,36503552,-9240576,-6094848,-2097152,192,4 + dd -33423360,-31064064,7536640,10682368,-2555904,188,4 + dd 33488896,36241408,7536640,10682368,-3014656,188,4 + dd -8192000,-5963776,32702464,35848192,-1966080,192,4 + dd 8519680,10878976,32702464,35848192,-2555904,188,4 + dd -24969216,-22740992,-26017792,-22872064,-1966080,190,4 + dd 25231360,27721728,-26017792,-22872064,-1835008,186,4 + dd -25427968,-22282240,24313856,27459584,-2818048,186,4 + dd 24772608,28180480,24313856,27459584,-2686976,190,4 + dd -12582912,-9961472,-34406400,-31260672,-2424832,191,4 + dd 12517376,15269888,-34406400,-31260672,-2359296,189,4 + dd -33619968,-30867456,-13434880,-10289152,-2359296,191,4 + dd 33292288,36438016,-13434880,-10289152,-2818048,191,4 + dd -33488896,-30998528,11730944,14876672,-1835008,189,4 + dd 33423360,36306944,11730944,14876672,-2293760,189,4 + dd -12713984,-9830400,32702464,35848192,-2293760,191,4 + dd 12386304,15400960,32702464,35848192,-2228224,189,4 + dd 36634624,38076416,-262144,1048576,-2883584,186,4 + dd -21037056,-18284544,-30212096,-27066368,-2359296,190,4 + dd 20774912,23789568,-30212096,-27066368,-2883584,192,4 + dd -29556736,-26542080,-21823488,-18677760,-1572864,190,4 + dd 28966912,32374784,-21823488,-18677760,-2686976,188,4 + dd -29163520,-26935296,20119552,23265280,-1966080,192,4 + dd 29360128,31981568,20119552,23265280,-3080192,190,4 + dd -20971520,-18350080,28508160,31653888,-1769472,188,4 + dd 20840448,23724032,28508160,31653888,-2293760,190,4 + dd 36634624,38076416,6291456,7602176,-2883584,186,4 + dd -17104896,-13828096,-34406400,-31260672,-2752512,190,4 + dd 16384000,19791872,-34406400,-31260672,-2031616,190,4 + dd -33554432,-30932992,-17629184,-14483456,-3080192,190,4 + dd 33423360,36306944,-17629184,-14483456,-1638400,190,4 + dd -33619968,-30867456,15925248,19070976,-3014656,190,4 + dd 33357824,36372480,15925248,19070976,-1572864,190,4 + dd -16580608,-14352384,32702464,35848192,-2621440,190,4 + dd 16908288,19267584,32702464,35848192,-1900544,190,4 + dd 38862848,40304640,-786432,524288,-2424832,187,4 + dd -25559040,-22151168,-30212096,-27066368,-2686976,189,4 + dd 25296896,27656192,-30212096,-27066368,-2555904,193,4 + dd -29491200,-26607616,-26017792,-22872064,-2293760,189,4 + dd 29097984,32243712,-26017792,-22872064,-1507328,187,4 + dd -29294592,-26804224,24313856,27459584,-3145728,193,4 + dd 29294592,32047104,24313856,27459584,-2359296,191,4 + dd -25493504,-22216704,28508160,31653888,-2097152,187,4 + dd 25362432,27590656,28508160,31653888,-1966080,191,4 + dd 38862848,40304640,6881280,8192000,-2424832,187,4 + dd -20971520,-18350080,-34406400,-31260672,-3080192,189,4 + dd 20905984,23658496,-34406400,-31260672,-1703936,191,4 + dd -33423360,-31064064,-21823488,-18677760,-1900544,189,4 + dd 33488896,36241408,-21823488,-18677760,-2359296,189,4 + dd -33685504,-30801920,20119552,23265280,-2293760,191,4 + dd 33226752,36503552,20119552,23265280,-2752512,191,4 + dd -21102592,-18219008,32702464,35848192,-2949120,189,4 + dd 20774912,23789568,32702464,35848192,-1572864,191,4 + dd 41091072,42532864,-1310720,0,-1966080,188,4 + dd 41091072,42532864,7471104,8781824,-1966080,188,4 + dd -29425664,-26673152,-30212096,-27066368,-3014656,188,4 + dd 29163520,32178176,-30212096,-27066368,-2228224,186,4 + dd -29360128,-26738688,28508160,31653888,-2424832,186,4 + dd 29229056,32112640,28508160,31653888,-1638400,192,4 + dd -25427968,-22282240,-34406400,-31260672,-1507328,188,4 + dd 25362432,27590656,-34406400,-31260672,-3276800,192,4 + dd -33357824,-31129600,-26017792,-22872064,-2621440,188,4 + dd 33554432,36175872,-26017792,-22872064,-3080192,188,4 + dd -33751040,-30736384,24313856,27459584,-1572864,192,4 + dd 33161216,36569088,24313856,27459584,-2031616,192,4 + dd -24969216,-22740992,32702464,35848192,-3276800,188,4 + dd 25231360,27721728,32702464,35848192,-3145728,192,4 + dd 43319296,44761088,-262144,1048576,-1507328,189,4 + dd 43319296,44761088,8060928,9371648,-1507328,189,4 + dd -29294592,-26804224,-34406400,-31260672,-1835008,187,4 + dd 29229056,32112640,-34406400,-31260672,-2949120,193,4 + dd -33882112,-30605312,-30212096,-27066368,-1441792,187,4 + dd 33685504,36044800,-30212096,-27066368,-1900544,187,4 + dd -33882112,-30605312,28508160,31653888,-2752512,193,4 + dd 33685504,36044800,28508160,31653888,-3211264,193,4 + dd -29425664,-26673152,32702464,35848192,-1703936,187,4 + dd 29097984,32243712,32702464,35848192,-2818048,193,4 + dd 45547520,46989312,-786432,524288,-2621440,190,4 + dd 45547520,46989312,6291456,7602176,-2621440,190,4 + dd 47775744,49217536,-1310720,0,-2162688,191,4 + dd -33816576,-30670848,-34406400,-31260672,-2162688,186,4 + dd 33751040,35979264,-34406400,-31260672,-2621440,186,4 + dd -33947648,-30539776,32702464,35848192,-2031616,186,4 + dd 33619968,36110336,32702464,35848192,-2490368,186,4 + dd 47775744,49217536,6881280,8192000,-2162688,191,4 + dd 50003968,51445760,-262144,1048576,-1703936,192,4 + dd 50003968,51445760,7471104,8781824,-1703936,192,4 + dd 52232192,53673984,-786432,524288,-2818048,193,4 + dd 52232192,53673984,8060928,9371648,-2818048,193,4 + dd 54460416,55902208,-1310720,0,-2359296,186,4 + dd 54460416,55902208,6291456,7602176,-2359296,186,4 + dd 56688640,58130432,-262144,1048576,-1900544,187,4 + dd 56688640,58130432,6881280,8192000,-1900544,187,4 +ROOF_PARK_COUNT equ 9 +roofCityParks3D LABEL DWORD + dd -3407872,-11075584 + dd 10747904,-1638400 + dd -1048576,720896 + dd 3670016,720896 + dd 13107200,720896 + dd -3407872,7798784 + dd 8388608,7798784 + dd -1048576,10158080 + dd -10485760,12517376 +ROOF_CITY_LAMP_COUNT equ 227 +roofCityLamps3D LABEL DWORD + dd -9515827,-11075584 + dd -9515827,-9109504 + dd -9515827,-7143424 + dd -9515827,-5177344 + dd -9515827,-3211264 + dd -9515827,-1245184 + dd -9515827,720896 + dd -9515827,2686976 + dd -9515827,6619136 + dd -9515827,8585216 + dd -9515827,10551296 + dd -9515827,12517376 + dd -7156531,-11075584 + dd -7156531,-9109504 + dd -7156531,-7143424 + dd -7156531,-5177344 + dd -7156531,-3211264 + dd -7156531,-1245184 + dd -7156531,720896 + dd -7156531,2686976 + dd -7156531,6619136 + dd -7156531,8585216 + dd -7156531,10551296 + dd -7156531,12517376 + dd -4797235,-11075584 + dd -4797235,-9109504 + dd -4797235,-7143424 + dd -4797235,-5177344 + dd -4797235,-3211264 + dd -4797235,-1245184 + dd -4797235,720896 + dd -4797235,2686976 + dd -4797235,6619136 + dd -4797235,8585216 + dd -4797235,10551296 + dd -4797235,12517376 + dd -2437939,-11075584 + dd -2437939,-9109504 + dd -2437939,-7143424 + dd -2437939,-5177344 + dd -2437939,-3211264 + dd -2437939,-1245184 + dd -2437939,720896 + dd -2437939,2686976 + dd -2437939,6619136 + dd -2437939,8585216 + dd -2437939,10551296 + dd -2437939,12517376 + dd -78643,-11075584 + dd -78643,-9109504 + dd -78643,-7143424 + dd -78643,-5177344 + dd -78643,-3211264 + dd -78643,-1245184 + dd -78643,720896 + dd -78643,2686976 + dd -78643,6619136 + dd -78643,8585216 + dd -78643,10551296 + dd -78643,12517376 + dd 2280653,-11075584 + dd 2280653,-9109504 + dd 2280653,-7143424 + dd 2280653,-5177344 + dd 2280653,-3211264 + dd 2280653,-1245184 + dd 2280653,720896 + dd 2280653,2686976 + dd 2280653,6619136 + dd 2280653,8585216 + dd 2280653,10551296 + dd 2280653,12517376 + dd 4639949,-11075584 + dd 4639949,-9109504 + dd 4639949,-7143424 + dd 4639949,-5177344 + dd 4639949,-3211264 + dd 4639949,-1245184 + dd 4639949,720896 + dd 4639949,6619136 + dd 4639949,8585216 + dd 4639949,10551296 + dd 4639949,12517376 + dd 6999245,-11075584 + dd 6999245,-9109504 + dd 6999245,-7143424 + dd 6999245,-5177344 + dd 6999245,-3211264 + dd 6999245,-1245184 + dd 6999245,720896 + dd 6999245,6619136 + dd 6999245,8585216 + dd 6999245,10551296 + dd 6999245,12517376 + dd 9358541,-11075584 + dd 9358541,-9109504 + dd 9358541,-7143424 + dd 9358541,-5177344 + dd 9358541,-3211264 + dd 9358541,-1245184 + dd 9358541,720896 + dd 9358541,6619136 + dd 9358541,8585216 + dd 9358541,10551296 + dd 9358541,12517376 + dd 11717837,-11075584 + dd 11717837,-9109504 + dd 11717837,-7143424 + dd 11717837,-5177344 + dd 11717837,-3211264 + dd 11717837,-1245184 + dd 11717837,720896 + dd 11717837,6619136 + dd 11717837,8585216 + dd 11717837,10551296 + dd 11717837,12517376 + dd 14077133,-11075584 + dd 14077133,-9109504 + dd 14077133,-7143424 + dd 14077133,-5177344 + dd 14077133,-3211264 + dd 14077133,-1245184 + dd 14077133,720896 + dd 14077133,6619136 + dd 14077133,8585216 + dd 14077133,10551296 + dd 14077133,12517376 + dd -11141120,3833856 + dd -11141120,5079040 + dd -9961472,3833856 + dd -9961472,5079040 + dd -8781824,3833856 + dd -8781824,5079040 + dd -7602176,3833856 + dd -7602176,5079040 + dd -6422528,3833856 + dd -6422528,5079040 + dd -5242880,3833856 + dd -5242880,5079040 + dd -4063232,3833856 + dd -4063232,5079040 + dd -2883584,3833856 + dd -2883584,5079040 + dd -1703936,3833856 + dd -1703936,5079040 + dd -524288,3833856 + dd -524288,5079040 + dd 655360,3833856 + dd 655360,5079040 + dd 1835008,3833856 + dd 1835008,5079040 + dd 3014656,3833856 + dd 3014656,5079040 + dd 4194304,3833856 + dd 4194304,5079040 + dd 5373952,5079040 + dd 6553600,5079040 + dd 7733248,5079040 + dd 8912896,5079040 + dd 10092544,5079040 + dd 11272192,5079040 + dd 12451840,5079040 + dd 13631488,5079040 + dd 5636096,1769472 + dd 5636096,5111808 + dd 6815744,1769472 + dd 6815744,5111808 + dd 7995392,1769472 + dd 7995392,5111808 + dd 9175040,1769472 + dd 9175040,5111808 + dd 10354688,1769472 + dd 10354688,5111808 + dd 11534336,1769472 + dd 11534336,5111808 + dd 12713984,1769472 + dd 12713984,5111808 + dd 13893632,1769472 + dd 13893632,5111808 + dd 15073280,1769472 + dd 15073280,5111808 + dd 16252928,1769472 + dd 16252928,5111808 + dd 17432576,1769472 + dd 17432576,5111808 + dd 18612224,1769472 + dd 18612224,5111808 + dd 19791872,1769472 + dd 19791872,5111808 + dd 20971520,1769472 + dd 20971520,5111808 + dd 22151168,1769472 + dd 22151168,5111808 + dd 23330816,1769472 + dd 23330816,5111808 + dd 24510464,1769472 + dd 24510464,5111808 + dd 25690112,1769472 + dd 25690112,5111808 + dd 26869760,1769472 + dd 26869760,5111808 + dd 28049408,1769472 + dd 28049408,5111808 + dd 29229056,1769472 + dd 29229056,5111808 + dd 30408704,1769472 + dd 30408704,5111808 + dd 31588352,1769472 + dd 31588352,5111808 + dd 32768000,1769472 + dd 32768000,5111808 + dd 33947648,1769472 + dd 33947648,5111808 + dd 35127296,1769472 + dd 35127296,5111808 + dd 36306944,1769472 + dd 36306944,5111808 + dd 37486592,1769472 + dd 37486592,5111808 + dd 38666240,1769472 + dd 38666240,5111808 + dd 39845888,1769472 + dd 39845888,5111808 + dd 41025536,1769472 + dd 41025536,5111808 + dd 42205184,1769472 + dd 42205184,5111808 +ROOF_LANDMARK_PARTS equ 26 +roofLandmarkParts3D LABEL DWORD + dd 0,131072,2490368,6619136,8978432,-3932160,-1441792,193 + dd 0,524288,2097152,7012352,8585216,-1441792,-1048576,168 + dd 0,1966080,2011955,7798784,7844659,-1048576,-655360,168 + dd 0,1916194,1962069,8049579,8095455,-1048576,-655360,168 + dd 0,1774130,1820005,8262194,8308069,-1048576,-655360,168 + dd 0,1561515,1607391,8404258,8450133,-1048576,-655360,168 + dd 0,1310720,1356595,8454144,8500019,-1048576,-655360,168 + dd 0,1059925,1105800,8404258,8450133,-1048576,-655360,168 + dd 0,847310,893186,8262194,8308069,-1048576,-655360,168 + dd 0,705246,751122,8049579,8095455,-1048576,-655360,168 + dd 0,655360,701235,7798784,7844659,-1048576,-655360,168 + dd 0,705246,751122,7547989,7593864,-1048576,-655360,168 + dd 0,847310,893186,7335374,7381250,-1048576,-655360,168 + dd 0,1059925,1105800,7193310,7239186,-1048576,-655360,168 + dd 0,1310720,1356595,7143424,7189299,-1048576,-655360,168 + dd 0,1561515,1607391,7193310,7239186,-1048576,-655360,168 + dd 0,1774130,1820005,7335374,7381250,-1048576,-655360,168 + dd 0,1916194,1962069,7547989,7593864,-1048576,-655360,168 + dd 0,524288,2097152,7012352,8585216,-655360,-602931,196 + dd 1,1310720,7798784,-616038,773325,773325,655360,196 + dd 0,1232077,1389363,7720141,7877427,0,262144,166 + dd 0,1297613,1323827,7785677,7811891,262144,655360,166 + dd 0,9830400,11665408,-196608,1638400,-3932160,-1703936,191 + dd 0,10354688,11141120,327680,1114112,-1703936,-524288,206 + dd 0,10485760,11010048,458752,983040,-524288,262144,191 + dd 2,10747904,720896,262144,235930,235930,2621440,166 +roofFog3D db 0,1,1,2,3,4,4,5,6,6,7,8,8,9,10,10,11,11,12,13,13,14,15,15,16,16,17,18,18,19,19,20 + db 20,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,30,30,31,31,32,32,33,33,34,34,35,35,36,36 + db 37,37,38,38,38,39,39,40,40,41,41,42,42,42,43,43,44,44,44,45,45,46,46,46,47,47,48,48,48,49,49,50 + db 50,50,51,51,51,52,52,53,53,53,54,54,54,55,55,55,56,56,56,57,57,57,58,58,58,59,59,59,60,60,60,61 + db 61,61,62,62,62,63,63,63,63,64,64,64,65,65,65,65,66,66,66,67,67,67,68,68,68,68,69,69,69,69,70,70 + db 70,71,71,71,71,72,72,72,72,73,73,73,73,74,74,74,74,75,75,75,75,76,76,76,76,77,77,77,77,77,78,78 + db 78,78,79,79,79,79,80,80,80,80,80,81,81,81,81,82,82,82,82,82,83,83,83,83,83,84,84,84,84,84,85,85 + db 85,85,85,86,86,86,86,86,87,87,87,87,87,88,88,88,88,88,89,89,89,89,89,89,90,90,90,90,90,91,91,91 + db 91,91,91,92,92,92,92,92,92,93,93,93,93,93,93,94,94,94,94,94,94,95,95,95,95,95,95,96,96,96,96,96 + db 96,97,97,97,97,97,97,97,98,98,98,98,98,98,98,99,99,99,99,99,99,99,100,100,100,100,100,100,100,101,101,101 + db 101,101,101,101,102,102,102,102,102,102,102,103,103,103,103,103,103,103,103,104,104,104,104,104,104,104,104,105,105,105,105,105 + db 105,105,105,106,106,106,106,106,106,106,106,107,107,107,107,107,107,107,107,108,108,108,108,108,108,108,108,108,109,109,109,109 + db 109,109,109,109,109,110,110,110,110,110,110,110,110,110,111,111,111,111,111,111,111,111,111,112,112,112,112,112,112,112,112,112 + db 112,113,113,113,113,113,113,113,113,113,113,114,114,114,114,114,114,114,114,114,114,114,115,115,115,115,115,115,115,115,115,115 + db 116,116,116,116,116,116,116,116,116,116,116,117,117,117,117,117,117,117,117,117,117,117,118,118,118,118,118,118,118,118,118,118 + db 118,118,119,119,119,119,119,119,119,119,119,119,119,119,120,120,120,120,120,120,120,120,120,120,120,120,120,121,121,121,121,121 + db 121,121,121,121,121,121,121,121,122,122,122,122,122,122,122,122,122,122,122,122,122,123,123,123,123,123,123,123,123,123,123,123 + db 123,123,123,124,124,124,124,124,124,124,124,124,124,124,124,124,124,125,125,125,125,125,125,125,125,125,125,125,125,125,125,125 + db 126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,126,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127,127 + db 128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,128,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129,129 + db 129,129,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,130,131,131,131,131,131,131,131,131,131,131,131,131 + db 131,131,131,131,131,131,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,132,133,133,133,133,133,133 + db 133,133,133,133,133,133,133,133,133,133,133,133,133,133,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134,134 + db 134,134,134,134,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,135,136,136,136,136,136,136 + db 136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,136,137,137,137,137,137,137,137,137,137,137,137,137,137,137,137 + db 137,137,137,137,137,137,137,137,137,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138,138 + db 138,138,138,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,139,140,140 + db 140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,140,141,141,141,141,141,141 + db 141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,141,142,142,142,142,142,142,142,142,142 + db 142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,142,143,143,143,143,143,143,143,143,143 + db 143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,143,144,144,144,144,144,144,144,144,144 + db 144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,144,145,145,145,145,145,145 +.code diff --git a/src_graphics/rooftop_gl.inc b/src_graphics/rooftop_gl.inc new file mode 100644 index 0000000..f37d6e0 --- /dev/null +++ b/src_graphics/rooftop_gl.inc @@ -0,0 +1,1118 @@ +RoofGLProc3D PROC procName:DWORD,alternate:DWORD + invoke wglGetProcAddress,procName + cmp eax,3 + jbe roof_gl_proc_alternate + cmp eax,-1 + jne roof_gl_proc_done +roof_gl_proc_alternate: + invoke wglGetProcAddress,alternate + cmp eax,3 + jbe roof_gl_proc_missing + cmp eax,-1 + jne roof_gl_proc_done +roof_gl_proc_missing: + xor eax,eax +roof_gl_proc_done: + ret +RoofGLProc3D ENDP + +LoadRoofMesh3D PROC uses ebx esi edi slot:DWORD,path:DWORD + local io:DWORD,allocation:DWORD + mov esi,slot + cmp DWORD PTR [esi+12],0 + jne roof_mesh_ready + invoke CreateFileA,path,80000000h,1,0,3,80h,0 + cmp eax,-1 + je roof_mesh_failed + mov ebx,eax + invoke ReadFile,ebx,esi,12,ADDR io,0 + test eax,eax + jz roof_mesh_close + cmp io,12 + jne roof_mesh_close + cmp DWORD PTR [esi],314D4752h + jne roof_mesh_close + mov eax,[esi+4] + test eax,eax + jz roof_mesh_close + cmp eax,65000 + ja roof_mesh_close + imul eax,100 + cmp eax,[esi+8] + jne roof_mesh_close + invoke GetFileSize,ebx,0 + sub eax,12 + cmp eax,[esi+8] + jne roof_mesh_close + invoke GetProcessHeap + invoke HeapAlloc,eax,0,[esi+8] + test eax,eax + jz roof_mesh_close + mov allocation,eax + invoke ReadFile,ebx,allocation,[esi+8],ADDR io,0 + test eax,eax + jz roof_mesh_free + mov eax,io + cmp eax,[esi+8] + jne roof_mesh_free + mov edi,allocation + mov ecx,[esi+4] +roof_mesh_validate: + mov eax,[edi] + cmp eax,ROOF_DECK + jb roof_mesh_free + cmp eax,209 + ja roof_mesh_free + add edi,100 + dec ecx + jnz roof_mesh_validate + invoke CloseHandle,ebx + mov eax,allocation + mov [esi+12],eax +roof_mesh_ready: + mov eax,1 + ret +roof_mesh_free: + invoke GetProcessHeap + invoke HeapFree,eax,0,allocation +roof_mesh_close: + invoke CloseHandle,ebx +roof_mesh_failed: + xor eax,eax + ret +LoadRoofMesh3D ENDP + +FreeRoofMesh3D PROC uses esi slot:DWORD + mov esi,slot + cmp DWORD PTR [esi+12],0 + je @F + invoke GetProcessHeap + invoke HeapFree,eax,0,[esi+12] + mov DWORD PTR [esi+12],0 +@@: + ret +FreeRoofMesh3D ENDP + +UploadRoofGLTexture3D PROC pixels:DWORD,tw:DWORD,th:DWORD,texture:DWORD + invoke glBindTexture,0DE1h,texture + invoke glTexParameteri,0DE1h,2801h,2601h + invoke glTexParameteri,0DE1h,2800h,2601h + invoke glTexParameteri,0DE1h,2802h,2901h + invoke glTexParameteri,0DE1h,2803h,2901h + invoke glTexImage2D,0DE1h,0,8058h,tw,th,0,80E1h,1401h,pixels + cmp roofGLMipmap3D,0 + je @F + push 0DE1h + call roofGLMipmap3D + invoke glTexParameteri,0DE1h,2801h,2703h +@@: + ret +UploadRoofGLTexture3D ENDP + +UploadRoofGLSky3D PROC uses ebx esi edi pack:DWORD,texture:DWORD + local allocation:DWORD,tile:DWORD,row:DWORD + invoke GetProcessHeap + invoke HeapAlloc,eax,0,2048*512*4 + test eax,eax + jz roof_gl_sky_failed + mov allocation,eax + mov tile,0 +roof_gl_sky_tile: + mov row,0 +roof_gl_sky_row: + mov esi,pack + mov eax,tile + shl eax,20 + add eax,[esi].AnimPack3D.pixels + mov edx,row + shl edx,11 + add eax,edx + mov esi,eax + mov eax,row + shl eax,13 + add eax,allocation + mov edx,tile + shl edx,11 + add eax,edx + mov edi,eax + mov ecx,512 + rep movsd + inc row + cmp row,512 + jb roof_gl_sky_row + inc tile + cmp tile,4 + jb roof_gl_sky_tile + invoke UploadRoofGLTexture3D,allocation,2048,512,texture + invoke glTexParameteri,0DE1h,2803h,812Fh + invoke GetProcessHeap + invoke HeapFree,eax,0,allocation + mov eax,1 + ret +roof_gl_sky_failed: + xor eax,eax + ret +UploadRoofGLSky3D ENDP + +BuildRoofGLList3D PROC uses ebx esi edi mesh:DWORD,theme:DWORD,emission:DWORD,listId:DWORD + local count:DWORD,currentMat:DWORD,inside:DWORD,vertex:DWORD,texture:DWORD + mov esi,mesh + mov eax,[esi+4] + mov count,eax + mov esi,[esi+12] + mov currentMat,-1 + mov inside,0 + invoke glNewList,listId,1300h +roof_gl_list_triangle: + mov ebx,[esi] + cmp ebx,currentMat + je roof_gl_list_vertices + cmp inside,0 + je @F + invoke glEnd + mov inside,0 +@@: + mov currentMat,ebx + sub ebx,ROOF_DECK + mov eax,roofHDDayTextures3D[ebx*4] + cmp theme,0 + je @F + mov eax,roofHDNightTextures3D[ebx*4] +@@: + cmp emission,0 + je @F + mov eax,roofHDEmissionTextures3D[ebx*4] +@@: + mov texture,eax + test eax,eax + jz roof_gl_list_skip + cmp currentMat,ROOF_CITY_WATER + je roof_gl_list_skip + invoke glBindTexture,0DE1h,texture + cmp emission,0 + jne roof_gl_list_begin + invoke glMaterialfv,408h,1600h,OFFSET roofGLEmissionZero3D + cmp theme,0 + je roof_gl_list_surface + cmp currentMat,ROOF_LIGHT + jne @F + invoke glMaterialfv,408h,1600h,OFFSET roofGLEmissionLamp3D +@@: + cmp currentMat,ROOF_BRASS + jne roof_gl_list_surface + invoke glMaterialfv,408h,1600h,OFFSET roofGLEmissionGold3D +roof_gl_list_surface: + cmp currentMat,208 + jne roof_gl_list_opaque + invoke glDisable,0B50h + invoke glEnable,0B60h + invoke glFogfv,0B66h,OFFSET roofGLFogWhite3D + invoke glEnable,0BE2h + invoke glBlendFunc,306h,0 + invoke glDepthMask,0 + jmp roof_gl_list_begin +roof_gl_list_opaque: + invoke glDisable,8037h + cmp currentMat,ROOF_CITY_ROAD + jne @F + invoke glEnable,8037h + invoke glPolygonOffset,0BF800000h,0BF800000h +@@: + invoke glEnable,0B50h + invoke glEnable,0B60h + invoke glDisable,0BE2h + invoke glDepthMask,1 +roof_gl_list_begin: + invoke glBegin,4 + mov inside,1 +roof_gl_list_vertices: + cmp inside,0 + je roof_gl_list_skip + lea edi,[esi+4] + mov vertex,3 +roof_gl_list_vertex: + invoke glNormal3f,[edi+12],[edi+16],[edi+20] + invoke glTexCoord2f,[edi+24],[edi+28] + invoke glVertex3f,[edi],[edi+4],[edi+8] + add edi,32 + dec vertex + jnz roof_gl_list_vertex +roof_gl_list_skip: + add esi,100 + dec count + jnz roof_gl_list_triangle + cmp inside,0 + je @F + invoke glEnd +@@: + invoke glEndList + ret +BuildRoofGLList3D ENDP + +InitRoofHD3D PROC uses ebx esi edi + local maxTexture:DWORD,oldTheme:DWORD,allocation:DWORD,model:DWORD,theme:DWORD + cmp roofHDReady3D,0 + jne roof_hd_init_status + mov roofHDReady3D,-1 + mov roofHDStage3D,1 + cmp roofSkyArt3D.pixels,0 + je roof_hd_init_failed + cmp roofNightArt3D.pixels,0 + je roof_hd_init_failed + cmp roofCityArt3D.pixels,0 + je roof_hd_init_failed + cmp roofCityNightArt3D.pixels,0 + je roof_hd_init_failed + cmp roofMaterialArt3D.pixels,0 + je roof_hd_init_failed + cmp roofDetailArt3D.pixels,0 + je roof_hd_init_failed + cmp roofLightingArt3D.pixels,0 + je roof_hd_init_failed + cmp roofLightingArt3D.frames,3 + jb roof_hd_init_failed + invoke GetModuleHandleA,0 + mov roofHDModule3D,eax + mov roofHDClass3D[16],eax + invoke RegisterClassA,OFFSET roofHDClass3D + invoke CreateWindowExA,0,OFFSET roofHDClassName3D,OFFSET roofHDTitle3D,86000000h,0,0,ROOF_HD_W,ROOF_HD_H,0,0,roofHDModule3D,0 + test eax,eax + jz roof_hd_init_failed + mov roofHDWindow3D,eax + invoke GetDC,eax + mov roofHDDC3D,eax + invoke ChoosePixelFormat,eax,OFFSET roofHDPixelFormat3D + test eax,eax + jz roof_hd_init_failed + invoke SetPixelFormat,roofHDDC3D,eax,OFFSET roofHDPixelFormat3D + test eax,eax + jz roof_hd_init_failed + invoke wglCreateContext,roofHDDC3D + test eax,eax + jz roof_hd_init_failed + mov roofHDRC3D,eax + invoke wglMakeCurrent,roofHDDC3D,eax + test eax,eax + jz roof_hd_init_failed + mov roofHDStage3D,2 + xor esi,esi +roof_hd_load_gl_proc: + invoke RoofGLProc3D,roofGLProcNames3D[esi*4],roofGLProcExtNames3D[esi*4] + mov edi,roofGLProcSlots3D[esi*4] + mov [edi],eax + test eax,eax + jnz roof_hd_gl_proc_ready + cmp esi,10 + jne roof_hd_init_failed +roof_hd_gl_proc_ready: + inc esi + cmp esi,11 + jb roof_hd_load_gl_proc + invoke glGetIntegerv,0D33h,ADDR maxTexture + cmp maxTexture,2048 + jb roof_hd_init_failed + mov roofHDStage3D,3 + push OFFSET roofHDFBO3D + push 1 + call roofGLGenFBO3D + push roofHDFBO3D + push 8D40h + call roofGLBindFBO3D + invoke glGenTextures,1,OFFSET roofHDColorTex3D + invoke glBindTexture,0DE1h,roofHDColorTex3D + invoke glTexParameteri,0DE1h,2801h,2601h + invoke glTexImage2D,0DE1h,0,8058h,ROOF_HD_W,ROOF_HD_H,0,1908h,1401h,0 + push 0 + push roofHDColorTex3D + push 0DE1h + push 8CE0h + push 8D40h + call roofGLAttachTexture3D + push OFFSET roofHDDepth3D + push 1 + call roofGLGenRBO3D + push roofHDDepth3D + push 8D41h + call roofGLBindRBO3D + push ROOF_HD_H + push ROOF_HD_W + push 81A6h + push 8D41h + call roofGLStoreRBO3D + push roofHDDepth3D + push 8D41h + push 8D00h + push 8D40h + call roofGLAttachRBO3D + push 8D40h + call roofGLCheckFBO3D + cmp eax,8CD5h + jne roof_hd_init_failed + invoke glDrawBuffer,8CE0h + invoke glReadBuffer,8CE0h + invoke GetProcessHeap + invoke HeapAlloc,eax,8,ROOF_HD_W*ROOF_HD_H*4 + test eax,eax + jz roof_hd_init_failed + mov roofHDBuffer3D,eax + mov roofHDStage3D,4 + invoke LoadRoofMesh3D,OFFSET roofHDMeshScene3D,OFFSET roofHDScenePath3D + test eax,eax + jz roof_hd_init_failed + invoke LoadRoofMesh3D,OFFSET roofHDMeshCar3D,OFFSET roofHDCarPath3D + test eax,eax + jz roof_hd_init_failed + invoke LoadRoofMesh3D,OFFSET roofHDMeshBoat3D,OFFSET roofHDBoatPath3D + test eax,eax + jz roof_hd_init_failed + lea edi,roofHDUsedMaterials3D + xor eax,eax + mov ecx,ROOF_GL_TEXTURES + rep stosb + mov model,0 +roof_hd_collect_materials: + mov ebx,model + mov esi,roofHDMeshPtrs3D[ebx*4] + mov ecx,[esi+4] + mov esi,[esi+12] +roof_hd_collect_triangle: + mov eax,[esi] + sub eax,ROOF_DECK + mov roofHDUsedMaterials3D[eax],1 + add esi,100 + dec ecx + jnz roof_hd_collect_triangle + inc model + cmp model,3 + jb roof_hd_collect_materials + invoke glGenTextures,ROOF_GL_TEXTURES,OFFSET roofHDDayTextures3D + invoke glGenTextures,ROOF_GL_TEXTURES,OFFSET roofHDNightTextures3D + invoke glGenTextures,ROOF_GL_TEXTURES,OFFSET roofHDEmissionTextures3D + mov eax,roofNight3D + mov oldTheme,eax + mov theme,0 +roof_hd_upload_theme: + mov eax,theme + mov roofNight3D,eax + mov esi,ROOF_DECK +roof_hd_upload_material: + mov ebx,esi + sub ebx,ROOF_DECK + cmp roofHDUsedMaterials3D[ebx],0 + jne roof_hd_upload_used + mov roofHDDayTextures3D[ebx*4],0 + mov roofHDNightTextures3D[ebx*4],0 + jmp roof_hd_upload_next +roof_hd_upload_used: + cmp theme,0 + je roof_hd_upload_pixels + cmp esi,ROOF_FACADE + jb roof_hd_share_material + cmp esi,ROOF_FACADE+7 + jbe roof_hd_upload_pixels + cmp esi,ROOF_MODERN_FACADE + jb roof_hd_share_material + cmp esi,ROOF_MODERN_FACADE+7 + jbe roof_hd_upload_pixels +roof_hd_share_material: + mov eax,roofHDDayTextures3D[ebx*4] + mov roofHDNightTextures3D[ebx*4],eax + jmp roof_hd_upload_next +roof_hd_upload_pixels: + invoke ResolveRoofSurface3D,esi + mov edi,roofHDDayTextures3D[ebx*4] + cmp theme,0 + je @F + mov edi,roofHDNightTextures3D[ebx*4] +@@: + invoke UploadRoofGLTexture3D,eax,ecx,edx,edi +roof_hd_upload_next: + inc esi + cmp esi,ROOF_LAST_MATERIAL + jbe roof_hd_upload_material + mov esi,OFFSET roofLightingArt3D + mov eax,theme + shl eax,20 + add eax,[esi].AnimPack3D.pixels + mov edi,roofHDDayTextures3D[47*4] + cmp theme,0 + je @F + mov edi,roofHDNightTextures3D[47*4] +@@: + invoke UploadRoofGLTexture3D,eax,512,512,edi + invoke glTexParameteri,0DE1h,2802h,812Fh + invoke glTexParameteri,0DE1h,2803h,812Fh + inc theme + cmp theme,2 + jb roof_hd_upload_theme + mov roofHDDayTextures3D[48*4],0 + mov roofHDNightTextures3D[48*4],0 + mov roofHDStage3D,5 + invoke GetProcessHeap + invoke HeapAlloc,eax,0,512*512*4 + test eax,eax + jz roof_hd_restore_failed + mov allocation,eax + mov esi,ROOF_DECK +roof_hd_emission_material: + mov ebx,esi + sub ebx,ROOF_DECK + cmp roofHDUsedMaterials3D[ebx],0 + je roof_hd_emission_empty + cmp esi,ROOF_FACADE + jb roof_hd_emission_empty + cmp esi,ROOF_FACADE+7 + jbe roof_hd_emission_create + cmp esi,ROOF_MODERN_FACADE + jb roof_hd_emission_empty + cmp esi,ROOF_MODERN_FACADE+7 + ja roof_hd_emission_empty +roof_hd_emission_create: + invoke ResolveRoofSurface3D,esi + mov ebx,eax + mov edi,allocation + mov ecx,512*512 +roof_hd_emission_pixel: + mov eax,[ebx] + cmp eax,0FF000000h + jae @F + xor eax,eax +@@: + stosd + add ebx,4 + dec ecx + jnz roof_hd_emission_pixel + mov ebx,esi + sub ebx,ROOF_DECK + invoke UploadRoofGLTexture3D,allocation,512,512,roofHDEmissionTextures3D[ebx*4] + jmp roof_hd_emission_next +roof_hd_emission_empty: + mov ebx,esi + sub ebx,ROOF_DECK + ; No emission list binds these handles; zero is an explicit skipped material. + mov roofHDEmissionTextures3D[ebx*4],0 +roof_hd_emission_next: + inc esi + cmp esi,ROOF_LAST_MATERIAL + jbe roof_hd_emission_material + mov roofHDEmissionTextures3D[47*4],0 + mov eax,roofLightingArt3D.pixels + add eax,2*512*512*4 + invoke UploadRoofGLTexture3D,eax,512,512,roofHDEmissionTextures3D[48*4] + invoke glTexParameteri,0DE1h,2802h,812Fh + invoke glTexParameteri,0DE1h,2803h,812Fh + invoke GetProcessHeap + invoke HeapFree,eax,0,allocation + mov eax,oldTheme + mov roofNight3D,eax + invoke glGenTextures,2,OFFSET roofHDSkyTextures3D + invoke UploadRoofGLSky3D,OFFSET roofSkyArt3D,roofHDSkyTextures3D[0] + test eax,eax + jz roof_hd_init_failed + invoke UploadRoofGLSky3D,OFFSET roofNightArt3D,roofHDSkyTextures3D[4] + test eax,eax + jz roof_hd_init_failed + invoke glGenLists,9 + test eax,eax + jz roof_hd_init_failed + mov roofHDLists3D,eax + mov model,0 +roof_hd_compile_model: + mov ebx,model + mov edi,roofHDMeshPtrs3D[ebx*4] + imul ebx,3 + add ebx,roofHDLists3D + invoke BuildRoofGLList3D,edi,0,0,ebx + inc ebx + invoke BuildRoofGLList3D,edi,1,0,ebx + inc ebx + invoke BuildRoofGLList3D,edi,1,1,ebx + inc model + cmp model,3 + jb roof_hd_compile_model + invoke FreeRoofMesh3D,OFFSET roofHDMeshScene3D + invoke FreeRoofMesh3D,OFFSET roofHDMeshCar3D + invoke FreeRoofMesh3D,OFFSET roofHDMeshBoat3D + invoke CreateCompatibleDC,0 + test eax,eax + jz roof_hd_init_failed + mov roofHDOverlayDC3D,eax + invoke CreateDIBSection,eax,OFFSET roofHDOverlayBMI3D,0,OFFSET roofHDOverlayPixels3D,0,0 + test eax,eax + jz roof_hd_init_failed + mov roofHDOverlayBmp3D,eax + invoke SelectObject,roofHDOverlayDC3D,eax + mov roofHDOverlayOld3D,eax + invoke CreateCompatibleDC,0 + test eax,eax + jz roof_hd_init_failed + mov roofHDFinalDC3D,eax + invoke CreateDIBSection,eax,OFFSET roofHDBMI3D,0,OFFSET roofHDFinalPixels3D,0,0 + test eax,eax + jz roof_hd_init_failed + mov roofHDFinalBmp3D,eax + invoke SelectObject,roofHDFinalDC3D,eax + mov roofHDFinalOld3D,eax + invoke glGetError + test eax,eax + jnz roof_hd_init_failed + mov roofHDReady3D,1 + mov roofHDStage3D,0 + call WriteRoofRendererInfo3D +roof_hd_init_status: + xor eax,eax + cmp roofHDReady3D,1 + sete al + ret +roof_hd_restore_failed: + mov eax,oldTheme + mov roofNight3D,eax +roof_hd_init_failed: + call WriteRoofRendererInfo3D + call FreeRoofHD3D + mov roofHDReady3D,-1 + xor eax,eax + ret +InitRoofHD3D ENDP + +DrawRoofGLSky3D PROC uses ebx esi edi + local px:DWORD,x0:REAL4,x1:REAL4,u0:REAL4,u1:REAL4,v0:REAL4,v1:REAL4 + local skyWidth:DWORD,logicalHeight:DWORD + local fadeTop:REAL4,fadeBottom:REAL4,fadePixel:DWORD + mov skyWidth,2048 + invoke glDisable,0B44h + mov logicalHeight,480 + invoke glDisable,0B71h + invoke glDisable,0B50h + invoke glDisable,0B60h + invoke glDisable,0BE2h + invoke glEnable,0DE1h + invoke glDepthMask,0 + invoke glColor4f,3F800000h,3F800000h,3F800000h,3F800000h + invoke glMatrixMode,1701h + invoke glLoadIdentity + invoke glOrtho,DWORD PTR roofGLZeroD3D,DWORD PTR roofGLZeroD3D+4,DWORD PTR roofGLScreenWidthD3D,DWORD PTR roofGLScreenWidthD3D+4,DWORD PTR roofGLZeroD3D,DWORD PTR roofGLZeroD3D+4,DWORD PTR roofGLScreenHeightD3D,DWORD PTR roofGLScreenHeightD3D+4,DWORD PTR roofGLMinusOneD3D,DWORD PTR roofGLMinusOneD3D+4,DWORD PTR roofGLOneD3D,DWORD PTR roofGLOneD3D+4 + invoke glMatrixMode,1700h + invoke glLoadIdentity + mov eax,roofNight3D + invoke glBindTexture,0DE1h,roofHDSkyTextures3D[eax*4] + mov eax,HALF_H + sub eax,viewHorizon3D + mov px,eax + fild px + fidiv logicalHeight + fst v0 + fld1 + faddp st(1),st + fstp v1 + mov px,0 +roof_hd_sky_segment: + mov ebx,px + fild px + fstp x0 + add px,16 + fild px + fstp x1 + shr ebx,1 + mov eax,roofSkyColumns3D[ebx*4] + mov ecx,playerAngle + shl ecx,2 + add eax,ecx + mov skyWidth,2048 + mov ebx,eax + push eax + fild DWORD PTR [esp] + add esp,4 + fidiv skyWidth + fstp u0 + mov ebx,px + shr ebx,1 + cmp ebx,639 + jbe @F + mov ebx,639 +@@: + mov eax,roofSkyColumns3D[ebx*4] + mov ecx,playerAngle + shl ecx,2 + add eax,ecx + push eax + fild DWORD PTR [esp] + add esp,4 + fidiv skyWidth + fstp u1 + invoke glBegin,7 + invoke glTexCoord2f,DWORD PTR u0,DWORD PTR v1 + invoke glVertex3f,DWORD PTR x0,0,0 + invoke glTexCoord2f,DWORD PTR u1,DWORD PTR v1 + invoke glVertex3f,DWORD PTR x1,0,0 + invoke glTexCoord2f,DWORD PTR u1,DWORD PTR v0 + invoke glVertex3f,DWORD PTR x1,44700000h,0 + invoke glTexCoord2f,DWORD PTR u0,DWORD PTR v0 + invoke glVertex3f,DWORD PTR x0,44700000h,0 + invoke glEnd + cmp px,ROOF_HD_W + jb roof_hd_sky_segment + ; The atmospheric horizon blends into the same haze as distant geometry. + mov eax,viewHorizon3D + shl eax,1 + neg eax + add eax,ROOF_HD_H+120 + mov fadePixel,eax + fild fadePixel + fstp fadeTop + sub fadePixel,210 + fild fadePixel + fstp fadeBottom + invoke glDisable,0DE1h + invoke glEnable,0BE2h + invoke glBlendFunc,302h,303h + invoke glBegin,7 + invoke glColor4f,DWORD PTR roofGLFogCurrent3D[0],DWORD PTR roofGLFogCurrent3D[4],DWORD PTR roofGLFogCurrent3D[8],3F800000h + invoke glVertex3f,0,0,0 + invoke glVertex3f,44A00000h,0,0 + invoke glVertex3f,44A00000h,DWORD PTR fadeBottom,0 + invoke glVertex3f,0,DWORD PTR fadeBottom,0 + invoke glVertex3f,0,DWORD PTR fadeBottom,0 + invoke glVertex3f,44A00000h,DWORD PTR fadeBottom,0 + invoke glColor4f,DWORD PTR roofGLFogCurrent3D[0],DWORD PTR roofGLFogCurrent3D[4],DWORD PTR roofGLFogCurrent3D[8],0 + invoke glVertex3f,44A00000h,DWORD PTR fadeTop,0 + invoke glVertex3f,0,DWORD PTR fadeTop,0 + invoke glEnd + invoke glDisable,0BE2h + invoke glEnable,0DE1h + invoke glColor4f,3F800000h,3F800000h,3F800000h,3F800000h + ret +DrawRoofGLSky3D ENDP + +DrawRoofGLTraffic3D PROC uses ebx esi edi + local px:DWORD,py:DWORD,fx:REAL4,fy:REAL4,level:REAL4,angle:DWORD,listId:DWORD + xor esi,esi +roof_hd_car: + imul eax,esi,17000 + add eax,animationClock3D + mov ecx,393 + mul ecx + mov ecx,340*FP_1 + div ecx + sub edx,160*FP_1 + mov px,edx + mov py,-FP_1*59/10 + mov angle,0 + test esi,1 + jz @F + neg edx + mov px,edx + mov py,-FP_1*81/10 + mov angle,43340000h +@@: + cmp esi,4 + jb roof_hd_car_pose + mov eax,px + mov py,eax + mov px,FP_1*391/10 + mov angle,42B40000h + test esi,1 + jz roof_hd_car_pose + mov px,FP_1*369/10 + mov angle,43870000h +roof_hd_car_pose: + fild px + fmul roofGLInvFP3D + fstp fx + fild py + fmul roofGLInvFP3D + fstp fy + invoke glPushMatrix + invoke glTranslatef,DWORD PTR fx,0C2700000h,DWORD PTR fy + invoke glRotatef,angle,0,3F800000h,0 + mov ebx,roofHDLists3D + add ebx,3 + add ebx,roofNight3D + invoke glCallList,ebx + cmp roofNight3D,0 + je roof_hd_car_pop + invoke glDisable,0B50h + invoke glDisable,0B60h + invoke glEnable,0BE2h + invoke glBlendFunc,1,1 + invoke glEnable,8037h + invoke glPolygonOffset,0BF800000h,0BF800000h + invoke glDepthMask,0 + mov eax,roofHDLists3D + add eax,5 + invoke glCallList,eax + invoke glDisable,8037h + invoke glDepthMask,1 +roof_hd_car_pop: + invoke glPopMatrix + inc esi + cmp esi,8 + jb roof_hd_car + xor esi,esi +roof_hd_boat: + imul eax,esi,47000 + add eax,animationClock3D + mov ecx,98 + mul ecx + mov ecx,340*FP_1 + div ecx + sub edx,160*FP_1 + mov px,edx + mov py,68*FP_1 + cmp esi,3 + jb roof_hd_boat_pose + imul eax,esi,61000 + add eax,animationClock3D + mov ecx,98 + mul ecx + mov ecx,660*FP_1 + div ecx + add edx,90*FP_1 + mov px,edx + mov py,44*FP_1 + test esi,1 + jz roof_hd_boat_pose + mov py,63*FP_1 +roof_hd_boat_pose: + fild px + fmul roofGLInvFP3D + fstp fx + fild py + fmul roofGLInvFP3D + fstp fy + invoke glPushMatrix + invoke glTranslatef,DWORD PTR fx,0C2700000h,DWORD PTR fy + mov eax,roofHDLists3D + add eax,6 + add eax,roofNight3D + invoke glCallList,eax + invoke glPopMatrix + inc esi + cmp esi,6 + jb roof_hd_boat + ret +DrawRoofGLTraffic3D ENDP + +DrawRoofGLWater3D PROC uses ebx esi + local ticks:DWORD,flowX:REAL4,flowY:REAL4 + mov eax,animationClock3D + xor edx,edx + mov ecx,200000 + div ecx + mov ticks,edx + fild ticks + fdiv roofGLWaveDiv3D + fst flowX + fmul roofGLHalf3D + fstp flowY + invoke glMatrixMode,1702h + invoke glLoadIdentity + invoke glTranslatef,DWORD PTR flowX,DWORD PTR flowY,0 + invoke glMatrixMode,1700h + mov eax,roofHDDayTextures3D[(ROOF_CITY_WATER-ROOF_DECK)*4] + cmp roofNight3D,0 + je @F + mov eax,roofHDNightTextures3D[(ROOF_CITY_WATER-ROOF_DECK)*4] +@@: + invoke glBindTexture,0DE1h,eax + invoke glNormal3f,0,3F800000h,0 + invoke glBegin,7 + mov esi,OFFSET roofGLWaterVertices3D + mov ebx,8 +roof_gl_water_vertex: + invoke glTexCoord2f,[esi+12],[esi+16] + invoke glVertex3f,[esi],[esi+4],[esi+8] + add esi,20 + dec ebx + jnz roof_gl_water_vertex + invoke glEnd + invoke glMatrixMode,1702h + invoke glLoadIdentity + invoke glMatrixMode,1700h + ret +DrawRoofGLWater3D ENDP + +UpdateRoofGLFog3D PROC uses ebx esi + local channel:DWORD + mov esi,roofSkyArt3D.pixels + cmp roofNight3D,0 + je @F + mov esi,roofNightArt3D.pixels +@@: + mov eax,playerAngle + shl eax,2 + and eax,2047 + mov edx,eax + shr edx,9 + shl edx,20 + add esi,edx + and eax,511 + lea esi,[esi+eax*4+256*512*4] + movzx eax,BYTE PTR [esi+2] + mov channel,eax + fild channel + fdiv roofGLColorScale3D + fstp roofGLFogCurrent3D[0] + movzx eax,BYTE PTR [esi+1] + mov channel,eax + fild channel + fdiv roofGLColorScale3D + fstp roofGLFogCurrent3D[4] + movzx eax,BYTE PTR [esi] + mov channel,eax + fild channel + fdiv roofGLColorScale3D + fstp roofGLFogCurrent3D[8] + ret +UpdateRoofGLFog3D ENDP + +RenderRoofHD3D PROC uses ebx esi edi + local left:REAL8,right:REAL8,bottom:REAL8,top:REAL8 + local angle:REAL4,px:REAL4,py:REAL4,eye:REAL4,clock:DWORD + call UpdateRoofQuality3D + invoke InitRoofHD3D + test eax,eax + jz roof_hd_render_failed + mov eax,animationClock3D + xor edx,edx + mov ecx,33 + div ecx + mov clock,eax + cmp eax,roofHDRenderClock3D + jne roof_hd_render_refresh + mov eax,playerX + cmp eax,roofHDRenderX3D + jne roof_hd_render_refresh + mov eax,playerY + cmp eax,roofHDRenderY3D + jne roof_hd_render_refresh + mov eax,playerAngle + cmp eax,roofHDRenderAngle3D + jne roof_hd_render_refresh + mov eax,viewEye3D + cmp eax,roofHDRenderEye3D + jne roof_hd_render_refresh + mov eax,viewHorizon3D + cmp eax,roofHDRenderHorizon3D + jne roof_hd_render_refresh + mov eax,roofNight3D + cmp eax,roofHDRenderTheme3D + je roof_hd_render_ready +roof_hd_render_refresh: + invoke wglMakeCurrent,roofHDDC3D,roofHDRC3D + push roofHDFBO3D + push 8D40h + call roofGLBindFBO3D + invoke glViewport,0,0,roofRenderW3D,roofRenderH3D + invoke glDepthMask,1 + invoke glClearColor,0,0,0,3F800000h + invoke glClear,4100h + call UpdateRoofGLFog3D + call DrawRoofGLSky3D + invoke glEnable,0B71h + invoke glDepthFunc,203h + invoke glDepthMask,1 + invoke glEnable,0B44h + invoke glFrontFace,901h + invoke glCullFace,405h + invoke glEnable,0DE1h + invoke glEnable,0B50h + invoke glEnable,4000h + invoke glEnable,0BA1h + invoke glEnable,0B57h + invoke glColorMaterial,408h,1602h + invoke glColor4f,3F800000h,3F800000h,3F800000h,3F800000h + invoke glShadeModel,1D01h + invoke glEnable,0B60h + invoke glFogi,0B65h,801h + invoke glFogf,0B62h,3B2A64C3h + mov eax,OFFSET roofGLFogCurrent3D + mov ebx,OFFSET roofGLAmbientDay3D + mov esi,OFFSET roofGLSunColorDay3D + cmp roofNight3D,0 + je @F + mov ebx,OFFSET roofGLAmbientNight3D + mov esi,OFFSET roofGLSunColorNight3D +@@: + invoke glFogfv,0B66h,eax + invoke glLightModelfv,0B53h,ebx + invoke glLightfv,4000h,1201h,esi + fild viewHorizon3D + fidiv roofGLCameraH3D + fst top + fld1 + fsubp st(1),st + fstp bottom + invoke glMatrixMode,1701h + invoke glLoadIdentity + invoke glFrustum,DWORD PTR roofGLLeft3D,DWORD PTR roofGLLeft3D+4,DWORD PTR roofGLRight3D,DWORD PTR roofGLRight3D+4,DWORD PTR bottom,DWORD PTR bottom+4,DWORD PTR top,DWORD PTR top+4,DWORD PTR roofGLNear3D,DWORD PTR roofGLNear3D+4,DWORD PTR roofGLFar3D,DWORD PTR roofGLFar3D+4 + invoke glMatrixMode,1700h + invoke glLoadIdentity + fild playerAngle + fmul roofGLDegrees3D + fadd roofGLNinety3D + fstp angle + invoke glRotatef,DWORD PTR angle,0,3F800000h,0 + fild playerX + fmul roofGLInvFP3D + fchs + fstp px + fild playerY + fmul roofGLInvFP3D + fchs + fstp py + fild viewEye3D + fmul roofGLInvFP3D + fchs + fstp eye + invoke glTranslatef,DWORD PTR px,DWORD PTR eye,DWORD PTR py + invoke glLightfv,4000h,1203h,OFFSET roofGLSunDirection3D + invoke glMaterialfv,408h,1600h,OFFSET roofGLEmissionZero3D + invoke glDisable,0BE2h + call DrawRoofGLWater3D + mov eax,roofHDLists3D + add eax,roofNight3D + invoke glCallList,eax + cmp roofNight3D,0 + je roof_hd_render_traffic + invoke glDisable,0B50h + invoke glEnable,0B60h + invoke glFogfv,0B66h,OFFSET roofGLFogBlack3D + invoke glEnable,0BE2h + invoke glBlendFunc,1,1 + invoke glEnable,8037h + invoke glPolygonOffset,0BF800000h,0BF800000h + invoke glDepthMask,0 + mov eax,roofHDLists3D + add eax,2 + invoke glCallList,eax + invoke glDisable,8037h + invoke glDepthMask,1 +roof_hd_render_traffic: + invoke glFogfv,0B66h,OFFSET roofGLFogCurrent3D + call DrawRoofGLTraffic3D + invoke glReadPixels,0,0,roofRenderW3D,roofRenderH3D,80E1h,1401h,roofHDBuffer3D + invoke glGetError + test eax,eax + jnz roof_hd_render_failed + mov eax,playerX + mov roofHDRenderX3D,eax + mov eax,playerY + mov roofHDRenderY3D,eax + mov eax,playerAngle + mov roofHDRenderAngle3D,eax + mov eax,viewEye3D + mov roofHDRenderEye3D,eax + mov eax,viewHorizon3D + mov roofHDRenderHorizon3D,eax + mov eax,roofNight3D + mov roofHDRenderTheme3D,eax + mov eax,clock + mov roofHDRenderClock3D,eax +roof_hd_render_ready: + mov eax,1 + ret +roof_hd_render_failed: + call FreeRoofHD3D + mov roofHDReady3D,-1 + xor eax,eax + ret +RenderRoofHD3D ENDP + +PresentRoofHD3D PROC uses ebx esi edi dc:DWORD,left:DWORD,top:DWORD,viewW:DWORD,viewH:DWORD + cmp hallLevel3D,HALL_ROOF + jne roof_hd_present_fallback + cmp engineMode,MODE_WALK + jne roof_hd_present_fallback + call RenderRoofHD3D + test eax,eax + jz roof_hd_present_fallback + call ComposeRoofFrame3D + invoke StretchBlt,dc,left,top,viewW,viewH,roofHDFinalDC3D,0,0,roofRenderW3D,roofRenderH3D,SRCCOPY + ret +roof_hd_present_fallback: + xor eax,eax + ret +PresentRoofHD3D ENDP + +FreeRoofHD3D PROC uses ebx esi edi + cmp roofHDRC3D,0 + je roof_hd_free_windows + invoke wglMakeCurrent,roofHDDC3D,roofHDRC3D + cmp roofHDLists3D,0 + je @F + invoke glDeleteLists,roofHDLists3D,9 + mov roofHDLists3D,0 +@@: + invoke glDeleteTextures,ROOF_GL_TEXTURES,OFFSET roofHDDayTextures3D + invoke glDeleteTextures,ROOF_GL_TEXTURES,OFFSET roofHDNightTextures3D + invoke glDeleteTextures,ROOF_GL_TEXTURES,OFFSET roofHDEmissionTextures3D + invoke glDeleteTextures,2,OFFSET roofHDSkyTextures3D + cmp roofHDFBO3D,0 + je @F + cmp roofGLDeleteFBO3D,0 + je @F + push OFFSET roofHDFBO3D + push 1 + call roofGLDeleteFBO3D + mov roofHDFBO3D,0 +@@: + cmp roofHDDepth3D,0 + je @F + cmp roofGLDeleteRBO3D,0 + je @F + push OFFSET roofHDDepth3D + push 1 + call roofGLDeleteRBO3D + mov roofHDDepth3D,0 +@@: + invoke glDeleteTextures,1,OFFSET roofHDColorTex3D + invoke wglMakeCurrent,0,0 + invoke wglDeleteContext,roofHDRC3D + mov roofHDRC3D,0 +roof_hd_free_windows: + cmp roofHDFinalDC3D,0 + je @F + invoke SelectObject,roofHDFinalDC3D,roofHDFinalOld3D + invoke DeleteDC,roofHDFinalDC3D + mov roofHDFinalDC3D,0 +@@: + cmp roofHDFinalBmp3D,0 + je @F + invoke DeleteObject,roofHDFinalBmp3D + mov roofHDFinalBmp3D,0 +@@: + cmp roofHDOverlayDC3D,0 + je @F + invoke SelectObject,roofHDOverlayDC3D,roofHDOverlayOld3D + invoke DeleteDC,roofHDOverlayDC3D + mov roofHDOverlayDC3D,0 +@@: + cmp roofHDOverlayBmp3D,0 + je @F + invoke DeleteObject,roofHDOverlayBmp3D + mov roofHDOverlayBmp3D,0 +@@: + cmp roofHDDC3D,0 + je @F + invoke ReleaseDC,roofHDWindow3D,roofHDDC3D + mov roofHDDC3D,0 +@@: + cmp roofHDWindow3D,0 + je @F + invoke DestroyWindow,roofHDWindow3D + mov roofHDWindow3D,0 +@@: + cmp roofHDBuffer3D,0 + je @F + invoke GetProcessHeap + invoke HeapFree,eax,0,roofHDBuffer3D + mov roofHDBuffer3D,0 +@@: + invoke FreeRoofMesh3D,OFFSET roofHDMeshScene3D + invoke FreeRoofMesh3D,OFFSET roofHDMeshCar3D + invoke FreeRoofMesh3D,OFFSET roofHDMeshBoat3D + mov roofHDReady3D,0 + mov roofHDRenderClock3D,-1 + ret +FreeRoofHD3D ENDP diff --git a/src_graphics/rooftop_gl_api.inc b/src_graphics/rooftop_gl_api.inc new file mode 100644 index 0000000..edbb5fc --- /dev/null +++ b/src_graphics/rooftop_gl_api.inc @@ -0,0 +1,91 @@ +; Windows' system OpenGL entry points. All scene logic and calls are native MASM. +includelib opengl32.lib +includelib msimg32.lib +glBegin PROTO STDCALL :DWORD +glEnd PROTO STDCALL +glVertex3f PROTO STDCALL :DWORD,:DWORD,:DWORD +glNormal3f PROTO STDCALL :DWORD,:DWORD,:DWORD +glTexCoord2f PROTO STDCALL :DWORD,:DWORD +glColor4f PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD +glEnable PROTO STDCALL :DWORD +glDisable PROTO STDCALL :DWORD +glClear PROTO STDCALL :DWORD +glClearColor PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD +glViewport PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD +glMatrixMode PROTO STDCALL :DWORD +glLoadIdentity PROTO STDCALL +glPushMatrix PROTO STDCALL +glPopMatrix PROTO STDCALL +glTranslatef PROTO STDCALL :DWORD,:DWORD,:DWORD +glRotatef PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD +glFrustum PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD +glOrtho PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD +glGenTextures PROTO STDCALL :DWORD,:DWORD +glDeleteTextures PROTO STDCALL :DWORD,:DWORD +glBindTexture PROTO STDCALL :DWORD,:DWORD +glTexParameteri PROTO STDCALL :DWORD,:DWORD,:DWORD +glTexImage2D PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD +glGenLists PROTO STDCALL :DWORD +glNewList PROTO STDCALL :DWORD,:DWORD +glEndList PROTO STDCALL +glCallList PROTO STDCALL :DWORD +glDeleteLists PROTO STDCALL :DWORD,:DWORD +glLightfv PROTO STDCALL :DWORD,:DWORD,:DWORD +glLightModelfv PROTO STDCALL :DWORD,:DWORD +glMaterialfv PROTO STDCALL :DWORD,:DWORD,:DWORD +glColorMaterial PROTO STDCALL :DWORD,:DWORD +glFogfv PROTO STDCALL :DWORD,:DWORD +glFogf PROTO STDCALL :DWORD,:DWORD +glFogi PROTO STDCALL :DWORD,:DWORD +glShadeModel PROTO STDCALL :DWORD +glBlendFunc PROTO STDCALL :DWORD,:DWORD +glDepthMask PROTO STDCALL :DWORD +glDepthFunc PROTO STDCALL :DWORD +glPolygonOffset PROTO STDCALL :DWORD,:DWORD +glReadPixels PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD +glReadBuffer PROTO STDCALL :DWORD +glDrawBuffer PROTO STDCALL :DWORD +glGetError PROTO STDCALL +glGetString PROTO STDCALL :DWORD +glGetIntegerv PROTO STDCALL :DWORD,:DWORD +glFlush PROTO STDCALL +glFinish PROTO STDCALL +glCullFace PROTO STDCALL :DWORD +glFrontFace PROTO STDCALL :DWORD +wglCreateContext PROTO STDCALL :DWORD +wglDeleteContext PROTO STDCALL :DWORD +wglMakeCurrent PROTO STDCALL :DWORD,:DWORD +wglGetProcAddress PROTO STDCALL :DWORD +IFNDEF RegisterClassA +RegisterClassA PROTO STDCALL :DWORD +ENDIF +IFNDEF ChoosePixelFormat +ChoosePixelFormat PROTO STDCALL :DWORD,:DWORD +ENDIF +IFNDEF SetPixelFormat +SetPixelFormat PROTO STDCALL :DWORD,:DWORD,:DWORD +ENDIF +IFNDEF CreateDIBSection +CreateDIBSection PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD +ENDIF +IFNDEF CreateCompatibleDC +CreateCompatibleDC PROTO STDCALL :DWORD +ENDIF +IFNDEF DeleteDC +DeleteDC PROTO STDCALL :DWORD +ENDIF +IFNDEF SelectObject +SelectObject PROTO STDCALL :DWORD,:DWORD +ENDIF +IFNDEF DeleteObject +DeleteObject PROTO STDCALL :DWORD +ENDIF +IFNDEF AlphaBlend +AlphaBlend PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD +ENDIF +IFNDEF GdiFlush +GdiFlush PROTO STDCALL +ENDIF +IFNDEF StretchBlt +StretchBlt PROTO STDCALL :DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD,:DWORD +ENDIF diff --git a/src_graphics/rooftop_gl_data.inc b/src_graphics/rooftop_gl_data.inc new file mode 100644 index 0000000..5e607ad --- /dev/null +++ b/src_graphics/rooftop_gl_data.inc @@ -0,0 +1,189 @@ +include src_graphics\rooftop_gl_api.inc +ROOF_HD_W equ 1280 +ROOF_HD_H equ 960 +ROOF_GL_TEXTURES equ 49 +InitRoofHD3D PROTO +FreeRoofHD3D PROTO +RenderRoofHD3D PROTO +PresentRoofHD3D PROTO :DWORD,:DWORD,:DWORD,:DWORD,:DWORD +LoadRoofMesh3D PROTO :DWORD,:DWORD +FreeRoofMesh3D PROTO :DWORD +RoofGLProc3D PROTO :DWORD,:DWORD +UploadRoofGLTexture3D PROTO :DWORD,:DWORD,:DWORD,:DWORD +BuildRoofGLList3D PROTO :DWORD,:DWORD,:DWORD,:DWORD +DrawRoofGLSky3D PROTO +DrawRoofGLTraffic3D PROTO +DrawRoofGLWater3D PROTO +UploadRoofGLSky3D PROTO :DWORD,:DWORD +UpdateRoofGLFog3D PROTO +UpdateRoofQuality3D PROTO +ComposeRoofFrame3D PROTO +RenderRoofQualityToast3D PROTO +WriteRoofRendererInfo3D PROTO +WriteRoofLogString3D PROTO :DWORD,:DWORD +.data +roofHDReady3D dd 0 +roofHDStage3D dd 0 +roofHDWindow3D dd 0 +roofHDDC3D dd 0 +roofHDRC3D dd 0 +roofHDModule3D dd 0 +roofHDBuffer3D dd 0 +roofHDFBO3D dd 0 +roofHDColorTex3D dd 0 +roofHDDepth3D dd 0 +roofHDLists3D dd 0 +roofHDSkyTextures3D dd 2 dup(0) +roofHDDayTextures3D dd ROOF_GL_TEXTURES dup(0) +roofHDNightTextures3D dd ROOF_GL_TEXTURES dup(0) +roofHDEmissionTextures3D dd ROOF_GL_TEXTURES dup(0) +roofHDMeshScene3D dd 4 dup(0) +roofHDMeshCar3D dd 4 dup(0) +roofHDMeshBoat3D dd 4 dup(0) +roofHDScenePath3D db "assets\animations\rooftop\mesh-city-v1.bin",0 +roofHDCarPath3D db "assets\animations\rooftop\mesh-car-v1.bin",0 +roofHDBoatPath3D db "assets\animations\rooftop\mesh-boat-v1.bin",0 +roofHDMeshPtrs3D dd OFFSET roofHDMeshScene3D,OFFSET roofHDMeshCar3D,OFFSET roofHDMeshBoat3D +roofHDClassName3D db "AssemblerCasinoRoofOffscreen",0 +roofHDTitle3D db "Roof offscreen renderer",0 +roofHDClass3D dd 20h,OFFSET DefWindowProcA,0,0,0,0,0,0,0,OFFSET roofHDClassName3D +; PIXELFORMATDESCRIPTOR: RGBA32, depth24, draw-to-window/OpenGL/double-buffer. +roofHDPixelFormat3D dw 40,1 + dd 25h + db 0,32,0,0,0,0,0,0,8,0,0,0,0,0,0,24,8,0,0,0 + dd 0,0,0 +roofHDOverlayDC3D dd 0 +roofHDOverlayBmp3D dd 0 +roofHDOverlayOld3D dd 0 +roofHDFinalDC3D dd 0 +roofHDFinalBmp3D dd 0 +roofHDFinalOld3D dd 0 +roofHDFinalPixels3D dd 0 +roofHDOverlayPixels3D dd 0 +roofHDOverlayBMI3D dd 40,640,-480 + dw 1,32 + dd 0,640*480*4,0,0,0,0 +roofHDBMI3D dd 40,ROOF_HD_W,ROOF_HD_H + dw 1,32 + dd 0,ROOF_HD_W*ROOF_HD_H*4,0,0,0,0 +roofGLGenFBO3D dd 0 +roofGLBindFBO3D dd 0 +roofGLAttachTexture3D dd 0 +roofGLCheckFBO3D dd 0 +roofGLDeleteFBO3D dd 0 +roofGLGenRBO3D dd 0 +roofGLBindRBO3D dd 0 +roofGLStoreRBO3D dd 0 +roofGLAttachRBO3D dd 0 +roofGLDeleteRBO3D dd 0 +roofGLMipmap3D dd 0 +roofGLProcSlots3D dd OFFSET roofGLGenFBO3D,OFFSET roofGLBindFBO3D,OFFSET roofGLAttachTexture3D + dd OFFSET roofGLCheckFBO3D,OFFSET roofGLDeleteFBO3D,OFFSET roofGLGenRBO3D + dd OFFSET roofGLBindRBO3D,OFFSET roofGLStoreRBO3D,OFFSET roofGLAttachRBO3D + dd OFFSET roofGLDeleteRBO3D,OFFSET roofGLMipmap3D +roofGLProcNames3D dd OFFSET roofGLName0,OFFSET roofGLName1,OFFSET roofGLName2,OFFSET roofGLName3 + dd OFFSET roofGLName4,OFFSET roofGLName5,OFFSET roofGLName6,OFFSET roofGLName7 + dd OFFSET roofGLName8,OFFSET roofGLName9,OFFSET roofGLName10 +roofGLProcExtNames3D dd OFFSET roofGLExt0,OFFSET roofGLExt1,OFFSET roofGLExt2,OFFSET roofGLExt3 + dd OFFSET roofGLExt4,OFFSET roofGLExt5,OFFSET roofGLExt6,OFFSET roofGLExt7 + dd OFFSET roofGLExt8,OFFSET roofGLExt9,OFFSET roofGLExt10 +roofGLName0 db "glGenFramebuffers",0 +roofGLName1 db "glBindFramebuffer",0 +roofGLName2 db "glFramebufferTexture2D",0 +roofGLName3 db "glCheckFramebufferStatus",0 +roofGLName4 db "glDeleteFramebuffers",0 +roofGLName5 db "glGenRenderbuffers",0 +roofGLName6 db "glBindRenderbuffer",0 +roofGLName7 db "glRenderbufferStorage",0 +roofGLName8 db "glFramebufferRenderbuffer",0 +roofGLName9 db "glDeleteRenderbuffers",0 +roofGLName10 db "glGenerateMipmap",0 +roofGLExt0 db "glGenFramebuffersEXT",0 +roofGLExt1 db "glBindFramebufferEXT",0 +roofGLExt2 db "glFramebufferTexture2DEXT",0 +roofGLExt3 db "glCheckFramebufferStatusEXT",0 +roofGLExt4 db "glDeleteFramebuffersEXT",0 +roofGLExt5 db "glGenRenderbuffersEXT",0 +roofGLExt6 db "glBindRenderbufferEXT",0 +roofGLExt7 db "glRenderbufferStorageEXT",0 +roofGLExt8 db "glFramebufferRenderbufferEXT",0 +roofGLExt9 db "glDeleteRenderbuffersEXT",0 +roofGLExt10 db "glGenerateMipmapEXT",0 +roofGLFogDay3D REAL4 0.67,0.61,0.69,1.0 +roofGLFogNight3D REAL4 0.05,0.09,0.17,1.0 +roofGLFogCurrent3D REAL4 0.67,0.61,0.69,1.0 +roofGLFogWhite3D REAL4 1.0,1.0,1.0,1.0 +roofGLFogBlack3D REAL4 0.0,0.0,0.0,1.0 +roofGLColorScale3D REAL4 255.0 +roofGLWaveDiv3D REAL4 100000.0 +roofGLHalf3D REAL4 0.5 +roofGLWaterVertices3D REAL4 -2000.0,-62.0,76.0,-166.666667,6.333333 + REAL4 2000.0,-62.0,76.0,166.666667,6.333333 + REAL4 2000.0,-62.0,60.0,166.666667,5.0 + REAL4 -2000.0,-62.0,60.0,-166.666667,5.0 + REAL4 70.0,-62.0,60.0,5.833333,5.0 + REAL4 2000.0,-62.0,60.0,166.666667,5.0 + REAL4 2000.0,-62.0,29.0,166.666667,2.416667 + REAL4 70.0,-62.0,29.0,5.833333,2.416667 +roofGLAmbientDay3D REAL4 0.43,0.43,0.48,1.0 +roofGLAmbientNight3D REAL4 0.20,0.23,0.32,1.0 +roofGLSunColorDay3D REAL4 0.75,0.57,0.39,1.0 +roofGLSunColorNight3D REAL4 0.12,0.15,0.25,1.0 +roofGLSunDirection3D REAL4 -0.8,0.8,0.4,0.0 +roofGLEmissionZero3D REAL4 0.0,0.0,0.0,1.0 +roofGLEmissionGold3D REAL4 0.42,0.28,0.08,1.0 +roofGLEmissionLamp3D REAL4 0.9,0.67,0.3,1.0 +roofGLInvFP3D REAL4 0.0000152587890625 +roofGLDegrees3D REAL4 0.703125 +roofGLNinety3D REAL4 90.0 +roofGLHalfWidth3D REAL8 0.66 +roofGLLeft3D REAL8 -0.66 +roofGLRight3D REAL8 0.66 +roofGLNear3D REAL8 1.0 +roofGLFar3D REAL8 2048.0 +roofGLZeroD3D REAL8 0.0 +roofGLOneD3D REAL8 1.0 +roofGLMinusOneD3D REAL8 -1.0 +roofGLScreenWidthD3D REAL8 1280.0 +roofGLScreenHeightD3D REAL8 960.0 +roofGLCameraH3D dd 480 +roofHDRenderX3D dd -1 +roofHDRenderY3D dd -1 +roofHDRenderAngle3D dd -1 +roofHDRenderEye3D dd -1 +roofHDRenderHorizon3D dd -1 +roofHDRenderTheme3D dd -1 +roofHDRenderClock3D dd -1 +roofRenderW3D dd 960 +roofRenderH3D dd 720 +roofQualityMode3D dd 0 +roofAutoWidth3D dd 960 +roofQualityMapW3D dd 0 +roofQualityClock3D dd -1 +roofQualitySlow3D dd 0 +roofQualityToast3D dd 0 +roofQualityAutoText3D db 202,208,219,216,192,58,32,192,194,210,206,0 +roofQualityEcoText3D db 202,208,219,216,192,58,32,221,202,206,0 +roofQualityHighText3D db 202,208,219,216,192,58,32,196,197,210,192,203,200,0 +roofLogPath3D db "runtime\roof-renderer.txt",0 +roofLogHeader3D db "Native MASM rooftop renderer",13,10,0 +roofLogVendor3D db "Vendor: ",0 +roofLogRenderer3D db "Renderer: ",0 +roofLogVersion3D db "OpenGL: ",0 +roofLogStage3D db "Init stage (0=ready): ",0 +roofLogAssets3D db "City texture pack present: ",0 +roofLogWidth3D db "Render width: ",0 +roofLogSmall3D db "640",0 +roofLogMedium3D db "960",0 +roofLogLarge3D db "1280",0 +roofLogUnknown3D db "unavailable",0 +roofLogNL3D db 13,10,0 +roofLogDigit3D db "0",0 +.data? +roofHDBackdrop3D dd SCREEN_SIZE dup(?) +roofHDUsedMaterials3D db ROOF_GL_TEXTURES dup(?) +roofQualityX3D dd SCREEN_W dup(?) +roofQualityXCount3D dd SCREEN_W dup(?) +roofQualityY3D dd SCREEN_H dup(?) +roofQualityYCount3D dd SCREEN_H dup(?) +.code diff --git a/src_graphics/rooftop_map.inc b/src_graphics/rooftop_map.inc new file mode 100644 index 0000000..e6c0be4 --- /dev/null +++ b/src_graphics/rooftop_map.inc @@ -0,0 +1,74 @@ +; Generated by tools/generators/pack_rooftop.py. +.data +roofMap3D db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 + db 102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102,102 +roofSkyColumns3D dd -190,-190,-189,-189,-188,-188,-187,-187,-186,-186,-185,-185,-184,-184,-184,-183 + dd -183,-182,-182,-181,-181,-180,-180,-179,-179,-178,-178,-177,-177,-176,-176,-175 + dd -175,-174,-174,-173,-173,-172,-172,-171,-171,-170,-170,-169,-169,-168,-168,-167 + dd -167,-166,-166,-165,-165,-164,-164,-163,-163,-162,-161,-161,-160,-160,-159,-159 + dd -158,-158,-157,-157,-156,-156,-155,-155,-154,-154,-153,-153,-152,-151,-151,-150 + dd -150,-149,-149,-148,-148,-147,-147,-146,-145,-145,-144,-144,-143,-143,-142,-142 + dd -141,-141,-140,-139,-139,-138,-138,-137,-137,-136,-135,-135,-134,-134,-133,-133 + dd -132,-132,-131,-130,-130,-129,-129,-128,-128,-127,-126,-126,-125,-125,-124,-123 + dd -123,-122,-122,-121,-121,-120,-119,-119,-118,-118,-117,-116,-116,-115,-115,-114 + dd -113,-113,-112,-112,-111,-111,-110,-109,-109,-108,-108,-107,-106,-106,-105,-105 + dd -104,-103,-103,-102,-101,-101,-100,-100,-99,-98,-98,-97,-97,-96,-95,-95 + dd -94,-93,-93,-92,-92,-91,-90,-90,-89,-89,-88,-87,-87,-86,-85,-85 + dd -84,-84,-83,-82,-82,-81,-80,-80,-79,-78,-78,-77,-77,-76,-75,-75 + dd -74,-73,-73,-72,-71,-71,-70,-70,-69,-68,-68,-67,-66,-66,-65,-64 + dd -64,-63,-62,-62,-61,-60,-60,-59,-59,-58,-57,-57,-56,-55,-55,-54 + dd -53,-53,-52,-51,-51,-50,-49,-49,-48,-47,-47,-46,-45,-45,-44,-43 + dd -43,-42,-41,-41,-40,-39,-39,-38,-37,-37,-36,-35,-35,-34,-33,-33 + dd -32,-31,-31,-30,-29,-29,-28,-27,-27,-26,-25,-25,-24,-23,-23,-22 + dd -21,-21,-20,-19,-19,-18,-17,-17,-16,-15,-15,-14,-13,-13,-12,-11 + dd -11,-10,-9,-9,-8,-7,-7,-6,-5,-5,-4,-3,-3,-2,-1,-1 + dd 0,1,1,2,3,3,4,5,5,6,7,7,8,9,9,10 + dd 11,11,12,13,13,14,15,15,16,17,17,18,19,19,20,21 + dd 21,22,23,23,24,25,25,26,27,27,28,29,29,30,31,31 + dd 32,33,33,34,35,35,36,37,37,38,39,39,40,41,41,42 + dd 43,43,44,45,45,46,47,47,48,49,49,50,51,51,52,53 + dd 53,54,55,55,56,57,57,58,59,59,60,60,61,62,62,63 + dd 64,64,65,66,66,67,68,68,69,70,70,71,71,72,73,73 + dd 74,75,75,76,77,77,78,78,79,80,80,81,82,82,83,84 + dd 84,85,85,86,87,87,88,89,89,90,90,91,92,92,93,93 + dd 94,95,95,96,97,97,98,98,99,100,100,101,101,102,103,103 + dd 104,105,105,106,106,107,108,108,109,109,110,111,111,112,112,113 + dd 113,114,115,115,116,116,117,118,118,119,119,120,121,121,122,122 + dd 123,123,124,125,125,126,126,127,128,128,129,129,130,130,131,132 + dd 132,133,133,134,134,135,135,136,137,137,138,138,139,139,140,141 + dd 141,142,142,143,143,144,144,145,145,146,147,147,148,148,149,149 + dd 150,150,151,151,152,153,153,154,154,155,155,156,156,157,157,158 + dd 158,159,159,160,160,161,161,162,163,163,164,164,165,165,166,166 + dd 167,167,168,168,169,169,170,170,171,171,172,172,173,173,174,174 + dd 175,175,176,176,177,177,178,178,179,179,180,180,181,181,182,182 + dd 183,183,184,184,184,185,185,186,186,187,187,188,188,189,189,190 diff --git a/src_graphics/rooftop_quality.inc b/src_graphics/rooftop_quality.inc new file mode 100644 index 0000000..a9a043d --- /dev/null +++ b/src_graphics/rooftop_quality.inc @@ -0,0 +1,274 @@ +; Quality changes only roof rendering. Simulation, collisions and other games stay native. +UpdateRoofQuality3D PROC uses ebx esi edi + cmp hallLevel3D,HALL_ROOF + jne roof_quality_apply + cmp engineMode,MODE_WALK + jne roof_quality_apply + mov eax,animationClock3D + cmp eax,roofQualityClock3D + je roof_quality_apply + mov roofQualityClock3D,eax + invoke Pressed3D,77h + test eax,eax + jz roof_quality_auto + inc roofQualityMode3D + cmp roofQualityMode3D,3 + jb @F + mov roofQualityMode3D,0 + mov roofAutoWidth3D,960 +@@: + mov roofQualityToast3D,1800 + mov roofQualitySlow3D,0 +roof_quality_auto: + mov eax,deltaTime3D + cmp eax,100 + jbe @F + mov eax,100 +@@: + sub roofQualityToast3D,eax + jns @F + mov roofQualityToast3D,0 +@@: + cmp roofQualityMode3D,0 + jne roof_quality_apply + cmp roofAutoWidth3D,640 + je roof_quality_apply + cmp deltaTime3D,34 + jbe roof_quality_cool + add roofQualitySlow3D,eax + cmp roofQualitySlow3D,1000 + jb roof_quality_apply + mov roofAutoWidth3D,640 + mov roofQualitySlow3D,0 + mov roofQualityToast3D,1800 + jmp roof_quality_apply +roof_quality_cool: + sub roofQualitySlow3D,8 + jns roof_quality_apply + mov roofQualitySlow3D,0 +roof_quality_apply: + mov eax,roofAutoWidth3D + cmp roofQualityMode3D,0 + je roof_quality_size + mov eax,640 + cmp roofQualityMode3D,1 + je roof_quality_size + mov eax,1280 +roof_quality_size: + mov roofRenderW3D,eax + imul eax,3 + shr eax,2 + mov roofRenderH3D,eax + mov eax,roofRenderW3D + cmp eax,roofQualityMapW3D + je roof_quality_done + mov roofQualityMapW3D,eax + mov roofHDRenderClock3D,-1 + xor esi,esi +roof_quality_x: + mov eax,esi + imul eax,roofRenderW3D + xor edx,edx + mov ecx,SCREEN_W + div ecx + mov ebx,eax + shl eax,2 + mov roofQualityX3D[esi*4],eax + mov eax,esi + inc eax + imul eax,roofRenderW3D + xor edx,edx + div ecx + sub eax,ebx + mov roofQualityXCount3D[esi*4],eax + inc esi + cmp esi,SCREEN_W + jb roof_quality_x + xor esi,esi +roof_quality_y: + mov eax,esi + imul eax,roofRenderH3D + xor edx,edx + mov ecx,SCREEN_H + div ecx + mov ebx,eax + mov edi,ROOF_HD_H-1 + sub edi,eax + imul edi,ROOF_HD_W*4 + mov roofQualityY3D[esi*4],edi + mov eax,esi + inc eax + imul eax,roofRenderH3D + xor edx,edx + div ecx + sub eax,ebx + mov roofQualityYCount3D[esi*4],eax + inc esi + cmp esi,SCREEN_H + jb roof_quality_y + cmp roofHDReady3D,1 + jne roof_quality_done + call WriteRoofRendererInfo3D +roof_quality_done: + ret +UpdateRoofQuality3D ENDP + +ComposeRoofFrame3D PROC uses ebx esi edi + local rows:DWORD,y:DWORD,x:DWORD,targetRow:DWORD,repeatRow:DWORD + invoke GdiFlush + mov esi,roofHDBuffer3D + mov eax,ROOF_HD_H + sub eax,roofRenderH3D + imul eax,ROOF_HD_W*4 + add eax,roofHDFinalPixels3D + mov edi,eax + mov eax,roofRenderH3D + mov rows,eax +roof_compose_background: + mov ecx,roofRenderW3D + rep movsd + mov eax,ROOF_HD_W + sub eax,roofRenderW3D + lea edi,[edi+eax*4] + dec rows + jnz roof_compose_background + xor esi,esi + mov y,0 +roof_compose_row: + mov ebx,y + mov eax,roofQualityY3D[ebx*4] + add eax,roofHDFinalPixels3D + mov targetRow,eax + mov eax,roofQualityYCount3D[ebx*4] + mov repeatRow,eax + mov x,0 +roof_compose_pixel: + mov eax,screenBuffer[esi*4] + cmp roofCityMask3D[esi],0 + je roof_compose_opaque + cmp eax,roofHDBackdrop3D[esi*4] + je roof_compose_next +roof_compose_opaque: + mov ebx,x + mov edi,roofQualityX3D[ebx*4] + add edi,targetRow + mov [edi],eax + cmp roofQualityXCount3D[ebx*4],2 + jne @F + mov [edi+4],eax +@@: + cmp repeatRow,2 + jne roof_compose_next + mov [edi-ROOF_HD_W*4],eax + cmp roofQualityXCount3D[ebx*4],2 + jne roof_compose_next + mov [edi-ROOF_HD_W*4+4],eax +roof_compose_next: + inc esi + inc x + cmp x,SCREEN_W + jb roof_compose_pixel + inc y + cmp y,SCREEN_H + jb roof_compose_row + ret +ComposeRoofFrame3D ENDP + +RenderRoofQualityToast3D PROC + cmp roofQualityToast3D,0 + je roof_quality_toast_done + mov eax,OFFSET roofQualityAutoText3D + cmp roofRenderW3D,640 + jne @F + mov eax,OFFSET roofQualityEcoText3D +@@: + cmp roofQualityMode3D,2 + jne @F + mov eax,OFFSET roofQualityHighText3D +@@: + push eax + invoke DrawBox,12,40,160,18,00151B1Eh + pop eax + invoke DrawString8x8,20,45,eax,00E8D7ADh +roof_quality_toast_done: + ret +RenderRoofQualityToast3D ENDP + +WriteRoofLogString3D PROC uses ebx esi handle:DWORD,value:DWORD + local written:DWORD + mov ebx,handle + mov esi,value + test esi,esi + jnz @F + mov esi,OFFSET roofLogUnknown3D +@@: + xor ecx,ecx +roof_log_length: + cmp BYTE PTR [esi+ecx],0 + je roof_log_write + inc ecx + jmp roof_log_length +roof_log_write: + invoke WriteFile,ebx,esi,ecx,ADDR written,0 + ret +WriteRoofLogString3D ENDP + +WriteRoofRendererInfo3D PROC uses ebx esi + invoke CreateDirectoryA,OFFSET runtime3D,0 + invoke CreateFileA,OFFSET roofLogPath3D,40000000h,1,0,2,80h,0 + cmp eax,-1 + je roof_log_done + mov ebx,eax + invoke WriteRoofLogString3D,ebx,OFFSET roofLogHeader3D + mov esi,0 +roof_log_gl_string: + mov eax,OFFSET roofLogVendor3D + cmp esi,0 + je @F + mov eax,OFFSET roofLogRenderer3D + cmp esi,1 + je @F + mov eax,OFFSET roofLogVersion3D +@@: + invoke WriteRoofLogString3D,ebx,eax + xor eax,eax + cmp roofHDRC3D,0 + je @F + mov eax,esi + add eax,1F00h + invoke glGetString,eax +@@: + invoke WriteRoofLogString3D,ebx,eax + invoke WriteRoofLogString3D,ebx,OFFSET roofLogNL3D + inc esi + cmp esi,3 + jb roof_log_gl_string + invoke WriteRoofLogString3D,ebx,OFFSET roofLogStage3D + mov eax,roofHDStage3D + add al,'0' + mov roofLogDigit3D,al + invoke WriteRoofLogString3D,ebx,OFFSET roofLogDigit3D + invoke WriteRoofLogString3D,ebx,OFFSET roofLogNL3D + invoke WriteRoofLogString3D,ebx,OFFSET roofLogAssets3D + mov roofLogDigit3D,'0' + cmp roofCityArt3D.pixels,0 + je @F + mov roofLogDigit3D,'1' +@@: + invoke WriteRoofLogString3D,ebx,OFFSET roofLogDigit3D + invoke WriteRoofLogString3D,ebx,OFFSET roofLogNL3D + invoke WriteRoofLogString3D,ebx,OFFSET roofLogWidth3D + mov eax,OFFSET roofLogMedium3D + cmp roofRenderW3D,960 + je @F + mov eax,OFFSET roofLogSmall3D + cmp roofRenderW3D,640 + je @F + mov eax,OFFSET roofLogLarge3D +@@: + invoke WriteRoofLogString3D,ebx,eax + invoke WriteRoofLogString3D,ebx,OFFSET roofLogNL3D + invoke CloseHandle,ebx +roof_log_done: + ret +WriteRoofRendererInfo3D ENDP diff --git a/src_graphics/rooftop_surfaces.inc b/src_graphics/rooftop_surfaces.inc new file mode 100644 index 0000000..4697365 --- /dev/null +++ b/src_graphics/rooftop_surfaces.inc @@ -0,0 +1,317 @@ +; Ray/plane intersection for real pitched roofs, including perspective depth. +DrawRoofSlope3D PROC uses ebx esi edi axis:DWORD,x0:DWORD,x1:DWORD,y0:DWORD,y1:DWORD,baseZ:DWORD,rise:DWORD,material:DWORD + local slope:DWORD,numerator:DWORD,leftPx:DWORD,rightPx:DWORD,topPx:DWORD,bottomPx:DWORD + local corner:DWORD,wx:DWORD,wy:DWORD,wz:DWORD,visible:DWORD,nearClip:DWORD + local row:DWORD,column:DWORD,rayZ:DWORD,depth:DWORD,along:DWORD,denominator:DWORD + local texture:DWORD,textureW:DWORD,textureH:DWORD,light:DWORD,sourceU:DWORD + invoke ResolveRoofSurface3D,material + test eax,eax + jz roof_slope_done + mov texture,eax + mov textureW,ecx + mov textureH,edx + mov eax,rise + cdq + shld edx,eax,16 + shl eax,16 + mov ecx,x1 + sub ecx,x0 + cmp axis,0 + je @F + mov ecx,y1 + sub ecx,y0 +@@: + idiv ecx + mov slope,eax + mov light,242 + test eax,eax + jns @F + mov light,184 +@@: + mov eax,playerX + sub eax,x0 + cmp axis,0 + je @F + mov eax,playerY + sub eax,y0 +@@: + imul slope + shrd eax,edx,16 + neg eax + add eax,viewEye3D + sub eax,baseZ + mov numerator,eax + mov leftPx,SCREEN_W + mov rightPx,0 + mov topPx,SCREEN_H + mov bottomPx,0 + mov visible,0 + mov nearClip,0 + mov corner,0 +roof_slope_corner: + mov eax,x0 + test corner,1 + jz @F + mov eax,x1 +@@: + mov wx,eax + mov ebx,y0 + test corner,2 + jz @F + mov ebx,y1 +@@: + mov wy,ebx + mov ecx,baseZ + mov eax,corner + cmp axis,0 + jne @F + and eax,1 + jmp roof_slope_corner_height +@@: + and eax,2 +roof_slope_corner_height: + test eax,eax + jz @F + add ecx,rise +@@: + mov wz,ecx + invoke ProjectCityPoint3D,wx,wy + cmp edx,4096 + jg roof_slope_projected_corner + mov nearClip,1 + jmp roof_slope_corner_next +roof_slope_projected_corner: + inc visible + mov esi,edx + cmp eax,leftPx + jge @F + mov leftPx,eax +@@: + cmp eax,rightPx + jle @F + mov rightPx,eax +@@: + mov eax,viewEye3D + sub eax,wz + mov ecx,SCREEN_H + imul ecx + idiv esi + add eax,viewHorizon3D + cmp eax,topPx + jge @F + mov topPx,eax +@@: + cmp eax,bottomPx + jle @F + mov bottomPx,eax +@@: +roof_slope_corner_next: + inc corner + cmp corner,4 + jb roof_slope_corner + cmp visible,0 + je roof_slope_done + cmp nearClip,0 + je @F + mov leftPx,0 + mov rightPx,SCREEN_W-1 + mov topPx,0 + mov bottomPx,SCREEN_H-1 +@@: + cmp leftPx,0 + jge @F + mov leftPx,0 +@@: + cmp rightPx,SCREEN_W-1 + jle @F + mov rightPx,SCREEN_W-1 +@@: + cmp topPx,0 + jge @F + mov topPx,0 +@@: + cmp bottomPx,SCREEN_H-1 + jle @F + mov bottomPx,SCREEN_H-1 +@@: + mov eax,leftPx + cmp eax,rightPx + jge roof_slope_done + mov eax,topPx + cmp eax,bottomPx + jge roof_slope_done + mov row,eax + mov ebx,leftPx +roof_slope_prepare: + mov eax,cityRayX3D[ebx*4] + cmp axis,0 + je @F + mov eax,cityRayY3D[ebx*4] +@@: + imul slope + shrd eax,edx,16 + mov roofSlopeRay3D[ebx*4],eax + inc ebx + cmp ebx,rightPx + jbe roof_slope_prepare +roof_slope_row: + mov eax,viewHorizon3D + sub eax,row + shl eax,16 + cdq + mov ecx,SCREEN_H + idiv ecx + mov rayZ,eax + mov eax,row + imul eax,SCREEN_W*4 + mov ebx,leftPx + lea edi,[eax+ebx*4] + mov column,ebx +roof_slope_pixel: + mov ebx,column + mov ecx,roofSlopeRay3D[ebx*4] + sub ecx,rayZ + mov denominator,ecx + mov eax,ecx + cdq + xor eax,edx + sub eax,edx + cmp eax,64 + jb roof_slope_advance + shl eax,10 + mov along,eax + mov eax,numerator + cdq + xor eax,edx + sub eax,edx + cmp eax,along + jae roof_slope_advance + mov eax,numerator + cdq + shld edx,eax,16 + shl eax,16 + idiv denominator + cmp eax,4096 + jle roof_slope_advance + cmp eax,cityDepthBuffer3D[edi] + jae roof_slope_advance + mov depth,eax + imul cityRayX3D[ebx*4] + shrd eax,edx,16 + add eax,playerX + cmp eax,x0 + jl roof_slope_advance + cmp eax,x1 + jge roof_slope_advance + mov wx,eax + sub eax,x0 + sar eax,9 + mov ecx,textureW + dec ecx + and eax,ecx + mov sourceU,eax + mov eax,depth + imul cityRayY3D[ebx*4] + shrd eax,edx,16 + add eax,playerY + cmp eax,y0 + jl roof_slope_advance + cmp eax,y1 + jge roof_slope_advance + mov wy,eax + cmp roofSlopeMask3D,0 + je roof_slope_sample + mov eax,wx + sub eax,roofSlopeCentreX3D + cdq + xor eax,edx + sub eax,edx + imul roofSlopeHalfY3D + shrd eax,edx,16 + mov along,eax + mov eax,wy + sub eax,roofSlopeCentreY3D + cdq + xor eax,edx + sub eax,edx + imul roofSlopeHalfX3D + shrd eax,edx,16 + cmp roofSlopeMask3D,1 + jne @F + cmp eax,along + ja roof_slope_advance + jmp roof_slope_sample +@@: + cmp eax,along + jb roof_slope_advance +roof_slope_sample: + mov eax,wy + sub eax,y0 + sar eax,9 + mov ecx,textureH + dec ecx + and eax,ecx + imul eax,textureW + add eax,sourceU + mov esi,texture + mov eax,[esi+eax*4] + invoke ShadeRoofCityPixel3D,eax,depth,light,material + mov ecx,edi + shr ecx,2 + mov edx,roofPixelGlow3D + mov roofGlowMask3D[ecx],dl + mov screenBuffer[edi],eax + mov eax,depth + mov cityDepthBuffer3D[edi],eax + mov eax,edi + shr eax,2 + mov roofCityMask3D[eax],1 +roof_slope_advance: + add edi,4 + inc column + mov eax,column + cmp eax,rightPx + jbe roof_slope_pixel + inc row + mov eax,row + cmp eax,bottomPx + jbe roof_slope_row +roof_slope_done: + ret +DrawRoofSlope3D ENDP + +DrawRoofPyramid3D PROC uses ebx esi centreX:DWORD,centreY:DWORD,baseZ:DWORD,halfX:DWORD,halfY:DWORD,height:DWORD,mat:DWORD + local x0:DWORD,x1:DWORD,y0:DWORD,y1:DWORD,topZ:DWORD,down:DWORD + mov eax,centreX + mov roofSlopeCentreX3D,eax + sub eax,halfX + mov x0,eax + mov eax,centreX + add eax,halfX + mov x1,eax + mov eax,centreY + mov roofSlopeCentreY3D,eax + sub eax,halfY + mov y0,eax + mov eax,centreY + add eax,halfY + mov y1,eax + mov eax,halfX + mov roofSlopeHalfX3D,eax + mov eax,halfY + mov roofSlopeHalfY3D,eax + mov eax,baseZ + add eax,height + mov topZ,eax + mov eax,height + neg eax + mov down,eax + mov roofSlopeMask3D,1 + invoke DrawRoofSlope3D,0,x0,centreX,y0,y1,baseZ,height,mat + invoke DrawRoofSlope3D,0,centreX,x1,y0,y1,topZ,down,mat + mov roofSlopeMask3D,2 + invoke DrawRoofSlope3D,1,x0,x1,y0,centreY,baseZ,height,mat + invoke DrawRoofSlope3D,1,x0,x1,centreY,y1,topZ,down,mat + mov roofSlopeMask3D,0 + ret +DrawRoofPyramid3D ENDP diff --git a/tools/generators/pack_darts.py b/tools/generators/pack_darts.py index 8e38f00..0549cdf 100644 --- a/tools/generators/pack_darts.py +++ b/tools/generators/pack_darts.py @@ -1,5 +1,6 @@ """Exact clock-board geometry plus fixed-grid full-arm animation packing.""" from pathlib import Path +import argparse import math import random import struct @@ -19,58 +20,76 @@ def pack(name, frames): def board(): rng = random.Random(301) - image = Image.new("RGBA", (512, 512)) + scale = 2 + center = 256*scale + image = Image.new("RGBA", (512*scale, 512*scale)) pixels = image.load() - for y in range(512): - for x in range(512): - dx, dy = x-256, 256-y + for y in range(image.height): + for x in range(image.width): + dx, dy = (x-center)/scale, (center-y)/scale 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) + color = (23, 29, 31) if red else (232, 225, 201) if 99 <= radius <= 107 or 162 <= radius: - color = (174, 37, 47) if red else (28, 112, 87) + color = (191, 42, 55) if red else (24, 145, 103) if radius <= 15.9: - color = (28, 112, 87) + color = (24, 145, 103) if radius <= 6.35: - color = (187, 37, 47) - elif radius < 210: - color = (20, 24, 25) + color = (191, 42, 55) + elif radius < 220: + color = (10, 14, 17) 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) + grain = rng.randrange(-2, 3) pixels[x, y] = tuple(max(0, min(255, c+grain)) for c in color) + (255,) draw = ImageDraw.Draw(image) - wire = (171, 178, 173, 255) + wire = (139, 158, 161, 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) + bounds = tuple(round(v*scale) for v in (256-r, 256-r, 256+r, 256+r)) + draw.ellipse(bounds, outline=wire, width=2*scale) 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) + ends = [(round((256+math.sin(theta)*r)*scale), + round((256-math.cos(theta)*r)*scale)) for r in (16,170)] + draw.line(ends, fill=wire, width=2*scale) + font = ImageFont.truetype("C:/Windows/Fonts/arialbd.ttf", 32*scale) + labels = [] 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") + point = ((256+math.sin(theta)*198)*scale, (256-math.cos(theta)*198)*scale) + labels.append(draw.textbbox(point, str(value), font=font, anchor="mm")) + draw.text(point, str(value), + font=font, fill=(249, 247, 232), anchor="mm") + for i, a in enumerate(labels): + for b in labels[i+1:]: + assert a[2] <= b[0] or b[2] <= a[0] or a[3] <= b[1] or b[3] <= a[1], "Overlapping board numbers" + draw.ellipse((12*scale, 12*scale, 500*scale, 500*scale), + outline=(104, 114, 120), width=3*scale) + image = image.resize((512,512), Image.Resampling.LANCZOS) + image.save(ART / "board-v2.png") return image def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--board-only", action="store_true") + args = parser.parse_args() ART.mkdir(parents=True, exist_ok=True) OUT.mkdir(parents=True, exist_ok=True) - pack("board-v1.bin", [board()]) + pack("board-v2.bin", [board()]) + if args.board_only: + print("Packed sharper 512px board: unchanged footprint and scoring radii.") + return wall = Image.open(ART / "lounge-wall-v1.png").convert("RGBA") pack("lounge-wall-v1.bin", [wall.resize((512,512), Image.Resampling.LANCZOS)]) anchors = [] diff --git a/tools/generators/pack_fortune_orb.py b/tools/generators/pack_fortune_orb.py new file mode 100644 index 0000000..0e3100d --- /dev/null +++ b/tools/generators/pack_fortune_orb.py @@ -0,0 +1,102 @@ +"""Offline orb tracking metadata and antialiased numeral sprites; no runtime Python.""" +from pathlib import Path +import json +import struct + +import numpy as np +from PIL import Image, ImageDraw, ImageFilter, ImageFont + +ROOT = Path(__file__).resolve().parents[2] +OUT = ROOT / "assets/animations/fortune" +ART = ROOT / "assets/sprites/fortune" +PREVIEW = ROOT / "build/hall-expansion" + + +def load_orbs(): + raw = (OUT / "orb-mist-v1.bin").read_bytes() + magic, width, height, count, length = struct.unpack_from("<4s4I", raw) + assert magic == b"ANM1" and count == 16 + assert length == width * height * count * 4 == len(raw) - 20 + stride = width * height * 4 + return [Image.frombytes("RGBA", (width, height), raw[20+i*stride:20+(i+1)*stride], + "raw", "BGRA") for i in range(count)] + + +def sphere_geometry(frame): + # Fit the upper glass silhouette, explicitly excluding the metal pedestal. + alpha = np.asarray(frame.getchannel("A")) + rows, midpoints, half_widths = [], [], [] + for y in range(12, round(frame.height * .70)): + xs = np.flatnonzero(alpha[y] >= 170) + if len(xs) < frame.width * .35: + continue + left, right = int(xs[0]), int(xs[-1]) + rows.append(y) + midpoints.append((left + right) / 2) + half_widths.append((right - left) / 2) + a, b, c = np.polyfit(rows, np.square(half_widths), 2) + assert a < 0 + cy = -b / (2*a) + cx = float(np.median(midpoints)) + rx = float(np.sqrt(c - b*b/(4*a))) + ry = float(rx / np.sqrt(-a)) + assert 70 < cx < 125 and 60 < cy < 115 + assert 55 < rx < 95 and 55 < ry < 95 + return {"cx": cx, "cy": cy, "rx": rx, "ry": ry} + + +def numeral(digit, font): + factor = 4 + mask = Image.new("L", (32*factor, 48*factor)) + draw = ImageDraw.Draw(mask) + box = draw.textbbox((0, 0), str(digit), font=font) + x = (mask.width-(box[2]-box[0]))/2-box[0] + y = (mask.height-(box[3]-box[1]))/2-box[1] + draw.text((round(x), round(y)), str(digit), font=font, fill=255) + mask = mask.resize((32, 48), Image.Resampling.LANCZOS) + shadow = Image.new("RGBA", mask.size, (8, 30, 66, 0)) + shadow.putalpha(mask.filter(ImageFilter.MaxFilter(3)).point(lambda a: round(a*.65))) + halo = Image.new("RGBA", mask.size, (65, 168, 243, 0)) + halo.putalpha(mask.filter(ImageFilter.GaussianBlur(2.0)).point(lambda a: round(a*.65))) + ink = Image.new("RGBA", mask.size, (200, 235, 255, 0)) + ink.putalpha(mask.point(lambda a: round(a*.90))) + return Image.alpha_composite(Image.alpha_composite(halo, shadow), ink) + + +def main(): + PREVIEW.mkdir(parents=True, exist_ok=True) + orbs = load_orbs() + geometry = [sphere_geometry(frame) for frame in orbs] + # Q8 offsets in the exact 128px runtime drawing rectangle, not its centre. + lines = ["; Generated by tools/generators/pack_fortune_orb.py; glass only, pedestal excluded."] + for name, key in (("fortuneOrbAnchorX3D", "cx"), ("fortuneOrbAnchorY3D", "cy")): + values = [round(item[key] * 128 / 192 * 256) for item in geometry] + lines.append(name + " dd " + ",".join(map(str, values))) + (ROOT / "src_graphics/fortune_orb_art.inc").write_text("\n".join(lines)+"\n", encoding="ascii") + font = ImageFont.truetype("C:/Windows/Fonts/cambria.ttc", 34*4) + digits = [numeral(i, font) for i in range(10)] + payload = b"".join(frame.tobytes("raw", "BGRA") for frame in digits) + (OUT / "orb-numerals-v1.bin").write_bytes(struct.pack("<4s4I", b"ANM1", 32, 48, 10, + len(payload)) + payload) + sheet = Image.new("RGBA", (320, 48)) + for i, digit in enumerate(digits): + sheet.alpha_composite(digit, (32*i, 0)) + sheet.save(ART / "orb-numerals-v1.png") + (ART / "orb-tracking-v1.json").write_text(json.dumps({"canvas": [192, 192], + "font": "Cambria Regular, rasterized at 4x; no font needed at runtime", + "frames": geometry}, indent=2)+"\n", encoding="ascii") + sheet = Image.new("RGB", (192*4, 192*4), (5, 15, 28)) + for i, (frame, item) in enumerate(zip(orbs, geometry)): + debug = frame.copy() + draw = ImageDraw.Draw(debug) + x, y = item["cx"], item["cy"] + draw.ellipse((x-item["rx"], y-item["ry"], x+item["rx"], y+item["ry"]), outline="yellow") + draw.line((x-5, y, x+5, y), fill="yellow") + draw.line((x, y-5, x, y+5), fill="yellow") + sheet.paste(debug, ((i%4)*192, (i//4)*192), debug) + sheet.save(PREVIEW / "fortune-orb-tracking.png") + print("Packed 10 antialiased Cambria numerals and 16 measured glass-centre anchors.") + + +if __name__ == "__main__": + main() diff --git a/tools/generators/pack_rooftop.py b/tools/generators/pack_rooftop.py new file mode 100644 index 0000000..44d953b --- /dev/null +++ b/tools/generators/pack_rooftop.py @@ -0,0 +1,369 @@ +"""Offline rooftop atlas/map compiler. Game rendering and travel remain MASM.""" +from pathlib import Path +import json +import math +import struct +import numpy as np +import cv2 +from PIL import Image + +ROOT = Path(__file__).resolve().parents[2] +ART = ROOT / "assets/sprites/rooftop" +OUT = ROOT / "assets/animations/rooftop" + + +def grid(path, columns, rows): + source = Image.open(ART/path).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((256, 256), Image.Resampling.LANCZOS) + for y in range(rows) for x in range(columns)] + + +def pack(name, frames): + w, h = frames[0].size + payload = b"".join(frame.tobytes("raw", "BGRA") for frame in frames) + (OUT/name).write_bytes(struct.pack("<4s4I", b"ANM1", w, h, len(frames), len(payload))+payload) + + +def clean_hands(frame): + pixels = np.asarray(frame).copy() + mask = (pixels[:,:,3] >= 128).astype(np.uint8) + mask = cv2.morphologyEx(mask,cv2.MORPH_OPEN,np.ones((3,3),np.uint8)) + count,labels,stats,_ = cv2.connectedComponentsWithStats(mask) + keep = [i for i in range(1,count) if stats[i,cv2.CC_STAT_AREA] > 2500] + support = np.isin(labels,keep).astype(np.uint8) + support = cv2.erode(support,np.ones((3,3),np.uint8)) + pixels[:,:,3] = support*255 + pixels[support == 0,:3] = 0 + return Image.fromarray(pixels) + + +def night_facade(frame, seed): + """Identify window apertures; mark lit glass with FF alpha and unlit walls FE.""" + pixels = np.asarray(frame).copy() + rgb = pixels[:,:,:3].astype(np.int16) + mask = ((rgb.max(axis=2)<145) & (rgb[:,:,2]>rgb[:,:,0]-10)).astype(np.uint8)*255 + mask = cv2.morphologyEx(mask,cv2.MORPH_CLOSE,np.ones((7,5),np.uint8)) + contours,_ = cv2.findContours(mask,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) + pixels[:,:,3] = 254 + rng = np.random.default_rng(seed) + count = 0 + for contour in contours: + x,y,w,h = cv2.boundingRect(contour) + if not (8 <= w <= 110 and 14 <= h <= 180 and .8 <= h/w <= 4): + continue + if rng.random() > .48: + continue + region = pixels[y+2:y+h-2,x+2:x+w-2] + source = region[:,:,:3].astype(np.int16) + glass = (source[:,:,2]>source[:,:,0]+4) & (source.max(axis=2)<155) + if glass.sum() < 15: + continue + yy,xx = np.mgrid[:h-4,:w-4] + glow = np.clip(1-((xx-(w-4)/2)/(w/2))**2*.22, .5, 1) + warmth = np.array([210,148,76])*rng.uniform(.7,1.1) + lit = np.clip(glow[:,:,None]*warmth,0,255).astype(np.uint8) + region[:,:,:3][glass] = lit[glass] + region[:,:,3][glass] = 255 + count += 1 + return Image.fromarray(pixels),count + + +def main(): + OUT.mkdir(parents=True, exist_ok=True) + sky = Image.open(ART/"sunset-sky-v1.png").convert("RGBA").resize((2048,512),Image.Resampling.LANCZOS) + # A short symmetric overlap makes the cylindrical wrap continuous. + original = sky.copy() + for x in range(32): + mixed = Image.blend(original.crop((x,0,x+1,512)), + original.crop((2047-x,0,2048-x,512)), (32-x)/64) + sky.paste(mixed, (x,0)) + mixed = Image.blend(original.crop((2047-x,0,2048-x,512)), + original.crop((x,0,x+1,512)), (32-x)/64) + sky.paste(mixed, (2047-x,0)) + pack("panorama-v1.bin", [sky.crop((i*512,0,(i+1)*512,512)) for i in range(4)]) + night = Image.open(ART/"night-sky-v1.png").convert("RGBA").resize((2048,512),Image.Resampling.LANCZOS) + pack("night-v1.bin",[night.crop((i*512,0,(i+1)*512,512)) for i in range(4)]) + hands = Image.open(ART/"rail-hands-v1.png").convert("RGBA") + poses = [hands.crop((round(x*hands.width/4),round(y*hands.height/2), + round((x+1)*hands.width/4),round((y+1)*hands.height/2))) + .resize((640,320),Image.Resampling.LANCZOS) + for y in range(2) for x in range(4)] + # Offline bidirectional flow supplies in-between poses; playback is pure MASM. + coordinates = np.mgrid[:320,:640][::-1].transpose(1,2,0).astype(np.float32) + smooth = [] + for first,second in zip(poses,poses[1:]): + a,b = np.asarray(first),np.asarray(second) + ag = cv2.cvtColor(a,cv2.COLOR_RGBA2GRAY) + bg = cv2.cvtColor(b,cv2.COLOR_RGBA2GRAY) + forward = cv2.calcOpticalFlowFarneback(ag,bg,None,.5,5,35,6,7,1.5,0) + backward = cv2.calcOpticalFlowFarneback(bg,ag,None,.5,5,35,6,7,1.5,0) + for index in range(4): + q = index/4 + aw = cv2.remap(a,coordinates-forward*q,None,cv2.INTER_LINEAR, + borderMode=cv2.BORDER_CONSTANT) + bw = cv2.remap(b,coordinates-backward*(1-q),None,cv2.INTER_LINEAR, + borderMode=cv2.BORDER_CONSTANT) + smooth.append(Image.fromarray(cv2.addWeighted(aw,1-q,bw,q,0))) + smooth += [poses[-1]]*4 + assert len(smooth) == 32 + pack("rail-hands-v1.bin",[clean_hands(frame) for frame in smooth]) + materials = grid("materials-v1.png",4,2) + pack("materials-v1.bin",materials) + panels = grid("panels-v1.png",4,2)[:4] + # One moving leaf, not a photograph of a whole elevator on each leaf. + panels[0] = panels[0].crop((72,16,126,235)).resize((256,256),Image.Resampling.LANCZOS) + # Use only the fluted facade, not the reference's pictured skyline above it. + panels[2] = panels[2].crop((0,116,256,240)).resize((256,256),Image.Resampling.LANCZOS) + cutouts = grid("cutouts-v1.png",2,2) + for frame in cutouts: + assert frame.getchannel("A").getextrema()[0] == 0, "cutouts require actual alpha" + sofas = Image.open(ART/"sofa-views-v1.png").convert("RGBA") + occupied = np.sum(np.asarray(sofas.getchannel("A")) > 128,axis=0) > 10 + edges = np.flatnonzero(np.diff(np.r_[False,occupied,False])) + spans = [(int(a),int(b)) for a,b in zip(edges[::2],edges[1::2]) if b-a > 80] + assert len(spans) == 3, spans + boxes = [] + for lo,hi in spans: + box = sofas.crop((lo,0,hi,sofas.height)).getchannel("A").point(lambda a:255 if a>128 else 0).getbbox() + boxes.append((lo,box[1],hi,box[3])) + top = min(box[1] for box in boxes) + bottom = max(box[3] for box in boxes) + views = [sofas.crop((box[0],top,box[2],bottom)).resize((256,256),Image.Resampling.LANCZOS) + for box in boxes] + chairs = Image.open(ART/"chair-views-v1.png").convert("RGBA") + split = round(chairs.width*.55) + boxes = [(0,0,split,chairs.height//2),(split,0,chairs.width,chairs.height//2), + (0,chairs.height//2,split,chairs.height),(split,chairs.height//2,chairs.width,chairs.height)] + chair_views = [] + for box in boxes: + frame = chairs.crop(box) + tight = frame.getchannel("A").point(lambda a:255 if a>128 else 0).getbbox() + chair_views.append(frame.crop(tight).resize((256,256),Image.Resampling.LANCZOS)) + pack("details-v1.bin",panels+cutouts+views+chair_views) + facade = Image.open(ART/"city-facades-v1.png").convert("RGBA") + city = [facade.crop((round(x*facade.width/4),round(y*facade.height/2), + round((x+1)*facade.width/4),round((y+1)*facade.height/2))) + .resize((512,512),Image.Resampling.LANCZOS) + for y in range(2) for x in range(4)] + # Foliage-only tuft, not a planter photographed on a vertical card. + city.append(cutouts[1].crop((0,0,256,160)).resize((512,512),Image.Resampling.LANCZOS)) + earth = np.asarray(materials[1].resize((512,512))).copy() + earth[:,:,:3] = (earth[:,:,:3].astype(np.uint16)*np.array([37,31,24])//100).astype(np.uint8) + city.append(Image.fromarray(earth)) + city.append(Image.open(ART/"roof-zinc-v1.png").convert("RGBA").resize((512,512),Image.Resampling.LANCZOS)) + # Existing high-detail stone is also a road aggregate; water is a generated + # native texture below. Neither is a photograph of a whole city object. + road = np.asarray(materials[1].resize((512,512))).copy() + road[:,:,:3] = (road[:,:,:3].astype(np.uint16)*45//100).astype(np.uint8) + city.append(Image.fromarray(road)) + yy,xx = np.mgrid[:512,:512] + ripple = (np.sin(xx*.17+np.sin(yy*.09)*2)+np.sin(yy*.31))*4 + water = np.zeros((512,512,4),dtype=np.uint8) + for channel,base in enumerate((24,55,72)): + water[:,:,channel] = np.clip(base+ripple,0,255).astype(np.uint8) + water[:,:,3] = 255 + city.append(Image.fromarray(water)) + modern = Image.open(ART/"modern-facades-v1.png").convert("RGBA") + city += [modern.crop((round(x*modern.width/4),round(y*modern.height/2), + round((x+1)*modern.width/4),round((y+1)*modern.height/2))) + .resize((512,512),Image.Resampling.LANCZOS) + for y in range(2) for x in range(4)] + noise = np.random.default_rng(701).integers(-8,9,size=(512,512,1)) + lawn = np.empty((512,512,4),dtype=np.uint8) + lawn[:,:,:3] = np.clip(np.array([39,68,43])+noise,0,255) + lawn[:,:,3] = 255 + city.append(Image.fromarray(lawn)) + pack("city-v1.bin",city) + night_city,lit_windows = [],0 + for index,frame in enumerate(city): + if index < 8 or 13 <= index < 21: + frame,count = night_facade(frame,2000+index) + lit_windows += count + night_city.append(frame) + pack("city-night-v1.bin",night_city) + # A connected street/courtyard district, not a ring of independent props. + blocks = [] + lots,parks = [],[] + for gy in range(-5,6): + for gx in range(-5,6): + x,y = 20+gx*36,11+gy*36 + if gx == 0 and gy == 0: + continue + # The cross-city canal has continuous quays, not houses over water. + if y+14 > 54 and y-14 < 82: + continue + # Two open squares reserve space around the dome and golden spire. + if (gx,gy) in ((0,3),(4,0)): + continue + key = (gx*17+gy*31)%37 + if key%11 == 0 or (gx,gy) in ((1,0),(-1,0)): + parks.append((x,y)) + continue + facade = 186+(gx*3+gy)%8 + if max(abs(gx),abs(gy)) <= 1: + top = -60+6+key%6 + facade = 203 + wings = [(x-6,x+5,y-5,y+5)] + elif key%7 == 0: + top = -60+24+key%17 + facade = 199+(key%2)*5 + wings = [(x-8,x+8,y-10,y+10)] + else: + top = -60+12+key%16 + wings = [(x-14,x+14,y-14,y-7),(x-14,x+14,y+7,y+14), + (x-14,x-7,y-7,y+7),(x+7,x+14,y-7,y+7)] + if key%3 == 0: + wings = wings[:3] + elif key%5 == 0: + wings = wings[1:3] + lots.append({"centre":[x,y],"courtyard":[x-7,x+7,y-7,y+7], + "facade":facade,"height":top+60}) + for index,wing in enumerate(wings): + blocks.append((*wing,top+(index%2)*.3,facade,index)) + # Distant quarters are aggregated geometry, not a scaled city photograph. + for gy in range(-8,9): + for gx in range(-8,9): + if max(abs(gx),abs(gy)) <= 3: + continue + x,y = 20+gx*64,11+gy*64 + if y+24 > 54 and y-24 < 82: + continue + key = (gx*5+gy*11)%29 + top = -60+10+key + half = 17+key%10 + blocks.append((x-half,x+half,y-24,y+24,top,186+(gx+gy)%8,4)) + towers = [(320,-130,90),(355,-165,126),(390,-125,108),(425,-195,155), + (460,-145,102),(345,-225,115),(405,-260,132),(475,-240,88)] + for index,(x,y,height) in enumerate(towers): + blocks.append((x-8,x+8,y-10,y+10,-60+height,200+index%3,2)) + blocks.append((x-5,x+5,y-7,y+7,-60+height+7,200+index%3,2)) + # The eastern avenue opens into a broad river, not an endless paved corridor. + blocks = [b for b in blocks if not (b[1] > 68 and b[2] < 81 and b[3] > 26)] + # Far riverfront quarters break the empty, perfectly straight horizon. + for side in (-1,1): + for i in range(10): + x = 570+i*34 + y = 6-(i%3)*8 if side < 0 else 106+(i%4)*9 + h = 16+(i*7)%24 + blocks.append((x-11,x+11,y-10,y+10,-60+h,186+i%8,4)) + blocks.sort(key=lambda b:(((b[0]+b[1])/2-20)**2+((b[2]+b[3])/2-11)**2)) + # Ground-only baked illumination follows the same real building footprints. + elevation = np.zeros((512,512),dtype=np.float32) + for x0,x1,y0,y1,z,_,_ in blocks: + loX,hiX = max(0,math.floor(x0/2)+256),min(512,math.ceil(x1/2)+256) + loY,hiY = max(0,math.floor(y0/2)+256),min(512,math.ceil(y1/2)+256) + if loX < hiX and loY < hiY: + elevation[loY:hiY,loX:hiX] = np.maximum(elevation[loY:hiY,loX:hiX],z+60) + occlusion = np.zeros_like(elevation) + padded = np.pad(elevation,128) + for step in range(1,100): + sx,sy = round(-step*.89),round(step*.45) + ray_height = step*2*math.tan(math.radians(18)) + blocked = padded[128+sy:640+sy,128+sx:640+sx]-ray_height + occlusion = np.maximum(occlusion,blocked) + shadow = cv2.GaussianBlur(np.where(occlusion>0,155,250).astype(np.uint8),(5,5),1) + day = np.empty((512,512,4),dtype=np.uint8) + day[:,:,:3] = shadow[:,:,None] + day[:,:,3] = 255 + lamps = [] + for gx in range(-5,6): + for gy in range(-6,7): + lamps.append((34.8+36*gx,11+30*gy)) + lamps += [(x,y) for x in range(-170,211,18) for y in (58.5,77.5)] + lamps = [(x,y) for x,y in lamps if not (60 < y < 76 or (x > 68 and 29 < y < 76))] + lamps += [(x,y) for x in range(86,650,18) for y in (27,78)] + yy,xx = np.mgrid[:512,:512] + world_x,world_y = (xx-256)*2,(yy-256)*2 + illumination = np.zeros((512,512,3),dtype=np.float32)+[24,30,46] + for lx,ly in lamps: + energy = np.exp(-((world_x-lx)**2+(world_y-ly)**2)/(2*5.5**2)) + illumination += energy[:,:,None]*np.array([215,150,64]) + if ly in (27,78,58.5,77.5): + reflected = np.exp(-((world_x-lx)**2/(2*1.6**2)+(world_y-ly)**2/(2*13**2))) + reflected *= ((world_y>=60)&(world_y<=76)) | ((world_x>=70)&(world_y>=29)&(world_y<=76)) + illumination += reflected[:,:,None]*np.array([180,125,60]) + night_light = np.empty((512,512,4),dtype=np.uint8) + night_light[:,:,:3] = np.clip(illumination,0,255).astype(np.uint8) + night_light[:,:,3] = 255 + glow = np.empty_like(night_light) + glow[:,:,:3] = np.clip((illumination-np.array([24,30,46]))*.38,0,255).astype(np.uint8) + glow[:,:,3] = 255 + pack("lighting-v1.bin",[Image.fromarray(day),Image.fromarray(night_light),Image.fromarray(glow)]) + district = ["; Generated city block footprints and rooftop-relative heights.", + f"ROOF_BUILDING_COUNT equ {len(blocks)}", ".data", "roofDistrict3D LABEL DWORD"] + for block in blocks: + district.append(" dd "+",".join(str(round(v*65536)) if i<5 else str(v) + for i,v in enumerate(block))) + district += [f"ROOF_PARK_COUNT equ {len(parks)}","roofCityParks3D LABEL DWORD"] + district += [f" dd {round(x*65536)},{round(y*65536)}" for x,y in parks] + district += [f"ROOF_CITY_LAMP_COUNT equ {len(lamps)}","roofCityLamps3D LABEL DWORD"] + district += [f" dd {round(x*65536)},{round(y*65536)}" for x,y in lamps] + pieces = [] + def landmark_piece(kind,coords,material,pivot,target,scale=2): + ox,oy = pivot + tx,ty = target + if kind == 0: + a,b,c,d,e,f = coords + coords = ((a-ox)*scale+tx,(b-ox)*scale+tx,(c-oy)*scale+ty,(d-oy)*scale+ty, + (e+17)*scale-60,(f+17)*scale-60) + else: + a,b,c,d,e,f = coords + coords = ((a-ox)*scale+tx,(b-oy)*scale+ty,(c+17)*scale-60,d*scale,e*scale,f*scale) + pieces.append((kind,*[round(v*65536) for v in coords],material)) + dome = lambda kind,coords,mat: landmark_piece(kind,coords,mat,(56,47),(20,119)) + dome(0,(47,65,38,56,-17,2),193) + dome(0,(50,62,41,53,2,5),168) + for index in range(16): + angle = index*math.tau/16 + x,y = 56+5*math.cos(angle),47+5*math.sin(angle) + dome(0,(x,x+.35,y,y+.35,5,8),168) + dome(0,(50,62,41,53,8,8.4),196) + dome(1,(56,47,8.3,5.9,5.9,5),196) + dome(0,(55.4,56.6,46.4,47.6,13,15),166) + dome(0,(55.9,56.1,46.9,47.1,15,18),166) + spire = lambda kind,coords,mat: landmark_piece(kind,coords,mat,(-16,-61),(164,11)) + spire(0,(-23,-9,-68,-54,-17,0),191) + spire(0,(-19,-13,-64,-58,0,9),206) + spire(0,(-18,-14,-63,-59,9,15),191) + spire(2,(-16,-61,15,1.8,1.8,20),166) + district += [f"ROOF_LANDMARK_PARTS equ {len(pieces)}","roofLandmarkParts3D LABEL DWORD"] + district += [" dd "+",".join(map(str,piece)) for piece in pieces] + fog = [min(190,round(d*180/(d+250))) for d in range(1024)] + for start in range(0,1024,32): + district.append(("roofFog3D" if start == 0 else " ")+" db "+ + ",".join(map(str,fog[start:start+32]))) + district.append(".code") + (ROOT/"src_graphics/rooftop_district_data.inc").write_text("\n".join(district)+"\n",encoding="ascii") + (ART/"district-map-v1.json").write_text(json.dumps({"extent":[-520,560,-530,550], + "casino":[10.9,29.1,.9,21.1],"street_spacing":36,"street_width":8, + "street_level":-60,"canal":[-190,230,60,76],"bridges":[-34,38,110], + "east_river":[70,1800,29,76],"river_bridges":[110,254,398], + "lots":lots,"parks":parks,"lamps":lamps,"towers":towers,"buildings":blocks, + "landmarks":{"dome":[20,119],"spire":[164,11]}},indent=2)+"\n",encoding="ascii") + # Collision-only enclosure uses a valid legacy prepass material; the final + # rooftop pass replaces its tall walls with low world-space railings. + roof = [[102]*32 for _ in range(32)] + for y in range(1,21): + for x in range(11,29): + roof[y][x] = 0 + lines = ["; Generated by tools/generators/pack_rooftop.py.", ".data"] + for y,row in enumerate(roof): + lines.append(("roofMap3D" if y == 0 else " ")+" db "+",".join(map(str,row))) + columns = [round(math.atan((x/320-1)*.66)*2048/(2*math.pi)) for x in range(640)] + for start in range(0,640,16): + lines.append(("roofSkyColumns3D" if start == 0 else " ")+" dd "+ + ",".join(map(str,columns[start:start+16]))) + (ROOT/"src_graphics/rooftop_map.inc").write_text("\n".join(lines)+"\n",encoding="ascii") + (ART/"packed-v1.json").write_text(json.dumps({"panorama":[512,512,4], + "materials":[256,256,8],"details":[256,256,15],"city":[512,512,22], + "city-night":[512,512,22],"night":[512,512,4],"rail-hands":[640,320,32], + "lighting":[512,512,3],"city_renderer":"world-space MASM geometry; sky only is raster", + "source":"Built-in image generation; runtime geometry and interaction are assembly-only", + "reference":"https://www.loftprojectetagi.ru/visitors/krysha/"},indent=2)+"\n",encoding="ascii") + print(f"Packed roof: sunset/night, {lit_windows} distinct lit window apertures, 32 hand frames, {len(blocks)} buildings.") + + +if __name__ == "__main__": + main() diff --git a/tools/generators/pack_rooftop_gl.py b/tools/generators/pack_rooftop_gl.py new file mode 100644 index 0000000..2aa9e9e --- /dev/null +++ b/tools/generators/pack_rooftop_gl.py @@ -0,0 +1,319 @@ +"""Offline city meshes. Playback, GL submission and compositing remain MASM.""" +from pathlib import Path +import json +import math +import struct +from collections import Counter + +ROOT = Path(__file__).resolve().parents[2] +OUT = ROOT/"assets/animations/rooftop" +ART = ROOT/"assets/sprites/rooftop" + + +class Mesh: + def __init__(self): + self.triangles = [] + + def quad(self, material, points, uv=None, smooth=None): + # Points use the game's X/Y ground plane and Z up. GL receives X/Z/Y. + a,b,c = points[:3] + u = [b[i]-a[i] for i in range(3)] + v = [c[i]-a[i] for i in range(3)] + normal = (u[1]*v[2]-u[2]*v[1],u[2]*v[0]-u[0]*v[2],u[0]*v[1]-u[1]*v[0]) + size = math.sqrt(sum(x*x for x in normal)) + if size < 1e-8: + return + normal = tuple(x/size for x in normal) + uv = uv or [(0,0),(1,0),(1,1),(0,1)] + for ids in ((0,1,2),(0,2,3)): + if len({points[i] for i in ids}) < 3: + continue + vertices = [] + for i in ids: + x,y,z = points[i] + nx,ny,nz = smooth[i] if smooth else normal + # Vertical texture convention: the top of a facade is v=0. + vertices.extend((x,z,y,nx,nz,ny,*uv[i])) + self.triangles.append((material,vertices)) + + def box(self, x0,x1,y0,y1,z0,z1,material,roof=None,repeat_v=1): + if min(x1-x0,y1-y0,z1-z0) <= 0: + return + # Explicit winding/normals face outwards; negative scale is never used. + uv = [(0,repeat_v),(1,repeat_v),(1,0),(0,0)] + self.quad(material,[(x0,y0,z0),(x1,y0,z0),(x1,y0,z1),(x0,y0,z1)],uv) + self.quad(material,[(x1,y1,z0),(x0,y1,z0),(x0,y1,z1),(x1,y1,z1)],uv) + self.quad(material,[(x0,y1,z0),(x0,y0,z0),(x0,y0,z1),(x0,y1,z1)],uv) + self.quad(material,[(x1,y0,z0),(x1,y1,z0),(x1,y1,z1),(x1,y0,z1)],uv) + self.horizontal(x0,x1,y0,y1,z1,roof or material) + + def horizontal(self,x0,x1,y0,y1,z,material,uv=None): + points = [(x0,y1,z),(x1,y1,z),(x1,y0,z),(x0,y0,z)] + uv = uv or [(0,0),(1,0),(1,1),(0,1)] + self.quad(material,list(reversed(points)),list(reversed(uv))) + + def ellipsoid(self,x,y,z,rx,ry,rz,material,upper=False,rings=10,sides=24): + end = math.pi/2 if upper else math.pi + def point(t,p): + nx,ny,nz = math.sin(t)*math.cos(p),math.sin(t)*math.sin(p),math.cos(t) + n = (nx/rx,ny/ry,nz/rz) + length = math.sqrt(sum(v*v for v in n)) + return (x+rx*nx,y+ry*ny,z+rz*nz),tuple(v/length for v in n) + for row in range(rings): + t0,t1 = max(.0001,row*end/rings),(row+1)*end/rings + for col in range(sides): + p0,p1 = col*math.tau/sides,(col+1)*math.tau/sides + samples = [point(t0,p0),point(t1,p0),point(t1,p1),point(t0,p1)] + self.quad(material,[p for p,n in samples], + [(col/sides,row/rings),(col/sides,(row+1)/rings), + ((col+1)/sides,(row+1)/rings),((col+1)/sides,row/rings)], + [n for p,n in samples]) + + def cylinder(self,x,y,z0,z1,radius,material,sides=16,repeat=1): + for col in range(sides): + a,b = col*math.tau/sides,(col+1)*math.tau/sides + na,nb = (math.cos(a),math.sin(a),0),(math.cos(b),math.sin(b),0) + self.quad(material,[(x+radius*na[0],y+radius*na[1],z0), + (x+radius*nb[0],y+radius*nb[1],z0), + (x+radius*nb[0],y+radius*nb[1],z1), + (x+radius*na[0],y+radius*na[1],z1)], + uv=[(col*repeat/sides,1),((col+1)*repeat/sides,1), + ((col+1)*repeat/sides,0),(col*repeat/sides,0)], + smooth=[na,nb,nb,na]) + self.quad(material,[(x,y,z1),(x+radius*na[0],y+radius*na[1],z1), + (x+radius*nb[0],y+radius*nb[1],z1), + (x+radius*nb[0],y+radius*nb[1],z1)]) + + def hip(self,x0,x1,y0,y1,z,height,material=196): + if x1-x0 >= y1-y0: + ridge = [(x0+1,(y0+y1)/2,z+height),(x1-1,(y0+y1)/2,z+height)] + self.quad(material,[(x0,y0,z),(x1,y0,z),ridge[1],ridge[0]],[(0,0),((x1-x0)/4,0),((x1-x0)/4,1),(0,1)]) + self.quad(material,[(x1,y1,z),(x0,y1,z),ridge[0],ridge[1]],[(0,0),((x1-x0)/4,0),((x1-x0)/4,1),(0,1)]) + self.quad(material,[(x0,y1,z),(x0,y0,z),ridge[0],ridge[0]]) + self.quad(material,[(x1,y0,z),(x1,y1,z),ridge[1],ridge[1]]) + else: + # Transposing X/Y reverses winding; save() normalises every triangle. + cy = (x0+x1)/2 + a,b = (cy,y0+1,z+height),(cy,y1-1,z+height) + self.quad(material,[(x0,y1,z),(x0,y0,z),a,b],[(0,0),((y1-y0)/4,0),((y1-y0)/4,1),(0,1)]) + self.quad(material,[(x1,y0,z),(x1,y1,z),b,a],[(0,0),((y1-y0)/4,0),((y1-y0)/4,1),(0,1)]) + self.quad(material,[(x0,y0,z),(x1,y0,z),a,a]) + self.quad(material,[(x1,y1,z),(x0,y1,z),b,b]) + + def save(self,name,static=False): + original_count = len(self.triangles) + oriented = [] + for mat,packed in self.triangles: + vertices = [packed[i:i+8] for i in (0,8,16)] + a,b,c = [v[:3] for v in vertices] + u,v = [b[i]-a[i] for i in range(3)],[c[i]-a[i] for i in range(3)] + normal = [u[1]*v[2]-u[2]*v[1],u[2]*v[0]-u[0]*v[2],u[0]*v[1]-u[1]*v[0]] + if sum(x*x for x in normal) < 1e-16: + continue + expected = [sum(vertex[i+3] for vertex in vertices) for i in range(3)] + if sum(x*y for x,y in zip(normal,expected)) < 0: + vertices[1],vertices[2] = vertices[2],vertices[1] + normal = [-x for x in normal] + if static: + # Union of every standing/seated/leaning eye position, in GL X/Z/Y axes. + bounds = ((11.0,29.0),(1.05,1.8),(1.0,21.0)) + maximum = sum(n*((hi if n >= 0 else lo)-p) + for n,(lo,hi),p in zip(normal,bounds,a)) + if maximum < -1e-7: + continue + oriented.append((mat,[value for vertex in vertices for value in vertex])) + self.triangles = oriented + self.triangles.sort(key=lambda t:t[0]) + payload = b"".join(struct.pack("= x1 or y0 >= y1: + return [rect] + return [r for r in ((a,x0,c,d),(x1,b,c,d),(x0,x1,c,y0),(x0,x1,y1,d)) + if r[1]-r[0] > .01 and r[3]-r[2] > .01] + def road(rect,vertical=False): + pieces = [rect] + obstacles = [(-2000,2000,58,79),(68,2000,26,81)] + if vertical: + obstacles += [(-2000,2000,29+36*i-4,29+36*i+4) for i in range(-16,17)] + for obstacle in obstacles: + pieces = [p for r in pieces for p in subtract(r,obstacle)] + for a,b,c,d in pieces: + roads.append((a,b,c,d)) + scene.horizontal(a,b,c,d,-59.98,197,world_uv(a,b,c,d,4)) + roads = [] + for i in range(-16,17): + x = 38+36*i + road((x-4,x+4,-600,600),vertical=True) + y = 29+36*i + road((-600,600,y-4,y+4)) + # Small lane dashes and pedestrian crossings give the streets a readable scale. + for gx in range(-6,7): + x = 38+36*gx + for y in range(-205,231,9): + if not 54 < y < 82 and not (x > 68 and 24 < y < 82): + scene.horizontal(x-.07,x+.07,y,y+3,-59.965,168) + for gy in range(-6,7): + y = 29+36*gy + if 54 < y < 82 or (x > 68 and 24 < y < 82): + continue + for stripe in range(7): + sx = x-3.2+stripe*.95 + scene.horizontal(sx,sx+.45,y+5,y+7,-59.96,168) + for index,(x0,x1,y0,y1,roof,mat,kind) in enumerate(city["buildings"]): + modern = mat >= 199 + repeat = max(1,round((roof+60)/26)) if modern else 1 + scene.box(x0,x1,y0,y1,-60,roof,mat,repeat_v=repeat) + scene.box(x0-.18,x1+.18,y0-.18,y1+.18,roof,roof+.18,165 if modern else 168) + distance = math.hypot((x0+x1)/2-20,(y0+y1)/2-11) + if not modern and distance < 220: + scene.box(x0-.12,x1+.12,y0-.12,y1+.12,-60,-58.8,162) + for z in (roof-.55,roof-.3): + scene.box(x0-.25,x1+.25,y0-.25,y1+.25,z,z+.12,168) + if distance < 150: + for x in (x0,x1-.28): + for y in (y0,y1-.28): + scene.box(x-.08,x+.36,y-.08,y+.36,-58.8,roof-.5,168) + scene.hip(x0-.25,x1+.25,y0-.25,y1+.25,roof+.18,2.4) + scene.box(x0+.8,x0+1.45,y0+.8,y0+1.35,roof+1.2,roof+3.2,190,roof=162) + # Recessed dormer volumes interrupt long, otherwise empty roof slopes. + if x1-x0 > 12 and distance < 150: + for x in range(math.ceil(x0+3),math.floor(x1-2),6): + scene.box(x-.55,x+.55,y0+.8,y0+1.55,roof+.45,roof+1.45,168) + scene.quad(200,[(x-.4,y0+.79,roof+.6),(x+.4,y0+.79,roof+.6), + (x+.4,y0+.79,roof+1.25),(x-.4,y0+.79,roof+1.25)]) + scene.hip(x-.65,x+.65,y0+.7,y0+1.65,roof+1.45,.4) + else: + scene.horizontal(x0,x1,y0,y1,roof+.19,196) + # Static landmark records share their exact footprints with the MASM world. + data = (ROOT/"src_graphics/rooftop_district_data.inc").read_text() + section = data.split("roofLandmarkParts3D LABEL DWORD",1)[1].split("roofFog3D",1)[0] + for line in section.splitlines(): + if not line.strip().startswith("dd "): + continue + values = list(map(int,line.strip()[3:].split(","))) + kind,coords,mat = values[0],[v/65536 for v in values[1:7]],values[7] + # The dome's old rectangular drum is replaced with a proper circular one. + if kind == 0 and 7 < coords[0] < 12 and 128 < coords[3] < 134 and coords[4] > -30: + continue + if kind == 0: + x0,x1,y0,y1,z0,z1 = coords + if x1-x0 < 1.1 and y1-y0 < 1.1 and z1-z0 > 3: + scene.cylinder((x0+x1)/2,(y0+y1)/2,z0,z1,(x1-x0)/2,mat) + scene.box(x0-.12,x1+.12,y0-.12,y1+.12,z1-.25,z1,mat) + else: + scene.box(*coords,mat) + elif kind == 1: + scene.ellipsoid(*coords,mat,upper=True,rings=14,sides=48) + elif kind == 2: + x,y,z,rx,ry,h = coords + h *= .65 + for i in range(8): + a,b = i*math.tau/8,(i+1)*math.tau/8 + scene.quad(mat,[(x+rx*math.cos(a),y+ry*math.sin(a),z), + (x+rx*math.cos(b),y+ry*math.sin(b),z), + (x,y,z+h),(x,y,z+h)]) + # Cathedral drum, portico and a solid entablature under the copper dome. + scene.cylinder(20,119,-22,-10,9.5,191,sides=48,repeat=6) + for z,radius in ((-22,10.3),(-16,10.0),(-10.5,12.4)): + scene.cylinder(20,119,z,z+.45,radius,168,sides=48,repeat=6) + for step in range(4): + scene.box(3+step*.4,37-step*.4,95+step*.5,104,-60+step*.35,-59.65+step*.35,162) + for x in (6,11.5,17,22.5,28,33.5): + scene.cylinder(x,99,-58.6,-40,.55,168,sides=16) + scene.cylinder(x,99,-58.6,-57.8,.8,162,sides=16) + scene.cylinder(x,99,-40,-39.5,.8,168,sides=16) + scene.box(3,37,97,104,-39.5,-38.5,168) + scene.quad(168,[(3,96.99,-38.5),(37,96.99,-38.5),(20,96.99,-33),(20,96.99,-33)]) + scene.hip(2.8,37.2,96.8,104.2,-38.5,5.5) + for x in sorted(set(city["bridges"]+city["river_bridges"])): + start,end = (28,77) if x >= 70 else (59,77) + scene.horizontal(x-3,x+3,start,end,-59.9,197,world_uv(x-3,x+3,start,end,4)) + for side in (x-3,x+3): + for i in range(24): + y0,y1 = start+(end-start)*i/24,start+(end-start)*(i+1)/24 + mid,radius = (start+end)/2,(end-start)/2 + z0 = -62+1.8*math.sqrt(max(0,1-((y0-mid)/radius)**2)) + z1 = -62+1.8*math.sqrt(max(0,1-((y1-mid)/radius)**2)) + points = [(side,y0,z0),(side,y1,z1),(side,y1,-60),(side,y0,-60)] + scene.quad(162,list(reversed(points)) if side < x else points) + for y in range(start,end+1,2): + scene.box(side-.1,side+.1,y-.1,y+.1,-60,-59,165) + scene.box(side-.08,side+.08,start,end,-59.1,-59,165) + # Raised embankments, a tree-lined promenade and small piers follow the river. + for y0,y1 in ((26,29),(76,81)): + scene.box(70,1100,y0,y1,-62,-59.75,162) + for x in range(84,640,28): + y = (y0+y1)/2 + if min(abs(x-b) for b in city["river_bridges"]) < 6: + continue + scene.cylinder(x,y,-59.75,-56,.14,162,sides=6) + scene.ellipsoid(x,y,-55,1.7,1.7,2.1,207,rings=4,sides=8) + scene.box(x+3,x+5,y-.3,y+.3,-59.75,-59.3,162) + for x in (156,300,444): + scene.box(x,x+7,28,34,-62,-60.3,196) + for x,y in city["parks"]: + scene.horizontal(x-12,x+12,y-12,y+12,-59.9,207) + scene.horizontal(x-1,x+1,y-12,y+12,-59.8,162) + for dx in (-8,8): + for dy in (-8,0,8): + scene.cylinder(x+dx,y+dy,-60,-56,.15,162,sides=8) + scene.ellipsoid(x+dx,y+dy,-55.5,2.0,2.0,2.4,207,rings=5,sides=10) + for x,y in city.get("lamps",[]): + scene.cylinder(x,y,-60,-55,.055,165,sides=6) + scene.box(x-.22,x+.22,y-.22,y+.22,-55.15,-54.9,178) + # A second ground pass multiplies the baked, geometry-derived light map. + for a,b,c,d,water in patches(-512,512): + uv = [((a+512)/1024,(d+512)/1024),((b+512)/1024,(d+512)/1024), + ((b+512)/1024,(c+512)/1024),((a+512)/1024,(c+512)/1024)] + scene.horizontal(a,b,c,d,-61.94 if water else -59.94,208,uv) + scene.horizontal(a,b,c,d,-61.93 if water else -59.93,209,uv) + car = Mesh() + car.box(-1.9,1.9,-.8,.8,.25,.85,196) + car.box(-.9,.9,-.65,.65,.85,1.35,200) + for y in (-.57,.57): + car.box(1.88,1.93,y-.12,y+.12,.5,.68,178) + for x in (-1.2,1.2): + for y in (-.83,.83): + car.ellipsoid(x,y,.3,.32,.12,.32,197,rings=5,sides=10) + boat = Mesh() + boat.ellipsoid(0,0,-1.6,3,1,.5,196,rings=6,sides=16) + boat.box(-1,1,-.6,.6,-1.4,-.7,168) + counts = {"scene":scene.save("mesh-city-v1.bin",static=True),"car":car.save("mesh-car-v1.bin"), + "boat":boat.save("mesh-boat-v1.bin")} + (ART/"mesh-v1.json").write_text(json.dumps({"format":"RGM1: count/bytes + material/3*(position,normal,uv)", + "gl_axes":"X/Z/Y; normals explicitly outward; no runtime model engine", "triangles":counts, + "road_rectangles":roads,"river_patches":[p for p in patches(-2000,2000) if p[-1]], + "removed_invisible_triangles":scene.removed,"front_face":"CCW"},indent=2)+"\n") + print("Packed native GL meshes:",counts) + + +if __name__ == "__main__": + main() diff --git a/tools/tests/check_billiards.py b/tools/tests/check_billiards.py index e6e7adc..0010e92 100644 --- a/tools/tests/check_billiards.py +++ b/tools/tests/check_billiards.py @@ -6,6 +6,7 @@ from pathlib import Path from PIL import Image from check_card_exit_build import Coff from check_coin_detail import font, text_matches +from check_terminals import signature ROOT = Path(__file__).resolve().parents[2] OUT = ROOT / "build/actions" @@ -62,11 +63,11 @@ def main(parity=False): contact.paste(frames[name], ((i % 3) * 640, (i // 3) * 480)) contact.save(OUT / "billiards-contact.png") if parity: - tested = Coff(OUT / "actions-test.obj") + tested = Coff(ROOT / "build/billiards/module.obj") shipped = Coff(ROOT / "build/graphics/poker.obj") for name, size in FUNCTIONS.items(): symbol = f"_{name}@{size}" - assert tested.signature(symbol) == shipped.signature(symbol), name + assert signature(tested,symbol,tail_branches=True) == signature(shipped,symbol,tail_branches=True), name print("PASS: Russian selection, both ball sets, native captures, rebound prediction" + (" and shipping/tested billiards parity" if parity else "")) diff --git a/tools/tests/check_darts.py b/tools/tests/check_darts.py index 0f95480..8b380fe 100644 --- a/tools/tests/check_darts.py +++ b/tools/tests/check_darts.py @@ -133,7 +133,9 @@ def main(): 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.", + modern_ml = Path("C:/Program Files/Microsoft Visual Studio/18/Community/VC/Tools/MSVC/14.51.36231/bin/Hostx64/x86/ml.exe") + assembler = modern_ml if modern_ml.is_file() else masm / "bin/ml.exe" + subprocess.run([str(assembler), "/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" @@ -165,7 +167,7 @@ def main(): sway[0].save(OUT / "sway.gif", save_all=True, append_images=sway[1:], duration=40, loop=0) triples, = struct.unpack(" 1500 + for i,(sx,sy) in enumerate(zip(xs,ys)): + assert any((ox+x*step-sx)**2+(oy+y*step-sy)**2 < (1.8*65536)**2 + for x,y in reached),(i,"seat has no reachable approach") + for point in ((15.5,6.75),(27,18),(13,17)): + assert nav_point(*point) in reached,(point,"blocked rooftop route") + assert list(kinds).count(0) == 3 and list(kinds).count(1) == 4 + city = json.loads((ROOT/"assets/sprites/rooftop/district-map-v1.json").read_text()) + assert city["street_level"] == -60 + assert len(city["parks"]) >= 6 and len(city["towers"]) >= 8 + assert len(city["buildings"]) > 400 + heights = {round(b[4]+60,1) for b in city["buildings"]} + assert len(heights) >= 35 and min(heights) <= 6 and max(heights) >= 150 + assert len({(round(b[1]-b[0],1),round(b[3]-b[2],1)) for b in city["buildings"]}) >= 15 + assert {b[5] for b in city["buildings"]} >= {186,187,188,199,200,201,202,203,204} + for x0,x1,y0,y1,top,material,kind in city["buildings"]: + assert not (x0 < 29.1 and x1 > 10.9 and y0 < 21.1 and y1 > .9) + if max(abs((x0+x1)/2-20),abs((y0+y1)/2-11)) < 60: + assert top <= -19,"a neighbour must not block the sixty-metre-high terrace" + print(f"PASS: sixty-metre rooftop, {len(heights)} distinct city heights, streets/courtyards, parks, bridges and distant towers") + print(f"PASS: dense rooftop, {count} seats, {len(reached)} connected native walk points, lift/bar return and camera restore") + + def main(): parser = argparse.ArgumentParser() parser.add_argument("--parity",action="store_true") @@ -151,6 +204,7 @@ 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("--prove-fate-anchor-regression",action="store_true") args = parser.parse_args() if args.parity: native,shipping = Coff(OUT/"module.obj"),Coff(ROOT/"build/graphics/poker.obj") @@ -162,10 +216,25 @@ def main(): print("PASS: all tested navigation, collision, hall/upper rendering, guests and cabaret procedures match the shipping assembly") return prepare() + walk = read("casino3d.inc").split("check_walk_mode:",1)[1].split("check_slots_mode:",1)[0] + assert walk.index("je walk_world_ready") < walk.index("call RenderCeilingAndFloor") + assert walk.index("walk_world_ready:") > walk.index("call RenderSprites") room = proc(read("src_graphics/fortune_room.inc"), "RenderFortuneFurnishings3D") assert "DrawCityObject3D" not in room, "tarot furniture must not face the camera" assert room.count("DrawFortuneFixedProp3D") == 24 stem = "module" + if args.prove_fate_anchor_regression: + stem = "module-fate-anchor-regression" + source = (OUT/"module.asm").read_text(encoding="cp1251") + before = proc(source,"ResolveFortuneOrb3D") + broken = before.replace("mov fortuneOrbCenterX3D,eax", "mov fortuneOrbCenterX3D,278") + broken = broken.replace("mov fortuneOrbCenterY3D,eax", "mov fortuneOrbCenterY3D,259") + assert broken != before + source = source.replace(before,broken) + source = source.replace("include tools/tests/hall_expansion_test.inc", + expand("tools/tests/hall_expansion_test.inc")) + source = source.replace("build\\hall-expansion\\", "build\\hall-expansion\\anchor-regression-") + (OUT/(stem+".asm")).write_text(source,encoding="cp1251") if args.prove_bar_camera_regression: stem = "module-camera-regression" source = (OUT/"module.asm").read_text(encoding="cp1251") @@ -199,7 +268,9 @@ def main(): "invoke ExpansionPortalSnapshot,OFFSET expansionUpEntry\n invoke ExitProcess,0") (OUT/(stem+".asm")).write_text(source,encoding="cp1251") masm = Path("F:/MASM32/masm32") - subprocess.run([str(masm/"bin/ml.exe"),"/c","/coff","/Cp","/nologo","/I.", + modern_ml = Path("C:/Program Files/Microsoft Visual Studio/18/Community/VC/Tools/MSVC/14.51.36231/bin/Hostx64/x86/ml.exe") + assembler = modern_ml if modern_ml.is_file() else masm/"bin/ml.exe" + subprocess.run([str(assembler),"/c","/coff","/Cp","/nologo","/I.", f"/Fobuild/hall-expansion/{stem}.obj",f"build/hall-expansion/{stem}.asm"],cwd=ROOT,check=True) sdk = sorted(Path("C:/Program Files (x86)/Windows Kits/10/Lib").glob("*/um/x86"))[-1] subprocess.run([str(masm/"bin/link.exe"),"/nologo","/SUBSYSTEM:CONSOLE","/ENTRY:ExpansionTestStart@0", @@ -207,6 +278,10 @@ def main(): f"build/hall-expansion/{stem}.obj","build/hall-expansion/helpers.obj", "kernel32.lib","user32.lib","gdi32.lib","winmm.lib","ws2_32.lib"],cwd=ROOT,check=True) result = subprocess.run([str(OUT/(stem+".exe"))],cwd=ROOT,timeout=360) + if args.prove_fate_anchor_regression: + assert result.returncode == 81,result.returncode + print("PASS: restoring fixed screen coordinates fails the native crystal-ball tracking assertions") + return if args.prove_bar_camera_regression: assert result.returncode == 70,result.returncode print("PASS: removing camera recovery fails the native bar regression") @@ -220,6 +295,8 @@ def main(): print("PASS: restoring eight-metre cutoff fails the native far-doorway assertion") return assert result.returncode == 0,f"native case {result.returncode}" + if not args.prove_seam_regression: + check_rooftop_navigation() closed = Image.open(OUT/"fortune-closed.bmp").tobytes() opened = Image.open(OUT/"fortune-open.bmp").tobytes() assert closed != opened, "secret panel must visibly reveal the stairwell" @@ -246,6 +323,37 @@ def main(): 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) + fate_motion = [Image.open(OUT/f"fortune-fate-motion-{i:02}.bmp").convert("RGB") + for i in range(32)] + assert len({im.crop((134,32,414,323)).tobytes() for im in fate_motion}) >= 24 + fate_motion[0].save(OUT/"fortune-fate-mist.gif", save_all=True, + append_images=fate_motion[1:], duration=32, loop=0) + fate_result = Image.open(OUT/"fortune-fate-result.bmp").convert("RGB") + fate_form = Image.open(OUT/"fortune-fate-form.bmp").convert("RGB") + assert fate_form.crop((16,298,624,446)).tobytes() != fate_result.crop((16,298,624,446)).tobytes() + # Difference captures isolate the actual numeral, independently of fog highlights. + tracking = (ROOT/"assets/sprites/fortune/orb-tracking-v1.json").read_text() + anchors = json.loads(tracking)["frames"] + tracked = [] + for i, anchor in enumerate(anchors): + result = Image.open(OUT/f"fortune-fate-track-{i:02}.bmp").convert("RGB") + base = Image.open(OUT/f"fortune-fate-base-{i:02}.bmp").convert("RGB") + box = ImageChops.difference(result, base).getbbox() + assert box, (i, "no numeral rendered") + cx = 214 + int(anchor["cx"]*128/192 + .5) + cy = 195 + int(anchor["cy"]*128/192 + .5) + assert abs((box[0]+box[2])/2-cx) <= 2.5, (i, box, cx) + assert abs((box[1]+box[3])/2-cy) <= 2.5, (i, box, cy) + assert cx-42 <= box[0] < box[2] <= cx+42 + assert cy-28 <= box[1] < box[3] <= cy+28 + tracked.append(result) + assert max(a["cx"] for a in anchors)-min(a["cx"] for a in anchors) > 8 + assert max(a["cy"] for a in anchors)-min(a["cy"] for a in anchors) > 8 + tracked[0].save(OUT/"fortune-fate-tracked.gif", save_all=True, + append_images=tracked[1:], duration=64, loop=0) + numerals = (ROOT/"assets/animations/fortune/orb-numerals-v1.bin").read_bytes() + assert struct.unpack_from("<4s4I", numerals) == (b"ANM1", 32, 48, 10, 32*48*10*4) + assert len(set(numerals[23::4])) > 50, "font must retain soft edges and glow alpha" 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() @@ -270,8 +378,35 @@ def main(): assert dominant < 0.5,"stair entrance must show textured continuation on both sides, not a solid rear-wall block" for path in OUT.glob("*.bmp"): image = Image.open(path).convert("RGB") - assert image.size == (640,480) and len(image.getcolors(640*480)) > 100 + expected = (1280,960) if path.name.startswith("roof-hd-") else (640,480) + assert image.size == expected and len(image.getcolors(image.width*image.height)) > 100 image.save(path.with_suffix(".png")) + hd_day = Image.open(OUT/"roof-hd-south.bmp").convert("RGB") + hd_night = Image.open(OUT/"roof-hd-night.bmp").convert("RGB") + assert ImageChops.difference(hd_day,hd_night).getbbox(), "day/night must change actual native geometry lighting" + white_ground = sum(1 for rgb in hd_night.crop((0,650,1280,960)).get_flattened_data() if min(rgb) > 235) + assert white_ground < 500, "uninitialised emission textures must not turn ground white" + traffic = Image.open(OUT/"roof-hd-traffic.bmp").convert("RGB") + east = Image.open(OUT/"roof-hd-east.bmp").convert("RGB") + assert ImageChops.difference(east,traffic).getbbox(), "native clock must move city vehicles" + left = Image.open(OUT/"roof-hd-look-left.bmp").convert("RGB") + right = Image.open(OUT/"roof-hd-look-right.bmp").convert("RGB") + assert left.tobytes() != east.tobytes() != right.tobytes(), "leaning must allow actual head turns" + mesh = json.loads((ROOT/"assets/sprites/rooftop/mesh-v1.json").read_text()) + assert mesh["triangles"]["scene"] < 36000, "never-visible static faces must be pruned" + for profile in ("eco", "auto"): + low = Image.open(OUT/f"roof-hd-{profile}.bmp").convert("RGB") + difference = ImageStat.Stat(ImageChops.difference(low, east)).mean + assert max(difference) < 12, (profile, "quality must not remove scene geometry", difference) + roads = mesh["road_rectangles"] + for i,(a,b,c,d) in enumerate(roads): + for x0,x1,y0,y1 in roads[i+1:]: + assert min(b,x1)-max(a,x0) < 1e-6 or min(d,y1)-max(c,y0) < 1e-6, "coplanar street overlap" + millis,frames,cpu,gpu,present = struct.unpack("<5I",(OUT/"roof-hd-timing.bin").read_bytes()) + print(f"Roof passes ms/frame: CPU {cpu/frames:.2f}, GPU/readback {gpu/frames:.2f}, composite {present/frames:.2f}") + print(f"PASS: non-overlapping roads, head turns with anchored hands, final offscreen composite; {millis/frames:.2f} ms/frame in 24-frame native roof probe") + print("PASS: native GL city at 640/960/1280, auto downgrade and F8, direct foreground composite") + print("PASS: sunset/night, animated traffic, software roads/water even with missing city textures") gait = [Image.open(OUT/f"gait-{i}.bmp").convert("RGB") for i in range(8)] assert len({frame.crop((245,360,410,480)).tobytes() for frame in gait}) == 8, "legs must visibly change in native renders" gait[0].save(OUT/"gait-native.gif",save_all=True,append_images=gait[1:],duration=110,loop=0) diff --git a/tools/tests/check_jackbox.py b/tools/tests/check_jackbox.py index 6543d83..069bc84 100644 --- a/tools/tests/check_jackbox.py +++ b/tools/tests/check_jackbox.py @@ -64,11 +64,11 @@ def test_code_structure(): # Check constants assert "MODE_JACKBOX equ 28" in data_inc - assert "INTERACT_JACKBOX equ 48" in data_inc + assert "INTERACT_JACKBOX equ 52" in data_inc assert "JACKBOX_X equ FP_1*306/10" in data_inc assert "JACKBOX_Y equ FP_1*235/10" in data_inc assert "MATERIAL_JACKBOX_SIDE equ 121" in data_inc - assert "MATERIAL_JACKBOX_BACK equ 163" in data_inc + assert "MATERIAL_JACKBOX_BACK equ 213" in data_inc # Check procs procs = [ diff --git a/tools/tests/darts_test.inc b/tools/tests/darts_test.inc index 14f4386..7e5ad23 100644 --- a/tools/tests/darts_test.inc +++ b/tools/tests/darts_test.inc @@ -71,6 +71,51 @@ dtVectorCount3D equ 67 dtTestTriples3D dd 0 dtTestMetricPath db "build\darts\aim-metrics.bin",0 .code +DartsTestReticle PROC uses ebx esi edi + mov edi,OFFSET screenBuffer + mov ecx,640*480 + mov eax,00E0E0E0h + rep stosd + invoke DrawDartsReticle3D,320,214 + mov esi,OFFSET screenBuffer + mov ecx,640*480 + xor ebx,ebx +dt_test_reticle_light: + lodsd + cmp eax,00E0E0E0h + je @F + cmp eax,00152021h + jne dt_test_reticle_fail + inc ebx +@@: + loop dt_test_reticle_light + cmp ebx,9 + jne dt_test_reticle_fail + mov edi,OFFSET screenBuffer + mov ecx,640*480 + mov eax,00101010h + rep stosd + invoke DrawDartsReticle3D,0,32 + mov esi,OFFSET screenBuffer + mov ecx,640*480 + xor ebx,ebx +dt_test_reticle_dark: + lodsd + cmp eax,00101010h + je @F + cmp eax,00F6E6ADh + jne dt_test_reticle_fail + inc ebx +@@: + loop dt_test_reticle_dark + cmp ebx,5 + jne dt_test_reticle_fail + ret +dt_test_reticle_fail: + invoke ExitProcess,8506 + ret +DartsTestReticle ENDP + DartsTestWriteMetrics PROC uses ebx local written:DWORD invoke CreateFileA,OFFSET dtTestMetricPath,40000000h,0,0,2,80h,0 @@ -88,6 +133,7 @@ DartsTestWriteMetrics ENDP DartsTestStart PROC uses ebx esi edi local legs:DWORD call InitPaths3D + call DartsTestReticle lea esi,dtVectors3D mov ebx,dtVectorCount3D dt_test_vector: diff --git a/tools/tests/fortune_dialog_test.inc b/tools/tests/fortune_dialog_test.inc index 66d3ef9..9f17c44 100644 --- a/tools/tests/fortune_dialog_test.inc +++ b/tools/tests/fortune_dialog_test.inc @@ -1,6 +1,18 @@ .data fortuneSessionBank dd 0 fortuneSessionPlayer dd 0 +fortuneFateSavedResult dd 0 +fortuneFateSavedSeed dd 0 +fortuneFateTestText db "Ivan",0,"Petrov",0,"29022000",0,"IS-42",0 +fortuneFateFormShot db "build\hall-expansion\fortune-fate-form.bmp",0 +fortuneFateResultShot db "build\hall-expansion\fortune-fate-result.bmp",0 +fortuneFateHundredShot db "build\hall-expansion\fortune-fate-hundred.bmp",0 +fortuneFateMotion db "build\hall-expansion\fortune-fate-motion-" +fortuneFateDigits db "00.bmp",0 +fortuneFateTracking db "build\hall-expansion\fortune-fate-track-" +fortuneFateTrackingDigits db "00.bmp",0 +fortuneFateTrackingBase db "build\hall-expansion\fortune-fate-base-" +fortuneFateTrackingBaseDigits db "00.bmp",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 @@ -43,6 +55,8 @@ FortuneDialogNativeTests PROC uses ebx esi edi jne fortune_session_fail cmp fortuneSessionCards3D.frames,45 jne fortune_session_fail + cmp fortuneSessionNumerals3D.frames,10 + jne fortune_session_fail cmp fortuneProcess3D,0 jne fortune_session_fail cmp fortuneRead3D,0 @@ -181,7 +195,7 @@ fortune_session_unique_next: ja fortune_session_fail cmp fortuneSpeechHeight3D,140 ja fortune_session_fail - ; Return to selection, then display the neutral, non-grading orb sequence. + ; The fictional crystal-ball lottery requires a form, not a neural worker. mov mouseX3D,200 mov mouseY3D,232 mov mouseClick3D,1 @@ -191,6 +205,126 @@ fortune_session_unique_next: invoke WindowEvents3D,0,102h,'2',0 cmp fortuneSubMode3D,FORTUNE_SUBMODE_ORB jne fortune_session_fail + cmp fortunePhase3D,0 + jne fortune_session_fail + call SubmitFortuneFate3D + cmp fortunePhase3D,0 + jne fortune_session_fail + cmp fortuneFateField3D,0 + jne fortune_session_fail + ; CP1251, control filtering and maximum field widths use the real input path. + mov ebx,40 +fortune_fate_name_limit: + invoke WindowEvents3D,0,102h,200,0 + dec ebx + jnz fortune_fate_name_limit + cmp fortuneFateLengths3D,30 + jne fortune_session_fail + cmp fortuneFateFields3D[30],0 + jne fortune_session_fail + invoke FortuneChar3D,127 + cmp fortuneFateLengths3D,30 + jne fortune_session_fail + call ResetFortuneFate3D + mov esi,OFFSET fortuneFateTestText + xor ebx,ebx +fortune_fate_input_fields: + movzx eax,BYTE PTR [esi] + inc esi + test eax,eax + jz fortune_fate_input_next + invoke WindowEvents3D,0,102h,eax,0 + jmp fortune_fate_input_fields +fortune_fate_input_next: + invoke WindowEvents3D,0,102h,9,0 + inc ebx + cmp ebx,4 + jb fortune_fate_input_fields + cmp fortuneFateLengths3D[8],10 + jne fortune_session_fail + call ValidateFortuneBirth3D + cmp eax,1 + jne fortune_session_fail + mov fortuneFateFields3D[137],'1' + call ValidateFortuneBirth3D + test eax,eax + jnz fortune_session_fail + call SubmitFortuneFate3D + cmp fortuneFateField3D,2 + jne fortune_session_fail + cmp fortunePhase3D,0 + jne fortune_session_fail + mov fortuneFateFields3D[137],'0' + mov fortuneFateField3D,4 + invoke FortuneChar3D,'x' + cmp fortuneFateLengths3D[16],1 + jne fortune_session_fail + ; A value larger than 100 is rejected, not clamped or used as an outcome. + mov fortuneFateFields3D[320],'1' + mov fortuneFateFields3D[321],'0' + mov fortuneFateFields3D[322],'1' + mov fortuneFateLengths3D[20],3 + call SubmitFortuneFate3D + cmp fortunePhase3D,0 + jne fortune_session_fail + mov fortuneFateFields3D[320],'8' + mov fortuneFateFields3D[321],0 + mov fortuneFateFields3D[322],0 + mov fortuneFateLengths3D[20],1 + call SubmitFortuneFate3D + cmp fortunePhase3D,0 + jne fortune_session_fail + cmp fortuneFateField3D,5 + jne fortune_session_fail + mov fortuneFateFields3D[320],'1' + mov fortuneFateFields3D[321],'0' + mov fortuneFateLengths3D[20],2 + invoke SetFortuneSpeech3D,OFFSET fortuneFateReady3D + mov fortuneSpeechTime3D,3000 + call RenderFortuneDialog3D + invoke ExpansionSnapshot,OFFSET fortuneFateFormShot + ; Mouse focus and backspace obey per-field limits and reject control bytes. + mov mouseX3D,200 + mov mouseY3D,168 + mov mouseClick3D,1 + call HandleFortuneDialog3D + cmp fortuneFateField3D,1 + jne fortune_session_fail + invoke FortuneChar3D,8 + cmp fortuneFateLengths3D[4],5 + jne fortune_session_fail + invoke FortuneChar3D,'v' + invoke WindowEvents3D,0,100h,0Dh,0 + invoke WindowEvents3D,0,102h,0Dh,0 + call HandleFortuneDialog3D + invoke WindowEvents3D,0,101h,0Dh,0 + cmp fortunePhase3D,1 + jne fortune_session_fail + mov eax,fortuneFateResult3D + cmp eax,9 + jb fortune_session_fail + cmp eax,10 + ja fortune_session_fail + mov fortuneFateSavedResult,eax + mov eax,rngSeed3D + mov fortuneFateSavedSeed,eax + call SubmitFortuneFate3D + invoke FortuneChar3D,'X' + cmp fortuneFateLengths3D[4],6 + jne fortune_session_fail + mov eax,fortuneFateSavedResult + cmp fortuneFateResult3D,eax + jne fortune_session_fail + mov eax,rngSeed3D + cmp eax,fortuneFateSavedSeed + jne fortune_session_fail + ; Again cannot bypass the ritual or reroll while hands are moving. + mov mouseX3D,120 + mov mouseY3D,232 + mov mouseClick3D,1 + call HandleFortuneDialog3D + cmp fortunePhase3D,1 + jne fortune_session_fail mov fortunePhaseTime3D,700 call RenderFortuneDialog3D invoke ExpansionSnapshot,OFFSET fortuneSessionShot13 @@ -202,6 +336,27 @@ fortune_session_unique_next: mov fortuneSpeechTime3D,3000 call RenderFortuneDialog3D invoke ExpansionSnapshot,OFFSET fortuneSessionShot14 + xor edi,edi +fortune_fate_motion: + mov eax,edi + xor edx,edx + mov ecx,10 + div ecx + add al,'0' + add dl,'0' + mov fortuneFateDigits[0],al + mov fortuneFateDigits[1],dl + mov eax,edi + imul eax,32 + mov fortunePhaseTime3D,eax + call RenderFortuneDialog3D + invoke ExpansionSnapshot,OFFSET fortuneFateMotion + mov eax,fortuneFateSavedResult + cmp fortuneFateResult3D,eax + jne fortune_session_fail + inc edi + cmp edi,32 + jb fortune_fate_motion mov fortunePhaseTime3D,5000 call FortuneSessionStepTest cmp fortunePhase3D,3 @@ -209,6 +364,102 @@ fortune_session_unique_next: mov fortuneSpeechTime3D,3000 call RenderFortuneDialog3D invoke ExpansionSnapshot,OFFSET fortuneSessionShot15 + mov fortunePhaseTime3D,1200 + call RenderFortuneDialog3D + invoke ExpansionSnapshot,OFFSET fortuneFateResultShot + xor edi,edi +fortune_fate_tracking_frames: + mov eax,edi + xor edx,edx + mov ecx,10 + div ecx + add al,'0' + add dl,'0' + mov fortuneFateTrackingDigits[0],al + mov fortuneFateTrackingDigits[1],dl + mov fortuneFateTrackingBaseDigits[0],al + mov fortuneFateTrackingBaseDigits[1],dl + mov eax,edi + shl eax,6 + add eax,2048 + mov fortunePhaseTime3D,eax + call RenderFortuneDialog3D + cmp fortuneOrbFrame3D,edi + jne fortune_session_fail + mov eax,fortuneOrbAnchorX3D[edi*4] + add eax,128 + shr eax,8 + add eax,214 + cmp fortuneOrbCenterX3D,eax + jne fortune_session_fail + mov eax,fortuneOrbAnchorY3D[edi*4] + add eax,128 + shr eax,8 + add eax,195 + cmp fortuneOrbCenterY3D,eax + jne fortune_session_fail + cmp animOpacity3D,255 + jne fortune_session_fail + invoke ExpansionSnapshot,OFFSET fortuneFateTracking + mov fortuneFateResult3D,-1 + call RenderFortuneDialog3D + invoke ExpansionSnapshot,OFFSET fortuneFateTrackingBase + mov eax,fortuneFateSavedResult + mov fortuneFateResult3D,eax + inc edi + cmp edi,16 + jb fortune_fate_tracking_frames + mov mouseX3D,120 + mov mouseY3D,232 + mov mouseClick3D,1 + call HandleFortuneDialog3D + cmp fortunePhase3D,0 + jne fortune_session_fail + cmp fortuneFateResult3D,-1 + jne fortune_session_fail + cmp fortuneFateLengths3D[4],6 + jne fortune_session_fail + ; Test the full inclusive range, zero, fixed outcomes and both endpoints. + mov ebx,400 +fortune_fate_range_samples: + mov fortunePhase3D,0 + mov fortuneFateFields3D[256],'0' + mov fortuneFateLengths3D[16],1 + mov fortuneFateFields3D[320],'1' + mov fortuneFateFields3D[321],'0' + mov fortuneFateFields3D[322],'0' + mov fortuneFateFields3D[323],0 + mov fortuneFateLengths3D[20],3 + call SubmitFortuneFate3D + cmp fortuneFateResult3D,100 + ja fortune_session_fail + dec ebx + jnz fortune_fate_range_samples + ; Endpoints are checked separately without relying on a random sample. + mov fortunePhase3D,0 + mov fortuneFateFields3D[256],'0' + mov fortuneFateFields3D[320],'0' + mov fortuneFateFields3D[321],0 + mov fortuneFateLengths3D[20],1 + call SubmitFortuneFate3D + cmp fortuneFateResult3D,0 + jne fortune_session_fail + mov fortunePhase3D,0 + mov fortuneFateFields3D[256],'1' + mov fortuneFateFields3D[257],'0' + mov fortuneFateFields3D[258],'0' + mov fortuneFateLengths3D[16],3 + mov fortuneFateFields3D[320],'1' + mov fortuneFateFields3D[321],'0' + mov fortuneFateFields3D[322],'0' + mov fortuneFateLengths3D[20],3 + call SubmitFortuneFate3D + cmp fortuneFateResult3D,100 + jne fortune_session_fail + mov fortunePhase3D,3 + mov fortunePhaseTime3D,1200 + call RenderFortuneDialog3D + invoke ExpansionSnapshot,OFFSET fortuneFateHundredShot ; Exit in the middle of an animation as well as after completion. mov mouseX3D,260 mov mouseY3D,232 @@ -218,6 +469,12 @@ fortune_session_unique_next: jne fortune_session_fail cmp fortuneSessionRoom3D.pixels,0 jne fortune_session_fail + cmp fortuneSessionNumerals3D.pixels,0 + jne fortune_session_fail + cmp fortuneFateLengths3D,0 + jne fortune_session_fail + cmp fortuneFateFields3D,0 + jne fortune_session_fail call BeginFortuneDialog3D invoke FortuneChar3D,'1' invoke FortuneChar3D,'a' diff --git a/tools/tests/hall_expansion_test.inc b/tools/tests/hall_expansion_test.inc index c77d56d..9c1aa98 100644 --- a/tools/tests/hall_expansion_test.inc +++ b/tools/tests/hall_expansion_test.inc @@ -1,3 +1,4 @@ +RoofTestCrashFilter PROTO :DWORD .data expansionStage dd 1 expansionOcclusionPixel dd 0 @@ -91,10 +92,13 @@ ExpansionSnapshot ENDP ExpansionFrame PROC call UpdateCamera3D + cmp hallLevel3D,HALL_ROOF + je expansion_world_ready call RenderCeilingAndFloor call RenderWalls call PrepareCityDepth3D call RenderSprites +expansion_world_ready: call RenderHall3D ret ExpansionFrame ENDP @@ -130,6 +134,7 @@ expansion_door_tick: ExpansionDoorTicks ENDP ExpansionTestStart PROC + invoke SetUnhandledExceptionFilter,OFFSET RoofTestCrashFilter call InitPaths3D call LoadTextures3D call InitializeCasinoSprites3D @@ -1164,6 +1169,7 @@ expansion_leaf_shut: call BarCameraNativeTests call FortuneDialogNativeTests call ExpansionDartsTests + call RoofNativeTests invoke ExitProcess,0 expansion_fail: invoke ExitProcess,expansionStage @@ -1173,3 +1179,4 @@ 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 +include tools/tests/rooftop_test.inc diff --git a/tools/tests/rooftop_test.inc b/tools/tests/rooftop_test.inc new file mode 100644 index 0000000..0dd9098 --- /dev/null +++ b/tools/tests/rooftop_test.inc @@ -0,0 +1,769 @@ +ROOF_NAV_W equ 70 +ROOF_NAV_H equ 78 +.data +roofTestHall db "build\hall-expansion\roof-lift-hall.bmp",0 +roofTestOpening db "build\hall-expansion\roof-lift-opening.bmp",0 +roofTestCabin db "build\hall-expansion\roof-lift-cabin.bmp",0 +roofTestEntry db "build\hall-expansion\roof-arrival.bmp",0 +roofTestSouth db "build\hall-expansion\roof-terrace.bmp",0 +roofTestEast db "build\hall-expansion\roof-city-east.bmp",0 +roofTestWest db "build\hall-expansion\roof-city-west.bmp",0 +roofTestSeated db "build\hall-expansion\roof-seated.bmp",0 +roofTestBar db "build\hall-expansion\roof-bar.bmp",0 +roofTestMix db "build\hall-expansion\roof-bar-mixing.bmp",0 +roofTestReturn db "build\hall-expansion\roof-return.bmp",0 +roofTestChairSide db "build\hall-expansion\roof-chair-side.bmp",0 +roofTestChairBack db "build\hall-expansion\roof-chair-back.bmp",0 +roofTestPlanter db "build\hall-expansion\roof-planter.bmp",0 +roofTestLean db "build\hall-expansion\roof-lean.bmp",0 +roofTestNight db "build\hall-expansion\roof-night.bmp",0 +roofTestHDEast db "build\hall-expansion\roof-hd-east.bmp",0 +roofTestHDSouth db "build\hall-expansion\roof-hd-south.bmp",0 +roofTestHDNight db "build\hall-expansion\roof-hd-night.bmp",0 +roofTestHDComposite db "build\hall-expansion\roof-hd-terrace.bmp",0 +roofTestHDMotion db "build\hall-expansion\roof-hd-traffic.bmp",0 +roofTestHDLeft db "build\hall-expansion\roof-hd-look-left.bmp",0 +roofTestHDRight db "build\hall-expansion\roof-hd-look-right.bmp",0 +roofTestHDEco db "build\hall-expansion\roof-hd-eco.bmp",0 +roofTestHDAuto db "build\hall-expansion\roof-hd-auto.bmp",0 +roofTestSoftware db "build\hall-expansion\roof-software-ground.bmp",0 +roofTestMissingArt db "build\hall-expansion\roof-missing-art-ground.bmp",0 +roofTestHDTiming db "build\hall-expansion\roof-hd-timing.bin",0 +roofTestDriver db "build\hall-expansion\roof-driver.txt",0 +roofTestNewline db 13,10 +roofTestHDMillis dd 0,24,0,0,0 +roofTestHDBmp db 42h,4Dh + dd 54+ROOF_HD_W*ROOF_HD_H*4 + dw 0,0 + dd 54,40,ROOF_HD_W,-ROOF_HD_H + dw 1,32 + dd 0,ROOF_HD_W*ROOF_HD_H*4,0,0,0,0 +roofTestHDBMI dd 40,ROOF_HD_W,-ROOF_HD_H + dw 1,32 + dd 0,ROOF_HD_W*ROOF_HD_H*4,0,0,0,0 +roofTestHDDC dd 0 +roofTestHDBitmap dd 0 +roofTestHDPixels dd 0 +roofTestHDOld dd 0 +roofTestLeanMove db "build\hall-expansion\roof-lean-motion.bin",0 +roofTestGuestX dd 0 +roofTestFloorColor dd 0 +roofTestBank dd 0 +roofTestX dd 0 +roofTestY dd 0 +roofTestCrash db "build\hall-expansion\roof-crash.bin",0 +roofTestNavPath db "build\hall-expansion\roof-navigation.bin",0 +roofTestNavHeader dd ROOF_NAV_W,ROOF_NAV_H,FP_1*45/4,FP_1*5/4,FP_1/4,ROOF_SEAT_COUNT +.data? +roofTestWalkPixels db ROOF_NAV_W*ROOF_NAV_H dup(?) +.code +RoofTestHDSnapshot PROC uses ebx path:DWORD + local count:DWORD + call UpdateCamera3D + call RenderRoof3D + invoke PresentRoofHD3D,roofTestHDDC,0,0,ROOF_HD_W,ROOF_HD_H + test eax,eax + jz roof_test_hd_capture_failed + invoke GdiFlush + invoke CreateFileA,path,40000000h,0,0,2,80h,0 + mov ebx,eax + invoke WriteFile,ebx,OFFSET roofTestHDBmp,54,ADDR count,0 + invoke WriteFile,ebx,roofTestHDPixels,ROOF_HD_W*ROOF_HD_H*4,ADDR count,0 + invoke CloseHandle,ebx + ret +roof_test_hd_capture_failed: + invoke ExitProcess,95 + ret +RoofTestHDSnapshot ENDP + +RoofTestHD PROC uses ebx esi + local started:DWORD,written:DWORD,driverPtr:DWORD,driverLen:DWORD + mov expansionStage,94 + mov roofQualityMode3D,2 + call InitRoofHD3D + test eax,eax + jnz roof_test_hd_ready + mov eax,roofHDStage3D + add eax,100 + invoke ExitProcess,eax +roof_test_hd_ready: + invoke CreateFileA,OFFSET roofTestDriver,40000000h,0,0,2,80h,0 + mov ebx,eax + mov esi,1F00h +roof_test_driver_string: + invoke glGetString,esi + mov driverPtr,eax + xor ecx,ecx +roof_test_driver_length: + cmp BYTE PTR [eax+ecx],0 + je roof_test_driver_write + inc ecx + jmp roof_test_driver_length +roof_test_driver_write: + mov driverLen,ecx + invoke WriteFile,ebx,driverPtr,driverLen,ADDR written,0 + invoke WriteFile,ebx,OFFSET roofTestNewline,2,ADDR written,0 + inc esi + cmp esi,1F02h + jbe roof_test_driver_string + invoke CloseHandle,ebx + invoke CreateCompatibleDC,0 + mov roofTestHDDC,eax + invoke CreateDIBSection,eax,OFFSET roofTestHDBMI,0,OFFSET roofTestHDPixels,0,0 + mov roofTestHDBitmap,eax + invoke SelectObject,roofTestHDDC,eax + mov roofTestHDOld,eax + mov hallLevel3D,HALL_ROOF + mov engineMode,MODE_WALK + mov worldMap3D,OFFSET roofMap3D + mov floorTexture3D,ROOF_DECK + mov playerX,FP_1*275/10 + mov playerY,10*FP_1 + mov playerAngle,0 + mov roofLean3D,2 + mov roofLeanAngle3D,0 + mov roofLeanTime3D,1100 + mov roofNight3D,0 + mov keys3D[41h],1 + mov deltaTime3D,16 + add animationClock3D,16 + call UpdateRoof3D + cmp roofLean3D,2 + jne roof_test_hd_failed + cmp playerAngle,0 + je roof_test_hd_failed + mov keys3D[41h],0 + mov playerAngle,0 + invoke RoofTestHDSnapshot,OFFSET roofTestHDEast + add animationClock3D,1600 + invoke RoofTestHDSnapshot,OFFSET roofTestHDMotion + mov playerAngle,464 + invoke RoofTestHDSnapshot,OFFSET roofTestHDLeft + cmp roofLean3D,2 + jne roof_test_hd_failed + cmp playerAngle,464 + jne roof_test_hd_failed + cmp roofHandsContactX3D,320 + jle roof_test_hd_failed + mov playerAngle,48 + invoke RoofTestHDSnapshot,OFFSET roofTestHDRight + cmp playerAngle,48 + jne roof_test_hd_failed + cmp roofHandsContactX3D,320 + jge roof_test_hd_failed + mov playerAngle,180 + call UpdateRoofLook3D + cmp playerAngle,104 + jne roof_test_hd_failed + call RenderRoof3D + invoke PresentRoofHD3D,roofTestHDDC,0,0,ROOF_HD_W,ROOF_HD_H + test eax,eax + jz roof_test_hd_failed + mov playerAngle,408 + call RenderRoof3D + invoke PresentRoofHD3D,roofTestHDDC,0,0,ROOF_HD_W,ROOF_HD_H + test eax,eax + jz roof_test_hd_failed + mov roofLean3D,0 + mov playerX,20*FP_1 + mov playerY,20*FP_1 + mov playerAngle,128 + invoke RoofTestHDSnapshot,OFFSET roofTestHDSouth + mov roofNight3D,1 + invoke RoofTestHDSnapshot,OFFSET roofTestHDNight + mov roofNight3D,0 + mov playerX,20*FP_1 + mov playerY,7*FP_1 + invoke RoofTestHDSnapshot,OFFSET roofTestHDComposite + invoke GetTickCount + mov started,eax + mov esi,24 +roof_test_hd_frame_time: + add animationClock3D,16 + call RenderRoof3D + invoke PresentRoofHD3D,roofTestHDDC,0,0,ROOF_HD_W,ROOF_HD_H + test eax,eax + jz roof_test_hd_failed + dec esi + jnz roof_test_hd_frame_time + invoke GdiFlush + invoke GetTickCount + sub eax,started + mov roofTestHDMillis,eax + invoke GetTickCount + mov started,eax + mov esi,24 +roof_test_cpu_only_time: + call RenderRoof3D + dec esi + jnz roof_test_cpu_only_time + invoke GetTickCount + sub eax,started + mov roofTestHDMillis[8],eax + invoke GetTickCount + mov started,eax + mov esi,24 +roof_test_gpu_only_time: + add animationClock3D,33 + call RenderRoofHD3D + dec esi + jnz roof_test_gpu_only_time + invoke GetTickCount + sub eax,started + mov roofTestHDMillis[12],eax + invoke GetTickCount + mov started,eax + mov esi,24 +roof_test_present_only_time: + invoke PresentRoofHD3D,roofTestHDDC,0,0,ROOF_HD_W,ROOF_HD_H + dec esi + jnz roof_test_present_only_time + invoke GdiFlush + invoke GetTickCount + sub eax,started + mov roofTestHDMillis[16],eax + invoke CreateFileA,OFFSET roofTestHDTiming,40000000h,0,0,2,80h,0 + mov ebx,eax + invoke WriteFile,ebx,OFFSET roofTestHDMillis,20,ADDR written,0 + invoke CloseHandle,ebx + ; Quality profiles retain the full scene and a correctly aligned foreground. + mov playerX,FP_1*275/10 + mov playerY,10*FP_1 + mov playerAngle,0 + mov roofLean3D,2 + mov roofLeanAngle3D,0 + mov roofQualityMode3D,1 + invoke RoofTestHDSnapshot,OFFSET roofTestHDEco + cmp roofRenderW3D,640 + jne roof_test_hd_failed + cmp roofRenderH3D,480 + jne roof_test_hd_failed + mov roofQualityMode3D,0 + mov roofAutoWidth3D,960 + invoke RoofTestHDSnapshot,OFFSET roofTestHDAuto + cmp roofRenderW3D,960 + jne roof_test_hd_failed + mov deltaTime3D,100 + mov roofQualitySlow3D,0 + mov esi,11 +roof_test_auto_slow: + add animationClock3D,100 + call UpdateRoofQuality3D + dec esi + jnz roof_test_auto_slow + cmp roofRenderW3D,640 + jne roof_test_hd_failed + call ClearKeys3D + invoke WindowEvents3D,0,100h,77h,0 + add animationClock3D,16 + call UpdateRoofQuality3D + cmp roofQualityMode3D,1 + jne roof_test_hd_failed + call ClearKeys3D + invoke WindowEvents3D,0,100h,77h,0 + add animationClock3D,16 + call UpdateRoofQuality3D + cmp roofQualityMode3D,2 + jne roof_test_hd_failed + cmp roofRenderW3D,1280 + jne roof_test_hd_failed + call ClearKeys3D + mov roofLean3D,0 + invoke SelectObject,roofTestHDDC,roofTestHDOld + invoke DeleteObject,roofTestHDBitmap + invoke DeleteDC,roofTestHDDC + call FreeRoofHD3D + cmp roofHDWindow3D,0 + jne roof_test_hd_failed + cmp roofHDBuffer3D,0 + jne roof_test_hd_failed + cmp roofHDRC3D,0 + jne roof_test_hd_failed + cmp roofHDMeshScene3D[12],0 + jne roof_test_hd_failed + cmp roofHDFinalDC3D,0 + jne roof_test_hd_failed + cmp roofHDFinalBmp3D,0 + jne roof_test_hd_failed + ; Capability failure must return to software, without repeatedly opening contexts. + mov roofHDReady3D,-1 + invoke PresentRoofHD3D,0,0,0,1280,960 + test eax,eax + jnz roof_test_hd_failed + cmp roofHDReady3D,-1 + jne roof_test_hd_failed + ; Software roads and water remain visible even if the city pack is missing. + mov deltaTime3D,16 + mov roofQualityMode3D,0 + mov roofAutoWidth3D,960 + call UpdateCamera3D + call RenderRoof3D + cmp roofGroundRoadPixels3D,0 + je roof_test_hd_failed + cmp roofGroundWaterPixels3D,0 + je roof_test_hd_failed + invoke ExpansionSnapshot,OFFSET roofTestSoftware + mov eax,roofCityArt3D.pixels + push eax + mov roofCityArt3D.pixels,0 + call RenderRoof3D + cmp roofGroundRoadPixels3D,0 + je roof_test_hd_failed + cmp roofGroundWaterPixels3D,0 + je roof_test_hd_failed + invoke ExpansionSnapshot,OFFSET roofTestMissingArt + pop eax + mov roofCityArt3D.pixels,eax + mov roofHDReady3D,0 + ret +roof_test_hd_failed: + invoke ExitProcess,94 + ret +RoofTestHD ENDP + +RoofTestNavigation PROC uses ebx esi edi + local px:DWORD,py:DWORD,written:DWORD + lea edi,roofTestWalkPixels + xor esi,esi +roof_test_nav_row: + mov eax,esi + imul eax,FP_1/4 + add eax,FP_1*5/4 + mov py,eax + xor ebx,ebx +roof_test_nav_column: + mov eax,ebx + imul eax,FP_1/4 + add eax,FP_1*45/4 + mov px,eax + invoke CanWalk3D,px,py + mov [edi],al + inc edi + inc ebx + cmp ebx,ROOF_NAV_W + jb roof_test_nav_column + inc esi + cmp esi,ROOF_NAV_H + jb roof_test_nav_row + invoke CreateFileA,OFFSET roofTestNavPath,40000000h,0,0,2,80h,0 + mov ebx,eax + invoke WriteFile,ebx,OFFSET roofTestNavHeader,24,ADDR written,0 + invoke WriteFile,ebx,OFFSET roofSeatX3D,ROOF_SEAT_COUNT*4,ADDR written,0 + invoke WriteFile,ebx,OFFSET roofSeatY3D,ROOF_SEAT_COUNT*4,ADDR written,0 + invoke WriteFile,ebx,OFFSET roofSeatKind3D,ROOF_SEAT_COUNT*4,ADDR written,0 + invoke WriteFile,ebx,OFFSET roofTestWalkPixels,ROOF_NAV_W*ROOF_NAV_H,ADDR written,0 + invoke CloseHandle,ebx + ret +RoofTestNavigation ENDP +RoofTestCrashFilter PROC uses ebx esi pointers:DWORD + local written:DWORD + mov esi,pointers + mov esi,[esi+4] + invoke CreateFileA,OFFSET roofTestCrash,40000000h,0,0,2,80h,0 + mov ebx,eax + invoke WriteFile,ebx,esi,204,ADDR written,0 + invoke CloseHandle,ebx + invoke ExitProcess,expansionStage + mov eax,1 + ret +RoofTestCrashFilter ENDP +RoofTestTicks PROC uses esi count:DWORD + mov esi,count +roof_test_tick: + mov deltaTime3D,16 + add animationClock3D,16 + call UpdateInteriorTravel3D + call UpdateInteriorEye3D + dec esi + jnz roof_test_tick + ret +RoofTestTicks ENDP + +RoofNativeTests PROC uses ebx esi edi + invoke SetUnhandledExceptionFilter,OFFSET RoofTestCrashFilter + mov expansionStage,90 + mov eax,bankroll + mov roofTestBank,eax + call InitRoof3D + call LoadRoofAssets3D + cmp roofSkyArt3D.imageWidth,512 + jne roof_test_fail + cmp roofSkyArt3D.frames,4 + jne roof_test_fail + cmp roofDetailArt3D.frames,15 + jne roof_test_fail + cmp roofHandsArt3D.frames,32 + jne roof_test_fail + cmp roofCityArt3D.frames,22 + jne roof_test_fail + cmp roofNightArt3D.frames,4 + jne roof_test_fail + xor esi,esi +roof_test_surface_theme: + mov roofNight3D,esi + mov ebx,ROOF_DECK +roof_test_surface: + invoke ResolveRoofSurface3D,ebx + cmp eax,65535 + jbe roof_test_fail + mov edx,256 + cmp ebx,ROOF_FACADE + jb @F + mov edx,512 +@@: + cmp ebx,ROOF_INDICATOR + jne @F + mov edx,64 +@@: + cmp ebx,ROOF_LIGHT + jne @F + mov edx,1 +@@: + cmp ecx,edx + jne roof_test_fail + inc ebx + cmp ebx,ROOF_LAST_MATERIAL + jbe roof_test_surface + inc esi + cmp esi,2 + jb roof_test_surface_theme + mov roofNight3D,0 + invoke CityRayPlane3D,-512*FP_1,24*FP_1,405 + cmp eax,7FFFFFFFh + jne roof_test_fail + mov engineMode,MODE_WALK + mov hallLevel3D,0 + mov worldMap3D,OFFSET casinoMap + mov floorTexture3D,100 + mov playerX,LIFT_CENTRE_X + mov playerY,FP_1*63/10 + mov playerAngle,384 + call UpdateCamera3D + call UpdateInteriorEye3D + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestHall + mov playerY,FP_1*48/10 + call UpdateCamera3D + invoke CanWalk3D,LIFT_CENTRE_X,FP_1*34/10 + test eax,eax + jnz roof_test_fail + call CheckInteractions + cmp interactTargetId,INTERACT_LIFT_CALL + jne roof_test_fail + call TriggerHallInteraction3D + cmp liftPhase3D,1 + jne roof_test_fail + invoke RoofTestTicks,32 + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestOpening + invoke RoofTestTicks,36 + cmp liftDoor3D,1000 + jne roof_test_fail + invoke CanWalk3D,LIFT_CENTRE_X,FP_1*34/10 + test eax,eax + jz roof_test_fail + ; Walk through the opened doorway using the actual normal movement handler. + mov keys3D[57h],1 + mov esi,120 +roof_test_walk_in: + call ExpansionWalkFrame + cmp playerY,FP_1*26/10 + jb roof_test_inside + dec esi + jnz roof_test_walk_in + jmp roof_test_fail +roof_test_inside: + call ClearKeys3D + mov eax,playerX + mov roofTestX,eax + mov eax,playerY + mov roofTestY,eax + cmp hallLevel3D,0 + jne roof_test_fail + invoke WindowEvents3D,0,100h,32h,0 + invoke RoofTestTicks,1 + cmp roofNight3D,1 + jne roof_test_fail + call ClearKeys3D + invoke WindowEvents3D,0,100h,31h,0 + invoke RoofTestTicks,1 + cmp roofNight3D,0 + jne roof_test_fail + call ClearKeys3D + call CheckInteractions + cmp interactTargetId,INTERACT_LIFT_TRAVEL + jne roof_test_fail + call TriggerHallInteraction3D + cmp liftPhase3D,3 + jne roof_test_fail + invoke CanWalk3D,LIFT_CENTRE_X,2*FP_1 + test eax,eax + jnz roof_test_fail + invoke RoofTestTicks,68 + cmp liftPhase3D,4 + jne roof_test_fail + cmp hallLevel3D,0 + jne roof_test_fail + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestCabin + invoke RoofTestTicks,270 + cmp hallLevel3D,HALL_ROOF + jne roof_test_fail + cmp worldMap3D,OFFSET roofMap3D + jne roof_test_fail + cmp liftPhase3D,2 + jne roof_test_fail + mov eax,roofTestX + cmp playerX,eax + jne roof_test_fail + mov eax,roofTestY + cmp playerY,eax + jne roof_test_fail + mov playerAngle,128 + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestEntry + mov keys3D[57h],1 + mov esi,120 +roof_test_walk_out: + call ExpansionWalkFrame + cmp playerY,FP_1*46/10 + ja roof_test_outside + dec esi + jnz roof_test_walk_out + jmp roof_test_fail +roof_test_outside: + call ClearKeys3D + call RoofTestNavigation + mov expansionStage,91 + invoke CanWalk3D,29*FP_1,10*FP_1 + test eax,eax + jnz roof_test_fail + invoke CanWalk3D,15*FP_1,FP_1*45/10 + test eax,eax + jnz roof_test_fail + invoke CanWalk3D,FP_1*145/10,10*FP_1 + test eax,eax + jnz roof_test_fail + invoke CanWalk3D,FP_1*145/10,FP_1*77/10 + test eax,eax + jnz roof_test_fail + invoke CanWalk3D,12*FP_1,FP_1*68/10 + test eax,eax + jnz roof_test_fail + mov playerX,20*FP_1 + mov playerY,FP_1*66/10 + mov playerAngle,128 + call UpdateCamera3D + call UpdateRoofEye3D + call RenderCeilingAndFloor + mov eax,screenBuffer[(460*640+320)*4] + mov roofTestFloorColor,eax + call ExpansionFrame + mov eax,roofTestFloorColor + cmp screenBuffer[(460*640+320)*4],eax + jne roof_test_fail + cmp cityDepthBuffer3D[(460*640+320)*4],4*FP_1 + ja roof_test_fail + cmp playerX,20*FP_1 + jne roof_test_fail + cmp playerY,FP_1*66/10 + jne roof_test_fail + invoke ExpansionSnapshot,OFFSET roofTestSouth + mov playerAngle,0 + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestEast + mov playerAngle,256 + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestWest + mov playerX,FP_1*145/10 + mov playerY,FP_1*84/10 + mov playerAngle,128 + call UpdateCamera3D + call CheckInteractions + cmp interactTargetId,INTERACT_ROOF_SEAT + jne roof_test_fail + call TriggerHallInteraction3D + call UpdateInteriorEye3D + cmp roofSeat3D,1 + jne roof_test_fail + cmp viewEye3D,ROOF_SEATED_EYE + jne roof_test_fail + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestSeated + mov keys3D[57h],1 + invoke RoofTestTicks,1 + cmp roofSeat3D,0 + jne roof_test_fail + cmp playerY,FP_1*84/10 + jne roof_test_fail + call ClearKeys3D + xor ebx,ebx +roof_test_all_seats: + mov playerX,20*FP_1 + mov playerY,7*FP_1 + mov playerAngle,128 + mov roofTargetSeat3D,ebx + mov interactTargetId,INTERACT_ROOF_SEAT + call TryRoofInteraction3D + lea eax,[ebx+1] + cmp roofSeat3D,eax + jne roof_test_fail + call UpdateRoofEye3D + mov eax,ROOF_SEATED_EYE + cmp roofSeatKind3D[ebx*4],2 + jb @F + mov eax,ROOF_STANDING_EYE +@@: + cmp viewEye3D,eax + jne roof_test_fail + mov eax,384 + cmp roofSeatKind3D[ebx*4],3 + jne @F + mov eax,256 +@@: + cmp playerAngle,eax + jne roof_test_fail + mov interactTargetId,INTERACT_ROOF_SEAT + call TryRoofInteraction3D + cmp roofSeat3D,0 + jne roof_test_fail + cmp playerX,20*FP_1 + jne roof_test_fail + cmp playerY,7*FP_1 + jne roof_test_fail + cmp playerAngle,128 + jne roof_test_fail + call UpdateRoofEye3D + inc ebx + cmp ebx,ROOF_SEAT_COUNT + jb roof_test_all_seats + mov expansionStage,92 + mov playerX,20*FP_1 + mov playerY,7*FP_1 + mov engineMode,MODE_BAR + mov barPhase3D,0 + mov esi,30 +roof_test_bar_repeat: + call RenderBar3D + cmp playerX,20*FP_1 + jne roof_test_fail + cmp playerY,7*FP_1 + jne roof_test_fail + cmp viewEye3D,ROOF_STANDING_EYE + jne roof_test_fail + dec esi + jnz roof_test_bar_repeat + invoke ExpansionSnapshot,OFFSET roofTestBar + mov barPhase3D,1 + mov barSelection3D,1 + mov barTime3D,1500 + call RenderBar3D + invoke ExpansionSnapshot,OFFSET roofTestMix + mov engineMode,MODE_WALK + mov barPhase3D,0 + ; Scale and full furniture volumes must survive side and back inspection. + mov expansionStage,93 + mov playerX,FP_1*191/10 + mov playerY,10*FP_1 + mov playerAngle,256 + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestChairSide + mov playerX,FP_1*177/10 + mov playerY,FP_1*116/10 + mov playerAngle,384 + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestChairBack + mov playerX,FP_1*167/10 + mov playerY,FP_1*197/10 + mov playerAngle,256 + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestPlanter + ; The native gait advances position while every step respects roof collision. + mov eax,roofGuestX3D[0] + mov roofTestGuestX,eax + mov roofGuestWait3D[0],0 + mov roofGuestDir3D[0],0 + invoke RoofTestTicks,20 + mov eax,roofGuestX3D[0] + cmp eax,roofTestGuestX + je roof_test_fail + cmp roofGuestMoving3D[0],1 + jne roof_test_fail + mov playerX,FP_1*275/10 + mov playerY,10*FP_1 + mov playerAngle,0 + call UpdateCamera3D + call CheckRoofInteractions3D + cmp interactTargetId,INTERACT_ROOF_RAIL + jne roof_test_fail + call TryRoofInteraction3D + cmp roofLean3D,1 + jne roof_test_fail + invoke RoofTestTicks,75 + cmp roofLean3D,2 + jne roof_test_fail + cmp roofLeanTime3D,1100 + jne roof_test_fail + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestLean + invoke CanWalk3D,27*FP_1,10*FP_1 + test eax,eax + jnz roof_test_fail + mov roofNight3D,1 + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestNight + mov roofNight3D,0 + mov interactTargetId,INTERACT_ROOF_RAIL + call TryRoofInteraction3D + invoke RoofTestTicks,40 + cmp roofLean3D,0 + jne roof_test_fail + cmp playerX,FP_1*275/10 + jne roof_test_fail + cmp playerY,10*FP_1 + jne roof_test_fail + cmp viewEye3D,ROOF_STANDING_EYE + jne roof_test_fail + mov expansionStage,92 + invoke RoofTestTicks,250 + cmp liftDoor3D,0 + jne roof_test_fail + mov playerX,LIFT_CENTRE_X + mov playerY,FP_1*48/10 + mov playerAngle,384 + call UpdateCamera3D + call CheckInteractions + call TriggerHallInteraction3D + invoke RoofTestTicks,70 + mov playerY,FP_1*24/10 + call CheckInteractions + cmp interactTargetId,INTERACT_LIFT_TRAVEL + jne roof_test_fail + call TriggerHallInteraction3D + invoke RoofTestTicks,340 + cmp hallLevel3D,0 + jne roof_test_fail + cmp worldMap3D,OFFSET casinoMap + jne roof_test_fail + mov playerY,FP_1*48/10 + call ExpansionFrame + invoke ExpansionSnapshot,OFFSET roofTestReturn + mov eax,bankroll + cmp eax,roofTestBank + jne roof_test_fail + call FreeRoofAssets3D + cmp roofSkyArt3D.pixels,0 + jne roof_test_fail + cmp roofMaterialArt3D.pixels,0 + jne roof_test_fail + cmp roofDetailArt3D.pixels,0 + jne roof_test_fail + call LoadRoofAssets3D + call RoofTestHD + mov hallLevel3D,0 + mov worldMap3D,OFFSET casinoMap + ret +roof_test_fail: + invoke ExitProcess,expansionStage + ret +RoofNativeTests ENDP diff --git a/tools/tests/test_billiards_module.ps1 b/tools/tests/test_billiards_module.ps1 index 503837f..3cfe1d6 100644 --- a/tools/tests/test_billiards_module.ps1 +++ b/tools/tests/test_billiards_module.ps1 @@ -5,7 +5,10 @@ Push-Location $root try { & 'C:\Python314\python.exe' tools\tests\prepare_billiards_module.py if ($LASTEXITCODE -ne 0) { throw 'Billiards helper preparation failed' } - & "$MasmRoot\bin\ml.exe" /c /coff /Cp /nologo /I. /Fobuild\billiards\module.obj tools\tests\billiards_module.asm + $assembler = "$MasmRoot\bin\ml.exe" + $modernAssembler = 'C:\Program Files\Microsoft Visual Studio\18\Community\VC\Tools\MSVC\14.51.36231\bin\Hostx64\x86\ml.exe' + if (Test-Path -LiteralPath $modernAssembler) { $assembler = $modernAssembler } + & $assembler /c /coff /Cp /nologo /I. /Fobuild\billiards\module.obj tools\tests\billiards_module.asm if ($LASTEXITCODE -ne 0) { throw 'Billiards module assembly failed' } $sdk = Get-ChildItem -LiteralPath "${env:ProgramFiles(x86)}\Windows Kits\10\Lib" -Directory | Sort-Object Name -Descending |