z_mirror/sdl-osu

git clone https://git.raiguard.me/z_mirror/sdl-osu

Caleb HeuerRename Osu_BackToSongSelector to Osu_ExitBeatmapf1c2888

trunk
7.7 KiB245 linesraw
1#include "game.h"
2#include "audio.h"
3#include "beatmap.h"
4#include "beatmapselector.h"
5#include "beatmapstore.h"
6#include "defines.h"
7#include "frametiming.h"
8#include "graphics.h"
9#include "hitobject.h"
10#include "util.h"
11#include <libgen.h>
12#include <SDL3_mixer/SDL_mixer.h>
13#include <SDL3_ttf/SDL_ttf.h>
14#include <stdio.h>
15#include <stdlib.h>
16
17struct Osu_Game* Osu_CreateGame() {
18    struct Osu_Game* game = calloc(1, sizeof(struct Osu_Game));
19
20    const char* basePath;
21    SDL_SAFEASSN(basePath, SDL_GetBasePath);
22    SDL_asprintf(&game->dataPath, "%s", basePath);
23    dirname(game->dataPath);
24
25    game->audio = Osu_CreateAudio(game->dataPath);
26    game->beatmaps = Osu_CreateBeatmapStore(game->dataPath);
27    game->graphics = Osu_CreateGraphics(game->dataPath);
28
29    game->phase = OSU_GAMEPHASE_LOADING_BEATMAPS;
30
31    return game;
32}
33
34void Osu_DestroyGame(struct Osu_Game* game) {
35    if (game->hitObjects) {
36        Osu_DestroyHitObjects(game->hitObjects);
37    }
38
39    if (game->beatmapSelector) {
40        Osu_DestroyBeatmapSelector(game, game->beatmapSelector);
41    }
42
43    Osu_DestroyBeatmapStore(game->beatmaps);
44
45    Osu_DestroyAudio(game->audio);
46
47    Osu_DestroyGraphics(game->graphics);
48
49    free(game->dataPath);
50
51    SDL_Quit();
52
53    free(game);
54}
55
56void Osu_PreUpdateGame(struct Osu_Game* game) {
57    struct Osu_BeatmapStore* beatmaps = game->beatmaps;
58    if (game->phase == OSU_GAMEPHASE_LOADING_BEATMAPS) {
59        SDL_LockMutex(beatmaps->mutex);
60        if (beatmaps->finishedLoading && !game->beatmapSelector) {
61            game->beatmapSelector = Osu_CreateBeatmapSelector(game, game->beatmaps);
62            Osu_SetSelectedBeatmap(game, beatmaps->beatmaps[Osu_Random(0, game->beatmaps->beatmapsLen - 1)]);
63            game->phase = OSU_GAMEPHASE_SONG_SELECTION;
64        }
65        SDL_UnlockMutex(beatmaps->mutex);
66    }
67}
68
69void Osu_UpdateGame(struct Osu_Game* game) {
70    if (game->phase != OSU_GAMEPHASE_IN_GAME) {
71        return;
72    }
73
74    if (game->beatmapPaused) {
75        return;
76    }
77
78    if (game->beatmapTick == 0) {
79        Osu_PlayMusic(game->audio);
80    }
81
82    Osu_UpdateHitObjects(game->hitObjects, game);
83
84    if (game->beatmapTick == 700 && !game->audioSynced) {
85        game->audioSynced = true;
86        game->beatmapTick = MIX_AudioFramesToMS(game->audio->music, MIX_GetTrackPlaybackPosition(game->audio->musicTrack));
87    } else {
88        game->beatmapTick++;
89    }
90}
91
92void Osu_PostUpdateGame(struct Osu_Game* game) {
93    if (game->phase != OSU_GAMEPHASE_IN_GAME) {
94        return;
95    }
96
97    if (game->beatmapTick > 0) {
98        double musicPosition = MIX_TrackFramesToMS(game->audio->musicTrack, MIX_GetTrackPlaybackPosition(game->audio->musicTrack));
99        float audioLatency = musicPosition - game->beatmapTick;
100        game->audioLatencySamples[game->audioLatencySampleIndex] = audioLatency;
101        game->audioLatencySampleIndex = (game->audioLatencySampleIndex + 1) % OSU_AUDIO_LATENCY_SAMPLES;
102        game->musicPosition = (Uint64)musicPosition;
103    }
104
105    if (game->hitObjects->cursor >= game->hitObjects->len) {
106        Osu_ExitBeatmap(game);
107    }
108}
109
110bool Osu_HandleGameEvent(struct Osu_Game* game, union SDL_Event* event) {
111    if (event->type == SDL_EVENT_QUIT) {
112        return true;
113    }
114
115    if (game->beatmapSelector) {
116        Osu_HandleBeatmapSelectorEvent(game, game->beatmapSelector, event);
117    }
118
119    if (game->phase == OSU_GAMEPHASE_IN_GAME) {
120        if (event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_ESCAPE) {
121            Osu_ExitBeatmap(game);
122        } else if (event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_SPACE) {
123            if (game->beatmapPaused) {
124                game->beatmapPaused = false;
125                MIX_ResumeTrack(game->audio->musicTrack);
126            } else {
127                game->beatmapPaused = true;
128                MIX_PauseTrack(game->audio->musicTrack);
129            }
130        }
131    }
132
133    if (event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_EQUALS) {
134        game->graphics->beatmapDimFactor += 10;
135    } else if (event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_MINUS) {
136        game->graphics->beatmapDimFactor -= 10;
137    }
138
139    return false;
140}
141
142void Osu_RenderGame(struct Osu_Game* game) {
143    Osu_PrepareRender(game->graphics);
144
145    SDL_SetRenderDrawColor(game->graphics->renderer, 0, 0, 0, 1);
146    SDL_RenderClear(game->graphics->renderer);
147
148    Osu_RenderWallpaper(game->graphics);
149
150    if (game->phase == OSU_GAMEPHASE_IN_GAME) {
151        SDL_SetRenderDrawBlendMode(game->graphics->renderer, SDL_BLENDMODE_MUL);
152        // TODO: Make adjustable
153        SDL_SetRenderDrawColor(game->graphics->renderer, 0, 0, 0, game->graphics->beatmapDimFactor);
154        SDL_RenderFillRect(game->graphics->renderer, &game->graphics->screenRect);
155    }
156
157    if (game->beatmapSelector) {
158        Osu_RenderBeatmapSelector(game->beatmapSelector, game->graphics);
159    }
160
161    if (game->hitObjects) {
162        Osu_RenderHitObjects(game->hitObjects, game->graphics, game->beatmapTick);
163    }
164
165    Osu_RenderDebugText(game);
166
167    Osu_RenderCursor(game->graphics);
168
169    SDL_RenderPresent(game->graphics->renderer);
170}
171
172void Osu_RenderDebugText(struct Osu_Game* game) {
173    float averageAudioLatency = 0;
174    for (size_t i = 0; i < OSU_AUDIO_LATENCY_SAMPLES; i++) {
175        averageAudioLatency += game->audioLatencySamples[i];
176    }
177    averageAudioLatency /= OSU_AUDIO_LATENCY_SAMPLES;
178
179    SDL_LockMutex(game->beatmaps->mutex);
180    size_t beatmapsLen = game->beatmaps->beatmapsLen;
181    SDL_UnlockMutex(game->beatmaps->mutex);
182
183    char buf[2048];
184    double avgFPS = (double)1000000000 / game->frameTiming.avgFrameDuration;
185    snprintf(buf, sizeof(buf),
186             "FPS: %.2f\nBeatmaps loaded: %zu\nSearch: %s\nTick: %i\nAudio progress: %lu\nLatency: %.2fms",
187             avgFPS, beatmapsLen,
188             game->beatmapSelector ? game->beatmapSelector->searchText : "",
189             game->beatmapTick,
190             game->musicPosition,
191             averageAudioLatency);
192    TTF_SetTextString(game->graphics->debugText, buf, 0);
193
194    int w, h;
195    TTF_GetTextSize(game->graphics->debugText, &w, &h);
196    SDL_FRect backgroundRect = {
197        .x = 5,
198        .y = 5,
199        .w = w + 10,
200        .h = h + 10,
201    };
202    SDL_SetRenderDrawColor(game->graphics->renderer, 0, 0, 0, 255);
203    SDL_RenderFillRect(game->graphics->renderer, &backgroundRect);
204    TTF_DrawRendererText(game->graphics->debugText, 10, 10);
205}
206
207void Osu_SetSelectedBeatmap(struct Osu_Game* game, struct Osu_Beatmap* beatmap) {
208    game->beatmapSelector->selectedBeatmap = beatmap;
209    Osu_SetMusic(game->audio, beatmap->audioFilename, beatmap->previewTime, true);
210    Osu_PlayMusic(game->audio);
211
212    if (beatmap->backgroundPath) {
213        Osu_SetWallpaper(game->graphics, beatmap->backgroundPath);
214    }
215}
216
217void Osu_EnterBeatmap(struct Osu_Game* game, struct Osu_Beatmap* beatmap) {
218    if (game->hitObjects) {
219        Osu_DestroyHitObjects(game->hitObjects);
220    }
221
222    game->activeBeatmap = beatmap;
223    game->hitObjects = Osu_LoadHitObjectsFromBeatmap(beatmap);
224    game->beatmapTick = -beatmap->audioLeadIn - 2000;
225    game->phase = OSU_GAMEPHASE_IN_GAME;
226    Osu_SetMusic(game->audio, beatmap->audioFilename, 0, false);
227
228    memset(game->audioLatencySamples, 0, sizeof(game->audioLatencySamples));
229    game->audioLatencySampleIndex = 0;
230
231    Osu_DestroyBeatmapSelector(game, game->beatmapSelector);
232    game->beatmapSelector = NULL;
233}
234
235void Osu_ExitBeatmap(struct Osu_Game* game) {
236    SDL_assert(game->phase == OSU_GAMEPHASE_IN_GAME);
237    game->phase = OSU_GAMEPHASE_SONG_SELECTION;
238    Osu_DestroyHitObjects(game->hitObjects);
239    game->hitObjects = NULL;
240    game->beatmapSelector = Osu_CreateBeatmapSelector(game, game->beatmaps);
241    Osu_SetSelectedBeatmap(game, game->activeBeatmap);
242    game->activeBeatmap = NULL;
243    game->beatmapTick = 0;
244    game->audioSynced = false;
245}