Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tile bounds #105

Merged
merged 2 commits into from
Sep 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/game/gmouse.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
4 changes: 4 additions & 0 deletions src/game/map.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}

Expand Down
30 changes: 29 additions & 1 deletion src/game/object.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
235 changes: 225 additions & 10 deletions src/game/tile.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "game/tile.h"

#include <assert.h>
#include <limits.h>
#include <string.h>

#define _USE_MATH_DEFINES
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
}
}
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
6 changes: 6 additions & 0 deletions src/game/tile.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */