Add tile bounds (#105)

This commit is contained in:
Alexander Batalov 2023-09-28 17:22:47 +03:00 committed by GitHub
parent 1db15fe6b5
commit 271601221e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 269 additions and 11 deletions

View File

@ -859,6 +859,11 @@ void gmouse_handle_event(int mouseX, int mouseY, int mouseState)
return;
}
// CE: Make sure we cannot go outside of the map.
if (!tile_point_inside_bound(mouseX, mouseY)) {
return;
}
if ((mouseState & MOUSE_EVENT_RIGHT_BUTTON_DOWN) != 0) {
if ((mouseState & MOUSE_EVENT_RIGHT_BUTTON_REPEAT) == 0) {
if (gmouse_3d_is_on()) {

View File

@ -455,6 +455,9 @@ int map_set_elevation(int elevation)
gmouse_set_cursor(MOUSE_CURSOR_NONE);
map_elevation = elevation;
// CE: Recalculate bounds.
tile_update_bounds_base();
register_clear(obj_dude);
dude_stand(obj_dude, obj_dude->rotation, obj_dude->fid);
partyMemberSyncPosition();
@ -1611,6 +1614,7 @@ static void map_scroll_refresh_game(Rect* rect)
grid_render(&rectToUpdate, map_elevation);
obj_render_pre_roof(&rectToUpdate, map_elevation);
square_render_roof(&rectToUpdate, map_elevation);
bounds_render(&rectToUpdate, map_elevation);
obj_render_post_roof(&rectToUpdate, map_elevation);
}

View File

@ -772,6 +772,19 @@ void obj_render_pre_roof(Rect* rect, int elevation)
return;
}
// CE: Constrain rect to tile bounds so that we don't draw outside.
if (tile_inside_bound(&updatedRect) != 0) {
// Mouse hex cursor is a special case - should be shown as outline when
// out of bounds (see `obj_render_outline`).
outlineCount = 0;
if ((obj_mouse_flat->flags & OBJECT_HIDDEN) == 0
&& (obj_mouse_flat->outline & OUTLINE_TYPE_MASK) != 0
&& (obj_mouse_flat->outline & OUTLINE_DISABLED) == 0) {
outlinedObjects[outlineCount++] = obj_mouse_flat;
}
return;
}
int ambientIntensity = light_get_ambient();
int minX = updatedRect.ulx - 320;
int minY = updatedRect.uly - 240;
@ -874,8 +887,23 @@ void obj_render_post_roof(Rect* rect, int elevation)
return;
}
// CE: Constrain rect to tile bounds so that we don't draw outside.
Rect constrainedRect = updatedRect;
if (tile_inside_bound(&constrainedRect) != 0) {
constrainedRect.ulx = 0;
constrainedRect.uly = 0;
constrainedRect.lrx = 0;
constrainedRect.lry = 0;
}
for (int index = 0; index < outlineCount; index++) {
obj_render_outline(outlinedObjects[index], &updatedRect);
// Mouse hex cursor is a special case - should be shown without
// constraining otherwise its hidden.
if (outlinedObjects[index] == obj_mouse_flat) {
obj_render_outline(outlinedObjects[index], &updatedRect);
} else {
obj_render_outline(outlinedObjects[index], &constrainedRect);
}
}
text_object_render(&updatedRect);

View File

