From f68ebe2eafcff406ab0394036ecbb19560663756 Mon Sep 17 00:00:00 2001 From: mszczepa <marin.szczepanski@inria.fr> Date: Mon, 23 May 2022 13:35:10 +0200 Subject: [PATCH] Fullgame engine as lib + level interr avec la lib --- engines/fullgame/action.c | 171 +++++++------------- engines/fullgame/action.h | 52 +++---- engines/fullgame/agdbentures.h | 6 +- engines/fullgame/common.c | 40 +---- engines/fullgame/common.h | 34 +--- engines/fullgame/events.c | 75 ++++++--- engines/fullgame/events.h | 36 ++--- engines/fullgame/main | Bin 24008 -> 0 bytes engines/fullgame/map.c | 219 +++++++------------------- engines/fullgame/map.h | 59 +++---- engines/fullgame/read_input.c | 150 ++++++++++++------ engines/fullgame/read_input.h | 30 ++-- levels/interrupteurs/Makefile | 7 +- levels/interrupteurs/action.c | 224 --------------------------- levels/interrupteurs/action.h | 62 -------- levels/interrupteurs/agdbentures.h | 9 -- levels/interrupteurs/common.c | 45 ------ levels/interrupteurs/common.h | 46 ------ levels/interrupteurs/custom_map.c | 116 ++++++++++++++ levels/interrupteurs/custom_map.h | 38 +++++ levels/interrupteurs/events.c | 58 ------- levels/interrupteurs/events.h | 37 ----- levels/interrupteurs/events_map.c | 61 ++++---- levels/interrupteurs/events_map.h | 19 +-- levels/interrupteurs/input_manager.c | 57 +++++++ levels/interrupteurs/input_manager.h | 9 ++ levels/interrupteurs/main.c | 66 +++----- levels/interrupteurs/map.c | 208 ------------------------- levels/interrupteurs/map.h | 69 --------- levels/interrupteurs/read_input.c | 83 ---------- levels/interrupteurs/read_input.h | 32 ---- 31 files changed, 626 insertions(+), 1492 deletions(-) delete mode 100755 engines/fullgame/main delete mode 100644 levels/interrupteurs/action.c delete mode 100644 levels/interrupteurs/action.h delete mode 100644 levels/interrupteurs/agdbentures.h delete mode 100644 levels/interrupteurs/common.c delete mode 100644 levels/interrupteurs/common.h create mode 100644 levels/interrupteurs/custom_map.c create mode 100644 levels/interrupteurs/custom_map.h delete mode 100644 levels/interrupteurs/events.c delete mode 100644 levels/interrupteurs/events.h create mode 100644 levels/interrupteurs/input_manager.c create mode 100644 levels/interrupteurs/input_manager.h delete mode 100644 levels/interrupteurs/map.c delete mode 100644 levels/interrupteurs/map.h delete mode 100644 levels/interrupteurs/read_input.c delete mode 100644 levels/interrupteurs/read_input.h diff --git a/engines/fullgame/action.c b/engines/fullgame/action.c index 1e162d54..9c6b7aae 100644 --- a/engines/fullgame/action.c +++ b/engines/fullgame/action.c @@ -21,68 +21,49 @@ #include "action.h" -/***********************************/ -/* Constants, types, variables */ -/***********************************/ - -/***********************************/ -/* Methods and functions */ -/***********************************/ - -void turn_right (void) -{ +void turn_right(void) { + map * m = current_map(); int d; - switch (player_direction) { + switch (m->player_direction) { case UP: d=RIGHT; break; case LEFT: d=UP; break; case DOWN: d=LEFT; break; case RIGHT: d=DOWN; break; default: assert(false); } - player_direction = d; + m->player_direction = d; } -void turn_left (void) -{ + +void turn_left(void) { + map * m = current_map(); int d; - switch (player_direction) { + switch (m->player_direction) { case UP: d=LEFT; break; case LEFT: d=DOWN; break; case DOWN: d=RIGHT; break; case RIGHT: d=UP; break; default: assert(false); } - player_direction = d; + m->player_direction = d; } -void coord_in_dir_n (int y, int x, int dir, int *cy, int *cx, int n) -{ - assert (dir >= 1 && dir <= 9); +void coord_in_dir_n(int y, int x, direction dir, int *cy, int *cx, int n) { // warning: (0, 0) is top left // vertical movement - if (dir >= 7) { - // 7, 8, 9 + if (dir == UP) { *cy = y - n; - /* BUGLINE: forgetting that y = 0 is at the top - * y = player_y + 1; - */ - } else if (dir <= 3) { - // 1, 2, 3 + } else if (dir == DOWN) { *cy = y + n; } else { *cy = y; } - if (dir % 3 == 0) { - // 3, 6, 9 + if (dir == RIGHT) { *cx = x + n; - /* BUGLINE: thinking x is also inverted because y is - * x = pox_x - 1; - */ - } else if (dir % 3 == 1) { - // 1, 4, 7 + } else if (dir == LEFT) { *cx = x - n; } else { *cx = x; @@ -90,101 +71,107 @@ void coord_in_dir_n (int y, int x, int dir, int *cy, int *cx, int n) } -void coord_in_dir (int y, int x, int dir, int *cy, int *cx) -{ - coord_in_dir_n (y, x, dir, cy, cx, 1); +void coord_in_dir(int y, int x, direction dir, int *cy, int *cx) { + coord_in_dir_n(y, x, dir, cy, cx, 1); } -void player_in_dir(int *cy, int *cx) -{ - coord_in_dir (player_y, player_x, player_direction, cy, cx); + +void player_in_dir(int *cy, int *cx) { + map * m = current_map(); + coord_in_dir(m->player_y, m->player_x, m->player_direction, cy, cx); } -void player_in_dir_n(int *cy, int *cx, int n) -{ - coord_in_dir_n (player_y, player_x, player_direction, cy, cx, n); + +void player_in_dir_n(int *cy, int *cx, int n) { + map * m = current_map(); + coord_in_dir_n(m->player_y, m->player_x, m->player_direction, cy, cx, n); } -int player_idx_in_dir(void) -{ +int player_idx_in_dir(void) { int y, x; player_in_dir(&y, &x); - return coord_idx (y, x); + return coord_idx(y, x); } -int player_idx_in_dir_n(int n) -{ + +int player_idx_in_dir_n(int n) { int y, x; player_in_dir_n(&y, &x, n); - return coord_idx (y, x); + return coord_idx(y, x); } + void up() { - player_direction = UP; + current_map()->player_direction = UP; forward(); } + void up_n(unsigned n) { - player_direction = UP; + current_map()->player_direction = UP; forward_n(n); } + void down() { - player_direction = DOWN; + current_map()->player_direction = DOWN; forward(); } + void down_n(unsigned n) { - player_direction = DOWN; + current_map()->player_direction = DOWN; forward_n(n); } + void left() { - player_direction = LEFT; + current_map()->player_direction = LEFT; forward(); } + void left_n(unsigned n) { - player_direction = LEFT; + current_map()->player_direction = LEFT; forward_n(n); } + void right() { - player_direction = RIGHT; + current_map()->player_direction = RIGHT; forward(); } + void right_n(unsigned n) { - player_direction = RIGHT; + current_map()->player_direction = RIGHT; forward_n(n); } -void forward (void) -{ +void forward(void) { int y, x; - coord_in_dir (player_y, player_x, player_direction, &y, &x); + map * m = current_map(); + coord_in_dir(m->player_y, m->player_x, m->player_direction, &y, &x); // in this version there is no screen transition // screen edges act as obstaces if (collision(y, x)) { - printf ("You bump into something!\n"); + printf("You bump into something!\n"); return ; } - player_x = x; - player_y = y; + m->player_x = x; + m->player_y = y; } -void forward_n (unsigned num_steps) -{ + +void forward_n(unsigned num_steps) { for (unsigned int i=0; i<num_steps; i++) { forward(); /* the last apply_event will be done by the main loop - this avoids triggering an event twice - BUGIDEA: remove the next if and observe how events are applied twice at the end - of a FORWARD_N */ + this avoids triggering an event twice */ if (i < num_steps - 1) apply_events(); if (exit_main_loop) @@ -192,60 +179,16 @@ void forward_n (unsigned num_steps) } } + int collision(int y, int x) { - if (x < 0 || x > SCREEN_WIDTH - 1 || y < 0 || y > SCREEN_HEIGHT -1) { - /* BUGLINE: off by one - * if (x < 0 || x > SCREEN_WIDTH || y < 0 || y > SCREEN_HEIGHT) { - */ + map * m = current_map(); + if (x < 0 || x > m->width - 1 || y < 0 || y > m->height -1) { return -1; } - // complex but allows modifiying the possible obstacles quickly. Maybe strlen(OBSTACLES) could be a literal defined in move.h - /* BUGIDEA: - * adding a char at the end of OBSTACLES, hence erasing the \0, making - * strlen fail... - */ if (is_obstacle(y, x)) { return 1; } return 0; } - -void talk(void) -{ - int y, x; - player_in_dir_n(&y, &x, 2); /* talk at distance 2 */ - - monster *mon = is_monster(y, x); - if (!mon) { - printf("There is no one to talk to here.\n"); - return; - } - - talk_monster(mon); -} - - - -void fight(equipment eq) -{ - int y, x; - player_in_dir(&y, &x); - - monster *mon = is_monster(y, x); - if (!mon) { - printf("There is no monster to fight here.\n"); - return; - } - - fight_monster(mon, eq); - - map *m = current_map(); - - /* BUGIDEA: forgetting to remove the monster from the map */ - int i = coord_idx(mon->pos_y, mon->pos_x); - m->monsters[i] = NULL; - free(mon); - -} diff --git a/engines/fullgame/action.h b/engines/fullgame/action.h index c6c63fc0..1e3302cd 100644 --- a/engines/fullgame/action.h +++ b/engines/fullgame/action.h @@ -2,53 +2,39 @@ #define ACTION_H #include "agdbentures.h" +#include <stdlib.h> -// the width of a screen, in tiles -#define SCREEN_WIDTH 60 - -// the height of the screen, in tiles -#define SCREEN_HEIGHT 8 - -#define UP 8 -#define DOWN 2 -#define LEFT 4 -#define RIGHT 6 - -/** - * pos_x : current x position of the character - * pos_y : current y position of the character - * dir : Defines the direction in which the player moves. - * Must be in [1..9], where 5 is no movement, 1 is down-left, 9 is top-right... Do you follow? - * If dir is not in [1..9], it is treated as 5, so no movement occurs. - */ - -void turn_right (void); -void turn_left (void); +void turn_right(void); +void turn_left(void); /** * Computes the coordinates from (x,y) when moving 1 square in direction 'dir' * and stores the results in pointers (*cx, *cy). */ -void coord_in_dir (int y, int x, int dir, int *cy, int *cx); +void coord_in_dir (int y, int x, direction dir, int *cy, int *cx); /** * Idem but when moving 'n' squares in direction 'dir'. */ -void coord_in_dir_n (int y, int x, int dir, int *cy, int *cx, int n); +void coord_in_dir_n(int y, int x, direction dir, int *cy, int *cx, int n); + +// Same 2 functions but using the player's position +void player_in_dir(int *cy, int *cx); +void player_in_dir_n(int *cy, int *cx, int n); // Functions to move the character -void up(); +void up(void); void up_n(unsigned n); -void down(); +void down(void); void down_n(unsigned n); -void left(); +void left(void); void left_n(unsigned n); -void right(); +void right(void); void right_n(unsigned n); -void forward (void); -void forward_n (unsigned num_steps); +void forward(void); +void forward_n(unsigned num_steps); /** * Verifies if the player can move to tile (x, y). @@ -59,7 +45,9 @@ void forward_n (unsigned num_steps); */ int collision(int x, int y); -void talk(void); -void fight(equipment eq); +/** + * Activates the switch in front of the player. + */ +void touch(void); -#endif//ACTION_H +#endif //ACTION_H diff --git a/engines/fullgame/agdbentures.h b/engines/fullgame/agdbentures.h index 7eaeabd4..80fb6fa2 100644 --- a/engines/fullgame/agdbentures.h +++ b/engines/fullgame/agdbentures.h @@ -2,11 +2,9 @@ #define AGDBENTURES_H #include "common.h" -#include "monster.h" #include "map.h" #include "action.h" -#include "shop.h" #include "events.h" -#include "items.h" +#include "read_input.h" -#endif//AGDBENTURES_H +#endif //AGDBENTURES_H diff --git a/engines/fullgame/common.c b/engines/fullgame/common.c index 5a23adb6..9b3c3deb 100644 --- a/engines/fullgame/common.c +++ b/engines/fullgame/common.c @@ -1,50 +1,14 @@ #include "common.h" -const char *str_map = -"\ - ~~~~~ \n\ - +-----+ ~~~~~ \n\ - +--D--+ ~~~~~ \n\ - ~~~~~ \n\ - T > \n\ - @ $$$$$$$$$$$$$ ~~~~~ \n\ - ~~~~~ \n\ -"; - -const char *str_shop = -"\ -+---------+\n\ -| [[[/))) |\n\ -| S |\n\ -+---------+\n\ -| |\n\ -| @ |\n\ -+----D----+\n\ -"; - -// x position of the character -int player_x = -1; - -// y position of the character -int player_y = -1; - -// Initial direction: facing East -int player_direction = 6; - -// nomber of hit/health points -int health_points = 20; - // when true, the main loop ends. Should only be used in level_failed and level_success bool exit_main_loop = false; -void level_failed(void) -{ +void level_failed(void) { printf("DEFEAT!\n"); exit_main_loop = true; } -void level_success() -{ +void level_success() { printf("VICTORY!\n"); exit_main_loop = true; } diff --git a/engines/fullgame/common.h b/engines/fullgame/common.h index f546c244..75c53a0a 100644 --- a/engines/fullgame/common.h +++ b/engines/fullgame/common.h @@ -6,41 +6,11 @@ #include <string.h> #include <assert.h> #include <stdbool.h> -#include "items.h" - -#define max(a,b) (a>b?a:b) - -/** number of slots in the player's inventory. - * Each slot can stack several items of the same type, up to stack_limit -*/ -#define INVENTORY_SIZE MAX_POSSIBLE_ITEMS - -// the list of all possible items -type * list[MAX_POSSIBLE_ITEMS]; - -// the player's inventory -instance inventory[INVENTORY_SIZE]; - -// x position of the character -extern int player_x; - -// y position of the character -extern int player_y; - -// the direction the player is facing -extern int player_direction; - -// the player's life -extern int health_points; - -const char* str_map; -const char * str_shop; - -bool exit_main_loop; +extern bool exit_main_loop; void level_failed(void); void level_success(void); -#endif//COMMON_H +#endif //COMMON_H diff --git a/engines/fullgame/events.c b/engines/fullgame/events.c index af3be273..aa050034 100644 --- a/engines/fullgame/events.c +++ b/engines/fullgame/events.c @@ -20,36 +20,65 @@ #include "events.h" -void load_events() { - // The events have to be hardcoded... - // add more events here (the list MUST be full, that is to say have NUMBER_OF_EVENTS elements) - event_list[0].trigger = walks_on_item; - event_list[0].resolution = pick_item; +event * event_list = NULL; - event_list[1].trigger = can_enter_building; - event_list[1].resolution = enter_building; +void add_event(void (* event_to_add) (void)) { + if (!event_list) { + event_list = malloc(sizeof(event)); + event_list->event = event_to_add; + event_list->next = NULL; + return; + } - event_list[2].trigger = walks_on_water; - event_list[2].resolution = drown; + event * cur = event_list; - event_list[3].trigger = walks_on_monster; - event_list[3].resolution = hit_by_monster; + while (cur->next){ + cur = cur->next; + } - event_list[4].trigger = death_imminent; - event_list[4].resolution = death; - - event_list[5].trigger = verify_exit; - event_list[5].resolution = event_level_success; + cur->next = malloc(sizeof(event)); + cur->next->event = event_to_add; + cur->next->next = NULL; } -void apply_events() { - int trig_res; +void remove_event(void (* event_to_remove) (void)) { + if (!event_list) + return; + + event * cur = event_list; + event * pred = NULL; - for (int i = 0; i < NUMBER_OF_EVENTS; i++) { - trig_res = event_list[i].trigger(); - if (trig_res) { - event_list[i].resolution(trig_res); - } + while (cur->event != event_to_remove && cur) { + pred = cur; + cur = cur->next; } + if (!cur) + return; + + if (!pred) { + event_list = cur->next; + } else { + pred->next = cur->next; + } + + free(cur); +} + +void remove_all_events(void) { + event * to_free; + + while (event_list) { + to_free = event_list; + event_list = to_free->next; + free(to_free); + } +} + +void apply_events(void) { + event * cur = event_list; + while (cur != NULL) { + cur->event(); + cur = cur->next; + } } diff --git a/engines/fullgame/events.h b/engines/fullgame/events.h index a93f4a64..f13c78c2 100644 --- a/engines/fullgame/events.h +++ b/engines/fullgame/events.h @@ -1,32 +1,20 @@ -#pragma once +#ifndef EVENTS_H +#define EVENTS_H -#include "common.h" -// include all files containing trigger and resolution functions -#include "events_player.h" -#include "events_map.h" +#include "agdbentures.h" -#define NUMBER_OF_EVENTS 6 - -// The structure of a single event -typedef struct _event { - // the trigger function, only reads and can't write the state of the game - int (*trigger)(void); - - /* the resolution function, called if trigger returned a non-null value - * the parameter trig will be the result of trigger (in some cases this may avoid redundant checks) - */ - void (*resolution) (int trig); +typedef struct Event { + void (* event) (void); + struct Event * next; } event; -event event_list[NUMBER_OF_EVENTS]; - - -/** - * Reads all events the game wants and loads them in a table to call them later. - */ -void load_events(); +void add_event(void (* event_to_add) (void)); +void remove_event(void (* event_to_remove) (void)); +void remove_all_events(void); /** * Applies all triggers and events in sequence. Due to this, the event ordering matters. */ -void apply_events(); +void apply_events(void); + +#endif // EVENTS_H diff --git a/engines/fullgame/main b/engines/fullgame/main deleted file mode 100755 index a06e1d1a13a25850ead7f0fb6cdfdd8f620355ca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24008 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 X-LB-)z4!e3chdtdlx~}X4Q2ldd5peC diff --git a/engines/fullgame/map.c b/engines/fullgame/map.c index bf46bfd3..93c51c11 100644 --- a/engines/fullgame/map.c +++ b/engines/fullgame/map.c @@ -1,207 +1,94 @@ #include "map.h" + static map_stack mstack; -void init_map_stack(void) -{ + +void init_map_stack(void) { mstack.length = 0; } -map* current_map (void) -{ + +map * current_map(void) { + if (mstack.length <= 0) + return NULL; return &mstack.maps[mstack.length-1]; } -void load_map(const char * str_map, const char * new_name) { - map *m; - - /*BUGIDEA : forgetting to save player position */ - if (mstack.length > 0) { - m = current_map(); - m->player_x = player_x; - m->player_y = player_y; +map * load_map(const char * name, int width, int height, int player_x, int player_y, unsigned player_direction) { + if (mstack.length >= MAX_MAPS - 1) { + printf("load_map: can't load a new map, the maximum number has been reached.\n"); + return current_map(); } + map * m; mstack.length++; - m = current_map(); - m->exit_x = -1; - m->exit_y = -1; - m->player_x = -1; - m->player_y = -1; - - /* initialize the map */ - /* width is the num of characters in the first line */ - const char *ch = strchr (str_map, '\n'); - if (! ch) { - fprintf (stderr, "Error: cannot find line return in map description!"); - exit(EXIT_FAILURE); - } - m->width = ch - str_map; - m->height = 0; - while (ch) { - ch = strchr (ch+1, '\n'); - m->height++; - } - /* printf ("Size of map: %dx%d\n", m->width, m->height); */ + m->player_x = player_x; + m->player_y = player_y; + m->player_direction = player_direction; + m->width = width; + m->height = height; /* allocating enough space to hold the map */ - m->name = (char*) malloc(sizeof(char) * (strlen(new_name) + 1)); - strcpy(m->name, new_name); - m->floor = (floortype*)malloc(m->width * m->height * sizeof(floortype)); - m->items = (itemtype*)malloc(m->width * m->height * sizeof(itemtype)); - m->monsters = (monster**)malloc(m->width * m->height * sizeof(monster*)); - - for (int i=0; i<m->width*m->height; i++) { - m->items[i] = NO_ITEM; - m->monsters[i] = NULL; - } - - ch = str_map; - floortype f; - monster *mon; - for (int y=0; y<m->height; y++) { - for (int x=0; x<m->width; x++) { - f = EMPTY; - switch (*ch) { - case '+': - case '|': - case '-': f=WALL; break; - case '$': m->items[y*m->width + x] = I_GOLD; - f=GRASS; break; - case ' ': f=GRASS; break; - case '~': f=WATER; break; - case 'D': f=DOOR; break; - case '@': f=GRASS; - m->player_x = x; - m->player_y = y; - break; - case '>': f=GRASS; - m->exit_x = x; - m->exit_y = y; - break; - case 'S': f=GRASS; - mon = (monster*)malloc(sizeof(monster)); - mon->pos_x = x; - mon->pos_y = y; - mon->name = "Shopkeeper"; - mon->health_points = 1200; - mon->armor = 3; - mon->weapon = 12; - m->monsters[y*m->width + x] = mon; - break; - case 'T': f=GRASS; - mon = (monster*)malloc(sizeof(monster)); - mon->pos_x = x; - mon->pos_y = y; - mon->name = "Troll"; - mon->health_points = 25; - mon->armor = 1; - mon->weapon = 4; - m->monsters[y*m->width + x] = mon; - break; - case '[': /* armor */ - case '/': /* wand */ - case ')': /* weapon */ - f=GRASS; - break; - default: - fprintf (stderr, "Unknown map character '%c'!\n",*ch); - } - m->floor[y*m->width + x] = f; - ch++; - } - assert (*ch == '\n'); - ch++; - } - - player_x = m->player_x; - player_y = m->player_y; - assert (player_x != -1 && player_y != -1); -} - - -void unload_map (void) -{ - map *m = current_map(); - - for (int i=0; i<m->width*m->height; i++) { - free(m->monsters[i]); - } - free(m->floor); - free(m->items); - free(m->monsters); - free(m->name); + m->name = (char *) malloc(sizeof(char) * (strlen(name) + 1)); + strcpy(m->name, name); + m->floor = (tile **) malloc(m->width * m->height * sizeof(tile *)); + m->entities = (entity **) malloc(m->width * m->height * sizeof(entity *)); - mstack.length--; - m = current_map(); - - /* place player at saved position on previous map */ - player_x = m->player_x; - player_y = m->player_y; -} - -int coord_idx(int y, int x) -{ - map *m = current_map(); - return y * m->width + x; -} - -int player_idx(void) -{ - return coord_idx (player_y, player_x); + return m; } +map * unload_map(void) { + if (mstack.length > 0) { + map * m = current_map(); + free(m->name); + for (int i = 0; i < m->width * m->height; i++) { + if (m->entities[i] != NULL) { + if (m->entities[i]->stats) + free(m->entities[i]->stats); -bool is_obstacle(int y, int x) -{ - map *m = current_map(); - int i = coord_idx(y, x); + free(m->entities[i]); + } + if (m->floor[i] != NULL) { + free(m->floor[i]); + } + } - floortype f = m->floor[i]; + free(m->entities); + free(m->floor); - if (f == WALL) { - return true; + mstack.length--; + } else { + printf("unload_map: Can't unload map: map stack is already empty.\n"); } - return false; + return current_map(); } - - -itemtype item(int y, int x) -{ - map *m = current_map(); - int i = coord_idx(y, x); - - return m->items[i]; /* BUGIDEA: was 'm->items[i] == NO_ITEMS' */ +void free_map_stack(void) { + while (mstack.length > 0) { + unload_map(); + } } -monster* is_monster(int y, int x) -{ - map *m = current_map(); - int i = coord_idx(y, x); - return m->monsters[i]; + +int coord_idx(int y, int x) { + return y * current_map()->width + x; } -void remove_item(void) -{ +int player_idx(void) { map *m = current_map(); - int i = player_idx(); - assert (m->items[i] != NO_ITEM); - m->items[i] = NO_ITEM; + return coord_idx(m->player_y, m->player_x); } - -bool is_water(int y, int x) -{ - map *m = current_map(); - return m->floor[coord_idx(y,x)] == WATER; +bool is_obstacle(int y, int x) { + return current_map()->floor[coord_idx(y, x)]->obstacle; } diff --git a/engines/fullgame/map.h b/engines/fullgame/map.h index ac18b48d..b73f52d3 100644 --- a/engines/fullgame/map.h +++ b/engines/fullgame/map.h @@ -1,32 +1,39 @@ #ifndef MAP_H #define MAP_H +typedef enum Direction { + UP, + DOWN, + LEFT, + RIGHT, + UNKNOWN +} direction; + #include "agdbentures.h" -typedef enum { - EMPTY, /* unreachable area */ - GRASS, - DIRT, - WOOD, - WALL, - WATER, - DOOR, - FLOORTYPE_N, /* last one to know how many types there are */ -} floortype; +typedef struct { + int id; // identifier to display the tile + bool obstacle; // whether the player can move on it or not +} tile; + +typedef struct { + int id; // identifier to display and use in events + int category; // used to group in events, like monster, NPC... + int * stats; // any property the entity may have (life points, state...) +} entity; typedef struct { char * name; // shop, overworld, dungeon_Nth_level... - floortype *floor; /* matrix of floortype */ - itemtype *items; /* matrix of items */ - monster **monsters; /* matrix of monsters */ - int width; /* size of map */ + tile ** floor; // immobile floor + entity ** entities; // ennemies, NPCs, items, switches... anything used by events + int width; // map size int height; - int player_x; /* position of player */ + int player_x; // player position int player_y; - int exit_x; - int exit_y; /* position of the exit if any */ + direction player_direction; } map; + #define MAX_MAPS 256 typedef struct { @@ -36,22 +43,16 @@ typedef struct { void init_map_stack(void); -void load_map(const char * str_map, const char * new_name); -void unload_map(void); +map * load_map(const char * name, int width, int height, int player_x, int player_y, unsigned player_direction); +map * unload_map(void); int coord_idx(int y, int x); int player_idx(void); -map* current_map (void); - -bool is_obstacle(int y, int x); -itemtype item(int y, int x); -void remove_item(void); - -bool is_water(int y, int x); -monster* is_monster(int y, int x); +map * current_map(void); +void free_map_stack(void); +bool is_obstacle(int y, int x); -void receive_damage(monster *mon); -#endif//MAP_H +#endif //MAP_H diff --git a/engines/fullgame/read_input.c b/engines/fullgame/read_input.c index a28df349..e731ead2 100644 --- a/engines/fullgame/read_input.c +++ b/engines/fullgame/read_input.c @@ -1,22 +1,71 @@ #include "read_input.h" -int read_input_file(const char* filename, CommandList* commands) { - FILE* input_file = fopen(filename, "r"); +typedef struct { + command * com; +} commandList; + +bool file_inputs = false; +commandList * internal_list = NULL; + + +command * allocate_command(char * command_buffer, size_t buffer_len) { + // Allocate the new command structure + command * com = malloc(sizeof(command)); + + com->command_buffer = command_buffer; + command_buffer[buffer_len-1] = '\0'; + + // count args and replace space with \0 + com->n_args = 0; + for (size_t current_char_index = 0; current_char_index < buffer_len; current_char_index++) { + if (command_buffer[current_char_index] == ' ') { + com->n_args++; + command_buffer[current_char_index] = '\0'; + } + } + + // redo a pass to place args pointers + if (com->n_args > 0) + com->args = malloc(sizeof(char *) * com->n_args); + else + com->args = NULL; + int n_args = 0; + for (size_t current_char_index = 0; n_args < com->n_args; current_char_index++) { + if (command_buffer[current_char_index] == '\0') { + com->args[n_args] = command_buffer + current_char_index + 1; + n_args++; + } + } + + com->next = NULL; + + return com; +} + + +bool read_input_file(const char * filename) { + FILE * input_file = fopen(filename, "r"); + + if (!input_file) + return false; size_t command_length; - char* line = NULL; + char * line = NULL; + + if (!internal_list) + internal_list = malloc(sizeof(commandList)); // Push a sentinel - Command sentinel; - commands->command = &sentinel; - Command* prev_command = commands->command; + command sentinel; + internal_list->com = &sentinel; + command * prev_command = internal_list->com; while (!feof(input_file)) { int linesize = getline(&line, &command_length, input_file); if (linesize != -1) { - Command* next = allocate_command(line, linesize); + command * next = allocate_command(line, linesize); prev_command->next = next; prev_command = next; } else { @@ -26,58 +75,65 @@ int read_input_file(const char* filename, CommandList* commands) { } // Remove the sentinel - commands->command = commands->command->next; + internal_list->com = internal_list->com->next; fclose(input_file); - return 0; + return true; } -Command* allocate_command(char* command_buffer, size_t buffer_len) { - // Allocate the new command structure - Command* command = malloc(sizeof(Command)); - command->command_buffer = command_buffer; - command_buffer[buffer_len-1] = '\0'; +void free_command(command * com) { + if (!com) + return; + free(com->command_buffer); // Free the buffer allocated by getline + if (com->args != NULL) + free(com->args); + free(com); +} - // count args and replace space with \0 - command->n_args = 0; - for (size_t current_char_index = 0; current_char_index < buffer_len; current_char_index++) { - if (command_buffer[current_char_index] == ' ') { - command->n_args ++; - command_buffer[current_char_index] = '\0'; - } - } - // redo a pass to place args pointers - if (command->n_args > 0) - command->args = malloc(sizeof(char*) * command->n_args); - else - command->args = NULL; - int n_args = 0; - for (size_t current_char_index = 0; n_args < command->n_args; current_char_index++) { - if (command_buffer[current_char_index] == '\0') { - command->args[n_args] = command_buffer + current_char_index + 1; - n_args++; - } - } +void free_commands(commandList* commands) { + free_command(commands->com); +} + - command->next = NULL; +bool init_inputs(const char * filename) { + if (strlen(filename) == 0) { + file_inputs = false; + return true; + } - return command; + file_inputs = true; + return read_input_file(filename); } -void free_command(Command* command) { - // Free next commands - if (command->next != NULL) { - free_command(command->next); + +command * get_next_input(void) { + command * i; + + if (file_inputs) { + + if (!internal_list || !internal_list->com) + return NULL; + + i= internal_list->com; + internal_list->com = i->next; + if (!internal_list->com) { + free(internal_list); + internal_list = NULL; + } + return i; + + } else { + return NULL; + // todo get command from standard input } - // Free current command - free(command->command_buffer); // Free the buffer allocated by getline - if (command->args != NULL) - free(command->args); - free(command); } -void free_commands(CommandList* commands) { - free_command(commands->command); + +command * get_and_free_input(command * i) { + command * r = get_next_input(); + if (i) + free_command(i); + return r; } diff --git a/engines/fullgame/read_input.h b/engines/fullgame/read_input.h index 5627a69d..1af49af4 100644 --- a/engines/fullgame/read_input.h +++ b/engines/fullgame/read_input.h @@ -3,30 +3,22 @@ #include <stdlib.h> #include <stdio.h> +#include <stdbool.h> +#include <string.h> // Command structure to hold a single instruction with arguments as strings -struct Command { - char* command_buffer; +typedef struct Command { + char * command_buffer; int n_args; - char** args; + char ** args; struct Command* next; -}; +} command; -typedef struct Command Command; -// A list of commands with its size -typedef struct { - Command* command; -} CommandList; +bool init_inputs(const char * filename); +command * get_next_input(void); +void free_command(command * i); +command * get_and_free_input(command * i); -// Reads the whole given input file and returns a list of commands. -// 1 means means the function failed and 0 succeeded -int read_input_file(const char* filename, CommandList* commands); - -void free_commands(CommandList* commands); - -// Takes a command buffer as input and prepare all the interior data in a new allocated buffer -Command* allocate_command(char* command_buffer, size_t buffer_len); - -#endif +#endif // READ_INPUT_H diff --git a/levels/interrupteurs/Makefile b/levels/interrupteurs/Makefile index 0d7a136a..b4efc1b7 100644 --- a/levels/interrupteurs/Makefile +++ b/levels/interrupteurs/Makefile @@ -1,5 +1,7 @@ MAINPROG := main -OBJECTS := main.o map.o action.o common.o events.o events_map.o read_input.o +OBJECTS := main.o \ + ../../engines/fullgame/map.o ../../engines/fullgame/action.o ../../engines/fullgame/common.o ../../engines/fullgame/events.o ../../engines/fullgame/read_input.o \ + events_map.o custom_map.o input_manager.o #DEFINES := -Dtemplate -Dinverse_xy -Dstring_comparison #DEFINES := -Dstack_alloc -Dinverse_xy -Dstring_comparison @@ -7,7 +9,7 @@ OBJECTS := main.o map.o action.o common.o events.o events_map.o read_input.o #DEFINES := -Dstring_comparison DEFINES := -CC := clang +CC := gcc CFLAGS := -g -Wall -Wextra $(DEFINES) all: main @@ -17,6 +19,7 @@ main: $(OBJECTS) clean: rm *.o + rm ../../engines/fullgame/*.o clear: clean rm main diff --git a/levels/interrupteurs/action.c b/levels/interrupteurs/action.c deleted file mode 100644 index c6e6beb3..00000000 --- a/levels/interrupteurs/action.c +++ /dev/null @@ -1,224 +0,0 @@ -/** - * Very simple program representing a character moving in 8 directions. Can't pass through walls, - * and can pick "items" by walking on it. - * - * Concepts used: Variable locality, reference passing, remainder operator - * - * Possible bugs: - * - not understanding the % operator - * - passing a negative int while the parameter is an unsigned. I think in that case it underflows and is MAX_UNSIGNED - n - * - confusing x and y, because this program does level[y][x] - * - * Game design related tip: - * it is common in fighting games to represent the directional inputs using numbers from 1 to 9. - * Just think on how they are disposed on a numeric keypad: - * ↖ ↑ ↗ - * 7 8 9 - * ↠4 5 6 → - * 1 2 3 - * ↙ ↓ ↘ -*/ - -#include "action.h" - -/***********************************/ -/* Constants, types, variables */ -/***********************************/ - -/***********************************/ -/* Methods and functions */ -/***********************************/ - -void turn_right (void) -{ - int d; - switch (player_direction) { - case UP: d=RIGHT; break; - case LEFT: d=UP; break; - case DOWN: d=LEFT; break; - case RIGHT: d=DOWN; break; - default: assert(false); - } - player_direction = d; -} - -void turn_left (void) -{ - int d; - switch (player_direction) { - case UP: d=LEFT; break; - case LEFT: d=DOWN; break; - case DOWN: d=RIGHT; break; - case RIGHT: d=UP; break; - default: assert(false); - } - player_direction = d; -} - -void coord_in_dir_n (int y, int x, int dir, int *cy, int *cx, int n) -{ - assert (dir >= 1 && dir <= 9); - - // warning: (0, 0) is top left - - // vertical movement - if (dir >= 7) { - // 7, 8, 9 - *cy = y - n; - /* BUGLINE: forgetting that y = 0 is at the top - * y = player_y + 1; - */ - } else if (dir <= 3) { - // 1, 2, 3 - *cy = y + n; - } else { - *cy = y; - } - - if (dir % 3 == 0) { - // 3, 6, 9 - *cx = x + n; - /* BUGLINE: thinking x is also inverted because y is - * x = pox_x - 1; - */ - } else if (dir % 3 == 1) { - // 1, 4, 7 - *cx = x - n; - } else { - *cx = x; - } -} - - -void coord_in_dir (int y, int x, int dir, int *cy, int *cx) -{ - coord_in_dir_n (y, x, dir, cy, cx, 1); -} - -void player_in_dir(int *cy, int *cx) -{ - coord_in_dir (player_y, player_x, player_direction, cy, cx); -} - -void player_in_dir_n(int *cy, int *cx, int n) -{ - coord_in_dir_n (player_y, player_x, player_direction, cy, cx, n); -} - - -int player_idx_in_dir(void) -{ - int y, x; - player_in_dir(&y, &x); - return coord_idx (y, x); -} - -int player_idx_in_dir_n(int n) -{ - int y, x; - player_in_dir_n(&y, &x, n); - return coord_idx (y, x); -} - -void up() { - player_direction = UP; - forward(); -} - -void up_n(unsigned n) { - player_direction = UP; - forward_n(n); -} - -void down() { - player_direction = DOWN; - forward(); -} - -void down_n(unsigned n) { - player_direction = DOWN; - forward_n(n); -} - -void left() { - player_direction = LEFT; - forward(); -} - -void left_n(unsigned n) { - player_direction = LEFT; - forward_n(n); -} - -void right() { - player_direction = RIGHT; - forward(); -} - -void right_n(unsigned n) { - player_direction = RIGHT; - forward_n(n); -} - -void forward (void) -{ - int y, x; - coord_in_dir (player_y, player_x, player_direction, &y, &x); - - // in this version there is no screen transition - // screen edges act as obstaces - if (collision(y, x)) { - printf ("You bump into something!\n"); - return ; - } - - player_x = x; - player_y = y; -} - -void forward_n (unsigned num_steps) -{ - for (unsigned int i=0; i<num_steps; i++) { - forward(); - /* the last apply_event will be done by the main loop - this avoids triggering an event twice - BUGIDEA: remove the next if and observe how events are applied twice at the end - of a FORWARD_N */ - if (i < num_steps - 1) - apply_events(); - if (exit_main_loop) - i = num_steps; - } -} - -int collision(int y, int x) { - if (x < 0 || x > SCREEN_WIDTH - 1 || y < 0 || y > SCREEN_HEIGHT -1) { - /* BUGLINE: off by one - * if (x < 0 || x > SCREEN_WIDTH || y < 0 || y > SCREEN_HEIGHT) { - */ - return -1; - } - - // complex but allows modifiying the possible obstacles quickly. Maybe strlen(OBSTACLES) could be a literal defined in move.h - /* BUGIDEA: - * adding a char at the end of OBSTACLES, hence erasing the \0, making - * strlen fail... - */ - if (is_obstacle(y, x)) { - return 1; - } - - return 0; -} - -void touch(void) -{ - int y, x; - player_in_dir_n(&y, &x, 1); - // @AGDB this bug is just an inversion of x and y, but the result is funny - #ifdef inverse_xy - activate_switch(x, y); - #else - activate_switch(y, x); - #endif -} diff --git a/levels/interrupteurs/action.h b/levels/interrupteurs/action.h deleted file mode 100644 index f1942872..00000000 --- a/levels/interrupteurs/action.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef ACTION_H -#define ACTION_H - -#include "agdbentures.h" -#include <stdlib.h> - -#define UP 8 -#define DOWN 2 -#define LEFT 4 -#define RIGHT 6 - -/** - * pos_x : current x position of the character - * pos_y : current y position of the character - * dir : Defines the direction in which the player moves. - * Must be in [1..9], where 5 is no movement, 1 is down-left, 9 is top-right... Do you follow? - * If dir is not in [1..9], it is treated as 5, so no movement occurs. - */ - -void turn_right (void); -void turn_left (void); - - -/** - * Computes the coordinates from (x,y) when moving 1 square in direction 'dir' - * and stores the results in pointers (*cx, *cy). - */ -void coord_in_dir (int y, int x, int dir, int *cy, int *cx); - -/** - * Idem but when moving 'n' squares in direction 'dir'. - */ -void coord_in_dir_n (int y, int x, int dir, int *cy, int *cx, int n); - -// Functions to move the character -void up(); -void up_n(unsigned n); -void down(); -void down_n(unsigned n); -void left(); -void left_n(unsigned n); -void right(); -void right_n(unsigned n); - -void forward (void); -void forward_n (unsigned num_steps); - -/** - * Verifies if the player can move to tile (x, y). - * Returns 0 if (x, y) is accessible - * Returns 1 if (x, y) is no accessible because it's occupied by an obstacle - * Returns -1 if (x, y) is outside the screen. In that case it's up to the game to decide - * wether the player will transition to another screen or can't move anymore. - */ -int collision(int x, int y); - -/** - * Activates the switch in front of the player. - */ -void touch(void); - -#endif//ACTION_H diff --git a/levels/interrupteurs/agdbentures.h b/levels/interrupteurs/agdbentures.h deleted file mode 100644 index 75c2ffc5..00000000 --- a/levels/interrupteurs/agdbentures.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef AGDBENTURES_H -#define AGDBENTURES_H - -#include "common.h" -#include "map.h" -#include "action.h" -#include "events.h" - -#endif//AGDBENTURES_H diff --git a/levels/interrupteurs/common.c b/levels/interrupteurs/common.c deleted file mode 100644 index b8d98c3b..00000000 --- a/levels/interrupteurs/common.c +++ /dev/null @@ -1,45 +0,0 @@ -#include "common.h" - -const char *str_map = -"\ -+-----+--+\n\ -|X X|~~|\n\ -| +--+\n\ -|> D @\n\ -| +--+\n\ -|X X|~~|\n\ -+-----+--+\n\ -"; - -// x position of the character -int player_x = -1; - -// y position of the character -int player_y = -1; - -// Initial direction: facing East -int player_direction = 6; - -// when true, the main loop ends. Should only be used in level_failed and level_success -bool exit_main_loop = false; - -// @AGDB in the exercise we forget the +1 so the string doesn't end in \0 and played_order becomes corrupted -#ifdef string_comparison -const char correct_order[N_SWITCHES] = "2103"; -char played_order[N_SWITCHES] = ""; -#else -const char correct_order[N_SWITCHES+1] = "2103"; -char played_order[N_SWITCHES+1] = ""; -#endif - -void level_failed(void) -{ - printf("DEFEAT!\n"); - exit_main_loop = true; -} - -void level_success() -{ - printf("VICTORY!\n"); - exit_main_loop = true; -} diff --git a/levels/interrupteurs/common.h b/levels/interrupteurs/common.h deleted file mode 100644 index 8fc27575..00000000 --- a/levels/interrupteurs/common.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef COMMON_H -#define COMMON_H - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <stdbool.h> - -// the width of a screen, in tiles -#define SCREEN_WIDTH 10 - -// the height of the screen, in tiles -#define SCREEN_HEIGHT 7 - -// number of switches -#define N_SWITCHES 4 - -// x position of the character -extern int player_x; - -// y position of the character -extern int player_y; - -// the direction the player is facing -extern int player_direction; - -extern const char * str_map; - -extern bool exit_main_loop; - -// the order in which the switches are activated and the expected order -// @AGDB see common.c for explanation about this bug -#ifdef string_comparison -extern char played_order[N_SWITCHES]; -extern const char correct_order[N_SWITCHES]; -#else -extern char played_order[N_SWITCHES+1]; -extern const char correct_order[N_SWITCHES+1]; -#endif - -void level_failed(void); - -void level_success(void); - -#endif//COMMON_H diff --git a/levels/interrupteurs/custom_map.c b/levels/interrupteurs/custom_map.c new file mode 100644 index 00000000..20996c4e --- /dev/null +++ b/levels/interrupteurs/custom_map.c @@ -0,0 +1,116 @@ +#include "custom_map.h" + +const char *str_map = +"\ ++-----+--+\n\ +|X X|~~|\n\ +| +--+\n\ +|> D @\n\ +| +--+\n\ +|X X|~~|\n\ ++-----+--+\n\ +"; + +#ifdef string_comparison +// @AGDB in the exercise we forget the +1 so the string doesn't end in \0 and played_order becomes corrupted +const char correct_order[N_SWITCHES] = "2103"; +char played_order[N_SWITCHES] = ""; +#else +const char correct_order[N_SWITCHES+1] = "2103"; +char played_order[N_SWITCHES+1] = ""; +#endif + +#ifdef template +entity template_swi = {SWI, 0, NULL}; +#endif + +#ifdef stack_alloc +/* @AGDB returning a local variable address has undefined results + which depend of the compiler. With GCC, this returns NULL and the switch is not existent */ +entity * create_switch(int n) { + entity swi; + + swi.category = SWI; + swi.id = n; + swi.stats = malloc(sizeof(int)); + swi.stats[0] = DEACTIVATED; + return &swi; +} +#else // stack_alloc +entity * create_switch(int n) { + entity * swi = malloc(sizeof(entity)); + #ifdef template + /* @AGDB using a template will have all the switches be the same + + memory leak because the malloc is lost */ + swi = &template_swi; + #endif + swi->category = SWI; + swi->id = n; + // a switch has only one stat: its state + swi->stats = malloc(sizeof(int)); + swi->stats[0] = DEACTIVATED; + return swi; +} +#endif // stack_alloc + + +void init_map(map * m) { + const char *ch = strchr (str_map, '\n'); + if (! ch) { + fprintf (stderr, "Error: cannot find line return in map description!"); + exit(EXIT_FAILURE); + } + m->width = ch - str_map; + m->height = 0; + while (ch) { + ch = strchr (ch+1, '\n'); + m->height++; + } + + // we have 4 switches only + int nth_swi = 0; + + ch = str_map; + tile * f; + entity * i; + for (int y=0; y<m->height; y++) { + for (int x=0; x<m->width; x++) { + f = malloc(sizeof(tile)); + f->id = EMPTY; + f->obstacle = false; + i = NULL; + switch (*ch) { + case '+': + case '|': + case '-': f->id=WALL; f->obstacle = true; break; + case ' ': f->id=GRASS; break; + case '~': f->id=WATER; break; + case 'D': f->id=DOOR; + f->obstacle = true; + break; + case '>': f->id=GRASS; + m->player_x = x; + m->player_y = y; + break; + case '@': f->id=GRASS; + i = malloc(sizeof(entity)); + i->category = FLAG; + i->id = 0; + i->stats = NULL; + break; + case 'X': f->id=WALL; f->obstacle = true; + assert(nth_swi < N_SWITCHES); + i = create_switch(nth_swi); + nth_swi++; + break; + default: + fprintf (stderr, "Unknown map character '%c'!\n",*ch); + } + m->floor[y*m->width + x] = f; + m->entities[y*m->width+x] = i; + ch++; + } + assert (*ch == '\n'); + ch++; + } +} diff --git a/levels/interrupteurs/custom_map.h b/levels/interrupteurs/custom_map.h new file mode 100644 index 00000000..47dcd092 --- /dev/null +++ b/levels/interrupteurs/custom_map.h @@ -0,0 +1,38 @@ +#ifndef CUSTOM_MAP_H +#define CUSTOM_MAP_H + +#include "../../engines/fullgame/agdbentures.h" + +#define DEACTIVATED 0 +#define ACTIVATED 1 +#define N_SWITCHES 4 + +typedef enum { + EMPTY, /* unreachable area */ + GRASS, + WALL, + WATER, + DOOR, + FLOORTYPE_N, /* last one to know how many types there are */ +} floortype; + +typedef enum { + FLAG, // win if reached + SWI +} entitytype; + +extern const char * str_map; + +// the order in which the switches are activated and the expected order +#ifdef string_comparison +// @AGDB see common.c for explanation about this bug +extern char played_order[N_SWITCHES]; +extern const char correct_order[N_SWITCHES]; +#else +extern char played_order[N_SWITCHES+1]; +extern const char correct_order[N_SWITCHES+1]; +#endif + +void init_map(map * m); + +#endif // CUSTOM_MAP_H diff --git a/levels/interrupteurs/events.c b/levels/interrupteurs/events.c deleted file mode 100644 index 02395462..00000000 --- a/levels/interrupteurs/events.c +++ /dev/null @@ -1,58 +0,0 @@ -/** - * The event manager. Loads, watches and triggers events. - * Called by the main program at each frame (main loop). - * - * Concepts used: - * - pointers to functions - * - side-effecting functions - * - communication with the main function (to stop the main loop) - * - * Possible bugs: - * - creating a trigger function which changes the state of the game - * - calling two events in the wrong order because they were loaded in reverse - * - * Game design related tip: - * Simple games may not use events at all. More complete game engines have several complex - * events loops. For example, have a look at how Unity handles the execution sequence: - * <https://docs.unity3d.com/Manual/ExecutionOrder.html> - * Of course, if you create a game with Unity you will probably not use all of them. -*/ - -#include "events.h" - -event event_list[NUMBER_OF_EVENTS]; - -int return_false() { - return false; -} - -void load_events() { - // The events have to be hardcoded... - // add more events here (the list MUST be full, that is to say have NUMBER_OF_EVENTS elements) - event_list[0].trigger = verify_switch_order; - event_list[0].resolution = trap_door; - event_list[0].once = true; - - event_list[1].trigger = walks_on_water; - event_list[1].resolution = drown; - event_list[1].once = false; - - event_list[2].trigger = verify_exit; - event_list[2].resolution = event_level_success; - event_list[2].once = false; -} - -void apply_events() { - int trig_res; - - for (int i = 0; i < NUMBER_OF_EVENTS; i++) { - trig_res = event_list[i].trigger(); - if (trig_res) { - event_list[i].resolution(trig_res); - if (event_list[i].once) { - event_list[i].trigger = return_false; - } - } - } - -} diff --git a/levels/interrupteurs/events.h b/levels/interrupteurs/events.h deleted file mode 100644 index 7117b459..00000000 --- a/levels/interrupteurs/events.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef EVENTS_H -#define EVENTS_H - -#include "common.h" -// include all files containing trigger and resolution functions -#include "events_map.h" - -#define NUMBER_OF_EVENTS 3 - -// The structure of a single event -typedef struct _event { - // the trigger function, only reads and can't write the state of the game - int (*trigger)(void); - - /* the resolution function, called if trigger returned a non-null value - * the parameter trig will be the result of trigger (in some cases this may avoid redundant checks) - */ - void (*resolution) (int trig); - - // if the event can be done only once (= the resolution function is called once) - bool once; -} event; - -extern event event_list[NUMBER_OF_EVENTS]; - - -/** - * Reads all events the game wants and loads them in a table to call them later. - */ -void load_events(); - -/** - * Applies all triggers and events in sequence. Due to this, the event ordering matters. - */ -void apply_events(); - -#endif // EVENTS_H diff --git a/levels/interrupteurs/events_map.c b/levels/interrupteurs/events_map.c index 714d9b41..f44128ca 100644 --- a/levels/interrupteurs/events_map.c +++ b/levels/interrupteurs/events_map.c @@ -1,49 +1,47 @@ #include "events_map.h" -int verify_exit(void) -{ - map *m = current_map(); - if (player_x == m->exit_x && player_y == m->exit_y) { - return true; - } else { - return false; +int once = 1; + +void verify_exit(void) { + map * m = current_map(); + int i = player_idx(); + if (m->entities[i] && m->entities[i]->category == FLAG) { + printf("You reached the exit!\n"); + level_success(); } } -void event_level_success(__attribute__((unused)) int trig) { - level_success(); -} -int verify_switch_order() { - // not all switches are on - if (strlen(played_order) < 4) { - return 0; +void verify_switch_order(void) { + // not all switches are on or this event has already been done + if (!once || strlen(played_order) < 4) { + return; } + int trig; + + #ifdef string_comparison /* @AGDB use strcmp instead of strncmp and set the size of these strings to N_SWITCHES instead of N_SWITCHES+1 see common.c for more explanations - in the end the comparaison will never work even if the order is correct - */ - #ifdef string_comparison + in the end the comparaison will never work even if the order is correct */ if (strcmp(played_order, correct_order)) { #else if (strncmp(played_order, correct_order, 4)) { #endif - return -1; + trig = -1; } else { - return 1; + trig = 1; } -} -void trap_door(int trig) { map * m = current_map(); for (int i = 0; i < m->width * m->height; i++) { - if (m->floor[i] == GRASS && trig == -1) { - m->floor[i] = WATER; - } else if (m->floor[i] == DOOR && trig == 1) { - m->floor[i] = GRASS; + if (m->floor[i]->id == GRASS && trig == -1) { + m->floor[i]->id = WATER; + } else if (m->floor[i]->id == DOOR && trig == 1) { + m->floor[i]->id = GRASS; + m->floor[i]->obstacle = false; } } @@ -51,13 +49,14 @@ void trap_door(int trig) { printf("You activated the switches in a wrong order! A trap triggers and the whole room becomes flooded!\n"); else if (trig == 1) printf("You activated the switches in the correct order!\nThe door opens!\n"); -} -int walks_on_water() { - return is_water(player_y, player_x); + once = 0; } -void drown(__attribute__((unused)) int trig) { - printf("You drown!\n"); - level_failed(); + +void drown(void) { + if (current_map()->floor[player_idx()]->id == WATER) { + printf("You drown!\n"); + level_failed(); + } } diff --git a/levels/interrupteurs/events_map.h b/levels/interrupteurs/events_map.h index c19b3c69..f060e916 100644 --- a/levels/interrupteurs/events_map.h +++ b/levels/interrupteurs/events_map.h @@ -1,27 +1,18 @@ #ifndef EVENTS_MAP_H #define EVENTS_MAP_H -#include "common.h" -#include "map.h" +#include "../../engines/fullgame/agdbentures.h" +#include "custom_map.h" // Renvoie true si le joueur se trouve sur la case de victoire. -int verify_exit(void); - -// Fonction interne qui ne fait qu'appeler level_success -void event_level_success(__attribute__((unused)) int trig); +void verify_exit(void); /* renvoie 0 si les 4 interrupteurs ne sont pas encore allumés, 1 s'ils sont allumés dans le bon ordre, -1 s'ils sont allumés dans le mauvais ordre */ -int verify_switch_order(); - -// selon le résultat de verify_switch_order, ouvre la porte ou active le piège -void trap_door(int trig); - -// S'active si le joueur est au-dessus de l'eau -int walks_on_water(); +void verify_switch_order(void); // Le joueur se noie et level_failed est appelé -void drown(__attribute__((unused)) int trig); +void drown(void); #endif // EVENTS_MAP_H diff --git a/levels/interrupteurs/input_manager.c b/levels/interrupteurs/input_manager.c new file mode 100644 index 00000000..798e8d21 --- /dev/null +++ b/levels/interrupteurs/input_manager.c @@ -0,0 +1,57 @@ +#include "input_manager.h" + +void activate_switch(int y, int x) { + map * m = current_map(); + int i = coord_idx(y, x); + + if (!m->entities[i] || m->entities[i]->category != SWI) { + printf("There is no switch to touch in front of you.\n"); + return; + } else if (m->entities[i]->stats[0] == ACTIVATED) { + printf("This switch is already activated.\n"); + return; + } + + m->entities[i]->stats[0] = ACTIVATED; + + printf("You activate the %dth switch.\n", m->entities[i]->id); + + char buffer[2]; + sprintf(buffer, "%d", m->entities[i]->id); + strcat(played_order, buffer); +} + +void touch(void) { + int y, x; + player_in_dir_n(&y, &x, 1); + #ifdef inverse_xy + // @AGDB this bug is just an inversion of x and y, but the result is funny + activate_switch(x, y); + #else + activate_switch(y, x); + #endif +} + +void apply_input(command * c) { + if (!strcmp(c->command_buffer, "UP")) { + up(); + } else if (!strcmp(c->command_buffer, "UP_N")) { + up_n(atoi(c->args[0])); + } else if (!strcmp(c->command_buffer, "DOWN")) { + down(); + } else if (!strcmp(c->command_buffer, "DOWN_N")) { + down_n(atoi(c->args[0])); + } else if (!strcmp(c->command_buffer, "LEFT")) { + left(); + } else if (!strcmp(c->command_buffer, "LEFT_N")) { + left_n(atoi(c->args[0])); + } else if (!strcmp(c->command_buffer, "RIGHT")) { + right(); + } else if (!strcmp(c->command_buffer, "RIGHT_N")) { + right_n(atoi(c->args[0])); + } else if (!strcmp(c->command_buffer, "TOUCH")) { + touch(); + } else { + printf("Unknown input command.\n"); + } +} diff --git a/levels/interrupteurs/input_manager.h b/levels/interrupteurs/input_manager.h new file mode 100644 index 00000000..2d36b1ba --- /dev/null +++ b/levels/interrupteurs/input_manager.h @@ -0,0 +1,9 @@ +#ifndef INPUT_MANAGER_H +#define INPUT_MANAGER_H + +#include "../../engines/fullgame/agdbentures.h" +#include "custom_map.h" + +void apply_input(command * c); + +#endif // INPUT_MANAGER_H \ No newline at end of file diff --git a/levels/interrupteurs/main.c b/levels/interrupteurs/main.c index a2ee2b9d..f412d70d 100644 --- a/levels/interrupteurs/main.c +++ b/levels/interrupteurs/main.c @@ -6,10 +6,10 @@ * map_height: 7 * map_width: 10 * - * BUG: todo interrupteur - * tag: todo interrupteur + * BUG: several bugs. One is a string comparison forgetting the \0, one is a template used instead of malloc, one is a stack alloc, the last one is an inversion of y and x + * tag: inverse_xy stack_alloc template string_comparison * - * HINT1: todo interrupteur + * HINT1: What is the value of current_map()->entities for switches? */ /** @@ -43,69 +43,47 @@ * - ... and many others! */ -#include "agdbentures.h" -#include "read_input.h" +#include "../../engines/fullgame/agdbentures.h" +#include "events_map.h" +#include "input_manager.h" +#include "custom_map.h" -void apply_input(Command * c) { - if (!strcmp(c->command_buffer, "UP")) { - up(); - } else if (!strcmp(c->command_buffer, "UP_N")) { - up_n(atoi(c->args[0])); - } else if (!strcmp(c->command_buffer, "DOWN")) { - down(); - } else if (!strcmp(c->command_buffer, "DOWN_N")) { - down_n(atoi(c->args[0])); - } else if (!strcmp(c->command_buffer, "LEFT")) { - left(); - } else if (!strcmp(c->command_buffer, "LEFT_N")) { - left_n(atoi(c->args[0])); - } else if (!strcmp(c->command_buffer, "RIGHT")) { - right(); - } else if (!strcmp(c->command_buffer, "RIGHT_N")) { - right_n(atoi(c->args[0])); - } else if (!strcmp(c->command_buffer, "TOUCH")) { - touch(); - } else { - printf("Unknown input command.\n"); - } -} - int main() { - CommandList inputs; char * filename = "input.txt"; - if (read_input_file(filename, &inputs)) { + if (!init_inputs(filename)) { printf("Error while reading the inputs!\n"); - free_commands(&inputs); - exit(1); + return EXIT_FAILURE; } - load_map(str_map, "overworld"); - load_events(); + init_map(load_map("overworld", 10, 7, 1, 3, RIGHT)); + add_event(verify_exit); + add_event(verify_switch_order); + add_event(drown); - Command * command = inputs.command; + command * com = get_next_input(); // The Holy Main Loop while (!exit_main_loop) { - apply_input(command); + apply_input(com); /* since the events may be applied if the input is FORWARD_N, we need to check to avoid triggering the same event twice*/ if (!exit_main_loop) apply_events(); + + com = get_and_free_input(com); // plus aucun input mais on a pas encore gagné ou perdu -> défaite - // exception à la règle qui dit d'appeler level_* seulement depuis une résolution d'event - if (command->next == NULL && !exit_main_loop) + if (com == NULL && !exit_main_loop) level_failed(); - else - command = command->next; } - free_commands(&inputs); - unload_map(); + free_command(com); + remove_all_events(); + free_map_stack(); - return 0; + return EXIT_SUCCESS; } diff --git a/levels/interrupteurs/map.c b/levels/interrupteurs/map.c deleted file mode 100644 index f2f0371f..00000000 --- a/levels/interrupteurs/map.c +++ /dev/null @@ -1,208 +0,0 @@ -#include "map.h" - -static map_stack mstack; - -#ifdef template -interactable template_swi = {0,0,DEACTIVATED,-1}; -#endif - -#ifndef stack_alloc -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; - #endif - swi->x = x; - swi->y = y; - swi->number = n; - return swi; -} -#else//stack_alloc -interactable * create_switch(int x, int y, int n) { - // @AGDB does not allocate but returns a local address instead - interactable swi; - swi.x = x; - swi.y = y; - swi.state = DEACTIVATED; - swi.number = n; - return &swi; -} -#endif//stack_alloc - - -void init_map_stack(void) -{ - mstack.length = 0; -} - -map* current_map (void) -{ - return &mstack.maps[mstack.length-1]; -} - - -void load_map(const char * str_map, const char * new_name) { - map *m; - - /*BUGIDEA : forgetting to save player position */ - if (mstack.length > 0) { - m = current_map(); - m->player_x = player_x; - m->player_y = player_y; - } - - mstack.length++; - - m = current_map(); - m->exit_x = -1; - m->exit_y = -1; - m->player_x = -1; - m->player_y = -1; - - /* initialize the map */ - /* width is the num of characters in the first line */ - const char *ch = strchr (str_map, '\n'); - if (! ch) { - fprintf (stderr, "Error: cannot find line return in map description!"); - exit(EXIT_FAILURE); - } - m->width = ch - str_map; - m->height = 0; - while (ch) { - ch = strchr (ch+1, '\n'); - m->height++; - } - - /* printf ("Size of map: %dx%d\n", m->width, m->height); */ - - /* allocating enough space to hold the map */ - m->name = (char*) malloc(sizeof(char) * (strlen(new_name) + 1)); - strcpy(m->name, new_name); - m->floor = (floortype*)malloc(m->width * m->height * sizeof(floortype)); - m->inter = (interactable**) malloc(m->width * m->height * sizeof(interactable)); - - // we have 4 switches only - int nth_swi = 0; - - ch = str_map; - floortype f; - interactable * i; - for (int y=0; y<m->height; y++) { - for (int x=0; x<m->width; x++) { - f = EMPTY; - i = NULL; - switch (*ch) { - case '+': - case '|': - case '-': f=WALL; break; - case ' ': f=GRASS; break; - case '~': f=WATER; break; - case 'D': f=DOOR; - m->door_x = x; - m->door_y = y; - break; - case '>': f=GRASS; - m->player_x = x; - m->player_y = y; - break; - case '@': f=GRASS; - m->exit_x = x; - m->exit_y = y; - break; - case 'X': f=WALL; - assert(nth_swi < 4); - i = create_switch(x, y, nth_swi); - nth_swi++; - break; - default: - fprintf (stderr, "Unknown map character '%c'!\n",*ch); - } - m->floor[y*m->width + x] = f; - m->inter[y*m->width+x] = i; - ch++; - } - assert (*ch == '\n'); - ch++; - } - - player_x = m->player_x; - player_y = m->player_y; - assert (player_x != -1 && player_y != -1); -} - - -void unload_map (void) -{ - map *m = current_map(); - - 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]); - } - - free(m->inter); - - mstack.length--; - m = current_map(); - - /* place player at saved position on previous map */ - player_x = m->player_x; - player_y = m->player_y; -} - -int coord_idx(int y, int x) -{ - map *m = current_map(); - return y * m->width + x; -} - -int player_idx(void) -{ - return coord_idx (player_y, player_x); -} - -bool is_obstacle(int y, int x) -{ - map *m = current_map(); - int i = coord_idx(y, x); - - floortype f = m->floor[i]; - - return (f == WALL || f == DOOR || f == ON_SWI || f == OFF_SWI); -} - - -bool is_water(int y, int x) -{ - map *m = current_map(); - return m->floor[coord_idx(y,x)] == WATER; -} - -void activate_switch(int y, int x) { - map * m = current_map(); - int i = coord_idx(y, x); - - if (m->inter[i] == NULL) { - printf("There is no switch to touch in front of you.\n"); - return; - } else if (m->inter[i]->state == ACTIVATED) { - printf("This switch is already activated.\n"); - return; - } - - m->inter[i]->state = ACTIVATED; - - printf("You activate the %dth switch.\n", m->inter[i]->number); - - char buffer[2]; - sprintf(buffer, "%d", m->inter[i]->number); - strcat(played_order, buffer); -} diff --git a/levels/interrupteurs/map.h b/levels/interrupteurs/map.h deleted file mode 100644 index 7753cd59..00000000 --- a/levels/interrupteurs/map.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef MAP_H -#define MAP_H - -#include "agdbentures.h" - -#define ACTIVATED 1 -#define DEACTIVATED 0 - -typedef enum { - EMPTY, /* unreachable area */ - GRASS, - WALL, - WATER, - DOOR, - OFF_SWI, - ON_SWI, - FLOORTYPE_N, /* last one to know how many types there are */ -} floortype; - -typedef struct { - int x; - int y; - int number; - int state; -} interactable; - -typedef struct { - char * name; // shop, overworld, dungeon_Nth_level... - floortype *floor; /* matrix of floortype */ - interactable ** inter; - int width; /* size of map */ - int height; - int player_x; /* position of player */ - int player_y; - int exit_x; - int exit_y; /* position of the exit if any */ - int door_x; - int door_y; -} map; - -#define MAX_MAPS 256 - -typedef struct { - map maps[MAX_MAPS]; - int length; -} map_stack; - - -void init_map_stack(void); -void load_map(const char * str_map, const char * new_name); -void unload_map(void); - -int coord_idx(int y, int x); -int player_idx(void); - -map* current_map (void); - -bool is_obstacle(int y, int x); - -bool is_water(int y, int x); - -/** - * active l'interrupteur donné, - * ou affiche un message s'il n'y en a pas à cette position, - * ou affiche un message s'il est déjà activé. - */ -void activate_switch(int y, int x); - -#endif//MAP_H diff --git a/levels/interrupteurs/read_input.c b/levels/interrupteurs/read_input.c deleted file mode 100644 index a28df349..00000000 --- a/levels/interrupteurs/read_input.c +++ /dev/null @@ -1,83 +0,0 @@ -#include "read_input.h" - -int read_input_file(const char* filename, CommandList* commands) { - - FILE* input_file = fopen(filename, "r"); - - size_t command_length; - char* line = NULL; - - // Push a sentinel - Command sentinel; - commands->command = &sentinel; - Command* prev_command = commands->command; - - while (!feof(input_file)) { - int linesize = getline(&line, &command_length, input_file); - - if (linesize != -1) { - Command* next = allocate_command(line, linesize); - prev_command->next = next; - prev_command = next; - } else { - free(line); - } - line = NULL; - } - - // Remove the sentinel - commands->command = commands->command->next; - - fclose(input_file); - return 0; -} - -Command* allocate_command(char* command_buffer, size_t buffer_len) { - // Allocate the new command structure - Command* command = malloc(sizeof(Command)); - - command->command_buffer = command_buffer; - command_buffer[buffer_len-1] = '\0'; - - // count args and replace space with \0 - command->n_args = 0; - for (size_t current_char_index = 0; current_char_index < buffer_len; current_char_index++) { - if (command_buffer[current_char_index] == ' ') { - command->n_args ++; - command_buffer[current_char_index] = '\0'; - } - } - - // redo a pass to place args pointers - if (command->n_args > 0) - command->args = malloc(sizeof(char*) * command->n_args); - else - command->args = NULL; - int n_args = 0; - for (size_t current_char_index = 0; n_args < command->n_args; current_char_index++) { - if (command_buffer[current_char_index] == '\0') { - command->args[n_args] = command_buffer + current_char_index + 1; - n_args++; - } - } - - command->next = NULL; - - return command; -} - -void free_command(Command* command) { - // Free next commands - if (command->next != NULL) { - free_command(command->next); - } - // Free current command - free(command->command_buffer); // Free the buffer allocated by getline - if (command->args != NULL) - free(command->args); - free(command); -} - -void free_commands(CommandList* commands) { - free_command(commands->command); -} diff --git a/levels/interrupteurs/read_input.h b/levels/interrupteurs/read_input.h deleted file mode 100644 index 5627a69d..00000000 --- a/levels/interrupteurs/read_input.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef READ_INPUT_H -#define READ_INPUT_H - -#include <stdlib.h> -#include <stdio.h> - -// Command structure to hold a single instruction with arguments as strings -struct Command { - char* command_buffer; - int n_args; - char** args; - - struct Command* next; -}; - -typedef struct Command Command; - -// A list of commands with its size -typedef struct { - Command* command; -} CommandList; - -// Reads the whole given input file and returns a list of commands. -// 1 means means the function failed and 0 succeeded -int read_input_file(const char* filename, CommandList* commands); - -void free_commands(CommandList* commands); - -// Takes a command buffer as input and prepare all the interior data in a new allocated buffer -Command* allocate_command(char* command_buffer, size_t buffer_len); - -#endif -- GitLab