#include "beatmapselector.h" #include "beatmap.h" #include "beatmapstore.h" #include "game.h" #include "graphics.h" #include "util.h" #include #include #include #include int Osu_SortBeatmapsByTitle(const void* userdata1, const void* userdata2) { const struct Osu_Beatmap* beatmap1 = *(struct Osu_Beatmap**)userdata1; const struct Osu_Beatmap* beatmap2 = *(struct Osu_Beatmap**)userdata2; SDL_assert(beatmap1->title); SDL_assert(beatmap2->title); return strcmp(beatmap1->title, beatmap2->title); } struct Osu_BeatmapSelector* Osu_CreateBeatmapSelector(struct Osu_Game* game, struct Osu_BeatmapStore* beatmapStore) { struct Osu_BeatmapSelector* selector = calloc(1, sizeof(struct Osu_BeatmapSelector)); selector->beatmaps = calloc(beatmapStore->beatmapsLen, sizeof(struct Osu_BeatmapStore)); selector->beatmapsLen = beatmapStore->beatmapsLen; memcpy(selector->beatmaps, beatmapStore->beatmaps, sizeof(struct Osu_Beatmap*) * selector->beatmapsLen); qsort(selector->beatmaps, selector->beatmapsLen, sizeof(struct Osu_Beatmap*), Osu_SortBeatmapsByTitle); selector->beatmapTexts = calloc(selector->beatmapsLen, sizeof(struct SDL_Text*)); for (size_t i = 0; i < selector->beatmapsLen; i++) { struct Osu_Beatmap* beatmap = selector->beatmaps[i]; char label[512]; snprintf(label, 512, "%s (%s)", beatmap->title, beatmap->version); selector->beatmapTexts[i] = TTF_CreateText(game->graphics->textEngine, game->graphics->font, label, 0); } SDL_SAFECALL(SDL_StartTextInput, game->graphics->window); return selector; } void Osu_DestroyBeatmapSelector(struct Osu_Game* game, struct Osu_BeatmapSelector* selector) { if (selector->beatmapTexts) { for (size_t i = 0; i < selector->beatmapsLen; i++) { TTF_DestroyText(selector->beatmapTexts[i]); } free(selector->beatmapTexts); selector->beatmapTexts = NULL; } SDL_SAFECALL(SDL_StopTextInput, game->graphics->window); free(selector->beatmaps); // Does not own the contained pointers. free(selector); } void Osu_HandleBeatmapSelectorEvent(struct Osu_Game* game, struct Osu_BeatmapSelector* selector, union SDL_Event* event) { if (event->type == SDL_EVENT_MOUSE_WHEEL) { selector->beatmapYOffset += event->wheel.y * 100; if (selector->beatmapYOffset > 0) { selector->beatmapYOffset = 0; } } if (event->type == SDL_EVENT_KEY_UP && event->key.key == SDLK_F2) { Osu_SetSelectedBeatmap(game, game->beatmaps->beatmaps[Osu_Random(0, game->beatmaps->beatmapsLen - 1)]); } if (event->type == SDL_EVENT_TEXT_INPUT) { SDL_strlcat(selector->searchText, event->text.text, sizeof(selector->searchText)); } else if (event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_BACKSPACE) { size_t len = SDL_strlen(selector->searchText); if (len > 0) { const char* cursor = selector->searchText + len; SDL_StepBackUTF8(selector->searchText, &cursor); selector->searchText[cursor - selector->searchText] = 0; } } else if (event->type == SDL_EVENT_KEY_UP && event->key.key == SDLK_RETURN) { Osu_EnterBeatmap(game, selector->selectedBeatmap); } // TODO: This is all very messy and hardcoded, bad! if (event->type == SDL_EVENT_MOUSE_BUTTON_UP) { int screenW, screenH; SDL_GetWindowSizeInPixels(game->graphics->window, &screenW, &screenH); float backgroundW, backgroundH; SDL_GetTextureSize(game->graphics->beatmapWedgeTexture, &backgroundW, &backgroundH); SDL_FRect backgroundRect = { .x = screenW - ((backgroundW / 5) * 4), .y = 0, .w = backgroundW, .h = backgroundH, }; SDL_FPoint mousePoint = { .x = event->button.x * SDL_GetWindowDisplayScale(game->graphics->window), .y = event->button.y * SDL_GetWindowDisplayScale(game->graphics->window), }; size_t drawnIndex = 0; for (size_t i = 0; i < selector->beatmapsLen; i++) { if (strlen(selector->searchText) > 0 && !SDL_strstr(selector->beatmaps[i]->title, selector->searchText)) { continue; } drawnIndex++; int y = selector->beatmapYOffset + drawnIndex * (backgroundH * 0.8); if (y + backgroundH < 0) { continue; } if (y > screenH) { break; } backgroundRect.y = y; if (SDL_PointInRectFloat(&mousePoint, &backgroundRect)) { Osu_SetSelectedBeatmap(game, selector->beatmaps[i]); break; } } } } void Osu_RenderBeatmapSelector(struct Osu_BeatmapSelector* selector, struct Osu_Graphics* graphics) { float wedgeW, wedgeH; SDL_GetTextureSize(graphics->beatmapWedgeTexture, &wedgeW, &wedgeH); struct SDL_FRect backgroundRect = { .x = graphics->screenRect.w - ((wedgeW / 5) * 4), .y = 0, .w = wedgeW, .h = wedgeH, }; struct SDL_FPoint mousePoint; SDL_GetMouseState(&mousePoint.x, &mousePoint.y); mousePoint.x *= SDL_GetWindowDisplayScale(graphics->window); mousePoint.y *= SDL_GetWindowDisplayScale(graphics->window); size_t drawnIndex = 0; for (size_t i = 0; i < selector->beatmapsLen; i++) { if (strlen(selector->searchText) > 0 && !SDL_strstr(selector->beatmaps[i]->title, selector->searchText)) { continue; } drawnIndex++; int y = selector->beatmapYOffset + drawnIndex * (wedgeH * 0.8); if (y + wedgeH < 0) { continue; } if (y > graphics->screenRect.h) { break; } backgroundRect.y = y; if (selector->selectedBeatmap == selector->beatmaps[i]) { SDL_SetTextureColorMod(graphics->beatmapWedgeTexture, 200, 200, 200); } else if (SDL_PointInRectFloat(&mousePoint, &backgroundRect)) { SDL_SetTextureColorMod(graphics->beatmapWedgeTexture, 255, 150, 245); } else { SDL_SetTextureColorMod(graphics->beatmapWedgeTexture, 255, 110, 245); } SDL_RenderTexture(graphics->renderer, graphics->beatmapWedgeTexture, NULL, &backgroundRect); TTF_DrawRendererText(selector->beatmapTexts[i], backgroundRect.x + 255, y + 30); } struct SDL_FRect topTexRect = Osu_GetTextureRect(graphics->songSelectTopTexture); struct SDL_FRect destRect = { .x = 0, .y = 0, .w = topTexRect.w * 2, .h = topTexRect.h * 2, }; SDL_RenderTexture(graphics->renderer, graphics->songSelectTopTexture, &topTexRect, &destRect); }