mirror of https://github.com/nmlgc/ReC98.git
215 lines
9.1 KiB
C++
215 lines
9.1 KiB
C++
#define PLAYFIELD_LEFT (32)
|
||
#define PLAYFIELD_TOP (16)
|
||
#define PLAYFIELD_W (384)
|
||
#define PLAYFIELD_H (368)
|
||
|
||
#define PLAYFIELD_RIGHT (PLAYFIELD_LEFT + PLAYFIELD_W)
|
||
#define PLAYFIELD_BOTTOM (PLAYFIELD_TOP + PLAYFIELD_H)
|
||
|
||
#define PLAYFIELD_VRAM_LEFT (PLAYFIELD_LEFT / BYTE_DOTS)
|
||
#define PLAYFIELD_VRAM_W (PLAYFIELD_W / BYTE_DOTS)
|
||
#define PLAYFIELD_VRAM_RIGHT (PLAYFIELD_RIGHT / BYTE_DOTS)
|
||
|
||
#define PLAYFIELD_TRAM_LEFT (PLAYFIELD_LEFT / 8)
|
||
#define PLAYFIELD_TRAM_TOP (PLAYFIELD_TOP / GLYPH_H)
|
||
#define PLAYFIELD_TRAM_W (PLAYFIELD_W / 8)
|
||
#define PLAYFIELD_TRAM_CENTER_X \
|
||
((PLAYFIELD_LEFT + (PLAYFIELD_W / 2)) / GLYPH_HALF_W)
|
||
#define PLAYFIELD_TRAM_CENTER_Y \
|
||
((PLAYFIELD_TOP + (PLAYFIELD_H / 2)) / GLYPH_H)
|
||
#define PLAYFIELD_TRAM_RIGHT (PLAYFIELD_RIGHT / 8)
|
||
#define PLAYFIELD_TRAM_BOTTOM (PLAYFIELD_BOTTOM / GLYPH_H)
|
||
|
||
/// Workarounds for the lack of clipping in master.lib's super_*() functions
|
||
/// ------------------------------------------------------------------------
|
||
/// ZUN bug: These functions ignore not only the grc_setclip() region, but also
|
||
/// at least the left and right edges of VRAM. They always blit any sprite at
|
||
/// its full size to the naively calculated ((top * ROW_SIZE) + left) offset,
|
||
/// incorrectly wrapping around at the horizontal edges into the previous
|
||
/// (left) or next (right) row. While the *_roll_*() functions do have to
|
||
/// correctly wrap at the top and bottom edges of VRAM, the non-rolling ones
|
||
/// don't, falling back on the hardware's unhelpful wrapping around the 0x8000
|
||
/// offset limit for VRAM segments.
|
||
/// (Yes, the hardware ANDs VRAM offsets with 0x7FFF before a write, but this
|
||
/// does *not* mean that such offsets are wrapped vertically. 0x8000 is not a
|
||
/// multiple of the ROW_SIZE, and any pixels wrapped this way would be
|
||
/// horizontally shifted by 384 pixels.)
|
||
///
|
||
/// For this reason, any sprite larger than the 32×16 (non-rolling) or 32×32
|
||
/// (rolling) playfield margin must be clipped earlier than the technically
|
||
/// correct position. The smallest allowed top-left coordinate in screen space
|
||
/// therefore is
|
||
///
|
||
/// ( 0, 0) non-rolling / ( 0, -16) rolling,
|
||
///
|
||
/// and the bottom-right coordinate must never reach
|
||
///
|
||
/// (448, 400) non-rolling / (448, 416) rolling.
|
||
///
|
||
/// This is the only way to avoid glitches from master.lib's incorrect
|
||
/// wraparounds, such as HUD background or tile source area corruption. Since
|
||
/// these larger sprites can still be displayed at the calculated clipping
|
||
/// point, we use < and > for the clipping condition. For smaller sprites, we
|
||
/// instead use ≤ and ≥ to clip them as early as possible.
|
||
///
|
||
/// (Technically, non-rolling sprites could even extend into the invisible 9.6
|
||
/// rows of VRAM between offsets 0x7D00 and 0x7FFF inclusive, which would allow
|
||
/// them to be clipped 9 pixels later. These rows are not used for anything
|
||
/// else, and could be overwritten without observable effects. Thankfully, ZUN
|
||
/// doesn't make use of this.)
|
||
///
|
||
/// ZUN bloat: Unfortunately, Turbo C++ 4.0J can't constant-fold the ternary
|
||
/// expressions that would allow us to abstract away the difference between
|
||
/// small and large sprites. This forces us to duplicate every macro, and every
|
||
/// call site to spell out which of these two it wants to use. Then again, ZUN
|
||
/// even forces us to, as certain rendering functions actually use the
|
||
/// incorrect variant...
|
||
///
|
||
/// These macros should only be used in rendering code. This allows all usages
|
||
/// to share the same ZUN bug, and to be fixed simultaneously without affecting
|
||
/// gameplay. For clipping checks that *are* supposed to influence gameplay,
|
||
/// use the playfield_encloses*() functions further below, which always clip
|
||
/// correctly and without this workaround.
|
||
|
||
#define PLAYFIELD_CLIP_RIGHT (HUD_LEFT * GLYPH_HALF_W)
|
||
|
||
// Sum of the top and bottom margin. Rolled sprites can be freely blitted
|
||
// within this area where they will consequently wrap vertically at the edge of
|
||
// the screen, but these wrapped pixels won't be visible because they're always
|
||
// covered by opaque black TRAM cells.
|
||
#define PLAYFIELD_ROLL_MARGIN (PLAYFIELD_TOP + (RES_Y - PLAYFIELD_BOTTOM))
|
||
|
||
#define playfield_clip_center_left_small(center_x, w) ( \
|
||
center_x <= to_sp((w / 2) - PLAYFIELD_LEFT) \
|
||
)
|
||
#define playfield_clip_center_left_large(center_x, w) ( \
|
||
center_x < to_sp((w / 2) - PLAYFIELD_LEFT) \
|
||
)
|
||
|
||
#define playfield_clip_center_right_small(center_x, w) ( \
|
||
center_x >= to_sp(PLAYFIELD_CLIP_RIGHT - PLAYFIELD_LEFT - (w / 2)) \
|
||
)
|
||
#define playfield_clip_center_right_large(center_x, w) ( \
|
||
center_x > to_sp(PLAYFIELD_CLIP_RIGHT - PLAYFIELD_LEFT - (w / 2)) \
|
||
)
|
||
|
||
#define playfield_clip_center_top_small_roll(center_y, h) ( \
|
||
center_y <= to_sp((h / 2) - PLAYFIELD_ROLL_MARGIN) \
|
||
)
|
||
#define playfield_clip_center_top_large_roll(center_y, h) ( \
|
||
center_y < to_sp((h / 2) - PLAYFIELD_ROLL_MARGIN) \
|
||
)
|
||
|
||
#define playfield_clip_center_bottom_small_roll(center_y, h) ( \
|
||
center_y >= to_sp(PLAYFIELD_H + PLAYFIELD_ROLL_MARGIN - (h / 2)) \
|
||
)
|
||
#define playfield_clip_center_bottom_large_roll(center_y, h) ( \
|
||
center_y > to_sp(PLAYFIELD_H + PLAYFIELD_ROLL_MARGIN - (h / 2)) \
|
||
)
|
||
|
||
#define playfield_clip_left_small( left, w) (left <= (PLAYFIELD_LEFT - w))
|
||
#define playfield_clip_right_small(left, w) (left >= PLAYFIELD_RIGHT)
|
||
|
||
#define playfield_clip_left_large( left, w) (left < 0)
|
||
#define playfield_clip_right_large(left, w) (left > (PLAYFIELD_CLIP_RIGHT - w))
|
||
|
||
#define playfield_clip_top_small( top, h) (top <= (PLAYFIELD_TOP - h))
|
||
#define playfield_clip_bottom_small(top, h) (top >= PLAYFIELD_BOTTOM)
|
||
|
||
// Also working with unsigned [top] values.
|
||
#define playfield_clip_top_large( top, h) (top < 0)
|
||
#define playfield_clip_bottom_large(top, h) (top > (RES_Y - h))
|
||
|
||
#define playfield_clip_topleft_small(left, top, w, h) ( \
|
||
playfield_clip_left_small(left, w) || \
|
||
playfield_clip_right_small(left, w) || \
|
||
playfield_clip_top_small(top, h) || \
|
||
playfield_clip_bottom_small(top, h) \
|
||
)
|
||
#define playfield_clip_topleft_large(left, top, w, h) ( \
|
||
playfield_clip_left_large(left, w) || \
|
||
playfield_clip_right_large(left, w) || \
|
||
playfield_clip_top_large(top, h) || \
|
||
playfield_clip_bottom_large(top, h) \
|
||
)
|
||
|
||
#define playfield_clip_center_yx_small_roll(center_x, center_y, w, h) ( \
|
||
(playfield_clip_center_top_small_roll((subpixel_t)(center_y), h)) || \
|
||
(playfield_clip_center_bottom_small_roll((subpixel_t)(center_y), h)) || \
|
||
(playfield_clip_center_left_small((subpixel_t)(center_x), w)) || \
|
||
(playfield_clip_center_right_small((subpixel_t)(center_x), w)) \
|
||
)
|
||
#define playfield_clip_center_yx_large_roll(center_x, center_y, w, h) ( \
|
||
(playfield_clip_center_top_large_roll((subpixel_t)(center_y), h)) || \
|
||
(playfield_clip_center_bottom_large_roll((subpixel_t)(center_y), h)) || \
|
||
(playfield_clip_center_left_large((subpixel_t)(center_x), w)) || \
|
||
(playfield_clip_center_right_large((subpixel_t)(center_x), w)) \
|
||
)
|
||
|
||
#define playfield_clip_point_yx_small_roll(center, w, h) \
|
||
playfield_clip_center_yx_small_roll(center.x, center.y, w, h)
|
||
#define playfield_clip_point_yx_large_roll(center, w, h) \
|
||
playfield_clip_center_yx_large_roll(center.x, center.y, w, h)
|
||
/// ------------------------------------------------------------------------
|
||
|
||
#define playfield_encloses_yx_lt_ge(center_x, center_y, w, h) ( \
|
||
/* Casting the center coordinate allows this macro to easily be used */ \
|
||
/* with the _AX and _DX pseudoregisters after motion_update(). */ \
|
||
(static_cast<subpixel_t>(center_y) >= to_sp(0 - (h / 2))) && \
|
||
(static_cast<subpixel_t>(center_y) < to_sp(PLAYFIELD_H + (h / 2))) && \
|
||
(static_cast<subpixel_t>(center_x) >= to_sp(0 - (w / 2))) && \
|
||
(static_cast<subpixel_t>(center_x) < to_sp(PLAYFIELD_W + (w / 2))) \
|
||
)
|
||
|
||
#define playfield_encloses(center_x, center_y, w, h) ( \
|
||
/* Casting the center coordinate allows this macro to easily be used */ \
|
||
/* with the _AX and _DX pseudoregisters after motion_update(). */ \
|
||
(static_cast<subpixel_t>(center_x) > to_sp(0 - (w / 2))) && \
|
||
(static_cast<subpixel_t>(center_x) < to_sp(PLAYFIELD_W + (w / 2))) && \
|
||
(static_cast<subpixel_t>(center_y) > to_sp(0 - (h / 2))) && \
|
||
(static_cast<subpixel_t>(center_y) < to_sp(PLAYFIELD_H + (h / 2))) \
|
||
)
|
||
|
||
#define playfield_encloses_point(center, w, h) \
|
||
playfield_encloses(center.x, center.y, w, h)
|
||
|
||
#define playfield_to_screen_left(subpixel_center_x, sprite_w) ( \
|
||
PLAYFIELD_LEFT + TO_PIXEL(subpixel_center_x) - (sprite_w / 2) \
|
||
)
|
||
|
||
#define playfield_to_screen_top(subpixel_center_y, sprite_h) ( \
|
||
PLAYFIELD_TOP + TO_PIXEL(subpixel_center_y) - (sprite_h / 2) \
|
||
)
|
||
|
||
#ifdef SUBPIXEL_HPP
|
||
struct PlayfieldPoint : public SPPoint {
|
||
screen_x_t to_screen_left(pixel_t sprite_w_if_centered = 0) const {
|
||
return playfield_to_screen_left(x, sprite_w_if_centered);
|
||
}
|
||
|
||
screen_y_t to_screen_top(pixel_t sprite_h_if_centered = 0) const {
|
||
return playfield_to_screen_top(y, sprite_h_if_centered);
|
||
}
|
||
|
||
#ifdef SCROLL_HPP
|
||
vram_y_t to_vram_top_scrolled_seg1(
|
||
pixel_t sprite_h_if_centered
|
||
) const {
|
||
return scroll_subpixel_y_to_vram_seg1(
|
||
y + (PLAYFIELD_TOP - (sprite_h_if_centered / 2))
|
||
);
|
||
}
|
||
|
||
vram_y_t to_vram_top_scrolled_seg3(
|
||
pixel_t sprite_h_if_centered
|
||
) const {
|
||
return scroll_subpixel_y_to_vram_seg3(
|
||
y + (PLAYFIELD_TOP - (sprite_h_if_centered / 2))
|
||
);
|
||
}
|
||
#endif
|
||
};
|
||
#endif
|
||
|
||
#include "th01/main/playfld.hpp"
|