189 lines
5.1 KiB
C
189 lines
5.1 KiB
C
#include <SDL3/SDL.h>
|
|
#include <SDL3_image/SDL_image.h>
|
|
#include <SDL3_mixer/SDL_mixer.h>
|
|
#include <SDL3_ttf/SDL_ttf.h>
|
|
#include <stdio.h>
|
|
#include <akerror.h>
|
|
#include <semver.h>
|
|
|
|
#include <akgl/game.h>
|
|
#include <akgl/controller.h>
|
|
#include <akgl/tilemap.h>
|
|
#include <akgl/sprite.h>
|
|
#include <akgl/heap.h>
|
|
#include <akgl/registry.h>
|
|
#include <akgl/staticstring.h>
|
|
#include <akgl/iterator.h>
|
|
#include <akgl/SDL_GameControllerDB.h>
|
|
|
|
SDL_Window *window = NULL;
|
|
SDL_Renderer *renderer = NULL;
|
|
akgl_Frame ball;
|
|
akgl_Frame paddle1;
|
|
akgl_Frame paddle2;
|
|
akgl_Frame table;
|
|
akgl_Tilemap gamemap;
|
|
MIX_Audio *bgm = NULL;
|
|
MIX_Mixer *akgl_mixer = NULL;
|
|
MIX_Track *akgl_tracks[AKGL_GAME_AUDIO_MAX_TRACKS];
|
|
SDL_FRect camera;
|
|
akgl_Game game;
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_init()
|
|
{
|
|
int i = 0;
|
|
PREPARE_ERROR(errctx);
|
|
ATTEMPT {
|
|
game.gameStartTime = SDL_GetTicksNS();
|
|
game.lastIterTime = game.gameStartTime;
|
|
game.lastFPSTime = game.gameStartTime;
|
|
FAIL_ZERO_BREAK(errctx, strlen((char *)&game.name), AKERR_NULLPOINTER, "Must provide game name");
|
|
FAIL_ZERO_BREAK(errctx, strlen((char *)&game.version), AKERR_NULLPOINTER, "Must provide game version");
|
|
FAIL_ZERO_BREAK(errctx, strlen((char *)&game.uri), AKERR_NULLPOINTER, "Must provide game uri");
|
|
CATCH(errctx, akgl_heap_init());
|
|
CATCH(errctx, akgl_registry_init_actor());
|
|
CATCH(errctx, akgl_registry_init_sprite());
|
|
CATCH(errctx, akgl_registry_init_spritesheet());
|
|
CATCH(errctx, akgl_registry_init_character());
|
|
CATCH(errctx, akgl_registry_init_font());
|
|
CATCH(errctx, akgl_registry_init_music());
|
|
CATCH(errctx, akgl_registry_init_properties());
|
|
CATCH(errctx, akgl_registry_init_actor_state_strings());
|
|
} CLEANUP {
|
|
} PROCESS(errctx) {
|
|
} FINISH(errctx, true)
|
|
|
|
SDL_SetAppMetadata(game.name, game.version, game.uri);
|
|
|
|
for ( i = 0 ; i < AKGL_MAX_CONTROL_MAPS; i++ ) {
|
|
memset(&GAME_ControlMaps[i], 0x00, sizeof(akgl_ControlMap));
|
|
}
|
|
|
|
FAIL_ZERO_RETURN(
|
|
errctx,
|
|
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD | SDL_INIT_AUDIO),
|
|
AKGL_ERR_SDL,
|
|
"Couldn't initialize SDL: %s",
|
|
SDL_GetError());
|
|
|
|
// Load the Game Controller DB
|
|
|
|
for ( i = 0; i < AKGL_SDL_GAMECONTROLLER_DB_LEN ; i++ ) {
|
|
if ( SDL_AddGamepadMapping(SDL_GAMECONTROLLER_DB[i]) == -1 ) {
|
|
FAIL_ZERO_RETURN(errctx, 0, AKGL_ERR_SDL, "%s", SDL_GetError());
|
|
}
|
|
}
|
|
|
|
FAIL_ZERO_RETURN(
|
|
errctx,
|
|
SDL_CreateWindowAndRenderer(game.uri, game.screenwidth, game.screenheight, 0, &window, &renderer),
|
|
AKGL_ERR_SDL,
|
|
"Couldn't create window/renderer: %s",
|
|
SDL_GetError());
|
|
|
|
FAIL_ZERO_RETURN(
|
|
errctx,
|
|
MIX_Init(),
|
|
AKGL_ERR_SDL,
|
|
"Couldn't initialize audio: %s",
|
|
SDL_GetError());
|
|
akgl_mixer = MIX_CreateMixerDevice(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, 0);
|
|
FAIL_ZERO_RETURN(
|
|
errctx,
|
|
akgl_mixer,
|
|
AKGL_ERR_SDL,
|
|
"Unable to create mixer device: %s",
|
|
SDL_GetError());
|
|
|
|
FAIL_ZERO_RETURN(
|
|
errctx,
|
|
TTF_Init(),
|
|
AKGL_ERR_SDL,
|
|
"Couldn't initialize front engine: %s",
|
|
SDL_GetError());
|
|
|
|
camera.x = 0;
|
|
camera.y = 0;
|
|
camera.w = game.screenwidth;
|
|
camera.h = game.screenheight;
|
|
SUCCEED(errctx);
|
|
}
|
|
|
|
void akgl_game_updateFPS()
|
|
{
|
|
SDL_Time curTime;
|
|
curTime = SDL_GetTicksNS();
|
|
if ( (curTime - game.lastFPSTime) > AKGL_TIME_ONESEC_NS ) {
|
|
game.fps = game.framesSinceUpdate;
|
|
game.framesSinceUpdate = 0;
|
|
game.lastFPSTime = curTime;
|
|
}
|
|
game.framesSinceUpdate += 1;
|
|
game.lastIterTime = curTime;
|
|
}
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_save(char *fpath)
|
|
{
|
|
FILE *fp = NULL;
|
|
PREPARE_ERROR(errctx);
|
|
|
|
FAIL_ZERO_RETURN(errctx, fpath, AKERR_NULLPOINTER, "NULL file path");
|
|
fp = fopen(fpath, "wb");
|
|
FAIL_ZERO_RETURN(errctx, fp, errno, "%s", fpath);
|
|
fwrite(&game, 1, sizeof(akgl_Game), fp);
|
|
fclose(fp);
|
|
SUCCEED_RETURN(errctx); // SUCCEED_NORETURN if in main().
|
|
}
|
|
|
|
akerr_ErrorContext AKERR_NOIGNORE *akgl_game_load(char *fpath)
|
|
{
|
|
FILE *fp = NULL;
|
|
char versionstr[32];
|
|
semver_t current_version = {};
|
|
semver_t compare_version = {};
|
|
|
|
PREPARE_ERROR(errctx);
|
|
FAIL_ZERO_RETURN(errctx, fpath, AKERR_NULLPOINTER, "NULL file path");
|
|
|
|
ATTEMPT {
|
|
FAIL_NONZERO_BREAK(
|
|
errctx,
|
|
semver_parse((const char *)&game.version, ¤t_version),
|
|
AKERR_VALUE,
|
|
"Invalid semantic version in current game: %s",
|
|
(char *)¤t_version);
|
|
fp = fopen(fpath, "rb");
|
|
FAIL_NONZERO_BREAK(
|
|
errctx,
|
|
(fread((void *)&versionstr, 1, 32, fp) < 32),
|
|
AKERR_IO,
|
|
"Corrupt save file");
|
|
FAIL_NONZERO_BREAK(
|
|
errctx,
|
|
semver_parse((const char *)&versionstr, &compare_version),
|
|
AKERR_VALUE,
|
|
"Invalid semantic version in save game: %s",
|
|
(char *)&versionstr);
|
|
FAIL_ZERO_BREAK(
|
|
errctx,
|
|
semver_satisfies(compare_version, current_version, "^"),
|
|
AKERR_API,
|
|
"Incompatible save game version");
|
|
rewind(fp);
|
|
FAIL_NONZERO_BREAK(
|
|
errctx,
|
|
(fread((void *)&game, 1, sizeof(akgl_Game), fp) < sizeof(akgl_Game)),
|
|
AKERR_IO,
|
|
"Corrupt save file");
|
|
} CLEANUP {
|
|
if ( fp != NULL ) {
|
|
fclose(fp);
|
|
}
|
|
semver_free(¤t_version);
|
|
semver_free(&compare_version);
|
|
} PROCESS(errctx) {
|
|
} FINISH(errctx, true);
|
|
|
|
SUCCEED_RETURN(errctx);
|
|
}
|