Compare commits

13 Commits

Author SHA1 Message Date
635e7b1c1e feat: Initial bandit roaming 2025-10-05 21:05:29 +01:00
b8f47b8f61 feat: added npc poi for custom npcs 2025-10-05 18:28:58 +01:00
e20e537e97 feat: Player controller 2025-10-05 18:05:01 +01:00
98095c907f chore: removed nav printf 2025-10-05 16:53:56 +01:00
8dc0ac1d3e Merge remote-tracking branch 'origin' 2025-10-05 16:51:59 +01:00
222575b318 feat: People go walkies 2025-10-05 16:49:18 +01:00
9a46a802e0 add ignore 2025-10-05 16:12:23 +01:00
628a6c5ade raycast fin 2025-10-05 16:12:23 +01:00
c8dfcd857e raycast start 2025-10-05 16:11:47 +01:00
394366480b feat: Random npc walkabouts 2025-10-05 15:32:57 +01:00
8f148b6894 Merge remote-tracking branch 'origin' 2025-10-05 15:22:09 +01:00
7306c8fba4 chore: struct reorg 2025-10-05 15:21:23 +01:00
5cabf845b6 Added random number generator
Fixed typo in calculate camera
Removed some warning
2025-10-05 15:17:37 +01:00
18 changed files with 397 additions and 118 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
build/ build/
code/compile_commands.json code/compile_commands.json
code/.cache code/.cache
.vscode

View File

@@ -1,8 +1,22 @@
#include <math.h>
V2f V2F(F32 x, F32 y) { V2f V2F(F32 x, F32 y) {
V2f result = { x, y }; V2f result = { x, y };
return result; 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 V3F(F32 x, F32 y, F32 z) {
V3f result = { x, y, z }; V3f result = { x, y, z };
return result; return result;
@@ -160,4 +174,46 @@ Mat4x4FInv M4x4F_CameraView(V3f x, V3f y, V3f z, V3f p) {
return result; return result;
} }
// Random
Random Random_Seed(U64 seed) {
Random result = { seed };
return result;
}
U64 Random_Next(Random *rnd) {
U64 result = rnd->state;
result ^= (result << 13);
result ^= (result >> 7);
result ^= (result << 17);
rnd->state = result;
return result;
}
F32 Random_F32(Random *rnd, F32 min, F32 max) {
F32 result = min + (Random_Unilateral(rnd) * (max - min));
return result;
}
F64 Random_F64(Random *rnd, F64 min, F64 max) {
F64 result = min + (Random_Unilateral(rnd) * (max - min));
return result;
}
U32 Random_U32(Random *rnd, U32 min, U32 max) {
U32 result = min + (U32) (Random_Unilateral(rnd) * (max - min));
return result;
}
F32 Random_Unilateral(Random *rnd) {
F32 result = Random_Next(rnd) / (F32) U64_MAX;
return result;
}
F32 Random_Bilateral(Random *rnd) {
F32 result = -1.0f + (2.0f * Random_Unilateral(rnd));
return result;
}

View File

@@ -4,6 +4,13 @@
#define PI_F32 (3.14159265358979323846264338f) #define PI_F32 (3.14159265358979323846264338f)
#define TAU_F32 (2.0f * PI_F32) #define TAU_F32 (2.0f * PI_F32)
#define Abs(x) (((x) < 0 ? -(x) : (x)))
typedef struct Random Random;
struct Random {
U64 state;
};
typedef union V2f V2f; typedef union V2f V2f;
union V2f { union V2f {
struct { struct {
@@ -114,4 +121,21 @@ function V3f M4x4F_VMul3(Mat4x4F m, V3f v);
function Mat4x4FInv M4x4F_Perspective(F32 fov, F32 aspect, F32 nearp, F32 farp); 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 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);
function U64 Random_Next(Random *rnd);
function F32 Random_F32(Random *rnd, F32 min, F32 max);
function F64 Random_F64(Random *rnd, F64 min, F64 max);
function U32 Random_U32(Random *rnd, U32 min, U32 max);
function F32 Random_Unilateral(Random *rnd);
function F32 Random_Bilateral(Random *rnd);
#endif // LD_CORE_MATH_H_ #endif // LD_CORE_MATH_H_

View File

@@ -1,7 +1,7 @@
#if !defined(LD_DRAW_CORE_H_) #if !defined(LD_DRAW_CORE_H_)
#define LD_DRAW_CORE_H_ #define LD_DRAW_CORE_H_
#define D_MAX_RECTS 1024 #define D_MAX_RECTS (262144)
typedef struct D_Image D_Image; typedef struct D_Image D_Image;
struct D_Image { struct D_Image {

View File

@@ -1,6 +1,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <SDL3/SDL_keycode.h>
#define STB_IMAGE_IMPLEMENTATION 1 #define STB_IMAGE_IMPLEMENTATION 1
#include <stb_image.h> #include <stb_image.h>
@@ -17,9 +18,11 @@
#include "game/impl/world.c" #include "game/impl/world.c"
#include "game/impl/npc.c" #include "game/impl/npc.c"
#include "game/impl/bandit.c"
#include "game/testnavmesh.h" #include "game/testnavmesh.h"
int main(int argc, char **argv) { int main(int argc, char **argv)
{
(void)argc; (void)argc;
(void)argv; (void)argv;
@@ -61,39 +64,49 @@ int main(int argc, char **argv) {
camera->farp = 1000.0f; camera->farp = 1000.0f;
game->draw.camera = camera; game->draw.camera = camera;
World *world = M_ArenaPush(arena, World);
game->world = world;
world->random = Random_Seed(29237489723847);
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 = 1;
npc1->collision.size.y = 1;
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;
} }
Vk_Buffer rbo = { 0 }; Bandit *badman = &world->bandit;
rbo.usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; badman->collision.pos.x = 15;
rbo.size = KB(4); badman->collision.pos.y = 15;
rbo.host_visible = true; 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;
Vk_BufferCreate(&rbo); 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; 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)); printf("%zu size in bytes\n", sizeof(TestNavMesh));
@@ -106,9 +119,11 @@ int main(int argc, char **argv) {
{ {
running = false; running = false;
} }
ProcessEvents(&e, &world); ProcessEvents(&e, game->world);
} }
UpdateWorld(1.0/60.0, &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; int w, h;
SDL_GetWindowSizeInPixels(window, &w, &h); SDL_GetWindowSizeInPixels(window, &w, &h);
@@ -116,7 +131,7 @@ int main(int argc, char **argv) {
game->draw.window_width = w; game->draw.window_width = w;
game->draw.window_height = h; game->draw.window_height = h;
G_CalulateCamera(&game->camera, (F32) w / (F32) h); G_CalculateCamera(&game->camera, (F32)w / (F32)h);
Vk_Frame *frame = Vk_FrameBegin(window); Vk_Frame *frame = Vk_FrameBegin(window);
VkCommandBuffer cmd = frame->cmd; VkCommandBuffer cmd = frame->cmd;
@@ -146,6 +161,7 @@ int main(int argc, char **argv) {
D_Begin(&game->draw, frame, D_MAX_RECTS); 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, 0.0f, 0.0f, .texture = 1);
D_Rect(&game->draw, -8.0f, 0.0f, .texture = 2, .scale = 2.0f); 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);

View File

@@ -3,15 +3,15 @@
#include "../core/types.h" #include "../core/types.h"
#include "../core/macros.h" #include "../core/macros.h"
typedef struct AABB AABB; typedef struct AABB AABB;
struct AABB { struct AABB
{
V2f pos; V2f pos;
V2f size; V2f size;
}; };
function bool AABB_Collide(AABB a, AABB b); function bool AABB_Collide(AABB a, AABB b);
function bool AABB_Point(AABB a, V2f v); function bool AABB_Point(AABB a, V2f v);
function bool AABB_Slab(V2f origin, V2f point, AABB a);
#endif // LD_GAME_AABB_H_ #endif // LD_GAME_AABB_H_

View File

@@ -6,7 +6,7 @@ enum BANDIT_ACTION {
BANDIT_WAITING, BANDIT_WAITING,
BANDIT_WALKING, BANDIT_WALKING,
BANDIT_RUNNING, BANDIT_RUNNING,
BANDIT_SHOOTOUT, BANDIT_SHOOTING,
}; };
typedef struct Bandit Bandit; typedef struct Bandit Bandit;
@@ -22,6 +22,7 @@ struct Bandit {
// How long they will wait in this location. // How long they will wait in this location.
F32 maxWaitTime; F32 maxWaitTime;
U32 poiCount;
// Places the bandit walks to / from // Places the bandit walks to / from
// E.g. hide outs, home, saloon // E.g. hide outs, home, saloon
U32 pointsOfInterest[12]; U32 pointsOfInterest[12];

View File

@@ -290,7 +290,7 @@ void G_PipelinesLoad(G_State *game) {
Vk_PipelineCreate(basic); Vk_PipelineCreate(basic);
} }
void G_CalulateCamera(G_Camera *camera, F32 aspect) { void G_CalculateCamera(G_Camera *camera, F32 aspect) {
Mat4x4FInv proj = M4x4F_Perspective(camera->fov, aspect, camera->nearp, camera->farp); Mat4x4FInv proj = M4x4F_Perspective(camera->fov, aspect, camera->nearp, camera->farp);
Mat4x4FInv view = M4x4F_CameraView(camera->x, camera->y, camera->z, camera->p); Mat4x4FInv view = M4x4F_CameraView(camera->x, camera->y, camera->z, camera->p);

View File

@@ -1,5 +1,6 @@
#if !defined(LD_GAME_CORE_H_) #if !defined(LD_GAME_CORE_H_)
#define LD_GAME_CORE_H_ #define LD_GAME_CORE_H_
#include "world.h"
typedef struct G_Camera G_Camera; typedef struct G_Camera G_Camera;
struct G_Camera { struct G_Camera {
@@ -18,17 +19,18 @@ struct G_State {
D_Context draw; D_Context draw;
G_Camera camera; G_Camera camera;
World *world;
}; };
function void G_ImagesLoad(G_State *game); function void G_ImagesLoad(G_State *game);
function void G_PipelinesLoad(G_State *game); function void G_PipelinesLoad(G_State *game);
function void G_CalulateCamera(G_Camera *camera, F32 aspect); function void G_CalculateCamera(G_Camera *camera, F32 aspect);
#include "aabb.h" #include "aabb.h"
#include "player.h" #include "player.h"
#include "nav.h" #include "nav.h"
#include "npc.h" #include "npc.h"
#include "world.h"
#endif // LD_GAME_CORE_H_ #endif // LD_GAME_CORE_H_

View File

@@ -1,15 +1,36 @@
#include "game/aabb.h" #include "game/aabb.h"
#include "core/types.h" #include <math.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_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; 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; 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_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; bool collision_y = a.pos.x + a.size.y >= v.y && a.pos.y <= v.y;
return collision_x && collision_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 invdirection = {1 / (origin.x - point.x), 1 / (origin.y - point.y)};
// 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.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 >= tMin;
}

41
code/game/impl/bandit.c Normal file
View File

@@ -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
}
}

View File

@@ -6,21 +6,25 @@
#define MAX_UNFINISHED 128 #define MAX_UNFINISHED 128
typedef struct navSearchNodeState navSearchNodeState; typedef struct navSearchNodeState navSearchNodeState;
struct navSearchNodeState{ struct navSearchNodeState
{
bool visited; bool visited;
U64 distance; F64 distance;
U32 shortest; U32 shortest;
bool addedToUnvisited; bool addedToUnvisited;
}; };
typedef struct navSearchState navSearchState; typedef struct navSearchState navSearchState;
struct navSearchState{ struct navSearchState
{
navSearchNodeState nodeStates[NAV_MAX_NODES]; navSearchNodeState nodeStates[NAV_MAX_NODES];
}; };
navSearchState initState(U32 start, U32 meshSize) { navSearchState initState(U32 start, U32 meshSize)
navSearchState state = {}; {
for(U32 i = 0; i < meshSize; i++) { navSearchState state;
for (U32 i = 0; i < meshSize; i++)
{
state.nodeStates[i].visited = false; state.nodeStates[i].visited = false;
state.nodeStates[i].addedToUnvisited = false; state.nodeStates[i].addedToUnvisited = false;
state.nodeStates[i].distance = U64_MAX; state.nodeStates[i].distance = U64_MAX;
@@ -30,20 +34,25 @@ navSearchState initState(U32 start, U32 meshSize) {
return state; return state;
} }
U32 getLowestState(U32 unfinishedIndexes[128], U32 unfinishedCount, navSearchState state, U32 *offset) { U32 getLowestState(U32 unfinishedIndexes[128], U32 unfinishedCount, navSearchState state, U32 *offset)
{
U32 lowest = U32_MAX; U32 lowest = U32_MAX;
U32 lowestI = U32_MAX; U32 lowestI = U32_MAX;
bool startFound = false; bool startFound = false;
for(U32 i = *offset; i < unfinishedCount; i++) { for (U32 i = *offset; i < unfinishedCount; i++)
{
navSearchNodeState checkNode = state.nodeStates[unfinishedIndexes[i]]; navSearchNodeState checkNode = state.nodeStates[unfinishedIndexes[i]];
if(checkNode.visited) { if (checkNode.visited)
if(!startFound) { {
if (!startFound)
{
*offset = i; *offset = i;
} }
continue; continue;
} }
startFound = true; startFound = true;
if (lowest > checkNode.distance) { if (lowest > checkNode.distance)
{
lowest = cast(U32) checkNode.distance; lowest = cast(U32) checkNode.distance;
lowestI = unfinishedIndexes[i]; lowestI = unfinishedIndexes[i];
} }
@@ -53,6 +62,12 @@ U32 getLowestState(U32 unfinishedIndexes[128], U32 unfinishedCount, navSearchSta
// Generate a path to follow between the start and end node. // 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) {
// 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); navSearchState state = initState(start, mesh->nodeCount);
U32 unfinishedCount = 1; U32 unfinishedCount = 1;
U32 unfinishedIndexes[NAV_MAX_NODES] = {start}; U32 unfinishedIndexes[NAV_MAX_NODES] = {start};
@@ -68,14 +83,15 @@ NavPath Nav_Path(NavMesh *mesh, U32 start, U32 end) {
NavConnection *connection = &mesh->nodes[lowestNodeIndex].connections[connectionI]; NavConnection *connection = &mesh->nodes[lowestNodeIndex].connections[connectionI];
navSearchNodeState *testNode = &state.nodeStates[connection->NodeIndex]; navSearchNodeState *testNode = &state.nodeStates[connection->NodeIndex];
if(testNode->visited) {continue;} if(testNode->visited) {continue;}
U32 distance = cast(U32) (state.nodeStates[lowestNodeIndex].distance + connection->Cost); F64 distance = cast(F64) (state.nodeStates[lowestNodeIndex].distance + connection->Cost);
distance += cast(U32) (mesh->nodes[end].pos.x - mesh->nodes[connection->NodeIndex].pos.x); distance += cast(F64) Abs((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); distance += cast(F64) Abs((mesh->nodes[end].pos.y - mesh->nodes[connection->NodeIndex].pos.y));
if(testNode->distance > distance) { if(testNode->distance > distance) {
testNode->distance = distance; testNode->distance = distance;
testNode->shortest = lowestNodeIndex; testNode->shortest = lowestNodeIndex;
} }
if(!testNode->addedToUnvisited) { if (!testNode->addedToUnvisited)
{
unfinishedIndexes[unfinishedCount] = connection->NodeIndex; unfinishedIndexes[unfinishedCount] = connection->NodeIndex;
unfinishedCount++; unfinishedCount++;
testNode->addedToUnvisited = true; testNode->addedToUnvisited = true;
@@ -83,13 +99,15 @@ NavPath Nav_Path(NavMesh *mesh, U32 start, U32 end) {
} }
state.nodeStates[lowestNodeIndex].visited = true; state.nodeStates[lowestNodeIndex].visited = true;
lowestNodeIndex = getLowestState(unfinishedIndexes, unfinishedCount, state, &unfinishedOffset); lowestNodeIndex = getLowestState(unfinishedIndexes, unfinishedCount, state, &unfinishedOffset);
if(lowestNodeIndex == end) { if (lowestNodeIndex == end)
{
found = true; found = true;
} }
} }
NavPath res_path = {0}; NavPath res_path = {0};
U32 index = end; U32 index = end;
while(index!=start) { while (index != start)
{
res_path.indexes[res_path.nodeCount] = index; res_path.indexes[res_path.nodeCount] = index;
res_path.nodeCount++; res_path.nodeCount++;
index = state.nodeStates[index].shortest; index = state.nodeStates[index].shortest;

View File

@@ -1,22 +1,22 @@
#include "game/npc.h" #include "game/npc.h"
#include "game/world.h" #include "game/world.h"
#include "core/types.h" #include "core/types.h"
#include "core/math.h"
#include <stdio.h> void UpdateNPC(F32 delta, NPC *npc, World *world) {
void updateNPC(F32 delta, NPC *npc, World *world) {
switch (npc->mode) { switch (npc->mode) {
case NPC_ACTION_WAITING: case NPC_ACTION_WAITING:
npc->waitTime+=delta; npc->waitTime+=delta;
if(npc->waitTime > npc->maxWaitTime) { if(npc->waitTime > npc->maxWaitTime) {
npc->mode = NPC_ACTION_WALKING; npc->mode = NPC_ACTION_WALKING;
U32 next = npc->targetNavNode == 100 ? 20 : 100; // TODO change targets to poi's rather than just random nodes
npc->targetNavNode = next; // TODO RANDOM // TODO choose either global POI's or use NPC custom poi if
printf("Starting to nav path\n"); // customPOI is true
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->path = Nav_Path(world->navMesh, npc->currentNavNode, npc->targetNavNode);
printf("done\n");
npc->walkTimer = 0; npc->walkTimer = 0;
printf("%.*s started walking to %d\n", Sv(npc->name), npc->targetNavNode);
} }
break; break;
case NPC_ACTION_WALKING: case NPC_ACTION_WALKING:
@@ -24,18 +24,18 @@ void updateNPC(F32 delta, NPC *npc, World *world) {
if(npc->walkTimer >= NPC_SPEED){ if(npc->walkTimer >= NPC_SPEED){
npc->walkTimer = 0; npc->walkTimer = 0;
if(npc->path.nodeCount == npc->pathIndex+1){ if(npc->path.nodeCount == npc->pathIndex+1){
printf("Finished! so I'm waiting\n");
npc->mode = NPC_ACTION_WAITING; npc->mode = NPC_ACTION_WAITING;
npc->maxWaitTime = 20; // TODO RANDOM npc->maxWaitTime = Random_F32(&world->random, 20, 140);
npc->waitTime = 0; npc->waitTime = 0;
npc->currentNavNode = npc->targetNavNode;
npc->pathIndex = 0; npc->pathIndex = 0;
return; return;
} }
npc->pathIndex+=1;
npc->currentNavNode = npc->path.indexes[npc->pathIndex]; npc->currentNavNode = npc->path.indexes[npc->pathIndex];
npc->pathIndex+=1;
} }
NavNode cNav = world->navMesh->nodes[npc->currentNavNode]; 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.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; npc->collision.pos.y = cNav.pos.y * (1 - npc->walkTimer/NPC_SPEED) + tNav.pos.y * npc->walkTimer/NPC_SPEED;
break; break;

View File

@@ -1,26 +1,78 @@
#include "../player.h" #include "../player.h"
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_keycode.h> #include <SDL3/SDL_keycode.h>
#include <stdio.h> #include <stdio.h>
void PlayerUpdate(SDL_Event *event, Player *player) void PlayerInput(SDL_Event *event, Player *player)
{ {
player->controls.shot = false;
SDL_KeyboardEvent key = event->key; SDL_KeyboardEvent key = event->key;
switch(key.key) { SDL_MouseButtonEvent mouseBtn = event->button;
case SDLK_W: { if(event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_KEY_UP) {
player->pos.y += 10; bool val = event->type == SDL_EVENT_KEY_DOWN;
switch (key.key)
{
case SDLK_W:
{
player->controls.upDown = val;
break; break;
} }
case SDLK_A: { case SDLK_A:
player->pos.x -= 10; {
player->controls.leftDown = val;
break; break;
} }
case SDLK_D: { case SDLK_D:
player->pos.x += 10; {
player->controls.rightDown = val;
break; break;
} }
case SDLK_S: { case SDLK_S:
player->pos.y -= 10; {
player->controls.downDown = val;
break; break;
} }
} }
} }
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) {
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;
}
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;
}

View File

@@ -3,16 +3,36 @@
#include "../player.h" #include "../player.h"
#include <SDL3/SDL_events.h> #include <SDL3/SDL_events.h>
void UpdateWorld(F32 delta, World *world) { void UpdateWorld(F32 delta, World *world)
{
UpdateBandit(delta, &world->bandit, world);
UpdateNPCs(delta, world); UpdateNPCs(delta, world);
PlayerUpdate(delta, &world->player);
} }
void UpdateNPCs(F32 delta, World *world) { void UpdateNPCs(F32 delta, World *world)
{
for (U32 i = 0; i < world->npcCount; i++)
{
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));
}
}
}
void ProcessEvents(SDL_Event *event, World *world)
{
PlayerInput(event, &world->player);
}
void RenderWorld(World *world, D_Context *draw) {
for(int i = 0; i < world->npcCount; i++) { for(int i = 0; i < world->npcCount; i++) {
updateNPC(delta, &world->npcs[i], world); 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);
void ProcessEvents(SDL_Event *event, World *world) {
PlayerUpdate(event, &world->player);
} }

View File

@@ -5,7 +5,7 @@
#include "npc_look.h" #include "npc_look.h"
#include "../core/types.h" #include "../core/types.h"
#define NPC_SPEED 0.2f #define NPC_SPEED 1.0f
typedef enum NPC_ACTION NPC_ACTION; typedef enum NPC_ACTION NPC_ACTION;
enum NPC_ACTION { enum NPC_ACTION {
@@ -23,6 +23,10 @@ struct NPC {
Str8 name; Str8 name;
NPC_LOOK look; NPC_LOOK look;
bool customPOI;
U32 customPOICount;
U32 npcPOI[16];
//// Actions //// Actions
NPC_ACTION mode; NPC_ACTION mode;
// How long they've been waiting // How long they've been waiting

View File

@@ -6,12 +6,31 @@
#include <SDL3/SDL_events.h> #include <SDL3/SDL_events.h>
#define PLAYER_SPEED 10.0f
#define PLAYER_RELOAD_TIME 1.5f
#define PLAYER_BULLET_COUNT 6
typedef struct ControlState ControlState;
struct ControlState {
bool rightDown;
bool leftDown;
bool upDown;
bool downDown;
bool shot;
};
typedef struct Player Player; typedef struct Player Player;
struct Player struct Player
{ {
V2f pos; V2f pos;
U32 bulletsLoaded;
ControlState controls;
V2f shotPos;
F32 reloadTimer;
}; };
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_ #endif // LD_GAME_PLAYER_H_

View File

@@ -4,6 +4,7 @@
#include "player.h" #include "player.h"
#include "npc.h" #include "npc.h"
#include "bandit.h" #include "bandit.h"
#include "../core/math.h"
// Areas are which // Areas are which
typedef U32 World_Area; typedef U32 World_Area;
@@ -16,6 +17,7 @@ typedef struct World World;
struct World { struct World {
//// Static stuff //// Static stuff
NavMesh *navMesh; NavMesh *navMesh;
Random random;
//// Player //// Player
Player player; Player player;
@@ -33,8 +35,10 @@ struct World {
}; };
function void UpdateWorld(F32 delta, World *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 ProcessEvents(SDL_Event *event, World *world);
function void UpdateNPCs(F32 delta, 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_ #endif // LD_GAME_WORLD_H_