#include "gameover.h" #include #include #include #include #include #include "chick.h" #include "colors.h" #include "logic.h" #include "gfx/gfx.h" #define CHICKEN_LEFT_X (LCD_WIDTH / 2 - LARGE_CHICKEN_WIDTH / 2) #define CHICKEN_RIGHT_X (LCD_WIDTH / 2 + LARGE_CHICKEN_WIDTH / 2) #define CHICKEN_TOP_Y (LCD_HEIGHT / 2 - LARGE_CHICKEN_HEIGHT / 2) #define CHICKEN_BOTTOM_Y (LCD_HEIGHT / 2 + LARGE_CHICKEN_HEIGHT / 2) enum { ENTER, CLEAR, IN_PROGRESS }; // Stuff that's common to all animations - e.g. the score number, the getkey check, the swap // call after drawing everything uint8_t common(int24_t score) { // Linker (???) gets upset if this is later in the function (???) static uint8_t blinkiness = 0; if(++blinkiness == 60) blinkiness = 0; if(kb_IsDown(kb_KeyEnter)) return ENTER; if(kb_IsDown(kb_Key2nd)) return ENTER; if(kb_IsDown(kb_KeyClear)) return CLEAR; if(kb_IsDown(kb_KeyDel)) return CLEAR; if(kb_IsDown(kb_KeyMode)) return CLEAR; gfx_SetTextFGColor(WHITE); gfx_PrintStringXY("game over", 20, 20); if(score >= 0) { gfx_SetTextXY(20, 36); gfx_PrintUInt(score, 1); gfx_PrintString(" days"); } if(blinkiness < 30) { const char *str = "* dramatic reenactment *"; uint24_t x = LCD_WIDTH - 10 - gfx_GetStringWidth(str); gfx_PrintStringXY(str, x, LCD_HEIGHT - 10 - 8); } gfx_SwapDraw(); return IN_PROGRESS; } uint8_t fly_chicken_in(int24_t score) { for(uint8_t y = LCD_HEIGHT; y > CHICKEN_TOP_Y; y -= 10) { gfx_FillScreen(BLACK); gfx_Sprite(large_chicken, CHICKEN_LEFT_X, y); uint8_t s = common(score); if(s != IN_PROGRESS) return s; } return IN_PROGRESS; } uint8_t only_chicken(int24_t score, uint8_t frames) { for(uint8_t x = 0; x < frames; x++) { gfx_FillScreen(BLACK); gfx_Sprite_NoClip(large_chicken, CHICKEN_LEFT_X, CHICKEN_TOP_Y); uint8_t s = common(score); if(s != IN_PROGRESS) return s; } return IN_PROGRESS; } uint8_t black_screen(int24_t score, uint8_t frames) { for(uint8_t x = 0; x < frames; x++) { gfx_FillScreen(BLACK); uint8_t s = common(score); if(s != IN_PROGRESS) return s; } return IN_PROGRESS; } #define BURN_TIME (3 * 60) #define NUM_FLAMES 15 uint8_t burn(int24_t score) { struct { uint24_t x; uint8_t y; uint8_t size; } flames[NUM_FLAMES]; for(uint8_t i = 0; i < NUM_FLAMES; i++) { uint8_t size = randInt(2, 6); flames[i].size = size; flames[i].x = randInt(CHICKEN_LEFT_X, CHICKEN_RIGHT_X - flame_width * size); flames[i].y = randInt(CHICKEN_TOP_Y, CHICKEN_BOTTOM_Y - flame_height * size); } for(uint8_t x = 0; x < BURN_TIME; x++) { gfx_FillScreen(BLACK); gfx_Sprite_NoClip(large_chicken, CHICKEN_LEFT_X, CHICKEN_TOP_Y); for(uint8_t i = 0; i < NUM_FLAMES; i++) { gfx_ScaledTransparentSprite_NoClip(flame, flames[i].x, flames[i].y, flames[i].size, flames[i].size); flames[i].x += randInt(-5, 5); flames[i].y += randInt(-5, 5); if(flames[i].x + flames[i].size * flame_width > CHICKEN_RIGHT_X) { flames[i].x = CHICKEN_RIGHT_X - flames[i].size * flame_width; } if(flames[i].x < CHICKEN_LEFT_X) { flames[i].x = CHICKEN_LEFT_X; } if(flames[i].y + flames[i].size * flame_height > CHICKEN_BOTTOM_Y) { flames[i].y = CHICKEN_BOTTOM_Y - flames[i].size * flame_height; } if(flames[i].y < CHICKEN_TOP_Y) { flames[i].y = CHICKEN_TOP_Y; } } uint8_t s = common(score); if(s != IN_PROGRESS) return s; } return IN_PROGRESS; } #define FREEZE_TIME BURN_TIME #define FREEZE_RATE 16 uint8_t freeze(int24_t score) { gfx_TempSprite(frozen_chicken, chicken_width, chicken_height); memcpy(frozen_chicken_data, chicken_data, chicken_width * chicken_height + 2); for(uint8_t f = 0; f < FREEZE_TIME; f++) { gfx_FillScreen(BLACK); // Set random pixels in the sprite to an icy color for(uint8_t i = 0; i < FREEZE_RATE; i++) { uint24_t x = randInt(0, chicken_width - 1); uint8_t y = randInt(0, chicken_height - 1); if(frozen_chicken_data[2 + x + y * chicken_width]) { frozen_chicken_data[2 + x + y * chicken_width] = FREEZE_COLOR; } } // Draw legs gfx_ScaledTransparentSprite_NoClip(left_leg, CHICKEN_LEFT_X + LEFT_LEG_X_OFFSET * LARGE_CHICKEN_SCALE, CHICKEN_TOP_Y + LEFT_LEG_Y_OFFSET * LARGE_CHICKEN_SCALE, LARGE_CHICKEN_SCALE, LARGE_CHICKEN_SCALE); gfx_ScaledTransparentSprite_NoClip(right_leg, CHICKEN_LEFT_X + RIGHT_LEG_X_OFFSET * LARGE_CHICKEN_SCALE, CHICKEN_TOP_Y + RIGHT_LEG_Y_OFFSET * LARGE_CHICKEN_SCALE, LARGE_CHICKEN_SCALE, LARGE_CHICKEN_SCALE); // Draw body gfx_ScaledTransparentSprite_NoClip(frozen_chicken, CHICKEN_LEFT_X, CHICKEN_TOP_Y, LARGE_CHICKEN_SCALE, LARGE_CHICKEN_SCALE); uint8_t s = common(score); if(s != IN_PROGRESS) return s; } return IN_PROGRESS; } #define PARADOX_TIME 60 #define PORTAL_CENTER_X (LCD_WIDTH - LCD_WIDTH - 40) #define PORTAL_CENTER_Y 80 uint8_t paradox(void) { for(uint8_t f = 0; f < PARADOX_TIME; f++) { gfx_FillScreen(BLACK); // Draw portal gfx_RotatedScaledTransparentSprite_NoClip(portal, PORTAL_CENTER_X - portal_width / 2, PORTAL_CENTER_Y - portal_height / 2, f * 4, 64); // Draw legs gfx_ScaledTransparentSprite_NoClip(left_leg, CHICKEN_LEFT_X + LEFT_LEG_X_OFFSET * LARGE_CHICKEN_SCALE, CHICKEN_TOP_Y + LEFT_LEG_Y_OFFSET * LARGE_CHICKEN_SCALE, LARGE_CHICKEN_SCALE, LARGE_CHICKEN_SCALE); gfx_ScaledTransparentSprite_NoClip(right_leg, CHICKEN_LEFT_X + RIGHT_LEG_X_OFFSET * LARGE_CHICKEN_SCALE, CHICKEN_TOP_Y + RIGHT_LEG_Y_OFFSET * LARGE_CHICKEN_SCALE, LARGE_CHICKEN_SCALE, LARGE_CHICKEN_SCALE); // Draw body gfx_RotatedScaledTransparentSprite_NoClip(chicken_square, CHICKEN_LEFT_X + f * (LCD_WIDTH / 2 - PORTAL_CENTER_X) / PARADOX_TIME, CHICKEN_TOP_Y + f * (PORTAL_CENTER_Y - CHICKEN_TOP_Y) / PARADOX_TIME, // these should be 3s, but apparently this function doesn't behave like it's documented to 0, 63 * 2 - f * 2); gfx_SetTextFGColor(LAMP_COLOR); const char *str = "time paradox detected"; gfx_PrintStringXY(str, LCD_WIDTH - 20 - gfx_GetStringWidth(str), 20); uint8_t s = common(randInt(0, 10000)); if(s != IN_PROGRESS) return s; } return IN_PROGRESS; } bool gameover(uint8_t status, int24_t score) { dbg_printf("Game over %u, score %u\n", status, score); uint8_t s; for(;;) { s = black_screen(score, 30); if(s != IN_PROGRESS) break; s = fly_chicken_in(score); if(s != IN_PROGRESS) break; s = only_chicken(score, 30); if(s != IN_PROGRESS) break; switch(status) { case STATUS_GAME_OVER_HOT: s = burn(score); break; case STATUS_GAME_OVER_COLD: s = freeze(score); break; case STATUS_GAME_OVER_TIME: s = paradox(); break; } if(s != IN_PROGRESS) break; } if(s == ENTER) { while(kb_AnyKey()); while(score-- > 0) { gfx_FillScreen(BLACK); common(score); } return false; } else return true; }