ic-explorer/main.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;
}