@ -1,6 +1,7 @@
#include "game/tile.h"
#include <assert.h>
#include <limits.h>
#include <string.h>
#define _USE_MATH_DEFINES
@ -557,6 +558,9 @@ int tile_set_center(int tile, int flags)
tile_center_tile = tile;
// CE: Updates bounds screen coordinates.
tile_update_bounds_rect();
if ((flags & TILE_SET_CENTER_REFRESH_WINDOW) != 0) {
// NOTE: Uninline.
tile_refresh_display();
@ -606,6 +610,7 @@ static void refresh_game(Rect* rect, int elevation)
square_render_floor(&rectToUpdate, elevation);
obj_render_pre_roof(&rectToUpdate, elevation);
square_render_roof(&rectToUpdate, elevation);
bounds_render(&rectToUpdate, elevation);
obj_render_post_roof(&rectToUpdate, elevation);
blit(&rectToUpdate);
}
@ -1154,10 +1159,16 @@ void square_render_roof(Rect* rect, int elevation)
int maxX;
int maxY;
square_xy_roof(rect->ulx, rect->uly, elevation, &temp, &minY);
square_xy_roof(rect->lrx, rect->uly, elevation, &minX, &temp);
square_xy_roof(rect->ulx, rect->lry, elevation, &maxX, &temp);
square_xy_roof(rect->lrx, rect->lry, elevation, &temp, &maxY);
// CE: Constrain rect to tile bounds so that we don't draw outside.
Rect constrainedRect = *rect;
if (tile_inside_bound(&constrainedRect) != 0) {
return;
}
square_xy_roof(constrainedRect.ulx, constrainedRect.uly, elevation, &temp, &minY);
square_xy_roof(constrainedRect.lrx, constrainedRect.uly, elevation, &minX, &temp);
square_xy_roof(constrainedRect.ulx, constrainedRect.lry, elevation, &maxX, &temp);
square_xy_roof(constrainedRect.lrx, constrainedRect.lry, elevation, &temp, &maxY);
if (minX < 0) {
minX = 0;
@ -1191,7 +1202,7 @@ void square_render_roof(Rect* rect, int elevation)
int screenX;
int screenY;
square_coord_roof(squareTile, &screenX, &screenY, elevation);
roof_draw(fid, screenX, screenY, rect, light);
roof_draw(fid, screenX, screenY, &constrainedRect, light);
}
}
}
@ -1378,10 +1389,16 @@ void square_render_floor(Rect* rect, int elevation)
int minX;
int temp;
square_xy(rect->ulx, rect->uly, elevation, &temp, &minY);
square_xy(rect->lrx, rect->uly, elevation, &minX, &temp);
square_xy(rect->ulx, rect->lry, elevation, &maxX, &temp);
square_xy(rect->lrx, rect->lry, elevation, &temp, &maxY);
// CE: Constrain rect to tile bounds so that we don't draw outside.
Rect constrainedRect = *rect;
if (tile_inside_bound(&constrainedRect) != 0) {
return;
}
square_xy(constrainedRect.ulx, constrainedRect.uly, elevation, &temp, &minY);
square_xy(constrainedRect.lrx, constrainedRect.uly, elevation, &minX, &temp);
square_xy(constrainedRect.ulx, constrainedRect.lry, elevation, &maxX, &temp);
square_xy(constrainedRect.lrx, constrainedRect.lry, elevation, &temp, &maxY);
if (minX < 0) {
minX = 0;
@ -1412,7 +1429,7 @@ void square_render_floor(Rect* rect, int elevation)
int tileScreenY;
square_coord(squareTile, &tileScreenX, &tileScreenY, elevation);
int fid = art_id(OBJ_TYPE_TILE, frmId & 0xFFF, 0, 0, 0);
floor_draw(fid, tileScreenX, tileScreenY, rect);
floor_draw(fid, tileScreenX, tileScreenY, &constrainedRect);
}
}
baseSquareTile += square_width;
@ -1960,4 +1977,202 @@ int tile_scroll_to(int tile, int flags)
return rc;
}
static Rect tile_bounds_rect;
static int tile_bounds_left_off;
static int tile_bounds_top_off;
static int tile_bounds_right_off;
static int tile_bounds_bottom_off;
void tile_update_bounds_base()
{
int min_x = INT_MAX;
int min_y = INT_MAX;
int max_x = INT_MIN;
int max_y = INT_MIN;
// Determine bounding rectangle of scroll blocking objects.
for (int tile = 0; tile < grid_size; tile++) {
if (obj_scroll_blocking_at(tile, map_elevation) == 0) {
int x;
int y;
tile_coord(tile, &x, &y, map_elevation);
x += 16;
y += 8;
if (x < min_x) {
min_x = x;
}
if (y < min_y) {
min_y = y;
}
if (x > max_x) {
max_x = x;
}
if (y > max_y) {
max_y = y;
}
}
}
// Translate bounding rectangle in screen coordinates (which are relative
// to screen center tile) to offsets from reference tile (geometric center
// of the map).
int geometric_center_x;
int geometric_center_y;
tile_coord(20100, &geometric_center_x, &geometric_center_y, map_elevation);
geometric_center_x += 16;
geometric_center_y += 8;
tile_bounds_left_off = min_x - geometric_center_x;
tile_bounds_top_off = min_y - geometric_center_y;
tile_bounds_right_off = max_x - geometric_center_x;
tile_bounds_bottom_off = max_y - geometric_center_y;
}
void tile_update_bounds_rect()
{
// Translate offsets from reference tile to screen coordinates.
int geometric_center_x;
int geometric_center_y;
tile_coord(20100, &geometric_center_x, &geometric_center_y, map_elevation);
geometric_center_x += 16;
geometric_center_y += 8;
tile_bounds_rect.ulx = tile_bounds_left_off + geometric_center_x;
tile_bounds_rect.uly = tile_bounds_top_off + geometric_center_y;
tile_bounds_rect.lrx = tile_bounds_right_off + geometric_center_x;
tile_bounds_rect.lry = tile_bounds_bottom_off + geometric_center_y;
// The bounding rectangle' corners are centers from scroll blocking objects.
// Since we're dealing with hex map where each row is shifted, we have two
// sets of blockers on each edge - to handle odd and even rows. Depending
// on scroll blockers location we can either have center tile to "touch"
// one scroll blocker or be "surrounded" by three of them. This requires
// bounds to be multiple of scroll steps.
int tile_center_x;
int tile_center_y;
tile_coord(tile_center_tile, &tile_center_x, &tile_center_y, map_elevation);
tile_center_x += 16;
tile_center_y += 8;
tile_bounds_rect.ulx -= (tile_bounds_rect.ulx - tile_center_x) % 32;
tile_bounds_rect.uly -= (tile_bounds_rect.uly - tile_center_y) % 24;
tile_bounds_rect.lrx -= (tile_bounds_rect.lrx - tile_center_x) % 32;
tile_bounds_rect.lry -= (tile_bounds_rect.lry - tile_center_y) % 24;
// Scroll blocker itself cannot become center tile, so inset bounds for one
// full tile size.
tile_bounds_rect.ulx += 32;
tile_bounds_rect.uly += 16;
tile_bounds_rect.lrx -= 32;
tile_bounds_rect.lry -= 16;
// Scroll blockers where placed for 640x480 resolution, which means visible
// rect is half of than amount in each direction.
tile_bounds_rect.ulx -= 640 / 2;
tile_bounds_rect.uly -= (480 - 100) / 2;
tile_bounds_rect.lrx += 640 / 2;
tile_bounds_rect.lry += (480 - 100) / 2;
// Adjust for vertical layout.
tile_bounds_rect.uly += 8;
tile_bounds_rect.lry -= 8;
// Decrement one px to make sure rect is what engine expects it to be.
tile_bounds_rect.lrx -= 1;
tile_bounds_rect.lry -= 1;
}
int tile_inside_bound(Rect* rect)
{
return rect_inside_bound(rect, &tile_bounds_rect, rect);
}
bool tile_point_inside_bound(int x, int y)
{
return x >= tile_bounds_rect.ulx && x <= tile_bounds_rect.lrx
&& y >= tile_bounds_rect.uly && y <= tile_bounds_rect.lry;
}
void bounds_render(Rect* rect, int elevation)
{
constexpr int kShadowSize = 16;
Rect edge;
// Left.
edge.ulx = tile_bounds_rect.ulx;
edge.uly = tile_bounds_rect.uly;
edge.lrx = tile_bounds_rect.ulx + kShadowSize;
edge.lry = tile_bounds_rect.lry;
if (rect_inside_bound(&edge, rect, &edge) == 0) {
for (int y = edge.uly; y <= edge.lry; y++) {
unsigned char* dest = buf + buf_full * y + edge.ulx;
int step = edge.ulx - tile_bounds_rect.ulx;
for (int x = edge.ulx; x <= edge.lrx; x++) {
unsigned char color = *dest;
*dest++ = intensityColorTable[color][step * 128 / kShadowSize];
step++;
}
}
}
// Top.
edge.ulx = tile_bounds_rect.ulx;
edge.uly = tile_bounds_rect.uly;
edge.lrx = tile_bounds_rect.lrx;
edge.lry = tile_bounds_rect.uly + kShadowSize;
if (rect_inside_bound(&edge, rect, &edge) == 0) {
int step = edge.uly - tile_bounds_rect.uly;
for (int y = edge.uly; y <= edge.lry; y++) {
unsigned char* dest = buf + buf_full * y + edge.ulx;
for (int x = edge.ulx; x <= edge.lrx; x++) {
unsigned char color = *dest;
*dest++ = intensityColorTable[color][step * 128 / kShadowSize];
}
step++;
}
}
// Right.
edge.ulx = tile_bounds_rect.lrx - kShadowSize;
edge.uly = tile_bounds_rect.uly;
edge.lrx = tile_bounds_rect.lrx;
edge.lry = tile_bounds_rect.lry;
if (rect_inside_bound(&edge, rect, &edge) == 0) {
for (int y = edge.uly; y <= edge.lry; y++) {
unsigned char* dest = buf + buf_full * y + edge.lrx;
int step = tile_bounds_rect.lrx - edge.lrx;
for (int x = edge.lrx; x >= edge.ulx; x--) {
unsigned char color = *dest;
*dest-- = intensityColorTable[color][step * 128 / kShadowSize];
step++;
}
}
}
// Bottom.
edge.ulx = tile_bounds_rect.ulx;
edge.uly = tile_bounds_rect.lry - kShadowSize;
edge.lrx = tile_bounds_rect.lrx;
edge.lry = tile_bounds_rect.lry;
if (rect_inside_bound(&edge, rect, &edge) == 0) {
int step = tile_bounds_rect.lry - edge.lry;
for (int y = edge.lry; y >= edge.uly; y--) {
unsigned char* dest = buf + buf_full * y + edge.ulx;
for (int x = edge.ulx; x <= edge.lrx; x++) {
unsigned char color = *dest;
*dest++ = intensityColorTable[color][step * 128 / kShadowSize];
}
step++;
}
}
}
} // namespace fallout

View File

@ -63,6 +63,12 @@ void floor_draw(int fid, int x, int y, Rect* rect);
int tile_make_line(int currentCenterTile, int newCenterTile, int* tiles, int tilesCapacity);
int tile_scroll_to(int tile, int flags);
void tile_update_bounds_base();
void tile_update_bounds_rect();
int tile_inside_bound(Rect* rect);
bool tile_point_inside_bound(int x, int y);
void bounds_render(Rect* rect, int elevation);
} // namespace fallout
#endif /* FALLOUT_GAME_TILE_H_ */