From 7306c8fba4678391828613f0ce21d9e734f242d5 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 5 Oct 2025 15:21:23 +0100 Subject: [PATCH 01/10] chore: struct reorg --- code/first.c | 60 ++++++++++++++++++++++++++++-------------------- code/game/core.h | 4 +++- 2 files changed, 38 insertions(+), 26 deletions(-) diff --git a/code/first.c b/code/first.c index b6fd869..6741e03 100644 --- a/code/first.c +++ b/code/first.c @@ -61,6 +61,39 @@ int main(int argc, char **argv) { camera->farp = 1000.0f; game->draw.camera = camera; + World *world = M_ArenaPush(arena, World); + game->world = world; + world->npcCount = 2; + NPC *npc1 = &world->npcs[0]; + npc1->collision.pos.x = 15; + npc1->collision.pos.y = 15; + npc1->collision.size.x = 10; + npc1->collision.size.y = 10; + npc1->name = S("Matt"); + npc1->mode = NPC_ACTION_WAITING; + npc1->waitTime = 0; + npc1->maxWaitTime = 5; + npc1->currentNavNode = 87; + npc1->collision.pos.x = 15; + npc1->collision.pos.y = 15; + npc1->collision.size.x = 10; + npc1->collision.size.y = 10; + + NPC *npc2 = &world->npcs[0]; + npc2->collision.pos.x = 15; + npc2->collision.pos.y = 15; + npc2->collision.size.x = 10; + npc2->collision.size.y = 10; + npc2->name = S("James"); + npc2->mode = NPC_ACTION_WAITING; + npc2->waitTime = 0; + npc2->maxWaitTime = 10; + npc2->currentNavNode = 0; + + world->navMesh = &TestNavMesh; + world->npcPOI[0] = 100; + world->player.pos.x = 0; + world->player.pos.y = 0; } Vk_Buffer rbo = { 0 }; @@ -71,29 +104,6 @@ int main(int argc, char **argv) { Vk_BufferCreate(&rbo); bool running = true; - World world = { - .npcCount = 2, - .npcs = { - { - .collision = {{10, 10}, {10, 10}}, - .name = S("Matt"), - .mode = NPC_ACTION_WAITING, - .waitTime = 0, - .maxWaitTime = 5, - .currentNavNode = 87 - },{ - .collision = {{15, 15}, {10, 10}}, - .name = S("James"), - .mode = NPC_ACTION_WAITING, - .waitTime = 0, - .maxWaitTime = 10, - .currentNavNode = 0 - } - }, - .navMesh = &TestNavMesh, - .npcPOI = {100}, - .player = {.pos = {0,0}} - }; printf("%zu size in bytes\n", sizeof(TestNavMesh)); @@ -106,9 +116,9 @@ int main(int argc, char **argv) { { running = false; } - ProcessEvents(&e, &world); + ProcessEvents(&e, game->world); } - UpdateWorld(1.0/60.0, &world); + UpdateWorld(1.0/60.0, game->world); int w, h; SDL_GetWindowSizeInPixels(window, &w, &h); diff --git a/code/game/core.h b/code/game/core.h index 03df95f..7a71f7e 100644 --- a/code/game/core.h +++ b/code/game/core.h @@ -1,5 +1,6 @@ #if !defined(LD_GAME_CORE_H_) #define LD_GAME_CORE_H_ +#include "world.h" typedef struct G_Camera G_Camera; struct G_Camera { @@ -18,6 +19,8 @@ struct G_State { D_Context draw; G_Camera camera; + + World *world; }; function void G_ImagesLoad(G_State *game); @@ -29,6 +32,5 @@ function void G_CalulateCamera(G_Camera *camera, F32 aspect); #include "player.h" #include "nav.h" #include "npc.h" -#include "world.h" #endif // LD_GAME_CORE_H_ From 394366480b8b37aa9da9bb09663c7bed0fcf6bee Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 5 Oct 2025 15:32:57 +0100 Subject: [PATCH 02/10] feat: Random npc walkabouts --- code/first.c | 3 ++- code/game/impl/npc.c | 7 ++++--- code/game/world.h | 2 ++ 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/code/first.c b/code/first.c index 59fd591..4a2bd06 100644 --- a/code/first.c +++ b/code/first.c @@ -63,6 +63,7 @@ int main(int argc, char **argv) { game->draw.camera = camera; World *world = M_ArenaPush(arena, World); game->world = world; + world->random = Random_Seed(29237489723847); world->npcCount = 2; NPC *npc1 = &world->npcs[0]; npc1->collision.pos.x = 15; @@ -79,7 +80,7 @@ int main(int argc, char **argv) { npc1->collision.size.x = 10; npc1->collision.size.y = 10; - NPC *npc2 = &world->npcs[0]; + NPC *npc2 = &world->npcs[1]; npc2->collision.pos.x = 15; npc2->collision.pos.y = 15; npc2->collision.size.x = 10; diff --git a/code/game/impl/npc.c b/code/game/impl/npc.c index 4c9d1a2..bc7358e 100644 --- a/code/game/impl/npc.c +++ b/code/game/impl/npc.c @@ -1,6 +1,7 @@ #include "game/npc.h" #include "game/world.h" #include "core/types.h" +#include "core/math.h" #include @@ -10,8 +11,8 @@ void updateNPC(F32 delta, NPC *npc, World *world) { npc->waitTime+=delta; if(npc->waitTime > npc->maxWaitTime) { npc->mode = NPC_ACTION_WALKING; - U32 next = npc->targetNavNode == 100 ? 20 : 100; - npc->targetNavNode = next; // TODO RANDOM + // TODO change targets to poi's rather than just random nodes + npc->targetNavNode = Random_U32(&world->random, 0, world->navMesh->nodeCount); printf("Starting to nav path\n"); npc->path = Nav_Path(world->navMesh, npc->currentNavNode, npc->targetNavNode); printf("done\n"); @@ -26,7 +27,7 @@ void updateNPC(F32 delta, NPC *npc, World *world) { if(npc->path.nodeCount == npc->pathIndex+1){ printf("Finished! so I'm waiting\n"); npc->mode = NPC_ACTION_WAITING; - npc->maxWaitTime = 20; // TODO RANDOM + npc->maxWaitTime = Random_F32(&world->random, 10, 40); npc->waitTime = 0; npc->pathIndex = 0; return; diff --git a/code/game/world.h b/code/game/world.h index 433070b..d237538 100644 --- a/code/game/world.h +++ b/code/game/world.h @@ -4,6 +4,7 @@ #include "player.h" #include "npc.h" #include "bandit.h" +#include "../core/math.h" // Areas are which typedef U32 World_Area; @@ -16,6 +17,7 @@ typedef struct World World; struct World { //// Static stuff NavMesh *navMesh; + Random random; //// Player Player player; From c8dfcd857e9bacf474879c1e0d1a49a6f4753c5d Mon Sep 17 00:00:00 2001 From: declan Date: Sat, 4 Oct 2025 17:36:47 +0100 Subject: [PATCH 03/10] raycast start --- code/first.c | 101 ++++++++++++++++++++-------------------- code/game/aabb.h | 7 ++- code/game/impl/aabb.c | 24 +++++++++- code/game/impl/player.c | 49 +++++++++++-------- code/game/player.h | 1 + 5 files changed, 107 insertions(+), 75 deletions(-) diff --git a/code/first.c b/code/first.c index 4a2bd06..4f186d8 100644 --- a/code/first.c +++ b/code/first.c @@ -19,9 +19,10 @@ #include "game/impl/npc.c" #include "game/testnavmesh.h" -int main(int argc, char **argv) { - (void) argc; - (void) argv; +int main(int argc, char **argv) +{ + (void)argc; + (void)argv; if (!SDL_Init(SDL_INIT_VIDEO)) { @@ -58,43 +59,43 @@ int main(int argc, char **argv) { camera->fov = 60.0f; camera->nearp = 0.01f; - camera->farp = 1000.0f; + camera->farp = 1000.0f; game->draw.camera = camera; World *world = M_ArenaPush(arena, World); - game->world = world; - world->random = Random_Seed(29237489723847); - world->npcCount = 2; - NPC *npc1 = &world->npcs[0]; - npc1->collision.pos.x = 15; - npc1->collision.pos.y = 15; - npc1->collision.size.x = 10; - npc1->collision.size.y = 10; - npc1->name = S("Matt"); - npc1->mode = NPC_ACTION_WAITING; - npc1->waitTime = 0; - npc1->maxWaitTime = 5; - npc1->currentNavNode = 87; - npc1->collision.pos.x = 15; - npc1->collision.pos.y = 15; - npc1->collision.size.x = 10; - npc1->collision.size.y = 10; + game->world = world; + world->random = Random_Seed(29237489723847); + world->npcCount = 2; + NPC *npc1 = &world->npcs[0]; + npc1->collision.pos.x = 15; + npc1->collision.pos.y = 15; + npc1->collision.size.x = 10; + npc1->collision.size.y = 10; + npc1->name = S("Matt"); + npc1->mode = NPC_ACTION_WAITING; + npc1->waitTime = 0; + npc1->maxWaitTime = 5; + npc1->currentNavNode = 87; + npc1->collision.pos.x = 15; + npc1->collision.pos.y = 15; + npc1->collision.size.x = 10; + npc1->collision.size.y = 10; - NPC *npc2 = &world->npcs[1]; - npc2->collision.pos.x = 15; - npc2->collision.pos.y = 15; - npc2->collision.size.x = 10; - npc2->collision.size.y = 10; - npc2->name = S("James"); - npc2->mode = NPC_ACTION_WAITING; - npc2->waitTime = 0; - npc2->maxWaitTime = 10; - npc2->currentNavNode = 0; + NPC *npc2 = &world->npcs[1]; + npc2->collision.pos.x = 15; + npc2->collision.pos.y = 15; + npc2->collision.size.x = 10; + npc2->collision.size.y = 10; + npc2->name = S("James"); + npc2->mode = NPC_ACTION_WAITING; + npc2->waitTime = 0; + npc2->maxWaitTime = 10; + npc2->currentNavNode = 0; - world->navMesh = &TestNavMesh; - world->npcPOI[0] = 100; - world->player.pos.x = 0; - world->player.pos.y = 0; + world->navMesh = &TestNavMesh; + world->npcPOI[0] = 100; + world->player.pos.x = 0; + world->player.pos.y = 0; } bool running = true; @@ -110,17 +111,17 @@ int main(int argc, char **argv) { { running = false; } - ProcessEvents(&e, game->world); + ProcessEvents(&e, game->world); } - UpdateWorld(1.0/60.0, game->world); + UpdateWorld(1.0 / 60.0, game->world); int w, h; SDL_GetWindowSizeInPixels(window, &w, &h); - game->draw.window_width = w; + game->draw.window_width = w; game->draw.window_height = h; - G_CalculateCamera(&game->camera, (F32) w / (F32) h); + G_CalculateCamera(&game->camera, (F32)w / (F32)h); Vk_Frame *frame = Vk_FrameBegin(window); VkCommandBuffer cmd = frame->cmd; @@ -132,27 +133,27 @@ int main(int argc, char **argv) { clear_colour.color.float32[3] = 1.0f; VkRenderingAttachmentInfo colour_attachment = {0}; - colour_attachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; - colour_attachment.imageView = vk.swapchain.views[frame->image]; + colour_attachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO; + colour_attachment.imageView = vk.swapchain.views[frame->image]; colour_attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - colour_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - colour_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; - colour_attachment.clearValue = clear_colour; + colour_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; + colour_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; + colour_attachment.clearValue = clear_colour; VkRenderingInfo rendering_info = {0}; - rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; - rendering_info.renderArea = (VkRect2D) { 0, 0, w, h }; - rendering_info.layerCount = 1; + rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO; + rendering_info.renderArea = (VkRect2D){0, 0, w, h}; + rendering_info.layerCount = 1; rendering_info.colorAttachmentCount = 1; - rendering_info.pColorAttachments = &colour_attachment; + rendering_info.pColorAttachments = &colour_attachment; vk.CmdBeginRendering(cmd, &rendering_info); D_Begin(&game->draw, frame, D_MAX_RECTS); - D_Rect(&game->draw, 0.0f, 0.0f, .texture = 1); + D_Rect(&game->draw, 0.0f, 0.0f, .texture = 1); D_Rect(&game->draw, -8.0f, 0.0f, .texture = 2, .scale = 2.0f); - D_Rect(&game->draw, 6.0f, 0.0f, .texture = 3); + D_Rect(&game->draw, 6.0f, 0.0f, .texture = 3); D_End(&game->draw, frame); diff --git a/code/game/aabb.h b/code/game/aabb.h index e61ba60..ca7f2f4 100644 --- a/code/game/aabb.h +++ b/code/game/aabb.h @@ -3,15 +3,14 @@ #include "../core/types.h" #include "../core/macros.h" - typedef struct AABB AABB; -struct AABB { +struct AABB +{ V2f pos; V2f size; }; - function bool AABB_Collide(AABB a, AABB b); function bool AABB_Point(AABB a, V2f v); -#endif // LD_GAME_AABB_H_ +#endif // LD_GAME_AABB_H_ diff --git a/code/game/impl/aabb.c b/code/game/impl/aabb.c index c1d1fba..a6a6360 100644 --- a/code/game/impl/aabb.c +++ b/code/game/impl/aabb.c @@ -1,15 +1,35 @@ #include "game/aabb.h" #include "core/types.h" -bool AABB_Collide(AABB a, AABB b) { +bool AABB_Collide(AABB a, AABB b) +{ bool collision_x = a.pos.x + a.size.x >= b.pos.x && b.pos.x + b.size.x >= a.pos.x; bool collision_y = a.pos.y + a.size.x >= b.pos.y && b.pos.y + b.size.y >= a.pos.y; return collision_x && collision_y; } -bool AABB_Point(AABB a, V2f v) { +bool AABB_Point(AABB a, V2f v) +{ bool collision_x = a.pos.x + a.size.x >= v.x && a.pos.x <= v.x; bool collision_y = a.pos.x + a.size.y >= v.y && a.pos.y <= v.y; return collision_x && collision_y; } +bool AABB_Slab(V2f origin, V2f point, AABB a) +{ + V2f start = a.pos; + V2f finish = {a.pos.x + a.size.x, a.pos.y + a.size.y}; + V2f direction = {origin.x - point.x, origin.y - point.y}; + // x + F32 tLow = (start.x - origin.x) / direction.x; + F32 tHigh = (finish.x - origin.x) / direction.x; + F32 tMin = min(tLow, tHigh); + F32 tMax = max(tLow, tHigh); + // y + tLow = (start.x - origin.x) / direction.x; + tHigh = (finish.x - origin.x) / direction.x; + + tMin = max(tMin, min(tLow, tHigh)); + tMax = min(tMax, max(tLow, tHigh)); + return tMax >= max(0.0, tMin); +} \ No newline at end of file diff --git a/code/game/impl/player.c b/code/game/impl/player.c index ae8ded7..043a729 100644 --- a/code/game/impl/player.c +++ b/code/game/impl/player.c @@ -4,23 +4,34 @@ void PlayerUpdate(SDL_Event *event, Player *player) { - SDL_KeyboardEvent key = event->key; - switch(key.key) { - case SDLK_W: { - player->pos.y += 10; - break; - } - case SDLK_A: { - player->pos.x -= 10; - break; - } - case SDLK_D: { - player->pos.x += 10; - break; - } - case SDLK_S: { - player->pos.y -= 10; - break; - } - } + SDL_KeyboardEvent key = event->key; + SDL_MouseButtonEvent mouseBtn = event->button; + switch (key.key) + { + case SDLK_W: + { + player->pos.y += 10; + break; + } + case SDLK_A: + { + player->pos.x -= 10; + break; + } + case SDLK_D: + { + player->pos.x += 10; + break; + } + case SDLK_S: + { + player->pos.y -= 10; + break; + } + } + if (mouseBtn.clicks == 1) + { + // shooting + player->bulletsLoaded -= 1; + } } diff --git a/code/game/player.h b/code/game/player.h index 8067026..fddf014 100644 --- a/code/game/player.h +++ b/code/game/player.h @@ -10,6 +10,7 @@ typedef struct Player Player; struct Player { V2f pos; + U32 bulletsLoaded; }; function void PlayerUpdate(SDL_Event *event, Player *player); From 628a6c5adea3e8e9e72145fa96d04013d3a4f0a9 Mon Sep 17 00:00:00 2001 From: declan Date: Sun, 5 Oct 2025 16:05:57 +0100 Subject: [PATCH 04/10] raycast fin --- code/game/aabb.h | 1 + code/game/impl/aabb.c | 15 +++++---- code/game/impl/nav.c | 75 ++++++++++++++++++++++++++---------------- code/game/impl/world.c | 12 ++++--- 4 files changed, 64 insertions(+), 39 deletions(-) diff --git a/code/game/aabb.h b/code/game/aabb.h index ca7f2f4..9344521 100644 --- a/code/game/aabb.h +++ b/code/game/aabb.h @@ -12,5 +12,6 @@ struct AABB function bool AABB_Collide(AABB a, AABB b); function bool AABB_Point(AABB a, V2f v); +function bool AABB_Slab(V2f origin, V2f point, AABB a); #endif // LD_GAME_AABB_H_ diff --git a/code/game/impl/aabb.c b/code/game/impl/aabb.c index a6a6360..9711a44 100644 --- a/code/game/impl/aabb.c +++ b/code/game/impl/aabb.c @@ -1,5 +1,5 @@ #include "game/aabb.h" -#include "core/types.h" +#include bool AABB_Collide(AABB a, AABB b) { @@ -19,17 +19,18 @@ bool AABB_Slab(V2f origin, V2f point, AABB a) { V2f start = a.pos; V2f finish = {a.pos.x + a.size.x, a.pos.y + a.size.y}; - V2f direction = {origin.x - point.x, origin.y - point.y}; + V2f invdirection = {1 / (origin.x - point.x), 1 / (origin.y - point.y)}; // x - F32 tLow = (start.x - origin.x) / direction.x; - F32 tHigh = (finish.x - origin.x) / direction.x; + F32 tLow = (start.x - origin.x) * invdirection.x; + F32 tHigh = (finish.x - origin.x) * invdirection.x; F32 tMin = min(tLow, tHigh); F32 tMax = max(tLow, tHigh); // y - tLow = (start.x - origin.x) / direction.x; - tHigh = (finish.x - origin.x) / direction.x; + tLow = (start.y - origin.y) * invdirection.y; + tHigh = (finish.y - origin.y) * invdirection.y; tMin = max(tMin, min(tLow, tHigh)); tMax = min(tMax, max(tLow, tHigh)); - return tMax >= max(0.0, tMin); + + return tMax >= tMin; } \ No newline at end of file diff --git a/code/game/impl/nav.c b/code/game/impl/nav.c index a6e7fe9..b420475 100644 --- a/code/game/impl/nav.c +++ b/code/game/impl/nav.c @@ -6,7 +6,8 @@ #define MAX_UNFINISHED 128 typedef struct navSearchNodeState navSearchNodeState; -struct navSearchNodeState{ +struct navSearchNodeState +{ bool visited; U64 distance; U32 shortest; @@ -14,37 +15,45 @@ struct navSearchNodeState{ }; typedef struct navSearchState navSearchState; -struct navSearchState{ +struct navSearchState +{ navSearchNodeState nodeStates[NAV_MAX_NODES]; }; -navSearchState initState(U32 start, U32 meshSize) { - navSearchState state = {}; - for(U32 i = 0; i < meshSize; i++) { - state.nodeStates[i].visited = false; +navSearchState initState(U32 start, U32 meshSize) +{ + navSearchState state; + for (U32 i = 0; i < meshSize; i++) + { + state.nodeStates[i].visited = false; state.nodeStates[i].addedToUnvisited = false; - state.nodeStates[i].distance = U64_MAX; - state.nodeStates[i].shortest = 0; + state.nodeStates[i].distance = U64_MAX; + state.nodeStates[i].shortest = 0; } state.nodeStates[start].distance = 0; return state; } -U32 getLowestState(U32 unfinishedIndexes[128], U32 unfinishedCount, navSearchState state, U32 *offset) { - U32 lowest = U32_MAX; +U32 getLowestState(U32 unfinishedIndexes[128], U32 unfinishedCount, navSearchState state, U32 *offset) +{ + U32 lowest = U32_MAX; U32 lowestI = U32_MAX; bool startFound = false; - for(U32 i = *offset; i < unfinishedCount; i++) { + for (U32 i = *offset; i < unfinishedCount; i++) + { navSearchNodeState checkNode = state.nodeStates[unfinishedIndexes[i]]; - if(checkNode.visited) { - if(!startFound) { + if (checkNode.visited) + { + if (!startFound) + { *offset = i; } continue; } startFound = true; - if (lowest > checkNode.distance) { - lowest = cast(U32) checkNode.distance; + if (lowest > checkNode.distance) + { + lowest = cast(U32) checkNode.distance; lowestI = unfinishedIndexes[i]; } } @@ -52,7 +61,8 @@ U32 getLowestState(U32 unfinishedIndexes[128], U32 unfinishedCount, navSearchSta } // Generate a path to follow between the start and end node. -NavPath Nav_Path(NavMesh *mesh, U32 start, U32 end) { +NavPath Nav_Path(NavMesh *mesh, U32 start, U32 end) +{ navSearchState state = initState(start, mesh->nodeCount); U32 unfinishedCount = 1; U32 unfinishedIndexes[NAV_MAX_NODES] = {start}; @@ -63,19 +73,26 @@ NavPath Nav_Path(NavMesh *mesh, U32 start, U32 end) { U32 unfinishedOffset = 0; U32 lowestNodeIndex = start; bool found = false; - while(!found) { - for(int connectionI = 0 ; connectionI < mesh->nodes[lowestNodeIndex].connectionCount; connectionI++) { - NavConnection *connection = &mesh->nodes[lowestNodeIndex].connections[connectionI]; - navSearchNodeState *testNode = &state.nodeStates[connection->NodeIndex]; - if(testNode->visited) {continue;} - U32 distance = cast(U32) (state.nodeStates[lowestNodeIndex].distance + connection->Cost); - distance += cast(U32) (mesh->nodes[end].pos.x - mesh->nodes[connection->NodeIndex].pos.x); - distance += cast(U32) (mesh->nodes[end].pos.y - mesh->nodes[connection->NodeIndex].pos.y); - if(testNode->distance > distance) { + while (!found) + { + for (int connectionI = 0; connectionI < mesh->nodes[lowestNodeIndex].connectionCount; connectionI++) + { + NavConnection *connection = &mesh->nodes[lowestNodeIndex].connections[connectionI]; + navSearchNodeState *testNode = &state.nodeStates[connection->NodeIndex]; + if (testNode->visited) + { + continue; + } + U32 distance = cast(U32)(state.nodeStates[lowestNodeIndex].distance + connection->Cost); + distance += cast(U32)(mesh->nodes[end].pos.x - mesh->nodes[connection->NodeIndex].pos.x); + distance += cast(U32)(mesh->nodes[end].pos.y - mesh->nodes[connection->NodeIndex].pos.y); + if (testNode->distance > distance) + { testNode->distance = distance; testNode->shortest = lowestNodeIndex; } - if(!testNode->addedToUnvisited) { + if (!testNode->addedToUnvisited) + { unfinishedIndexes[unfinishedCount] = connection->NodeIndex; unfinishedCount++; testNode->addedToUnvisited = true; @@ -83,13 +100,15 @@ NavPath Nav_Path(NavMesh *mesh, U32 start, U32 end) { } state.nodeStates[lowestNodeIndex].visited = true; lowestNodeIndex = getLowestState(unfinishedIndexes, unfinishedCount, state, &unfinishedOffset); - if(lowestNodeIndex == end) { + if (lowestNodeIndex == end) + { found = true; } } NavPath res_path = {0}; U32 index = end; - while(index!=start) { + while (index != start) + { res_path.indexes[res_path.nodeCount] = index; res_path.nodeCount++; index = state.nodeStates[index].shortest; diff --git a/code/game/impl/world.c b/code/game/impl/world.c index 405601f..900ff79 100644 --- a/code/game/impl/world.c +++ b/code/game/impl/world.c @@ -3,16 +3,20 @@ #include "../player.h" #include -void UpdateWorld(F32 delta, World *world) { +void UpdateWorld(F32 delta, World *world) +{ UpdateNPCs(delta, world); } -void UpdateNPCs(F32 delta, World *world) { - for(U32 i = 0; i < world->npcCount; i++) { +void UpdateNPCs(F32 delta, World *world) +{ + for (U32 i = 0; i < world->npcCount; i++) + { updateNPC(delta, &world->npcs[i], world); } } -void ProcessEvents(SDL_Event *event, World *world) { +void ProcessEvents(SDL_Event *event, World *world) +{ PlayerUpdate(event, &world->player); } From 9a46a802e09c8d79951a4202733ccf96f7f2c11b Mon Sep 17 00:00:00 2001 From: declan Date: Sun, 5 Oct 2025 16:07:55 +0100 Subject: [PATCH 05/10] add ignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index f25181b..c094b4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ build/ code/compile_commands.json code/.cache +.vscode From 222575b318db8f68cfe1db372f0d4b6dbe474ec3 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 5 Oct 2025 16:49:18 +0100 Subject: [PATCH 06/10] feat: People go walkies --- code/core/math.h | 2 ++ code/first.c | 49 +++++++++++++++++++++++++++++------------- code/game/impl/nav.c | 14 ++++++++---- code/game/impl/npc.c | 7 +++--- code/game/impl/world.c | 6 ++++++ code/game/npc.h | 2 +- code/game/world.h | 1 + 7 files changed, 58 insertions(+), 23 deletions(-) diff --git a/code/core/math.h b/code/core/math.h index 7ac915d..7c916a6 100644 --- a/code/core/math.h +++ b/code/core/math.h @@ -4,6 +4,8 @@ #define PI_F32 (3.14159265358979323846264338f) #define TAU_F32 (2.0f * PI_F32) +#define Abs(x) (((x) < 0 ? -(x) : (x))) + typedef struct Random Random; struct Random { U64 state; diff --git a/code/first.c b/code/first.c index 4a2bd06..6491283 100644 --- a/code/first.c +++ b/code/first.c @@ -1,6 +1,7 @@ #include #include #include +#include #define STB_IMAGE_IMPLEMENTATION 1 #include @@ -61,24 +62,27 @@ int main(int argc, char **argv) { camera->farp = 1000.0f; game->draw.camera = camera; + game->camera.p.z = 200; World *world = M_ArenaPush(arena, World); game->world = world; world->random = Random_Seed(29237489723847); - world->npcCount = 2; - NPC *npc1 = &world->npcs[0]; - npc1->collision.pos.x = 15; - npc1->collision.pos.y = 15; - npc1->collision.size.x = 10; - npc1->collision.size.y = 10; - npc1->name = S("Matt"); - npc1->mode = NPC_ACTION_WAITING; - npc1->waitTime = 0; - npc1->maxWaitTime = 5; - npc1->currentNavNode = 87; - npc1->collision.pos.x = 15; - npc1->collision.pos.y = 15; - npc1->collision.size.x = 10; - npc1->collision.size.y = 10; + world->npcCount = 100; + for(int i = 0; i < 100; i++) { + NPC *npc1 = &world->npcs[i]; + npc1->collision.pos.x = 15; + npc1->collision.pos.y = 15; + npc1->collision.size.x = 10; + npc1->collision.size.y = 10; + npc1->name = S("Matt"); + npc1->mode = NPC_ACTION_WAITING; + npc1->waitTime = 0; + npc1->maxWaitTime = 5; + npc1->currentNavNode = 0; + npc1->collision.pos.x = 15; + npc1->collision.pos.y = 15; + npc1->collision.size.x = 10; + npc1->collision.size.y = 10; + } NPC *npc2 = &world->npcs[1]; npc2->collision.pos.x = 15; @@ -110,6 +114,20 @@ int main(int argc, char **argv) { { running = false; } + if (e.type == SDL_EVENT_KEY_DOWN) { + if(e.key.key == SDLK_DOWN) { + game->camera.p.y += 5; + } + if(e.key.key == SDLK_UP) { + game->camera.p.y -= 5; + } + if(e.key.key == SDLK_RIGHT) { + game->camera.p.x += 5; + } + if(e.key.key == SDLK_LEFT) { + game->camera.p.x -= 5; + } + } ProcessEvents(&e, game->world); } UpdateWorld(1.0/60.0, game->world); @@ -150,6 +168,7 @@ int main(int argc, char **argv) { D_Begin(&game->draw, frame, D_MAX_RECTS); + RenderWorld(game->world, &game->draw); D_Rect(&game->draw, 0.0f, 0.0f, .texture = 1); D_Rect(&game->draw, -8.0f, 0.0f, .texture = 2, .scale = 2.0f); D_Rect(&game->draw, 6.0f, 0.0f, .texture = 3); diff --git a/code/game/impl/nav.c b/code/game/impl/nav.c index a6e7fe9..0e6c416 100644 --- a/code/game/impl/nav.c +++ b/code/game/impl/nav.c @@ -8,7 +8,7 @@ typedef struct navSearchNodeState navSearchNodeState; struct navSearchNodeState{ bool visited; - U64 distance; + F64 distance; U32 shortest; bool addedToUnvisited; }; @@ -53,6 +53,12 @@ U32 getLowestState(U32 unfinishedIndexes[128], U32 unfinishedCount, navSearchSta // Generate a path to follow between the start and end node. NavPath Nav_Path(NavMesh *mesh, U32 start, U32 end) { + // This is a stupid fix, since it's easier to work backwards + // to generate a path, I'm swapping the start / end, to generate + // it backwards + U32 tmp = end; + end = start; + start = tmp; navSearchState state = initState(start, mesh->nodeCount); U32 unfinishedCount = 1; U32 unfinishedIndexes[NAV_MAX_NODES] = {start}; @@ -68,9 +74,9 @@ NavPath Nav_Path(NavMesh *mesh, U32 start, U32 end) { NavConnection *connection = &mesh->nodes[lowestNodeIndex].connections[connectionI]; navSearchNodeState *testNode = &state.nodeStates[connection->NodeIndex]; if(testNode->visited) {continue;} - U32 distance = cast(U32) (state.nodeStates[lowestNodeIndex].distance + connection->Cost); - distance += cast(U32) (mesh->nodes[end].pos.x - mesh->nodes[connection->NodeIndex].pos.x); - distance += cast(U32) (mesh->nodes[end].pos.y - mesh->nodes[connection->NodeIndex].pos.y); + F64 distance = cast(F64) (state.nodeStates[lowestNodeIndex].distance + connection->Cost); + distance += cast(F64) Abs((mesh->nodes[end].pos.x - mesh->nodes[connection->NodeIndex].pos.x)); + distance += cast(F64) Abs((mesh->nodes[end].pos.y - mesh->nodes[connection->NodeIndex].pos.y)); if(testNode->distance > distance) { testNode->distance = distance; testNode->shortest = lowestNodeIndex; diff --git a/code/game/impl/npc.c b/code/game/impl/npc.c index bc7358e..bcc54fe 100644 --- a/code/game/impl/npc.c +++ b/code/game/impl/npc.c @@ -13,7 +13,7 @@ void updateNPC(F32 delta, NPC *npc, World *world) { npc->mode = NPC_ACTION_WALKING; // TODO change targets to poi's rather than just random nodes npc->targetNavNode = Random_U32(&world->random, 0, world->navMesh->nodeCount); - printf("Starting to nav path\n"); + printf("Starting to nav path from %d to %d\n", npc->currentNavNode, npc->targetNavNode); npc->path = Nav_Path(world->navMesh, npc->currentNavNode, npc->targetNavNode); printf("done\n"); npc->walkTimer = 0; @@ -29,14 +29,15 @@ void updateNPC(F32 delta, NPC *npc, World *world) { npc->mode = NPC_ACTION_WAITING; npc->maxWaitTime = Random_F32(&world->random, 10, 40); npc->waitTime = 0; + npc->currentNavNode = npc->targetNavNode; npc->pathIndex = 0; return; } - npc->pathIndex+=1; npc->currentNavNode = npc->path.indexes[npc->pathIndex]; + npc->pathIndex+=1; } NavNode cNav = world->navMesh->nodes[npc->currentNavNode]; - NavNode tNav = world->navMesh->nodes[npc->pathIndex]; + NavNode tNav = world->navMesh->nodes[npc->path.indexes[npc->pathIndex]]; npc->collision.pos.x = cNav.pos.x * (1 - npc->walkTimer/NPC_SPEED) + tNav.pos.x * npc->walkTimer/NPC_SPEED; npc->collision.pos.y = cNav.pos.y * (1 - npc->walkTimer/NPC_SPEED) + tNav.pos.y * npc->walkTimer/NPC_SPEED; break; diff --git a/code/game/impl/world.c b/code/game/impl/world.c index 405601f..f168744 100644 --- a/code/game/impl/world.c +++ b/code/game/impl/world.c @@ -16,3 +16,9 @@ void UpdateNPCs(F32 delta, World *world) { void ProcessEvents(SDL_Event *event, World *world) { PlayerUpdate(event, &world->player); } + +void RenderWorld(World *world, D_Context *draw) { + for(int i = 0; i < world->npcCount; i++) { + D_Rect(draw, world->npcs[i].collision.pos.x, world->npcs[i].collision.pos.y, .texture = 1); + } +} diff --git a/code/game/npc.h b/code/game/npc.h index ad66f50..b0f77aa 100644 --- a/code/game/npc.h +++ b/code/game/npc.h @@ -5,7 +5,7 @@ #include "npc_look.h" #include "../core/types.h" -#define NPC_SPEED 0.2f +#define NPC_SPEED 1.0f typedef enum NPC_ACTION NPC_ACTION; enum NPC_ACTION { diff --git a/code/game/world.h b/code/game/world.h index d237538..b8f1763 100644 --- a/code/game/world.h +++ b/code/game/world.h @@ -35,6 +35,7 @@ struct World { }; function void UpdateWorld(F32 delta, World *world); +function void RenderWorld(World *world, D_Context *drawContext ); function void ProcessEvents(SDL_Event *event, World *world); function void UpdateNPCs(F32 delta, World *world); function void updateNPC(F32 delta, NPC *npc, World *world); From 98095c907f0f0a5be061e4fdbc7619152979fd42 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 5 Oct 2025 16:53:56 +0100 Subject: [PATCH 07/10] chore: removed nav printf --- code/game/impl/npc.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/code/game/impl/npc.c b/code/game/impl/npc.c index bcc54fe..00b03fa 100644 --- a/code/game/impl/npc.c +++ b/code/game/impl/npc.c @@ -3,8 +3,6 @@ #include "core/types.h" #include "core/math.h" -#include - void updateNPC(F32 delta, NPC *npc, World *world) { switch (npc->mode) { case NPC_ACTION_WAITING: @@ -13,11 +11,8 @@ void updateNPC(F32 delta, NPC *npc, World *world) { npc->mode = NPC_ACTION_WALKING; // TODO change targets to poi's rather than just random nodes npc->targetNavNode = Random_U32(&world->random, 0, world->navMesh->nodeCount); - printf("Starting to nav path from %d to %d\n", npc->currentNavNode, npc->targetNavNode); npc->path = Nav_Path(world->navMesh, npc->currentNavNode, npc->targetNavNode); - printf("done\n"); npc->walkTimer = 0; - printf("%.*s started walking to %d\n", Sv(npc->name), npc->targetNavNode); } break; case NPC_ACTION_WALKING: @@ -25,7 +20,6 @@ void updateNPC(F32 delta, NPC *npc, World *world) { if(npc->walkTimer >= NPC_SPEED){ npc->walkTimer = 0; if(npc->path.nodeCount == npc->pathIndex+1){ - printf("Finished! so I'm waiting\n"); npc->mode = NPC_ACTION_WAITING; npc->maxWaitTime = Random_F32(&world->random, 10, 40); npc->waitTime = 0; From e20e537e978b5f175449d8d7d76c4c7669558ef5 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 5 Oct 2025 18:05:01 +0100 Subject: [PATCH 08/10] feat: Player controller --- code/core/impl/math.c | 14 +++++++++ code/core/math.h | 3 ++ code/first.c | 21 +++---------- code/game/impl/npc.c | 6 ++-- code/game/impl/player.c | 70 +++++++++++++++++++++++++++-------------- code/game/impl/world.c | 4 ++- code/game/npc.h | 2 +- code/game/player.h | 14 ++++++++- code/game/world.h | 2 +- 9 files changed, 89 insertions(+), 47 deletions(-) diff --git a/code/core/impl/math.c b/code/core/impl/math.c index 8859ff0..440cb77 100644 --- a/code/core/impl/math.c +++ b/code/core/impl/math.c @@ -1,8 +1,22 @@ +#include V2f V2F(F32 x, F32 y) { V2f result = { x, y }; return result; } +function V2f V2f_Scale(V2f x, F32 scale) { + return V2F(x.x * scale, x.y * scale); +} + +V2f NormaliseV2F(V2f x) { + F32 magnitude = sqrtf((x.x * x.x) + (x.y +x.y)); + if(magnitude > 0.0){ + F32 inverse = 1.0f/magnitude; + return V2F(x.x*inverse, x.y*inverse); + } + return x; +} + V3f V3F(F32 x, F32 y, F32 z) { V3f result = { x, y, z }; return result; diff --git a/code/core/math.h b/code/core/math.h index 7c916a6..3ed95d6 100644 --- a/code/core/math.h +++ b/code/core/math.h @@ -121,6 +121,9 @@ function V3f M4x4F_VMul3(Mat4x4F m, V3f v); function Mat4x4FInv M4x4F_Perspective(F32 fov, F32 aspect, F32 nearp, F32 farp); function Mat4x4FInv M4x4F_CameraView(V3f x, V3f y, V3f z, V3f p); +function V2f NormaliseV2F(V2f x); +function V2f V2f_Scale(V2f x, F32 scale); + // Random function Random Random_Seed(U64 seed); diff --git a/code/first.c b/code/first.c index ea0c706..cf01a09 100644 --- a/code/first.c +++ b/code/first.c @@ -63,12 +63,11 @@ int main(int argc, char **argv) camera->farp = 1000.0f; game->draw.camera = camera; - game->camera.p.z = 200; World *world = M_ArenaPush(arena, World); game->world = world; world->random = Random_Seed(29237489723847); - world->npcCount = 100; - for(int i = 0; i < 100; i++) { + world->npcCount = 1023; + for(int i = 0; i < world->npcCount; i++) { NPC *npc1 = &world->npcs[i]; npc1->collision.pos.x = 15; npc1->collision.pos.y = 15; @@ -115,23 +114,11 @@ int main(int argc, char **argv) { running = false; } - if (e.type == SDL_EVENT_KEY_DOWN) { - if(e.key.key == SDLK_DOWN) { - game->camera.p.y += 5; - } - if(e.key.key == SDLK_UP) { - game->camera.p.y -= 5; - } - if(e.key.key == SDLK_RIGHT) { - game->camera.p.x += 5; - } - if(e.key.key == SDLK_LEFT) { - game->camera.p.x -= 5; - } - } ProcessEvents(&e, game->world); } UpdateWorld(1.0 / 60.0, game->world); + game->camera.p.x = game->world->player.pos.x; + game->camera.p.y = game->world->player.pos.y; int w, h; SDL_GetWindowSizeInPixels(window, &w, &h); diff --git a/code/game/impl/npc.c b/code/game/impl/npc.c index 00b03fa..0893aad 100644 --- a/code/game/impl/npc.c +++ b/code/game/impl/npc.c @@ -10,7 +10,9 @@ void updateNPC(F32 delta, NPC *npc, World *world) { if(npc->waitTime > npc->maxWaitTime) { npc->mode = NPC_ACTION_WALKING; // TODO change targets to poi's rather than just random nodes - npc->targetNavNode = Random_U32(&world->random, 0, world->navMesh->nodeCount); + do { + npc->targetNavNode = Random_U32(&world->random, 0, world->navMesh->nodeCount); + } while(npc->targetNavNode == npc->currentNavNode); npc->path = Nav_Path(world->navMesh, npc->currentNavNode, npc->targetNavNode); npc->walkTimer = 0; } @@ -21,7 +23,7 @@ void updateNPC(F32 delta, NPC *npc, World *world) { npc->walkTimer = 0; if(npc->path.nodeCount == npc->pathIndex+1){ npc->mode = NPC_ACTION_WAITING; - npc->maxWaitTime = Random_F32(&world->random, 10, 40); + npc->maxWaitTime = Random_F32(&world->random, 1, 2); npc->waitTime = 0; npc->currentNavNode = npc->targetNavNode; npc->pathIndex = 0; diff --git a/code/game/impl/player.c b/code/game/impl/player.c index 043a729..9f998da 100644 --- a/code/game/impl/player.c +++ b/code/game/impl/player.c @@ -1,33 +1,36 @@ #include "../player.h" +#include #include -#include -void PlayerUpdate(SDL_Event *event, Player *player) +void PlayerInput(SDL_Event *event, Player *player) { SDL_KeyboardEvent key = event->key; SDL_MouseButtonEvent mouseBtn = event->button; - switch (key.key) - { - case SDLK_W: - { - player->pos.y += 10; - break; - } - case SDLK_A: - { - player->pos.x -= 10; - break; - } - case SDLK_D: - { - player->pos.x += 10; - break; - } - case SDLK_S: - { - player->pos.y -= 10; - break; - } + if(event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_KEY_UP) { + bool val = event->type == SDL_EVENT_KEY_DOWN; + switch (key.key) + { + case SDLK_W: + { + player->controls.upDown = val; + break; + } + case SDLK_A: + { + player->controls.leftDown = val; + break; + } + case SDLK_D: + { + player->controls.rightDown = val; + break; + } + case SDLK_S: + { + player->controls.downDown = val; + break; + } + } } if (mouseBtn.clicks == 1) { @@ -35,3 +38,22 @@ void PlayerUpdate(SDL_Event *event, Player *player) player->bulletsLoaded -= 1; } } + +void PlayerUpdate(F32 delta, Player *player) { + V2f dir = V2F(0, 0); + if(player->controls.upDown) { + dir.y -= 1; + } + if(player->controls.downDown) { + dir.y += 1; + } + if(player->controls.leftDown) { + dir.x -= 1; + } + if(player->controls.rightDown) { + dir.x += 1; + } + dir = V2f_Scale(NormaliseV2F(dir), PLAYER_SPEED*delta); + player->pos.x += dir.x; + player->pos.y += dir.y; +} diff --git a/code/game/impl/world.c b/code/game/impl/world.c index a06fd6b..e6aec5a 100644 --- a/code/game/impl/world.c +++ b/code/game/impl/world.c @@ -6,6 +6,7 @@ void UpdateWorld(F32 delta, World *world) { UpdateNPCs(delta, world); + PlayerUpdate(delta, &world->player); } void UpdateNPCs(F32 delta, World *world) @@ -18,11 +19,12 @@ void UpdateNPCs(F32 delta, World *world) void ProcessEvents(SDL_Event *event, World *world) { - PlayerUpdate(event, &world->player); + PlayerInput(event, &world->player); } void RenderWorld(World *world, D_Context *draw) { for(int i = 0; i < world->npcCount; i++) { D_Rect(draw, world->npcs[i].collision.pos.x, world->npcs[i].collision.pos.y, .texture = 1); } + D_Rect(draw, world->player.pos.x, world->player.pos.y, .texture = 1); } diff --git a/code/game/npc.h b/code/game/npc.h index b0f77aa..0692f09 100644 --- a/code/game/npc.h +++ b/code/game/npc.h @@ -5,7 +5,7 @@ #include "npc_look.h" #include "../core/types.h" -#define NPC_SPEED 1.0f +#define NPC_SPEED 0.1f typedef enum NPC_ACTION NPC_ACTION; enum NPC_ACTION { diff --git a/code/game/player.h b/code/game/player.h index fddf014..dffdc0e 100644 --- a/code/game/player.h +++ b/code/game/player.h @@ -6,13 +6,25 @@ #include +#define PLAYER_SPEED 10.0f + +typedef struct ControlState ControlState; +struct ControlState { + bool rightDown; + bool leftDown; + bool upDown; + bool downDown; +}; + typedef struct Player Player; struct Player { V2f pos; U32 bulletsLoaded; + ControlState controls; }; -function void PlayerUpdate(SDL_Event *event, Player *player); +function void PlayerInput(SDL_Event *event, Player *player); +function void PlayerUpdate(F32 delta, Player *player); #endif // LD_GAME_PLAYER_H_ diff --git a/code/game/world.h b/code/game/world.h index b8f1763..896faae 100644 --- a/code/game/world.h +++ b/code/game/world.h @@ -24,7 +24,7 @@ struct World { //// NPCs U32 npcCount; - NPC npcs[128]; + NPC npcs[1024]; ////Bandit // The bandit the player is after. From b8f47b8f612a96dc3392bf114e188b88b840b74a Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 5 Oct 2025 18:28:58 +0100 Subject: [PATCH 09/10] feat: added npc poi for custom npcs --- code/core/impl/math.c | 2 +- code/first.c | 2 +- code/game/impl/npc.c | 4 +++- code/game/npc.h | 6 +++++- code/game/world.h | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/code/core/impl/math.c b/code/core/impl/math.c index 440cb77..0385ddd 100644 --- a/code/core/impl/math.c +++ b/code/core/impl/math.c @@ -9,7 +9,7 @@ function V2f V2f_Scale(V2f x, F32 scale) { } V2f NormaliseV2F(V2f x) { - F32 magnitude = sqrtf((x.x * x.x) + (x.y +x.y)); + F32 magnitude = sqrtf((x.x * x.x) + (x.y * x.y)); if(magnitude > 0.0){ F32 inverse = 1.0f/magnitude; return V2F(x.x*inverse, x.y*inverse); diff --git a/code/first.c b/code/first.c index cf01a09..89bf15d 100644 --- a/code/first.c +++ b/code/first.c @@ -66,7 +66,7 @@ int main(int argc, char **argv) World *world = M_ArenaPush(arena, World); game->world = world; world->random = Random_Seed(29237489723847); - world->npcCount = 1023; + world->npcCount = 100; for(int i = 0; i < world->npcCount; i++) { NPC *npc1 = &world->npcs[i]; npc1->collision.pos.x = 15; diff --git a/code/game/impl/npc.c b/code/game/impl/npc.c index 0893aad..d9a8e6e 100644 --- a/code/game/impl/npc.c +++ b/code/game/impl/npc.c @@ -10,6 +10,8 @@ void updateNPC(F32 delta, NPC *npc, World *world) { if(npc->waitTime > npc->maxWaitTime) { npc->mode = NPC_ACTION_WALKING; // TODO change targets to poi's rather than just random nodes + // TODO choose either global POI's or use NPC custom poi if + // customPOI is true do { npc->targetNavNode = Random_U32(&world->random, 0, world->navMesh->nodeCount); } while(npc->targetNavNode == npc->currentNavNode); @@ -23,7 +25,7 @@ void updateNPC(F32 delta, NPC *npc, World *world) { npc->walkTimer = 0; if(npc->path.nodeCount == npc->pathIndex+1){ npc->mode = NPC_ACTION_WAITING; - npc->maxWaitTime = Random_F32(&world->random, 1, 2); + npc->maxWaitTime = Random_F32(&world->random, 20, 140); npc->waitTime = 0; npc->currentNavNode = npc->targetNavNode; npc->pathIndex = 0; diff --git a/code/game/npc.h b/code/game/npc.h index 0692f09..3805ba4 100644 --- a/code/game/npc.h +++ b/code/game/npc.h @@ -5,7 +5,7 @@ #include "npc_look.h" #include "../core/types.h" -#define NPC_SPEED 0.1f +#define NPC_SPEED 1.0f typedef enum NPC_ACTION NPC_ACTION; enum NPC_ACTION { @@ -23,6 +23,10 @@ struct NPC { Str8 name; NPC_LOOK look; + bool customPOI; + U32 customPOICount; + U32 npcPOI[16]; + //// Actions NPC_ACTION mode; // How long they've been waiting diff --git a/code/game/world.h b/code/game/world.h index 896faae..b8f1763 100644 --- a/code/game/world.h +++ b/code/game/world.h @@ -24,7 +24,7 @@ struct World { //// NPCs U32 npcCount; - NPC npcs[1024]; + NPC npcs[128]; ////Bandit // The bandit the player is after. From 635e7b1c1e31bcd5a3b624831fdbaf0f7bb206d8 Mon Sep 17 00:00:00 2001 From: Matt Date: Sun, 5 Oct 2025 21:05:29 +0100 Subject: [PATCH 10/10] feat: Initial bandit roaming --- code/first.c | 31 ++++++++++++++++++------------- code/game/bandit.h | 3 ++- code/game/impl/bandit.c | 41 +++++++++++++++++++++++++++++++++++++++++ code/game/impl/npc.c | 2 +- code/game/impl/player.c | 29 ++++++++++++++++++++++++----- code/game/impl/world.c | 12 ++++++++++-- code/game/player.h | 6 ++++++ code/game/world.h | 3 ++- 8 files changed, 104 insertions(+), 23 deletions(-) create mode 100644 code/game/impl/bandit.c diff --git a/code/first.c b/code/first.c index 89bf15d..0258a0d 100644 --- a/code/first.c +++ b/code/first.c @@ -18,6 +18,7 @@ #include "game/impl/world.c" #include "game/impl/npc.c" +#include "game/impl/bandit.c" #include "game/testnavmesh.h" int main(int argc, char **argv) @@ -66,13 +67,13 @@ int main(int argc, char **argv) World *world = M_ArenaPush(arena, World); game->world = world; world->random = Random_Seed(29237489723847); - world->npcCount = 100; + world->npcCount = 1; for(int i = 0; i < world->npcCount; i++) { NPC *npc1 = &world->npcs[i]; npc1->collision.pos.x = 15; npc1->collision.pos.y = 15; - npc1->collision.size.x = 10; - npc1->collision.size.y = 10; + npc1->collision.size.x = 1; + npc1->collision.size.y = 1; npc1->name = S("Matt"); npc1->mode = NPC_ACTION_WAITING; npc1->waitTime = 0; @@ -84,21 +85,25 @@ int main(int argc, char **argv) npc1->collision.size.y = 10; } - NPC *npc2 = &world->npcs[1]; - npc2->collision.pos.x = 15; - npc2->collision.pos.y = 15; - npc2->collision.size.x = 10; - npc2->collision.size.y = 10; - npc2->name = S("James"); - npc2->mode = NPC_ACTION_WAITING; - npc2->waitTime = 0; - npc2->maxWaitTime = 10; - npc2->currentNavNode = 0; + Bandit *badman = &world->bandit; + badman->collision.pos.x = 15; + badman->collision.pos.y = 15; + badman->collision.size.x = 10; + badman->collision.size.y = 10; + badman->name = S("Leroy Brown"); + badman->mode = BANDIT_WAITING; + badman->waitTime = 0; + badman->maxWaitTime = 2; + badman->poiCount = 2; + badman->pointsOfInterest[0] = 937; + badman->pointsOfInterest[1] = 12; world->navMesh = &TestNavMesh; world->npcPOI[0] = 100; world->player.pos.x = 0; world->player.pos.y = 0; + world->player.bulletsLoaded = PLAYER_BULLET_COUNT; + world->player.reloadTimer = 0; } bool running = true; diff --git a/code/game/bandit.h b/code/game/bandit.h index 2c25215..3814017 100644 --- a/code/game/bandit.h +++ b/code/game/bandit.h @@ -6,7 +6,7 @@ enum BANDIT_ACTION { BANDIT_WAITING, BANDIT_WALKING, BANDIT_RUNNING, - BANDIT_SHOOTOUT, + BANDIT_SHOOTING, }; typedef struct Bandit Bandit; @@ -22,6 +22,7 @@ struct Bandit { // How long they will wait in this location. F32 maxWaitTime; + U32 poiCount; // Places the bandit walks to / from // E.g. hide outs, home, saloon U32 pointsOfInterest[12]; diff --git a/code/game/impl/bandit.c b/code/game/impl/bandit.c new file mode 100644 index 0000000..5aeb2a4 --- /dev/null +++ b/code/game/impl/bandit.c @@ -0,0 +1,41 @@ +#include "game/world.h" +#include "game/bandit.h" + +void UpdateBandit(F32 delta, Bandit *bandit, World *world) { + switch (bandit->mode) { + case BANDIT_WAITING: + bandit->waitTime+=delta; + if(bandit->waitTime > bandit->maxWaitTime) { + bandit->mode = BANDIT_WALKING; + do { + U32 randomChoice = Random_U32(&world->random, 0, bandit->poiCount); + bandit->targetNavNode = bandit->pointsOfInterest[randomChoice]; + } while(bandit->targetNavNode == bandit->currentNavNode); + bandit->path = Nav_Path(world->navMesh, bandit->currentNavNode, bandit->targetNavNode); + bandit->walkTimer = 0; + } + break; + case BANDIT_WALKING: + bandit->walkTimer+=delta; + if(bandit->walkTimer >= NPC_SPEED){ + bandit->walkTimer = 0; + if(bandit->path.nodeCount == bandit->pathIndex+1){ + bandit->mode = BANDIT_WAITING; + bandit->maxWaitTime = Random_F32(&world->random, 20, 140); + bandit->waitTime = 0; + bandit->currentNavNode = bandit->targetNavNode; + bandit->pathIndex = 0; + return; + } + bandit->currentNavNode = bandit->path.indexes[bandit->pathIndex]; + bandit->pathIndex+=1; + } + NavNode cNav = world->navMesh->nodes[bandit->currentNavNode]; + NavNode tNav = world->navMesh->nodes[bandit->path.indexes[bandit->pathIndex]]; + bandit->collision.pos.x = cNav.pos.x * (1 - bandit->walkTimer/NPC_SPEED) + tNav.pos.x * bandit->walkTimer/NPC_SPEED; + bandit->collision.pos.y = cNav.pos.y * (1 - bandit->walkTimer/NPC_SPEED) + tNav.pos.y * bandit->walkTimer/NPC_SPEED; + break; + // TODO Shooting + // TODO Running away + } +} diff --git a/code/game/impl/npc.c b/code/game/impl/npc.c index d9a8e6e..3ea5599 100644 --- a/code/game/impl/npc.c +++ b/code/game/impl/npc.c @@ -3,7 +3,7 @@ #include "core/types.h" #include "core/math.h" -void updateNPC(F32 delta, NPC *npc, World *world) { +void UpdateNPC(F32 delta, NPC *npc, World *world) { switch (npc->mode) { case NPC_ACTION_WAITING: npc->waitTime+=delta; diff --git a/code/game/impl/player.c b/code/game/impl/player.c index 9f998da..f9c442c 100644 --- a/code/game/impl/player.c +++ b/code/game/impl/player.c @@ -1,9 +1,11 @@ #include "../player.h" #include #include +#include void PlayerInput(SDL_Event *event, Player *player) { + player->controls.shot = false; SDL_KeyboardEvent key = event->key; SDL_MouseButtonEvent mouseBtn = event->button; if(event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_KEY_UP) { @@ -32,11 +34,21 @@ void PlayerInput(SDL_Event *event, Player *player) } } } - if (mouseBtn.clicks == 1) - { - // shooting - player->bulletsLoaded -= 1; - } + if ( + event->type == SDL_EVENT_MOUSE_BUTTON_DOWN + && mouseBtn.button == SDL_BUTTON_LEFT + ) { + if(player->bulletsLoaded > 0) { + // shooting + player->bulletsLoaded -= 1; + player->controls.shot = true; + player->shotPos = V2F(mouseBtn.x, mouseBtn.y); + printf("shot %f %f\n", mouseBtn.x, mouseBtn.y); + } else if(player->reloadTimer == 0) { + player->reloadTimer = PLAYER_RELOAD_TIME; + printf("reloading\n"); + }; + } } void PlayerUpdate(F32 delta, Player *player) { @@ -53,6 +65,13 @@ void PlayerUpdate(F32 delta, Player *player) { if(player->controls.rightDown) { dir.x += 1; } + if(player->reloadTimer > 0) { + player->reloadTimer-=delta; + if(player->reloadTimer <= 0) { + player->bulletsLoaded = PLAYER_BULLET_COUNT; + player->reloadTimer = 0; + } + } dir = V2f_Scale(NormaliseV2F(dir), PLAYER_SPEED*delta); player->pos.x += dir.x; player->pos.y += dir.y; diff --git a/code/game/impl/world.c b/code/game/impl/world.c index e6aec5a..07f8ab3 100644 --- a/code/game/impl/world.c +++ b/code/game/impl/world.c @@ -5,6 +5,7 @@ void UpdateWorld(F32 delta, World *world) { + UpdateBandit(delta, &world->bandit, world); UpdateNPCs(delta, world); PlayerUpdate(delta, &world->player); } @@ -13,7 +14,11 @@ void UpdateNPCs(F32 delta, World *world) { for (U32 i = 0; i < world->npcCount; i++) { - updateNPC(delta, &world->npcs[i], world); + UpdateNPC(delta, &world->npcs[i], world); + if(world->player.controls.shot && AABB_Point(world->npcs[i].collision, world->player.shotPos)) { + // TODO we need to unproject the mouse location !!! + printf("You shot %.*s\n", Sv(world->npcs[i].name)); + } } } @@ -24,7 +29,10 @@ void ProcessEvents(SDL_Event *event, World *world) void RenderWorld(World *world, D_Context *draw) { for(int i = 0; i < world->npcCount; i++) { - D_Rect(draw, world->npcs[i].collision.pos.x, world->npcs[i].collision.pos.y, .texture = 1); + NPC npc = world->npcs[i]; + D_Rect(draw, npc.collision.pos.x, npc.collision.pos.y, .texture = 1); + D_Rect(draw, npc.collision.pos.x, npc.collision.pos.y, .texture = 0, .dim = npc.collision.size); } + D_Rect(draw, world->bandit.collision.pos.x, world->bandit.collision.pos.y, .texture = 9); D_Rect(draw, world->player.pos.x, world->player.pos.y, .texture = 1); } diff --git a/code/game/player.h b/code/game/player.h index dffdc0e..5a994dc 100644 --- a/code/game/player.h +++ b/code/game/player.h @@ -7,6 +7,8 @@ #include #define PLAYER_SPEED 10.0f +#define PLAYER_RELOAD_TIME 1.5f +#define PLAYER_BULLET_COUNT 6 typedef struct ControlState ControlState; struct ControlState { @@ -14,6 +16,7 @@ struct ControlState { bool leftDown; bool upDown; bool downDown; + bool shot; }; typedef struct Player Player; @@ -22,6 +25,9 @@ struct Player V2f pos; U32 bulletsLoaded; ControlState controls; + V2f shotPos; + + F32 reloadTimer; }; function void PlayerInput(SDL_Event *event, Player *player); diff --git a/code/game/world.h b/code/game/world.h index b8f1763..2cd96eb 100644 --- a/code/game/world.h +++ b/code/game/world.h @@ -38,6 +38,7 @@ function void UpdateWorld(F32 delta, World *world); function void RenderWorld(World *world, D_Context *drawContext ); function void ProcessEvents(SDL_Event *event, World *world); function void UpdateNPCs(F32 delta, World *world); -function void updateNPC(F32 delta, NPC *npc, World *world); +function void UpdateNPC(F32 delta, NPC *npc, World *world); +function void UpdateBandit(F32 delta, Bandit *bandit, World *world); #endif // LD_GAME_WORLD_H_