feat: add temporary hazardous barriers in hard mode
This commit is contained in:
1 parent
08a4b41280
commit
9a3a0bd9fd
8 files changed
+466
-17
No files matched your search
+85
-6
@@ -155,7 +155,8 @@ void draw_apples_cursed(const GameState &state, const RenderAssets &assets,
|
||||
void invalidate_hud(RenderAssets &assets) { assets.hudCache.dirty = true; }
|
||||
|
||||
void draw_game(const GameState &state, const RenderAssets &assets) {
|
||||
uint16_t oamId = 0;
|
||||
// OAM slots below BARRIER_OAM_SLOTS are owned by draw_barriers() (VBlank)
|
||||
uint16_t oamId = BARRIER_OAM_SLOTS;
|
||||
|
||||
if (!state.gameOver) {
|
||||
draw_apples(state, assets, oamId);
|
||||
@@ -180,6 +181,79 @@ void draw_game(const GameState &state, const RenderAssets &assets) {
|
||||
iprintf("\x1b[11;12HGAME OVER!");
|
||||
}
|
||||
|
||||
void draw_barriers(const GameState &state, const RenderAssets &assets,
|
||||
uint8_t frame) {
|
||||
if (state.scene != Scene::Playing || state.gameOver || !state.hardMode) {
|
||||
oamClear(&oamSub, 0, BARRIER_OAM_SLOTS);
|
||||
return;
|
||||
}
|
||||
|
||||
// Most imminent warning drives the shared warning colour intensity
|
||||
uint16_t min_warn_frames = BARRIER_WARN_FRAMES;
|
||||
|
||||
for (size_t b = 0; b < state.barriers.size(); b++) {
|
||||
const Barrier &barrier = state.barriers.at(b);
|
||||
const int base_slot = static_cast<int>(b) * BARRIER_MAX_LENGTH;
|
||||
|
||||
if (barrier.phase == BarrierPhase::Inactive) {
|
||||
oamClear(&oamSub, base_slot, BARRIER_MAX_LENGTH);
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint16_t frames_left = barrier.framesLeft;
|
||||
bool visible = true;
|
||||
u16 *gfx = assets.gfx_barrier;
|
||||
|
||||
if (barrier.phase == BarrierPhase::Warning) {
|
||||
min_warn_frames = std::min(min_warn_frames, frames_left);
|
||||
if (frames_left < BARRIER_SOLIDIFY_FRAMES) {
|
||||
// About to become solid: rapid flicker between outline and solid look
|
||||
gfx = (((frames_left >> 1U) & 1U) != 0U) ? assets.gfx_barrier
|
||||
: assets.gfx_barrier_warn;
|
||||
} else {
|
||||
// Blink period shrinks from 24 to 4 frames as activation approaches
|
||||
const int elapsed = BARRIER_WARN_FRAMES - frames_left;
|
||||
const int span = BARRIER_WARN_FRAMES - BARRIER_SOLIDIFY_FRAMES;
|
||||
const int period = std::max(4, 24 - ((elapsed * 20) / span));
|
||||
visible = (elapsed % period) < (period / 2);
|
||||
gfx = assets.gfx_barrier_warn;
|
||||
}
|
||||
} else if (frames_left < BARRIER_FADE_FRAMES) {
|
||||
// Still fatal, but slowly blinking to signal it's about to vanish
|
||||
visible = ((frames_left >> 3U) & 1U) == 0U;
|
||||
}
|
||||
|
||||
for (int i = 0; i < BARRIER_MAX_LENGTH; i++) {
|
||||
const int slot = base_slot + i;
|
||||
if (i >= barrier.length) {
|
||||
oamClear(&oamSub, slot, 1);
|
||||
continue;
|
||||
}
|
||||
const int cx = barrier.x + (barrier.vertical ? 0 : i);
|
||||
const int cy = barrier.y + (barrier.vertical ? i : 0);
|
||||
oamSet(&oamSub, slot, cx * 8, cy * 8, 0, 0, SpriteSize_8x8,
|
||||
SpriteColorFormat_256Color, gfx, -1, false, !visible, false, false,
|
||||
false);
|
||||
}
|
||||
}
|
||||
|
||||
// Palette animation (shared by all barriers)
|
||||
const uint8_t pulse = (frame >> 1U) & 7U;
|
||||
const int wave = (pulse < 4U) ? pulse : (7 - pulse); // 0..3..0
|
||||
SPRITE_PALETTE_SUB[BARRIER_PAL_EDGE] = static_cast<uint16_t>(RGB15(12, 0, 3));
|
||||
SPRITE_PALETTE_SUB[BARRIER_PAL_FILL] =
|
||||
static_cast<uint16_t>(RGB15(22 + (wave * 3), 2 + wave, 4));
|
||||
SPRITE_PALETTE_SUB[BARRIER_PAL_STRIPE] =
|
||||
static_cast<uint16_t>(RGB15(31, 22 + (wave * 3), 6 + (wave * 4)));
|
||||
|
||||
// Warning colour ramps from dim orange to hot yellow-white
|
||||
const int urgency =
|
||||
31 - ((static_cast<int>(min_warn_frames) * 31) / BARRIER_WARN_FRAMES);
|
||||
SPRITE_PALETTE_SUB[BARRIER_PAL_WARN] = static_cast<uint16_t>(
|
||||
RGB15(18 + ((urgency * 13) / 31), 4 + ((urgency * 22) / 31),
|
||||
2 + ((urgency * 10) / 31)));
|
||||
}
|
||||
|
||||
void draw_stats(const GameState &state, RenderAssets &assets, bool internals,
|
||||
bool musicEnabled, uint8_t frame) {
|
||||
if (internals) {
|
||||
@@ -196,7 +270,12 @@ void draw_stats(const GameState &state, RenderAssets &assets, bool internals,
|
||||
iprintf("\x1b[7;0HSpeed: %u.%01u Hz (%u%%) ",
|
||||
static_cast<unsigned int>(freq_mhz / 1000U),
|
||||
static_cast<unsigned int>((freq_mhz % 1000U) / 100U),
|
||||
static_cast<unsigned int>((static_cast<uint32_t>(intensity) * 100U) / 31U));
|
||||
static_cast<unsigned int>(
|
||||
(static_cast<uint32_t>(intensity) * 100U) / 31U));
|
||||
if (state.hardMode) {
|
||||
iprintf("\x1b[8;0HBarriers: max len %u ",
|
||||
static_cast<unsigned int>(calculate_barrier_max_length(state)));
|
||||
}
|
||||
}
|
||||
|
||||
HudCache &cache = assets.hudCache;
|
||||
@@ -274,14 +353,14 @@ void draw_menu(const GameState &state, RenderAssets &assets,
|
||||
iprintf("\x1b[3;10HSNAKE PLUS");
|
||||
iprintf("\x1b[5;5H---------------------");
|
||||
|
||||
const char *hardModeLabel = state.hardMode ? "ON " : "OFF";
|
||||
const char *musicLabel = musicEnabled ? "ON " : "OFF";
|
||||
const char *hard_mode_label = state.hardMode ? "ON " : "OFF";
|
||||
const char *music_label = musicEnabled ? "ON " : "OFF";
|
||||
|
||||
iprintf("\x1b[8;7H%c Start Game", state.menuSelection == 0 ? '>' : ' ');
|
||||
iprintf("\x1b[10;7H%c Hard Mode: %s", state.menuSelection == 1 ? '>' : ' ',
|
||||
hardModeLabel);
|
||||
hard_mode_label);
|
||||
iprintf("\x1b[12;7H%c Music: %s", state.menuSelection == 2 ? '>' : ' ',
|
||||
musicLabel);
|
||||
music_label);
|
||||
|
||||
iprintf("\x1b[19;3HUP/DOWN: Navigate");
|
||||
iprintf("\x1b[20;3HA/START: Select");
|
||||
|
||||
Reference in new issue
Block a user