|
|
|
@ -1,5 +1,5 @@
|
|
|
|
|
// you will die someday
|
|
|
|
|
#define CURRENT_VERSION 8 // wehenver you change Entity increment this boz
|
|
|
|
|
#define CURRENT_VERSION 9 // wehenver you change Entity increment this boz
|
|
|
|
|
|
|
|
|
|
#define SOKOL_IMPL
|
|
|
|
|
#if defined(WIN32) || defined(_WIN32)
|
|
|
|
@ -580,6 +580,75 @@ typedef struct GameState {
|
|
|
|
|
Entity entities[MAX_ENTITIES];
|
|
|
|
|
} GameState;
|
|
|
|
|
GameState gs = {0};
|
|
|
|
|
|
|
|
|
|
PathCache cached_paths[32] = {0};
|
|
|
|
|
|
|
|
|
|
bool is_path_cache_old(double elapsed_time, PathCache *cache)
|
|
|
|
|
{
|
|
|
|
|
double time_delta = elapsed_time - cache->elapsed_time;
|
|
|
|
|
if(time_delta < 0.0)
|
|
|
|
|
{
|
|
|
|
|
// path was cached in the future... likely from old save or something. Always invalidate
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return time_delta >= TIME_BETWEEN_PATH_GENS;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PathCacheHandle cache_path(double elapsed_time, AStarPath *path)
|
|
|
|
|
{
|
|
|
|
|
ARR_ITER_I(PathCache, cached_paths, i)
|
|
|
|
|
{
|
|
|
|
|
if(!it->exists || is_path_cache_old(elapsed_time, it))
|
|
|
|
|
{
|
|
|
|
|
int gen = it->generation;
|
|
|
|
|
*it = (PathCache){0};
|
|
|
|
|
it->generation = gen + 1;
|
|
|
|
|
|
|
|
|
|
it->path = *path;
|
|
|
|
|
it->elapsed_time = elapsed_time;
|
|
|
|
|
it->exists = true;
|
|
|
|
|
return (PathCacheHandle){.generation = it->generation, .index = i};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return (PathCacheHandle){0};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// passes in the time to return 0 and invalidate if too old
|
|
|
|
|
PathCache *get_path_cache(double elapsed_time, PathCacheHandle handle)
|
|
|
|
|
{
|
|
|
|
|
if(handle.generation == 0)
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
assert(handle.index >= 0);
|
|
|
|
|
assert(handle.index < ARRLEN(cached_paths));
|
|
|
|
|
PathCache *to_return = &cached_paths[handle.index];
|
|
|
|
|
if(to_return->exists && to_return->generation == handle.generation)
|
|
|
|
|
{
|
|
|
|
|
if(is_path_cache_old(elapsed_time, to_return))
|
|
|
|
|
{
|
|
|
|
|
to_return->exists = false;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return to_return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
double unprocessed_gameplay_time = 0.0;
|
|
|
|
|
#define MINIMUM_TIMESTEP (1.0/60.0)
|
|
|
|
|
|
|
|
|
@ -2528,8 +2597,19 @@ G H
|
|
|
|
|
SUM
|
|
|
|
|
F cost: G + H
|
|
|
|
|
*/
|
|
|
|
|
Vec2 from = it->pos;
|
|
|
|
|
Vec2 to = targeting->pos;
|
|
|
|
|
|
|
|
|
|
PathCache *cached = get_path_cache(elapsed_time, it->cached_path);
|
|
|
|
|
AStarPath path = {0};
|
|
|
|
|
bool succeeded = false;
|
|
|
|
|
if(cached)
|
|
|
|
|
{
|
|
|
|
|
path = cached->path;
|
|
|
|
|
succeeded = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
Vec2 from = it->pos;
|
|
|
|
|
typedef struct AStarNode {
|
|
|
|
|
bool exists;
|
|
|
|
|
struct AStarNode * parent;
|
|
|
|
@ -2540,7 +2620,7 @@ F cost: G + H
|
|
|
|
|
Vec2 pos;
|
|
|
|
|
} AStarNode;
|
|
|
|
|
|
|
|
|
|
BUFF(AStarNode, 1024) nodes = {0};
|
|
|
|
|
BUFF(AStarNode, MAX_ASTAR_NODES) nodes = {0};
|
|
|
|
|
struct { Vec2 key; AStarNode *value; } *node_cache = 0;
|
|
|
|
|
#define V2_HASH(v) (FloorV2(v))
|
|
|
|
|
const float jump_size = TILE_SIZE/2.0f;
|
|
|
|
@ -2550,7 +2630,6 @@ F cost: G + H
|
|
|
|
|
hmput(node_cache, from_hash, &nodes.data[0]);
|
|
|
|
|
|
|
|
|
|
bool should_quit = false;
|
|
|
|
|
bool succeeded = false;
|
|
|
|
|
AStarNode *last_node = 0;
|
|
|
|
|
PROFILE_SCOPE("A* Pathfinding") // astar pathfinding a star
|
|
|
|
|
while(!should_quit)
|
|
|
|
@ -2698,7 +2777,6 @@ F cost: G + H
|
|
|
|
|
node_cache = 0;
|
|
|
|
|
|
|
|
|
|
// reconstruct path
|
|
|
|
|
BUFF(Vec2, ARRLEN(nodes.data)) path = {0};
|
|
|
|
|
if(succeeded)
|
|
|
|
|
{
|
|
|
|
|
assert(last_node);
|
|
|
|
@ -2710,17 +2788,35 @@ F cost: G + H
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(succeeded)
|
|
|
|
|
it->cached_path = cache_path(elapsed_time, &path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vec2 next_point_on_path = {0};
|
|
|
|
|
if(succeeded)
|
|
|
|
|
{
|
|
|
|
|
assert(path.cur_index > 0);
|
|
|
|
|
if(path.cur_index == 1)
|
|
|
|
|
float nearest_dist = INFINITY;
|
|
|
|
|
int nearest_index = -1;
|
|
|
|
|
Entity *from = it;
|
|
|
|
|
BUFF_ITER_I(Vec2, &path, i)
|
|
|
|
|
{
|
|
|
|
|
float dist = LenV2(SubV2(*it, from->pos));
|
|
|
|
|
if(dist < nearest_dist)
|
|
|
|
|
{
|
|
|
|
|
nearest_dist = dist;
|
|
|
|
|
nearest_index = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
assert(nearest_index >= 0);
|
|
|
|
|
int target_index = (nearest_index + 1);
|
|
|
|
|
|
|
|
|
|
if(target_index >= path.cur_index)
|
|
|
|
|
{
|
|
|
|
|
next_point_on_path = to;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
next_point_on_path = path.data[1];
|
|
|
|
|
next_point_on_path = path.data[target_index];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|