A port of Zachtronics' match-4 game HACK*MATCH to the TI-84 Plus CE
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

made files fall gradually, fixed scoring bug, updated README

ariajd27 83b99ded 5b05202a

+106 -44
+2 -1
CHANGELOG.txt
··· 2 2 - game end screen now flashes "GAME OVER" instead of just displaying it 3 3 - displayed high score now updates when the game is restarted 4 4 - added clear to exit to controls list in readme 5 - - changed file removal behavior 5 + - overhauled file match, scoring, and removal behavior 6 + - made files fall gradually
+6 -2
README.md
··· 6 6 7 7 Support the official release! Check out *EXAPUNKS*, an incredible open-ended programming puzzle game and good introduction to Zachtronics's catalog; *Last Call BBS*, a collection best described as several outstanding games rolled into one; or check out the NES port this is most closely based on on itch.io. 8 8 9 + ## Installation 10 + 11 + Using any linking program (TILP, TI Connect CE, etc), import HACKMTCH.8xg onto your calculator. This program also relies on the standard C library available [here](https://github.com/CE-Programming/libraries/releases). If launching the program displays the error "need libload", try reinstalling that library. 12 + 9 13 ## How to Play 10 14 11 15 *HACK\*MATCH* is a match-4 game in the vein of Candy Crush, but (hopefully!) better. Move matching files into groups of 4 to remove them and score points. Files will continue to be added from the top; if they reach the bottom, it's game over! ··· 26 30 Program Type: ASM 27 31 28 32 Size and Variable Usage: 29 - - RAM: 7360 B 30 - - HACKMTCH: 7340 B 33 + - RAM: 7705 B 34 + - HACKMTCH: 7685 B 31 35 - HKMCHDAT: 20 B 32 36 - ARC: 54117 B 33 37 - HKMCHGFX: 54117 B
+33 -13
src/drawing.c
··· 12 12 #include "variables.h" 13 13 #include "drawing.h" 14 14 15 + #include <debug.h> 16 + 15 17 unsigned char global_palette[32 + sizeof_grid_palette]; 16 18 unsigned char deathStage; 17 19 bool deathFlashOn; ··· 53 55 { 54 56 const unsigned int x = GRID_HOFFSET + col * GRID_SIZE; 55 57 56 - unsigned char y = GRID_VOFFSET; 58 + unsigned char y = GRID_VOFFSET - gridMoveOffset; 57 59 for (unsigned char row = 0; row < MAX_ROWS; row++) 58 60 { 59 61 if (files[col][row] != FILE_EMPTY) 60 62 { 61 63 // draw a file 62 - if (files[col][row] & 0x80) gfx_Sprite_NoClip(fileMatchSprites[files[col][row] & 0x7f], x, y); 63 - else if (files[col][row] & 0x10) gfx_Sprite_NoClip(file_locked, x, y); 64 - else gfx_Sprite_NoClip(fileSprites[files[col][row]], x, y); 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); 65 71 } 66 72 else 67 73 { 68 74 gfx_SetColor(COLOR_BLACK); 69 - gfx_FillRectangle_NoClip(x, y, GRID_SIZE, GRID_SIZE); 75 + gfx_FillRectangle(x, y, GRID_SIZE, GRID_SIZE); 70 76 71 77 if (col == exaCol) 72 78 { ··· 75 81 const unsigned char xx = x + DASH_HOFFSET; 76 82 for (unsigned char yy = y + DASH_VOFFSET; yy < y + GRID_SIZE; yy += DASH_INTERVAL) 77 83 { 78 - gfx_SetPixel(xx, yy); 84 + gfx_FillRectangle(xx, yy, DASH_WIDTH, DASH_HEIGHT); 79 85 } 80 86 } 81 87 } 82 88 y += GRID_SIZE; 83 89 } 84 90 85 - if (col == exaCol) drawExa(); // else it will have been drawn over a bit 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 + } 86 106 } 87 107 88 108 void clearifySprites(const unsigned char step) 89 109 { 90 - unsigned int y = GRID_VOFFSET; 110 + unsigned int y = GRID_VOFFSET - gridMoveOffset; 91 111 for (unsigned char row = 0; row < MAX_ROWS; row++) 92 112 { 93 113 unsigned char x = GRID_HOFFSET; ··· 97 117 { 98 118 if (files[col][row] & 0x08) 99 119 { 100 - if (step == 0) gfx_Sprite_NoClip(star_erase_1, x, y); 101 - else gfx_Sprite_NoClip(star_erase_2, x, y); 120 + if (step == 0) gfx_Sprite(star_erase_1, x, y); 121 + else gfx_Sprite(star_erase_2, x, y); 102 122 } 103 123 else 104 124 { 105 - if (step == 0) gfx_Sprite_NoClip(file_erase_1, x, y); 106 - else gfx_Sprite_NoClip(file_erase_2, x, y); 125 + if (step == 0) gfx_Sprite(file_erase_1, x, y); 126 + else gfx_Sprite(file_erase_2, x, y); 107 127 } 108 128 } 109 129 ··· 135 155 gfx_BlitBuffer(); 136 156 } 137 157 138 - nextLineTime += clock() - refundTimer; 158 + nextMoveTime += clock() - refundTimer; 139 159 } 140 160 141 161 void drawNumber(const unsigned int x, const unsigned char y, const unsigned int toDraw)
+59 -27
src/main.c
··· 24 24 bool isHoldingFile; 25 25 unsigned char heldFile; 26 26 27 - clock_t nextLineTime; 27 + unsigned char gridMoveOffset; 28 + clock_t nextMoveTime; 28 29 29 30 unsigned char prevRight, prevLeft, prev2nd, prevAlpha; 30 31 31 32 bool matched; 32 33 unsigned char starMatches; 33 34 clock_t clearTime; 34 - unsigned char nextValue; 35 + unsigned int nextValue; 35 36 36 37 bool getTargetedFile(unsigned char *output) // each of these returns false on failure 37 38 { ··· 91 92 gfx_SetDrawBuffer(); 92 93 #endif 93 94 94 - nextLineTime += clock() - refundTimer; 95 + nextMoveTime += clock() - refundTimer; 95 96 96 97 return true; 97 98 } ··· 127 128 gfx_SetDrawBuffer(); 128 129 #endif 129 130 130 - nextLineTime += clock() - refundTimer; 131 + nextMoveTime += clock() - refundTimer; 131 132 132 133 return true; 133 134 } ··· 224 225 } 225 226 } 226 227 227 - void getNextLineTime() 228 + void getNextMoveTime() 228 229 { 229 230 if (score < ROW_INTERVAL_SCALE_END) 230 231 { 231 - nextLineTime = clock() + MAX_NEW_ROW_INTERVAL; 232 + nextMoveTime = clock() + MAX_NEW_ROW_INTERVAL / 4; 232 233 233 234 if (score > ROW_INTERVAL_SCALE_START) 234 235 { 235 - nextLineTime -= (score - ROW_INTERVAL_SCALE_START) * ROW_INTERVAL_SCALE_FACTOR; 236 + nextMoveTime -= (score - ROW_INTERVAL_SCALE_START) * ROW_INTERVAL_SCALE_FACTOR / 4; 236 237 } 237 238 } 238 239 else 239 240 { 240 - nextLineTime = clock() + MIN_NEW_ROW_INTERVAL; 241 + nextMoveTime = clock() + MIN_NEW_ROW_INTERVAL / 4; 241 242 } 242 243 } 243 244 ··· 291 292 break; 292 293 } 293 294 } 295 + 296 + // move all the blocks back up so it's not too jarring 297 + gridMoveOffset += GRID_SIZE; 294 298 } 295 299 296 300 void collapseGrid() ··· 326 330 clearTime = clock() + MATCH_TIME; 327 331 } 328 332 333 + void clearMarks(const bool toMark) 334 + { 335 + for (unsigned char checkRow = 0; checkRow < MAX_ROWS; checkRow++) 336 + { 337 + for (unsigned char checkCol = 0; checkCol < NUM_COLS; checkCol++) 338 + { 339 + if (toMark) files[checkCol][checkRow] &= 0xbf; 340 + else if (files[checkCol][checkRow] & 0x40) files[checkCol][checkRow] &= 0x3f; 341 + } 342 + } 343 + } 344 + 329 345 void checkForMatch() 330 346 { 331 347 // check for sets to pop and score ··· 344 360 if (numMatchingStars < 2) 345 361 { 346 362 // if not, we don't care about it, bc it can't form a set 347 - files[col][row] &= 0x7f; 363 + clearMarks(false); 348 364 } 349 365 else 350 366 { 367 + starMatches += numMatchingStars; 368 + 351 369 // if we get here, it does have a match! 352 370 const unsigned char target = files[col][row] & 0x07; 353 371 for (unsigned char starRow = 0; starRow < MAX_ROWS; starRow++) ··· 370 388 // find all contiguous matching blocks and their count 371 389 unsigned char numMatchingBlocks = 0; 372 390 findMatchRegion(row, col, files[col][row], &numMatchingBlocks); 373 - const bool toMark = numMatchingBlocks >= AMOUNT_FILES_TO_MATCH; 374 391 375 392 // unmark or cement the matching region 376 - for (unsigned char checkRow = 0; checkRow < MAX_ROWS; checkRow++) 377 - { 378 - for (unsigned char checkCol = 0; checkCol < NUM_COLS; checkCol++) 379 - { 380 - if (toMark) files[checkCol][checkRow] &= 0xbf; 381 - else if (files[checkCol][checkRow] & 0x40) files[checkCol][checkRow] &= 0x3f; 382 - } 383 - } 384 - 393 + const bool toMark = numMatchingBlocks >= AMOUNT_FILES_TO_MATCH; 394 + clearMarks(toMark); 385 395 if (toMark) setClearTime(); 386 396 } 387 397 } ··· 407 417 } 408 418 409 419 // calculate the score for the matching region 410 - unsigned char atBase = nextValue == BASE_BLOCK_VALUE ? AMOUNT_FILES_TO_MATCH : 0; 420 + unsigned char atBase = 0; 421 + if (nextValue == BASE_BLOCK_VALUE) atBase = AMOUNT_FILES_TO_MATCH; 411 422 numMatchingBlocks -= starMatches; 412 423 while (numMatchingBlocks > 0) 413 424 { ··· 416 427 417 428 score += nextValue; 418 429 numMatchingBlocks--; 430 + 431 + dbg_printf("added block with value %u\n", nextValue); 419 432 } 420 433 434 + dbg_printf("total score: %u\n", score); 435 + 421 436 // tidy up 422 437 matched = false; 423 438 starMatches = 0; ··· 426 441 427 442 void updateGrid() 428 443 { 444 + clock_t refundTimer = clock(); 445 + 429 446 // remove the matched blocks and collapse the grid if it's time 430 447 if (matched && clock() > clearTime) 431 448 { ··· 436 453 checkForMatch(); 437 454 438 455 // reset the score if not 439 - if (!matched) nextValue = BASE_BLOCK_VALUE; 456 + if (!matched) 457 + { 458 + nextValue = BASE_BLOCK_VALUE; 459 + 460 + // ...and move the grid down if it's time 461 + if (clock() > nextMoveTime) 462 + { 463 + gridMoveOffset -= GRID_SIZE / GRID_MOVE_STEPS; 464 + if (gridMoveOffset == 0) addNewRow(); 465 + getNextMoveTime(); 466 + } 467 + } 440 468 441 - // move the grid down if it's time 442 - if (clock() > nextLineTime) 469 + // if there is a match, don't move the grid down 470 + else 443 471 { 444 - addNewRow(); 445 - getNextLineTime(); 472 + nextMoveTime += clock() - refundTimer; 446 473 } 447 474 } 448 475 ··· 481 508 } 482 509 483 510 // populate the initial grid 484 - for (unsigned char i = 0; i < NUM_INITIAL_ROWS; i++) 511 + for (unsigned char i = 0; i <= NUM_INITIAL_ROWS; i++) 485 512 { 486 513 addNewRow(); 487 514 } 488 515 516 + gridMoveOffset = GRID_SIZE; 517 + 489 518 // set and draw initial exa position 490 519 exaCol = EXA_START_COL; 491 520 gfx_GetSprite(behindExa, EXA_HOFFSET + EXA_START_COL * GRID_SIZE, EXA_VOFFSET); 492 521 gfx_TransparentSprite_NoClip(exa_empty, EXA_HOFFSET + EXA_START_COL * GRID_SIZE, EXA_VOFFSET); 493 522 494 - nextLineTime = clock() + MIN_NEW_ROW_INTERVAL; 523 + nextMoveTime = clock() + MIN_NEW_ROW_INTERVAL; 495 524 } 496 525 497 526 bool endGame() ··· 574 603 #else 575 604 gfx_SetDrawBuffer(); 576 605 #endif 606 + 607 + // set the clip area so that the grid can be offset 608 + gfx_SetClipRegion(0, CLIP_VOFFSET, GFX_LCD_WIDTH, GFX_LCD_HEIGHT); 577 609 578 610 // prepare the sketchy combination palette 579 611 for (unsigned char i = 0; i < sizeof_fixed_palette; i++) global_palette[i] = fixed_palette[i];
+6 -1
src/variables.h
··· 50 50 #define GRID_HOFFSET (BG_HOFFSET + 48) 51 51 #define GRID_VOFFSET (BG_VOFFSET + 23) 52 52 #define GRID_SIZE 16 53 + #define GRID_MOVE_STEPS 4 54 + #define CLIP_VOFFSET (BG_VOFFSET + 22) 53 55 #define EXA_HOFFSET (BG_HOFFSET + 44) 54 56 #define EXA_VOFFSET (BG_VOFFSET + 193) 55 57 #define EXA_HELD_HOFFSET 4 ··· 57 59 #define DASH_HOFFSET 8 58 60 #define DASH_VOFFSET 3 59 61 #define DASH_INTERVAL 4 62 + #define DASH_WIDTH 1 63 + #define DASH_HEIGHT 1 60 64 #define SCORE_HOFFSET (BG_HOFFSET + 160) 61 65 #define SCORE_VOFFSET (BG_VOFFSET + 41) 62 66 #define HIGH_SCORE_HOFFSET (BG_HOFFSET + 160) ··· 78 82 extern unsigned char exaCol; 79 83 extern bool isHoldingFile; 80 84 extern unsigned char heldFile; 85 + extern unsigned char gridMoveOffset; 81 86 82 - extern clock_t nextLineTime; 87 + extern clock_t nextMoveTime; 83 88 84 89 extern unsigned char prevRight, prevLeft, prev2nd, prevAlpha; 85 90