#include #include #include "game.h" #include "card.h" #include "teyaku.h" #include "dekiyaku.h" #include "field_multiplier.h" #include "special_cases.h" #include "play.h" Vector2 mouse_pos; char teyaku_calculation[400]; void initialize_game(Game *g) { g->deck.count = 0; g->deck.position = (Vector2) { 800, 400 }; g->deck.display_type = HAND_DISPLAY_DECK; g->should_close = false; g->state = GAME_STATE_INITIALIZING; g->field_multiplier = NULL; g->player.teyaku.calculated = false; g->left.teyaku.calculated = false; g->right.teyaku.calculated = false; g->kan_value = 12; g->current_play_from_hand = NULL; g->current_play_target = NULL; for (int i = 0; i < 48; i++) { CardType t = CHAFF; RibbonType rt = RIBBON_NONE; Month month = i / 4; switch (i) { case 0: case 8: case 28: case 40: case 44: t = BRIGHT; break; case 1: case 5: case 9: t = RIBBON; rt = RIBBON_POETRY; break; case 21: case 33: case 37: t = RIBBON; rt = RIBBON_BLUE; break; case 13: case 17: case 25: case 42: t = RIBBON; rt = RIBBON_PLAIN; break; case 4: case 12: case 16: case 20: case 24: case 29: case 32: case 36: case 41: t = ANIMAL; break; } g->cards[i] = (Card) { i, t, rt, month, { 800, 100 }, false }; g->cards[i].move.end_time = 0.; g->cards[i].move.position = &g->cards[i].position; g->cards[i].move.destination = (Vector2) { 800, 400 }; g->cards[i].order = i; g->deck.cards[i] = &g->cards[i]; g->deck.count++; } shuffle_hand(&g->deck); g->player.points = 10 * g->kan_value; g->right.points = 10 * g->kan_value; g->left.points = 10 * g->kan_value; g->player.points_string[0] = '\0'; g->right.points_string[0] = '\0'; g->left.points_string[0] = '\0'; g->player.hand.count = 0; g->player.hand.position = (Vector2) { 300, 600 }; g->player.hand.display_type = HAND_DISPLAY_ROW; g->right.hand.count = 0; g->right.hand.position = (Vector2) { 750, 125 }; g->right.hand.display_type = HAND_DISPLAY_ROW; g->left.hand.count = 0; g->left.hand.position = (Vector2) { 50, 125 }; g->left.hand.display_type = HAND_DISPLAY_ROW; g->field.count = 0; g->field.position = (Vector2) { 400, 300 }; g->field.display_type = HAND_DISPLAY_FIELD; g->player.scored.count = 0; g->player.scored.position = (Vector2) { 300, 750 }; g->player.scored.display_type = HAND_DISPLAY_SCORED; g->right.scored.count = 0; g->right.scored.position = (Vector2) { 750, 25 }; g->right.scored.display_type = HAND_DISPLAY_SCORED; g->left.scored.count = 0; g->left.scored.position = (Vector2) { 50, 25 }; g->left.scored.display_type = HAND_DISPLAY_SCORED; strcpy(teyaku_calculation, ""); Image cards_image = LoadImage("img/cards.png"); g->cards_texture = LoadTextureFromImage(cards_image); UnloadImage(cards_image); g->state = GAME_STATE_DEALING; } bool stale_calculation = true; void handle_input(Game *g) { switch (g->state) { case GAME_STATE_PLAYER_CHOOSING_FROM_HAND: if (IsMouseButtonPressed(0)) { mouse_pos = GetMousePosition(); for (int i = 0; i < g->player.hand.count; i++) { if (point_within_card(g->player.hand.cards[i], mouse_pos)) { for (int j = 0; j < 48; j++) { g->cards[j].selected = false; } g->player.hand.cards[i]->selected = true; break; } } } break; case GAME_STATE_PLAYER_CHOOSING_TARGET: if (IsMouseButtonPressed(0)) { mouse_pos = GetMousePosition(); for (int i = 0; i < g->player.hand.count; i++) { if (point_within_card(g->player.hand.cards[i], mouse_pos)) { if (g->player.hand.cards[i]->selected) { g->player.hand.cards[i]->selected = false; } else { for (int j = 0; j < g->player.hand.count; j++) { g->player.hand.cards[j]->selected = false; } g->player.hand.cards[i]->selected = true; } } } Card *selected_card = NULL; for (int i = 0; i < g->player.hand.count; i++) { if (g->player.hand.cards[i]->selected) { selected_card = g->player.hand.cards[i]; break; } } for (int i = 0; i < g->field.count; i++) { if (point_within_card(g->field.cards[i], mouse_pos)) { if (selected_card == NULL) printf("No card selected, whoops"); if (valid_play(&g->field, selected_card, g->field.cards[i])) { g->current_play_from_hand = selected_card; g->current_play_target = g->field.cards[i]; } else { printf("Invalid\n"); } break; } } if (CheckCollisionPointRec(mouse_pos, next_card_position(&g->field))) { if (valid_play(&g->field, selected_card, NULL)) { g->current_play_from_hand = selected_card; g->current_play_target = NULL; } else { printf("Invalid\n"); } } } break; default: break; } } bool run_frame_from_deck(Game *g, Hand *to_hand) { Card *top_card = g->deck.cards[g->deck.count - 1]; if (top_card->visible) { Card *target = valid_target(top_card, &g->field); if (target) { remove_from_hand(&g->field, target); add_to_hand(to_hand, target); remove_from_hand(&g->deck, top_card); add_to_hand(to_hand, top_card); } else { remove_from_hand(&g->deck, top_card); add_to_hand(&g->field, top_card); } return true; } else { remove_from_hand(&g->deck, top_card); add_to_hand(&g->deck, top_card); top_card->visible = true; top_card->move.end_time = 0.3; top_card->move.destination.x = top_card->move.destination.x + 100; return false; } } void run_frame_ai_playing(Game *g, Hand *hand, Hand *scored) { Play play = ai_play(hand, &g->field); play.played->visible = true; if (play.target) { remove_from_hand(hand, play.played); add_to_hand(scored, play.played); remove_from_hand(&g->field, play.target); add_to_hand(scored, play.target); } else { remove_from_hand(hand, play.played); add_to_hand(&g->field, play.played); } } void run_frame_dealing(Game *g) { // TODO maybe we only need these once per game kan_points_string(g, g->player.points, g->player.points_string); kan_points_string(g, g->right.points, g->right.points_string); kan_points_string(g, g->left.points, g->left.points_string); if (g->player.hand.count < 4) { deal(&g->deck, &g->player.hand, 4, true); } else if (g->left.hand.count < 4) { deal(&g->deck, &g->left.hand, 4, false); } else if (g->right.hand.count < 4) { deal(&g->deck, &g->right.hand, 4, false); } else if (g->field.count < 3) { deal(&g->deck, &g->field, 3, true); } else if (g->player.hand.count < 7) { deal(&g->deck, &g->player.hand, 3, true); } else if (g->left.hand.count < 7) { deal(&g->deck, &g->left.hand, 3, false); } else if (g->right.hand.count < 7) { deal(&g->deck, &g->right.hand, 3, false); } else if (g->field.count < 6) { deal(&g->deck, &g->field, 3, true); } else { g->state = GAME_STATE_CALCULATING_FIELD_MULTIPLIER; } } void run_frame_calculating_field_multiplier(Game *g) { g->field_multiplier = calculate_field_multiplier(&g->field); g->state = GAME_STATE_CALCULATING_TEYAKU; } void run_frame_calculating_teyaku(Game *g) { calculate_teyaku(g->player.hand, &g->player.teyaku); for (int i = 0; i < 48; i++) { g->cards[i].selected = false; } g->state = GAME_STATE_PLAYER_CHOOSING_FROM_HAND; } void run_frame_player_choosing_from_hand(Game *g) { for (int i = 0; i < g->player.hand.count; i++) { if (g->player.hand.cards[i]->selected) { g->state = GAME_STATE_PLAYER_CHOOSING_TARGET; return; } } } void run_frame_player_choosing_target(Game *g) { bool no_cards_selected = true; for (int i = 0; i < g->player.hand.count; i++) { if (g->player.hand.cards[i]->selected) { no_cards_selected = false; break; } } if (no_cards_selected) { g->state = GAME_STATE_PLAYER_CHOOSING_FROM_HAND; return; } if (g->current_play_from_hand) { if (g->current_play_target) { g->current_play_from_hand->selected = false; remove_from_hand(&g->player.hand, g->current_play_from_hand); add_to_hand(&g->player.scored, g->current_play_from_hand); remove_from_hand(&g->field, g->current_play_target); add_to_hand(&g->player.scored, g->current_play_target); g->current_play_from_hand = NULL; g->current_play_target = NULL; } else { g->current_play_from_hand->selected = false; remove_from_hand(&g->player.hand, g->current_play_from_hand); add_to_hand(&g->field, g->current_play_from_hand); g->current_play_from_hand = NULL; } g->state = GAME_STATE_PLAYER_FROM_DECK; } } void run_frame_player_from_deck(Game *g) { if (run_frame_from_deck(g, &g->player.scored)) { calculate_dekiyaku(&g->player.scored, &g->player.dekiyaku); if (dekiyaku_score(&g->player.dekiyaku) != g->player.dekiyaku_score) { g->player.dekiyaku_score = dekiyaku_score(&g->player.dekiyaku); g->state = GAME_STATE_PLAYER_HAS_DEKIYAKU; } else { g->state = GAME_STATE_RIGHT_PLAYING; } } } void run_frame_player_has_dekiyaku(Game *g) { if (g->player.dekiyaku_action == DEKIYAKU_ACTION_SAGE) { g->state = GAME_STATE_RIGHT_PLAYING; } else if (g->player.dekiyaku_action == DEKIYAKU_ACTION_SHOUBU) { g->state = GAME_STATE_CALCULATING_DEKIYAKU_SCORE; } } void run_frame_right_playing(Game *g) { run_frame_ai_playing(g, &g->right.hand, &g->right.scored); g->state = GAME_STATE_RIGHT_FROM_DECK; } void run_frame_right_from_deck(Game *g) { if (run_frame_from_deck(g, &g->right.scored)) { calculate_dekiyaku(&g->right.scored, &g->right.dekiyaku); g->state = GAME_STATE_LEFT_PLAYING; } } void run_frame_left_playing(Game *g) { run_frame_ai_playing(g, &g->left.hand, &g->left.scored); g->state = GAME_STATE_LEFT_FROM_DECK; } void run_frame_left_from_deck(Game *g) { if (run_frame_from_deck(g, &g->left.scored)) { if (g->player.hand.count) { g->state = GAME_STATE_PLAYER_CHOOSING_FROM_HAND; } else { g->state = GAME_STATE_CALCULATING_SCORES; } } } void run_frame_calculating_scores(Game *g) { printf("Hand scores: %d %d %d\n", hand_points(&g->player.scored), hand_points(&g->right.scored), hand_points(&g->left.scored)); SpecialCase special_case = calculate_special_case(g); switch(special_case.type) { case SPECIAL_CASE_ALL_EIGHTS: printf("All eights! Dealer gets %d kan\n", special_case.score); break; case SPECIAL_CASE_DOUBLE_EIGHTS: case SPECIAL_CASE_SIXTEEN_CHAFF: printf("Double eights or 16 chaff! Player %d gets %d kan\n", special_case.target, special_case.score); switch (special_case.target) { case SPECIAL_CASE_TARGET_PLAYER: transfer_kan(g, &g->player.points, &g->right.points, special_case.score); transfer_kan(g, &g->player.points, &g->left.points, special_case.score); break; case SPECIAL_CASE_TARGET_RIGHT: transfer_kan(g, &g->right.points, &g->player.points, special_case.score); transfer_kan(g, &g->right.points, &g->left.points, special_case.score); break; case SPECIAL_CASE_TARGET_LEFT: transfer_kan(g, &g->left.points, &g->right.points, special_case.score); transfer_kan(g, &g->left.points, &g->player.points, special_case.score); break; } break; default: transfer_points(g, &g->player.points, &g->temp_points, hand_points(&g->player.scored)); transfer_points(g, &g->right.points, &g->temp_points, hand_points(&g->right.scored)); transfer_points(g, &g->left.points, &g->temp_points, hand_points(&g->left.scored)); break; } g->state = GAME_STATE_INITIALIZING; } void run_frame(Game *g) { handle_input(g); float delta = GetFrameTime(); bool done_moving = true; for (int i = 0; i < 48; i++) { move_position(&g->cards[i].move, delta); if (!card_done_moving(&g->cards[i])) done_moving = false; } if (!done_moving) return; switch (g->state) { case GAME_STATE_INITIALIZING: return; case GAME_STATE_DEALING: run_frame_dealing(g); break; case GAME_STATE_CALCULATING_FIELD_MULTIPLIER: run_frame_calculating_field_multiplier(g); break; case GAME_STATE_CALCULATING_TEYAKU: run_frame_calculating_teyaku(g); break; case GAME_STATE_PLAYER_CHOOSING_FROM_HAND: run_frame_player_choosing_from_hand(g); break; case GAME_STATE_PLAYER_CHOOSING_TARGET: run_frame_player_choosing_target(g); break; case GAME_STATE_PLAYER_FROM_DECK: run_frame_player_from_deck(g); break; case GAME_STATE_RIGHT_PLAYING: run_frame_right_playing(g); break; case GAME_STATE_RIGHT_FROM_DECK: run_frame_right_from_deck(g); break; case GAME_STATE_LEFT_PLAYING: run_frame_left_playing(g); break; case GAME_STATE_LEFT_FROM_DECK: run_frame_left_from_deck(g); break; case GAME_STATE_CALCULATING_SCORES: run_frame_calculating_scores(g); break; } } void draw_frame(Game *g) { BeginDrawing(); ClearBackground(RAYWHITE); for (int i = 0; i < 48; i++) { draw_card(&g->cards[i], &g->cards_texture); } if (g->field_multiplier) DrawText(g->field_multiplier->name, 60, 385, 40, BLACK); if (g->player.teyaku.calculated) { char s[200]; teyaku_to_string(&g->player.teyaku, s); DrawText(s, 5, 25, 30, BLACK); } DrawText(g->player.points_string, 40, 700, 20, BLACK); DrawText(g->right.points_string, 40, 750, 20, BLACK); DrawText(g->left.points_string, 40, 800, 20, BLACK); switch (g->state) { case GAME_STATE_PLAYER_CHOOSING_FROM_HAND: DrawText("Choose a card to play", 60, 485, 20, BLACK); break; case GAME_STATE_PLAYER_CHOOSING_TARGET: DrawText("Choose a target on the field", 60, 485, 20, BLACK); DrawRectangleRec(next_card_position(&g->field), BLUE); break; default: break; } EndDrawing(); } void run_until_closing(Game *g) { while (!WindowShouldClose() && !g->should_close) { run_frame(g); draw_frame(g); } } void transfer_points(Game *g, int *to, int *from, int amount) { amount *= g->field_multiplier->value; *to += amount; *from -= amount; } void transfer_kan(Game *g, int *to, int *from, int amount) { amount *= g->kan_value; transfer_points(g, to, from, amount); }