mirror of
https://git.planet-casio.com/Slyvtt/Collab_RPG.git
synced 2024-12-29 13:03:43 +01:00
Merge pull request 'Merge npc' (#54) from npc into dev
Reviewed-on: https://git.planet-casio.com/Slyvtt/Collab_RPG/pulls/54
This commit is contained in:
commit
903755f010
4 changed files with 274 additions and 37 deletions
|
@ -59,7 +59,7 @@ int dialogs_text_opt(Game *game, bopti_image_t *face, char *text,
|
||||||
for(i=0;i<=BOX_HEIGHT;i++){
|
for(i=0;i<=BOX_HEIGHT;i++){
|
||||||
/* Redrawing the entire screen, because maybe there was no dialog
|
/* Redrawing the entire screen, because maybe there was no dialog
|
||||||
displayed before. */
|
displayed before. */
|
||||||
update_npc(game);
|
update_npcs(game);
|
||||||
game_draw(game);
|
game_draw(game);
|
||||||
|
|
||||||
/* Fill the dialog box with white */
|
/* Fill the dialog box with white */
|
||||||
|
@ -182,7 +182,7 @@ int dialogs_text_opt(Game *game, bopti_image_t *face, char *text,
|
||||||
/* Run another little fancy animation if we should. */
|
/* Run another little fancy animation if we should. */
|
||||||
for(i=BOX_HEIGHT;i>0;i--){
|
for(i=BOX_HEIGHT;i>0;i--){
|
||||||
/* It is the same as the start animation. */
|
/* It is the same as the start animation. */
|
||||||
update_npc(game);
|
update_npcs(game);
|
||||||
game_draw(game);
|
game_draw(game);
|
||||||
drect(0, 0, DWIDTH, i*PXSIZE, C_WHITE);
|
drect(0, 0, DWIDTH, i*PXSIZE, C_WHITE);
|
||||||
drect(0, i*PXSIZE, DWIDTH, (i+1)*PXSIZE, C_BLACK);
|
drect(0, i*PXSIZE, DWIDTH, (i+1)*PXSIZE, C_BLACK);
|
||||||
|
@ -303,7 +303,7 @@ int _choice_call_before_end(Game *game, [[maybe_unused]] unsigned int org_i) {
|
||||||
/* Make a little animation because we looove little animations ;) */
|
/* Make a little animation because we looove little animations ;) */
|
||||||
for(i=DWIDTH/8+1;i>0;i--){
|
for(i=DWIDTH/8+1;i>0;i--){
|
||||||
/* I'm drawing the same box as on the start animation */
|
/* I'm drawing the same box as on the start animation */
|
||||||
update_npc(game);
|
update_npcs(game);
|
||||||
game_draw(game);
|
game_draw(game);
|
||||||
dialogs_text_opt(game, _face, _text, NULL, false, false, NULL, 0, false,
|
dialogs_text_opt(game, _face, _text, NULL, false, false, NULL, 0, false,
|
||||||
_i, false);
|
_i, false);
|
||||||
|
@ -376,4 +376,4 @@ void dialogs_initiate_sequence(Game *game, bopti_image_t *face,
|
||||||
dialogs_text(game, face, text, true, true);
|
dialogs_text(game, face, text, true, true);
|
||||||
if (nextOther!=-1) dialogs_initiate_sequence(game, face, nextOther);
|
if (nextOther!=-1) dialogs_initiate_sequence(game, face, nextOther);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ extern uint32_t nbNPC;
|
||||||
|
|
||||||
void game_logic(Game *game) {
|
void game_logic(Game *game) {
|
||||||
|
|
||||||
update_npc( game );
|
update_npcs( game );
|
||||||
|
|
||||||
/* we check if interactions are possible close to the player */
|
/* we check if interactions are possible close to the player */
|
||||||
for( uint32_t i=0; i<game->map_level->nbextradata; i++ ){
|
for( uint32_t i=0; i<game->map_level->nbextradata; i++ ){
|
||||||
|
|
265
src/npc.c
265
src/npc.c
|
@ -22,36 +22,239 @@ float length( float x, float y )
|
||||||
return sqrtf( x*x+y*y );
|
return sqrtf( x*x+y*y );
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_npc( [[maybe_unused]] Game *game)
|
int npc_clear_path(NPC *npc)
|
||||||
|
{
|
||||||
|
npc->currentPoint = 0;
|
||||||
|
npc->hasPath = 0;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
int npc_append_path(uint16_t x, uint16_t y, NPC *npc)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void as_clean(uint8_t *visited, uint8_t *gscore, uint8_t *fscore)
|
||||||
|
{
|
||||||
|
free(visited);
|
||||||
|
free(gscore);
|
||||||
|
free(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;
|
||||||
|
|
||||||
|
int16_t next = came_from[dest];
|
||||||
|
|
||||||
|
unsigned int i;
|
||||||
|
|
||||||
|
for(i = 0; i < 64; i++)
|
||||||
|
{
|
||||||
|
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)*T_WIDTH,(spos/h)*T_HEIGHT, npc))
|
||||||
|
goto as_recons_fail;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t tx, ty;
|
||||||
|
|
||||||
|
//Flip the path because it started from the end
|
||||||
|
|
||||||
|
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-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:
|
||||||
|
|
||||||
|
free(came_from);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Returns non zero error code on failure
|
||||||
|
//Custom a* implemetation
|
||||||
|
//Unoptimized, may become an issue
|
||||||
|
int npc_pathfind(int32_t dest_x, int32_t dest_y, Map *full_map, NPC *npc)
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
npc_clear_path(npc);
|
||||||
|
|
||||||
|
uint8_t *visited = malloc(w*h);
|
||||||
|
for(i=0; i<w*h; i++) visited[i] = 1;
|
||||||
|
visited[spos] = 0;
|
||||||
|
|
||||||
|
int16_t *came_from = malloc(w*h*2);
|
||||||
|
for(i=0; i<w*h; i++) came_from[i] = -1;
|
||||||
|
|
||||||
|
uint8_t *gscore = malloc(w*h*2);
|
||||||
|
for(i=0; i<w*h; i++) gscore[i] = 255;
|
||||||
|
gscore[spos] = 0;
|
||||||
|
|
||||||
|
uint8_t *fscore = malloc(w*h*2);
|
||||||
|
for(i=0; i<w*h; i++) fscore[i] = 255;
|
||||||
|
fscore[spos] = length(dest_x-x, dest_y-y);
|
||||||
|
|
||||||
|
uint8_t bscore;
|
||||||
|
int32_t bx = x;
|
||||||
|
int32_t by = y;
|
||||||
|
|
||||||
|
for(int iter=0; iter < 64; iter++)
|
||||||
|
{
|
||||||
|
bscore = 255;
|
||||||
|
//Cheapest known tile
|
||||||
|
for(i = 0; i <= w*h; i++)
|
||||||
|
{
|
||||||
|
if(visited[i]) continue;
|
||||||
|
if(map[i] == 1) 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+2; i++)
|
||||||
|
{
|
||||||
|
if(i > w) break;
|
||||||
|
for(j = by-1; j < by+2; 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 + round(
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
for( uint32_t u=0; u<nbNPC; u++ )
|
for( uint32_t u=0; u<nbNPC; u++ )
|
||||||
{
|
{
|
||||||
/* if the NPC has a path to follow AND is not currently in pause */
|
update_npc(&npcRPG[u]);
|
||||||
/* (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);
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
npcRPG[u].curx += vecX;
|
|
||||||
npcRPG[u].cury += vecY;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update_npc(NPC *npc)
|
||||||
|
{
|
||||||
|
/* 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;
|
||||||
|
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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +270,7 @@ void reload_npc(Game *game)
|
||||||
nbNPC = 0;
|
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];
|
ExtraData *Data = &game->map_level->extradata[u];
|
||||||
|
|
||||||
|
@ -78,9 +281,10 @@ void reload_npc(Game *game)
|
||||||
}
|
}
|
||||||
|
|
||||||
npcRPG = (NPC*) malloc( nbNPC * sizeof(NPC) );
|
npcRPG = (NPC*) malloc( nbNPC * sizeof(NPC) );
|
||||||
|
if(npcRPG == NULL) return;
|
||||||
int currentNPC=0;
|
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];
|
ExtraData *Data = &game->map_level->extradata[u];
|
||||||
|
|
||||||
|
@ -106,12 +310,11 @@ void reload_npc(Game *game)
|
||||||
void npc_draw(Game *game) {
|
void npc_draw(Game *game) {
|
||||||
Player *pl = &game->player;
|
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];
|
NPC *Data = &npcRPG[u];
|
||||||
|
|
||||||
/* TODO : This is for debugging purpose, JUste to render the path */
|
/* Render the path if in debug*/
|
||||||
/* to be followed by the NPC when this will be implemented */
|
|
||||||
#if DEBUGMODE
|
#if DEBUGMODE
|
||||||
if (Data->hasPath==1) /* this NPC has a trajectory */
|
if (Data->hasPath==1) /* this NPC has a trajectory */
|
||||||
{
|
{
|
||||||
|
|
36
src/npc.h
36
src/npc.h
|
@ -8,6 +8,15 @@
|
||||||
#include "game.h"
|
#include "game.h"
|
||||||
#include "memory.h"
|
#include "memory.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
|
||||||
|
NPC_NONE = 0,
|
||||||
|
NPC_FRIENDLY = 1, //The player's team
|
||||||
|
NPC_HOSTILE = 2, //to the player
|
||||||
|
NPC_ALL = 3
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
|
@ -29,19 +38,44 @@ typedef struct
|
||||||
int16_t *xpath;
|
int16_t *xpath;
|
||||||
int16_t *ypath;
|
int16_t *ypath;
|
||||||
|
|
||||||
|
int type;
|
||||||
|
|
||||||
|
int8_t current_group;
|
||||||
|
int8_t hostile_to_group;
|
||||||
|
|
||||||
/* is the current NPC in pause (during dialog) */
|
/* is the current NPC in pause (during dialog) */
|
||||||
bool paused;
|
bool paused;
|
||||||
|
|
||||||
char *face;
|
char *face;
|
||||||
} NPC;
|
} 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(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
|
/* Draws the player player. This function should be called after drawing the
|
||||||
* map! */
|
* map! */
|
||||||
void npc_draw(Game *game);
|
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);
|
void reload_npc(Game *game);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue