#define MAP_I 328 #define MAP_TILE TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I) #define MAP_PLAYER_TILE TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 1) #define MAP_ENEMY_TILE TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 2) #define MAP_BOSS_TILE TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 9) #define MAP_TREASURE_TILE TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 3) #define MAP_BORDER_X_TILE TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 4) 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){ for(u8 i = 0; i < strlen(str); i++){ if(str[i] >= 48){ VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, (shadow ? 32 : 0) + FONT_BIG_I + str[i] - 48), x + i, y); VDP_setTileMapXY(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, (shadow ? 32 : 0) + FONT_BIG_I + 16 + str[i] - 48), x + i, y + 1); } } } char scoreStr[SCORE_LENGTH]; u32 lastScore; s16 scoreLength; #define SCORE_X 1 #define SCORE_Y 5 #define LIFE_I (FONT_BIG_I + 64) #define LIVES_X 38 #define LIVES_Y 5 s16 lastLives; static void drawLives(){ VDP_clearTileMapRect(BG_A, LIVES_X, LIVES_Y, 1, 16); for(u8 i = 0; i < (player.lives - 1); i++) VDP_fillTileMapRectInc(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, LIFE_I + (i > 0 ? 2 : 0)), LIVES_X, LIVES_Y + i, 1, 2); lastLives = player.lives; } // previous map positions: -1 means not drawn s16 mapEnemyCol[ENEMY_COUNT], mapEnemyRow[ENEMY_COUNT]; s16 mapTreasureCol[TREASURE_COUNT], mapTreasureRow[TREASURE_COUNT]; s16 mapPlayerRow; static void drawScore(){ if(lastScore < 10) scoreLength = 1; else if(lastScore < 100) scoreLength = 2; else if(lastScore < 1000) scoreLength = 3; else if(lastScore < 10000) scoreLength = 4; else if(lastScore < 100000) scoreLength = 5; else if(lastScore < 1000000) scoreLength = 6; else if(lastScore < 10000000) scoreLength = 7; else scoreLength = 8; uintToStr(lastScore, scoreStr, scoreLength); bigText(scoreStr, SCORE_X, SCORE_Y, FALSE); } // load map when stage does so we have all enemies + player void loadMap(){ VDP_fillTileMapRect(BG_A, MAP_TILE, MAP_X, MAP_Y, MAP_W, MAP_H); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 4), MAP_X, MAP_Y - 1, MAP_W, 1); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 5), MAP_X, MAP_Y + MAP_H, MAP_W, 1); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 6), MAP_X - 1, MAP_Y, 1, MAP_H); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 1, MAP_I + 6), MAP_X + MAP_W, MAP_Y, 1, MAP_H); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 7), MAP_X - 1, MAP_Y + MAP_H, 1, 1); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 1, MAP_I + 7), MAP_X + MAP_W, MAP_Y + MAP_H, 1, 1); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 8), MAP_X - 1, MAP_Y - 1, 1, 1); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 1, MAP_I + 8), MAP_X + MAP_W, MAP_Y - 1, 1, 1); for(s16 i = 0; i < ENEMY_COUNT; i++){ mapEnemyCol[i] = -1; mapEnemyRow[i] = -1; } for(s16 i = 0; i < TREASURE_COUNT; i++){ mapTreasureCol[i] = -1; mapTreasureRow[i] = -1; } mapPlayerRow = -1; } // temp arrays for new positions s16 mapNewCol[ENEMY_COUNT], mapNewRow[ENEMY_COUNT]; s16 mapNewTreasureCol[TREASURE_COUNT], mapNewTreasureRow[TREASURE_COUNT]; static bool mapTileOccupied(s16 col, s16 row, s16 pRow){ // player always at center column if(col == MAP_W / 2 && row == pRow) return TRUE; for(s16 i = 0; i < ENEMY_COUNT; i++) if(mapNewCol[i] == col && mapNewRow[i] == row) return TRUE; for(s16 i = 0; i < TREASURE_COUNT; i++) if(mapNewTreasureCol[i] == col && mapNewTreasureRow[i] == row) return TRUE; return FALSE; } static void updateMap(){ // compute new player row s16 pRow = F32_toInt(player.pos.y) / 75; if(pRow < 0) pRow = 0; if(pRow >= MAP_H) pRow = MAP_H - 1; // compute new enemy positions for(s16 i = 0; i < ENEMY_COUNT; i++){ if(!enemies[i].active || enemies[i].image == NULL || enemies[i].pos.y < FIX32(0) || enemies[i].pos.y > GAME_H_F){ mapNewCol[i] = -1; mapNewRow[i] = -1; continue; } fix32 dx = getWrappedDelta(enemies[i].pos.x, player.pos.x); s16 col = F32_toInt(dx) / MAP_SCALE + MAP_W / 2; if(col < 0) col = 0; if(col >= MAP_W) col = MAP_W - 1; s16 row = F32_toInt(enemies[i].pos.y) / 75; if(row < 0) row = 0; if(row >= MAP_H) row = MAP_H - 1; mapNewCol[i] = col; mapNewRow[i] = row; } // compute new treasure positions for(s16 i = 0; i < TREASURE_COUNT; i++){ if(!treasures[i].active || treasures[i].image == NULL || treasures[i].state == TREASURE_COLLECTED){ mapNewTreasureCol[i] = -1; mapNewTreasureRow[i] = -1; continue; } fix32 dx = getWrappedDelta(treasures[i].pos.x, player.pos.x); s16 col = F32_toInt(dx) / MAP_SCALE + MAP_W / 2; if(col < 0) col = 0; if(col >= MAP_W) col = MAP_W - 1; s16 row = F32_toInt(treasures[i].pos.y) / 75; if(row < 0) row = 0; if(row >= MAP_H) row = MAP_H - 1; mapNewTreasureCol[i] = col; mapNewTreasureRow[i] = row; } // clear old player tile if it moved and nothing new occupies it if(mapPlayerRow >= 0 && mapPlayerRow != pRow) if(!mapTileOccupied(MAP_W / 2, mapPlayerRow, pRow)) VDP_setTileMapXY(BG_A, MAP_TILE, MAP_X + MAP_W / 2, MAP_Y + mapPlayerRow); // clear old enemy tiles that moved or disappeared for(s16 i = 0; i < ENEMY_COUNT; i++){ if(mapEnemyCol[i] < 0) continue; if(mapEnemyCol[i] == mapNewCol[i] && mapEnemyRow[i] == mapNewRow[i]) continue; if(!mapTileOccupied(mapEnemyCol[i], mapEnemyRow[i], pRow)) VDP_setTileMapXY(BG_A, MAP_TILE, MAP_X + mapEnemyCol[i], MAP_Y + mapEnemyRow[i]); } // clear old treasure tiles that moved or disappeared for(s16 i = 0; i < TREASURE_COUNT; i++){ if(mapTreasureCol[i] < 0) continue; if(mapTreasureCol[i] == mapNewTreasureCol[i] && mapTreasureRow[i] == mapNewTreasureRow[i]) continue; if(!mapTileOccupied(mapTreasureCol[i], mapTreasureRow[i], pRow)) VDP_setTileMapXY(BG_A, MAP_TILE, MAP_X + mapTreasureCol[i], MAP_Y + mapTreasureRow[i]); } // draw treasure dots (skip if player occupies same tile) for(s16 i = 0; i < TREASURE_COUNT; i++){ mapTreasureCol[i] = mapNewTreasureCol[i]; mapTreasureRow[i] = mapNewTreasureRow[i]; if(mapNewTreasureCol[i] < 0) continue; if(mapNewTreasureCol[i] == MAP_W / 2 && mapNewTreasureRow[i] == pRow) continue; VDP_setTileMapXY(BG_A, MAP_TREASURE_TILE, MAP_X + mapNewTreasureCol[i], MAP_Y + mapNewTreasureRow[i]); } // draw enemy dots (skip if player occupies same tile) for(s16 i = 0; i < ENEMY_COUNT; i++){ mapEnemyCol[i] = mapNewCol[i]; mapEnemyRow[i] = mapNewRow[i]; if(mapNewCol[i] < 0) continue; if(mapNewCol[i] == MAP_W / 2 && mapNewRow[i] == pRow) continue; u16 eTile = (enemies[i].type == ENEMY_TYPE_BOSS) ? MAP_BOSS_TILE : MAP_ENEMY_TILE; VDP_setTileMapXY(BG_A, eTile, MAP_X + mapNewCol[i], MAP_Y + mapNewRow[i]); } // draw player dot on top mapPlayerRow = pRow; 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; static void drawLevel(){ if(isAttract) return; char lvlStr[4]; uintToStr(level + 1, lvlStr, 1); VDP_setTextPalette(hudPal); // VDP_drawText(lvlStr, 1, 7); VDP_setTextPalette(PAL0); lastLevel = level; } static void repaintMap(){ VDP_fillTileMapRect(BG_A, MAP_TILE, MAP_X, MAP_Y, MAP_W, MAP_H); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 4), MAP_X, MAP_Y - 1, MAP_W, 1); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 5), MAP_X, MAP_Y + MAP_H, MAP_W, 1); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 6), MAP_X - 1, MAP_Y, 1, MAP_H); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 1, MAP_I + 6), MAP_X + MAP_W, MAP_Y, 1, MAP_H); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 7), MAP_X - 1, MAP_Y + MAP_H, 1, 1); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 1, MAP_I + 7), MAP_X + MAP_W, MAP_Y + MAP_H, 1, 1); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 0, MAP_I + 8), MAP_X - 1, MAP_Y - 1, 1, 1); VDP_fillTileMapRect(BG_A, TILE_ATTR_FULL(hudPal, 1, 0, 1, MAP_I + 8), MAP_X + MAP_W, MAP_Y - 1, 1, 1); // redraw tracked dots for(s16 i = 0; i < TREASURE_COUNT; i++) if(mapTreasureCol[i] >= 0) VDP_setTileMapXY(BG_A, MAP_TREASURE_TILE, MAP_X + mapTreasureCol[i], MAP_Y + mapTreasureRow[i]); for(s16 i = 0; i < ENEMY_COUNT; i++) if(mapEnemyCol[i] >= 0){ u16 eTile = (enemies[i].type == ENEMY_TYPE_BOSS) ? MAP_BOSS_TILE : MAP_ENEMY_TILE; VDP_setTileMapXY(BG_A, eTile, MAP_X + mapEnemyCol[i], MAP_Y + mapEnemyRow[i]); } if(mapPlayerRow >= 0) 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(){ VDP_loadTileSet(imageFontBigger.tileset, FONT_BIG_I, DMA); VDP_loadTileSet(imageFontBigShadow.tileset, FONT_BIG_I + 32, DMA); VDP_loadTileSet(imageChromeLife.tileset, LIFE_I, DMA); VDP_loadTileSet(imageChromeLife2.tileset, LIFE_I + 2, DMA); VDP_loadTileSet(mapIndicator.tileset, MAP_I, DMA); lastScore = 1; if(!isAttract) drawScore(); if(!isAttract) drawLives(); drawLevel(); } bool didGameOver; u32 gameOverClock; static bool gameOverFading; static void doGameOver(){ didGameOver = TRUE; // check and save high score if(score > highScore){ highScore = score; saveHighScore(); VDP_drawText("NEW HIGH SCORE!", 14, 15); } 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){ if(treasures[i].state == TREASURE_COLLECTED){ // spawn player bullet explosion at carried treasure position struct bulletSpawner spawner = { .x = treasures[i].pos.x, .y = treasures[i].pos.y, .anim = 0, .speed = 0, .angle = 0, .player = TRUE }; void noop(s16 j){ (void)j; } spawnBullet(spawner, noop); for(s16 j = BULLET_COUNT - 1; j >= 0; j--){ if(bullets[j].active && bullets[j].pos.x == treasures[i].pos.x && bullets[j].pos.y == treasures[i].pos.y){ killBullet(j, TRUE); break; } } } killTreasure(i); } SPR_releaseSprite(player.image); clearPickups(); // clear lives VDP_clearTileMapRect(BG_A, LIVES_X, LIVES_Y, 1, 16); // clear messages treasureCollectedClock = 0; allTreasureCollected = FALSE; hitMessageClock = 0; VDP_clearText(9, 5, 23); hudPal = PAL1; hudPal = PAL1; repaintHud(); VDP_drawText("GAME OVER", 15, 14); VDP_drawText("Press Any Button", 12, 16); } #define PAUSE_Y 15 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; repaintHud(); XGM2_pause(); VDP_drawText("PAUSED", 17, PAUSE_Y); } 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(); XGM2_resume(); VDP_clearText(17, PAUSE_Y, 6); } u32 pauseClock; static void updatePause(){ if(gameOver || isAttract || levelWaitClock > 0 || levelClearing) return; if(ctrl.start){ if(!isPausing){ isPausing = TRUE; if(!paused){ pauseClock = 0; paused = TRUE; showPause(); } else { paused = FALSE; clearPause(); } } } else { isPausing = FALSE; } if(paused){ if(pauseClock % 60 < 30) VDP_drawText("PAUSED", 17, PAUSE_Y); else VDP_clearText(17, PAUSE_Y, 6); pauseClock++; if(pauseClock >= 240) pauseClock = 0; } } #define TRANSITION_TREASURE_X 10 #define TRANSITION_TREASURE_Y 15 #define TRANSITION_LEVEL_X 12 #define TRANSITION_LEVEL_Y 13 void updateChrome(){ updatePopups(); updatePause(); if(gameOver && !didGameOver) doGameOver(); if(didGameOver){ gameOverClock++; if(!gameOverFading){ if((gameOverClock > 120 && (ctrl.a || ctrl.b || ctrl.c || ctrl.start)) || gameOverClock > 900){ gameOverFading = TRUE; PAL_fadeOut(0, 47, 20, TRUE); } } else if(!PAL_isDoingFade()){ SYS_hardReset(); } return; } // level transition overlay if(levelClearing){ if(bonusStage) return; if(levelClearClock == 2){ char numStr[12]; char lvlStr[4]; char livesStr[4]; score += 2048 + 1024 * level; lastScore = score; uintToStr(statTreasures, numStr, 1); VDP_drawText("Collected", TRANSITION_TREASURE_X, TRANSITION_TREASURE_Y); VDP_drawText(numStr, TRANSITION_TREASURE_X + 10, TRANSITION_TREASURE_Y); VDP_drawText("Treasure", TRANSITION_TREASURE_X + 10 + 2, TRANSITION_TREASURE_Y); if(statTreasures != 1) VDP_drawText("s", TRANSITION_TREASURE_X + 10 + 2 + 8, TRANSITION_TREASURE_Y); uintToStr(level + 1, lvlStr, 1); VDP_drawText("Completed Level", TRANSITION_LEVEL_X, TRANSITION_LEVEL_Y); VDP_drawText(lvlStr, TRANSITION_LEVEL_X + 16, TRANSITION_LEVEL_Y); uintToStr(lastScore, scoreStr, 1); VDP_drawText("Score", TRANSITION_LEVEL_X, TRANSITION_LEVEL_Y + 3); VDP_drawText(scoreStr, TRANSITION_LEVEL_X + 6, TRANSITION_LEVEL_Y + 3); uintToStr(player.lives, livesStr, 1); VDP_drawText(livesStr, TRANSITION_LEVEL_X, TRANSITION_LEVEL_Y + 5); if(player.lives == 1) VDP_drawText("Life Left", TRANSITION_LEVEL_X + 2, TRANSITION_LEVEL_Y + 5); 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; } if(!isAttract && lastScore != score){ lastScore = score; drawScore(); // check for extend while(score >= nextExtendScore){ player.lives++; nextExtendScore = (nextExtendScore * 5) / 2; // previous + previous * 1.5 drawLives(); } } if(!isAttract && lastLives != player.lives) drawLives(); if(lastLevel != level) drawLevel(); if(treasureCollectedClock > 0 && levelWaitClock == 0){ if(treasureCollectedClock == 120){ VDP_clearText(10, 5, 23); // check if all treasures are now collected or gone bool allDone = TRUE; for(s16 j = 0; j < TREASURE_COUNT; j++){ if(treasures[j].active && treasures[j].state != TREASURE_COLLECTED){ allDone = FALSE; break; } } 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"}; const char* lampPhrases[] = {"Strike Light", "Let There Be Lode!", "Bright Idea Deep Down", "Illuminate the Vein", "Glow from Below"}; const char* scarfPhrases[] = {"Cozy in the Caves", "Wrap the Underworld", "Snug as Bedrock", "Style from the Strata", "Warm the Depths"}; const char* swordPhrases[] = {"Ore You Ready?", "Mined Your Step", "Cutting Edge Geology", "Strike the Vein", "Spirit Steel"}; const char** sets[] = {mirrorPhrases, lampPhrases, scarfPhrases, swordPhrases}; const char* phrase = sets[treasureCollectedType][phraseIndex[treasureCollectedType]]; phraseIndex[treasureCollectedType] = (phraseIndex[treasureCollectedType] + 1) % 5; u8 len = strlen(phrase); u8 phraseX = 20 - len / 2; if(phraseX < 10) phraseX = 10; VDP_drawText(phrase, phraseX, 5); } } treasureCollectedClock--; if(treasureCollectedClock == 0) VDP_clearText(9, 5, 24); } if(hitMessageClock > 0){ if(hitMessageClock == 120){ VDP_clearText(9, 5, 23); treasureCollectedClock = 0; allTreasureCollected = FALSE; VDP_drawText(hitMessageBullet ? "Got You!" : "Collision!", 20 - (hitMessageBullet ? 8 : 10) / 2, 5); } hitMessageClock--; if(hitMessageClock == 0) VDP_clearText(9, 5, 23); } if(levelWaitClock == 210){ VDP_clearText(9, 5, 23); treasureCollectedClock = 0; 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(); }