phantasia

Phantasia - 2D SDL3 RPG prototype.
git clone git://git.beep.wimdupont.com/phantasia.git
Log | Files | Refs | README | LICENSE

replay.c (3085B)


      1 #include "game/replay.h"
      2 #include <string.h>
      3 #include "config.h"
      4 
      5 int
      6 ph_replay_header(FILE *file, unsigned int *seed, int writing)
      7 {
      8 	unsigned char header[16] = { 'P', 'H', 'R', 'E', 'P', 'L', 'A', 'Y', 1, 0 };
      9 	unsigned int value = *seed;
     10 	int i;
     11 
     12 	header[10] = PH_SIMULATION_HZ & 255;
     13 	header[11] = (PH_SIMULATION_HZ >> 8) & 255;
     14 	if (writing) {
     15 		for (i = 0; i < 4; ++i) header[12 + i] = (value >> (8 * i)) & 255;
     16 		return fwrite(header, 1, sizeof(header), file) == sizeof(header) ? 0 : -1;
     17 	}
     18 	{
     19 		unsigned char actual[16];
     20 
     21 		if (fread(actual, 1, sizeof(actual), file) != sizeof(actual) ||
     22 				memcmp(actual, header, 12)) return -1;
     23 		value = 0;
     24 		for (i = 0; i < 4; ++i) value |= (unsigned int)actual[12 + i] << (8 * i);
     25 		*seed = value;
     26 	}
     27 	return 0;
     28 }
     29 
     30 int
     31 ph_replay_write(FILE *file, int kind, PhAction action, PhInput input)
     32 {
     33 	unsigned char record[4] = { (unsigned char)kind };
     34 	size_t size = 1;
     35 
     36 	if (kind == PH_REPLAY_ACTION) {
     37 		if (action <= PH_ACTION_NONE || action > PH_ACTION_SPELLBOOK) return -1;
     38 		record[1] = action;
     39 		size = 2;
     40 	} else if (kind == PH_REPLAY_TICK) {
     41 		if (input.move_x < -1 || input.move_x > 1 || input.move_y < -1 ||
     42 				input.move_y > 1 || input.interact < 0 || input.interact > 1) return -1;
     43 		record[1] = input.move_x + 1;
     44 		record[2] = input.move_y + 1;
     45 		record[3] = input.interact;
     46 		size = 4;
     47 	} else if (kind != PH_REPLAY_END) return -1;
     48 	return fwrite(record, 1, size, file) == size ? 0 : -1;
     49 }
     50 
     51 int
     52 ph_replay_read(FILE *file, PhAction *action, PhInput *input)
     53 {
     54 	int kind = fgetc(file), x, y, interact;
     55 
     56 	*action = PH_ACTION_NONE;
     57 	*input = (PhInput){ 0 };
     58 	if (kind == PH_REPLAY_END)
     59 		return fgetc(file) == EOF && !ferror(file) ? PH_REPLAY_END : -1;
     60 	if (kind == PH_REPLAY_ACTION) {
     61 		x = fgetc(file);
     62 		if (x <= PH_ACTION_NONE || x > PH_ACTION_SPELLBOOK) return -1;
     63 		*action = (PhAction)x;
     64 	} else if (kind == PH_REPLAY_TICK) {
     65 		x = fgetc(file);
     66 		y = fgetc(file);
     67 		interact = fgetc(file);
     68 		if (x < 0 || x > 2 || y < 0 || y > 2 || interact < 0 || interact > 1) return -1;
     69 		*input = (PhInput){ .move_x = x - 1, .move_y = y - 1, .interact = interact };
     70 	} else return -1;
     71 	return kind;
     72 }
     73 
     74 int
     75 ph_replay_run(FILE *file, PhGameContext *context, uint64_t *ticks)
     76 {
     77 	PhAction action;
     78 	PhInput input;
     79 	int kind, exited = 0;
     80 
     81 	*ticks = 0;
     82 	while ((kind = ph_replay_read(file, &action, &input)) > 0) {
     83 		PhWorldEvent event;
     84 
     85 		if (exited) return -1;
     86 		if (kind == PH_REPLAY_ACTION) {
     87 			int request = ph_game_input(context, action);
     88 
     89 			if (request & PH_GAME_REQUEST_NEW) return -1;
     90 			exited = request & PH_GAME_REQUEST_EXIT;
     91 		} else {
     92 			if (context->menu != PH_MENU_NONE || *context->mode == PH_GAME_VICTORY ||
     93 					*context->mode == PH_GAME_OVER) return -1;
     94 			ph_game_update(context, input, 1.0f / PH_SIMULATION_HZ, NULL);
     95 			ph_game_interaction(context);
     96 			++*ticks;
     97 		}
     98 		/* Match the live host's consumption of one-shot presentation events. */
     99 		ph_battle_take_event(context->battle);
    100 		context->spellbook->spell_used = 0;
    101 		while (ph_world_take_event(context->world, &event)) { }
    102 	}
    103 	return kind == PH_REPLAY_END ? 0 : -1;
    104 }