pickups, native build, enemy/bullet/stage overhaul
- Add pickup system (bomb, spread, rapid, shield) with new sprites - Replace Docker build with native SGDK compile via m68k-elf-gcc - Rework enemy spawning, homing math, boss HP/number globals - Expand chrome: score popups, minimap, pause/game over improvements - Overhaul stage generation with threat-point system - Add explosion sprites, shield sprite, powerup sprite - Add tools/ for sprite downscaling utilities
This commit is contained in:
parent
3263b2597b
commit
073f96c9b1
25 changed files with 2320 additions and 1186 deletions
132
src/chrome.h
132
src/chrome.h
|
|
@ -8,6 +8,66 @@
|
|||
|
||||
u16 hudPal = PAL0;
|
||||
|
||||
#define POPUP_COUNT 4
|
||||
|
||||
struct scorePopup {
|
||||
bool active;
|
||||
u8 clock;
|
||||
u8 len;
|
||||
s16 tileX, tileY;
|
||||
char text[6];
|
||||
};
|
||||
struct scorePopup popups[POPUP_COUNT];
|
||||
|
||||
void spawnPopup(fix32 worldX, fix32 worldY, u32 value){
|
||||
s16 slot = -1;
|
||||
for(s16 i = 0; i < POPUP_COUNT; i++) if(!popups[i].active){ slot = i; break; }
|
||||
if(slot == -1) return;
|
||||
s16 screenX = getScreenX(worldX, player.camera);
|
||||
s16 screenY = F32_toInt(worldY);
|
||||
s16 tX = screenX / 8;
|
||||
s16 tY = screenY / 8;
|
||||
tX--;
|
||||
if(tX < 0) tX = 0;
|
||||
if(tX > 38) tX = 38;
|
||||
if(tY < 6) tY = 6;
|
||||
if(tY > 25) tY = 25;
|
||||
popups[slot].tileX = tX;
|
||||
popups[slot].tileY = tY;
|
||||
uintToStr(value, popups[slot].text, 1);
|
||||
popups[slot].len = strlen(popups[slot].text);
|
||||
popups[slot].clock = 0;
|
||||
popups[slot].active = TRUE;
|
||||
bigText(popups[slot].text, tX, tY, TRUE);
|
||||
}
|
||||
|
||||
static void updatePopups(){
|
||||
for(s16 i = 0; i < POPUP_COUNT; i++){
|
||||
if(!popups[i].active) continue;
|
||||
popups[i].clock++;
|
||||
if(popups[i].clock >= 24){
|
||||
VDP_clearTileMapRect(BG_A, popups[i].tileX, popups[i].tileY, popups[i].len, 2);
|
||||
popups[i].active = FALSE;
|
||||
continue;
|
||||
}
|
||||
if(popups[i].clock % 8 == 0){
|
||||
VDP_clearTileMapRect(BG_A, popups[i].tileX, popups[i].tileY, popups[i].len, 2);
|
||||
popups[i].tileY--;
|
||||
if(popups[i].tileY < 6) popups[i].tileY = 6;
|
||||
bigText(popups[i].text, popups[i].tileX, popups[i].tileY, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void clearPopups(){
|
||||
for(s16 i = 0; i < POPUP_COUNT; i++){
|
||||
if(popups[i].active){
|
||||
VDP_clearTileMapRect(BG_A, popups[i].tileX, popups[i].tileY, popups[i].len, 2);
|
||||
popups[i].active = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define FONT_BIG_I 340
|
||||
|
||||
void bigText(char* str, u16 x, u16 y, bool shadow){
|
||||
|
|
@ -185,6 +245,10 @@ static void updateMap(){
|
|||
VDP_setTileMapXY(BG_A, MAP_PLAYER_TILE, MAP_X + MAP_W / 2, MAP_Y + pRow);
|
||||
}
|
||||
|
||||
// pickup HUD tracking
|
||||
s16 lastBombCount = -1;
|
||||
s16 lastPowerupState = -1; // 0=none, 1=spread, 2=rapid, 3=shield (composite)
|
||||
|
||||
u8 phraseIndex[4];
|
||||
|
||||
s16 lastLevel;
|
||||
|
|
@ -221,11 +285,42 @@ static void repaintMap(){
|
|||
VDP_setTileMapXY(BG_A, MAP_PLAYER_TILE, MAP_X + MAP_W / 2, MAP_Y + mapPlayerRow);
|
||||
}
|
||||
|
||||
static void drawBombCount(){
|
||||
if(isAttract) return;
|
||||
if(player.bombCount > 0){
|
||||
char bStr[4] = "B:";
|
||||
char numStr[2];
|
||||
uintToStr(player.bombCount, numStr, 1);
|
||||
bStr[2] = numStr[0];
|
||||
bStr[3] = 0;
|
||||
VDP_drawText(bStr, 1, 7);
|
||||
} else {
|
||||
VDP_clearText(1, 7, 3);
|
||||
}
|
||||
lastBombCount = player.bombCount;
|
||||
}
|
||||
|
||||
static void drawPowerupIndicator(){
|
||||
if(isAttract) return;
|
||||
VDP_clearText(1, 8, 6);
|
||||
if(player.hasShield)
|
||||
VDP_drawText("SH", 1, 8);
|
||||
else if(player.activePowerup == 1)
|
||||
VDP_drawText("SPREAD", 1, 8);
|
||||
else if(player.activePowerup == 2)
|
||||
VDP_drawText("RAPID", 1, 8);
|
||||
s16 state = player.activePowerup;
|
||||
if(player.hasShield) state = 3;
|
||||
lastPowerupState = state;
|
||||
}
|
||||
|
||||
static void repaintHud(){
|
||||
bigText(scoreStr, SCORE_X, SCORE_Y, FALSE);
|
||||
drawLives();
|
||||
repaintMap();
|
||||
drawLevel();
|
||||
drawBombCount();
|
||||
drawPowerupIndicator();
|
||||
}
|
||||
|
||||
void loadChrome(){
|
||||
|
|
@ -263,7 +358,7 @@ static void doGameOver(){
|
|||
void noop(s16 j){ (void)j; }
|
||||
spawnBullet(spawner, noop);
|
||||
for(s16 j = BULLET_COUNT - 1; j >= 0; j--){
|
||||
if(bullets[j].active && !bullets[j].explosion
|
||||
if(bullets[j].active
|
||||
&& bullets[j].pos.x == treasures[i].pos.x && bullets[j].pos.y == treasures[i].pos.y){
|
||||
killBullet(j, TRUE);
|
||||
break;
|
||||
|
|
@ -273,6 +368,7 @@ static void doGameOver(){
|
|||
killTreasure(i);
|
||||
}
|
||||
SPR_releaseSprite(player.image);
|
||||
clearPickups();
|
||||
|
||||
// clear lives
|
||||
VDP_clearTileMapRect(BG_A, LIVES_X, LIVES_Y, 1, 16);
|
||||
|
|
@ -297,6 +393,7 @@ static void showPause(){
|
|||
for(s16 i = 0; i < BULLET_COUNT; i++) if(bullets[i].active) SPR_setPalette(bullets[i].image, PAL1);
|
||||
for(s16 i = 0; i < ENEMY_COUNT; i++) if(enemies[i].active) SPR_setPalette(enemies[i].image, PAL1);
|
||||
for(s16 i = 0; i < TREASURE_COUNT; i++) if(treasures[i].active) SPR_setPalette(treasures[i].image, PAL1);
|
||||
for(s16 i = 0; i < PICKUP_COUNT; i++) if(pickups[i].active) SPR_setPalette(pickups[i].image, PAL1);
|
||||
SPR_setPalette(player.image, PAL1);
|
||||
hudPal = PAL1;
|
||||
hudPal = PAL1;
|
||||
|
|
@ -309,6 +406,7 @@ static void clearPause(){
|
|||
for(s16 i = 0; i < BULLET_COUNT; i++) if(bullets[i].active) SPR_setPalette(bullets[i].image, PAL0);
|
||||
for(s16 i = 0; i < ENEMY_COUNT; i++) if(enemies[i].active) SPR_setPalette(enemies[i].image, PAL0);
|
||||
for(s16 i = 0; i < TREASURE_COUNT; i++) if(treasures[i].active) SPR_setPalette(treasures[i].image, PAL0);
|
||||
for(s16 i = 0; i < PICKUP_COUNT; i++) if(pickups[i].active) SPR_setPalette(pickups[i].image, PAL0);
|
||||
SPR_setPalette(player.image, PAL0);
|
||||
hudPal = PAL0;
|
||||
repaintHud();
|
||||
|
|
@ -345,12 +443,13 @@ static void updatePause(){
|
|||
}
|
||||
|
||||
#define TRANSITION_TREASURE_X 10
|
||||
#define TRANSITION_TREASURE_Y 13
|
||||
#define TRANSITION_TREASURE_Y 15
|
||||
|
||||
#define TRANSITION_LEVEL_X 12
|
||||
#define TRANSITION_LEVEL_Y 15
|
||||
#define TRANSITION_LEVEL_Y 13
|
||||
|
||||
void updateChrome(){
|
||||
updatePopups();
|
||||
updatePause();
|
||||
if(gameOver && !didGameOver) doGameOver();
|
||||
if(didGameOver){
|
||||
|
|
@ -396,12 +495,31 @@ void updateChrome(){
|
|||
else
|
||||
VDP_drawText("Lives Left", TRANSITION_LEVEL_X + 2, TRANSITION_LEVEL_Y + 5);
|
||||
|
||||
if(grazeCount > 0){
|
||||
char grazeStr[8];
|
||||
char grazePtsStr[12];
|
||||
uintToStr(grazeCount, grazeStr, 1);
|
||||
uintToStr(grazeCount * 64, grazePtsStr, 1);
|
||||
VDP_drawText("Grazes", TRANSITION_LEVEL_X, TRANSITION_LEVEL_Y + 7);
|
||||
VDP_drawText(grazeStr, TRANSITION_LEVEL_X + 7, TRANSITION_LEVEL_Y + 7);
|
||||
VDP_drawText(grazePtsStr, TRANSITION_LEVEL_X + 7 + strlen(grazeStr) + 1, TRANSITION_LEVEL_Y + 7);
|
||||
VDP_drawText("pts", TRANSITION_LEVEL_X + 7 + strlen(grazeStr) + 1 + strlen(grazePtsStr) + 1, TRANSITION_LEVEL_Y + 7);
|
||||
}
|
||||
|
||||
if(levelPerfect){
|
||||
score += 4096;
|
||||
lastScore = score;
|
||||
VDP_drawText("PERFECT! +4096", 13, TRANSITION_LEVEL_Y + 9);
|
||||
}
|
||||
|
||||
}
|
||||
if(levelClearClock >= 230){
|
||||
VDP_clearText(0, TRANSITION_TREASURE_Y, 40);
|
||||
VDP_clearText(0, TRANSITION_LEVEL_Y, 40);
|
||||
VDP_clearText(0, TRANSITION_LEVEL_Y + 3, 40);
|
||||
VDP_clearText(0, TRANSITION_LEVEL_Y + 5, 40);
|
||||
VDP_clearText(0, TRANSITION_LEVEL_Y + 7, 40);
|
||||
VDP_clearText(0, TRANSITION_LEVEL_Y + 9, 40);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -430,6 +548,7 @@ void updateChrome(){
|
|||
}
|
||||
if(allDone && collectedCount > 0){
|
||||
allTreasureCollected = TRUE;
|
||||
score += 4096;
|
||||
VDP_drawText("All Treasure Found!", 11, 5);
|
||||
} else {
|
||||
const char* mirrorPhrases[] = {"Reflect the Depths", "Dig Deeper Within", "See What Shines Below", "Mirror of the Mine", "Look Back, Strike Back"};
|
||||
|
|
@ -466,5 +585,12 @@ void updateChrome(){
|
|||
allTreasureCollected = FALSE;
|
||||
VDP_drawText("All Enemies Down!", 12, 5);
|
||||
}
|
||||
// pickup HUD
|
||||
if(!isAttract){
|
||||
if(lastBombCount != player.bombCount) drawBombCount();
|
||||
s16 curPowerup = player.activePowerup;
|
||||
if(player.hasShield) curPowerup = 3;
|
||||
if(lastPowerupState != curPowerup) drawPowerupIndicator();
|
||||
}
|
||||
if(clock % 4 == 0) updateMap();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue