From 96f413eb14885bf490b9ed6031b4d3bf82290659 Mon Sep 17 00:00:00 2001 From: attilavs2 Date: Sun, 21 Jul 2024 02:42:50 +0200 Subject: [PATCH 1/8] =?UTF-8?q?D=C3=A9but=20de=20pathfinding=20pour=20les?= =?UTF-8?q?=20NPCs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dialogs.c | 8 +-- src/game.c | 2 +- src/npc.c | 159 +++++++++++++++++++++++++++++++++++++++----------- src/npc.h | 17 +++++- 4 files changed, 147 insertions(+), 39 deletions(-) diff --git a/src/dialogs.c b/src/dialogs.c index cdb5ce5..413bef2 100644 --- a/src/dialogs.c +++ b/src/dialogs.c @@ -68,7 +68,7 @@ int showtext_opt(Game *game, bopti_image_t *face, char *text, for(i=0;i<=BOX_HEIGHT;i++){ /* Redrawing the entire screen, because maybe there was no dialog displayed before. */ - update_npc(game); + update_npcs(game); draw(game); /* Fill the dialog box with white */ @@ -181,7 +181,7 @@ int showtext_opt(Game *game, bopti_image_t *face, char *text, /* Run another little fancy animation if we should. */ for(i=BOX_HEIGHT;i>0;i--){ /* It is the same as the start animation. */ - update_npc(game); + update_npcs(game); draw(game); drect(0, 0, DWIDTH, i*PXSIZE, C_WHITE); drect(0, i*PXSIZE, DWIDTH, (i+1)*PXSIZE, C_BLACK); @@ -302,7 +302,7 @@ int _choice_call_before_end(Game *game, [[maybe_unused]] unsigned int org_i) { /* Make a little animation because we looove little animations ;) */ for(i=DWIDTH/8+1;i>0;i--){ /* I'm drawing the same box as on the start animation */ - update_npc(game); + update_npcs(game); draw(game); showtext_opt(game, _face, _text, NULL, false, false, NULL, 0, false, _i, false); @@ -379,4 +379,4 @@ void initiate_dialog_sequence(Game *game, bopti_image_t *face, uint32_t dialogNu showtext_dialog( game, face, text, true, true ); if (nextOther!=-1) initiate_dialog_sequence( game, face, nextOther ); } -} \ No newline at end of file +} diff --git a/src/game.c b/src/game.c index 135c336..bbdbccc 100644 --- a/src/game.c +++ b/src/game.c @@ -24,7 +24,7 @@ extern uint32_t nbNPC; void game_logic(Game *game) { - update_npc( game ); + update_npcs( game ); /* we check if interactions are possible close to the player */ for( uint32_t i=0; imap_level->nbextradata; i++ ) diff --git a/src/npc.c b/src/npc.c index 86a62b7..ac5cf24 100644 --- a/src/npc.c +++ b/src/npc.c @@ -15,7 +15,7 @@ extern bopti_image_t demo_PNJ_img; /* the color of the text to go to the next dialog phase */ -/* it improves readability to have somathing lighter */ +/* it improves readability to have something lighter */ #if defined(FXCG50) #define PATH_COLOR C_RED #else @@ -31,39 +31,133 @@ float length( float x, float y ) return sqrtf( x*x+y*y ); } -void update_npc( [[maybe_unused]] Game *game) +int npc_clear_path(NPC *npc) { - for( uint32_t u=0; upath_length = 0; + free(npc->xpath); + free(npc->ypath); + npc->xpath = malloc(4); + npc->ypath = malloc(4); + if(npc->xpath == NULL || npc->ypath == NULL) return 1; + return 0; +} - if (vecN>0.5f) - { - vecX /= vecN*2.0; - vecY /= vecN*2.0; - } - else - { - npcRPG[u].currentPoint++; - npcRPG[u].currentPoint = npcRPG[u].currentPoint % npcRPG[u].path_length; - } +//Won't work on static NPCs, use npc_clear_path before or make them on the heap +int npc_append_path(uint16_t x, uint16_t y, NPC *npc) +{ + npc->path_length++; + realloc(npc->xpath, npc->path_length); + realloc(npc->ypath, npc->path_length); + if(npc->xpath == NULL || npc->ypath == NULL) return 1; + npc->xpath[npc->path_length-1] = x; + npc->ypath[npc->path_length-1] = y; +} - npcRPG[u].curx += vecX; - npcRPG[u].cury += vecY; - - } - } +uint8_t *as_reconstruct_path(uint8_t *came_from, int came_size, uint8_t *current, int curr_size) +{ } +//Returns non zero error code on failure +//Custom a* implemetation +//(Very much unfinished for now) +int npc_pathfind(int dest_x, int dest_y, Map *full_map, NPC *npc) +{ + uint32_t w = full_map->w; + uint32_t h = full_map->h; + uint32_t x = npc->x; + uint32_t y = npc->y; + uint32_t spos = y*w+x; + uint8_t *map = full_map->walkable; + if(map[spos]) return 2; + + npc_clear_path(npc); + + uint8_t *came_from = malloc(w*h); + + uint8_t *gscore = malloc(w*h); + gscore[spos] = 0; + + uint8_t *fscore = malloc(w*h); + fscore[spos] = 0; + + int tx = x; + int ty = y; + int tpos = spos; + int bx; + int by; + int i, j; + + for(int iter=0; iter < 1024; iter++) + { + //Check surronding tiles for cost + bx = x-1; + by = y-1; + for(i = tx-1; i < tx+1; i++) + { + //Out of bounds (admissible) + if(i < 0) continue; + //Out of bounds (unforgiveable) + if(i > w) break; + for(j = ty-1; j < ty+1; j++) + { + //Out of bounds (admissible) + if(j < 0) continue; + //Out of bounds (joever for it) + if(j > h) break; + //Has collision + if(map[j*w+i]) continue; + if(i == dest_x && j == dest_y){ + return npc_append_path(i, j, npc); + } + //Calculate score (direct distance) + int deltX = tx-dest_x; + int deltY = ty-dest_y; + int tscore = sqrtf(deltX*deltX+deltY*deltY); + } + } + + } + return 1; +} + +//Refactoring to make adding complexity cleaner +void update_npcs([[maybe_unused]] Game *game) +{ + for( uint32_t u=0; uhasPath && npc->paused==true) return; + + float vecX = (float) (npc->xpath[ npc->currentPoint ] + + npc->x) - npc->curx; + float vecY = (float) (npc->ypath[ npc->currentPoint ] + + npc->y) - npc->cury; + float vecN = length(vecX, vecY); + + if (vecN>0.5f) + { + vecX /= vecN*2.0; + vecY /= vecN*2.0; + } + else + { + npc->currentPoint++; + npc->currentPoint = npc->currentPoint % npc->path_length; + } + + npc->curx += vecX; + npc->cury += vecY; + +} + +//TODO ; Add malloc() failure case void reload_npc(Game *game) { if (npcRPG!=NULL) @@ -76,7 +170,7 @@ void reload_npc(Game *game) nbNPC = 0; - for (uint32_t u=0; umap_level->nbextradata; u++) //uint pour enlever un warning + for (uint32_t u=0; umap_level->nbextradata; u++) { ExtraData *Data = &game->map_level->extradata[u]; @@ -89,7 +183,7 @@ void reload_npc(Game *game) npcRPG = (NPC*) malloc( nbNPC * sizeof(NPC) ); int currentNPC=0; - for (uint32_t u=0; umap_level->nbextradata; u++) //uint pour enlever un warning + for (uint32_t u=0; umap_level->nbextradata; u++) { ExtraData *Data = &game->map_level->extradata[u]; @@ -114,12 +208,11 @@ void reload_npc(Game *game) void npc_draw(Game *game) { Player *pl = &game->player; - for (uint32_t u=0; uhasPath==1) /* this NPC has a trajectory */ { diff --git a/src/npc.h b/src/npc.h index e7e1e12..b32f485 100644 --- a/src/npc.h +++ b/src/npc.h @@ -8,6 +8,15 @@ #include "game.h" #include "memory.h" +enum +{ + + NPC_NONE = 0, + NPC_FRIENDLY = 1, //The player's team + NPC_HOSTILE = 2, //to the player + NPC_ALL = 3 + +} NPC_groups; typedef struct { @@ -29,6 +38,10 @@ typedef struct int16_t *xpath; int16_t *ypath; + int8_t hostile_to_group; + + + /* is the current NPC in pause (during dialog) */ bool paused; } NPC; @@ -39,7 +52,9 @@ typedef struct * map! */ void npc_draw(Game *game); -void update_npc(Game *game); +void update_npcs(Game *game); + +void update_npc(NPC *npc); void reload_npc(Game *game); From e7ada9d3dab86a70837b4d39f008ff6538d3657d Mon Sep 17 00:00:00 2001 From: attilavs2 Date: Sun, 21 Jul 2024 11:50:12 +0200 Subject: [PATCH 2/8] Pathfinding NPC complet (a tester) --- src/npc.c | 110 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 77 insertions(+), 33 deletions(-) diff --git a/src/npc.c b/src/npc.c index ac5cf24..2910198 100644 --- a/src/npc.c +++ b/src/npc.c @@ -53,9 +53,38 @@ int npc_append_path(uint16_t x, uint16_t y, NPC *npc) npc->ypath[npc->path_length-1] = y; } -uint8_t *as_reconstruct_path(uint8_t *came_from, int came_size, uint8_t *current, int curr_size) +void as_clean(uint8_t *visited, uint8_t *gscore, uint8_t *fscore) { + free(visited); + free(gscore); + free(fscore); +} +int as_reconstruct_path(uint16_t *came_from, int w, int h, uint16_t spos, + uint16_t dest, NPC *npc) +{ + if(npc_clear_path(npc)) return 1; + + uint16_t next = came_from[dest]; + + int returnv = 0; + + while(true) + { + if(npc_append_path(next%w, next/h, npc)) + { + returnv = 1; + break; + } + if(next == dest){ + returnv = 0; + break; + } + next = came_from[next]; + } + free(came_from); + + return returnv; } //Returns non zero error code on failure @@ -63,60 +92,75 @@ uint8_t *as_reconstruct_path(uint8_t *came_from, int came_size, uint8_t *current //(Very much unfinished for now) int npc_pathfind(int dest_x, int dest_y, Map *full_map, NPC *npc) { + int i, j; + uint32_t w = full_map->w; uint32_t h = full_map->h; - uint32_t x = npc->x; - uint32_t y = npc->y; + uint32_t x = floor(npc->curx); + uint32_t y = floor(npc->cury); uint32_t spos = y*w+x; uint8_t *map = full_map->walkable; if(map[spos]) return 2; + if(map[dest_y*w+dest_x]) return 2; npc_clear_path(npc); - uint8_t *came_from = malloc(w*h); + uint8_t *visited = malloc(w*h); + for(i=0; i w) break; - for(j = ty-1; j < ty+1; j++) - { - //Out of bounds (admissible) - if(j < 0) continue; - //Out of bounds (joever for it) - if(j > h) break; - //Has collision - if(map[j*w+i]) continue; - if(i == dest_x && j == dest_y){ - return npc_append_path(i, j, npc); - } - //Calculate score (direct distance) - int deltX = tx-dest_x; - int deltY = ty-dest_y; - int tscore = sqrtf(deltX*deltX+deltY*deltY); - } + if(visited[i]) continue; + if(fscore[i] > bscore) continue; + bx = i%w; + by = i/w; + bscore = fscore[i]; + } + if(bx == dest_x && by == dest_y) + { + as_clean(visited, gscore, fscore); + return as_reconstruct_path(came_from, w, h, spos, dest_y*w+dest_x, npc); } + visited[by*w+bx] = 1; + + int att_score; + + for(i = bx-1; i < bx+1; i++) + { + for(j = by-1; j < by+1; j++) + { + if(map[j*w+i]) continue; + att_score = gscore[by*w+bx] + lenght(bx-i, by-j); + if(att_score < gscore[j*w+i]) + { + came_from[j*w+i] = by*w+bx; + gscore[j*w+i] = att_score; + fscore[j*w+i] = att_score + lenght(i-dest_x, j-dest_y); + if(visited[j*w+i]) visited[j*w+i] = 0; + } + } + } } return 1; } From 6de033d259ee4064b09257e7fd7a0504b368f637 Mon Sep 17 00:00:00 2001 From: attilavs2 Date: Sun, 21 Jul 2024 12:26:41 +0200 Subject: [PATCH 3/8] =?UTF-8?q?D=C3=A9but=20de=20d=C3=A9bbugage=20de=20npc?= =?UTF-8?q?=5Fpathfind?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/npc.c | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/npc.c b/src/npc.c index 2910198..46dbe26 100644 --- a/src/npc.c +++ b/src/npc.c @@ -33,6 +33,8 @@ float length( float x, float y ) int npc_clear_path(NPC *npc) { + npc->currentPoint = 0; + npc->hasPath = 1; npc->path_length = 0; free(npc->xpath); free(npc->ypath); @@ -46,11 +48,12 @@ int npc_clear_path(NPC *npc) int npc_append_path(uint16_t x, uint16_t y, NPC *npc) { npc->path_length++; - realloc(npc->xpath, npc->path_length); - realloc(npc->ypath, npc->path_length); + npc->xpath = realloc(npc->xpath, npc->path_length); + npc->ypath = realloc(npc->ypath, npc->path_length); if(npc->xpath == NULL || npc->ypath == NULL) return 1; - npc->xpath[npc->path_length-1] = x; - npc->ypath[npc->path_length-1] = y; + npc->xpath[npc->path_length-1] = x - npc->x; + npc->ypath[npc->path_length-1] = y - npc->y; + return 0; } void as_clean(uint8_t *visited, uint8_t *gscore, uint8_t *fscore) @@ -60,7 +63,7 @@ void as_clean(uint8_t *visited, uint8_t *gscore, uint8_t *fscore) free(fscore); } -int as_reconstruct_path(uint16_t *came_from, int w, int h, uint16_t spos, +int as_reconstruct_path(int16_t *came_from, int w, int h, uint16_t spos, uint16_t dest, NPC *npc) { if(npc_clear_path(npc)) return 1; @@ -89,7 +92,7 @@ int as_reconstruct_path(uint16_t *came_from, int w, int h, uint16_t spos, //Returns non zero error code on failure //Custom a* implemetation -//(Very much unfinished for now) +//(doesn't work yet) int npc_pathfind(int dest_x, int dest_y, Map *full_map, NPC *npc) { int i, j; @@ -112,14 +115,13 @@ int npc_pathfind(int dest_x, int dest_y, Map *full_map, NPC *npc) for(i=0; i w || i < 0) continue; + for(j = by-1; j < by+1; j++) + { + if(j > h || j < 0) continue; + if(map[j*w+i]) continue; + att_score = gscore[by*w+bx] + length(bx-i, by-j); + if(att_score < gscore[j*w+i]) { - if(map[j*w+i]) continue; - att_score = gscore[by*w+bx] + lenght(bx-i, by-j); - if(att_score < gscore[j*w+i]) - { - came_from[j*w+i] = by*w+bx; - gscore[j*w+i] = att_score; - fscore[j*w+i] = att_score + lenght(i-dest_x, j-dest_y); - if(visited[j*w+i]) visited[j*w+i] = 0; - } + came_from[j*w+i] = by*w+bx; + gscore[j*w+i] = att_score; + fscore[j*w+i] = att_score + length(i-dest_x, j-dest_y); + if(visited[j*w+i]) visited[j*w+i] = 0; } + } } } - return 1; + as_clean(visited, gscore, fscore); + free(came_from); + return 3; } //Refactoring to make adding complexity cleaner From c0d0c23ef99e36a6de85a558491a42ae98bc107b Mon Sep 17 00:00:00 2001 From: attilavs2 Date: Tue, 23 Jul 2024 17:54:22 +0200 Subject: [PATCH 4/8] =?UTF-8?q?npc=5Fpathfind=20compl=C3=A9t=C3=A9*=20*Tes?= =?UTF-8?q?t=C3=A9=20avec=20un=20nombre=20limit=C3=A9=20de=20cas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/npc.c | 78 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 51 insertions(+), 27 deletions(-) diff --git a/src/npc.c b/src/npc.c index 46dbe26..8f84f66 100644 --- a/src/npc.c +++ b/src/npc.c @@ -48,8 +48,8 @@ int npc_clear_path(NPC *npc) int npc_append_path(uint16_t x, uint16_t y, NPC *npc) { npc->path_length++; - npc->xpath = realloc(npc->xpath, npc->path_length); - npc->ypath = realloc(npc->ypath, npc->path_length); + npc->xpath = realloc(npc->xpath, npc->path_length*2); + npc->ypath = realloc(npc->ypath, npc->path_length*2); if(npc->xpath == NULL || npc->ypath == NULL) return 1; npc->xpath[npc->path_length-1] = x - npc->x; npc->ypath[npc->path_length-1] = y - npc->y; @@ -63,36 +63,54 @@ void as_clean(uint8_t *visited, uint8_t *gscore, uint8_t *fscore) free(fscore); } -int as_reconstruct_path(int16_t *came_from, int w, int h, uint16_t spos, - uint16_t dest, NPC *npc) +int as_reconstruct_path(int16_t *came_from, int w, int h, int16_t spos, + int16_t dest, NPC *npc) { - if(npc_clear_path(npc)) return 1; + if(npc_clear_path(npc) goto as_recons_fail; - uint16_t next = came_from[dest]; + int16_t next = came_from[dest]; - int returnv = 0; + int i; - while(true) + for(i = 0; i < 64; i++) { - if(npc_append_path(next%w, next/h, npc)) - { - returnv = 1; - break; - } - if(next == dest){ - returnv = 0; - break; - } + if(next == -1) goto as_recons_fail; + if(npc_append_path(next%w, next/h, npc)) goto as_recons_fail; + next = came_from[next]; + if(next == spos){ + if(npc_append_path(spos%w, spos/h, npc)) + goto as_recons_fail; + break; + } } + + uint16_t tx, ty; + + //Flip the path because it started from the end + + for(i = 1; i < npc->path_length/2; i++) + { + tx = npc->xpath[i]; + ty = npc->ypath[i]; + npc->xpath[i] = npc->xpath[npc->path_length-i]; + npc->ypath[i] = npc->ypath[npc->path_length-i]; + npc->ypath[npc->path_length-i] = tx; + npc->ypath[npc->path_length-i] = ty; + } + + return 0; + + as_recons_fail: + free(came_from); - return returnv; + return 1; } //Returns non zero error code on failure //Custom a* implemetation -//(doesn't work yet) +//Unoptimized, may become an issue int npc_pathfind(int dest_x, int dest_y, Map *full_map, NPC *npc) { int i, j; @@ -110,6 +128,7 @@ int npc_pathfind(int dest_x, int dest_y, Map *full_map, NPC *npc) uint8_t *visited = malloc(w*h); for(i=0; i bscore) continue; bx = i%w; by = i/w; @@ -148,25 +168,29 @@ int npc_pathfind(int dest_x, int dest_y, Map *full_map, NPC *npc) int att_score; - for(i = bx-1; i < bx+1; i++) + for(i = bx-1; i < bx+2; i++) { - if(i > w || i < 0) continue; - for(j = by-1; j < by+1; j++) + if(i < 0) continue; + if(i > w) break; + for(j = by-1; j < by+2; j++) { - if(j > h || j < 0) continue; + if(j > h ) break; + if(j < 0) continue; if(map[j*w+i]) continue; - att_score = gscore[by*w+bx] + length(bx-i, by-j); + att_score = gscore[by*w+bx] + ceil(length(bx-i,by-j)); if(att_score < gscore[j*w+i]) { came_from[j*w+i] = by*w+bx; gscore[j*w+i] = att_score; - fscore[j*w+i] = att_score + length(i-dest_x, j-dest_y); + fscore[j*w+i] = att_score + ceil(length(dest_x-i, dest_y-j)); if(visited[j*w+i]) visited[j*w+i] = 0; } } } } + as_clean(visited, gscore, fscore); + free(came_from); return 3; } From b5a5ae2a5a51fea06ee8e30ecb6c59afe18469ff Mon Sep 17 00:00:00 2001 From: attilavs2 Date: Tue, 23 Jul 2024 17:57:22 +0200 Subject: [PATCH 5/8] =?UTF-8?q?Fix=20:=20Fuite=20de=20m=C3=A9moire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/npc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/npc.c b/src/npc.c index 8f84f66..1002936 100644 --- a/src/npc.c +++ b/src/npc.c @@ -99,6 +99,8 @@ int as_reconstruct_path(int16_t *came_from, int w, int h, int16_t spos, npc->ypath[npc->path_length-i] = ty; } + free(came_from); + return 0; as_recons_fail: From 49303ba6adc3025dea1a210e45e40100fd0a1965 Mon Sep 17 00:00:00 2001 From: attilavs2 Date: Tue, 23 Jul 2024 18:06:42 +0200 Subject: [PATCH 6/8] =?UTF-8?q?npc=20:=20ajout=20des=20d=C3=A9clarations?= =?UTF-8?q?=20des=20nouvelles=20fonctions,=20typo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/npc.c | 3 +-- src/npc.h | 11 +++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/npc.c b/src/npc.c index 1002936..752aad6 100644 --- a/src/npc.c +++ b/src/npc.c @@ -44,7 +44,6 @@ int npc_clear_path(NPC *npc) return 0; } -//Won't work on static NPCs, use npc_clear_path before or make them on the heap int npc_append_path(uint16_t x, uint16_t y, NPC *npc) { npc->path_length++; @@ -66,7 +65,7 @@ void as_clean(uint8_t *visited, uint8_t *gscore, uint8_t *fscore) int as_reconstruct_path(int16_t *came_from, int w, int h, int16_t spos, int16_t dest, NPC *npc) { - if(npc_clear_path(npc) goto as_recons_fail; + if(npc_clear_path(npc)) goto as_recons_fail; int16_t next = came_from[dest]; diff --git a/src/npc.h b/src/npc.h index b32f485..29aa22c 100644 --- a/src/npc.h +++ b/src/npc.h @@ -46,7 +46,18 @@ typedef struct bool paused; } NPC; +//Frees then malloc()s a new path to npc +//Useful if you want to safely edit a path +int npc_clear_path(NPC *npc); +//Adds point x,y to the path of npc +//Won't work on static NPCs, use npc_clear_path before or make them on the heap +int npc_append_path(uint16_t x, uint16_t y, NPC *npc); + +//Clears the NPCs path and creates a new one going to dest, +//avoiding non-walkable tiles +//Returns non-zero on failure +int npc_pathfind(int dest_x, int dest_y, Map *full_map, NPC *npc); /* Draws the player player. This function should be called after drawing the * map! */ From 52da4be1af781d3775966d1c83e4ad6c8de37c6e Mon Sep 17 00:00:00 2001 From: attilavs2 Date: Tue, 23 Jul 2024 18:17:02 +0200 Subject: [PATCH 7/8] npc : correction de warnings --- src/npc.c | 14 +++++++------- src/npc.h | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/npc.c b/src/npc.c index 752aad6..12dd57b 100644 --- a/src/npc.c +++ b/src/npc.c @@ -69,7 +69,7 @@ int as_reconstruct_path(int16_t *came_from, int w, int h, int16_t spos, int16_t next = came_from[dest]; - int i; + unsigned int i; for(i = 0; i < 64; i++) { @@ -112,9 +112,9 @@ int as_reconstruct_path(int16_t *came_from, int w, int h, int16_t spos, //Returns non zero error code on failure //Custom a* implemetation //Unoptimized, may become an issue -int npc_pathfind(int dest_x, int dest_y, Map *full_map, NPC *npc) +int npc_pathfind(uint32_t dest_x, uint32_t dest_y, Map *full_map, NPC *npc) { - int i, j; + uint32_t i, j; uint32_t w = full_map->w; uint32_t h = full_map->h; @@ -143,8 +143,8 @@ int npc_pathfind(int dest_x, int dest_y, Map *full_map, NPC *npc) fscore[spos] = length(dest_x-x, dest_y-y); uint8_t bscore; - int bx = x; - int by = y; + uint32_t bx = x; + uint32_t by = y; for(int iter=0; iter < 1024; iter++) { @@ -171,12 +171,10 @@ int npc_pathfind(int dest_x, int dest_y, Map *full_map, NPC *npc) for(i = bx-1; i < bx+2; i++) { - if(i < 0) continue; if(i > w) break; for(j = by-1; j < by+2; j++) { if(j > h ) break; - if(j < 0) continue; if(map[j*w+i]) continue; att_score = gscore[by*w+bx] + ceil(length(bx-i,by-j)); if(att_score < gscore[j*w+i]) @@ -199,6 +197,8 @@ int npc_pathfind(int dest_x, int dest_y, Map *full_map, NPC *npc) //Refactoring to make adding complexity cleaner void update_npcs([[maybe_unused]] Game *game) { + //npc_pathfind(game->player.x, game->player.y, game->map_level, &npcRPG[0]); + for( uint32_t u=0; u Date: Sat, 27 Jul 2024 14:02:04 +0200 Subject: [PATCH 8/8] Continue working on pathfinding --- src/npc.c | 105 ++++++++++++++++++++++++++++++++++++------------------ src/npc.h | 15 ++++++-- 2 files changed, 82 insertions(+), 38 deletions(-) diff --git a/src/npc.c b/src/npc.c index 12dd57b..40ea8ad 100644 --- a/src/npc.c +++ b/src/npc.c @@ -34,7 +34,7 @@ float length( float x, float y ) int npc_clear_path(NPC *npc) { npc->currentPoint = 0; - npc->hasPath = 1; + npc->hasPath = 0; npc->path_length = 0; free(npc->xpath); free(npc->ypath); @@ -46,10 +46,10 @@ int npc_clear_path(NPC *npc) int npc_append_path(uint16_t x, uint16_t y, NPC *npc) { - npc->path_length++; - npc->xpath = realloc(npc->xpath, npc->path_length*2); - npc->ypath = realloc(npc->ypath, npc->path_length*2); + npc->xpath = realloc(npc->xpath, npc->path_length*2+2); + npc->ypath = realloc(npc->ypath, npc->path_length*2+2); if(npc->xpath == NULL || npc->ypath == NULL) return 1; + npc->path_length++; npc->xpath[npc->path_length-1] = x - npc->x; npc->ypath[npc->path_length-1] = y - npc->y; return 0; @@ -73,12 +73,14 @@ int as_reconstruct_path(int16_t *came_from, int w, int h, int16_t spos, for(i = 0; i < 64; i++) { - if(next == -1) goto as_recons_fail; - if(npc_append_path(next%w, next/h, npc)) goto as_recons_fail; + if(npc_append_path((next%w)*T_WIDTH,(next/h)*T_HEIGHT, npc)) + { + goto as_recons_fail; + } next = came_from[next]; if(next == spos){ - if(npc_append_path(spos%w, spos/h, npc)) + if(npc_append_path((spos%w)*T_WIDTH,(spos/h)*T_HEIGHT, npc)) goto as_recons_fail; break; } @@ -88,18 +90,20 @@ int as_reconstruct_path(int16_t *came_from, int w, int h, int16_t spos, //Flip the path because it started from the end - for(i = 1; i < npc->path_length/2; i++) + for(i = 0; i < npc->path_length/2; i++) { tx = npc->xpath[i]; ty = npc->ypath[i]; - npc->xpath[i] = npc->xpath[npc->path_length-i]; - npc->ypath[i] = npc->ypath[npc->path_length-i]; - npc->ypath[npc->path_length-i] = tx; - npc->ypath[npc->path_length-i] = ty; + npc->xpath[i] = npc->xpath[npc->path_length-i-1]; + npc->ypath[i] = npc->ypath[npc->path_length-i-1]; + npc->ypath[npc->path_length-i-1] = tx; + npc->ypath[npc->path_length-i-1] = ty; } free(came_from); + npc->hasPath = true; + return 0; as_recons_fail: @@ -112,16 +116,21 @@ int as_reconstruct_path(int16_t *came_from, int w, int h, int16_t spos, //Returns non zero error code on failure //Custom a* implemetation //Unoptimized, may become an issue -int npc_pathfind(uint32_t dest_x, uint32_t dest_y, Map *full_map, NPC *npc) +int npc_pathfind(int32_t dest_x, int32_t dest_y, Map *full_map, NPC *npc) { - uint32_t i, j; + int32_t i, j; + + int32_t w = full_map->w; + int32_t h = full_map->h; + int32_t x = floor(npc->curx)/T_WIDTH; + int32_t y = floor(npc->cury)/T_HEIGHT; + dest_x /= T_WIDTH; + dest_y /= T_HEIGHT; + int32_t spos = y*w+x; - uint32_t w = full_map->w; - uint32_t h = full_map->h; - uint32_t x = floor(npc->curx); - uint32_t y = floor(npc->cury); - uint32_t spos = y*w+x; uint8_t *map = full_map->walkable; + + if(dest_x < 0 || dest_x > w || dest_y < 0 || dest_x > h) return 2; if(map[spos]) return 2; if(map[dest_y*w+dest_x]) return 2; @@ -134,26 +143,26 @@ int npc_pathfind(uint32_t dest_x, uint32_t dest_y, Map *full_map, NPC *npc) int16_t *came_from = malloc(w*h*2); for(i=0; i bscore) continue; bx = i%w; by = i/w; @@ -162,7 +171,8 @@ int npc_pathfind(uint32_t dest_x, uint32_t dest_y, Map *full_map, NPC *npc) if(bx == dest_x && by == dest_y) { as_clean(visited, gscore, fscore); - return as_reconstruct_path(came_from, w, h, spos, dest_y*w+dest_x, npc); + return as_reconstruct_path(came_from, w, h, spos, + dest_y*w+dest_x, npc); } visited[by*w+bx] = 1; @@ -174,14 +184,16 @@ int npc_pathfind(uint32_t dest_x, uint32_t dest_y, Map *full_map, NPC *npc) if(i > w) break; for(j = by-1; j < by+2; j++) { - if(j > h ) break; - if(map[j*w+i]) continue; - att_score = gscore[by*w+bx] + ceil(length(bx-i,by-j)); + if(j > h) break; + if(map[j*w+i] == 1) continue; + if(i == bx && j == by) continue; + att_score = gscore[by*w+bx] + round(length(bx-i,by-j)); if(att_score < gscore[j*w+i]) { came_from[j*w+i] = by*w+bx; gscore[j*w+i] = att_score; - fscore[j*w+i] = att_score + ceil(length(dest_x-i, dest_y-j)); + fscore[j*w+i] = att_score + round( + length(dest_x-i, dest_y-j)); if(visited[j*w+i]) visited[j*w+i] = 0; } } @@ -194,11 +206,34 @@ int npc_pathfind(uint32_t dest_x, uint32_t dest_y, Map *full_map, NPC *npc) return 3; } +NPC *npc_create() +{ + //Use temp pointer to avoid breaking the whole npcRPG on failure + void *temp = realloc(npcRPG, (nbNPC+1)*sizeof(NPC)); + if(temp == NULL) return NULL; + npcRPG = temp; + nbNPC++; + NPC *npc = &npcRPG[nbNPC-1]; + npc->xpath = malloc(2); + npc->ypath = malloc(2); + return npc; +} + +void npc_remove(NPC *npc) +{ + uint32_t pos = ((uint32_t)npc - (uint32_t)npcRPG)/sizeof(NPC); + if(pos > nbNPC-1) return; + if(pos == nbNPC-1) + { + nbNPC--; + return; + } + memmove(npc, &npc[1], (nbNPC-pos-1)*sizeof(NPC)); +} + //Refactoring to make adding complexity cleaner void update_npcs([[maybe_unused]] Game *game) { - //npc_pathfind(game->player.x, game->player.y, game->map_level, &npcRPG[0]); - for( uint32_t u=0; uhasPath && npc->paused==true) return; + /* if the NPC has no path or is paused, skip it */ + if (!npc->hasPath || npc->paused==true) return; float vecX = (float) (npc->xpath[ npc->currentPoint ] + npc->x) - npc->curx; @@ -232,7 +267,6 @@ void update_npc(NPC *npc) } -//TODO ; Add malloc() failure case void reload_npc(Game *game) { if (npcRPG!=NULL) @@ -256,6 +290,7 @@ void reload_npc(Game *game) } npcRPG = (NPC*) malloc( nbNPC * sizeof(NPC) ); + if(npcRPG == NULL) return; int currentNPC=0; for (uint32_t u=0; umap_level->nbextradata; u++) diff --git a/src/npc.h b/src/npc.h index d284ae8..f47369d 100644 --- a/src/npc.h +++ b/src/npc.h @@ -38,12 +38,14 @@ typedef struct int16_t *xpath; int16_t *ypath; + int type; + + int8_t current_group; int8_t hostile_to_group; - - /* is the current NPC in pause (during dialog) */ bool paused; + } NPC; //Frees then malloc()s a new path to npc @@ -57,7 +59,14 @@ int npc_append_path(uint16_t x, uint16_t y, NPC *npc); //Clears the NPCs path and creates a new one going to dest, //avoiding non-walkable tiles //Returns non-zero on failure -int npc_pathfind(uint32_t dest_x, uint32_t dest_y, Map *full_map, NPC *npc); +int npc_pathfind(int32_t dest_x, int32_t dest_y, Map *full_map, NPC *npc); + +//realloc()s npcRPG to adequate size and returns a pointer to the new element +//Returns NULL on failure +NPC *npc_create(); + +//Pops the NPC from npcRPG +void npc_remove(NPC *npc); /* Draws the player player. This function should be called after drawing the * map! */