A card solitaire game for the TI-84 Plus/83 Premium CE graphing calculators.
at main 537 lines 16 kB view raw
1#include "drawing.h" 2#include <graphx.h> 3#include <time.h> 4#include "card.h" 5#include "state.h" 6 7#include <debug.h> 8 9#define COLOR_CARD 0 10#define COLOR_BLACK 1 11#define COLOR_TABLE 2 12#define COLOR_RED 3 13#define COLOR_GREEN 4 14#define COLOR_BLUE 5 15#define COLOR_ORANGE 6 16#define COLOR_DIM 0x80 17 18#define CARD_WIDTH 25 19#define CARD_HEIGHT 51 20#define CARD_SPACING_X 4 21 22#define CARD_NUMERAL_DX 1 23#define CARD_NUMERAL_DY 1 24#define CARD_SUIT_DX 6 25#define CARD_SUIT_DY 1 26 27#define TABLEAU_DX (CARD_WIDTH + CARD_SPACING_X) 28#define TABLEAU_WIDTH ((TABLEAU_NUM_PILES * TABLEAU_DX) - CARD_SPACING_X) 29#define TABLEAU_MARGIN ((GFX_LCD_WIDTH - TABLEAU_WIDTH) / 2) 30#define TABLEAU_X0 TABLEAU_MARGIN 31#define TABLEAU_Y0 (GFX_LCD_HEIGHT / 3) 32#define TABLEAU_DY 8 33 34#define FOUNDATIONS_DX TABLEAU_DX 35#define FOUNDATIONS_X0 (GFX_LCD_WIDTH - FOUNDATIONS_DX * 4 - TABLEAU_MARGIN) 36#define FOUNDATIONS_Y (TABLEAU_Y0 - CARD_HEIGHT - CARD_SPACING_X * 2) 37#define DIM_AMOUNT 128 38 39#define TRUMPS_X0 (TABLEAU_MARGIN + CARD_SPACING_X) 40#define TRUMPS_DX CARD_SPACING_X 41#define TRUMPS_X1 (TRUMPS_X0 + (TRUMPS_DX * CARD_NUM_TRUMPS) + CARD_WIDTH) 42#define TRUMPS_Y FOUNDATIONS_Y 43 44#define HELD_CARD_X ((GFX_LCD_WIDTH - CARD_WIDTH) / 2) 45#define HELD_CARD_Y (GFX_LCD_HEIGHT - CARD_HEIGHT) 46 47#define MOVE_ANIM_LENGTH 40 48#define DEAL_ANIM_DECK_X (TABLEAU_X0 + TABLEAU_DX * 5) 49#define DEAL_ANIM_DECK_Y TABLEAU_Y0 50#define DEAL_ANIM_TOTAL_TIME 100000 51#define DEAL_ANIM_TRAVEL_TIME 6000 52#define DEAL_ANIM_LAUNCH_INTERVAL ((DEAL_ANIM_TOTAL_TIME - DEAL_ANIM_TRAVEL_TIME) / NUM_CARDS) 53 54#define DRAW_MODE 0x00 55#define DRAW_MODE_PARTIAL 0x01 56#define DRAW_MODE_DIMMABLE 0x02 57 58#include <time.h> 59 60// sprite references 61#include "gfx/gfx.h" 62const unsigned char *numerals_data[] = { 63 _1_data, _2_data, _3_data, _4_data, _5_data, _6_data, _7_data, 64 _8_data, _9_data, _10_data, J_data, Q_data, K_data 65}; 66const unsigned char *small_suits_data[] = { 67 clubs_data, swords_data, cups_data, coins_data 68}; 69const unsigned char *large_suits_data[] = { 70 large_clubs_data, large_swords_data, large_cups_data, large_coins_data 71}; 72 73// black magic for placing pips on cards 74const unsigned char segments[] = { 75 0x01, 0x24, 0x25, 0x48, 0x49, 0x4a, 0xa8, 0x90, 0x91, 0xb4 76}; 77const unsigned char pip_code[] = { 78 0x04, 06, 07, 14, 07, 06, 17, 14, 17, 79 0x02, 06, 12, 14, 12, 80 0x01, 10, 12, 81 0x84, 11, 34, 19, 34, 11, 44, 19, 44, 82 0x82, 11, 39, 19, 39, 83 0x81, 15, 39, 84 0x02, 06, 22, 14, 22, 85 0x01, 10, 22 86}; 87 88// roman numeral display widths 89const unsigned char roman_widths[] = { 90 3, 5, 7, 7, 5, 7, 9, 11, 7, 5, 7, // i - xi 91 9, 11, 11, 9, 11, 13, 15, 11, 9, 11, 13 // xii - xxii 92}; 93 94void draw_setup() 95{ 96 gfx_Begin(); 97 gfx_SetDrawBuffer(); 98 gfx_SetPalette(global_palette, sizeof_global_palette, 0); 99 gfx_SetPalette(global_palette, sizeof_global_palette, COLOR_DIM); 100} 101 102void darken_dim() 103{ 104 for (unsigned char i = COLOR_DIM; i < (sizeof_global_palette | COLOR_DIM); i++) 105 { 106 gfx_palette[i] = gfx_Darken(gfx_palette[i], DIM_AMOUNT); 107 } 108} 109 110void restore_dim() 111{ 112 gfx_SetPalette(gfx_palette, sizeof_global_palette, COLOR_DIM); 113} 114 115void draw_mask(const unsigned char *data, 116 unsigned char num_rows, unsigned int x, unsigned char y) 117{ 118 unsigned char yy = y; 119 120 for (unsigned char row = 0; row < num_rows; row++) 121 { 122 unsigned char row_data = data[row]; 123 124 for (unsigned int xx = x; xx < x + 8; xx++) 125 { 126 if (row_data & 0x80) gfx_SetPixel(xx, yy); 127 128 row_data <<= 1; 129 } 130 131 yy++; 132 } 133} 134 135void draw_mask_inverted(const unsigned char *data, 136 unsigned char num_rows, unsigned int x, unsigned char y) 137{ 138 unsigned char yy = y - num_rows; 139 140 for (unsigned char row = 1; row <= num_rows; row++) 141 { 142 unsigned char row_data = data[num_rows - row]; 143 144 for (unsigned int xx = x - 8; xx < x; xx++) 145 { 146 if (row_data & 0x01) gfx_SetPixel(xx, yy); 147 148 row_data >>= 1; 149 } 150 151 yy++; 152 } 153} 154 155void draw_card(const unsigned int x, const unsigned char y, card_t card, unsigned char draw_mode) 156{ 157 // check clipping 158 if (x > GFX_LCD_WIDTH - CARD_WIDTH || y > GFX_LCD_HEIGHT - CARD_HEIGHT) return; 159 160 // draw blank card 161 if (draw_mode & DRAW_MODE_DIMMABLE) gfx_SetColor(COLOR_CARD | COLOR_DIM); 162 else gfx_SetColor(COLOR_CARD); 163 gfx_HorizLine_NoClip(x + 1, y, CARD_WIDTH - 2); 164 gfx_FillRectangle_NoClip(x, y + 1, CARD_WIDTH, CARD_HEIGHT - 1); 165 166 // draw card border 167 gfx_SetColor(COLOR_BLACK); 168 gfx_SetPixel(x, y); 169 gfx_SetPixel(x + CARD_WIDTH - 1, y); 170 gfx_SetPixel(x, y + CARD_HEIGHT - 1); 171 gfx_SetPixel(x + CARD_WIDTH - 1, y + CARD_HEIGHT - 1); 172 gfx_HorizLine_NoClip(x + 1, y - 1, CARD_WIDTH - 2); 173 gfx_HorizLine_NoClip(x + 1, y + CARD_HEIGHT, CARD_WIDTH - 2); 174 175 if (card_is_trump(card)) 176 { 177 // draw card number as roman numeral 178 unsigned char remaining_value = card_get_value(card) + 1; 179 unsigned int xx = x + (CARD_WIDTH - roman_widths[remaining_value - 1]) / 2; 180 181 do { 182 if (remaining_value >= 10) { 183 draw_mask(x_data, 5, xx, y + CARD_NUMERAL_DY); 184 xx += 4; 185 remaining_value -= 10; 186 } 187 else if (remaining_value >= 9) { 188 draw_mask(i_data, 5, xx, y + CARD_NUMERAL_DY); 189 draw_mask(x_data, 5, xx + 2, y + CARD_NUMERAL_DY); 190 xx += 6; 191 remaining_value -= 9; 192 } 193 else if (remaining_value >= 5) { 194 draw_mask(v_data, 5, xx, y + CARD_NUMERAL_DY); 195 xx += 4; 196 remaining_value -= 5; 197 } 198 else if (remaining_value >= 4) { 199 draw_mask(i_data, 5, xx, y + CARD_NUMERAL_DY); 200 draw_mask(v_data, 5, xx + 2, y + CARD_NUMERAL_DY); 201 xx += 6; 202 remaining_value -= 4; 203 } 204 else { 205 draw_mask(i_data, 5, xx, y + CARD_NUMERAL_DY); 206 xx += 2; 207 remaining_value -= 1; 208 } 209 } while (remaining_value > 0); 210 } 211 else 212 { 213 // draw card number and suit 214 if (draw_mode & DRAW_MODE_DIMMABLE) 215 gfx_SetColor((card_get_suit(card) + COLOR_RED) | COLOR_DIM); 216 else gfx_SetColor(card_get_suit(card) + COLOR_RED); 217 draw_mask(numerals_data[card_get_value(card)], 5, 218 x + CARD_NUMERAL_DX, y + CARD_NUMERAL_DY); 219 draw_mask(small_suits_data[card_get_suit(card)], 5, 220 x + CARD_SUIT_DX, y + CARD_SUIT_DY); 221 draw_mask_inverted(numerals_data[card_get_value(card)], 5, 222 x + CARD_WIDTH - CARD_NUMERAL_DX, y + CARD_HEIGHT - CARD_NUMERAL_DY); 223 draw_mask_inverted(small_suits_data[card_get_suit(card)], 5, 224 x + CARD_WIDTH - CARD_SUIT_DX, y + CARD_HEIGHT - CARD_SUIT_DY); 225 } 226 227 if (draw_mode & DRAW_MODE_PARTIAL) return; 228 229 if (card_is_face(card) || card_is_trump(card)) return; // TODO 230 231 // otherwise, draw pips 232 const unsigned char *pip_mask = large_suits_data[card_get_suit(card)]; 233 const unsigned char *code_ptr = pip_code; 234 for (unsigned char pip_map = segments[card_get_value(card)]; 235 pip_map != 0x00; pip_map <<= 1) 236 { 237 unsigned char counter = *code_ptr & 0x0f; 238 if (pip_map & 0x80) 239 { 240 const bool is_negative = *code_ptr & 0x80; 241 code_ptr++; 242 243 while (counter-- > 0) 244 { 245 if (is_negative) draw_mask_inverted(pip_mask, 6, 246 x + code_ptr[0], y + code_ptr[1]); 247 else draw_mask(pip_mask, 6, x + code_ptr[0], y + code_ptr[1]); 248 249 code_ptr += 2; 250 } 251 } 252 else 253 { 254 code_ptr += counter * 2 + 1; 255 } 256 } 257} 258 259void draw_frame() 260{ 261 gfx_FillScreen(COLOR_TABLE); 262 263 // draw tableau 264 unsigned int x = TABLEAU_X0; 265 for (unsigned char i = 0; i < TABLEAU_NUM_PILES; i++) 266 { 267 unsigned char y = TABLEAU_Y0; 268 unsigned char j; 269 270 for (j = 0; j < TABLEAU_PILE_SIZE; j++) 271 { 272 if (!card_exists(tableau[i][j])) break; 273 274 draw_card(x, y, tableau[i][j], card_exists(tableau[i][j + 1])); 275 276 y += TABLEAU_DY; 277 } 278 279 if (cursor_stack == i) 280 { 281 if (j > 0) y -= TABLEAU_DY; 282 283 if (card_exists(held_card)) 284 { 285 if (j > 0) y += TABLEAU_DY; 286 gfx_SetColor(COLOR_RED); 287 } else { 288 gfx_SetColor(COLOR_BLACK); 289 } 290 draw_mask(selcorner_left_data, 6, 291 x - 2, y - 2); 292 draw_mask(selcorner_right_data, 6, 293 x + CARD_WIDTH - 4, y - 2); 294 draw_mask_inverted(selcorner_right_data, 6, 295 x + 4, y + CARD_HEIGHT + 2); 296 draw_mask_inverted(selcorner_left_data, 6, 297 x + CARD_WIDTH + 2, y + CARD_HEIGHT + 2); 298 } 299 300 x += TABLEAU_DX; 301 } 302 303 // draw foundations 304 x = FOUNDATIONS_X0; 305 for (unsigned char i = 0; i < FOUNDATIONS_NUM; i++) 306 { 307 draw_card(x, FOUNDATIONS_Y, foundations[i], DRAW_MODE_DIMMABLE); 308 x += FOUNDATIONS_DX; 309 } 310 311 // draw trumps 312 x = TRUMPS_X0; 313 for (unsigned char i = 0; i < next_low_trump; i++) 314 { 315 draw_card(x, TRUMPS_Y, CARD_SUIT_TRUMP | i, 0); 316 x += TRUMPS_DX; 317 } 318 x = TRUMPS_X1; 319 for (unsigned char i = CARD_NUM_TRUMPS - 1; i > next_high_trump; i--) 320 { 321 draw_card(x, TRUMPS_Y, CARD_SUIT_TRUMP | i, 0); 322 x -= TRUMPS_DX; 323 } 324 325 // draw held card 326 if (card_exists(held_card)) 327 { 328 draw_card(HELD_CARD_X, HELD_CARD_Y, held_card, 0); 329 } 330 331 gfx_BlitBuffer(); 332} 333 334void move_card(unsigned int x0, unsigned char y0, unsigned int x1, 335 unsigned char y1, card_t card, unsigned char no_redraw) 336{ 337 if (y0 == 0) 338 { 339 x0 = x1; 340 y0 = y1; 341 } 342 343 gfx_TempSprite(sprite_buffer, CARD_WIDTH, CARD_HEIGHT + 2); 344 if (!no_redraw) gfx_GetSprite(sprite_buffer, x1, y1 - 1); 345 346 draw_card(x1, y1, card, 0); 347 348 gfx_BlitRectangle(gfx_buffer, x0, y0 - 1, CARD_WIDTH, CARD_HEIGHT + 2); 349 gfx_BlitRectangle(gfx_buffer, x1, y1 - 1, CARD_WIDTH, CARD_HEIGHT + 2); 350 351 if (!no_redraw) gfx_Sprite(sprite_buffer, x1, y1 - 1); 352} 353 354void animate_move(unsigned int x0, unsigned char y0, 355 unsigned int x1, unsigned char y1, card_t card) 356{ 357 const unsigned char flip_x = x0 > x1; 358 const unsigned char flip_y = y0 > y1; 359 360 if (flip_x && x0 != x1) 361 { 362 x0 ^= x1; 363 x1 ^= x0; 364 x0 ^= x1; 365 } 366 367 if (flip_y && y0 != y1) 368 { 369 y0 ^= y1; 370 y1 ^= y0; 371 y0 ^= y1; 372 } 373 374 draw_frame(); 375 376 const unsigned int Dx = x1 - x0; 377 const unsigned char Dy = y1 - y0; 378 const clock_t duration = MOVE_ANIM_LENGTH * (Dy + Dx); 379 const clock_t start_time = clock(); 380 unsigned int last_x = 0; 381 unsigned char last_y = 0; 382 while (true) 383 { 384 const clock_t now_time = clock(); 385 const clock_t elapsed = now_time - start_time; 386 387 const unsigned char last_iteration = now_time - start_time > duration; 388 389 const unsigned int dx = last_iteration ? Dx : Dx * elapsed / duration; 390 const unsigned char dy = last_iteration ? Dy : Dy * elapsed / duration; 391 392 const unsigned int card_x = flip_x ? x1 - dx : x0 + dx; 393 const unsigned char card_y = flip_y ? y1 - dy : y0 + dy; 394 395 move_card(last_x, last_y, card_x, card_y, card, 0); 396 397 if (last_iteration) break; 398 399 last_x = card_x; 400 last_y = card_y; 401 } 402} 403 404void animate_build(unsigned char tableau_stack, unsigned char tableau_index) 405{ 406 const card_t card = tableau[tableau_stack][tableau_index]; 407 tableau[tableau_stack][tableau_index] = empty_card(); 408 409 const unsigned int x0 = TABLEAU_X0 + tableau_stack * TABLEAU_DX; 410 const unsigned char y0 = TABLEAU_Y0 + tableau_index * TABLEAU_DY; 411 412 if (card_is_trump(card)) 413 { 414 unsigned int x1 = TRUMPS_X0 + TRUMPS_DX * card_get_value(card); 415 if (card_get_value(card) != next_low_trump) 416 { 417 x1 += CARD_WIDTH + TRUMPS_DX; 418 } 419 420 animate_move(x0, y0, x1, TRUMPS_Y, card); 421 } 422 else 423 { 424 unsigned int x1 = FOUNDATIONS_X0 + card_get_suit(card) * FOUNDATIONS_DX; 425 animate_move(x0, y0, x1, FOUNDATIONS_Y, card); 426 } 427} 428 429void animate_grab(card_t card, unsigned char tableau_stack, 430 unsigned char tableau_index) 431{ 432 const unsigned int x0 = TABLEAU_X0 + tableau_stack * TABLEAU_DX; 433 const unsigned char y0 = TABLEAU_Y0 + tableau_index * TABLEAU_DY; 434 435 animate_move(x0, y0, HELD_CARD_X, HELD_CARD_Y, card); 436} 437 438void animate_drop(card_t card, unsigned char tableau_stack, 439 unsigned char tableau_index) 440{ 441 const unsigned char x1 = TABLEAU_X0 + tableau_stack * TABLEAU_DX; 442 const unsigned char y1 = TABLEAU_Y0 + tableau_index * TABLEAU_DY; 443 444 animate_move(HELD_CARD_X, HELD_CARD_Y, x1, y1, card); 445} 446 447void animate_deal(card_t *deck) 448{ 449 gfx_FillScreen(COLOR_TABLE); 450 gfx_BlitBuffer(); 451 452 const clock_t main_timer = clock(); 453 clock_t last_dt = 0; 454 while (clock() - main_timer < DEAL_ANIM_TOTAL_TIME) 455 { 456 const clock_t frame_dt = clock() - main_timer; 457 458 unsigned char tableau_stack = 0; 459 unsigned char tableau_index = 0; 460 for (unsigned char i = 0; i < NUM_CARDS; i++) 461 { 462 // should this card have launched yet? 463 const unsigned int delay = i * DEAL_ANIM_LAUNCH_INTERVAL; 464 if (frame_dt <= delay) continue; 465 466 // if last card launched, deck sprite disappears 467 // TODO 468 469 // where is the card going? 470 const card_t card = deck[i]; 471 unsigned int x1; 472 unsigned char y1; 473 if (!card_is_trump(card) && card_get_value(card) == 0) 474 { 475 // aces go to foundations 476 x1 = FOUNDATIONS_X0 + card_get_suit(card) * FOUNDATIONS_DX; 477 y1 = FOUNDATIONS_Y; 478 } 479 else 480 { 481 // other cards go to the tableau 482 x1 = TABLEAU_X0 + tableau_stack * TABLEAU_DX; 483 y1 = TABLEAU_Y0 + tableau_index * TABLEAU_DY; 484 do { tableau_stack++; } while (tableau_stack * 2 == TABLEAU_NUM_PILES - 1); 485 if (tableau_stack >= TABLEAU_NUM_PILES) 486 { 487 tableau_stack = 0; 488 tableau_index++; 489 } 490 } 491 492 // where was this card last time? 493 unsigned int prev_x; 494 unsigned char prev_y; 495 if (last_dt >= delay + DEAL_ANIM_TRAVEL_TIME) 496 { 497 continue; // if it was already done, there is no more movement 498 } 499 else 500 { 501 // interpolate the previous position 502 const signed int traveling_for = last_dt - delay; 503 const signed int dx = x1 - DEAL_ANIM_DECK_X; 504 const signed char dy = y1 - DEAL_ANIM_DECK_Y; 505 prev_x = DEAL_ANIM_DECK_X + traveling_for * dx / DEAL_ANIM_TRAVEL_TIME; 506 prev_y = DEAL_ANIM_DECK_Y + traveling_for * dy / DEAL_ANIM_TRAVEL_TIME; 507 } 508 509 // is this card done travelling now? 510 unsigned int x; 511 unsigned char y; 512 unsigned char no_redraw; 513 if (frame_dt >= delay + DEAL_ANIM_TRAVEL_TIME) 514 { 515 x = x1; 516 y = y1; 517 no_redraw = 1; 518 } 519 else 520 { 521 // interpolate the current position 522 const signed int traveling_for = frame_dt - delay; 523 const signed int dx = x1 - DEAL_ANIM_DECK_X; 524 const signed char dy = y1 - DEAL_ANIM_DECK_Y; 525 x = DEAL_ANIM_DECK_X + traveling_for * dx / DEAL_ANIM_TRAVEL_TIME; 526 y = DEAL_ANIM_DECK_Y + traveling_for * dy / DEAL_ANIM_TRAVEL_TIME; 527 no_redraw = 0; 528 } 529 530 move_card(prev_x, prev_y, x, y, card, no_redraw); 531 } 532 533 // TODO draw deck 534 535 last_dt = frame_dt; 536 } 537}