From a2800fef415a6c1ce89226b9eeb425a59a0834c8 Mon Sep 17 00:00:00 2001 From: John Westhoff Date: Tue, 3 Nov 2020 10:47:53 -0500 Subject: [PATCH] Demo Record + Replay (#150) (#152) * Added demo * Added default demo file --- src/core/args.c | 40 ++++++++++++++++++++++---- src/core/args.h | 2 ++ src/core/demo.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++ src/core/demo.h | 11 ++++++++ src/core/main.c | 3 +- 5 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 src/core/demo.c create mode 100644 src/core/demo.h diff --git a/src/core/args.c b/src/core/args.c index 6c09833..6db84b3 100644 --- a/src/core/args.c +++ b/src/core/args.c @@ -5,19 +5,22 @@ #include #include #include "args.h" - +#include "demo.h" +static unsigned long seed; void parse_args(int argc, char **argv) { - srand(time(NULL)); + int demo_started = 0; + set_seed(time(NULL)); while (1) { struct option long_options[] = { { "help", no_argument, NULL, 'h' }, { "seed", required_argument, NULL, 's' }, + { "play", optional_argument, NULL, 'r' }, + { "record", optional_argument, NULL, 'w' }, { 0, 0, 0, 0 } }; - int c = getopt_long(argc, argv, "hs:", long_options, NULL); - unsigned long seed; + int c = getopt_long(argc, argv, "hs:r?w?", long_options, NULL); if (c == -1) { break; @@ -29,7 +32,10 @@ void parse_args(int argc, char **argv) ("hag, an ncurses procedurally-generated dungeon crawler.\n\n"); printf("Usage: %s [options]\n", argv[0]); printf("-h, --help Show this help message\n"); - printf("-s, --seed RNG seed to use\n"); + printf("-s, --seed RNG seed to use\n"); + printf("-r, --play [file] demo file to replay\n"); + printf("-w, --record [file] file to save demo to\n"); + printf("\n\n--play and --record take an optional file - if these flags are used but no file is given, .demo.hag is used instead.\n"); exit(EXIT_SUCCESS); case 's': seed = strtoul(optarg, NULL, 10); @@ -39,10 +45,32 @@ void parse_args(int argc, char **argv) exit(EXIT_FAILURE); } - srand(seed); + set_seed(seed); + break; + case 'r': + demo_start(DEMO_REPLAY, optarg); + demo_started = 1; + break; + case 'w': + demo_start(DEMO_RECORD, optarg); + demo_started = 1; break; default: exit(EXIT_FAILURE); } } + if (!demo_started) { + demo_start(DEMO_NONE, 0); + } } + +void set_seed(unsigned long s) +{ + seed = s; + srand(s); +} + +unsigned long get_seed(void) +{ + return seed; +} \ No newline at end of file diff --git a/src/core/args.h b/src/core/args.h index dad9e44..f76fb84 100644 --- a/src/core/args.h +++ b/src/core/args.h @@ -1,4 +1,6 @@ #ifndef ARGS_H #define ARGS_H void parse_args(int argc, char **argv); +unsigned long get_seed(void); +void set_seed(unsigned long seed); #endif diff --git a/src/core/demo.c b/src/core/demo.c new file mode 100644 index 0000000..ac14ab7 --- /dev/null +++ b/src/core/demo.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include "demo.h" +#include "args.h" + +static void demo_write_header(void); +static void demo_read_header(void); + +static FILE* read_from = 0; +static FILE* write_to = 0; + +void usleep(long); /* needed because headers are big sad */ + +int demo_next(void) +{ + int move = 0; + if (read_from) { + fread(&move, sizeof(move), 1, read_from); + usleep(1000 /*ms to us*/ * 300 /*ms*/); + } + if (!move) { + move = getch(); + } + if (write_to) { + fwrite(&move, sizeof(move), 1, write_to); + } + return move; +} + +static void demo_read_header() +{ + int demo_version; + unsigned long s; + fread(&demo_version, sizeof(demo_version), 1, read_from); + fread(&s, sizeof(s), 1, read_from); + /* TODO: fail if demo version is wrong */ + set_seed(s); +} + + +static void demo_write_header(void) +{ + unsigned long s = get_seed(); + int demo_version = DEMO_VERSION; + fwrite(&demo_version, sizeof(demo_version), 1, write_to); + fwrite(&s, sizeof(s), 1, write_to); +} + +void demo_start(int mode, char* fname) +{ + if (!fname) { + fname = DEMO_DEFAULT_FILE; + } + if (read_from) { + fclose(read_from); + read_from = 0; + } + if (write_to) { + fclose(write_to); + write_to = 0; + } + switch (mode) { + default: + break; + case DEMO_REPLAY: + read_from = fopen(fname, "r"); + demo_read_header(); + break; + case DEMO_RECORD: + write_to = fopen(fname, "w"); + demo_write_header(); + break; + } +} \ No newline at end of file diff --git a/src/core/demo.h b/src/core/demo.h new file mode 100644 index 0000000..a4db92d --- /dev/null +++ b/src/core/demo.h @@ -0,0 +1,11 @@ +#ifndef DEMO_H +#define DEMO_H +int demo_next(void); +void demo_start(int mode, char* fname); +#define DEMO_NONE 0 +#define DEMO_REPLAY 1 +#define DEMO_RECORD 2 +#define DEMO_VERSION 0 +#define DEMO_DEFAULT_FILE ".demo.hag" +/* TODO: do we need a demo_close? */ +#endif \ No newline at end of file diff --git a/src/core/main.c b/src/core/main.c index d1db82c..fa9ba40 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -6,6 +6,7 @@ #include #include +#include "demo.h" #include "args.h" #include "player.h" #include "colors.h" @@ -126,7 +127,7 @@ int main(int argc, char **argv) xn = xp = player->x; yn = yp = player->y; ch = ERR; - if ((ch = repeat_act) || (ch = getch(), ch != ERR)) { + if ((ch = repeat_act) || (ch = demo_next(), ch != ERR)) { if (rand() % player->luck) { switch (ch) { case KEY_FIGHT: