fp: Add stalking of the hero using A*
This commit is contained in:
parent
d2bf171d98
commit
640c4e7fbb
2
External/tely
vendored
2
External/tely
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 39af5c5627404982ec9200b2155f0b5c8874bf8f
|
Subproject commit a807996225655d43d8c7265f82913dcb4c0cf9cf
|
234
feely_pona.cpp
234
feely_pona.cpp
@ -259,7 +259,113 @@ void TELY_Game_EntityChangeState(TELY_GameEntity *entity, TELY_GameEntityState s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct AStarNode
|
||||||
|
{
|
||||||
|
Dqn_usize cost;
|
||||||
|
Dqn_usize heuristic;
|
||||||
|
Dqn_V2I came_from;
|
||||||
|
};
|
||||||
|
|
||||||
Dqn_f32 const FP_TILE_SIZE = 37.f;
|
Dqn_f32 const FP_TILE_SIZE = 37.f;
|
||||||
|
Dqn_Slice<Dqn_V2I> AStarPathFind(Dqn_Arena *arena, TELY_Platform *platform, Dqn_V2I src_tile, Dqn_V2I dest_tile)
|
||||||
|
{
|
||||||
|
Dqn_DSMap<AStarNode> astar_info = Dqn_DSMap_Init<AStarNode>(128);
|
||||||
|
DQN_DEFER { Dqn_DSMap_Deinit(&astar_info); };
|
||||||
|
|
||||||
|
Dqn_FArray<Dqn_V2I, 128> frontier = {};
|
||||||
|
Dqn_FArray_Add(&frontier, src_tile);
|
||||||
|
|
||||||
|
Dqn_usize tile_count_x = DQN_CAST(Dqn_usize)(platform->core.window_size.w / FP_TILE_SIZE);
|
||||||
|
Dqn_usize tile_count_y = DQN_CAST(Dqn_usize)(platform->core.window_size.h / FP_TILE_SIZE);
|
||||||
|
|
||||||
|
// NOTE: Initialise the starting cost
|
||||||
|
uint64_t src_tile_u64 = (DQN_CAST(uint64_t)src_tile.y << 32) | (DQN_CAST(uint64_t)src_tile.x << 0);
|
||||||
|
Dqn_DSMap_MakeKeyU64(&astar_info, src_tile_u64);
|
||||||
|
|
||||||
|
while (frontier.size) {
|
||||||
|
Dqn_V2I curr_tile = Dqn_FArray_PopFront(&frontier, 1);
|
||||||
|
if (curr_tile == dest_tile)
|
||||||
|
break;
|
||||||
|
|
||||||
|
Dqn_FArray<Dqn_V2I, 4> neighbours = {};
|
||||||
|
{
|
||||||
|
Dqn_V2I left = Dqn_V2I_InitNx2(curr_tile.x - 1, curr_tile.y);
|
||||||
|
Dqn_V2I right = Dqn_V2I_InitNx2(curr_tile.x + 1, curr_tile.y);
|
||||||
|
Dqn_V2I top = Dqn_V2I_InitNx2(curr_tile.x, curr_tile.y - 1);
|
||||||
|
Dqn_V2I bottom = Dqn_V2I_InitNx2(curr_tile.x, curr_tile.y + 1);
|
||||||
|
|
||||||
|
if (left.x >= 0)
|
||||||
|
Dqn_FArray_Add(&neighbours, left);
|
||||||
|
if (right.x <= tile_count_x)
|
||||||
|
Dqn_FArray_Add(&neighbours, right);
|
||||||
|
if (top.y >= 0)
|
||||||
|
Dqn_FArray_Add(&neighbours, top);
|
||||||
|
if (bottom.y <= tile_count_y)
|
||||||
|
Dqn_FArray_Add(&neighbours, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t const curr_tile_u64 = (DQN_CAST(uint64_t)curr_tile.y << 32) | (DQN_CAST(uint64_t)curr_tile.x << 0);
|
||||||
|
Dqn_usize const curr_cost = Dqn_DSMap_FindKeyU64(&astar_info, curr_tile_u64).value->cost;
|
||||||
|
for (Dqn_V2I next_tile : neighbours) {
|
||||||
|
|
||||||
|
Dqn_usize new_cost = curr_cost + 1;
|
||||||
|
uint64_t next_tile_u64 = (DQN_CAST(uint64_t)next_tile.y << 32) | (DQN_CAST(uint64_t)next_tile.x << 0);
|
||||||
|
Dqn_DSMapResult<AStarNode> next_cost_result = Dqn_DSMap_MakeKeyU64(&astar_info, next_tile_u64);
|
||||||
|
|
||||||
|
if (next_cost_result.found && new_cost >= next_cost_result.value->cost)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Dqn_usize manhattan_dist = DQN_ABS(dest_tile.x - next_tile.x) + DQN_ABS(dest_tile.y - next_tile.y);
|
||||||
|
next_cost_result.value->cost = new_cost;
|
||||||
|
next_cost_result.value->came_from = curr_tile;
|
||||||
|
next_cost_result.value->heuristic = new_cost + manhattan_dist;
|
||||||
|
|
||||||
|
// TODO(doyle): Find the insert location into the frontier
|
||||||
|
bool inserted = false;
|
||||||
|
DQN_FOR_UINDEX(index, frontier.size) {
|
||||||
|
Dqn_V2I frontier_tile = frontier.data[index];
|
||||||
|
uint64_t frontier_tile_u64 = DQN_CAST(uint64_t)frontier_tile.y << 32 | DQN_CAST(uint64_t)frontier_tile.x << 0;
|
||||||
|
Dqn_usize frontier_heuristic = Dqn_DSMap_FindKeyU64(&astar_info, frontier_tile_u64).value->heuristic;
|
||||||
|
if (next_cost_result.value->heuristic >= frontier_heuristic)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
char const *src = DQN_CAST(char *)(frontier.data + index);
|
||||||
|
char const *dest = DQN_CAST(char *)(frontier.data + (index + 1));
|
||||||
|
char const *end = DQN_CAST(char *)(frontier.data + frontier.size);
|
||||||
|
Dqn_usize bytes_to_move = end - src;
|
||||||
|
DQN_MEMMOVE(DQN_CAST(void *)dest, src, bytes_to_move);
|
||||||
|
|
||||||
|
frontier.data[index] = next_tile;
|
||||||
|
frontier.size++;
|
||||||
|
inserted = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inserted)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Dqn_FArray_Add(&frontier, next_tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dqn_usize slice_size = 0;
|
||||||
|
for (Dqn_V2I it = dest_tile; it != src_tile; slice_size++) {
|
||||||
|
uint64_t key_u64 = (DQN_CAST(uint64_t)it.y << 32) | (DQN_CAST(uint64_t)it.x << 0);
|
||||||
|
it = Dqn_DSMap_FindKeyU64(&astar_info, key_u64).value->came_from;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dqn_Slice<Dqn_V2I> result = Dqn_Slice_Alloc<Dqn_V2I>(arena, slice_size, Dqn_ZeroMem_No);
|
||||||
|
slice_size = 0;
|
||||||
|
for (Dqn_V2I it = dest_tile; it != src_tile; ) {
|
||||||
|
result.data[slice_size++] = it;
|
||||||
|
uint64_t key_u64 = (DQN_CAST(uint64_t)it.y << 32) | (DQN_CAST(uint64_t)it.x << 0);
|
||||||
|
it = Dqn_DSMap_FindKeyU64(&astar_info, key_u64).value->came_from;
|
||||||
|
}
|
||||||
|
|
||||||
|
DQN_ASSERT(result.size == slice_size);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *renderer, TELY_PlatformInput *input)
|
void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *renderer, TELY_PlatformInput *input)
|
||||||
{
|
{
|
||||||
if (TELY_Platform_InputKeyIsReleased(input->mouse_left))
|
if (TELY_Platform_InputKeyIsReleased(input->mouse_left))
|
||||||
@ -293,76 +399,6 @@ void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *rend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TELY_Platform_InputScanCodeIsPressed(input, TELY_PlatformInputScanCode_F1)) {
|
|
||||||
// NOTE: Do A* algorithm
|
|
||||||
Dqn_V2 world_pos = TELY_Game_CalcEntityWorldPos(game, player->handle);
|
|
||||||
Dqn_V2I player_tile = Dqn_V2I_InitNx2(world_pos.x / FP_TILE_SIZE, world_pos.y / FP_TILE_SIZE);
|
|
||||||
Dqn_V2I target_tile = Dqn_V2I_InitNx2(30, 10);
|
|
||||||
|
|
||||||
Dqn_FArray<Dqn_V2I, 128> frontier = {};
|
|
||||||
Dqn_FArray_Add(&frontier, player_tile);
|
|
||||||
|
|
||||||
Dqn_usize tile_count_x = DQN_CAST(Dqn_usize)(platform->core.window_size.w / FP_TILE_SIZE);
|
|
||||||
Dqn_usize tile_count_y = DQN_CAST(Dqn_usize)(platform->core.window_size.h / FP_TILE_SIZE);
|
|
||||||
|
|
||||||
Dqn_DSMap<Dqn_usize> cost_so_far = Dqn_DSMap_Init<Dqn_usize>(128);
|
|
||||||
Dqn_DSMap<Dqn_usize> came_from = Dqn_DSMap_Init<Dqn_usize>(128);
|
|
||||||
DQN_DEFER {
|
|
||||||
Dqn_DSMap_Deinit(&came_from);
|
|
||||||
Dqn_DSMap_Deinit(&cost_so_far);
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE: Initialise the starting cost
|
|
||||||
{
|
|
||||||
uint64_t tile_as_u64 = (DQN_CAST(uint64_t)player_tile.y << 32) | (DQN_CAST(uint64_t)player_tile.x << 0);
|
|
||||||
Dqn_DSMapKey key = Dqn_DSMap_KeyU64(&cost_so_far, tile_as_u64);
|
|
||||||
Dqn_DSMap_Set<Dqn_usize>(&cost_so_far, key, 0 /*value*/, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (frontier.size) {
|
|
||||||
Dqn_V2I current = frontier.data[0];
|
|
||||||
if (current == target_tile)
|
|
||||||
break;
|
|
||||||
|
|
||||||
Dqn_FArray<Dqn_V2I, 4> neighbours = {};
|
|
||||||
Dqn_V2I left = Dqn_V2I_InitNx2(current.x - 1, current.y);
|
|
||||||
Dqn_V2I right = Dqn_V2I_InitNx2(current.x + 1, current.y);
|
|
||||||
Dqn_V2I top = Dqn_V2I_InitNx2(current.x, current.y - 1);
|
|
||||||
Dqn_V2I bottom = Dqn_V2I_InitNx2(current.x, current.y + 1);
|
|
||||||
|
|
||||||
if (left.x >= 0)
|
|
||||||
Dqn_FArray_Add(&neighbours, left);
|
|
||||||
if (right.x <= tile_count_x)
|
|
||||||
Dqn_FArray_Add(&neighbours, right);
|
|
||||||
if (top.y >= 0)
|
|
||||||
Dqn_FArray_Add(&neighbours, top);
|
|
||||||
if (bottom.y >= 0)
|
|
||||||
Dqn_FArray_Add(&neighbours, bottom);
|
|
||||||
|
|
||||||
uint64_t curr_tile_as_u64 = (DQN_CAST(uint64_t)current.y << 32) | (DQN_CAST(uint64_t)current.x << 0);
|
|
||||||
Dqn_DSMapKey curr_tile_key = Dqn_DSMap_KeyU64(&cost_so_far, curr_tile_as_u64);
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
for (Dqn_V2I next : neighbours) {
|
|
||||||
Dqn_usize new_cost = curr_cost + 1;
|
|
||||||
Dqn_usize *prev_cost = Dqn_DSMap_Find(&cost_so_far, curr_tile_key);
|
|
||||||
if (!prev_curr_cost || new_cost < *prev_curr_cost) {
|
|
||||||
*prev_cost = new_cost;
|
|
||||||
Dqn_usize manhattan_dist = DQN_ABS(target_tile.x - current.x) + DQN_ABS(target_tile.y - current.y);
|
|
||||||
Dqn_usize estimated_finish_cost = new_cost + manhattan_dist;
|
|
||||||
|
|
||||||
// TODO(doyle): Find the insert location
|
|
||||||
|
|
||||||
uint64_t next_tile_as_u64 = (DQN_CAST(uint64_t)next.y << 32) | (DQN_CAST(uint64_t)next.x << 0);
|
|
||||||
Dqn_DSMapKey next_tile_key = Dqn_DSMap_KeyU64(&came_from, next_tile_as_u64);
|
|
||||||
Dqn_DSMap_Set(&came_from, next_tile_key, curr_tile_as_u64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
game->camera.world_pos += dir_vector * 5.f;
|
game->camera.world_pos += dir_vector * 5.f;
|
||||||
}
|
}
|
||||||
@ -381,6 +417,64 @@ void FP_GameUpdate(TELY_Platform *platform, TELY_Game *game, TELY_Renderer *rend
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dqn_V2 entity_world_pos = TELY_Game_CalcEntityWorldPos(game, entity->handle);
|
||||||
|
if (entity->name == DQN_STRING8("Enemy")) {
|
||||||
|
if (entity->handle != game->clicked_entity && entity->stalk_entity != game->clicked_entity) {
|
||||||
|
entity->stalk_entity = game->clicked_entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
TELY_GameEntity *stalk_entity = TELY_Game_GetEntity(game, entity->stalk_entity);
|
||||||
|
if (stalk_entity) {
|
||||||
|
Dqn_V2 stalk_world_pos = TELY_Game_CalcEntityWorldPos(game, stalk_entity->handle);
|
||||||
|
Dqn_V2I stalk_tile = Dqn_V2I_InitNx2(stalk_world_pos.x / FP_TILE_SIZE, stalk_world_pos.y / FP_TILE_SIZE);
|
||||||
|
if (entity->stalk_entity_last_known_tile != stalk_tile) {
|
||||||
|
entity->stalk_entity_last_known_tile = stalk_tile;
|
||||||
|
|
||||||
|
// NOTE: Dealloc all waypoints
|
||||||
|
for (TELY_GameWaypoint *waypoint = entity->waypoints->next; waypoint != entity->waypoints; ) {
|
||||||
|
TELY_GameWaypoint *next = waypoint->next;
|
||||||
|
TELY_ChunkPool_Dealloc(&game->chunk_pool, waypoint);
|
||||||
|
waypoint = next;
|
||||||
|
}
|
||||||
|
entity->waypoints->next = entity->waypoints;
|
||||||
|
entity->waypoints->prev = entity->waypoints;
|
||||||
|
|
||||||
|
Dqn_V2I entity_tile = Dqn_V2I_InitNx2(entity_world_pos.x / FP_TILE_SIZE, entity_world_pos.y / FP_TILE_SIZE);
|
||||||
|
Dqn_Slice<Dqn_V2I> path_find = AStarPathFind(&platform->arena, platform, entity_tile, stalk_tile);
|
||||||
|
|
||||||
|
for (Dqn_usize index = path_find.size - 1; index < path_find.size; index--) {
|
||||||
|
TELY_GameWaypoint *waypoint = TELY_ChunkPool_New(&game->chunk_pool, TELY_GameWaypoint);
|
||||||
|
waypoint->pos = path_find.data[index];
|
||||||
|
waypoint->next = entity->waypoints;
|
||||||
|
waypoint->prev = entity->waypoints->prev;
|
||||||
|
waypoint->next->prev = waypoint;
|
||||||
|
waypoint->prev->next = waypoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TELY_GameWaypoint *waypoint = entity->waypoints->next; waypoint != entity->waypoints; waypoint = waypoint->next) {
|
||||||
|
Dqn_V2 circle_pos = Dqn_V2_InitNx2(waypoint->pos.x * FP_TILE_SIZE + FP_TILE_SIZE * .5f, waypoint->pos.y * FP_TILE_SIZE + FP_TILE_SIZE * .5f);
|
||||||
|
TELY_Render_CircleColourV4(renderer, circle_pos, 4.f, TELY_RenderShapeMode_Fill, TELY_COLOUR_MAGENTA_V4);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entity->waypoints->next != entity->waypoints) {
|
||||||
|
TELY_GameWaypoint *waypoint = entity->waypoints->next;
|
||||||
|
Dqn_V2I target_tile = entity->waypoints->next->pos;
|
||||||
|
Dqn_V2 target_pos = Dqn_V2_InitNx2(target_tile.x * FP_TILE_SIZE + FP_TILE_SIZE *.5f, target_tile.y * FP_TILE_SIZE + FP_TILE_SIZE * .5f);
|
||||||
|
Dqn_V2 entity_to_target_pos = target_pos - entity_world_pos;
|
||||||
|
|
||||||
|
if (Dqn_V2_LengthSq(entity_to_target_pos) < DQN_SQUARED(entity->local_hit_box_size.x * .5f)) {
|
||||||
|
waypoint->next->prev = waypoint->prev;
|
||||||
|
waypoint->prev->next = waypoint->next;
|
||||||
|
TELY_ChunkPool_Dealloc(&game->chunk_pool, waypoint);
|
||||||
|
} else {
|
||||||
|
Dqn_V2 entity_to_target_pos_norm = Dqn_V2_Normalise(entity_to_target_pos);
|
||||||
|
entity->local_pos += entity_to_target_pos_norm * (entity->local_hit_box_size.x * .05f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// NOTE: Core equations of motion ==========================================================
|
// NOTE: Core equations of motion ==========================================================
|
||||||
{
|
{
|
||||||
// f"(t) = a
|
// f"(t) = a
|
||||||
|
Loading…
Reference in New Issue
Block a user