159 lines
5.4 KiB
C
159 lines
5.4 KiB
C
#define TREASURE_OFF 16
|
|
|
|
|
|
void spawnTreasure(u8 zone){
|
|
s16 i = -1;
|
|
for(s16 j = 0; j < TREASURE_COUNT; j++) if(!treasures[j].active) { i = j; break; }
|
|
if(i == -1) return;
|
|
|
|
treasures[i].active = TRUE;
|
|
treasures[i].state = TREASURE_WALKING;
|
|
treasures[i].carriedBy = -1;
|
|
|
|
fix32 zoneStart = FIX32(zone * 512);
|
|
treasures[i].pos.x = zoneStart + FIX32(random() % 512);
|
|
treasures[i].pos.y = GAME_H_F - FIX32(24);
|
|
|
|
fix32 speeds[] = { FIX32(0.3), FIX32(0.4), FIX32(0.5) };
|
|
treasures[i].vel.x = (random() % 2 == 0) ? speeds[random() % 3] : -speeds[random() % 3];
|
|
treasures[i].vel.y = (random() % 2 == 0) ? FIX32(0.1) : FIX32(-0.1);
|
|
|
|
treasures[i].image = SPR_addSprite(&treasureSprite,
|
|
getScreenX(treasures[i].pos.x, player.camera) - TREASURE_OFF, fix32ToInt(treasures[i].pos.y) - TREASURE_OFF,
|
|
TILE_ATTR(PAL0, 0, 0, 0));
|
|
if(!treasures[i].image){
|
|
treasures[i].active = FALSE;
|
|
return;
|
|
}
|
|
SPR_setVisibility(treasures[i].image, HIDDEN);
|
|
treasures[i].type = random() % 4;
|
|
SPR_setAnim(treasures[i].image, treasures[i].type);
|
|
}
|
|
|
|
static void updateTreasure(u8 i){
|
|
switch(treasures[i].state){
|
|
case TREASURE_WALKING:
|
|
// Y bounce: bob 4px around ground level
|
|
if(treasures[i].pos.y >= GAME_H_F - FIX32(20) || treasures[i].pos.y <= GAME_H_F - FIX32(28))
|
|
treasures[i].vel.y *= -1;
|
|
|
|
// X wrap
|
|
if(treasures[i].pos.x >= GAME_WRAP)
|
|
treasures[i].pos.x -= GAME_WRAP;
|
|
if(treasures[i].pos.x < 0)
|
|
treasures[i].pos.x += GAME_WRAP;
|
|
|
|
treasures[i].pos.x += treasures[i].vel.x;
|
|
treasures[i].pos.y += treasures[i].vel.y;
|
|
break;
|
|
|
|
case TREASURE_CARRIED:
|
|
// follow carrier enemy position
|
|
if(treasures[i].carriedBy >= 0 && enemies[treasures[i].carriedBy].active){
|
|
treasures[i].pos.x = enemies[treasures[i].carriedBy].pos.x;
|
|
treasures[i].pos.y = enemies[treasures[i].carriedBy].pos.y + FIX32(16);
|
|
} else {
|
|
// carrier died (shouldn't normally reach here, killEnemy handles it)
|
|
treasures[i].state = TREASURE_FALLING;
|
|
treasures[i].carriedBy = -1;
|
|
treasures[i].vel.x = 0;
|
|
treasures[i].vel.y = FIX32(3);
|
|
treasureBeingCarried = FALSE;
|
|
}
|
|
break;
|
|
|
|
case TREASURE_FALLING:
|
|
treasures[i].pos.y += treasures[i].vel.y;
|
|
// land on ground
|
|
if(treasures[i].pos.y >= GAME_H_F - FIX32(24)){
|
|
treasures[i].pos.y = GAME_H_F - FIX32(24);
|
|
treasures[i].state = TREASURE_WALKING;
|
|
fix32 speeds[] = { FIX32(0.3), FIX32(0.4), FIX32(0.5) };
|
|
treasures[i].vel.x = (random() % 2 == 0) ? speeds[random() % 3] : -speeds[random() % 3];
|
|
treasures[i].vel.y = (random() % 2 == 0) ? FIX32(0.1) : FIX32(-0.1);
|
|
}
|
|
break;
|
|
|
|
case TREASURE_COLLECTED: {
|
|
fix32 targetX, targetY;
|
|
if(treasures[i].trailIndex == 0){
|
|
targetX = player.pos.x;
|
|
targetY = player.pos.y + FIX32(24);
|
|
} else {
|
|
// find the treasure ahead in the chain
|
|
targetX = player.pos.x;
|
|
targetY = player.pos.y + FIX32(24);
|
|
for(s16 j = 0; j < TREASURE_COUNT; j++){
|
|
if(treasures[j].active && treasures[j].state == TREASURE_COLLECTED
|
|
&& treasures[j].trailIndex == treasures[i].trailIndex - 1){
|
|
targetX = treasures[j].pos.x;
|
|
targetY = treasures[j].pos.y + FIX32(8);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
fix32 deltaX = getWrappedDelta(targetX, treasures[i].pos.x);
|
|
treasures[i].pos.x += deltaX >> 2;
|
|
treasures[i].pos.y += (targetY - treasures[i].pos.y) >> 2;
|
|
// X wrap
|
|
if(treasures[i].pos.x >= GAME_WRAP)
|
|
treasures[i].pos.x -= GAME_WRAP;
|
|
if(treasures[i].pos.x < 0)
|
|
treasures[i].pos.x += GAME_WRAP;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// collect: check overlap with player (walking or falling only)
|
|
fix32 dx = getWrappedDelta(treasures[i].pos.x, player.pos.x);
|
|
if(treasures[i].state != TREASURE_CARRIED && treasures[i].state != TREASURE_COLLECTED){
|
|
fix32 dy = treasures[i].pos.y - player.pos.y;
|
|
if(dx >= FIX32(-32) && dx <= FIX32(32) && dy >= FIX32(-32) && dy <= FIX32(32)){
|
|
score += (treasures[i].state == TREASURE_FALLING) ? 2000 : 1000;
|
|
sfxPickup();
|
|
treasureCollectedType = treasures[i].type;
|
|
treasureCollectedClock = 120;
|
|
// only add to trail if this type isn't already collected
|
|
bool duplicate = FALSE;
|
|
for(s16 j = 0; j < TREASURE_COUNT; j++){
|
|
if(treasures[j].active && treasures[j].state == TREASURE_COLLECTED
|
|
&& treasures[j].type == treasures[i].type){
|
|
duplicate = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if(duplicate){
|
|
killTreasure(i);
|
|
} else {
|
|
treasures[i].state = TREASURE_COLLECTED;
|
|
treasures[i].trailIndex = collectedCount++;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
s16 sx = getScreenX(treasures[i].pos.x, player.camera);
|
|
s16 sy = fix32ToInt(treasures[i].pos.y);
|
|
bool visible = (treasures[i].state == TREASURE_COLLECTED) || (dx >= -CULL_LIMIT && dx <= CULL_LIMIT);
|
|
if(visible && treasures[i].state == TREASURE_COLLECTED){
|
|
if(player.respawnClock > 0)
|
|
visible = FALSE;
|
|
else if(player.recoveringClock > 0)
|
|
visible = (player.recoveringClock % 20 > 10);
|
|
}
|
|
SPR_setVisibility(treasures[i].image, visible ? VISIBLE : HIDDEN);
|
|
SPR_setPosition(treasures[i].image, sx - TREASURE_OFF, sy - TREASURE_OFF);
|
|
// frame 0 = normal, frame 1 = flash, frame 2 = collected/faded
|
|
if(treasures[i].state == TREASURE_WALKING || treasures[i].state == TREASURE_FALLING)
|
|
SPR_setFrame(treasures[i].image, (clock / 15) & 1);
|
|
else if(treasures[i].state == TREASURE_COLLECTED)
|
|
SPR_setFrame(treasures[i].image, 2);
|
|
else
|
|
SPR_setFrame(treasures[i].image, 0);
|
|
}
|
|
|
|
void updateTreasures(){
|
|
for(s16 i = 0; i < TREASURE_COUNT; i++)
|
|
if(treasures[i].active)
|
|
updateTreasure(i);
|
|
}
|