#include "button.h" #include "strings.h" #include "textbox.h" #include "window.h" #include #include #include #include #define ERROR(...) fprintf(stderr, __VA_ARGS__) #define MUSIC_VOLUME 48 /* Resources */ #define DEFINE_RESOURCE(rec_name) extern const char _binary_##rec_name##_start, _binary_##rec_name##_end #define RESOURCE_DATA(rec_name) ((const void *) &_binary_##rec_name##_start) #define RESOURCE_SIZE(rec_name) ((size_t) (&_binary_##rec_name##_end - &_binary_##rec_name##_start)) DEFINE_RESOURCE(font_ttf); DEFINE_RESOURCE(background_png); DEFINE_RESOURCE(button_png); DEFINE_RESOURCE(button_pressed_png); DEFINE_RESOURCE(mute_png); DEFINE_RESOURCE(volume_png); DEFINE_RESOURCE(bgm_wav); DEFINE_RESOURCE(click_wav); DEFINE_RESOURCE(hover_wav); DEFINE_RESOURCE(release_wav); /* Constants */ static const SDL_Color COLOR_BLACK = {0x0, 0x0, 0x0, 0xFF}; static const SDL_Color COLOR_WHITE = {0xFF, 0xFF, 0xFF, 0xFF}; static const ButtonSoundSet NO_BUTTON_SOUNDS = {NULL, NULL, NULL}; /* Assets */ static TTF_Font *font; static SDL_Texture *backgroundTexture; static SDL_Texture *buttonTexture; static SDL_Texture *buttonPressedTexture; static SDL_Texture *muteTexture; static SDL_Texture *volumeTexture; static Mix_Music *bgm; static ButtonSoundSet buttonSounds = {NULL, NULL, NULL}; /* UI Elements */ static Button *backButton; static Button *muteButton; static TextBox *nameBox; static Button *blackHoleButton; static TextBox *blackHoleInfo; static Button *haloButton; static TextBox *haloInfo; static Button *lightButton; static TextBox *lightInfo; static Button *planetsAndStarsButton; static TextBox *planetsAndStarsInfo; static Button *deathButton; static TextBox *deathInfo; /* Variables */ static Window *window; static size_t locationCount; static Button **locations; static TextBox *currentBox = NULL; static int quit = 0; static int mute = 0; /* Functions */ static void init(void); static void loadAssets(void); static void createUI(void); static void drawLoop(void); static void cleanupAssets(void); static void cleanupObjects(void); static void addLocation(Button *button); static SDL_Texture *loadTexture(const char *data, size_t size); static Mix_Music *loadMusic(const char *data, size_t size); static Mix_Chunk *loadSound(const char *data, size_t size); static void backButtonAction(Button *button, Window *window, float x, float y); static void muteButtonAction(Button *button, Window *window, float x, float y); static void blackHoleZoomAction(Button *button, Window *window, float x, float y); static void haloZoomAction(Button *button, Window *window, float x, float y); static void lightZoomAction(Button *button, Window *window, float x, float y); static void planetsAndStarsZoomAction(Button *button, Window *window, float x, float y); static void deathZoomAction(Button *button, Window *window, float x, float y); int main() { init(); window = windowNew("IC Explorer", 500, 500); loadAssets(); createUI(); drawLoop(); cleanupObjects(); cleanupAssets(); SDL_Quit(); return 0; } void init() { if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO)) { ERROR("error: sdl2: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } if (!IMG_Init(IMG_INIT_PNG)) { ERROR("error: sdl2: image: %s\n", IMG_GetError()); SDL_Quit(); exit(EXIT_FAILURE); } if (TTF_Init() != 0) { ERROR("error: sdl2: ttf: %s\n", TTF_GetError()); SDL_Quit(); exit(EXIT_FAILURE); } if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) != 0) { ERROR("error: sdl2: mixer: %s\n", Mix_GetError()); SDL_Quit(); exit(EXIT_FAILURE); } Mix_VolumeMusic(MUSIC_VOLUME); locationCount = 0; locations = malloc(1); if (!locations) { ERROR("error: out of memory\n"); SDL_Quit(); exit(EXIT_FAILURE); } } void loadAssets() { font = TTF_OpenFontRW(SDL_RWFromConstMem(RESOURCE_DATA(font_ttf), RESOURCE_SIZE(font_ttf)), SDL_TRUE, 512); backgroundTexture = loadTexture(RESOURCE_DATA(background_png), RESOURCE_SIZE(background_png)); buttonTexture = loadTexture(RESOURCE_DATA(button_png), RESOURCE_SIZE(button_png)); buttonPressedTexture = loadTexture(RESOURCE_DATA(button_pressed_png), RESOURCE_SIZE(button_pressed_png)); muteTexture = loadTexture(RESOURCE_DATA(mute_png), RESOURCE_SIZE(mute_png)); volumeTexture = loadTexture(RESOURCE_DATA(volume_png), RESOURCE_SIZE(volume_png)); bgm = loadMusic(RESOURCE_DATA(bgm_wav), RESOURCE_SIZE(bgm_wav)); buttonSounds.hover = loadSound(RESOURCE_DATA(hover_wav), RESOURCE_SIZE(hover_wav)); buttonSounds.down = loadSound(RESOURCE_DATA(click_wav), RESOURCE_SIZE(click_wav)); buttonSounds.up = loadSound(RESOURCE_DATA(release_wav), RESOURCE_SIZE(release_wav)); } void createUI() { SDL_SetWindowMinimumSize(window->obj, 500, 500); window->background = backgroundTexture; backButton = buttonNew("Back", 0.05f, 0.05f, 0.15f, AUTOMATIC_SCALE, buttonTexture, buttonPressedTexture, COLOR_BLACK, font, buttonSounds, window); backButton->action = &backButtonAction; muteButton = buttonNew(NULL, 0.9f, 0.9f, 0.075f, 0.075f, volumeTexture, NULL, COLOR_BLACK, font, NO_BUTTON_SOUNDS, window); muteButton->action = &muteButtonAction; nameBox = textBoxNew("IC 1101", 0.02, 0.02, 0.07f, COLOR_WHITE, NULL, font, window); /* Black Hole */ blackHoleButton = buttonNew(BLACK_HOLE_BUTTON_TEXT, 0.55f, 0.55f, 0.15f, AUTOMATIC_SCALE, buttonTexture, buttonPressedTexture, COLOR_BLACK, font, buttonSounds, window); blackHoleButton->action = &blackHoleZoomAction; addLocation(blackHoleButton); blackHoleInfo = textBoxNew(BLACK_HOLE_INFO_TEXT, 0.6f, 0.3f, 0.04, COLOR_BLACK, buttonTexture, font, window); /* Halo */ haloButton = buttonNew(HALO_BUTTON_TEXT, 0.2f, 0.25f, 0.1f, AUTOMATIC_SCALE, buttonTexture, buttonPressedTexture, COLOR_BLACK, font, buttonSounds, window); haloButton->action = &haloZoomAction; addLocation(haloButton); haloInfo = textBoxNew(HALO_INFO_TEXT, 0.5f, 0.3f, 0.04, COLOR_BLACK, buttonTexture, font, window); /* Light */ lightButton = buttonNew( LIGHT_BUTTON_TEXT, 0.55f, 0.4f, 0.12f, AUTOMATIC_SCALE, buttonTexture, buttonPressedTexture, COLOR_BLACK, font, buttonSounds, window); lightButton->action = &lightZoomAction; addLocation(lightButton); lightInfo = textBoxNew(LIGHT_INFO_TEXT, 0.45f, 0.15f, 0.04, COLOR_BLACK, buttonTexture, font, window); /* Planets & Stars */ planetsAndStarsButton = buttonNew(PLANETS_AND_STARS_BUTTON_TEXT, 0.6f, 0.08f, 0.34f, AUTOMATIC_SCALE, buttonTexture, buttonPressedTexture, COLOR_BLACK, font, buttonSounds, window); planetsAndStarsButton->action = &planetsAndStarsZoomAction; addLocation(planetsAndStarsButton); planetsAndStarsInfo = textBoxNew(PLANETS_AND_STARS_INFO_TEXT, 0.05f, 0.3f, 0.04, COLOR_BLACK, buttonTexture, font, window); /* Death */ deathButton = buttonNew( DEATH_BUTTON_TEXT, 0.1f, 0.85f, 0.12f, AUTOMATIC_SCALE, buttonTexture, buttonPressedTexture, COLOR_BLACK, font, buttonSounds, window); deathButton->action = &deathZoomAction; addLocation(deathButton); deathInfo = textBoxNew(DEATH_INFO_TEXT, 0.5f, 0.15f, 0.04, COLOR_BLACK, buttonTexture, font, window); } void drawLoop() { while (!quit) { if (!Mix_PlayingMusic()) { Mix_FadeInMusic(bgm, 0, 1000); } windowDraw(window); buttonDraw(muteButton, window); if (window->state == WINDOW_STATE_NORMAL) { textBoxDraw(nameBox, window); size_t i; for (i = 0; i < locationCount; ++i) { buttonDraw(locations[i], window); } } else if (window->state == WINDOW_STATE_ZOOMED) { buttonDraw(backButton, window); if (currentBox) { textBoxDraw(currentBox, window); } } windowUpdate(window); SDL_Event e; while (SDL_PollEvent(&e)) { if (e.type == SDL_QUIT) { quit = 1; } else { buttonProcessInput(muteButton, window, &e); if (window->state == WINDOW_STATE_NORMAL) { size_t i; for (i = 0; i < locationCount; ++i) { buttonProcessInput(locations[i], window, &e); } } else if (window->state == WINDOW_STATE_ZOOMED) { if (e.type == SDL_KEYDOWN && e.key.keysym.sym == SDLK_ESCAPE) { windowZoomOut(window); } else { buttonProcessInput(backButton, window, &e); } } } } } } void cleanupAssets() { TTF_CloseFont(font); SDL_DestroyTexture(backgroundTexture); SDL_DestroyTexture(buttonTexture); SDL_DestroyTexture(buttonPressedTexture); SDL_DestroyTexture(muteTexture); SDL_DestroyTexture(volumeTexture); Mix_CloseAudio(); Mix_FreeMusic(bgm); Mix_FreeChunk(buttonSounds.hover); Mix_FreeChunk(buttonSounds.down); Mix_FreeChunk(buttonSounds.up); } void cleanupObjects() { size_t i; for (i = 0; i < locationCount; ++i) { buttonDelete(locations[i]); } free(locations); buttonDelete(backButton); buttonDelete(muteButton); textBoxDelete(nameBox); textBoxDelete(blackHoleInfo); textBoxDelete(haloInfo); textBoxDelete(lightInfo); textBoxDelete(planetsAndStarsInfo); textBoxDelete(deathInfo); windowDelete(window); } void addLocation(Button *button) { locations = realloc(locations, sizeof(Button *) * (++locationCount)); if (!locations) { ERROR("error: out of memory\n"); exit(EXIT_FAILURE); } locations[locationCount - 1] = button; } SDL_Texture *loadTexture(const char *data, size_t size) { SDL_Surface *surface = IMG_Load_RW(SDL_RWFromConstMem(data, size), SDL_TRUE); if (!surface) { ERROR("error: sdl2: image: %s\n", IMG_GetError()); exit(EXIT_FAILURE); } SDL_Texture *texture = SDL_CreateTextureFromSurface(window->render, surface); SDL_FreeSurface(surface); if (!texture) { ERROR("error: sdl2: %s\n", SDL_GetError()); exit(EXIT_FAILURE); } return texture; } Mix_Music *loadMusic(const char *data, size_t size) { Mix_Music *m = Mix_LoadMUS_RW(SDL_RWFromConstMem(data, size), SDL_TRUE); if (!m) { ERROR("error: sdl2: mixer: %s\n", Mix_GetError()); exit(EXIT_FAILURE); } return m; } Mix_Chunk *loadSound(const char *data, size_t size) { Mix_Chunk *c = Mix_LoadWAV_RW(SDL_RWFromConstMem(data, size), SDL_TRUE); if (!c) { ERROR("error: sdl2: mixer: %s\n", Mix_GetError()); exit(EXIT_FAILURE); } return c; } void backButtonAction(Button *button, Window *window, float x, float y) { windowZoomOut(window); } void muteButtonAction(Button *button, Window *window, float x, float y) { if (mute) { Mix_Volume(-1, 128); Mix_VolumeMusic(MUSIC_VOLUME); mute = 0; button->inactive = volumeTexture; button->current = volumeTexture; } else { Mix_Volume(-1, 0); Mix_VolumeMusic(0); mute = 1; button->inactive = muteTexture; button->current = muteTexture; } } void blackHoleZoomAction(Button *button, Window *window, float x, float y) { windowZoomTo(window, 0.6f, 0.5f); currentBox = blackHoleInfo; } void haloZoomAction(Button *button, Window *window, float x, float y) { windowZoomTo(window, 0.25f, 0.3f); currentBox = haloInfo; } void lightZoomAction(Button *button, Window *window, float x, float y) { windowZoomTo(window, 0.5f, 0.35f); currentBox = lightInfo; } void planetsAndStarsZoomAction(Button *button, Window *window, float x, float y) { windowZoomTo(window, 0.55f, 0.03f); currentBox = planetsAndStarsInfo; } void deathZoomAction(Button *button, Window *window, float x, float y) { windowZoomTo(window, 0.05f, 0.8f); currentBox = deathInfo; }