From 680d375b8cfdd05263e58b6bfcca322fa09db509 Mon Sep 17 00:00:00 2001 From: Matt Date: Mon, 6 Oct 2025 21:07:55 +0100 Subject: [PATCH] feat: Added portals --- code/first.c | 94 +++++++++++++++++++++++++++++++++++----- code/game/core.h | 3 +- code/game/impl/aabb.c | 4 +- code/game/impl/bandit.c | 5 +++ code/game/impl/npc.c | 5 +++ code/game/impl/player.c | 5 +++ code/game/impl/world.c | 59 +++++++++++++++++-------- code/game/world.h | 16 ++++++- code/world.sgdat | Bin 120960 -> 120960 bytes 9 files changed, 158 insertions(+), 33 deletions(-) diff --git a/code/first.c b/code/first.c index cbdc4b4..2bae5da 100644 --- a/code/first.c +++ b/code/first.c @@ -88,7 +88,7 @@ int main(int argc, char **argv) camera->x = V3F(1, 0, 0); camera->y = V3F(0, 1, 0); camera->z = V3F(0, 0, 1); - camera->p = V3F(0, 0, 16); + camera->p = V3F(0, 0, 48); camera->fov = 60.0f; @@ -112,7 +112,7 @@ int main(int argc, char **argv) npc1->mode = NPC_ACTION_WAITING; npc1->currentArea = WORLD_AREA_OUTSIDE; npc1->waitTime = 0; - npc1->maxWaitTime = 5; + npc1->maxWaitTime = 1; npc1->currentNavNode = 0; } @@ -174,9 +174,9 @@ int main(int argc, char **argv) world->propTypes = M_ArenaPush(arena, World_PropType, .count=WORLD_PROP_TYPE_MAX); world->propTypes[0].tag=S("rug0"); - world->propTypes[0].scale=1; + world->propTypes[0].scale=3; world->propTypes[1].tag=S("rug1"); - world->propTypes[1].scale=1; + world->propTypes[1].scale=3; world->propTypes[2].tag=S("skull"); world->propTypes[2].scale=1; world->propTypes[3].tag = S("table"); @@ -184,17 +184,17 @@ int main(int argc, char **argv) world->propTypes[4].tag = S("barrel"); world->propTypes[4].scale=1; world->propTypes[5].tag = S("can"); - world->propTypes[5].scale=1; + world->propTypes[5].scale=0.5; world->propTypes[6].tag = S("candle"); - world->propTypes[6].scale=1; + world->propTypes[6].scale=0.5; world->propTypes[7].tag = S("clock"); - world->propTypes[7].scale=1; + world->propTypes[7].scale=1.5; world->propTypes[8].tag = S("log_pile"); world->propTypes[8].scale=1; world->propTypes[9].tag = S("nightstand"); world->propTypes[9].scale=1; world->propTypes[10].tag = S("pool_table"); - world->propTypes[10].scale=2; + world->propTypes[10].scale=3; world->propTypes[11].tag = S("saloon_ext"); world->propTypes[11].scale=6.875f; world->propTypes[12].tag = S("saloon_int"); @@ -220,11 +220,14 @@ int main(int argc, char **argv) world->propCount = 0; world->props = M_ArenaPush(arena, World_Prop, .count=WORLD_PROP_MAX); world->hitboxes = M_ArenaPush(arena, AABB, .count=WORLD_HITBOX_MAX); + world->portals = M_ArenaPush(arena, World_Portal, .count=64); + world->portalCount=0; GenerateNavMesh(arena, world); } - game->editor.enabled = false; + game->editor.enabled = true; game->editor.mode = G_EDITOR_MODE_TILE; game->editor.currentLevel = WORLD_AREA_OUTSIDE; + game->editor.selectedNode = 0; bool running = true; @@ -274,11 +277,12 @@ int main(int argc, char **argv) break; } case G_EDITOR_MODE_HITBOX: { + case G_EDITOR_MODE_PORTAL: { game->editor.dragStart = game->editor.cursor; break; } } - } else if(e.type==SDL_EVENT_MOUSE_BUTTON_UP && e.button.button == SDL_BUTTON_LEFT && game->editor.mode == G_EDITOR_MODE_HITBOX) { + }} else if(e.type==SDL_EVENT_MOUSE_BUTTON_UP && e.button.button == SDL_BUTTON_LEFT && game->editor.mode == G_EDITOR_MODE_HITBOX) { // Add hitbox V2f topLeft = V2F(Min(game->editor.cursor.x, game->editor.dragStart.x), Min(game->editor.cursor.y, game->editor.dragStart.y)); game->world->hitboxes[game->world->hitboxCount].pos = topLeft; @@ -288,6 +292,17 @@ int main(int argc, char **argv) ); game->world->hitboxCount++; GenerateNavMesh(game->arena, game->world); + } else if(e.type==SDL_EVENT_MOUSE_BUTTON_UP && e.button.button == SDL_BUTTON_LEFT && game->editor.mode == G_EDITOR_MODE_PORTAL) { + // Add portal + V2f topLeft = V2F(Min(game->editor.cursor.x, game->editor.dragStart.x), Min(game->editor.cursor.y, game->editor.dragStart.y)); + game->world->portals[game->world->portalCount].box.pos = topLeft; + game->world->portals[game->world->portalCount].box.size = V2F( + Abs(game->editor.cursor.x-game->editor.dragStart.x), + Abs(game->editor.cursor.y-game->editor.dragStart.y) + ); + game->world->portals[game->world->portalCount].area = game->editor.currentAsset; + game->world->portalCount++; + GenerateNavMesh(game->arena, game->world); } } @@ -299,10 +314,12 @@ int main(int argc, char **argv) } case SDLK_RIGHT: { game->editor.currentAsset = Min(game->editor.currentAsset+1, 64); + printf("editing with %d\n", game->editor.currentAsset); break; } case SDLK_LEFT: { game->editor.currentAsset = Max(game->editor.currentAsset-1, 0); + printf("editing with %d\n", game->editor.currentAsset); break; } case SDLK_A: { @@ -331,6 +348,16 @@ int main(int argc, char **argv) game->world->player.currentArea--; break; } + case SDLK_E: { + game->editor.selectedNode++; + printf("selected %d\n", game->editor.selectedNode); + break; + } + case SDLK_Q: { + game->editor.selectedNode--; + printf("selected %d\n", game->editor.selectedNode); + break; + } case SDLK_SPACE: { game->editor.mode = (game->editor.mode + 1) % 4; printf("EDITOR MODE %d\n", game->editor.mode); @@ -398,6 +425,39 @@ int main(int argc, char **argv) G_Editor editor = game->editor; F32 tilex = cast(F32) floor(editor.cursor.x+TILE_SIZE/2); F32 tiley = cast(F32) floor(editor.cursor.y+TILE_SIZE/2); + for (int i = 0; i < game->world->navMesh->nodeCount; i++) { + NavNode n = game->world->navMesh->nodes[i]; + D_Rect( + &game->draw, + n.pos.x, + n.pos.y, + .texture = 0, + .scale = 0.2f, + ); + } + for (int i = 0; i < game->world->navMesh->nodeCount; i++) { + NavNode n = game->world->navMesh->nodes[i]; + if(i == editor.selectedNode) { + D_Rect( + &game->draw, + n.pos.x, + n.pos.y, + .texture = 0, + .scale = 0.2f, + .c = V4F(100, 255, 0, 100), + ); + for(int j = 0; j < n.connectionCount; j++) { + D_Rect( + &game->draw, + game->world->navMesh->nodes[n.connections[j].NodeIndex].pos.x, + game->world->navMesh->nodes[n.connections[j].NodeIndex].pos.y, + .texture = 0, + .scale = 0.2f, + .c = V4F(0, 100, 0, 100), + ); + } + } + } switch(game->editor.mode) { case G_EDITOR_MODE_TILE: { World_Tile asset = game->world->tileTypes[editor.currentAsset]; @@ -424,7 +484,19 @@ int main(int argc, char **argv) } break; } - case G_EDITOR_MODE_POI: { + case G_EDITOR_MODE_PORTAL: { + for(U32 i = 0; i < game->world->portalCount; i++) { + V2f centre = AABB_Centre(game->world->portals[i].box); + D_Rect( + &game->draw, + centre.x, + centre.y, + .texture=0, + .dim=game->world->portals[i].box.size, + .flags=D_RECT_IGNORE_ASPECT, + .c=V4F(0,0,100,0.7), + ); + } break; } } diff --git a/code/game/core.h b/code/game/core.h index 5b918ce..efa3448 100644 --- a/code/game/core.h +++ b/code/game/core.h @@ -21,7 +21,7 @@ enum G_EDITOR_MODE { G_EDITOR_MODE_TILE, G_EDITOR_MODE_PROP, G_EDITOR_MODE_HITBOX, - G_EDITOR_MODE_POI, + G_EDITOR_MODE_PORTAL, }; typedef struct G_Editor G_Editor; @@ -32,6 +32,7 @@ struct G_Editor { G_EDITOR_MODE mode; V2f cursor; V2f dragStart; + U32 selectedNode; }; typedef struct G_State G_State; diff --git a/code/game/impl/aabb.c b/code/game/impl/aabb.c index b7d475d..40c6e46 100644 --- a/code/game/impl/aabb.c +++ b/code/game/impl/aabb.c @@ -11,7 +11,7 @@ bool AABB_Collide(AABB a, AABB b) 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; + bool collision_y = a.pos.y + a.size.y >= v.y && a.pos.y <= v.y; return collision_x && collision_y; } @@ -45,4 +45,4 @@ bool AABB_Circle(F32 rad, V2f radOrigin, AABB a) F32 xSq = (Abs(aCentre.x) - Abs(radOrigin.x)) * (Abs(aCentre.x) - Abs(radOrigin.x)); F32 ySq = (Abs(aCentre.y) - Abs(radOrigin.y)) * (Abs(aCentre.y) - Abs(radOrigin.y)); return SDL_sqrt(xSq + ySq) < rad; -} \ No newline at end of file +} diff --git a/code/game/impl/bandit.c b/code/game/impl/bandit.c index d24f62c..f66af50 100644 --- a/code/game/impl/bandit.c +++ b/code/game/impl/bandit.c @@ -10,6 +10,11 @@ V2f shootTowards(Bandit *bandit, V2f target, Random* r) } void UpdateBandit(F32 delta, Bandit *bandit, World *world) { + for(int i = 0; i < world->portalCount; i++) { + if(AABB_Collide(world->portals[0].box, bandit->collision)) { + bandit->currentArea = world->portals[0].area; + } + } if ( world->player.controls.shot && AABB_Slab(world->player.collision.pos, world->player.shotPos, bandit->collision) && bandit->currentArea == world->player.currentArea) { diff --git a/code/game/impl/npc.c b/code/game/impl/npc.c index 3ea5599..7be61d0 100644 --- a/code/game/impl/npc.c +++ b/code/game/impl/npc.c @@ -4,6 +4,11 @@ #include "core/math.h" void UpdateNPC(F32 delta, NPC *npc, World *world) { + for(int i = 0; i < world->portalCount; i++) { + if(AABB_Collide(world->portals[0].box, npc->collision)) { + npc->currentArea = world->portals[0].area; + } + } 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 4ebdfc3..6585f3f 100644 --- a/code/game/impl/player.c +++ b/code/game/impl/player.c @@ -165,6 +165,11 @@ void PlayerUpdate(F32 delta, Player *player) { dir = V2f_Scale(NormaliseV2F(dir), PLAYER_SPEED*delta); player->collision.pos.x += dir.x; player->collision.pos.y += dir.y; + for(int i = 0; i < player->world->portalCount; i++) { + if(AABB_Collide(player->world->portals[0].box, player->collision)) { + player->currentArea = player->world->portals[0].area; + } + } } void PlayerDraw(D_Context *draw, Player *player) { diff --git a/code/game/impl/world.c b/code/game/impl/world.c index 2b96290..0f01f33 100644 --- a/code/game/impl/world.c +++ b/code/game/impl/world.c @@ -64,16 +64,6 @@ void RenderWorld(World *world, D_Context *draw) { ); } } - for (int i = 0; i < world->navMesh->nodeCount; i++) { - NavNode n = world->navMesh->nodes[i]; - D_Rect( - draw, - n.pos.x, - n.pos.y, - .texture = 0, - .scale = 0.2f, - ); - } for(U32 i = 0; i < world->npcCount; i++) { NPC npc = world->npcs[i]; if(npc.currentArea == world->player.currentArea) { @@ -127,6 +117,11 @@ void SaveWorld(M_Arena *arena, World *world) { FS_FileWrite(file, world->map, sizeof(U32)*WORLD_MAP_MAX, offset); offset += sizeof(U32)*WORLD_MAP_MAX; + FS_FileWrite(file, &world->portalCount, sizeof(U32), offset); + offset += sizeof(U32); + + FS_FileWrite(file, world->hitboxes, sizeof(World_Portal)*WORLD_PORTAL_MAX, offset); + FS_FileClose(file); printf("Saved world :)\n"); } @@ -175,6 +170,12 @@ void LoadWorld(M_Arena *arena, World *world) { FS_FileRead(file, world->map, sizeof(U32)*WORLD_MAP_MAX, offset); offset += sizeof(U32)*WORLD_MAP_MAX; + FS_FileRead(file, &world->portalCount, sizeof(U32), offset); + offset += sizeof(U32); + + world->portals = M_ArenaPush(arena, World_Portal, .count=WORLD_PORTAL_MAX); + FS_FileRead(file, world->hitboxes, sizeof(World_Portal)*WORLD_PORTAL_MAX, offset); + FS_FileClose(file); printf("loaded world\n"); } @@ -195,14 +196,10 @@ void GenerateNavMesh(M_Arena *arena, World *world) { if(skip) {continue;} world->navMesh->nodes[world->navMesh->nodeCount].pos.x = x; world->navMesh->nodes[world->navMesh->nodeCount].pos.y = y; - U32 cost = 20; - if(Str8_Equal(world->tileTypes[world->map[i]].tag, S("path_middle"), STR8_EQUAL_IGNORE_CASE)) { - cost = 10; - } world->navMesh->nodes[world->navMesh->nodeCount].connectionCount = 0; for(int nx = -1; nx < 2; nx++){ for(int ny = -1; ny < 2; ny++){ - if((nx==ny) && nx==0) {continue;} + if(nx==0 && ny==0) {continue;} if(x+nx < 0 || x+nx > 95) { continue; } @@ -216,14 +213,42 @@ void GenerateNavMesh(M_Arena *arena, World *world) { break; } } - if(skip) {continue;} - U32 index = x+nx + (y+ny)*96; U32 nCount = world->navMesh->nodeCount; + S32 index = -1; + for(int ohgod = 0; ohgod < nCount; ohgod++) { + if(world->navMesh->nodes[ohgod].pos.x == nx+x&& world->navMesh->nodes[ohgod].pos.y == y+ny) { + index=ohgod; + break; + } + } + F32 cost = 20; + if(ny+nx == 2) { + cost = 40; + }; + if(Str8_Equal(world->tileTypes[world->map[i]].tag, S("path_middle"), STR8_EQUAL_IGNORE_CASE)) { + cost = 1; + if(Abs(ny+nx) == 2) { + cost = 4; + }; + } + if(index < 0) {continue;} + skip |= index > nCount; + if(skip) {continue;} world->navMesh->nodes[nCount].connections[world->navMesh->nodes[nCount].connectionCount].NodeIndex = index; world->navMesh->nodes[nCount].connections[world->navMesh->nodes[nCount].connectionCount].Cost = cost; + world->navMesh->nodes[index].connections[world->navMesh->nodes[index].connectionCount].NodeIndex = nCount; + world->navMesh->nodes[index].connections[world->navMesh->nodes[index].connectionCount].Cost = cost; world->navMesh->nodes[nCount].connectionCount++; + world->navMesh->nodes[index].connectionCount++; } } world->navMesh->nodeCount++; } + for(int i = 0; i < world->npcCount; i++) { + world->npcs[i].mode = NPC_ACTION_WAITING; + world->npcs[i].maxWaitTime = Random_F32(&world->random, 20, 140); + world->npcs[i].waitTime = 0; + world->npcs[i].currentNavNode=0; + world->npcs[i].pathIndex = 0; + } } diff --git a/code/game/world.h b/code/game/world.h index e86c95d..03e5ba7 100644 --- a/code/game/world.h +++ b/code/game/world.h @@ -2,19 +2,21 @@ #define LD_GAME_WORLD_H_ #include "../core/math.h" +#include "./aabb.h" #define WORLD_HITBOX_MAX 4096 #define WORLD_PROP_MAX 2048 #define WORLD_PROP_TYPE_MAX 64 #define WORLD_TILE_TYPE_MAX 64 #define WORLD_MAP_MAX 4800 +#define WORLD_PORTAL_MAX 64 // Areas are which typedef U32 World_Area; enum World_Area { - WORLD_AREA_OUTSIDE = (1 << 0), - WORLD_AREA_SALOON = (1 << 1), + WORLD_AREA_OUTSIDE = 1, + WORLD_AREA_SALOON = 2, }; typedef struct World_Tile World_Tile; @@ -37,6 +39,13 @@ struct World_Prop V2f pos; }; +typedef struct World_Portal World_Portal; +struct World_Portal +{ + AABB box; + World_Area area; +}; + typedef struct World World; #include "player.h" #include "npc.h" @@ -54,11 +63,14 @@ struct World { World_PropType *propTypes; World_Prop *props; AABB *hitboxes; + World_Portal *portals; U32 *map; U32 propCount; + U32 portalCount; U32 hitboxCount; + //// Player Player player; diff --git a/code/world.sgdat b/code/world.sgdat index b02130a630b497ef065c279f72151fa4db8f7565..5788440ab0fd31cd515a47837ea2518f790e8265 100644 GIT binary patch delta 103 zcmZqJ$lkD#eZz5PAqNK*1_&rBO*dpli5*)!JZ8&oSc}Kl9LML*t5b!a`Kb2 n85j;swqvnpbZD+)*