Début de pathfinding pour les NPCs

This commit is contained in:
attilavs2 2024-07-21 02:42:50 +02:00
parent 15570a8d91
commit 96f413eb14
4 changed files with 147 additions and 39 deletions

View file

@ -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 );
}
}
}

View file

@ -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; i<game->map_level->nbextradata; i++ )

159
src/npc.c
View file

@ -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; u<nbNPC; u++ )
{
/* if the NPC has a path to follow AND is not currently in pause */
/* (talking with the player) */
if (npcRPG[u].hasPath==1 && npcRPG[u].paused==false)
{
float vecX = (float) (npcRPG[u].xpath[ npcRPG[u].currentPoint ] +
npcRPG[u].x) - npcRPG[u].curx;
float vecY = (float) (npcRPG[u].ypath[ npcRPG[u].currentPoint ] +
npcRPG[u].y) - npcRPG[u].cury;
float vecN = length(vecX, vecY);
npc->path_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; u<nbNPC; u++ )
{
update_npc(&npcRPG[u]);
}
}
void update_npc(NPC *npc)
{
/* if the NPC has no path and is paused, skip it */
if (!npc->hasPath && 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; u<game->map_level->nbextradata; u++) //uint pour enlever un warning
for (uint32_t u=0; u<game->map_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; u<game->map_level->nbextradata; u++) //uint pour enlever un warning
for (uint32_t u=0; u<game->map_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; u<nbNPC; u++) //uint pour enlever un warning
for (uint32_t u=0; u<nbNPC; u++)
{
NPC *Data = &npcRPG[u];
/* TODO : This is for debugging purpose, JUste to render the path */
/* to be followed by the NPC when this will be implemented */
/* Render the path if in debug*/
#if DEBUGMODE
if (Data->hasPath==1) /* this NPC has a trajectory */
{

View file

@ -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);