From 30d4fc68d4ca54135503dfb5b761305452d20e52 Mon Sep 17 00:00:00 2001
From: mszczepa <marin.szczepanski@inria.fr>
Date: Mon, 23 May 2022 13:26:13 +0200
Subject: [PATCH] Ajout d'un draft de documentation pour le moteur

---
 engines/fullgame/API.md     | 221 ++++++++++++++++++++++++++++++++++++
 levels/interrupteurs/main.c |   3 +-
 levels/interrupteurs/map.c  |   2 +
 3 files changed, 225 insertions(+), 1 deletion(-)
 create mode 100644 engines/fullgame/API.md

diff --git a/engines/fullgame/API.md b/engines/fullgame/API.md
new file mode 100644
index 00000000..2af3a59b
--- /dev/null
+++ b/engines/fullgame/API.md
@@ -0,0 +1,221 @@
+# Draft for the complete game engine API
+
+This is just a draft.
+
+## Including the library and common stuff
+(Implemented)
+Use `#include "agbdentures/agdbentures.h"`.
+
+The *bool exit_main_loop* is defined as static and global variable.
+Use it in your main loop. It will be modified by some functions of the library.
+
+Two basic functions for success and defeat are defined: `level_success(void)` and
+`void level_failure(void)`. They set *exit_main_loop* as true and display a message.  
+Feel free to write your own if you need something more complex.
+
+## Managing map and entities
+(Implemented)
+
+You have access to the following types and definitions:
+```c
+typedef enum {
+    UP,
+    DOWN,
+    LEFT,
+    RIGHT,
+    UNKNOWN
+} direction;
+
+typedef struct {
+    char * name; // shop, overworld, dungeon_Nth_level...
+    tile * floor; // immobile floor
+    entity * entities; // ennemies, NPCs, items, switches... anything used by events
+    int width; // map size
+    int height;
+    int player_x; // player position
+    int player_y;
+    int player_direction;
+} map;
+
+typedef struct {
+    int id; // identifier to display the tile
+    bool obstacle; // whether the player can move on it or not
+} tile;
+
+typedef struc {
+    int id; // identifier to display and use in events
+    int category; // used to group in events, like monster, NPC...
+    void * stats; // any property the entity may have (life points, state...)
+} entity;
+```
+
+For pEdaGoGIcaL PUrpOSeS, the maps are managed using a stack.
+This means it's super cool for creating a donjon, but terribly bad
+if you have any cyclic path in your world.
+
+**`void init_map_stack(void)`**  
+At the beginning, you have to initialize the map stack, by calling `init_map_stack(void)` once.  
+Don't call other map functions before that.  
+
+**`map * load_map(const char * name, int width, int height, int player_x, int player_y, unsigned player_direction)`**  
+Allocates memory for a map, which becomes the current map.  
+After creating the map with this function, you have to set yourself its values for *floor*
+and *entities*. That's because these are too game-dependant.
+But the memory for these two is already allocated.
+
+**`map * unload_map(void)`**  
+Frees the allocated memory of a map, and the previous map
+in the stack becomes the current map. Returns NULL if the unloaded
+map was the last in the stack.  
+Also frees the memory for *floor* and *entities* (and *entities->stats*).
+
+**`map * current_map(void)`**  
+Returns the current map, which is on top of the stack.  
+Returns NULL if there is no map in the stack.  
+
+**`void free_map_stack(void)`**  
+Call `unload_map(void)` for each map.    
+After that the map stack is the same as after calling `ini_map_stack(void)` at the beginning.
+
+**`int coord_idx(int y, int x)`**  
+Utilitary function, returns the number *i* such that *current_map()->floor[i]*
+and *current_map()->entities[i]* correspond to the coordinates (*x*, *y*).
+
+**`int player_idx(void)`**  
+Same but using *current_map()->player_x* and *current_map()->player_y*.
+
+## Managing inputs
+(Not implemented)
+
+The following type is defined:
+```c
+typedef struct Command {
+    char* command_buffer; // the name of the command
+    unsigned n_args; // a number of optional arguments (can be 0)
+    char** args; // the arguments, as many as n_args (NULL if n_args is 0)
+} command;
+```
+
+**`bool init_inputs(const char * filename)`**  
+If *filename* is an empty string "", the inputs will be prompted to the user *via* the standard input.  
+If *filename* is valid, the inputs will be read in this file (one per line, end with a blank line).  
+So each input in an input file must be written like `<input> [args]\n`.  
+Example of an input file (notice the blank line at the end):
+```
+UP
+RIGHT_N 5
+UP
+ATTACK
+
+```
+It should be possible to switch mode or to change the input file during the program execution,
+by calling this function again.  
+Returns true on success, false on failure (if you gave a non-empty *filename* but the
+file could not be opened).
+
+**`command * get_next_input(void)`**  
+Allocates memory for the next user input and returns it.  
+Blocking until the player gives an input,
+because the game engine is simplistic and turn-based.  
+Returns NULL if inputs are being read from a file but the end of it has been reached.
+
+**`void free_input(command * i)`**  
+Frees the allocated memory of an input. To avoid memory leaks
+you should call this once for each call to `get_next_input(void)`.
+
+**`command * get_and_free_input(command * i)`**  
+For your convenience, this function does exactly the same as `get_next_input(void)`
+but in addition it will free *i* if this parameter is not NULL.
+Setting *i* as NULL does the same exact thing is calling `get_next_input(void)`.
+
+If you are reading inputs from a file, you don't have to worry about the memory allocation
+for the file, you only care about the inputs, like in prompt mode.  
+But you have to manage the case where `get_new_input(void)` returns NULL, indicating the end of
+the file has been reached. Moreover, if you stop your program before the end of the file has
+been reached, some memory will be lost. You can avoid this by adding, for example, after your main loop:
+```c
+while (!get_new_input()) {}
+```
+
+## Managing events
+(Implemented)
+
+**`void add_event(void (* event) (void))`**  
+Adds an event to the end of the list. *event* must be a pointer to a function `void event(void)`.  
+If *event* is already in the list, nothing happens.
+
+**`void remove_event(void (* event) (void))`**  
+Removes the given *event* from the list.
+No effect if *event* is not in the list.
+
+**`void remove_all_events(void)`**  
+Removes all events from the list. Please call this at the end of
+your program to free all the memory allocated to events.
+
+**`void apply_events(void)`**  
+Calls all *event* functions added by `add_event(void (* event) (void))`,
+in a fixed order: the events added **last** are called **first**.
+
+Inside a event function, you can call `add_event(void (* event) (void))`,
+`remove_event(void (* event) (void))` (even on itself) and `remove_all_events(void)`.  
+In theory you can call `apply_events(void)`, but this will create
+infinite recursion if the function calling it is also in the event list.
+
+## Functions given for convenience
+todo: monsters and NPC bases
+(Implemented except monsters and NPC)
+
+**`void up(void)`**  
+**`void down(void)`**  
+**`void left(void)`**  
+**`void right(void)`**  
+Moves the player in the given direction and change its orientation.  
+Displays a message if the player can't move because of an obstacle.
+
+**`void up_n(unsigned n)`**  
+**`void down_n(unsigned n)`**  
+**`void left_n(unsigned n)`**  
+**`void right_n(unsigned n)`**  
+Moves *n* times in a given direction.
+
+**`void turn_left(void)`**  
+**`void turn_right(void)`**  
+**`void forward(void)`**  
+**`void forward_n(unsigned n)`**  
+Legacy functions to have the player direction change by a quarter of a turn,
+or to have the player move forward, depending of the current direction faced.
+
+All these functions will modify the values *player_x*, *player_y* and *player_direction*
+of the current map.  
+Functions `*_n(unsigned n)` will call `apply_events(void)` n - 1 times,
+so the main loop stays clean (see [Main Loop](#main-loop)).
+
+Talking, attacking, interacting depend of your game. Code them yourself.
+
+## Inventory
+todo
+
+## Main Loop
+
+The (Holy) Main Loop should look like this:
+```c
+// before: all initialization
+
+while (!exit_main_loop) {
+    command = get_next_input();
+
+    apply_input(command);
+
+    if (!exit_main_loop)
+        apply_events();
+
+    free_input(command);
+}
+
+// after: all frees
+```
+You have to write `apply_input(input * command)` yourself, but it's quite easy.
+
+Here we have the assumption that the game is turn-based, so nothing happens as long as the player doesn't give any input. For real-time games, the call to `get_next_input(void)` will generaly be at the end, and a timer determines when the next frame occurs, whether there has been an input or not. But this game engine is simplistic and turn-based, so `get_next_input(void)` is blocking.
+
+Don't forget to free all the allocated memory at the end, notably by calling `remove_all_events(void)` and `free_map_stack(void)`.
diff --git a/levels/interrupteurs/main.c b/levels/interrupteurs/main.c
index 8451f861..a2ee2b9d 100644
--- a/levels/interrupteurs/main.c
+++ b/levels/interrupteurs/main.c
@@ -74,8 +74,9 @@ void apply_input(Command * c) {
 int main() {
 
     CommandList inputs;
+    char * filename = "input.txt";
 
-    if (read_input_file("input.txt", &inputs)) {
+    if (read_input_file(filename, &inputs)) {
         printf("Error while reading the inputs!\n");
         free_commands(&inputs);
         exit(1);
diff --git a/levels/interrupteurs/map.c b/levels/interrupteurs/map.c
index 2572430a..f2f0371f 100644
--- a/levels/interrupteurs/map.c
+++ b/levels/interrupteurs/map.c
@@ -11,6 +11,7 @@ interactable * create_switch(int x, int y, int n) {
     // @AGDB always return the same switch, so they all light up at once
     interactable * swi = malloc(sizeof(interactable));
     #ifdef template
+    // @AGDB this creates memory leak because the malloc above is never free'd
     swi = &template_swi; /* copy template */
     #else
     swi->state = DEACTIVATED;
@@ -141,6 +142,7 @@ void unload_map (void)
     free(m->floor);
     free(m->name);
 
+    // @AGDB if the bug is stack_alloc, those free are invalid because m->inter[i] is on the stack
     for (int i = 0; i < m->width * m->height; i++) {
         if (m->inter[i] != NULL)
             free(m->inter[i]);
-- 
GitLab