Merge pull request #27 from sashakoshka/save-options

Save options
This commit is contained in:
Sasha Koshka
2022-05-06 19:47:46 -04:00
committed by GitHub
8 changed files with 235 additions and 123 deletions

View File

@@ -10,48 +10,34 @@
#include "data.h"
#include "textures.h"
data_Options data_options = { 0 };
data_WorldListItem *data_worldList = NULL;
char directoryName [PATH_MAX] = { 0 };
char settingsFileName [PATH_MAX] = { 0 };
char optionsFileName [PATH_MAX] = { 0 };
char worldsDirectoryName [PATH_MAX] = { 0 };
char screenshotsDirectoryName [PATH_MAX] = { 0 };
char username[8] = "guest";
static uint32_t getSurfacePixel (SDL_Surface *, int, int);
static uint32_t getSurfacePixel (SDL_Surface *, int, int);
static int findDirectoryName (char *, const char *);
/* data_init
* Initializes the data module. Returns zero on success, nonzero on failure.
*/
int data_init () {
int data_init (void) {
int err = 0;
err = data_findDirectoryName(directoryName, "/.m4kc");
err = findDirectoryName(directoryName, "/.m4kc");
if (err) { return err; }
err = data_findDirectoryName(settingsFileName, "/.m4kc/m4kc.conf");
err = findDirectoryName(optionsFileName, "/.m4kc/m4kc.conf");
if (err) { return err; }
err = data_findDirectoryName(worldsDirectoryName, "/.m4kc/worlds");
err = findDirectoryName(worldsDirectoryName, "/.m4kc/worlds");
if (err) { return err; }
err = data_findDirectoryName (
err = findDirectoryName (
screenshotsDirectoryName, "/.m4kc/screenshots");
if (err) { return err; }
data_options = (const data_Options) {
.fogType = 0,
.drawDistance = 20,
.trapMouse = 0,
.fov = 90.0,
.username = (const InputBuffer){
.buffer = username,
.len = 8,
.cursor = 5
}
};
return 0;
}
@@ -109,52 +95,11 @@ int data_ensureDirectoryExists (const char *path) {
return 0;
}
/* data_findDirectoryName
* Concatenates the user's home directory with the specified path. subDirectory
* must begin with a '/'. Uses %APPDATA% instead of home on windows.
/* data_getOptionsFileName
* Returns the file path of the configuration file.
*/
int data_findDirectoryName (char *path, const char *subDirectory) {
if (subDirectory[0] != '/') { return 2; }
#ifdef _WIN32
char *homeDirectory = getenv("APPDATA");
#else
char *homeDirectory = getenv("HOME");
#endif
if (homeDirectory == NULL) { return 3; }
snprintf(path, PATH_MAX, "%s%s", homeDirectory, subDirectory);
// Normalize path
for (char *ch = path; *ch; ch ++) {
if (*ch == '\\') { *ch = '/'; }
}
return 0;
}
/* data_getScreenshotPath
* Writes into path the path for a new screenshot. The name will take the form
* of: snip_YYYY-MM-DD_HH:MM:SS.bmp
* ... and will be located in the path stored in screenshotsDirectoryName. If
* the screenshots directory doesn't exist, this function will create it.
*/
int data_getScreenshotPath (char *path) {
if (data_ensureDirectoryExists(screenshotsDirectoryName)) { return 1; }
time_t unixTime = time(0);
struct tm *timeInfo = localtime(&unixTime);
snprintf (
path, PATH_MAX,
"%s/snip_%04i-%02i-%02i_%02i-%02i-%02i.bmp",
screenshotsDirectoryName,
timeInfo->tm_year + 1900,
timeInfo->tm_mon + 1,
timeInfo->tm_mday,
timeInfo->tm_hour,
timeInfo->tm_min,
timeInfo->tm_sec);
return 0;
char *data_getOptionsFileName (void) {
return optionsFileName;
}
/* data_getWorldPath
@@ -186,11 +131,36 @@ void data_getWorldPlayerPath (
snprintf(path, PATH_MAX, "%s/%s.player", worldPath, name);
}
/* data_getScreenshotPath
* Writes into path the path for a new screenshot. The name will take the form
* of: snip_YYYY-MM-DD_HH:MM:SS.bmp
* ... and will be located in the path stored in screenshotsDirectoryName. If
* the screenshots directory doesn't exist, this function will create it.
*/
int data_getScreenshotPath (char *path) {
if (data_ensureDirectoryExists(screenshotsDirectoryName)) { return 1; }
time_t unixTime = time(0);
struct tm *timeInfo = localtime(&unixTime);
snprintf (
path, PATH_MAX,
"%s/snip_%04i-%02i-%02i_%02i-%02i-%02i.bmp",
screenshotsDirectoryName,
timeInfo->tm_year + 1900,
timeInfo->tm_mon + 1,
timeInfo->tm_mday,
timeInfo->tm_hour,
timeInfo->tm_min,
timeInfo->tm_sec);
return 0;
}
/* data_refreshWorldList
* Regreshes the world list, clearing the previous one. Reads world names and
* thumbnails from ~/.m4kc/worlds
*/
int data_refreshWorldList () {
int data_refreshWorldList (void) {
// Free previous list
data_WorldListItem *item = data_worldList;
while (item != NULL) {
@@ -273,3 +243,26 @@ static uint32_t getSurfacePixel (SDL_Surface *surface, int x, int y) {
+ y * surface->pitch
+ x * surface->format->BytesPerPixel));
}
/* findDirectoryName
* Concatenates the user's home directory with the specified path. subDirectory
* must begin with a '/'. Uses %APPDATA% instead of home on windows.
*/
static int findDirectoryName (char *path, const char *subDirectory) {
if (subDirectory[0] != '/') { return 2; }
#ifdef _WIN32
char *homeDirectory = getenv("APPDATA");
#else
char *homeDirectory = getenv("HOME");
#endif
if (homeDirectory == NULL) { return 3; }
snprintf(path, PATH_MAX, "%s%s", homeDirectory, subDirectory);
// Normalize path
for (char *ch = path; *ch; ch ++) {
if (*ch == '\\') { *ch = '/'; }
}
return 0;
}

View File

@@ -15,25 +15,15 @@ typedef struct data_WorldListItem {
extern data_WorldListItem *data_worldList;
typedef struct {
int fogType;
int drawDistance;
int trapMouse;
double fov;
InputBuffer username;
} data_Options;
extern data_Options data_options;
int data_init ();
int data_init (void);
int data_directoryExists (const char *);
int data_fileExists (const char *);
int data_ensureDirectoryExists (const char *);
int data_findDirectoryName (char *, const char *);
int data_getScreenshotPath (char *);
int data_getWorldPath (char *, const char *);
void data_getWorldMetaPath (char *, const char *);
void data_getWorldPlayerPath (char *, const char *, const char *);
char *data_getOptionsFileName (void);
int data_getWorldPath (char *, const char *);
void data_getWorldMetaPath (char *, const char *);
void data_getWorldPlayerPath (char *, const char *, const char *);
int data_getScreenshotPath (char *);
int data_refreshWorldList ();
int data_refreshWorldList (void);

View File

@@ -2,6 +2,7 @@
#include "gameloop.h"
#include "textures.h"
#include "utility.h"
#include "options.h"
#include "blocks.h"
#include "menus.h"
#include "data.h"
@@ -199,7 +200,7 @@ static void gameLoop_gameplay (SDL_Renderer *renderer, Inputs *inputs) {
player->pos.y + 1,
player->pos.z) == BLOCK_WATER;
int effectDrawDistance = data_options.drawDistance;
int effectDrawDistance = options.drawDistance;
// Restrict view distance while in water
if (headInWater) { effectDrawDistance = 10; }
@@ -409,7 +410,7 @@ static void gameLoop_gameplay (SDL_Renderer *renderer, Inputs *inputs) {
selected to the blockSelected variable */
// Decrease fov when in water
double effectFov = data_options.fov;
double effectFov = options.fov;
if (headInWater) { effectFov += 20; }
selectedPass = 0;
@@ -572,11 +573,11 @@ static void gameLoop_gameplay (SDL_Renderer *renderer, Inputs *inputs) {
if (
f33 < f26 && (
(
! data_options.trapMouse
! options.trapMouse
&& pixelX == inputs->mouse.x / BUFFER_SCALE
&& pixelY == inputs->mouse.y / BUFFER_SCALE
) || (
data_options.trapMouse
options.trapMouse
&& pixelX == BUFFER_HALF_W
&& pixelY == BUFFER_HALF_H
)
@@ -612,7 +613,7 @@ static void gameLoop_gameplay (SDL_Renderer *renderer, Inputs *inputs) {
}
// Draw inverted color crosshair
if (data_options.trapMouse && (
if (options.trapMouse && (
(pixelX == BUFFER_HALF_W
&& abs(BUFFER_HALF_H - pixelY) < 4) ||
(pixelY == BUFFER_HALF_H
@@ -627,7 +628,7 @@ static void gameLoop_gameplay (SDL_Renderer *renderer, Inputs *inputs) {
((finalPixelColor >> 16 & 0xFF) * pixelShade) >> 8,
((finalPixelColor >> 8 & 0xFF) * pixelShade) >> 8,
((finalPixelColor & 0xFF) * pixelShade) >> 8,
data_options.fogType ? sqrt(pixelMist) * 16 : pixelMist
options.fogType ? sqrt(pixelMist) * 16 : pixelMist
);
SDL_RenderDrawPoint(renderer, pixelX, pixelY);
@@ -669,7 +670,7 @@ void gameLoop_drawPopup (SDL_Renderer *renderer, Inputs *inputs) {
switch (gamePopup) {
case POPUP_HUD:
// HUD
if (data_options.trapMouse) SDL_SetRelativeMouseMode(1);
if (options.trapMouse) SDL_SetRelativeMouseMode(1);
if (guiOn) popup_hud (
renderer, inputs, &world,
&debugOn, &fps_now, player
@@ -741,7 +742,7 @@ static void gameLoop_processMovement (Inputs *inputs, int inWater) {
// Only process movement controls if there are no active popup
if (gamePopup == 0) {
// Looking around
if (data_options.trapMouse) {
if (options.trapMouse) {
player->hRot += (double)inputs->mouse.x / 64;
player->vRot -= (double)inputs->mouse.y / 64;
} else {

View File

@@ -2,12 +2,13 @@
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include "terrain.h"
#include "textures.h"
#include "gui.h"
#include "gameloop.h"
#include "main.h"
#include "options.h"
#include "terrain.h"
#include "data.h"
#include "main.h"
#include "gui.h"
/* Minecraft 4k, C edition. Version 0.7
*
@@ -68,10 +69,18 @@ int main (int argc, char *argv[]) {
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
//--- initializing modules ---//
int dataInitFailed = data_init();
if (dataInitFailed) {
int err = 0;
err = data_init();
if (err) {
gameLoop_error("Cannot initialize data module.");
}
err = options_init();
if (err) {
gameLoop_error("Cannot initialize options module.");
}
//---- generating assets ----//

View File

@@ -2,6 +2,7 @@
#include "menus.h"
#include "data.h"
#include "blocks.h"
#include "options.h"
#include "gameloop.h"
static int menu_optionsMain (SDL_Renderer *, Inputs *);
@@ -714,7 +715,7 @@ void popup_chat (SDL_Renderer *renderer, Inputs *inputs, uint64_t gameTime) {
// 1: null
static char chatNameConcat[63 + 7 + 2 + 1];
snprintf (chatNameConcat, 63 + 7 + 2, "%s: %s",
data_options.username.buffer, chatBox.buffer);
options.username.buffer, chatBox.buffer);
// Add input to chat
chatAdd(chatNameConcat);
@@ -1091,8 +1092,8 @@ static int menu_optionsMain (SDL_Renderer *renderer, Inputs *inputs) {
switch (page) {
case 0:
manageInputBuffer(&data_options.username, inputs);
input (renderer, "Username", data_options.username.buffer,
manageInputBuffer(&options.username, inputs);
input (renderer, "Username", options.username.buffer,
BUFFER_HALF_W - 64, 20, 128,
inputs->mouse.x, inputs->mouse.y, 1);
@@ -1100,48 +1101,57 @@ static int menu_optionsMain (SDL_Renderer *renderer, Inputs *inputs) {
"Capture Mouse: OFF",
"Capture Mouse: ON"
};
if (button(renderer, trapMouseTexts[data_options.trapMouse],
if (button(renderer, trapMouseTexts[options.trapMouse],
BUFFER_HALF_W - 64, 42, 128,
inputs->mouse.x, inputs->mouse.y) &&
inputs->mouse.left
) {
data_options.trapMouse = !data_options.trapMouse;
options.trapMouse = !options.trapMouse;
}
break;
case 1:
;static char drawDistanceText[] = "Draw distance: 20\0";
;static char drawDistanceText[20] = { 0 };
if (!drawDistanceText[0]) {
snprintf (
drawDistanceText, 20,
"Draw distance: %i",
options.drawDistance);
}
if (button(renderer, drawDistanceText,
BUFFER_HALF_W - 64, 20, 128,
inputs->mouse.x, inputs->mouse.y) &&
inputs->mouse.left
) {
switch (data_options.drawDistance) {
switch (options.drawDistance) {
case 20:
data_options.drawDistance = 32;
options.drawDistance = 32;
break;
case 32:
data_options.drawDistance = 64;
options.drawDistance = 64;
break;
case 64:
data_options.drawDistance = 96;
options.drawDistance = 96;
break;
case 96:
data_options.drawDistance = 128;
options.drawDistance = 128;
break;
default:
data_options.drawDistance = 20;
options.drawDistance = 20;
break;
}
strnum(drawDistanceText, 15, data_options.drawDistance);
strnum(drawDistanceText, 15, options.drawDistance);
}
static char *fovTexts[] = {
"FOV: Low",
"FOV: Medium",
"FOV: High",
"FOV: ?"
};
char *fovText = NULL;
switch ((int)data_options.fov) {
switch ((int)options.fov) {
default: fovText = fovTexts[3]; break;
case 60: fovText = fovTexts[2]; break;
case 90: fovText = fovTexts[1]; break;
case 140: fovText = fovTexts[0]; break;
@@ -1152,10 +1162,10 @@ static int menu_optionsMain (SDL_Renderer *renderer, Inputs *inputs) {
inputs->mouse.x, inputs->mouse.y) &&
inputs->mouse.left
) {
switch ((int)data_options.fov) {
case 60: data_options.fov = 140; break;
case 90: data_options.fov = 60; break;
default: data_options.fov = 90; break;
switch ((int)options.fov) {
case 60: options.fov = 140; break;
case 90: options.fov = 60; break;
default: options.fov = 90; break;
}
}
@@ -1164,12 +1174,12 @@ static int menu_optionsMain (SDL_Renderer *renderer, Inputs *inputs) {
"Fog: Gradual",
"Fog: Sharp"
};
if (button(renderer, fogTexts[data_options.fogType],
if (button(renderer, fogTexts[options.fogType],
BUFFER_HALF_W - 64, 64, 128,
inputs->mouse.x, inputs->mouse.y) &&
inputs->mouse.left
) {
data_options.fogType = !data_options.fogType;
options.fogType = !options.fogType;
}
break;
@@ -1198,6 +1208,11 @@ static int menu_optionsMain (SDL_Renderer *renderer, Inputs *inputs) {
inputs->mouse.x, inputs->mouse.y) &&
inputs->mouse.left
) {
int err = options_save();
if (err) {
gameLoop_error("Could not save options");
}
page = 0;
return 1;
}

87
src/options.c Normal file
View File

@@ -0,0 +1,87 @@
#include <stdio.h>
#include "data.h"
#include "string.h"
#include "options.h"
Options options = { 0 };
char username[8] = "guest";
/* options_init
* Initializes the options module. Returns zero on success, nonzero on failure.
*/
int options_init (void) {
options = (const Options) {
.fogType = 0,
.drawDistance = 20,
.trapMouse = 0,
.fov = 90.0,
.username = (const InputBuffer){
.buffer = username,
.len = 8,
.cursor = 5
}
};
int err = options_load();
return err;
}
/* options_load
* Loads options from the configuration file into the options struct. Returns 0
* on success, non-zero on failure.
*/
int options_load (void) {
const char *path = data_getOptionsFileName();
// If the file doesn't exist, calmly exit the function and just use
// default values
FILE *file = fopen(path, "r");
if (file == NULL) { return 0; }
int version;
fscanf(file, "%i", &version);
if (version != 0) { return 1; }
char parameter[16];
while (1) {
if (fscanf(file, "%15s", parameter) == EOF) { break; }
#define PARAMETER(what, how, where) \
else if (strcmp(parameter, #what) == 0) { \
fscanf(file, how, where); \
}
PARAMETER(fogType, "%i", &options.fogType)
PARAMETER(drawDistance, "%i", &options.drawDistance)
PARAMETER(trapMouse, "%i", &options.trapMouse)
PARAMETER(fov, "%lf", &options.fov)
PARAMETER(username, "%7s", username)
#undef PARAMETER
}
options.username.cursor = strlen(username);
if (options.fogType > 1 || options.fogType < 0) { options.fogType = 0; }
fclose(file);
return 0;
}
/* options_save
* Saves options from the options struct to the configuration file. Returns 0 on
* success, non-zero on failure.
*/
int options_save (void) {
const char *path = data_getOptionsFileName();
FILE *file = fopen(path, "w");
if (file == NULL) { return 1; }
fprintf(file, "%i\n", 0);
fprintf(file, "fogType %i\n", options.fogType);
fprintf(file, "drawDistance %i\n", options.drawDistance);
fprintf(file, "trapMouse %i\n", options.trapMouse);
fprintf(file, "fov %lf\n", options.fov);
fprintf(file, "username %s\n", username);
fclose(file);
return 0;
}

16
src/options.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
#include "inputbuffer.h"
typedef struct {
int fogType;
int drawDistance;
int trapMouse;
double fov;
InputBuffer username;
} Options;
extern Options options;
int options_init (void);
int options_load (void);
int options_save (void);

View File

@@ -1,8 +1,9 @@
#include <stdio.h>
#include "gameloop.h"
#include "terrain.h"
#include "blocks.h"
#include "data.h"
#include "blocks.h"
#include "terrain.h"
#include "options.h"
#include "gameloop.h"
static void chunkFilePath (World *, char *, int, int, int);
static int Chunk_save (World *, Chunk *);
@@ -60,7 +61,7 @@ int World_save (World *world) {
char playerPath[PATH_MAX];
data_getWorldPlayerPath (
playerPath, world->path,
data_options.username.buffer);
options.username.buffer);
if (Player_save(&world->player, playerPath)) { return 3; }
return 0;
}
@@ -113,7 +114,7 @@ int World_load (World *world, const char *name) {
char playerPath[PATH_MAX];
data_getWorldPlayerPath (
playerPath, world->path,
data_options.username.buffer);
options.username.buffer);
if (data_fileExists(playerPath)) {
Player_load(&world->player, playerPath);
} else {