Collab_RPG/src/npc.c

311 lines
8.3 KiB
C

#include "npc.h"
#include "config.h"
#include "dialogs.h"
#include "game.h"
#include "map.h"
#include <gint/display.h>
#include <gint/keyboard.h> /*debug*/
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
extern bopti_image_t tiny_npc_male;
extern bopti_image_t tiny_npc_female;
extern bopti_image_t tiny_npc_milkman;
extern bopti_image_t tiny_npc_police;
NPC npc_stack[NPC_STACK_SIZE];
uint32_t npc_count;
NPC *npc_create() {
if(npc_count == NPC_STACK_SIZE)
return NULL;
return &npc_stack[npc_count++];
}
void npc_remove(NPC *npc) {
uint32_t pos = (uint32_t)npc - (uint32_t)npc_stack;
if(pos == npc_count) {
npc_count--;
return;
}
memmove(npc, npc + sizeof(NPC), sizeof(NPC) * (npc_count - pos));
return;
}
float length(float x, float y) { return sqrtf(x * x + y * y); }
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->xpath[npc->path_length] = x - npc->x;
npc->ypath[npc->path_length] = y - npc->y;
npc->path_length++;
return 0;
}
void as_clean(uint8_t *visited, uint8_t *gscore, uint8_t *fscore) {
free(visited);
free(gscore);
free(fscore);
}
// TODO : Fix
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;
}
/* 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 = (npc->curx >> PRECISION) / T_WIDTH;
int32_t y = (npc->cury >> PRECISION) / 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 */
/* Could be improved with a priority queue*/
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;
}
// Refactoring to make adding complexity cleaner
void update_npcs(Game *game) {
uint32_t i;
for(i = 0; i < game->map_level->nbNPC; i++) {
update_npc(&game->map_level->npcs[i]);
}
for(i = 0; i < npc_count; i++) {
update_npc(&npc_stack[i]);
}
}
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 >> PRECISION);
float vecY = (float)(npc->ypath[npc->currentPoint] + npc->y) -
(npc->cury >> PRECISION);
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 * (float)(1 << PRECISION);
npc->cury += vecY * (float)(1 << PRECISION);
}
bopti_image_t *npc_sprites[FACES] = {&tiny_npc_male, &tiny_npc_female,
&tiny_npc_milkman, &tiny_npc_police};
void npc_draw_single(NPC *npc, Game *game) {
Player *pl = &game->player;
/* Render the path if in debug and it has one*/
#if DEBUGMODE
if(npc->hasPath) {
int NbPoints = npc->path_length + 1;
for(int v = 0; v < NbPoints; v++) {
int16_t deltaX1 =
((int16_t)(npc->x + npc->xpath[v % NbPoints]) * PXSIZE) -
(int16_t)pl->x;
int16_t deltaY1 =
((int16_t)(npc->y + npc->ypath[v % NbPoints]) * PXSIZE) -
(int16_t)pl->y;
int16_t deltaX2 =
((int16_t)(npc->x + npc->xpath[(v + 1) % NbPoints]) * PXSIZE) -
(int16_t)pl->x;
int16_t deltaY2 =
((int16_t)(npc->y + npc->ypath[(v + 1) % NbPoints]) * PXSIZE) -
(int16_t)pl->y;
dline(pl->px + deltaX1, pl->py + deltaY1, pl->px + deltaX2,
pl->py + deltaY2, PATH_COLOR);
}
}
#endif /* DEBUGMODE */
int16_t delX = ((npc->curx * PXSIZE) >> PRECISION) - (int16_t)pl->x;
int16_t delY = ((npc->cury * PXSIZE) >> PRECISION) - (int16_t)pl->y;
game->npc_animation.image = npc_sprites[npc->face];
unsigned char frame = game->npc_animation.frame;
if(npc->paused || !npc->hasPath)
game->npc_animation.frame = 0;
animation_draw(&game->npc_animation, pl->px - P_WIDTH / 2 + delX,
pl->py - P_HEIGHT / 2 + delY);
game->npc_animation.frame = frame;
}
void npc_draw(Game *game) {
uint32_t u;
for(u = 0; u < game->map_level->nbNPC; u++) {
npc_draw_single(&game->map_level->npcs[u], game);
}
for(u = 0; u < npc_count; u++) {
npc_draw_single(&npc_stack[u], game);
}
}
void npc_reload(GUNUSED Game *game) { npc_count = 0; }