ReC98/th01/main/bullet/missile.cpp

176 lines
4.5 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.

extern "C" {
#include "planar.h"
#include "th01/math/overlap.hpp"
#include "th01/hardware/egc.h"
#include "th01/hardware/input.hpp"
#include "th01/snd/mdrv2.h"
#include "th01/formats/ptn.hpp"
}
#include "th01/main/vars.hpp"
#include "th01/main/bullet/missile.hpp"
void CMissiles::reset(void)
{
for(int i = 0; i < MISSILE_COUNT; i++) {
flag[i] = MF_FREE;
}
}
// Missiles are blitted to unaligned X positions (and are, in fact, the only
// 16×16 .PTN entity where this is the case), so a missile sprite is likely
// to cover two 16-pixel words. Since the left coordinate is rounded down to
// the previous multiple of 16, the unblitting width therefore has to be
// doubled.
// … Except that it doesn't have to, because egc_copy_rect_1_to_0_16() already
// ceils the unblitted width to the next multiple of 16. Which makes this
// addition just as unnecessary as the _word_w() wrapper.
#define sloppy_unput(missiles, i) { \
egc_copy_rect_1_to_0_16_word_w( \
(missiles).prev_left[i].to_pixel(), \
(missiles).prev_top[i].to_pixel(), \
(15 + MISSILE_W), \
MISSILE_H \
); \
}
#define in_current_interlace_field(i) \
((i % 2) == (frame_rand & 1))
// Calculates the current [ptn_id] and [quarter] for the missile at the given
// index.
// TODO: Should be turned into a class method once it can be part of this
// translation unit.
void ptn_cel_for(CMissiles& that, int i, main_ptn_id_t& ptn_id, int& quarter);
void CMissiles::unput_update_render(void)
{
int i;
// Unput/update/explode
for(i = 0; i < MISSILE_COUNT; i++) {
if(flag[i] == MF_FREE) {
continue;
}
if(in_current_interlace_field(i) && (prev_left[i].v != MISSILE_NEW)) {
sloppy_unput(*this, i);
}
if(flag[i] == MF_MOVING) {
cur_left[i].v += velocity_x[i].v;
cur_top[i].v += velocity_y[i].v;
if(!overlap_xy_lrtb_le_ge(
cur_left[i].to_pixel(),
cur_top[i].to_pixel(),
MISSILE_LEFT_MIN,
MISSILE_TOP_MIN,
MISSILE_LEFT_MAX,
MISSILE_TOP_MAX
)) {
flag[i] = MF_HIT;
sloppy_unput(*this, i);
if(cur_left[i].to_pixel() < MISSILE_LEFT_MIN) {
prev_left[i].v = to_sp(MISSILE_LEFT_MIN);
} else if(cur_left[i].to_pixel() > MISSILE_LEFT_MAX) {
prev_left[i].v = to_sp(MISSILE_LEFT_MAX);
}
if(cur_top[i].to_pixel() < MISSILE_TOP_MIN) {
prev_top[i].v = to_sp(MISSILE_TOP_MIN);
} else if(cur_top[i].to_pixel() > MISSILE_TOP_MAX) {
prev_top[i].v = to_sp(PLAYFIELD_BOTTOM - (MISSILE_H / 2));
}
}
} else {
if(in_current_interlace_field(i)) {
static_cast<int8_t>(flag[i])++;
}
if(flag[i] > MF_HIT_last) {
flag[i] = MF_FREE;
sloppy_unput(*this, i);
mdrv2_se_play(7);
}
}
}
// Render
for(i = 0; i < MISSILE_COUNT; i++) {
if((flag[i] == MF_FREE) || !in_current_interlace_field(i)) {
continue;
} else if(flag[i] == MF_MOVING) {
main_ptn_id_t ptn_id;
int quarter;
/* TODO: Replace with the decompiled calls
* ptn_cel_for(i, ptn_id, quarter);
* ptn_put_quarter(
* cur_left[i].to_pixel(), cur_top[i].to_pixel(), ptn_id, quarter
* );
* once ptn_cel_for() is part of this translation unit */
_asm {
push ss;
lea ax, quarter;
push ax
push ss;
lea ax, ptn_id;
push ax
db 0x56; // PUSH SI
db 0x66, 0xFF, 0x76, 0x06; // PUSH LARGE [this]
push cs;
call near ptr ptn_cel_for;
push quarter;
push ptn_id;
}
_AX = cur_top[i].to_pixel();
asm {
push ax;
}
_AX = ((Subpixel *)(
(uint8_t __seg *)(_ES) +
(uint8_t __near *)(_BX) +
(uint16_t)&(((CMissiles __near *)0)->cur_left)
))->to_pixel();
asm {
push ax;
call far ptr ptn_put_quarter
add sp, 22
}
prev_left[i] = cur_left[i];
prev_top[i] = cur_top[i];
} else { // >= MF_HIT
ptn_put_quarter(
prev_left[i].to_pixel(),
prev_top[i].to_pixel(),
(ptn_id_base + MISSILE_HIT_IMAGE),
(flag[i] - MF_HIT)
);
}
}
// Collision detection
if(player_invincible) {
return;
}
for(i = 0; i < MISSILE_COUNT; i++) {
if(flag[i] == MF_FREE) {
continue;
}
// 46×46 pixels around the player's center point
enum {
HITBOX_OFFSET_LEFT = (-(MISSILE_W / 2)),
HITBOX_OFFSET_RIGHT = ((PLAYER_W / 2) + (MISSILE_W / 2)),
HITBOX_OFFSET_TOP = (-(MISSILE_H / 2)),
HITBOX_OFFSET_BOTTOM = (PLAYER_H)
};
if(
(cur_left[i].to_pixel() > (player_left + HITBOX_OFFSET_LEFT)) &&
(cur_left[i].to_pixel() < (player_left + HITBOX_OFFSET_RIGHT)) &&
(cur_top[i].to_pixel() < (player_top + HITBOX_OFFSET_BOTTOM)) &&
(cur_top[i].to_pixel() > (player_top + HITBOX_OFFSET_TOP))
) {
done = true;
return;
}
}
}