fallout2-ce/src/tile.cc

1979 lines
48 KiB
C++
Raw Normal View History

2022-05-19 01:51:26 -07:00
#include "tile.h"
2022-09-15 02:38:23 -07:00
#include <assert.h>
#include <math.h>
#include <string.h>
2022-06-19 03:32:42 -07:00
#include "art.h"
2022-05-19 01:51:26 -07:00
#include "color.h"
#include "config.h"
#include "core.h"
#include "debug.h"
#include "draw.h"
#include "game_config.h"
#include "game_mouse.h"
#include "light.h"
#include "map.h"
#include "object.h"
#include "platform_compat.h"
2022-05-19 01:51:26 -07:00
2022-09-23 05:43:44 -07:00
namespace fallout {
2022-06-18 22:02:47 -07:00
typedef struct STRUCT_51D99C {
int field_0;
int field_4;
} STRUCT_51D99C;
typedef struct STRUCT_51DA04 {
int field_0;
int field_4;
} STRUCT_51DA04;
typedef struct STRUCT_51DA6C {
int field_0;
int field_4;
int field_8;
int field_C; // something with light level?
} STRUCT_51DA6C;
typedef struct STRUCT_51DB0C {
int field_0;
int field_4;
int field_8;
} STRUCT_51DB0C;
typedef struct STRUCT_51DB48 {
int field_0;
int field_4;
int field_8;
} STRUCT_51DB48;
2022-06-18 22:07:06 -07:00
static void tileSetBorder(int windowWidth, int windowHeight, int hexGridWidth, int hexGridHeight);
static void tileRefreshMapper(Rect* rect, int elevation);
static void tileRefreshGame(Rect* rect, int elevation);
static void _roof_fill_on(int x, int y, int elevation);
static void sub_4B23DC(int x, int y, int elevation);
static void tileRenderRoof(int fid, int x, int y, Rect* rect, int light);
static void _draw_grid(int tile, int elevation, Rect* rect);
static void tileRenderFloor(int fid, int x, int y, Rect* rect);
static int _tile_make_line(int currentCenterTile, int newCenterTile, int* tiles, int tilesCapacity);
2022-06-18 22:02:47 -07:00
2022-05-19 01:51:26 -07:00
// 0x50E7C7
2022-06-18 22:02:47 -07:00
static double const dbl_50E7C7 = -4.0;
2022-05-19 01:51:26 -07:00
// 0x51D950
2022-06-18 22:02:47 -07:00
static bool gTileBorderInitialized = false;
2022-05-19 01:51:26 -07:00
// 0x51D954
2022-06-18 22:02:47 -07:00
static bool gTileScrollBlockingEnabled = true;
2022-05-19 01:51:26 -07:00
// 0x51D958
2022-06-18 22:02:47 -07:00
static bool gTileScrollLimitingEnabled = true;
2022-05-19 01:51:26 -07:00
// 0x51D95C
2022-06-18 22:02:47 -07:00
static bool gTileRoofIsVisible = true;
2022-05-19 01:51:26 -07:00
// 0x51D960
2022-06-18 22:02:47 -07:00
static bool gTileGridIsVisible = false;
2022-05-19 01:51:26 -07:00
// 0x51D964
2022-06-18 22:02:47 -07:00
static TileWindowRefreshElevationProc* gTileWindowRefreshElevationProc = tileRefreshGame;
2022-05-19 01:51:26 -07:00
// 0x51D968
2022-06-18 22:02:47 -07:00
static bool gTileEnabled = true;
2022-05-19 01:51:26 -07:00
// 0x51D96C
const int _off_tile[6] = {
16,
32,
16,
-16,
-32,
-16,
};
// 0x51D984
const int dword_51D984[6] = {
-12,
0,
12,
12,
0,
-12,
};
// 0x51D99C
2022-06-18 22:02:47 -07:00
static STRUCT_51D99C _rightside_up_table[13] = {
2022-05-19 01:51:26 -07:00
{ -1, 2 },
{ 78, 2 },
{ 76, 6 },
{ 73, 8 },
{ 71, 10 },
{ 68, 14 },
{ 65, 16 },
{ 63, 18 },
{ 61, 20 },
{ 58, 24 },
{ 55, 26 },
{ 53, 28 },
{ 50, 32 },
};
// 0x51DA04
2022-06-18 22:02:47 -07:00
static STRUCT_51DA04 _upside_down_table[13] = {
2022-05-19 01:51:26 -07:00
{ 0, 32 },
{ 48, 32 },
{ 49, 30 },
{ 52, 26 },
{ 55, 24 },
{ 57, 22 },
{ 60, 18 },
{ 63, 16 },
{ 65, 14 },
{ 67, 12 },
{ 70, 8 },
{ 73, 6 },
{ 75, 4 },
};
// 0x51DA6C
2022-06-18 22:02:47 -07:00
static STRUCT_51DA6C _verticies[10] = {
2022-05-19 01:51:26 -07:00
{ 16, -1, -201, 0 },
{ 48, -2, -2, 0 },
{ 960, 0, 0, 0 },
{ 992, 199, -1, 0 },
{ 1024, 198, 198, 0 },
{ 1936, 200, 200, 0 },
{ 1968, 399, 199, 0 },
{ 2000, 398, 398, 0 },
{ 2912, 400, 400, 0 },
{ 2944, 599, 399, 0 },
};
// 0x51DB0C
2022-06-18 22:02:47 -07:00
static STRUCT_51DB0C _rightside_up_triangles[5] = {
2022-05-19 01:51:26 -07:00
{ 2, 3, 0 },
{ 3, 4, 1 },
{ 5, 6, 3 },
{ 6, 7, 4 },
{ 8, 9, 6 },
};
// 0x51DB48
2022-06-18 22:02:47 -07:00
static STRUCT_51DB48 _upside_down_triangles[5] = {
2022-05-19 01:51:26 -07:00
{ 0, 3, 1 },
{ 2, 5, 3 },
{ 3, 6, 4 },
{ 5, 8, 6 },
{ 6, 9, 7 },
};
// 0x668224
2022-06-18 22:02:47 -07:00
static int _intensity_map[3280];
2022-05-19 01:51:26 -07:00
// 0x66B564
2022-06-18 22:02:47 -07:00
static int _dir_tile2[2][6];
2022-05-19 01:51:26 -07:00
// Deltas to perform tile calculations in given direction.
//
// 0x66B594
2022-06-18 22:02:47 -07:00
static int _dir_tile[2][6];
2022-05-19 01:51:26 -07:00
// 0x66B5C4
2022-06-18 22:02:47 -07:00
static unsigned char _tile_grid_blocked[512];
2022-05-19 01:51:26 -07:00
// 0x66B7C4
2022-06-18 22:02:47 -07:00
static unsigned char _tile_grid_occupied[512];
2022-05-19 01:51:26 -07:00
// 0x66B9C4
2022-06-18 22:02:47 -07:00
static unsigned char _tile_mask[512];
2022-05-19 01:51:26 -07:00
// 0x66BBC4
2022-06-18 22:02:47 -07:00
static int gTileBorderMinX = 0;
2022-05-19 01:51:26 -07:00
// 0x66BBC8
2022-06-18 22:02:47 -07:00
static int gTileBorderMinY = 0;
2022-05-19 01:51:26 -07:00
// 0x66BBCC
2022-06-18 22:02:47 -07:00
static int gTileBorderMaxX = 0;
2022-05-19 01:51:26 -07:00
// 0x66BBD0
2022-06-18 22:02:47 -07:00
static int gTileBorderMaxY = 0;
2022-05-19 01:51:26 -07:00
// 0x66BBD4
2022-06-18 22:02:47 -07:00
static Rect gTileWindowRect;
2022-05-19 01:51:26 -07:00
// 0x66BBE4
2022-06-18 22:02:47 -07:00
static unsigned char _tile_grid[32 * 16];
2022-05-19 01:51:26 -07:00
// 0x66BDE4
2022-06-18 22:02:47 -07:00
static int _square_rect;
2022-05-19 01:51:26 -07:00
// 0x66BDE8
2022-06-18 22:02:47 -07:00
static int _square_x;
2022-05-19 01:51:26 -07:00
// 0x66BDEC
2022-06-18 22:02:47 -07:00
static int _square_offx;
2022-05-19 01:51:26 -07:00
// 0x66BDF0
2022-06-18 22:02:47 -07:00
static int _square_offy;
2022-05-19 01:51:26 -07:00
// 0x66BDF4
2022-06-18 22:02:47 -07:00
static TileWindowRefreshProc* gTileWindowRefreshProc;
2022-05-19 01:51:26 -07:00
// 0x66BDF8
2022-06-18 22:02:47 -07:00
static int _tile_offy;
2022-05-19 01:51:26 -07:00
// 0x66BDFC
2022-06-18 22:02:47 -07:00
static int _tile_offx;
2022-05-19 01:51:26 -07:00
// 0x66BE00
2022-06-18 22:02:47 -07:00
static int gSquareGridSize;
2022-05-19 01:51:26 -07:00
// Number of tiles horizontally.
//
// Currently this value is always 200.
//
// 0x66BE04
2022-06-18 22:02:47 -07:00
static int gHexGridWidth;
2022-05-19 01:51:26 -07:00
// 0x66BE08
2022-06-18 22:02:47 -07:00
static TileData** gTileSquares;
2022-05-19 01:51:26 -07:00
// 0x66BE0C
2022-06-18 22:02:47 -07:00
static unsigned char* gTileWindowBuffer;
2022-05-19 01:51:26 -07:00
// Number of tiles vertically.
//
// Currently this value is always 200.
//
// 0x66BE10
2022-06-18 22:02:47 -07:00
static int gHexGridHeight;
2022-05-19 01:51:26 -07:00
// 0x66BE14
2022-06-18 22:02:47 -07:00
static int gTileWindowHeight;
2022-05-19 01:51:26 -07:00
// 0x66BE18
2022-06-18 22:02:47 -07:00
static int _tile_x;
2022-05-19 01:51:26 -07:00
// 0x66BE1C
2022-06-18 22:02:47 -07:00
static int _tile_y;
2022-05-19 01:51:26 -07:00
// The number of tiles in the hex grid.
//
// 0x66BE20
int gHexGridSize;
// 0x66BE24
2022-06-18 22:02:47 -07:00
static int gSquareGridHeight;
2022-05-19 01:51:26 -07:00
// 0x66BE28
2022-06-18 22:02:47 -07:00
static int gTileWindowPitch;
2022-05-19 01:51:26 -07:00
// 0x66BE2C
2022-06-18 22:02:47 -07:00
static int gSquareGridWidth;
2022-05-19 01:51:26 -07:00
// 0x66BE30
2022-06-18 22:02:47 -07:00
static int gTileWindowWidth;
2022-05-19 01:51:26 -07:00
// 0x66BE34
int gCenterTile;
// 0x4B0C40
int tileInit(TileData** a1, int squareGridWidth, int squareGridHeight, int hexGridWidth, int hexGridHeight, unsigned char* buf, int windowWidth, int windowHeight, int windowPitch, TileWindowRefreshProc* windowRefreshProc)
{
int v11;
int v12;
int v13;
int v20;
int v21;
int v22;
int v23;
int v24;
int v25;
gSquareGridWidth = squareGridWidth;
2022-05-23 15:37:46 -07:00
gTileSquares = a1;
2022-05-19 01:51:26 -07:00
gHexGridHeight = hexGridHeight;
gSquareGridHeight = squareGridHeight;
gHexGridWidth = hexGridWidth;
_dir_tile[0][0] = -1;
_dir_tile[0][4] = 1;
_dir_tile[1][1] = -1;
gHexGridSize = hexGridWidth * hexGridHeight;
_dir_tile[1][3] = 1;
gTileWindowBuffer = buf;
_dir_tile2[0][0] = -1;
gTileWindowWidth = windowWidth;
_dir_tile2[0][3] = -1;
gTileWindowHeight = windowHeight;
_dir_tile2[1][1] = 1;
gTileWindowPitch = windowPitch;
_dir_tile2[1][2] = 1;
gTileWindowRect.right = windowWidth - 1;
gSquareGridSize = squareGridHeight * squareGridWidth;
gTileWindowRect.bottom = windowHeight - 1;
gTileWindowRect.left = 0;
gTileWindowRefreshProc = windowRefreshProc;
gTileWindowRect.top = 0;
_dir_tile[0][1] = hexGridWidth - 1;
_dir_tile[0][2] = hexGridWidth;
2022-05-23 15:37:46 -07:00
gTileGridIsVisible = 0;
2022-05-19 01:51:26 -07:00
_dir_tile[0][3] = hexGridWidth + 1;
_dir_tile[1][2] = hexGridWidth;
_dir_tile2[0][4] = hexGridWidth;
_dir_tile2[0][5] = hexGridWidth;
_dir_tile[0][5] = -hexGridWidth;
_dir_tile[1][0] = -hexGridWidth - 1;
_dir_tile[1][4] = 1 - hexGridWidth;
_dir_tile[1][5] = -hexGridWidth;
_dir_tile2[0][1] = -hexGridWidth - 1;
_dir_tile2[1][4] = -hexGridWidth;
_dir_tile2[0][2] = hexGridWidth - 1;
_dir_tile2[1][5] = -hexGridWidth;
_dir_tile2[1][0] = hexGridWidth + 1;
_dir_tile2[1][3] = 1 - hexGridWidth;
v11 = 0;
v12 = 0;
do {
v13 = 64;
do {
_tile_mask[v12++] = v13 > v11;
v13 -= 4;
} while (v13);
do {
_tile_mask[v12++] = v13 > v11 ? 2 : 0;
v13 += 4;
} while (v13 != 64);
v11 += 16;
} while (v11 != 64);
v11 = 0;
do {
v13 = 0;
do {
_tile_mask[v12++] = 0;
v13++;
} while (v13 < 32);
v11++;
} while (v11 < 8);
v11 = 0;
do {
v13 = 0;
do {
_tile_mask[v12++] = v13 > v11 ? 0 : 3;
v13 += 4;
} while (v13 != 64);
v13 = 64;
do {
_tile_mask[v12++] = v13 > v11 ? 0 : 4;
v13 -= 4;
} while (v13);
v11 += 16;
} while (v11 != 64);
bufferFill(_tile_grid, 32, 16, 32, 0);
bufferDrawLine(_tile_grid, 32, 16, 0, 31, 4, _colorTable[4228]);
bufferDrawLine(_tile_grid, 32, 31, 4, 31, 12, _colorTable[4228]);
bufferDrawLine(_tile_grid, 32, 31, 12, 16, 15, _colorTable[4228]);
bufferDrawLine(_tile_grid, 32, 0, 12, 16, 15, _colorTable[4228]);
bufferDrawLine(_tile_grid, 32, 0, 4, 0, 12, _colorTable[4228]);
bufferDrawLine(_tile_grid, 32, 16, 0, 0, 4, _colorTable[4228]);
bufferFill(_tile_grid_occupied, 32, 16, 32, 0);
bufferDrawLine(_tile_grid_occupied, 32, 16, 0, 31, 4, _colorTable[31]);
bufferDrawLine(_tile_grid_occupied, 32, 31, 4, 31, 12, _colorTable[31]);
bufferDrawLine(_tile_grid_occupied, 32, 31, 12, 16, 15, _colorTable[31]);
bufferDrawLine(_tile_grid_occupied, 32, 0, 12, 16, 15, _colorTable[31]);
bufferDrawLine(_tile_grid_occupied, 32, 0, 4, 0, 12, _colorTable[31]);
bufferDrawLine(_tile_grid_occupied, 32, 16, 0, 0, 4, _colorTable[31]);
bufferFill(_tile_grid_blocked, 32, 16, 32, 0);
bufferDrawLine(_tile_grid_blocked, 32, 16, 0, 31, 4, _colorTable[31744]);
bufferDrawLine(_tile_grid_blocked, 32, 31, 4, 31, 12, _colorTable[31744]);
bufferDrawLine(_tile_grid_blocked, 32, 31, 12, 16, 15, _colorTable[31744]);
bufferDrawLine(_tile_grid_blocked, 32, 0, 12, 16, 15, _colorTable[31744]);
bufferDrawLine(_tile_grid_blocked, 32, 0, 4, 0, 12, _colorTable[31744]);
bufferDrawLine(_tile_grid_blocked, 32, 16, 0, 0, 4, _colorTable[31744]);
for (v20 = 0; v20 < 16; v20++) {
v21 = v20 * 32;
v22 = 31;
v23 = v21 + 31;
if (_tile_grid_blocked[v23] == 0) {
do {
--v22;
--v23;
} while (v22 > 0 && _tile_grid_blocked[v23] == 0);
}
v24 = v21;
v25 = 0;
if (_tile_grid_blocked[v21] == 0) {
do {
++v25;
++v24;
} while (v25 < 32 && _tile_grid_blocked[v24] == 0);
}
bufferDrawLine(_tile_grid_blocked, 32, v25, v20, v22, v20, _colorTable[31744]);
}
2022-06-11 11:40:12 -07:00
// In order to calculate scroll borders correctly we need to pretend we're
// at original resolution. Since border is calculated only once at start,
// there is not need to change it all the time.
gTileWindowWidth = ORIGINAL_ISO_WINDOW_WIDTH;
gTileWindowHeight = ORIGINAL_ISO_WINDOW_HEIGHT;
2022-05-19 01:51:26 -07:00
tileSetCenter(hexGridWidth * (hexGridHeight / 2) + hexGridWidth / 2, 2);
2022-05-23 15:37:46 -07:00
tileSetBorder(windowWidth, windowHeight, hexGridWidth, hexGridHeight);
2022-05-19 01:51:26 -07:00
2022-06-11 11:40:12 -07:00
// Restore actual window size and set center one more time to calculate
// correct screen offsets, which are required for subsequent object update
// area calculations.
gTileWindowWidth = windowWidth;
gTileWindowHeight = windowHeight;
tileSetCenter(hexGridWidth * (hexGridHeight / 2) + hexGridWidth / 2, 2);
2022-05-19 01:51:26 -07:00
char* executable;
configGetString(&gGameConfig, GAME_CONFIG_SYSTEM_KEY, GAME_CONFIG_EXECUTABLE_KEY, &executable);
if (compat_stricmp(executable, "mapper") == 0) {
2022-05-23 15:37:46 -07:00
gTileWindowRefreshElevationProc = tileRefreshMapper;
2022-05-19 01:51:26 -07:00
}
return 0;
}
// 0x4B11E4
2022-06-18 22:07:06 -07:00
static void tileSetBorder(int windowWidth, int windowHeight, int hexGridWidth, int hexGridHeight)
2022-05-19 01:51:26 -07:00
{
2022-05-23 12:41:54 -07:00
// TODO: Borders, scroll blockers and tile system overall were designed
// with 640x480 in mind, so using windowWidth and windowHeight is
// meaningless for calculating borders. For now keep borders for original
// resolution.
2022-05-19 01:51:26 -07:00
int v1 = tileFromScreenXY(-320, -240, 0);
2022-05-23 12:41:54 -07:00
int v2 = tileFromScreenXY(-320, ORIGINAL_ISO_WINDOW_HEIGHT + 240, 0);
2022-05-19 01:51:26 -07:00
2022-05-23 15:37:46 -07:00
gTileBorderMinX = abs(hexGridWidth - 1 - v2 % hexGridWidth - _tile_x) + 6;
gTileBorderMinY = abs(_tile_y - v1 / hexGridWidth) + 7;
gTileBorderMaxX = hexGridWidth - gTileBorderMinX - 1;
gTileBorderMaxY = hexGridHeight - gTileBorderMinY - 1;
2022-05-19 01:51:26 -07:00
2022-05-23 15:37:46 -07:00
if ((gTileBorderMinX & 1) == 0) {
gTileBorderMinX++;
2022-05-19 01:51:26 -07:00
}
2022-05-23 15:37:46 -07:00
if ((gTileBorderMaxX & 1) == 0) {
gTileBorderMinX--;
2022-05-19 01:51:26 -07:00
}
2022-05-23 15:37:46 -07:00
gTileBorderInitialized = true;
2022-05-19 01:51:26 -07:00
}
// NOTE: Collapsed.
//
// 0x4B129C
void _tile_reset_()
{
}
// NOTE: Uncollapsed 0x4B129C.
void tileReset()
{
_tile_reset_();
}
// NOTE: Uncollapsed 0x4B129C.
void tileExit()
{
_tile_reset_();
}
// 0x4B12A8
void tileDisable()
{
gTileEnabled = false;
}
// 0x4B12B4
void tileEnable()
{
gTileEnabled = true;
}
// 0x4B12C0
void tileWindowRefreshRect(Rect* rect, int elevation)
{
if (gTileEnabled) {
if (elevation == gElevation) {
2022-05-23 15:37:46 -07:00
gTileWindowRefreshElevationProc(rect, elevation);
2022-05-19 01:51:26 -07:00
}
}
}
// 0x4B12D8
void tileWindowRefresh()
{
if (gTileEnabled) {
2022-05-23 15:37:46 -07:00
gTileWindowRefreshElevationProc(&gTileWindowRect, gElevation);
2022-05-19 01:51:26 -07:00
}
}
// 0x4B12F8
int tileSetCenter(int tile, int flags)
{
2022-05-23 15:37:46 -07:00
if (!tileIsValid(tile)) {
2022-05-19 01:51:26 -07:00
return -1;
}
2022-05-23 15:37:46 -07:00
if ((gTileScrollLimitingEnabled & ((flags & TILE_SET_CENTER_FLAG_0x02) == 0)) != 0) {
2022-05-19 01:51:26 -07:00
int tileScreenX;
int tileScreenY;
tileToScreenXY(tile, &tileScreenX, &tileScreenY, gElevation);
int dudeScreenX;
int dudeScreenY;
tileToScreenXY(gDude->tile, &dudeScreenX, &dudeScreenY, gElevation);
int dx = abs(dudeScreenX - tileScreenX);
int dy = abs(dudeScreenY - tileScreenY);
if (dx > abs(dudeScreenX - _tile_offx)
|| dy > abs(dudeScreenY - _tile_offy)) {
if (dx >= 480 || dy >= 400) {
return -1;
}
}
}
2022-05-23 15:37:46 -07:00
if ((gTileScrollBlockingEnabled & ((flags & TILE_SET_CENTER_FLAG_0x02) == 0)) != 0) {
2022-05-19 01:51:26 -07:00
if (_obj_scroll_blocking_at(tile, gElevation) == 0) {
return -1;
}
}
int tile_x = gHexGridWidth - 1 - tile % gHexGridWidth;
int tile_y = tile / gHexGridWidth;
2022-05-19 01:51:26 -07:00
2022-05-23 15:37:46 -07:00
if (gTileBorderInitialized) {
if (tile_x <= gTileBorderMinX || tile_x >= gTileBorderMaxX || tile_y <= gTileBorderMinY || tile_y >= gTileBorderMaxY) {
2022-05-19 01:51:26 -07:00
return -1;
}
}
_tile_y = tile_y;
2022-05-19 01:51:26 -07:00
_tile_offx = (gTileWindowWidth - 32) / 2;
_tile_x = tile_x;
2022-05-19 01:51:26 -07:00
_tile_offy = (gTileWindowHeight - 16) / 2;
if (tile_x & 1) {
2022-05-19 01:51:26 -07:00
_tile_x -= 1;
_tile_offx -= 32;
}
_square_x = _tile_x / 2;
_square_rect = _tile_y / 2;
_square_offx = _tile_offx - 16;
_square_offy = _tile_offy - 2;
if (_tile_y & 1) {
_square_offy -= 12;
_square_offx -= 16;
}
gCenterTile = tile;
if (flags & TILE_SET_CENTER_FLAG_0x01) {
2022-05-23 15:37:46 -07:00
// NOTE: Uninline.
tileWindowRefresh();
2022-05-19 01:51:26 -07:00
}
return 0;
}
// 0x4B1554
2022-06-18 22:07:06 -07:00
static void tileRefreshMapper(Rect* rect, int elevation)
2022-05-19 01:51:26 -07:00
{
Rect rectToUpdate;
if (rectIntersection(rect, &gTileWindowRect, &rectToUpdate) == -1) {
return;
}
bufferFill(gTileWindowBuffer + gTileWindowPitch * rectToUpdate.top + rectToUpdate.left,
rectToUpdate.right - rectToUpdate.left + 1,
rectToUpdate.bottom - rectToUpdate.top + 1,
gTileWindowPitch,
0);
tileRenderFloorsInRect(&rectToUpdate, elevation);
_grid_render(&rectToUpdate, elevation);
_obj_render_pre_roof(&rectToUpdate, elevation);
tileRenderRoofsInRect(&rectToUpdate, elevation);
_obj_render_post_roof(&rectToUpdate, elevation);
gTileWindowRefreshProc(&rectToUpdate);
}
// 0x4B15E8
2022-06-18 22:07:06 -07:00
static void tileRefreshGame(Rect* rect, int elevation)
2022-05-19 01:51:26 -07:00
{
Rect rectToUpdate;
if (rectIntersection(rect, &gTileWindowRect, &rectToUpdate) == -1) {
return;
}
tileRenderFloorsInRect(&rectToUpdate, elevation);
_obj_render_pre_roof(&rectToUpdate, elevation);
tileRenderRoofsInRect(&rectToUpdate, elevation);
_obj_render_post_roof(&rectToUpdate, elevation);
gTileWindowRefreshProc(&rectToUpdate);
}
// 0x4B166C
2022-05-23 15:37:46 -07:00
int tileRoofIsVisible()
2022-05-19 01:51:26 -07:00
{
2022-05-23 15:37:46 -07:00
return gTileRoofIsVisible;
2022-05-19 01:51:26 -07:00
}
// 0x4B1674
int tileToScreenXY(int tile, int* screenX, int* screenY, int elevation)
{
int v3;
int v4;
int v5;
int v6;
2022-05-23 15:37:46 -07:00
if (!tileIsValid(tile)) {
2022-05-19 01:51:26 -07:00
return -1;
}
v3 = gHexGridWidth - 1 - tile % gHexGridWidth;
v4 = tile / gHexGridWidth;
*screenX = _tile_offx;
*screenY = _tile_offy;
v5 = (v3 - _tile_x) / -2;
*screenX += 48 * ((v3 - _tile_x) / 2);
*screenY += 12 * v5;
if (v3 & 1) {
if (v3 <= _tile_x) {
*screenX -= 16;
*screenY += 12;
} else {
*screenX += 32;
}
}
v6 = v4 - _tile_y;
*screenX += 16 * v6;
*screenY += 12 * v6;
return 0;
}
// 0x4B1754
int tileFromScreenXY(int screenX, int screenY, int elevation)
{
int v2;
int v3;
int v4;
int v5;
int v6;
int v7;
int v8;
int v9;
int v10;
int v11;
int v12;
v2 = screenY - _tile_offy;
if (v2 >= 0) {
v3 = v2 / 12;
} else {
v3 = (v2 + 1) / 12 - 1;
}
v4 = screenX - _tile_offx - 16 * v3;
v5 = v2 - 12 * v3;
if (v4 >= 0) {
v6 = v4 / 64;
} else {
v6 = (v4 + 1) / 64 - 1;
}
v7 = v6 + v3;
v8 = v4 - (v6 * 64);
v9 = 2 * v6;
if (v8 >= 32) {
v8 -= 32;
v9++;
}
v10 = _tile_y + v7;
v11 = _tile_x + v9;
switch (_tile_mask[32 * v5 + v8]) {
case 2:
v11++;
if (v11 & 1) {
v10--;
}
break;
case 1:
v10--;
break;
case 3:
v11--;
if (!(v11 & 1)) {
v10++;
}
break;
case 4:
v10++;
break;
default:
break;
}
v12 = gHexGridWidth - 1 - v11;
if (v12 >= 0 && v12 < gHexGridWidth && v10 >= 0 && v10 < gHexGridHeight) {
return gHexGridWidth * v10 + v12;
}
return -1;
}
// tile_distance
// 0x4B185C
int tileDistanceBetween(int tile1, int tile2)
{
int i;
int v9;
int v8;
int v2;
if (tile1 == -1) {
return 9999;
}
if (tile2 == -1) {
return 9999;
}
int x1;
int y1;
tileToScreenXY(tile2, &x1, &y1, 0);
v2 = tile1;
for (i = 0; v2 != tile2; i++) {
// TODO: Looks like inlined rotation_to_tile.
int x2;
int y2;
tileToScreenXY(v2, &x2, &y2, 0);
int dx = x1 - x2;
int dy = y1 - y2;
if (x1 == x2) {
if (dy < 0) {
v9 = 0;
} else {
v9 = 2;
}
} else {
v8 = (int)trunc(atan2((double)-dy, (double)dx) * 180.0 * 0.3183098862851122);
v9 = 360 - (v8 + 180) - 90;
if (v9 < 0) {
v9 += 360;
}
v9 /= 60;
if (v9 >= 6) {
v9 = 5;
}
}
v2 += _dir_tile[v2 % gHexGridWidth & 1][v9];
}
return i;
}
// 0x4B1994
2022-05-23 15:37:46 -07:00
bool tileIsInFrontOf(int tile1, int tile2)
2022-05-19 01:51:26 -07:00
{
int x1;
int y1;
tileToScreenXY(tile1, &x1, &y1, 0);
int x2;
int y2;
tileToScreenXY(tile2, &x2, &y2, 0);
int dx = x2 - x1;
int dy = y2 - y1;
return (double)dx <= (double)dy * dbl_50E7C7;
}
// 0x4B1A00
2022-05-23 15:37:46 -07:00
bool tileIsToRightOf(int tile1, int tile2)
2022-05-19 01:51:26 -07:00
{
int x1;
int y1;
tileToScreenXY(tile1, &x1, &y1, 0);
int x2;
int y2;
tileToScreenXY(tile2, &x2, &y2, 0);
int dx = x2 - x1;
int dy = y2 - y1;
// NOTE: the value below looks like 4/3, which is 0x3FF55555555555, but it's
// binary value is slightly different: 0x3FF55555555556. This difference plays
// important role as seen right in the beginning of the game, comparing tiles
// 17488 (0x4450) and 15288 (0x3BB8).
return (double)dx <= (double)dy * 1.3333333333333335;
}
// tile_num_in_direction
// 0x4B1A6C
int tileGetTileInDirection(int tile, int rotation, int distance)
{
int newTile = tile;
for (int index = 0; index < distance; index++) {
2022-05-23 15:37:46 -07:00
if (tileIsEdge(newTile)) {
2022-05-19 01:51:26 -07:00
break;
}
int parity = (newTile % gHexGridWidth) & 1;
newTile += _dir_tile[parity][rotation];
}
return newTile;
}
// rotation_to_tile
// 0x4B1ABC
int tileGetRotationTo(int tile1, int tile2)
{
int x1;
int y1;
tileToScreenXY(tile1, &x1, &y1, 0);
int x2;
int y2;
tileToScreenXY(tile2, &x2, &y2, 0);
int dy = y2 - y1;
x2 -= x1;
y2 -= y1;
if (x2 != 0) {
// TODO: Check.
int v6 = (int)trunc(atan2((double)-dy, (double)x2) * 180.0 * 0.3183098862851122);
int v7 = 360 - (v6 + 180) - 90;
if (v7 < 0) {
v7 += 360;
}
v7 /= 60;
if (v7 >= ROTATION_COUNT) {
v7 = ROTATION_NW;
}
return v7;
}
return dy < 0 ? ROTATION_NE : ROTATION_SE;
}
// 0x4B1B84
int _tile_num_beyond(int from, int to, int distance)
{
if (distance <= 0 || from == to) {
return from;
}
int fromX;
int fromY;
tileToScreenXY(from, &fromX, &fromY, 0);
fromX += 16;
fromY += 8;
int toX;
int toY;
tileToScreenXY(to, &toX, &toY, 0);
toX += 16;
toY += 8;
int deltaX = toX - fromX;
int deltaY = toY - fromY;
int v27 = 2 * abs(deltaX);
int stepX = 0;
if (deltaX > 0)
stepX = 1;
else if (deltaX < 0)
stepX = -1;
int v26 = 2 * abs(deltaY);
int stepY = 0;
if (deltaY > 0)
stepY = 1;
else if (deltaY < 0)
stepY = -1;
int v28 = from;
int tileX = fromX;
int tileY = fromY;
int v6 = 0;
if (v27 > v26) {
int middle = v26 - v27 / 2;
while (true) {
int tile = tileFromScreenXY(tileX, tileY, 0);
if (tile != v28) {
v6 += 1;
2022-05-23 15:37:46 -07:00
if (v6 == distance || tileIsEdge(tile)) {
2022-05-19 01:51:26 -07:00
return tile;
}
v28 = tile;
}
if (middle >= 0) {
middle -= v27;
tileY += stepY;
}
middle += v26;
tileX += stepX;
}
} else {
int middle = v27 - v26 / 2;
while (true) {
int tile = tileFromScreenXY(tileX, tileY, 0);
if (tile != v28) {
v6 += 1;
2022-05-23 15:37:46 -07:00
if (v6 == distance || tileIsEdge(tile)) {
2022-05-19 01:51:26 -07:00
return tile;
}
v28 = tile;
}
if (middle >= 0) {
middle -= v26;
tileX += stepX;
}
middle += v27;
tileY += stepY;
}
}
assert(false && "Should be unreachable");
}
// 0x4B1D20
2022-05-23 15:37:46 -07:00
bool tileIsEdge(int tile)
2022-05-19 01:51:26 -07:00
{
2022-05-23 15:37:46 -07:00
if (!tileIsValid(tile)) {
return false;
2022-05-19 01:51:26 -07:00
}
if (tile < gHexGridWidth) {
2022-05-23 15:37:46 -07:00
return true;
2022-05-19 01:51:26 -07:00
}
if (tile >= gHexGridSize - gHexGridWidth) {
2022-05-23 15:37:46 -07:00
return true;
2022-05-19 01:51:26 -07:00
}
if (tile % gHexGridWidth == 0) {
2022-05-23 15:37:46 -07:00
return true;
2022-05-19 01:51:26 -07:00
}
if (tile % gHexGridWidth == gHexGridWidth - 1) {
2022-05-23 15:37:46 -07:00
return true;
2022-05-19 01:51:26 -07:00
}
2022-05-23 15:37:46 -07:00
return false;
2022-05-19 01:51:26 -07:00
}
// 0x4B1D80
2022-05-23 15:37:46 -07:00
void tileScrollBlockingEnable()
2022-05-19 01:51:26 -07:00
{
2022-05-23 15:37:46 -07:00
gTileScrollBlockingEnabled = true;
2022-05-19 01:51:26 -07:00
}
// 0x4B1D8C
2022-05-23 15:37:46 -07:00
void tileScrollBlockingDisable()
2022-05-19 01:51:26 -07:00
{
2022-05-23 15:37:46 -07:00
gTileScrollBlockingEnabled = false;
2022-05-19 01:51:26 -07:00
}
// 0x4B1D98
2022-05-23 15:37:46 -07:00
bool tileScrollBlockingIsEnabled()
2022-05-19 01:51:26 -07:00
{
2022-05-23 15:37:46 -07:00
return gTileScrollBlockingEnabled;
2022-05-19 01:51:26 -07:00
}
// 0x4B1DA0
2022-05-23 15:37:46 -07:00
void tileScrollLimitingEnable()
2022-05-19 01:51:26 -07:00
{
2022-05-23 15:37:46 -07:00
gTileScrollLimitingEnabled = true;
2022-05-19 01:51:26 -07:00
}
// 0x4B1DAC
2022-05-23 15:37:46 -07:00
void tileScrollLimitingDisable()
2022-05-19 01:51:26 -07:00
{
2022-05-23 15:37:46 -07:00
gTileScrollLimitingEnabled = false;
2022-05-19 01:51:26 -07:00
}
// 0x4B1DB8
2022-05-23 15:37:46 -07:00
bool tileScrollLimitingIsEnabled()
2022-05-19 01:51:26 -07:00
{
2022-05-23 15:37:46 -07:00
return gTileScrollLimitingEnabled;
2022-05-19 01:51:26 -07:00
}
// 0x4B1DC0
2022-05-23 15:37:46 -07:00
int squareTileToScreenXY(int squareTile, int* coordX, int* coordY, int elevation)
2022-05-19 01:51:26 -07:00
{
int v5;
int v6;
int v7;
int v8;
int v9;
2022-05-23 15:37:46 -07:00
if (squareTile < 0 || squareTile >= gSquareGridSize) {
2022-05-19 01:51:26 -07:00
return -1;
}
2022-05-23 15:37:46 -07:00
v5 = gSquareGridWidth - 1 - squareTile % gSquareGridWidth;
v6 = squareTile / gSquareGridWidth;
2022-05-19 01:51:26 -07:00
v7 = _square_x;
2022-05-23 15:37:46 -07:00
*coordX = _square_offx;
*coordY = _square_offy;
2022-05-19 01:51:26 -07:00
v8 = v5 - v7;
2022-05-23 15:37:46 -07:00
*coordX += 48 * v8;
*coordY -= 12 * v8;
2022-05-19 01:51:26 -07:00
v9 = v6 - _square_rect;
2022-05-23 15:37:46 -07:00
*coordX += 32 * v9;
*coordY += 24 * v9;
2022-05-19 01:51:26 -07:00
return 0;
}
// 0x4B1E60
2022-05-23 15:37:46 -07:00
int squareTileToRoofScreenXY(int squareTile, int* screenX, int* screenY, int elevation)
2022-05-19 01:51:26 -07:00
{
int v5;
int v6;
int v7;
int v8;
int v9;
int v10;
2022-05-23 15:37:46 -07:00
if (squareTile < 0 || squareTile >= gSquareGridSize) {
2022-05-19 01:51:26 -07:00
return -1;
}
2022-05-23 15:37:46 -07:00
v5 = gSquareGridWidth - 1 - squareTile % gSquareGridWidth;
v6 = squareTile / gSquareGridWidth;
2022-05-19 01:51:26 -07:00
v7 = _square_x;
2022-05-23 15:37:46 -07:00
*screenX = _square_offx;
*screenY = _square_offy;
2022-05-19 01:51:26 -07:00
v8 = v5 - v7;
2022-05-23 15:37:46 -07:00
*screenX += 48 * v8;
*screenY -= 12 * v8;
2022-05-19 01:51:26 -07:00
v9 = v6 - _square_rect;
2022-05-23 15:37:46 -07:00
*screenX += 32 * v9;
v10 = 24 * v9 + *screenY;
*screenY = v10;
*screenY = v10 - 96;
2022-05-19 01:51:26 -07:00
return 0;
}
// 0x4B1F04
2022-05-23 15:37:46 -07:00
int squareTileFromScreenXY(int screenX, int screenY, int elevation)
2022-05-19 01:51:26 -07:00
{
2022-05-23 15:37:46 -07:00
int coordY;
int coordX;
2022-05-19 01:51:26 -07:00
2022-05-23 15:37:46 -07:00
squareTileScreenToCoord(screenX, screenY, elevation, &coordX, &coordY);
2022-05-19 01:51:26 -07:00
2022-05-23 15:37:46 -07:00
if (coordX >= 0 && coordX < gSquareGridWidth && coordY >= 0 && coordY < gSquareGridHeight) {
return coordX + gSquareGridWidth * coordY;
2022-05-19 01:51:26 -07:00
}
return -1;
}
// 0x4B1F94
2022-05-23 15:37:46 -07:00
void squareTileScreenToCoord(int screenX, int screenY, int elevation, int* coordX, int* coordY)
2022-05-19 01:51:26 -07:00
{
int v4;
int v5;
int v6;
int v8;
2022-05-23 15:37:46 -07:00
v4 = screenX - _square_offx;
v5 = screenY - _square_offy - 12;
2022-05-19 01:51:26 -07:00
v6 = 3 * v4 - 4 * v5;
2022-05-23 15:37:46 -07:00
*coordX = v6 >= 0 ? (v6 / 192) : ((v6 + 1) / 192 - 1);
2022-05-19 01:51:26 -07:00
v8 = 4 * v5 + v4;
2022-05-23 15:37:46 -07:00
*coordY = v8 >= 0
2022-05-19 01:51:26 -07:00
? ((v8 - ((v8 >> 31) << 7)) >> 7)
: ((((v8 + 1) - (((v8 + 1) >> 31) << 7)) >> 7) - 1);
2022-05-23 15:37:46 -07:00
*coordX += _square_x;
*coordY += _square_rect;
2022-05-19 01:51:26 -07:00
2022-05-23 15:37:46 -07:00
*coordX = gSquareGridWidth - 1 - *coordX;
2022-05-19 01:51:26 -07:00
}
// 0x4B203C
2022-05-23 15:37:46 -07:00
void squareTileScreenToCoordRoof(int screenX, int screenY, int elevation, int* coordX, int* coordY)
2022-05-19 01:51:26 -07:00
{
int v4;
int v5;
int v6;
int v8;
2022-05-23 15:37:46 -07:00
v4 = screenX - _square_offx;
v5 = screenY + 96 - _square_offy - 12;
2022-05-19 01:51:26 -07:00
v6 = 3 * v4 - 4 * v5;
2022-05-23 15:37:46 -07:00
*coordX = (v6 >= 0) ? (v6 / 192) : ((v6 + 1) / 192 - 1);
2022-05-19 01:51:26 -07:00
v8 = 4 * v5 + v4;
2022-05-23 15:37:46 -07:00
*coordY = (v8 >= 0)
2022-05-19 01:51:26 -07:00
? ((v8 - ((v8 >> 31) << 7)) >> 7)
: ((((v8 + 1) - (((v8 + 1) >> 31) << 7)) >> 7) - 1);
2022-05-23 15:37:46 -07:00
*coordX += _square_x;
*coordY += _square_rect;
2022-05-19 01:51:26 -07:00
2022-05-23 15:37:46 -07:00
*coordX = gSquareGridWidth - 1 - *coordX;
2022-05-19 01:51:26 -07:00
}
// 0x4B20E8
void tileRenderRoofsInRect(Rect* rect, int elevation)
{
2022-05-23 15:37:46 -07:00
if (!gTileRoofIsVisible) {
2022-05-19 01:51:26 -07:00
return;
}
int temp;
int minY;
int minX;
int maxX;
int maxY;
2022-05-23 15:37:46 -07:00
squareTileScreenToCoordRoof(rect->left, rect->top, elevation, &temp, &minY);
squareTileScreenToCoordRoof(rect->right, rect->top, elevation, &minX, &temp);
squareTileScreenToCoordRoof(rect->left, rect->bottom, elevation, &maxX, &temp);
squareTileScreenToCoordRoof(rect->right, rect->bottom, elevation, &temp, &maxY);
2022-05-19 01:51:26 -07:00
if (minX < 0) {
minX = 0;
}
if (minX >= gSquareGridWidth) {
minX = gSquareGridWidth - 1;
}
if (minY < 0) {
minY = 0;
}
// FIXME: Probably a bug - testing X, then changing Y.
if (minX >= gSquareGridHeight) {
minY = gSquareGridHeight - 1;
}
int light = lightGetLightLevel();
int baseSquareTile = gSquareGridWidth * minY;
for (int y = minY; y <= maxY; y++) {
for (int x = minX; x <= maxX; x++) {
int squareTile = baseSquareTile + x;
2022-05-23 15:37:46 -07:00
int frmId = gTileSquares[elevation]->field_0[squareTile];
2022-05-19 01:51:26 -07:00
frmId >>= 16;
if ((((frmId & 0xF000) >> 12) & 0x01) == 0) {
int fid = buildFid(OBJ_TYPE_TILE, frmId & 0xFFF, 0, 0, 0);
if (fid != buildFid(OBJ_TYPE_TILE, 1, 0, 0, 0)) {
2022-05-19 01:51:26 -07:00
int screenX;
int screenY;
2022-05-23 15:37:46 -07:00
squareTileToRoofScreenXY(squareTile, &screenX, &screenY, elevation);
2022-05-19 01:51:26 -07:00
tileRenderRoof(fid, screenX, screenY, rect, light);
}
}
}
baseSquareTile += gSquareGridWidth;
}
}
// 0x4B22D0
2022-06-18 22:07:06 -07:00
static void _roof_fill_on(int a1, int a2, int elevation)
2022-05-19 01:51:26 -07:00
{
while ((a1 >= 0 && a1 < gSquareGridWidth) && (a2 >= 0 && a2 < gSquareGridHeight)) {
int squareTile = gSquareGridWidth * a2 + a1;
2022-05-23 15:37:46 -07:00
int value = gTileSquares[elevation]->field_0[squareTile];
2022-05-19 01:51:26 -07:00
int upper = (value >> 16) & 0xFFFF;
int id = upper & 0xFFF;
if (buildFid(OBJ_TYPE_TILE, id, 0, 0, 0) == buildFid(OBJ_TYPE_TILE, 1, 0, 0, 0)) {
2022-05-19 01:51:26 -07:00
break;
}
int flag = (upper & 0xF000) >> 12;
if ((flag & 0x01) == 0) {
break;
}
flag &= ~0x01;
2022-05-23 15:37:46 -07:00
gTileSquares[elevation]->field_0[squareTile] = (value & 0xFFFF) | (((flag << 12) | id) << 16);
2022-05-19 01:51:26 -07:00
_roof_fill_on(a1 - 1, a2, elevation);
_roof_fill_on(a1 + 1, a2, elevation);
_roof_fill_on(a1, a2 - 1, elevation);
a2++;
}
}
// 0x4B23D4
void _tile_fill_roof(int a1, int a2, int elevation, int a4)
{
if (a4) {
_roof_fill_on(a1, a2, elevation);
} else {
sub_4B23DC(a1, a2, elevation);
}
}
// 0x4B23DC
2022-06-18 22:07:06 -07:00
static void sub_4B23DC(int a1, int a2, int elevation)
2022-05-19 01:51:26 -07:00
{
while ((a1 >= 0 && a1 < gSquareGridWidth) && (a2 >= 0 && a2 < gSquareGridHeight)) {
int squareTile = gSquareGridWidth * a2 + a1;
2022-05-23 15:37:46 -07:00
int value = gTileSquares[elevation]->field_0[squareTile];
2022-05-19 01:51:26 -07:00
int upper = (value >> 16) & 0xFFFF;
int id = upper & 0xFFF;
if (buildFid(OBJ_TYPE_TILE, id, 0, 0, 0) == buildFid(OBJ_TYPE_TILE, 1, 0, 0, 0)) {
2022-05-19 01:51:26 -07:00
break;
}
int flag = (upper & 0xF000) >> 12;
if ((flag & 0x03) != 0) {
break;
}
flag |= 0x01;
2022-05-23 15:37:46 -07:00
gTileSquares[elevation]->field_0[squareTile] = (value & 0xFFFF) | (((flag << 12) | id) << 16);
2022-05-19 01:51:26 -07:00
sub_4B23DC(a1 - 1, a2, elevation);
sub_4B23DC(a1 + 1, a2, elevation);
sub_4B23DC(a1, a2 - 1, elevation);
a2++;
}
}
// 0x4B24E0
2022-06-18 22:07:06 -07:00
static void tileRenderRoof(int fid, int x, int y, Rect* rect, int light)
2022-05-19 01:51:26 -07:00
{
CacheEntry* tileFrmHandle;
Art* tileFrm = artLock(fid, &tileFrmHandle);
if (tileFrm == NULL) {
return;
}
int tileWidth = artGetWidth(tileFrm, 0, 0);
int tileHeight = artGetHeight(tileFrm, 0, 0);
Rect tileRect;
tileRect.left = x;
tileRect.top = y;
tileRect.right = x + tileWidth - 1;
tileRect.bottom = y + tileHeight - 1;
if (rectIntersection(&tileRect, rect, &tileRect) == 0) {
unsigned char* tileFrmBuffer = artGetFrameData(tileFrm, 0, 0);
tileFrmBuffer += tileWidth * (tileRect.top - y) + (tileRect.left - x);
CacheEntry* eggFrmHandle;
Art* eggFrm = artLock(gEgg->fid, &eggFrmHandle);
if (eggFrm != NULL) {
int eggWidth = artGetWidth(eggFrm, 0, 0);
int eggHeight = artGetHeight(eggFrm, 0, 0);
int eggScreenX;
int eggScreenY;
tileToScreenXY(gEgg->tile, &eggScreenX, &eggScreenY, gEgg->elevation);
eggScreenX += 16;
eggScreenY += 8;
eggScreenX += eggFrm->xOffsets[0];
eggScreenY += eggFrm->yOffsets[0];
eggScreenX += gEgg->x;
eggScreenY += gEgg->y;
Rect eggRect;
eggRect.left = eggScreenX - eggWidth / 2;
eggRect.top = eggScreenY - eggHeight + 1;
eggRect.right = eggRect.left + eggWidth - 1;
eggRect.bottom = eggScreenY;
gEgg->sx = eggRect.left;
gEgg->sy = eggRect.top;
Rect intersectedRect;
if (rectIntersection(&eggRect, &tileRect, &intersectedRect) == 0) {
Rect rects[4];
rects[0].left = tileRect.left;
rects[0].top = tileRect.top;
rects[0].right = tileRect.right;
rects[0].bottom = intersectedRect.top - 1;
rects[1].left = tileRect.left;
rects[1].top = intersectedRect.top;
rects[1].right = intersectedRect.left - 1;
rects[1].bottom = intersectedRect.bottom;
rects[2].left = intersectedRect.right + 1;
rects[2].top = intersectedRect.top;
rects[2].right = tileRect.right;
rects[2].bottom = intersectedRect.bottom;
rects[3].left = tileRect.left;
rects[3].top = intersectedRect.bottom + 1;
rects[3].right = tileRect.right;
rects[3].bottom = tileRect.bottom;
for (int i = 0; i < 4; i++) {
Rect* cr = &(rects[i]);
if (cr->left <= cr->right && cr->top <= cr->bottom) {
_dark_trans_buf_to_buf(tileFrmBuffer + tileWidth * (cr->top - tileRect.top) + (cr->left - tileRect.left),
cr->right - cr->left + 1,
cr->bottom - cr->top + 1,
tileWidth,
gTileWindowBuffer,
cr->left,
cr->top,
gTileWindowPitch,
light);
}
}
unsigned char* eggBuf = artGetFrameData(eggFrm, 0, 0);
_intensity_mask_buf_to_buf(tileFrmBuffer + tileWidth * (intersectedRect.top - tileRect.top) + (intersectedRect.left - tileRect.left),
intersectedRect.right - intersectedRect.left + 1,
intersectedRect.bottom - intersectedRect.top + 1,
tileWidth,
gTileWindowBuffer + gTileWindowPitch * intersectedRect.top + intersectedRect.left,
gTileWindowPitch,
eggBuf + eggWidth * (intersectedRect.top - eggRect.top) + (intersectedRect.left - eggRect.left),
eggWidth,
light);
} else {
_dark_trans_buf_to_buf(tileFrmBuffer, tileRect.right - tileRect.left + 1, tileRect.bottom - tileRect.top + 1, tileWidth, gTileWindowBuffer, tileRect.left, tileRect.top, gTileWindowPitch, light);
}
artUnlock(eggFrmHandle);
}
}
artUnlock(tileFrmHandle);
}
// 0x4B2944
void tileRenderFloorsInRect(Rect* rect, int elevation)
{
2022-05-23 15:37:46 -07:00
int minY;
int maxX;
int maxY;
int minX;
int temp;
2022-05-19 01:51:26 -07:00
2022-05-23 15:37:46 -07:00
squareTileScreenToCoord(rect->left, rect->top, elevation, &temp, &minY);
squareTileScreenToCoord(rect->right, rect->top, elevation, &minX, &temp);
squareTileScreenToCoord(rect->left, rect->bottom, elevation, &maxX, &temp);
squareTileScreenToCoord(rect->right, rect->bottom, elevation, &temp, &maxY);
2022-05-19 01:51:26 -07:00
2022-05-23 15:37:46 -07:00
if (minX < 0) {
minX = 0;
2022-05-19 01:51:26 -07:00
}
2022-05-23 15:37:46 -07:00
if (minX >= gSquareGridWidth) {
minX = gSquareGridWidth - 1;
2022-05-19 01:51:26 -07:00
}
2022-05-23 15:37:46 -07:00
if (minY < 0) {
minY = 0;
2022-05-19 01:51:26 -07:00
}
2022-05-23 15:37:46 -07:00
if (minX >= gSquareGridHeight) {
minY = gSquareGridHeight - 1;
2022-05-19 01:51:26 -07:00
}
lightGetLightLevel();
2022-05-23 15:37:46 -07:00
temp = gSquareGridWidth * minY;
for (int v15 = minY; v15 <= maxY; v15++) {
for (int i = minX; i <= maxX; i++) {
int v3 = temp + i;
int frmId = gTileSquares[elevation]->field_0[v3];
2022-05-19 01:51:26 -07:00
if ((((frmId & 0xF000) >> 12) & 0x01) == 0) {
int v12;
int v13;
2022-05-23 15:37:46 -07:00
squareTileToScreenXY(v3, &v12, &v13, elevation);
int fid = buildFid(OBJ_TYPE_TILE, frmId & 0xFFF, 0, 0, 0);
2022-05-19 01:51:26 -07:00
tileRenderFloor(fid, v12, v13, rect);
}
}
2022-05-23 15:37:46 -07:00
temp += gSquareGridWidth;
2022-05-19 01:51:26 -07:00
}
}
// 0x4B2B10
bool _square_roof_intersect(int x, int y, int elevation)
{
2022-05-23 15:37:46 -07:00
if (!gTileRoofIsVisible) {
2022-05-19 01:51:26 -07:00
return false;
}
bool result = false;
int tileX;
int tileY;
2022-05-23 15:37:46 -07:00
squareTileScreenToCoordRoof(x, y, elevation, &tileX, &tileY);
2022-05-19 01:51:26 -07:00
2022-05-23 15:37:46 -07:00
TileData* ptr = gTileSquares[elevation];
2022-05-19 01:51:26 -07:00
int idx = gSquareGridWidth * tileY + tileX;
int upper = ptr->field_0[gSquareGridWidth * tileY + tileX] >> 16;
int fid = buildFid(OBJ_TYPE_TILE, upper & 0xFFF, 0, 0, 0);
if (fid != buildFid(OBJ_TYPE_TILE, 1, 0, 0, 0)) {
2022-05-19 01:51:26 -07:00
if ((((upper & 0xF000) >> 12) & 1) == 0) {
int fid = buildFid(OBJ_TYPE_TILE, upper & 0xFFF, 0, 0, 0);
2022-05-19 01:51:26 -07:00
CacheEntry* handle;
Art* art = artLock(fid, &handle);
if (art != NULL) {
unsigned char* data = artGetFrameData(art, 0, 0);
if (data != NULL) {
int v18;
int v17;
2022-05-23 15:37:46 -07:00
squareTileToRoofScreenXY(idx, &v18, &v17, elevation);
2022-05-19 01:51:26 -07:00
int width = artGetWidth(art, 0, 0);
if (data[width * (y - v17) + x - v18] != 0) {
result = true;
}
}
artUnlock(handle);
}
}
}
return result;
}
// 0x4B2E98
void _grid_render(Rect* rect, int elevation)
{
2022-05-23 15:37:46 -07:00
if (!gTileGridIsVisible) {
2022-05-19 01:51:26 -07:00
return;
}
for (int y = rect->top - 12; y < rect->bottom + 12; y += 6) {
for (int x = rect->left - 32; x < rect->right + 32; x += 16) {
int tile = tileFromScreenXY(x, y, elevation);
_draw_grid(tile, elevation, rect);
}
}
}
// 0x4B2F4C
2022-06-18 22:07:06 -07:00
static void _draw_grid(int tile, int elevation, Rect* rect)
2022-05-19 01:51:26 -07:00
{
if (tile == -1) {
return;
}
int x;
int y;
tileToScreenXY(tile, &x, &y, elevation);
Rect r;
r.left = x;
r.top = y;
r.right = x + 32 - 1;
r.bottom = y + 16 - 1;
if (rectIntersection(&r, rect, &r) == -1) {
return;
}
if (_obj_blocking_at(NULL, tile, elevation) != NULL) {
blitBufferToBufferTrans(_tile_grid_blocked + 32 * (r.top - y) + (r.left - x),
r.right - r.left + 1,
r.bottom - r.top + 1,
32,
gTileWindowBuffer + gTileWindowPitch * r.top + r.left,
gTileWindowPitch);
return;
}
if (_obj_occupied(tile, elevation)) {
blitBufferToBufferTrans(_tile_grid_occupied + 32 * (r.top - y) + (r.left - x),
r.right - r.left + 1,
r.bottom - r.top + 1,
32,
gTileWindowBuffer + gTileWindowPitch * r.top + r.left,
gTileWindowPitch);
return;
}
_translucent_trans_buf_to_buf(_tile_grid_occupied + 32 * (r.top - y) + (r.left - x),
r.right - r.left + 1,
r.bottom - r.top + 1,
32,
gTileWindowBuffer + gTileWindowPitch * r.top + r.left,
0,
0,
gTileWindowPitch,
_wallBlendTable,
_commonGrayTable);
}
// 0x4B30C4
2022-06-18 22:07:06 -07:00
static void tileRenderFloor(int fid, int x, int y, Rect* rect)
2022-05-19 01:51:26 -07:00
{
if (artIsObjectTypeHidden(FID_TYPE(fid)) != 0) {
2022-05-19 01:51:26 -07:00
return;
}
CacheEntry* cacheEntry;
Art* art = artLock(fid, &cacheEntry);
if (art == NULL) {
return;
}
int elev = gElevation;
int left = rect->left;
int top = rect->top;
int width = rect->right - rect->left + 1;
int height = rect->bottom - rect->top + 1;
int frameWidth;
int frameHeight;
int v15;
int v76;
int v77;
int v78;
int v79;
int savedX = x;
int savedY = y;
if (left < 0) {
left = 0;
}
if (top < 0) {
top = 0;
}
if (left + width > gTileWindowWidth) {
width = gTileWindowWidth - left;
}
if (top + height > gTileWindowHeight) {
height = gTileWindowHeight - top;
}
if (x >= gTileWindowWidth || x > rect->right || y >= gTileWindowHeight || y > rect->bottom) goto out;
frameWidth = artGetWidth(art, 0, 0);
frameHeight = artGetHeight(art, 0, 0);
if (left < x) {
v79 = 0;
int v12 = left + width;
v77 = frameWidth + x <= v12 ? frameWidth : v12 - x;
} else {
v79 = left - x;
x = left;
v77 = frameWidth - v79;
if (v77 > width) {
v77 = width;
}
}
if (top < y) {
int v14 = height + top;
v78 = 0;
v76 = frameHeight + y <= v14 ? frameHeight : v14 - y;
} else {
v78 = top - y;
y = top;
v76 = frameHeight - v78;
if (v76 > height) {
v76 = height;
}
}
if (v77 <= 0 || v76 <= 0) goto out;
v15 = tileFromScreenXY(savedX, savedY + 13, gElevation);
if (v15 != -1) {
int v17 = lightGetLightLevel();
for (int i = v15 & 1; i < 10; i++) {
// NOTE: calling _light_get_tile two times, probably a result of using __min kind macro
int v21 = _light_get_tile(elev, v15 + _verticies[i].field_4);
if (v21 <= v17) {
v21 = v17;
}
_verticies[i].field_C = v21;
}
int v23 = 0;
for (int i = 0; i < 9; i++) {
if (_verticies[i + 1].field_C != _verticies[i].field_C) {
break;
}
v23++;
}
if (v23 == 9) {
unsigned char* buf = artGetFrameData(art, 0, 0);
_dark_trans_buf_to_buf(buf + frameWidth * v78 + v79, v77, v76, frameWidth, gTileWindowBuffer, x, y, gTileWindowPitch, _verticies[0].field_C);
goto out;
}
for (int i = 0; i < 5; i++) {
STRUCT_51DB0C* ptr_51DB0C = &(_rightside_up_triangles[i]);
int v32 = _verticies[ptr_51DB0C->field_8].field_C;
int v33 = _verticies[ptr_51DB0C->field_8].field_0;
int v34 = _verticies[ptr_51DB0C->field_4].field_C - _verticies[ptr_51DB0C->field_0].field_C;
// TODO: Probably wrong.
int v35 = v34 / 32;
int v36 = (_verticies[ptr_51DB0C->field_0].field_C - v32) / 13;
int* v37 = &(_intensity_map[v33]);
if (v35 != 0) {
if (v36 != 0) {
for (int i = 0; i < 13; i++) {
int v41 = v32;
int v42 = _rightside_up_table[i].field_4;
v37 += _rightside_up_table[i].field_0;
for (int j = 0; j < v42; j++) {
*v37++ = v41;
v41 += v35;
}
v32 += v36;
}
} else {
for (int i = 0; i < 13; i++) {
int v38 = v32;
int v39 = _rightside_up_table[i].field_4;
v37 += _rightside_up_table[i].field_0;
for (int j = 0; j < v39; j++) {
*v37++ = v38;
v38 += v35;
}
}
}
} else {
if (v36 != 0) {
for (int i = 0; i < 13; i++) {
int v46 = _rightside_up_table[i].field_4;
v37 += _rightside_up_table[i].field_0;
for (int j = 0; j < v46; j++) {
*v37++ = v32;
}
v32 += v36;
}
} else {
for (int i = 0; i < 13; i++) {
int v44 = _rightside_up_table[i].field_4;
v37 += _rightside_up_table[i].field_0;
for (int j = 0; j < v44; j++) {
*v37++ = v32;
}
}
}
}
}
for (int i = 0; i < 5; i++) {
STRUCT_51DB48* ptr_51DB48 = &(_upside_down_triangles[i]);
int v50 = _verticies[ptr_51DB48->field_0].field_C;
int v51 = _verticies[ptr_51DB48->field_0].field_0;
int v52 = _verticies[ptr_51DB48->field_8].field_C - v50;
// TODO: Probably wrong.
int v53 = v52 / 32;
int v54 = (_verticies[ptr_51DB48->field_4].field_C - v50) / 13;
int* v55 = &(_intensity_map[v51]);
if (v53 != 0) {
if (v54 != 0) {
for (int i = 0; i < 13; i++) {
int v59 = v50;
int v60 = _upside_down_table[i].field_4;
v55 += _upside_down_table[i].field_0;
for (int j = 0; j < v60; j++) {
*v55++ = v59;
v59 += v53;
}
v50 += v54;
}
} else {
for (int i = 0; i < 13; i++) {
int v56 = v50;
int v57 = _upside_down_table[i].field_4;
v55 += _upside_down_table[i].field_0;
for (int j = 0; j < v57; j++) {
*v55++ = v56;
v56 += v53;
}
}
}
} else {
if (v54 != 0) {
for (int i = 0; i < 13; i++) {
int v64 = _upside_down_table[i].field_4;
v55 += _upside_down_table[i].field_0;
for (int j = 0; j < v64; j++) {
*v55++ = v50;
}
v50 += v54;
}
} else {
for (int i = 0; i < 13; i++) {
int v62 = _upside_down_table[i].field_4;
v55 += _upside_down_table[i].field_0;
for (int j = 0; j < v62; j++) {
*v55++ = v50;
}
}
}
}
}
unsigned char* v66 = gTileWindowBuffer + gTileWindowPitch * y + x;
unsigned char* v67 = artGetFrameData(art, 0, 0) + frameWidth * v78 + v79;
int* v68 = &(_intensity_map[160 + 80 * v78]) + v79;
int v86 = frameWidth - v77;
int v85 = gTileWindowPitch - v77;
int v87 = 80 - v77;
while (--v76 != -1) {
for (int kk = 0; kk < v77; kk++) {
if (*v67 != 0) {
int t = (*v67 << 8) + (*v68 >> 9);
*v66 = _intensityColorTable[t];
}
v67++;
v68++;
v66++;
}
v66 += v85;
v68 += v87;
v67 += v86;
}
}
out:
artUnlock(cacheEntry);
}
// 0x4B372C
2022-06-18 22:07:06 -07:00
static int _tile_make_line(int from, int to, int* tiles, int tilesCapacity)
2022-05-19 01:51:26 -07:00
{
if (tilesCapacity <= 1) {
return 0;
}
int count = 0;
int fromX;
int fromY;
tileToScreenXY(from, &fromX, &fromY, gElevation);
fromX += 16;
fromY += 8;
int toX;
int toY;
tileToScreenXY(to, &toX, &toY, gElevation);
toX += 16;
toY += 8;
tiles[count++] = from;
int stepX;
int deltaX = toX - fromX;
if (deltaX > 0)
stepX = 1;
else if (deltaX < 0)
stepX = -1;
else
stepX = 0;
int stepY;
int deltaY = toY - fromY;
if (deltaY > 0)
stepY = 1;
else if (deltaY < 0)
stepY = -1;
else
stepY = 0;
int v28 = 2 * abs(toX - fromX);
int v27 = 2 * abs(toY - fromY);
int tileX = fromX;
int tileY = fromY;
if (v28 <= v27) {
int middleX = v28 - v27 / 2;
while (true) {
int tile = tileFromScreenXY(tileX, tileY, gElevation);
tiles[count] = tile;
if (tile == to) {
count++;
break;
}
if (tile != tiles[count - 1] && (count == 1 || tile != tiles[count - 2])) {
count++;
if (count == tilesCapacity) {
break;
}
}
if (tileY == toY) {
break;
}
if (middleX >= 0) {
tileX += stepX;
middleX -= v27;
}
middleX += v28;
tileY += stepY;
}
} else {
int middleY = v27 - v28 / 2;
while (true) {
int tile = tileFromScreenXY(tileX, tileY, gElevation);
tiles[count] = tile;
if (tile == to) {
count++;
break;
}
if (tile != tiles[count - 1] && (count == 1 || tile != tiles[count - 2])) {
count++;
if (count == tilesCapacity) {
break;
}
}
if (tileX == toX) {
return count;
}
if (middleY >= 0) {
tileY += stepY;
middleY -= v28;
}
middleY += v27;
tileX += stepX;
}
}
return count;
}
// 0x4B3924
int _tile_scroll_to(int tile, int flags)
{
if (tile == gCenterTile) {
return -1;
}
int oldCenterTile = gCenterTile;
int v9[200];
int count = _tile_make_line(gCenterTile, tile, v9, 200);
if (count == 0) {
return -1;
}
int index = 1;
for (; index < count; index++) {
if (tileSetCenter(v9[index], 0) == -1) {
break;
}
}
int rc = 0;
if ((flags & 0x01) != 0) {
if (index != count) {
tileSetCenter(oldCenterTile, 0);
rc = -1;
}
}
if ((flags & 0x02) != 0) {
2022-05-23 15:37:46 -07:00
// NOTE: Uninline.
tileWindowRefresh();
2022-05-19 01:51:26 -07:00
}
return rc;
}
2022-09-23 05:43:44 -07:00
} // namespace fallout