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