373 lines
12 KiB
C
373 lines
12 KiB
C
|
#include "button.h"
|
||
|
#include "strings.h"
|
||
|
#include "textbox.h"
|
||
|
#include "window.h"
|
||
|
|
||
|
#include <SDL_image.h>
|
||
|
#include <SDL_mixer.h>
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
|
||
|
#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;
|
||
|
}
|