180 lines
No EOL
8 KiB
C
180 lines
No EOL
8 KiB
C
#define BULLET_OFF 8
|
|
#define P_BULLET_OFF 16
|
|
|
|
static void doBulletRotation(u8 i){
|
|
if(bullets[i].anim >= FIRST_ROTATING_BULLET && !bullets[i].player){
|
|
bullets[i].vFlip = FALSE;
|
|
bullets[i].hFlip = FALSE;
|
|
|
|
if(bullets[i].angle < 0) bullets[i].angle += 1024;
|
|
else if(bullets[i].angle >= 1024) bullets[i].angle -= 1024;
|
|
|
|
|
|
// 0 - 256
|
|
if(bullets[i].angle >= 1008 || bullets[i].angle < 16) bullets[i].frame = 0;
|
|
else if(bullets[i].angle >= 16 && bullets[i].angle < 48) bullets[i].frame = 1;
|
|
else if(bullets[i].angle >= 48 && bullets[i].angle < 80) bullets[i].frame = 2;
|
|
else if(bullets[i].angle >= 80 && bullets[i].angle < 112) bullets[i].frame = 3;
|
|
else if(bullets[i].angle >= 112 && bullets[i].angle < 144) bullets[i].frame = 4;
|
|
else if(bullets[i].angle >= 112 && bullets[i].angle < 176) bullets[i].frame = 5;
|
|
else if(bullets[i].angle >= 176 && bullets[i].angle < 208) bullets[i].frame = 6;
|
|
else if(bullets[i].angle >= 208 && bullets[i].angle < 240) bullets[i].frame = 7;
|
|
else if(bullets[i].angle >= 240 && bullets[i].angle < 272) bullets[i].frame = 8;
|
|
|
|
// 256 - 512
|
|
else if(bullets[i].angle >= 272 && bullets[i].angle < 304) { bullets[i].frame = 7; bullets[i].hFlip = TRUE; }
|
|
else if(bullets[i].angle >= 304 && bullets[i].angle < 336) { bullets[i].frame = 6; bullets[i].hFlip = TRUE; }
|
|
else if(bullets[i].angle >= 336 && bullets[i].angle < 368) { bullets[i].frame = 5; bullets[i].hFlip = TRUE; }
|
|
else if(bullets[i].angle >= 368 && bullets[i].angle < 400) { bullets[i].frame = 4; bullets[i].hFlip = TRUE; }
|
|
else if(bullets[i].angle >= 400 && bullets[i].angle < 432) { bullets[i].frame = 3; bullets[i].hFlip = TRUE; }
|
|
else if(bullets[i].angle >= 432 && bullets[i].angle < 464) { bullets[i].frame = 2; bullets[i].hFlip = TRUE; }
|
|
else if(bullets[i].angle >= 464 && bullets[i].angle < 496) { bullets[i].frame = 1; bullets[i].hFlip = TRUE; }
|
|
else if(bullets[i].angle >= 496 && bullets[i].angle < 528) { bullets[i].frame = 0; bullets[i].hFlip = TRUE; }
|
|
|
|
// 512 - 768
|
|
else if(bullets[i].angle >= 528 && bullets[i].angle < 560) { bullets[i].frame = 1; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 560 && bullets[i].angle < 592) { bullets[i].frame = 2; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 592 && bullets[i].angle < 624) { bullets[i].frame = 3; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 624 && bullets[i].angle < 656) { bullets[i].frame = 4; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 656 && bullets[i].angle < 688) { bullets[i].frame = 5; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 688 && bullets[i].angle < 720) { bullets[i].frame = 6; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 720 && bullets[i].angle < 752) { bullets[i].frame = 7; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 752 && bullets[i].angle < 784) { bullets[i].frame = 8; bullets[i].hFlip = TRUE; bullets[i].vFlip = TRUE; }
|
|
|
|
// 768 - 1024
|
|
else if(bullets[i].angle >= 784 && bullets[i].angle < 816) { bullets[i].frame = 7; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 816 && bullets[i].angle < 848) { bullets[i].frame = 6; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 848 && bullets[i].angle < 880) { bullets[i].frame = 5; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 880 && bullets[i].angle < 912) { bullets[i].frame = 4; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 912 && bullets[i].angle < 944) { bullets[i].frame = 3; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 944 && bullets[i].angle < 976) { bullets[i].frame = 2; bullets[i].vFlip = TRUE; }
|
|
else if(bullets[i].angle >= 976 && bullets[i].angle < 1008) { bullets[i].frame = 1; bullets[i].vFlip = TRUE; }
|
|
|
|
SPR_setFrame(bullets[i].image, bullets[i].frame);
|
|
SPR_setHFlip(bullets[i].image, bullets[i].hFlip);
|
|
SPR_setVFlip(bullets[i].image, bullets[i].vFlip);
|
|
}
|
|
}
|
|
|
|
void spawnBullet(struct bulletSpawner spawner, void(*updater)){
|
|
s16 i = -1;
|
|
for(s16 j = 0; j < BULLET_COUNT; j++) if(!bullets[j].active && i == -1) i = j;
|
|
if(i > -1){
|
|
bullets[i].active = TRUE;
|
|
bullets[i].pos.x = spawner.x;
|
|
bullets[i].pos.y = spawner.y;
|
|
bullets[i].angle = spawner.angle;
|
|
bullets[i].player = spawner.player;
|
|
bullets[i].clock = 0;
|
|
if(spawner.vel.x || spawner.vel.y){
|
|
bullets[i].vel.x = spawner.vel.x;
|
|
bullets[i].vel.y = spawner.vel.y;
|
|
} else {
|
|
bullets[i].vel.x = fix32Mul(fix16ToFix32(cosFix16(spawner.angle)), spawner.speed);
|
|
bullets[i].vel.y = fix32Mul(fix16ToFix32(sinFix16(spawner.angle)), spawner.speed);
|
|
}
|
|
bullets[i].updater = updater;
|
|
bullets[i].dist = bullets[i].player ? 16 : (spawner.anim == 0 ? 4 : 7);
|
|
bullets[i].image = SPR_addSprite(spawner.player ? &pBulletSprite : &bulletsSprite,
|
|
getScreenX(bullets[i].pos.x, player.camera) - (spawner.player ? P_BULLET_OFF : BULLET_OFF),
|
|
fix32ToInt(bullets[i].pos.y) - (spawner.player ? P_BULLET_OFF : BULLET_OFF),
|
|
TILE_ATTR(gameOver ? PAL1 : PAL0, 0, 0, spawner.player && spawner.angle == 512 ? 1 : 0));
|
|
if(spawner.anim) SPR_setAnim(bullets[i].image, spawner.anim);
|
|
bullets[i].anim = spawner.anim;
|
|
bullets[i].frame = spawner.frame;
|
|
SPR_setFrame(bullets[i].image, spawner.frame);
|
|
SPR_setAnim(bullets[i].image, spawner.anim);
|
|
SPR_setDepth(bullets[i].image, spawner.player ? 7 : (spawner.top ? 3 : 4));
|
|
doBulletRotation(i);
|
|
}
|
|
}
|
|
|
|
s32 bulletDist;
|
|
#define BULLET_CHECK FIX32(32)
|
|
static void collideWithEnemy(u8 i){
|
|
for(s16 j = 0; j < ENEMY_COUNT; j++) {
|
|
if(enemies[j].active){
|
|
// Calculate wrapped distances
|
|
fix32 deltaX = getWrappedDelta(bullets[i].pos.x, enemies[j].pos.x);
|
|
fix32 deltaY = bullets[i].pos.y - enemies[j].pos.y;
|
|
|
|
// Quick bounding box check using wrapped distance
|
|
if(deltaY >= -BULLET_CHECK && deltaY <= BULLET_CHECK &&
|
|
deltaX >= -BULLET_CHECK && deltaX <= BULLET_CHECK){
|
|
// Precise distance check
|
|
bulletDist = getApproximatedDistance(
|
|
fix32ToInt(deltaX),
|
|
fix32ToInt(deltaY));
|
|
if(bulletDist <= bullets[i].dist){
|
|
killBullet(i);
|
|
killEnemy(j);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void collideWithPlayer(u8 i){
|
|
if(!bullets[i].player && bullets[i].active){
|
|
fix32 deltaX = getWrappedDelta(bullets[i].pos.x, player.pos.x);
|
|
fix32 deltaY = bullets[i].pos.y - player.pos.y;
|
|
|
|
s32 dist = getApproximatedDistance(
|
|
fix32ToInt(deltaX),
|
|
fix32ToInt(deltaY));
|
|
if(dist <= 4){ // Player hit radius
|
|
killBullet(i);
|
|
// player.lives--;
|
|
// if(player.lives <= 0) gameOver = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void updateBullet(u8 i){
|
|
bullets[i].pos.x += bullets[i].vel.x;
|
|
bullets[i].pos.y += bullets[i].vel.y;
|
|
|
|
// Wrap bullet position
|
|
if(bullets[i].pos.x >= GAME_WRAP){
|
|
bullets[i].pos.x -= GAME_WRAP;
|
|
}
|
|
if(bullets[i].pos.x < 0){
|
|
bullets[i].pos.x += GAME_WRAP;
|
|
}
|
|
|
|
// Cull bullets that go off-screen
|
|
// Horizontal: use wrapped distance to handle world wrap correctly
|
|
fix32 dx = getWrappedDelta(bullets[i].pos.x, player.pos.x);
|
|
bool offScreenX = (dx < FIX32(-256) || dx > FIX32(256));
|
|
|
|
// Vertical: simple linear check (no Y wrapping)
|
|
bool offScreenY = (bullets[i].pos.y < FIX32(-16) || bullets[i].pos.y > GAME_H_F + FIX32(16));
|
|
|
|
if(offScreenX || offScreenY){
|
|
killBullet(i);
|
|
return; // Skip rest of update for culled bullet
|
|
}
|
|
|
|
if(bullets[i].clock > 0) bullets[i].updater(i);
|
|
if(bullets[i].player) collideWithEnemy(i);
|
|
else collideWithPlayer(i);
|
|
if(bullets[i].active){
|
|
s16 sx = getScreenX(bullets[i].pos.x, player.camera);
|
|
s16 sy = fix32ToInt(bullets[i].pos.y);
|
|
u8 off = bullets[i].player ? P_BULLET_OFF : BULLET_OFF;
|
|
|
|
// Set visibility to prevent VDP 512px wrap ghosting
|
|
// bool onScreen = (sx >= VISIBLE_X_MIN && sx <= VISIBLE_X_MAX &&
|
|
// sy >= VISIBLE_Y_MIN && sy <= VISIBLE_Y_MAX);
|
|
// SPR_setVisibility(bullets[i].image, onScreen ? VISIBLE : HIDDEN);
|
|
|
|
SPR_setPosition(bullets[i].image, sx - off, sy - off);
|
|
bullets[i].clock++;
|
|
}
|
|
}
|
|
|
|
|
|
void updateBullets(){
|
|
for(s16 i = 0; i < BULLET_COUNT; i++) if(bullets[i].active)
|
|
updateBullet(i);
|
|
} |