Initial commit
This commit is contained in:
commit
246f5699f9
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
build
|
||||||
|
compile_commands.json
|
42
CMakeLists.txt
Normal file
42
CMakeLists.txt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16.0)
|
||||||
|
|
||||||
|
set(CMAKE_C_STANDARD 90)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
project(IC_Explorer)
|
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
|
||||||
|
pkg_check_modules(SDL2 REQUIRED sdl2 SDL2_mixer SDL2_image SDL2_ttf)
|
||||||
|
|
||||||
|
function(add_resources out_var)
|
||||||
|
set(result)
|
||||||
|
foreach(in_f ${ARGN})
|
||||||
|
file(RELATIVE_PATH src_f ${CMAKE_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/${in_f})
|
||||||
|
set(out_f ${PROJECT_BINARY_DIR}/${in_f}.o)
|
||||||
|
get_filename_component(f_name ${src_f} NAME)
|
||||||
|
get_filename_component(f_dir ${src_f} DIRECTORY)
|
||||||
|
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/${f_dir})
|
||||||
|
add_custom_command(OUTPUT ${out_f}
|
||||||
|
COMMAND ld -r -b binary -o ${out_f} ${f_name}
|
||||||
|
DEPENDS ${in_f}
|
||||||
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/${f_dir}
|
||||||
|
COMMENT "Building resource ${out_f}"
|
||||||
|
VERBATIM)
|
||||||
|
list(APPEND result ${out_f})
|
||||||
|
endforeach()
|
||||||
|
set(${out_var} "${result}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
add_resources(RESOURCES assets/font.ttf assets/background.png
|
||||||
|
assets/button.png assets/button-pressed.png assets/mute.png
|
||||||
|
assets/volume.png assets/bgm.wav assets/hover.wav assets/click.wav
|
||||||
|
assets/release.wav)
|
||||||
|
|
||||||
|
include_directories(${SDL2_INCLUDE_DIRS})
|
||||||
|
link_directories(${SDL2_LIBRARY_DIRS})
|
||||||
|
add_definitions(${SDL2_CFLAGS_OTHER} "-Wall")
|
||||||
|
|
||||||
|
add_executable(IC_Explorer main.c window.c button.c textbox.c ${RESOURCES})
|
||||||
|
|
||||||
|
target_link_libraries(IC_Explorer ${SDL2_LIBRARIES})
|
121
LICENSE
Normal file
121
LICENSE
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
Creative Commons Legal Code
|
||||||
|
|
||||||
|
CC0 1.0 Universal
|
||||||
|
|
||||||
|
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
|
||||||
|
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
|
||||||
|
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
|
||||||
|
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
|
||||||
|
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
|
||||||
|
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
|
||||||
|
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
|
||||||
|
HEREUNDER.
|
||||||
|
|
||||||
|
Statement of Purpose
|
||||||
|
|
||||||
|
The laws of most jurisdictions throughout the world automatically confer
|
||||||
|
exclusive Copyright and Related Rights (defined below) upon the creator
|
||||||
|
and subsequent owner(s) (each and all, an "owner") of an original work of
|
||||||
|
authorship and/or a database (each, a "Work").
|
||||||
|
|
||||||
|
Certain owners wish to permanently relinquish those rights to a Work for
|
||||||
|
the purpose of contributing to a commons of creative, cultural and
|
||||||
|
scientific works ("Commons") that the public can reliably and without fear
|
||||||
|
of later claims of infringement build upon, modify, incorporate in other
|
||||||
|
works, reuse and redistribute as freely as possible in any form whatsoever
|
||||||
|
and for any purposes, including without limitation commercial purposes.
|
||||||
|
These owners may contribute to the Commons to promote the ideal of a free
|
||||||
|
culture and the further production of creative, cultural and scientific
|
||||||
|
works, or to gain reputation or greater distribution for their Work in
|
||||||
|
part through the use and efforts of others.
|
||||||
|
|
||||||
|
For these and/or other purposes and motivations, and without any
|
||||||
|
expectation of additional consideration or compensation, the person
|
||||||
|
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
|
||||||
|
is an owner of Copyright and Related Rights in the Work, voluntarily
|
||||||
|
elects to apply CC0 to the Work and publicly distribute the Work under its
|
||||||
|
terms, with knowledge of his or her Copyright and Related Rights in the
|
||||||
|
Work and the meaning and intended legal effect of CC0 on those rights.
|
||||||
|
|
||||||
|
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||||
|
protected by copyright and related or neighboring rights ("Copyright and
|
||||||
|
Related Rights"). Copyright and Related Rights include, but are not
|
||||||
|
limited to, the following:
|
||||||
|
|
||||||
|
i. the right to reproduce, adapt, distribute, perform, display,
|
||||||
|
communicate, and translate a Work;
|
||||||
|
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||||
|
iii. publicity and privacy rights pertaining to a person's image or
|
||||||
|
likeness depicted in a Work;
|
||||||
|
iv. rights protecting against unfair competition in regards to a Work,
|
||||||
|
subject to the limitations in paragraph 4(a), below;
|
||||||
|
v. rights protecting the extraction, dissemination, use and reuse of data
|
||||||
|
in a Work;
|
||||||
|
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||||
|
European Parliament and of the Council of 11 March 1996 on the legal
|
||||||
|
protection of databases, and under any national implementation
|
||||||
|
thereof, including any amended or successor version of such
|
||||||
|
directive); and
|
||||||
|
vii. other similar, equivalent or corresponding rights throughout the
|
||||||
|
world based on applicable law or treaty, and any national
|
||||||
|
implementations thereof.
|
||||||
|
|
||||||
|
2. Waiver. To the greatest extent permitted by, but not in contravention
|
||||||
|
of, applicable law, Affirmer hereby overtly, fully, permanently,
|
||||||
|
irrevocably and unconditionally waives, abandons, and surrenders all of
|
||||||
|
Affirmer's Copyright and Related Rights and associated claims and causes
|
||||||
|
of action, whether now known or unknown (including existing as well as
|
||||||
|
future claims and causes of action), in the Work (i) in all territories
|
||||||
|
worldwide, (ii) for the maximum duration provided by applicable law or
|
||||||
|
treaty (including future time extensions), (iii) in any current or future
|
||||||
|
medium and for any number of copies, and (iv) for any purpose whatsoever,
|
||||||
|
including without limitation commercial, advertising or promotional
|
||||||
|
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
|
||||||
|
member of the public at large and to the detriment of Affirmer's heirs and
|
||||||
|
successors, fully intending that such Waiver shall not be subject to
|
||||||
|
revocation, rescission, cancellation, termination, or any other legal or
|
||||||
|
equitable action to disrupt the quiet enjoyment of the Work by the public
|
||||||
|
as contemplated by Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
3. Public License Fallback. Should any part of the Waiver for any reason
|
||||||
|
be judged legally invalid or ineffective under applicable law, then the
|
||||||
|
Waiver shall be preserved to the maximum extent permitted taking into
|
||||||
|
account Affirmer's express Statement of Purpose. In addition, to the
|
||||||
|
extent the Waiver is so judged Affirmer hereby grants to each affected
|
||||||
|
person a royalty-free, non transferable, non sublicensable, non exclusive,
|
||||||
|
irrevocable and unconditional license to exercise Affirmer's Copyright and
|
||||||
|
Related Rights in the Work (i) in all territories worldwide, (ii) for the
|
||||||
|
maximum duration provided by applicable law or treaty (including future
|
||||||
|
time extensions), (iii) in any current or future medium and for any number
|
||||||
|
of copies, and (iv) for any purpose whatsoever, including without
|
||||||
|
limitation commercial, advertising or promotional purposes (the
|
||||||
|
"License"). The License shall be deemed effective as of the date CC0 was
|
||||||
|
applied by Affirmer to the Work. Should any part of the License for any
|
||||||
|
reason be judged legally invalid or ineffective under applicable law, such
|
||||||
|
partial invalidity or ineffectiveness shall not invalidate the remainder
|
||||||
|
of the License, and in such case Affirmer hereby affirms that he or she
|
||||||
|
will not (i) exercise any of his or her remaining Copyright and Related
|
||||||
|
Rights in the Work or (ii) assert any associated claims and causes of
|
||||||
|
action with respect to the Work, in either case contrary to Affirmer's
|
||||||
|
express Statement of Purpose.
|
||||||
|
|
||||||
|
4. Limitations and Disclaimers.
|
||||||
|
|
||||||
|
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||||
|
surrendered, licensed or otherwise affected by this document.
|
||||||
|
b. Affirmer offers the Work as-is and makes no representations or
|
||||||
|
warranties of any kind concerning the Work, express, implied,
|
||||||
|
statutory or otherwise, including without limitation warranties of
|
||||||
|
title, merchantability, fitness for a particular purpose, non
|
||||||
|
infringement, or the absence of latent or other defects, accuracy, or
|
||||||
|
the present or absence of errors, whether or not discoverable, all to
|
||||||
|
the greatest extent permissible under applicable law.
|
||||||
|
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||||
|
that may apply to the Work or any use thereof, including without
|
||||||
|
limitation any person's Copyright and Related Rights in the Work.
|
||||||
|
Further, Affirmer disclaims responsibility for obtaining any necessary
|
||||||
|
consents, permissions or other rights required for any use of the
|
||||||
|
Work.
|
||||||
|
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||||
|
party to this document and has no duty or obligation with respect to
|
||||||
|
this CC0 or use of the Work.
|
18
README.md
Normal file
18
README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# IC Explorer
|
||||||
|
A small program for interactively exploring the IC 1101 galaxy. I made this for a project in my 11th grade physics class.
|
||||||
|
|
||||||
|
### Building
|
||||||
|
|
||||||
|
Building is done through CMake (obviously, there is a CMakeLists.txt file).
|
||||||
|
|
||||||
|
Building requires:
|
||||||
|
* PkgConfig for CMake
|
||||||
|
* SDL2
|
||||||
|
* SDL2_mixer
|
||||||
|
* SDL2_ttf
|
||||||
|
* SDL2_image
|
||||||
|
* GNU ld (or similar version with the ability to create binaries from images, etc.)
|
||||||
|
|
||||||
|
### Usage
|
||||||
|
|
||||||
|
Just run the executable, could not be simpler!
|
BIN
assets/background-full.jpg
Normal file
BIN
assets/background-full.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 MiB |
BIN
assets/background.png
Normal file
BIN
assets/background.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.0 MiB |
BIN
assets/background.xcf
Normal file
BIN
assets/background.xcf
Normal file
Binary file not shown.
BIN
assets/bgm.wav
Normal file
BIN
assets/bgm.wav
Normal file
Binary file not shown.
BIN
assets/button-pressed.png
Normal file
BIN
assets/button-pressed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.7 MiB |
BIN
assets/button.png
Normal file
BIN
assets/button.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.6 MiB |
BIN
assets/click.wav
Normal file
BIN
assets/click.wav
Normal file
Binary file not shown.
BIN
assets/font.ttf
Normal file
BIN
assets/font.ttf
Normal file
Binary file not shown.
BIN
assets/hover.wav
Normal file
BIN
assets/hover.wav
Normal file
Binary file not shown.
BIN
assets/mute.png
Normal file
BIN
assets/mute.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.0 KiB |
BIN
assets/mute.xcf
Normal file
BIN
assets/mute.xcf
Normal file
Binary file not shown.
BIN
assets/release.wav
Normal file
BIN
assets/release.wav
Normal file
Binary file not shown.
BIN
assets/volume.png
Normal file
BIN
assets/volume.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
BIN
assets/volume.xcf
Normal file
BIN
assets/volume.xcf
Normal file
Binary file not shown.
170
button.c
Normal file
170
button.c
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#include "button.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define ERROR(...) fprintf(stderr, __VA_ARGS__)
|
||||||
|
|
||||||
|
Button *buttonNew(const char *text, float x, float y, float w, float h,
|
||||||
|
SDL_Texture *inactive, SDL_Texture *active,
|
||||||
|
SDL_Color textColor, TTF_Font *font, ButtonSoundSet sounds,
|
||||||
|
const Window *window) {
|
||||||
|
Button *button = malloc(sizeof(Button));
|
||||||
|
if (!button) {
|
||||||
|
ERROR("error: out of memory\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (text) {
|
||||||
|
button->text = strdup(text);
|
||||||
|
if (!text) {
|
||||||
|
ERROR("error: out of memory\n");
|
||||||
|
free(button);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
button->text = NULL;
|
||||||
|
}
|
||||||
|
button->x = x;
|
||||||
|
button->y = y;
|
||||||
|
button->w = w;
|
||||||
|
button->h = h;
|
||||||
|
button->inactive = inactive;
|
||||||
|
button->active = active;
|
||||||
|
button->current = inactive;
|
||||||
|
button->font = font;
|
||||||
|
button->textColor = textColor;
|
||||||
|
button->sounds = sounds;
|
||||||
|
button->action = NULL;
|
||||||
|
button->renderedText = NULL;
|
||||||
|
button->down = 0;
|
||||||
|
buttonUpdate(button, window);
|
||||||
|
return button;
|
||||||
|
}
|
||||||
|
|
||||||
|
void buttonDelete(Button *button) {
|
||||||
|
if (button->text && strlen(button->text)) {
|
||||||
|
free(button->text);
|
||||||
|
SDL_DestroyTexture(button->renderedText);
|
||||||
|
}
|
||||||
|
free(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
void buttonDraw(const Button *button, const Window *window) {
|
||||||
|
float bw = button->w;
|
||||||
|
float bh = button->h;
|
||||||
|
int ww, wh;
|
||||||
|
SDL_GetWindowSize(window->obj, &ww, &wh);
|
||||||
|
if (button->w == 0 && button->h == 0) {
|
||||||
|
bh = (float)button->textW / ww;
|
||||||
|
bw = (float)button->textH / wh;
|
||||||
|
} else if (button->w == 0) {
|
||||||
|
bw = (((wh * button->h) * button->textH) / button->textW) / ww;
|
||||||
|
} else if (button->h == 0) {
|
||||||
|
bh = (((ww * button->w) * button->textH) / button->textW) / wh;
|
||||||
|
}
|
||||||
|
SDL_Rect backArea = {ww * button->x, wh * button->y, ww * bw, wh * bh};
|
||||||
|
SDL_RenderCopy(window->render, button->current, NULL, &backArea);
|
||||||
|
if (button->text && strlen(button->text)) {
|
||||||
|
SDL_Rect textArea = {backArea.x * (1.0f + BUTTON_PADDING),
|
||||||
|
backArea.y * (1.0f + BUTTON_PADDING),
|
||||||
|
backArea.w * (1.0f - (2 * BUTTON_PADDING)),
|
||||||
|
backArea.h * (1.0f - (2 * BUTTON_PADDING))};
|
||||||
|
SDL_RenderCopy(window->render, button->renderedText, NULL, &textArea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void buttonUpdate(Button *button, const Window *window) {
|
||||||
|
if (button->renderedText) {
|
||||||
|
SDL_DestroyTexture(button->renderedText);
|
||||||
|
}
|
||||||
|
if (button->text && strlen(button->text)) {
|
||||||
|
SDL_Surface *surface = TTF_RenderText_Blended(
|
||||||
|
button->font, button->text, button->textColor);
|
||||||
|
if (!surface) {
|
||||||
|
ERROR("error: sdl2: ttf: %s\n", TTF_GetError());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
button->textW = surface->w;
|
||||||
|
button->textH = surface->h;
|
||||||
|
button->renderedText =
|
||||||
|
SDL_CreateTextureFromSurface(window->render, surface);
|
||||||
|
SDL_FreeSurface(surface);
|
||||||
|
if (!button->renderedText) {
|
||||||
|
ERROR("error: sdl2: %s\n", SDL_GetError());
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int intersectsButton(const Button *button, float bw, float bh, float x,
|
||||||
|
float y) {
|
||||||
|
return x >= button->x && y >= button->y && x <= button->x + bw &&
|
||||||
|
y <= button->y + bh;
|
||||||
|
}
|
||||||
|
|
||||||
|
void buttonProcessInput(Button *button, Window *window,
|
||||||
|
const SDL_Event *event) {
|
||||||
|
int ww, wh;
|
||||||
|
SDL_GetWindowSize(window->obj, &ww, &wh);
|
||||||
|
float bw = button->w;
|
||||||
|
float bh = button->h;
|
||||||
|
if (button->w == 0 && button->h == 0) {
|
||||||
|
bh = (float)button->textW / ww;
|
||||||
|
bw = (float)button->textH / wh;
|
||||||
|
} else if (button->w == 0) {
|
||||||
|
bw = (((wh * button->h) * button->textH) / button->textW) / ww;
|
||||||
|
} else if (button->h == 0) {
|
||||||
|
bh = (((ww * button->w) * button->textH) / button->textW) / wh;
|
||||||
|
}
|
||||||
|
if (event->type == SDL_MOUSEMOTION) {
|
||||||
|
float x = (float)event->motion.x / ww;
|
||||||
|
float y = (float)event->motion.y / wh;
|
||||||
|
if (intersectsButton(button, bw, bh, x, y)) {
|
||||||
|
if (button->active && button->current != button->active) {
|
||||||
|
if (button->sounds.hover) {
|
||||||
|
Mix_PlayChannel(-1, button->sounds.hover, 0);
|
||||||
|
}
|
||||||
|
button->current = button->active;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (button->current != button->inactive) {
|
||||||
|
if (button->sounds.hover) {
|
||||||
|
Mix_PlayChannel(-1, button->sounds.hover, 0);
|
||||||
|
}
|
||||||
|
button->current = button->inactive;
|
||||||
|
}
|
||||||
|
if (button->down) {
|
||||||
|
button->down = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event->type == SDL_MOUSEBUTTONDOWN &&
|
||||||
|
event->button.button == SDL_BUTTON_LEFT) {
|
||||||
|
float x = (float)event->button.x / ww;
|
||||||
|
float y = (float)event->button.y / wh;
|
||||||
|
if (intersectsButton(button, bw, bh, x, y)) {
|
||||||
|
button->down = 1;
|
||||||
|
if (button->sounds.down) {
|
||||||
|
Mix_PlayChannel(-1, button->sounds.down, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (event->type == SDL_MOUSEBUTTONUP &&
|
||||||
|
event->button.button == SDL_BUTTON_LEFT) {
|
||||||
|
float x = (float)event->button.x / ww;
|
||||||
|
float y = (float)event->button.y / wh;
|
||||||
|
if (intersectsButton(button, bw, bh, x, y) && button->down) {
|
||||||
|
button->down = 0;
|
||||||
|
if (button->sounds.up) {
|
||||||
|
Mix_PlayChannel(-1, button->sounds.up, 0);
|
||||||
|
}
|
||||||
|
if (button->action) {
|
||||||
|
button->action(button, window, x - button->x, y - button->y);
|
||||||
|
}
|
||||||
|
button->current = button->inactive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void buttonGetCenter(const Button *button, Point *point) {
|
||||||
|
point->x = button->x + (button->w / 2);
|
||||||
|
point->y = button->y + (button->w / 2);
|
||||||
|
}
|
56
button.h
Normal file
56
button.h
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
#ifndef IC_EXPLORER_BUTTON_H
|
||||||
|
#define IC_EXPLORER_BUTTON_H
|
||||||
|
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
#include <SDL_mixer.h>
|
||||||
|
#include <SDL_ttf.h>
|
||||||
|
|
||||||
|
#define BUTTON_PADDING 0.005f
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Mix_Chunk *down;
|
||||||
|
Mix_Chunk *up;
|
||||||
|
Mix_Chunk *hover;
|
||||||
|
} ButtonSoundSet;
|
||||||
|
|
||||||
|
struct Button;
|
||||||
|
|
||||||
|
typedef void (*ButtonAction)(struct Button *button, Window *window, float x,
|
||||||
|
float y);
|
||||||
|
|
||||||
|
typedef struct Button {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float w;
|
||||||
|
float h;
|
||||||
|
char *text;
|
||||||
|
SDL_Texture *current;
|
||||||
|
SDL_Texture *inactive;
|
||||||
|
SDL_Texture *active;
|
||||||
|
SDL_Color textColor;
|
||||||
|
TTF_Font *font;
|
||||||
|
SDL_Texture *renderedText;
|
||||||
|
ButtonSoundSet sounds;
|
||||||
|
int textW;
|
||||||
|
int textH;
|
||||||
|
ButtonAction action;
|
||||||
|
int down;
|
||||||
|
} Button;
|
||||||
|
|
||||||
|
Button *buttonNew(const char *text, float x, float y, float w, float h,
|
||||||
|
SDL_Texture *inactive, SDL_Texture *active,
|
||||||
|
SDL_Color textColor, TTF_Font *font, ButtonSoundSet sounds,
|
||||||
|
const Window *window);
|
||||||
|
|
||||||
|
void buttonDelete(Button *button);
|
||||||
|
|
||||||
|
void buttonDraw(const Button *button, const Window *window);
|
||||||
|
|
||||||
|
void buttonUpdate(Button *button, const Window *window);
|
||||||
|
|
||||||
|
void buttonProcessInput(Button *button, Window *window, const SDL_Event *event);
|
||||||
|
|
||||||
|
void buttonGetCenter(const Button *button, Point *point);
|
||||||
|
|
||||||
|
#endif /* IC_EXPLORER_BUTTON_H */
|
372
main.c
Normal file
372
main.c
Normal file
@ -0,0 +1,372 @@
|
|||||||
|
#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;
|
||||||
|
}
|
24
strings.h
Normal file
24
strings.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef IC_EXPLORER_ASSETS_STRINGS_H
|
||||||
|
#define IC_EXPLORER_ASSETS_STRINGS_H
|
||||||
|
|
||||||
|
/* Black Hole */
|
||||||
|
static const char *BLACK_HOLE_BUTTON_TEXT = "Center";
|
||||||
|
static const char *BLACK_HOLE_INFO_TEXT = "In the center of\nIC 1101 there is\na large black hole.\nIt is one of the\nlargest black holes\nknown to exist.\nIt has a mass\nrange of 40-100\nbillion solar masses.\nThis is about \n80*10^39 to\n200*10^39 kg\nof mass!";
|
||||||
|
|
||||||
|
/* Halo */
|
||||||
|
static const char *HALO_BUTTON_TEXT = "Halo";
|
||||||
|
static const char *HALO_INFO_TEXT = "The halo is the fading\n,\"diffused\", light that\nextends beyond the\nmain \"sharp\" light\nradiating from the\ncenter of the galaxy.\nIt extends about 600 kpc\n(kilo-parsecs). That is \naround 2 million light\nyears. This makes it one\n of, if not the, largest\nand most luminous\ngalaxies in the universe!";
|
||||||
|
|
||||||
|
/* Light */
|
||||||
|
static const char *LIGHT_BUTTON_TEXT = "Light";
|
||||||
|
static const char *LIGHT_INFO_TEXT = "It is debated by scientists\nwhat the best way to\nmeasure the size of\na galaxy is. However,\nthe leading way seems to be\nby measuring its \"effective\nradius\". This is the area in\nwhich the galaxy lets off at\nleast half of its total light.\nThis area is different\nfrom the halo of the galaxy.\nIC 1101 has an \"effective\nradius\", of around 43 to\n77 kpc (kilo-parsecs). This\nmeans its radius is about\n173 to 251 light years!";
|
||||||
|
|
||||||
|
/* Planets & Stars */
|
||||||
|
static const char *PLANETS_AND_STARS_BUTTON_TEXT = "Planets & Stars";
|
||||||
|
static const char *PLANETS_AND_STARS_INFO_TEXT = "IC 1101 is filled\nwith about 10 trillion\nstars and countless\nplanets. All of these\nobjecs orbit around\nthe black hole\nlocated in the\ncenter of the galaxy.\nMany of these planets\nand stars also orbit\naround each other\ncreating solar systems\nmuch like the one we\nlive in. It is even\npossible that one of\nthe many planets has\nlife on it!";
|
||||||
|
|
||||||
|
/* Death */
|
||||||
|
static const char *DEATH_BUTTON_TEXT = "Death";
|
||||||
|
static const char *DEATH_INFO_TEXT = "IC 1101 is currently,\nclassified as a \"dying\ngalaxy\". This does not\nmean that there is no\nlife living on its\nplanets. It instead means\nthat it is losing more\nstars than it gains. This,\nhowever, does not mean\nit will die anytime\nsoon. It has likely been\na \"dying galaxy\"\nbefore. It will most\nlikely absorb another\ngalaxy and get the fuel\nit needs to keep\nproducing more stars.";
|
||||||
|
|
||||||
|
#endif /* IC_EXPLORER_ASSETS_STRINGS_H */
|
122
textbox.c
Normal file
122
textbox.c
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
#include "textbox.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define ERROR(...) fprintf(stderr, __VA_ARGS__)
|
||||||
|
|
||||||
|
TextBox *textBoxNew(const char *text, float x, float y, float textSize,
|
||||||
|
SDL_Color color, SDL_Texture *background, TTF_Font *font,
|
||||||
|
const Window *window) {
|
||||||
|
TextBox *box = malloc(sizeof(TextBox));
|
||||||
|
if (!box) {
|
||||||
|
ERROR("error: out of memory\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
box->text = strdup(text);
|
||||||
|
if (!box->text) {
|
||||||
|
ERROR("error: out of memory\n");
|
||||||
|
free(box);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
box->x = x;
|
||||||
|
box->y = y;
|
||||||
|
box->textColor = color;
|
||||||
|
box->background = background;
|
||||||
|
box->font = font;
|
||||||
|
box->textSize = textSize;
|
||||||
|
textBoxUpdate(box, window);
|
||||||
|
return box;
|
||||||
|
}
|
||||||
|
|
||||||
|
void textBoxDelete(TextBox *textBox) {
|
||||||
|
free(textBox->text);
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < textBox->lineCount; ++i) {
|
||||||
|
SDL_DestroyTexture(textBox->renderedText[i]);
|
||||||
|
}
|
||||||
|
free(textBox->renderedText);
|
||||||
|
free(textBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void getLineSizes(TextBox *textBox, int ww, int wh, int *widths,
|
||||||
|
int *width, int *height) {
|
||||||
|
int yInc = textBox->textSize * wh;
|
||||||
|
*width = 0;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < textBox->lineCount; ++i) {
|
||||||
|
int tw, th;
|
||||||
|
SDL_QueryTexture(textBox->renderedText[i], NULL, NULL, &tw, &th);
|
||||||
|
int lw = ((float)tw / (float)th) * (float)yInc;
|
||||||
|
widths[i] = lw;
|
||||||
|
if (lw > *width) {
|
||||||
|
*width = lw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*width += ww * TEXT_BOX_PADDING;
|
||||||
|
*height = yInc * textBox->lineCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void textBoxDraw(TextBox *textBox, const Window *window) {
|
||||||
|
int ww, wh;
|
||||||
|
SDL_GetWindowSize(window->obj, &ww, &wh);
|
||||||
|
int widths[textBox->lineCount];
|
||||||
|
int bw, bh;
|
||||||
|
getLineSizes(textBox, ww, wh, widths, &bw, &bh);
|
||||||
|
SDL_Rect backArea = {textBox->x * ww, textBox->y * wh, bw, bh};
|
||||||
|
if (textBox->background != NULL) {
|
||||||
|
SDL_RenderCopy(window->render, textBox->background, NULL, &backArea);
|
||||||
|
}
|
||||||
|
int yInc = textBox->textSize * wh;
|
||||||
|
size_t i;
|
||||||
|
for (i = 0; i < textBox->lineCount; ++i) {
|
||||||
|
int th;
|
||||||
|
SDL_QueryTexture(textBox->renderedText[i], NULL, NULL, NULL, &th);
|
||||||
|
SDL_Rect area = {backArea.x * (1.0f + TEXT_BOX_PADDING),
|
||||||
|
(backArea.y * (1.0f + TEXT_BOX_PADDING)) + (yInc * i),
|
||||||
|
widths[i] * (1.0f - (2 * TEXT_BOX_PADDING)),
|
||||||
|
yInc * (1.0f - (2 * TEXT_BOX_PADDING))};
|
||||||
|
SDL_RenderCopy(window->render, textBox->renderedText[i], NULL, &area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDL_Texture *renderLine(const char *text, const TextBox *textBox,
|
||||||
|
const Window *window) {
|
||||||
|
SDL_Surface *surface =
|
||||||
|
TTF_RenderText_Blended(textBox->font, text, textBox->textColor);
|
||||||
|
if (!surface) {
|
||||||
|
ERROR("error: sdl2: ttf: %s\n", TTF_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void textBoxUpdate(TextBox *textBox, const Window *window) {
|
||||||
|
textBox->renderedText = malloc(1);
|
||||||
|
if (!textBox->renderedText) {
|
||||||
|
ERROR("error: out of memory\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
char *savePtr;
|
||||||
|
char *line = strtok_r(textBox->text, "\n", &savePtr);
|
||||||
|
for (textBox->lineCount = 0; line != NULL; ++textBox->lineCount) {
|
||||||
|
textBox->renderedText =
|
||||||
|
realloc(textBox->renderedText,
|
||||||
|
sizeof(SDL_Texture *) * (textBox->lineCount + 1));
|
||||||
|
if (!textBox->renderedText) {
|
||||||
|
ERROR("error: out of memory\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
textBox->renderedText[textBox->lineCount] =
|
||||||
|
renderLine(line, textBox, window);
|
||||||
|
line = strtok_r(NULL, "\n", &savePtr);
|
||||||
|
}
|
||||||
|
}
|
32
textbox.h
Normal file
32
textbox.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef IC_EXPLORER_TEXTBOX_H
|
||||||
|
#define IC_EXPLORER_TEXTBOX_H
|
||||||
|
|
||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
#include <SDL_ttf.h>
|
||||||
|
|
||||||
|
#define TEXT_BOX_PADDING 0.005f
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float textSize;
|
||||||
|
SDL_Color textColor;
|
||||||
|
SDL_Texture *background;
|
||||||
|
TTF_Font *font;
|
||||||
|
char *text;
|
||||||
|
size_t lineCount;
|
||||||
|
SDL_Texture **renderedText;
|
||||||
|
} TextBox;
|
||||||
|
|
||||||
|
TextBox *textBoxNew(const char *text, float x, float y, float textSize,
|
||||||
|
SDL_Color color, SDL_Texture *background, TTF_Font *font,
|
||||||
|
const Window *window);
|
||||||
|
|
||||||
|
void textBoxDelete(TextBox *textBox);
|
||||||
|
|
||||||
|
void textBoxDraw(TextBox *textBox, const Window *window);
|
||||||
|
|
||||||
|
void textBoxUpdate(TextBox *textBox, const Window *window);
|
||||||
|
|
||||||
|
#endif /* IC_EXPLORER_TEXTBOX_H */
|
114
window.c
Normal file
114
window.c
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
#include "window.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#define ERROR(...) fprintf(stderr, __VA_ARGS__)
|
||||||
|
|
||||||
|
Window *windowNew(const char *title, int width, int height) {
|
||||||
|
Window *window = malloc(sizeof(Window));
|
||||||
|
if (!window) {
|
||||||
|
ERROR("error: out of memory\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
window->obj = SDL_CreateWindow(title, SDL_WINDOWPOS_UNDEFINED,
|
||||||
|
SDL_WINDOWPOS_UNDEFINED, width, height,
|
||||||
|
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
|
||||||
|
if (!window->obj) {
|
||||||
|
ERROR("error: sdl2: %s\n", SDL_GetError());
|
||||||
|
free(window);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
window->render = SDL_CreateRenderer(
|
||||||
|
window->obj, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
|
||||||
|
if (!window->render) {
|
||||||
|
ERROR("error: sdl2: %s\n", SDL_GetError());
|
||||||
|
SDL_DestroyWindow(window->obj);
|
||||||
|
free(window);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
window->background = NULL;
|
||||||
|
window->focus.x = 0.0f;
|
||||||
|
window->focus.y = 0.0f;
|
||||||
|
window->targetFocus.x = 0.0f;
|
||||||
|
window->targetFocus.y = 0.0f;
|
||||||
|
window->zoom = 1.0f;
|
||||||
|
window->state = WINDOW_STATE_NORMAL;
|
||||||
|
return window;
|
||||||
|
}
|
||||||
|
|
||||||
|
void windowDelete(Window *window) {
|
||||||
|
SDL_DestroyRenderer(window->render);
|
||||||
|
SDL_DestroyWindow(window->obj);
|
||||||
|
free(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zoomRect(const Window *window, SDL_Rect *rect) {
|
||||||
|
int winW, winH;
|
||||||
|
SDL_GetWindowSize(window->obj, &winW, &winH);
|
||||||
|
int texW, texH;
|
||||||
|
SDL_QueryTexture(window->background, NULL, NULL, &texW, &texH);
|
||||||
|
const Point *focus = &window->focus;
|
||||||
|
rect->x = (texW * focus->x) - (texW / window->zoom / 2);
|
||||||
|
rect->y = (texH * focus->y) - (texH / window->zoom / 2);
|
||||||
|
rect->w = texW / window->zoom;
|
||||||
|
rect->h = texH / window->zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
void windowDraw(Window *window) {
|
||||||
|
window->lastTime = window->nowTime;
|
||||||
|
window->nowTime = SDL_GetPerformanceCounter();
|
||||||
|
window->delta = (double)(((window->nowTime - window->lastTime) * 1000) /
|
||||||
|
SDL_GetPerformanceFrequency());
|
||||||
|
SDL_SetRenderDrawColor(window->render, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
|
||||||
|
SDL_RenderClear(window->render);
|
||||||
|
if (window->background) {
|
||||||
|
if (window->state == WINDOW_STATE_ZOOMING_IN) {
|
||||||
|
window->zoom += MAX_ZOOM * (window->delta / ZOOM_TIME);
|
||||||
|
window->focus.x += (window->targetFocus.x - window->startFocus.x) *
|
||||||
|
(window->delta / ZOOM_TIME);
|
||||||
|
window->focus.y += (window->targetFocus.y - window->startFocus.y) *
|
||||||
|
(window->delta / ZOOM_TIME);
|
||||||
|
if (window->zoom >= MAX_ZOOM) {
|
||||||
|
window->state = WINDOW_STATE_ZOOMED;
|
||||||
|
}
|
||||||
|
} else if (window->state == WINDOW_STATE_ZOOMING_OUT) {
|
||||||
|
window->zoom -= MAX_ZOOM * (window->delta / ZOOM_TIME);
|
||||||
|
window->focus.x += (window->targetFocus.x - window->startFocus.x) *
|
||||||
|
(window->delta / ZOOM_TIME);
|
||||||
|
window->focus.y += (window->targetFocus.y - window->startFocus.y) *
|
||||||
|
(window->delta / ZOOM_TIME);
|
||||||
|
if (window->zoom <= 1.0f) {
|
||||||
|
window->state = WINDOW_STATE_NORMAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (window->state == WINDOW_STATE_NORMAL) {
|
||||||
|
SDL_RenderCopy(window->render, window->background, NULL, NULL);
|
||||||
|
} else {
|
||||||
|
SDL_Rect area;
|
||||||
|
zoomRect(window, &area);
|
||||||
|
SDL_RenderCopy(window->render, window->background, &area, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void windowUpdate(Window *window) {
|
||||||
|
SDL_RenderPresent(window->render);
|
||||||
|
}
|
||||||
|
|
||||||
|
void windowZoomTo(Window *window, float x, float y) {
|
||||||
|
window->zoom = 1.0f;
|
||||||
|
window->startFocus.x = 0.5f;
|
||||||
|
window->startFocus.y = 0.5f;
|
||||||
|
window->focus = window->startFocus;
|
||||||
|
window->targetFocus.x = x;
|
||||||
|
window->targetFocus.y = y;
|
||||||
|
window->state = WINDOW_STATE_ZOOMING_IN;
|
||||||
|
}
|
||||||
|
|
||||||
|
void windowZoomOut(Window *window) {
|
||||||
|
window->startFocus = window->focus;
|
||||||
|
window->targetFocus.x = 0.5f;
|
||||||
|
window->targetFocus.y = 0.5f;
|
||||||
|
window->state = WINDOW_STATE_ZOOMING_OUT;
|
||||||
|
}
|
47
window.h
Normal file
47
window.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#ifndef IC_EXPLORER_WINDOW_H
|
||||||
|
#define IC_EXPLORER_WINDOW_H
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#define AUTOMATIC_SCALE 0.0f
|
||||||
|
|
||||||
|
#define MAX_ZOOM 2.0f
|
||||||
|
#define ZOOM_TIME 750
|
||||||
|
|
||||||
|
#define WINDOW_STATE_NORMAL 0
|
||||||
|
#define WINDOW_STATE_ZOOMING_IN 1
|
||||||
|
#define WINDOW_STATE_ZOOMING_OUT 2
|
||||||
|
#define WINDOW_STATE_ZOOMED 3
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
} Point;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
SDL_Window *obj;
|
||||||
|
SDL_Renderer *render;
|
||||||
|
SDL_Texture *background;
|
||||||
|
Point focus;
|
||||||
|
Point targetFocus;
|
||||||
|
Point startFocus;
|
||||||
|
float zoom;
|
||||||
|
int state;
|
||||||
|
double nowTime;
|
||||||
|
double lastTime;
|
||||||
|
double delta;
|
||||||
|
} Window;
|
||||||
|
|
||||||
|
Window *windowNew(const char *title, int width, int height);
|
||||||
|
|
||||||
|
void windowDelete(Window *window);
|
||||||
|
|
||||||
|
void windowDraw(Window *window);
|
||||||
|
|
||||||
|
void windowUpdate(Window *window);
|
||||||
|
|
||||||
|
void windowZoomTo(Window *window, float x, float y);
|
||||||
|
|
||||||
|
void windowZoomOut(Window *window);
|
||||||
|
|
||||||
|
#endif /* IC_EXPLORER_WINDOW_H */
|
Loading…
Reference in New Issue
Block a user