Mentions légales du service

Skip to content
Snippets Groups Projects
Commit 30d4fc68 authored by SZCZEPANSKI Marin's avatar SZCZEPANSKI Marin
Browse files

Ajout d'un draft de documentation pour le moteur

parent cd367b11
No related branches found
No related tags found
1 merge request!3Add API for game engine + Easytracker wrapper to record and replay inputs
# 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)`.
......@@ -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);
......
......@@ -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]);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment