main.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373
  1. #include "button.h"
  2. #include "strings.h"
  3. #include "textbox.h"
  4. #include "window.h"
  5. #include <SDL_image.h>
  6. #include <SDL_mixer.h>
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #define ERROR(...) fprintf(stderr, __VA_ARGS__)
  10. #define MUSIC_VOLUME 48
  11. /* Resources */
  12. #define DEFINE_RESOURCE(rec_name) extern const char _binary_##rec_name##_start, _binary_##rec_name##_end
  13. #define RESOURCE_DATA(rec_name) ((const void *) &_binary_##rec_name##_start)
  14. #define RESOURCE_SIZE(rec_name) ((size_t) (&_binary_##rec_name##_end - &_binary_##rec_name##_start))
  15. DEFINE_RESOURCE(font_ttf);
  16. DEFINE_RESOURCE(background_png);
  17. DEFINE_RESOURCE(button_png);
  18. DEFINE_RESOURCE(button_pressed_png);
  19. DEFINE_RESOURCE(mute_png);
  20. DEFINE_RESOURCE(volume_png);
  21. DEFINE_RESOURCE(bgm_wav);
  22. DEFINE_RESOURCE(click_wav);
  23. DEFINE_RESOURCE(hover_wav);
  24. DEFINE_RESOURCE(release_wav);
  25. /* Constants */
  26. static const SDL_Color COLOR_BLACK = {0x0, 0x0, 0x0, 0xFF};
  27. static const SDL_Color COLOR_WHITE = {0xFF, 0xFF, 0xFF, 0xFF};
  28. static const ButtonSoundSet NO_BUTTON_SOUNDS = {NULL, NULL, NULL};
  29. /* Assets */
  30. static TTF_Font *font;
  31. static SDL_Texture *backgroundTexture;
  32. static SDL_Texture *buttonTexture;
  33. static SDL_Texture *buttonPressedTexture;
  34. static SDL_Texture *muteTexture;
  35. static SDL_Texture *volumeTexture;
  36. static Mix_Music *bgm;
  37. static ButtonSoundSet buttonSounds = {NULL, NULL, NULL};
  38. /* UI Elements */
  39. static Button *backButton;
  40. static Button *muteButton;
  41. static TextBox *nameBox;
  42. static Button *blackHoleButton;
  43. static TextBox *blackHoleInfo;
  44. static Button *haloButton;
  45. static TextBox *haloInfo;
  46. static Button *lightButton;
  47. static TextBox *lightInfo;
  48. static Button *planetsAndStarsButton;
  49. static TextBox *planetsAndStarsInfo;
  50. static Button *deathButton;
  51. static TextBox *deathInfo;
  52. /* Variables */
  53. static Window *window;
  54. static size_t locationCount;
  55. static Button **locations;
  56. static TextBox *currentBox = NULL;
  57. static int quit = 0;
  58. static int mute = 0;
  59. /* Functions */
  60. static void init(void);
  61. static void loadAssets(void);
  62. static void createUI(void);
  63. static void drawLoop(void);
  64. static void cleanupAssets(void);
  65. static void cleanupObjects(void);
  66. static void addLocation(Button *button);
  67. static SDL_Texture *loadTexture(const char *data, size_t size);
  68. static Mix_Music *loadMusic(const char *data, size_t size);
  69. static Mix_Chunk *loadSound(const char *data, size_t size);
  70. static void backButtonAction(Button *button, Window *window, float x, float y);
  71. static void muteButtonAction(Button *button, Window *window, float x, float y);
  72. static void blackHoleZoomAction(Button *button, Window *window, float x,
  73. float y);
  74. static void haloZoomAction(Button *button, Window *window, float x, float y);
  75. static void lightZoomAction(Button *button, Window *window, float x, float y);
  76. static void planetsAndStarsZoomAction(Button *button, Window *window, float x,
  77. float y);
  78. static void deathZoomAction(Button *button, Window *window, float x, float y);
  79. int main() {
  80. init();
  81. window = windowNew("IC Explorer", 500, 500);
  82. loadAssets();
  83. createUI();
  84. drawLoop();
  85. cleanupObjects();
  86. cleanupAssets();
  87. SDL_Quit();
  88. return 0;
  89. }
  90. void init() {
  91. if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO)) {
  92. ERROR("error: sdl2: %s\n", SDL_GetError());
  93. exit(EXIT_FAILURE);
  94. }
  95. if (!IMG_Init(IMG_INIT_PNG)) {
  96. ERROR("error: sdl2: image: %s\n", IMG_GetError());
  97. SDL_Quit();
  98. exit(EXIT_FAILURE);
  99. }
  100. if (TTF_Init() != 0) {
  101. ERROR("error: sdl2: ttf: %s\n", TTF_GetError());
  102. SDL_Quit();
  103. exit(EXIT_FAILURE);
  104. }
  105. if (Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 2048) != 0) {
  106. ERROR("error: sdl2: mixer: %s\n", Mix_GetError());
  107. SDL_Quit();
  108. exit(EXIT_FAILURE);
  109. }
  110. Mix_VolumeMusic(MUSIC_VOLUME);
  111. locationCount = 0;
  112. locations = malloc(1);
  113. if (!locations) {
  114. ERROR("error: out of memory\n");
  115. SDL_Quit();
  116. exit(EXIT_FAILURE);
  117. }
  118. }
  119. void loadAssets() {
  120. font = TTF_OpenFontRW(SDL_RWFromConstMem(RESOURCE_DATA(font_ttf), RESOURCE_SIZE(font_ttf)), SDL_TRUE, 512);
  121. backgroundTexture = loadTexture(RESOURCE_DATA(background_png), RESOURCE_SIZE(background_png));
  122. buttonTexture = loadTexture(RESOURCE_DATA(button_png), RESOURCE_SIZE(button_png));
  123. buttonPressedTexture = loadTexture(RESOURCE_DATA(button_pressed_png), RESOURCE_SIZE(button_pressed_png));
  124. muteTexture = loadTexture(RESOURCE_DATA(mute_png), RESOURCE_SIZE(mute_png));
  125. volumeTexture = loadTexture(RESOURCE_DATA(volume_png), RESOURCE_SIZE(volume_png));
  126. bgm = loadMusic(RESOURCE_DATA(bgm_wav), RESOURCE_SIZE(bgm_wav));
  127. buttonSounds.hover = loadSound(RESOURCE_DATA(hover_wav), RESOURCE_SIZE(hover_wav));
  128. buttonSounds.down = loadSound(RESOURCE_DATA(click_wav), RESOURCE_SIZE(click_wav));
  129. buttonSounds.up = loadSound(RESOURCE_DATA(release_wav), RESOURCE_SIZE(release_wav));
  130. }
  131. void createUI() {
  132. SDL_SetWindowMinimumSize(window->obj, 500, 500);
  133. window->background = backgroundTexture;
  134. backButton = buttonNew("Back", 0.05f, 0.05f, 0.15f, AUTOMATIC_SCALE,
  135. buttonTexture, buttonPressedTexture, COLOR_BLACK,
  136. font, buttonSounds, window);
  137. backButton->action = &backButtonAction;
  138. muteButton = buttonNew(NULL, 0.9f, 0.9f, 0.075f, 0.075f, volumeTexture,
  139. NULL, COLOR_BLACK, font, NO_BUTTON_SOUNDS, window);
  140. muteButton->action = &muteButtonAction;
  141. nameBox = textBoxNew("IC 1101", 0.02, 0.02, 0.07f, COLOR_WHITE, NULL, font,
  142. window);
  143. /* Black Hole */
  144. blackHoleButton =
  145. buttonNew(BLACK_HOLE_BUTTON_TEXT, 0.55f, 0.55f, 0.15f, AUTOMATIC_SCALE,
  146. buttonTexture, buttonPressedTexture, COLOR_BLACK, font,
  147. buttonSounds, window);
  148. blackHoleButton->action = &blackHoleZoomAction;
  149. addLocation(blackHoleButton);
  150. blackHoleInfo = textBoxNew(BLACK_HOLE_INFO_TEXT, 0.6f, 0.3f, 0.04,
  151. COLOR_BLACK, buttonTexture, font, window);
  152. /* Halo */
  153. haloButton = buttonNew(HALO_BUTTON_TEXT, 0.2f, 0.25f, 0.1f, AUTOMATIC_SCALE,
  154. buttonTexture, buttonPressedTexture, COLOR_BLACK,
  155. font, buttonSounds, window);
  156. haloButton->action = &haloZoomAction;
  157. addLocation(haloButton);
  158. haloInfo = textBoxNew(HALO_INFO_TEXT, 0.5f, 0.3f, 0.04, COLOR_BLACK,
  159. buttonTexture, font, window);
  160. /* Light */
  161. lightButton = buttonNew(
  162. LIGHT_BUTTON_TEXT, 0.55f, 0.4f, 0.12f, AUTOMATIC_SCALE, buttonTexture,
  163. buttonPressedTexture, COLOR_BLACK, font, buttonSounds, window);
  164. lightButton->action = &lightZoomAction;
  165. addLocation(lightButton);
  166. lightInfo = textBoxNew(LIGHT_INFO_TEXT, 0.45f, 0.15f, 0.04, COLOR_BLACK,
  167. buttonTexture, font, window);
  168. /* Planets & Stars */
  169. planetsAndStarsButton =
  170. buttonNew(PLANETS_AND_STARS_BUTTON_TEXT, 0.6f, 0.08f, 0.34f,
  171. AUTOMATIC_SCALE, buttonTexture, buttonPressedTexture,
  172. COLOR_BLACK, font, buttonSounds, window);
  173. planetsAndStarsButton->action = &planetsAndStarsZoomAction;
  174. addLocation(planetsAndStarsButton);
  175. planetsAndStarsInfo =
  176. textBoxNew(PLANETS_AND_STARS_INFO_TEXT, 0.05f, 0.3f, 0.04, COLOR_BLACK,
  177. buttonTexture, font, window);
  178. /* Death */
  179. deathButton = buttonNew(
  180. DEATH_BUTTON_TEXT, 0.1f, 0.85f, 0.12f, AUTOMATIC_SCALE, buttonTexture,
  181. buttonPressedTexture, COLOR_BLACK, font, buttonSounds, window);
  182. deathButton->action = &deathZoomAction;
  183. addLocation(deathButton);
  184. deathInfo = textBoxNew(DEATH_INFO_TEXT, 0.5f, 0.15f, 0.04, COLOR_BLACK,
  185. buttonTexture, font, window);
  186. }
  187. void drawLoop() {
  188. while (!quit) {
  189. if (!Mix_PlayingMusic()) {
  190. Mix_FadeInMusic(bgm, 0, 1000);
  191. }
  192. windowDraw(window);
  193. buttonDraw(muteButton, window);
  194. if (window->state == WINDOW_STATE_NORMAL) {
  195. textBoxDraw(nameBox, window);
  196. size_t i;
  197. for (i = 0; i < locationCount; ++i) {
  198. buttonDraw(locations[i], window);
  199. }
  200. } else if (window->state == WINDOW_STATE_ZOOMED) {
  201. buttonDraw(backButton, window);
  202. if (currentBox) {
  203. textBoxDraw(currentBox, window);
  204. }
  205. }
  206. windowUpdate(window);
  207. SDL_Event e;
  208. while (SDL_PollEvent(&e)) {
  209. if (e.type == SDL_QUIT) {
  210. quit = 1;
  211. } else {
  212. buttonProcessInput(muteButton, window, &e);
  213. if (window->state == WINDOW_STATE_NORMAL) {
  214. size_t i;
  215. for (i = 0; i < locationCount; ++i) {
  216. buttonProcessInput(locations[i], window, &e);
  217. }
  218. } else if (window->state == WINDOW_STATE_ZOOMED) {
  219. if (e.type == SDL_KEYDOWN &&
  220. e.key.keysym.sym == SDLK_ESCAPE) {
  221. windowZoomOut(window);
  222. } else {
  223. buttonProcessInput(backButton, window, &e);
  224. }
  225. }
  226. }
  227. }
  228. }
  229. }
  230. void cleanupAssets() {
  231. TTF_CloseFont(font);
  232. SDL_DestroyTexture(backgroundTexture);
  233. SDL_DestroyTexture(buttonTexture);
  234. SDL_DestroyTexture(buttonPressedTexture);
  235. SDL_DestroyTexture(muteTexture);
  236. SDL_DestroyTexture(volumeTexture);
  237. Mix_CloseAudio();
  238. Mix_FreeMusic(bgm);
  239. Mix_FreeChunk(buttonSounds.hover);
  240. Mix_FreeChunk(buttonSounds.down);
  241. Mix_FreeChunk(buttonSounds.up);
  242. }
  243. void cleanupObjects() {
  244. size_t i;
  245. for (i = 0; i < locationCount; ++i) { buttonDelete(locations[i]); }
  246. free(locations);
  247. buttonDelete(backButton);
  248. buttonDelete(muteButton);
  249. textBoxDelete(nameBox);
  250. textBoxDelete(blackHoleInfo);
  251. textBoxDelete(haloInfo);
  252. textBoxDelete(lightInfo);
  253. textBoxDelete(planetsAndStarsInfo);
  254. textBoxDelete(deathInfo);
  255. windowDelete(window);
  256. }
  257. void addLocation(Button *button) {
  258. locations = realloc(locations, sizeof(Button *) * (++locationCount));
  259. if (!locations) {
  260. ERROR("error: out of memory\n");
  261. exit(EXIT_FAILURE);
  262. }
  263. locations[locationCount - 1] = button;
  264. }
  265. SDL_Texture *loadTexture(const char *data, size_t size) {
  266. SDL_Surface *surface = IMG_Load_RW(SDL_RWFromConstMem(data, size), SDL_TRUE);
  267. if (!surface) {
  268. ERROR("error: sdl2: image: %s\n", IMG_GetError());
  269. exit(EXIT_FAILURE);
  270. }
  271. SDL_Texture *texture =
  272. SDL_CreateTextureFromSurface(window->render, surface);
  273. SDL_FreeSurface(surface);
  274. if (!texture) {
  275. ERROR("error: sdl2: %s\n", SDL_GetError());
  276. exit(EXIT_FAILURE);
  277. }
  278. return texture;
  279. }
  280. Mix_Music *loadMusic(const char *data, size_t size) {
  281. Mix_Music *m = Mix_LoadMUS_RW(SDL_RWFromConstMem(data, size), SDL_TRUE);
  282. if (!m) {
  283. ERROR("error: sdl2: mixer: %s\n", Mix_GetError());
  284. exit(EXIT_FAILURE);
  285. }
  286. return m;
  287. }
  288. Mix_Chunk *loadSound(const char *data, size_t size) {
  289. Mix_Chunk *c = Mix_LoadWAV_RW(SDL_RWFromConstMem(data, size), SDL_TRUE);
  290. if (!c) {
  291. ERROR("error: sdl2: mixer: %s\n", Mix_GetError());
  292. exit(EXIT_FAILURE);
  293. }
  294. return c;
  295. }
  296. void backButtonAction(Button *button, Window *window, float x, float y) {
  297. windowZoomOut(window);
  298. }
  299. void muteButtonAction(Button *button, Window *window, float x, float y) {
  300. if (mute) {
  301. Mix_Volume(-1, 128);
  302. Mix_VolumeMusic(MUSIC_VOLUME);
  303. mute = 0;
  304. button->inactive = volumeTexture;
  305. button->current = volumeTexture;
  306. } else {
  307. Mix_Volume(-1, 0);
  308. Mix_VolumeMusic(0);
  309. mute = 1;
  310. button->inactive = muteTexture;
  311. button->current = muteTexture;
  312. }
  313. }
  314. void blackHoleZoomAction(Button *button, Window *window, float x, float y) {
  315. windowZoomTo(window, 0.6f, 0.5f);
  316. currentBox = blackHoleInfo;
  317. }
  318. void haloZoomAction(Button *button, Window *window, float x, float y) {
  319. windowZoomTo(window, 0.25f, 0.3f);
  320. currentBox = haloInfo;
  321. }
  322. void lightZoomAction(Button *button, Window *window, float x, float y) {
  323. windowZoomTo(window, 0.5f, 0.35f);
  324. currentBox = lightInfo;
  325. }
  326. void planetsAndStarsZoomAction(Button *button, Window *window, float x,
  327. float y) {
  328. windowZoomTo(window, 0.55f, 0.03f);
  329. currentBox = planetsAndStarsInfo;
  330. }
  331. void deathZoomAction(Button *button, Window *window, float x, float y) {
  332. windowZoomTo(window, 0.05f, 0.8f);
  333. currentBox = deathInfo;
  334. }