A port of Zachtronics' match-4 game HACK*MATCH to the TI-84 Plus CE
at main 271 lines 6.5 kB view raw
1#include <sys/rtc.h> 2#include <ti/getcsc.h> 3#include <fileioc.h> 4#include <graphx.h> 5#include <keypadc.h> 6#include <stdlib.h> 7#include <stdio.h> 8#include <math.h> 9#include <time.h> 10 11#include "gfx/gfx.h" 12#include "variables.h" 13#include "drawing.h" 14 15#include <debug.h> 16 17unsigned char global_palette[32 + sizeof_grid_palette]; 18unsigned char deathStage; 19bool deathFlashOn; 20unsigned long nextDeathFlash; 21 22struct gfx_sprite_t *fileSprites[14]; 23struct gfx_sprite_t *fileMatchSprites[6]; 24struct gfx_sprite_t *digitSprites[10]; 25struct gfx_sprite_t *deathSprites[3]; 26 27gfx_UninitedSprite(behindExa, exa_empty_width, exa_empty_height); 28gfx_UninitedSprite(behindGameOver, play_again_width, play_again_height); 29 30void drawExa() 31{ 32 const unsigned char exaX = EXA_HOFFSET + GRID_SIZE * exaCol; 33 if (deathStage == 0 && isHoldingFile) 34 { 35 if (heldFile & 0x10) 36 { 37 gfx_Sprite_NoClip(file_locked, exaX + EXA_HELD_HOFFSET, EXA_VOFFSET + EXA_HELD_VOFFSET); 38 } 39 else 40 { 41 gfx_Sprite_NoClip(fileSprites[heldFile], exaX + EXA_HELD_HOFFSET, EXA_VOFFSET + EXA_HELD_VOFFSET); 42 } 43 } 44 45 if (deathStage > 0) gfx_TransparentSprite_NoClip(deathSprites[deathStage], exaX, EXA_VOFFSET); 46 else if (isHoldingFile) 47 { 48 if (heldFile & 0x08) gfx_TransparentSprite_NoClip(exa_star, exaX, EXA_VOFFSET); 49 else gfx_TransparentSprite_NoClip(exa_file, exaX, EXA_VOFFSET); 50 } 51 else gfx_TransparentSprite_NoClip(exa_empty, exaX, EXA_VOFFSET); 52} 53 54void drawCol(const unsigned char col) 55{ 56 const unsigned int x = GRID_HOFFSET + col * GRID_SIZE; 57 58 unsigned char y = GRID_VOFFSET - gridMoveOffset; 59 for (unsigned char row = 0; row < MAX_ROWS; row++) 60 { 61 if (files[col][row] != FILE_EMPTY) 62 { 63 // draw a file 64 if (files[col][row] & 0x80) 65 { 66 if (files[col][row] & 0x08) gfx_Sprite(star_match, x, y); 67 else gfx_Sprite(fileMatchSprites[files[col][row] & 0x7f], x, y); 68 } 69 else if (files[col][row] & 0x10) gfx_Sprite(file_locked, x, y); 70 else gfx_Sprite(fileSprites[files[col][row]], x, y); 71 } 72 else 73 { 74 gfx_SetColor(COLOR_BLACK); 75 gfx_FillRectangle(x, y, GRID_SIZE, GRID_SIZE); 76 77 if (col == exaCol) 78 { 79 // draw the dashed line up from the exa 80 gfx_SetColor(COLOR_DASH); 81 const unsigned char xx = x + DASH_HOFFSET; 82 for (unsigned char yy = y + DASH_VOFFSET; yy < y + GRID_SIZE; yy += DASH_INTERVAL) 83 { 84 gfx_FillRectangle(xx, yy, DASH_WIDTH, DASH_HEIGHT); 85 } 86 } 87 } 88 y += GRID_SIZE; 89 } 90 91 // fill in below the grid for partially-lowered files 92 gfx_SetColor(COLOR_BLACK); 93 gfx_FillRectangle(x, y, GRID_SIZE, gridMoveOffset); 94 95 if (col == exaCol) 96 { 97 gfx_SetColor(COLOR_DASH); 98 const unsigned char xx = x + DASH_HOFFSET; 99 for (unsigned char yy = y + DASH_VOFFSET; yy < y + gridMoveOffset; yy += DASH_INTERVAL) 100 { 101 gfx_FillRectangle_NoClip(xx, yy, DASH_WIDTH, DASH_HEIGHT); 102 } 103 104 drawExa(); // else it will have been drawn over a bit 105 } 106} 107 108void clearifySprites(const unsigned char step) 109{ 110 unsigned int y = GRID_VOFFSET - gridMoveOffset; 111 for (unsigned char row = 0; row < MAX_ROWS; row++) 112 { 113 unsigned char x = GRID_HOFFSET; 114 for (unsigned char col = 0; col < NUM_COLS; col++) 115 { 116 if (files[col][row] & 0x80) 117 { 118 if (files[col][row] & 0x08) 119 { 120 if (step == 0) gfx_Sprite(star_erase_1, x, y); 121 else gfx_Sprite(star_erase_2, x, y); 122 } 123 else 124 { 125 if (step == 0) gfx_Sprite(file_erase_1, x, y); 126 else gfx_Sprite(file_erase_2, x, y); 127 } 128 } 129 130 x += GRID_SIZE; 131 } 132 133 y += GRID_SIZE; 134 } 135} 136 137void animateClear() // relies on to-clears being flagged with bit 7 138{ 139 clock_t refundTimer = clock(); 140 141 // get everything in position for the animation 142 for (unsigned char col = 0; col < NUM_COLS; col++) 143 { 144 drawCol(col); 145 } 146 147 for (unsigned char step = 0; step < 2; step++) 148 { 149 clock_t animationTimer = clock(); 150 151 clearifySprites(step); 152 153 while (clock() - animationTimer < CLEAR_ANIMATION_FRAME_TIME); 154 155 gfx_BlitBuffer(); 156 } 157 158 nextMoveTime += clock() - refundTimer; 159} 160 161void drawNumber(const unsigned int x, const unsigned char y, const unsigned int toDraw) 162{ 163 bool significant = false; 164 unsigned int remainder = toDraw; 165 unsigned int digitX = x; 166 for (unsigned int divisor = 100000; divisor >= 1; divisor /= 10) 167 { 168 const unsigned char thisDigit = remainder / divisor; 169 remainder -= divisor * thisDigit; 170 if (thisDigit > 0) significant = true; 171 172 if (!significant) gfx_Sprite_NoClip(digit_0_grey, digitX, y); 173 else gfx_Sprite_NoClip(digitSprites[thisDigit], digitX, y); 174 175 digitX += 8; 176 } 177} 178 179void drawFrame() 180{ 181 // erase the old exa 182 gfx_Sprite_NoClip(behindExa, EXA_HOFFSET + GRID_SIZE * exaCol, EXA_VOFFSET); 183 184 // draw the new grid and exa 185 for (unsigned char col = 0; col < NUM_COLS; col++) 186 { 187 drawCol(col); 188 } 189 190 // draw the score 191 drawNumber(SCORE_HOFFSET, SCORE_VOFFSET, score); 192 193#ifndef NO_BUFFER 194 gfx_BlitBuffer(); 195#endif 196} 197 198bool titleScreen() 199{ 200 gfx_FillScreen(COLOR_BLACK); 201 gfx_RLETSprite_NoClip(title, TITLE_SPRITE_HOFFSET, TITLE_SPRITE_VOFFSET); 202 203 gfx_SetTextFGColor(COLOR_RED); 204 gfx_SetTextXY(TITLE_TEXT_HOFFSET, TITLE_TEXT_VOFFSET); 205 gfx_PrintString("Press any key..."); 206 207#ifndef NO_BUFFER 208 gfx_BlitBuffer(); 209#endif 210 211 // wait for no keys to be pressed 212 while (kb_AnyKey()); 213 214 // wait for a key to be pressed 215 while (true) 216 { 217 kb_Scan(); 218 if (kb_IsDown(kb_KeyClear)) return true; 219 else if (kb_AnyKey()) return false; 220 } 221} 222 223void sleep(unsigned long time) 224{ 225 clock_t sleepTimer = clock(); 226 while (clock() - sleepTimer < time); 227} 228 229void darken() 230{ 231 for (unsigned char i = 16; i < 32; i++) gfx_palette[i] = gfx_Darken(gfx_palette[i], DEATH_DARKEN_LEVEL); 232} 233 234void animateDeath() 235{ 236 // do the animation 237 for (deathStage = 0; deathStage < 3; deathStage++) 238 { 239 darken(); 240 drawFrame(); 241 sleep(DEATH_ANIMATION_FRAME_TIME / 8); 242 243 for (unsigned char i = 0; i < 7; i++) 244 { 245 darken(); 246 sleep(DEATH_ANIMATION_FRAME_TIME / 8); 247 } 248 } 249 250 // say "GAME OVER" 251 behindGameOver->width = play_again_width; 252 behindGameOver->height = play_again_height; 253 gfx_GetSprite(behindGameOver, PLAY_AGAIN_HOFFSET, PLAY_AGAIN_VOFFSET); 254 gfx_RLETSprite_NoClip(play_again, PLAY_AGAIN_HOFFSET, PLAY_AGAIN_VOFFSET); 255 gfx_BlitBuffer(); 256 deathFlashOn = true; 257 nextDeathFlash = clock() + GAME_OVER_FLASH_TIME; 258} 259 260void deathPeriodic() 261{ 262 if (clock() < nextDeathFlash) return; 263 264 if (deathFlashOn) gfx_Sprite_NoClip(behindGameOver, PLAY_AGAIN_HOFFSET, PLAY_AGAIN_VOFFSET); 265 else gfx_RLETSprite_NoClip(play_again, PLAY_AGAIN_HOFFSET, PLAY_AGAIN_VOFFSET); 266 267 gfx_BlitBuffer(); 268 269 deathFlashOn = !deathFlashOn; 270 nextDeathFlash = clock() + GAME_OVER_FLASH_TIME; 271}