ReC98/th02/main/player/bomb.cpp

240 lines
5.9 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "platform.h"
#include "pc98.h"
#include "planar.h"
#include "master.hpp"
#include "th01/math/overlap.hpp"
#include "th02/main/playfld.hpp"
#include "th02/main/scroll.hpp"
#include "th02/main/tile/tile.hpp"
#include "th02/main/player/player.hpp"
#include "th02/main/player/bomb.hpp"
#include "th02/sprites/bombpart.h"
extern int bomb_frame;
extern point_t bomb_circle_center;
extern int bomb_circle_frame;
extern bool16 bomb_circle_done;
extern bool16 (near pascal *near playchar_bomb_func)(void);
// Function ordering fails
// -----------------------
// These assume the GRCG to be set to RMW mode, with a tile in the intended
// color.
void pascal near bomb_circle_point_put(screen_x_t left, screen_y_t top);
void pascal near bomb_particle_put_8(screen_x_t left, screen_y_t top, int cel);
void pascal near bomb_smear_put_8(screen_x_t left, screen_y_t column_bottom);
void pascal near bomb_bft_8tiles_put_8(
screen_x_t left, screen_y_t top, dots8_t dots
);
// -----------------------
void near bomb_circle_update_and_render(void)
;
void pascal near bomb_reimu_a(void);
void pascal near bomb_reimu_c(void);
void pascal near bomb_reimu_b(void);
void near bomb_update_and_render(void)
{
bool16 done = false;
if(!bombing) {
return;
}
bomb_frame++;
if(bomb_circle_done == false) {
if(bomb_frame <= BOMB_CIRCLE_FRAMES) {
bomb_circle_update_and_render();
} else if(bomb_circle_frame == 0) {
bomb_frame = 0;
bomb_circle_done++;
}
} else if(bomb_circle_done == true) {
done = playchar_bomb_func();
}
if(done) {
bombing = false;
player_invincible_via_bomb = false;
player_invincibility_time = 70;
palette_100();
}
}
// ZUN bloat: Needed to circumvent 16-bit promotion in a single comparison.
inline pixel_delta_8_t bomb_particle_h(void) {
return BOMB_PARTICLE_H;
}
void pascal near bomb_circle_point_put(screen_x_t left, screen_y_t top)
{
#define vram_top static_cast<vram_y_t>(_DX)
#define first_bit_mirrored _DX
register const bomb_particle_dots_t near* sprite;
pixel_delta_8_t y;
unsigned int first_bit;
if(!overlap_xy_ltrb_lt_gt(
left, top,
(PLAYFIELD_LEFT - BOMB_PARTICLE_W),
(PLAYFIELD_TOP - BOMB_PARTICLE_H),
PLAYFIELD_RIGHT,
PLAYFIELD_BOTTOM
)) {
return;
}
_ES = SEG_PLANE_B;
vram_top = scroll_screen_y_to_vram(vram_top, top);
_BX = left;
vram_offset_shift_fast_asm(di, bx, vram_top);
// ZUN bloat: Turbo C++ 4.0J would otherwise only ever generate an 8-bit
// AND.
asm { and bx, BYTE_MASK; }
first_bit = _BX;
first_bit_mirrored = (sizeof(dots16_t) * BYTE_DOTS);
// ZUN bloat: first_bit_mirrored -= _BX;
asm { sub dx, bx; }
sprite = sBOMB_CIRCLE;
y = 0;
do {
// ZUN bloat: A manual 16-bit right rotation. Turbo C++ 4.0J has
// intrinsics for that, which compile into a single ROR instruction:
//
// *reinterpret_cast<dots16_t __es *>(vo) = __rotr__(
// *sprite, first_bit
// );
asm { xor ax, ax; }
_AL = *sprite;
asm { mov bx, ax; }
_CX = first_bit;
_BX >>= _CL;
asm { mov cx, dx; }
_AX <<= _CL;
asm { add ax, bx; }
*reinterpret_cast<dots16_t __es *>(_DI) = _AX;
vram_offset_add_and_roll(_DI, ROW_SIZE);
sprite++;
y++;
} while(y < bomb_particle_h());
#undef first_bit_mirrored
#undef vram_top
}
void pascal near bomb_particle_put_8(screen_x_t left, screen_y_t top, int cel)
{
#define _DI static_cast<vram_offset_t>(_DI)
_ES = SEG_PLANE_B;
_DX = scroll_screen_y_to_vram(static_cast<vram_y_t>(_DX), top);
vram_offset_shift_fast_asm(di, left, _DX);
// ZUN bloat: _SI = &sBOMB_PARTICLES[cel];
_SI = reinterpret_cast<uint16_t>(&sBOMB_PARTICLES[0][0]);
_AX = (cel * sizeof(sBOMB_PARTICLES[0]));
asm { add si, ax; }
_CX = BOMB_PARTICLE_H;
loop: {
asm { movsb; }
vram_offset_add_and_roll(
_DI, (ROW_SIZE - (BOMB_PARTICLE_W / BYTE_DOTS))
);
asm { loop loop; }
}
#undef _DI
}
void pascal near bomb_smear_put_8(screen_x_t left, screen_y_t column_bottom_)
{
#define y static_cast<vram_y_t>(_DX)
#define column_bottom static_cast<screen_y_t>(_BX)
_ES = SEG_PLANE_B;
y = scroll_screen_y_to_vram(y, PLAYFIELD_TOP);
vram_offset_shift_fast_asm(di, left, y);
// ZUN bloat: &sBOMB_CIRCLE[BOMB_PARTICLE_H / 2];
register const bomb_particle_dots_t near* sprite = &sBOMB_CIRCLE[0];
sprite += ((BOMB_PARTICLE_H / 2) * sizeof(bomb_particle_dots_t));
_CX = (BOMB_PARTICLE_H / 2);
y = PLAYFIELD_TOP;
column_bottom = column_bottom_;
_AL = static_cast<bomb_particle_dots_t>(-1);
loop: {
*reinterpret_cast<bomb_particle_dots_t __es *>(_DI) = _AL;
vram_offset_add_and_roll(_DI, ROW_SIZE);
y++;
// ZUN bloat: if(y >= column_bottom)
asm { cmp dx, bx; }
asm { jl still_in_column; }
// We're in the bottom part; switch to drawing the sprite rather than
// the constant 0xFF set before the loop.
_AL = *sprite++;
_CX--;
still_in_column:
// ZUN bloat: do { … } while(_CX > 0);
asm { cmp cx, 0; }
asm { ja loop; }
}
#undef column_bottom
#undef y
}
void pascal near bomb_bft_tile_put_8(vram_offset_t vo)
{
_CX = TILE_H;
_DI = vo;
_BX = static_cast<dots_t(TILE_W)>(-1);
loop: {
*reinterpret_cast<dots_t(TILE_W) __es *>(_DI) = _BX;
vram_offset_add_and_roll(_DI, ROW_SIZE);
asm { loop loop; }
}
}
// Renders [dots] from BOMBS.BFT as a 8×1 row of 16×16 blocks, covering a total
// size of 128×16 pixels, to (⌊left/8⌋*8, top). Each tile is filled with the
// current GRCG tile if its corresponding bit is 1, or skipped otherwise.
// Conceptually identical to the tiles_bb_*() functions from TH04 and TH05.
void pascal near bomb_bft_8tiles_put_8(
screen_x_t left, screen_y_t top, dots8_t dots
)
{
#define tile_cur static_cast<dots8_t>(_AL)
#define tiles static_cast<dots8_t>(_AH)
_ES = SEG_PLANE_B;
_DX = scroll_screen_y_to_vram(static_cast<vram_y_t>(_DX), top);
vram_offset_shift_fast_asm(di, left, _DX);
tile_cur = 0x80;
tiles = dots;
asm { xor si, si; }
do {
if(tile_cur & tiles) {
bomb_bft_tile_put_8(_DI);
}
tile_cur >>= 1;
_DI += TILE_VRAM_W;
_SI++;
} while(_SI < BYTE_DOTS);
#undef tiles
#undef tile_cur
}