The game drew 2x2 px red dots per visited-sector neighbour (0x6c8580), in 64-tile steps and unrelated to the explored mask. Replaced by lines traced from the explored cells (2x2 tiles), simplified to 0.75 px, rebuilt while the map is open when the masks change. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
115 lines
5.7 KiB
C++
115 lines
5.7 KiB
C++
#pragma once
|
|
// Untextured quads on the UI's Direct3D 7 device (the argument of the UI's
|
|
// render methods, vf14).
|
|
|
|
#include <cstdint>
|
|
|
|
#include <windows.h>
|
|
|
|
namespace game {
|
|
|
|
constexpr int kDeviceDrawPrimitive = 25; // IDirect3DDevice7 vtable slots
|
|
constexpr int kDeviceSetTexture = 35;
|
|
constexpr int kDeviceSetRenderState = 20;
|
|
constexpr int kDeviceGetRenderState = 21;
|
|
constexpr DWORD kStateSourceBlend = 19; // D3DRENDERSTATETYPE
|
|
constexpr DWORD kStateDestinationBlend = 20;
|
|
constexpr DWORD kStateAlphaBlendEnable = 27;
|
|
constexpr DWORD kBlendOne = 2; // D3DBLEND
|
|
constexpr DWORD kTriangleStrip = 5;
|
|
constexpr DWORD kFvfScreenColourTexture = 0x1c4; // XYZRHW | DIFFUSE | SPECULAR | TEX1
|
|
|
|
struct ScreenVertex {
|
|
float x, y, z, rhw;
|
|
uint32_t diffuse, specular;
|
|
float u, v;
|
|
};
|
|
|
|
// Draws (x0, y0)-(x1, y1) with `surface` (a texture's Direct3D surface,
|
|
// game/ui/texture.h) from (u0, v0) to (u1, v1), tinted by `colour`.
|
|
inline void DrawTextured(void* device, void* surface, float x0, float y0, float x1, float y1, float u0, float v0,
|
|
float u1, float v1, uint32_t colour = 0xffffffff) {
|
|
void** vtable = *static_cast<void***>(device);
|
|
using SetTextureFn = HRESULT(__stdcall*)(void*, DWORD, void*);
|
|
using DrawPrimitiveFn = HRESULT(__stdcall*)(void*, DWORD, DWORD, void*, DWORD, DWORD);
|
|
reinterpret_cast<SetTextureFn>(vtable[kDeviceSetTexture])(device, 0, surface);
|
|
ScreenVertex quad[4] = {{x0, y0, 0, 1, colour, 0, u0, v0},
|
|
{x1, y0, 0, 1, colour, 0, u1, v0},
|
|
{x0, y1, 0, 1, colour, 0, u0, v1},
|
|
{x1, y1, 0, 1, colour, 0, u1, v1}};
|
|
reinterpret_cast<DrawPrimitiveFn>(vtable[kDeviceDrawPrimitive])(device, kTriangleStrip, kFvfScreenColourTexture,
|
|
quad, 4, 0);
|
|
reinterpret_cast<SetTextureFn>(vtable[kDeviceSetTexture])(device, 0, nullptr);
|
|
}
|
|
|
|
// DrawTextured added onto what is drawn (source and destination blend ONE):
|
|
// for the effect textures, drawn on black without alpha.
|
|
inline void DrawTexturedAdded(void* device, void* surface, float x0, float y0, float x1, float y1, float u0,
|
|
float v0, float u1, float v1) {
|
|
void** vtable = *static_cast<void***>(device);
|
|
using SetRenderStateFn = HRESULT(__stdcall*)(void*, DWORD, DWORD);
|
|
using GetRenderStateFn = HRESULT(__stdcall*)(void*, DWORD, DWORD*);
|
|
const auto set = reinterpret_cast<SetRenderStateFn>(vtable[kDeviceSetRenderState]);
|
|
const auto get = reinterpret_cast<GetRenderStateFn>(vtable[kDeviceGetRenderState]);
|
|
DWORD blending = 0, source = 0, destination = 0;
|
|
get(device, kStateAlphaBlendEnable, &blending);
|
|
get(device, kStateSourceBlend, &source);
|
|
get(device, kStateDestinationBlend, &destination);
|
|
set(device, kStateAlphaBlendEnable, TRUE);
|
|
set(device, kStateSourceBlend, kBlendOne);
|
|
set(device, kStateDestinationBlend, kBlendOne);
|
|
DrawTextured(device, surface, x0, y0, x1, y1, u0, v0, u1, v1);
|
|
set(device, kStateAlphaBlendEnable, blending);
|
|
set(device, kStateSourceBlend, source);
|
|
set(device, kStateDestinationBlend, destination);
|
|
}
|
|
|
|
// Fills (x0, y0)-(x1, y1), ARGB colours per corner: top left, top right,
|
|
// bottom left, bottom right (blended between them).
|
|
inline void FillCorners(void* device, float x0, float y0, float x1, float y1, uint32_t topLeft, uint32_t topRight,
|
|
uint32_t bottomLeft, uint32_t bottomRight) {
|
|
void** vtable = *static_cast<void***>(device);
|
|
using SetTextureFn = HRESULT(__stdcall*)(void*, DWORD, void*);
|
|
using DrawPrimitiveFn = HRESULT(__stdcall*)(void*, DWORD, DWORD, void*, DWORD, DWORD);
|
|
reinterpret_cast<SetTextureFn>(vtable[kDeviceSetTexture])(device, 0, nullptr);
|
|
ScreenVertex quad[4] = {{x0, y0, 0, 1, topLeft, 0, 0, 0},
|
|
{x1, y0, 0, 1, topRight, 0, 1, 0},
|
|
{x0, y1, 0, 1, bottomLeft, 0, 0, 1},
|
|
{x1, y1, 0, 1, bottomRight, 0, 1, 1}};
|
|
reinterpret_cast<DrawPrimitiveFn>(vtable[kDeviceDrawPrimitive])(device, kTriangleStrip, kFvfScreenColourTexture,
|
|
quad, 4, 0);
|
|
}
|
|
|
|
// Untextured triangles, three vertices each (a triangle list), in batches the
|
|
// device takes in one call.
|
|
inline void FillTriangles(void* device, ScreenVertex* vertices, size_t count) {
|
|
constexpr DWORD kTriangleList = 4;
|
|
constexpr size_t kBatch = 3 * 10000;
|
|
void** vtable = *static_cast<void***>(device);
|
|
using SetTextureFn = HRESULT(__stdcall*)(void*, DWORD, void*);
|
|
using DrawPrimitiveFn = HRESULT(__stdcall*)(void*, DWORD, DWORD, void*, DWORD, DWORD);
|
|
reinterpret_cast<SetTextureFn>(vtable[kDeviceSetTexture])(device, 0, nullptr);
|
|
for (size_t first = 0; first < count; first += kBatch) {
|
|
const size_t batch = count - first < kBatch ? count - first : kBatch;
|
|
reinterpret_cast<DrawPrimitiveFn>(vtable[kDeviceDrawPrimitive])(
|
|
device, kTriangleList, kFvfScreenColourTexture, vertices + first, static_cast<DWORD>(batch), 0);
|
|
}
|
|
}
|
|
|
|
// Fills (x0, y0)-(x1, y1) with the ARGB colour `color`.
|
|
inline void FillRect(void* device, float x0, float y0, float x1, float y1, uint32_t color) {
|
|
FillCorners(device, x0, y0, x1, y1, color, color, color, color);
|
|
}
|
|
|
|
// Top to bottom from `top` to `bottom`.
|
|
inline void FillVertical(void* device, float x0, float y0, float x1, float y1, uint32_t top, uint32_t bottom) {
|
|
FillCorners(device, x0, y0, x1, y1, top, top, bottom, bottom);
|
|
}
|
|
|
|
// Left to right from `left` to `right`.
|
|
inline void FillHorizontal(void* device, float x0, float y0, float x1, float y1, uint32_t left, uint32_t right) {
|
|
FillCorners(device, x0, y0, x1, y1, left, right, left, right);
|
|
}
|
|
|
|
} // namespace game
|