feat: Added portals

This commit is contained in:
2025-10-06 21:07:55 +01:00
parent be49003a8d
commit 680d375b8c
9 changed files with 158 additions and 33 deletions

View File

@@ -88,7 +88,7 @@ int main(int argc, char **argv)
camera->x = V3F(1, 0, 0); camera->x = V3F(1, 0, 0);
camera->y = V3F(0, 1, 0); camera->y = V3F(0, 1, 0);
camera->z = V3F(0, 0, 1); camera->z = V3F(0, 0, 1);
camera->p = V3F(0, 0, 16); camera->p = V3F(0, 0, 48);
camera->fov = 60.0f; camera->fov = 60.0f;
@@ -112,7 +112,7 @@ int main(int argc, char **argv)
npc1->mode = NPC_ACTION_WAITING; npc1->mode = NPC_ACTION_WAITING;
npc1->currentArea = WORLD_AREA_OUTSIDE; npc1->currentArea = WORLD_AREA_OUTSIDE;
npc1->waitTime = 0; npc1->waitTime = 0;
npc1->maxWaitTime = 5; npc1->maxWaitTime = 1;
npc1->currentNavNode = 0; 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 = M_ArenaPush(arena, World_PropType, .count=WORLD_PROP_TYPE_MAX);
world->propTypes[0].tag=S("rug0"); 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].tag=S("rug1");
world->propTypes[1].scale=1; world->propTypes[1].scale=3;
world->propTypes[2].tag=S("skull"); world->propTypes[2].tag=S("skull");
world->propTypes[2].scale=1; world->propTypes[2].scale=1;
world->propTypes[3].tag = S("table"); 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].tag = S("barrel");
world->propTypes[4].scale=1; world->propTypes[4].scale=1;
world->propTypes[5].tag = S("can"); 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].tag = S("candle");
world->propTypes[6].scale=1; world->propTypes[6].scale=0.5;
world->propTypes[7].tag = S("clock"); 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].tag = S("log_pile");
world->propTypes[8].scale=1; world->propTypes[8].scale=1;
world->propTypes[9].tag = S("nightstand"); world->propTypes[9].tag = S("nightstand");
world->propTypes[9].scale=1; world->propTypes[9].scale=1;
world->propTypes[10].tag = S("pool_table"); 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].tag = S("saloon_ext");
world->propTypes[11].scale=6.875f; world->propTypes[11].scale=6.875f;
world->propTypes[12].tag = S("saloon_int"); world->propTypes[12].tag = S("saloon_int");
@@ -220,11 +220,14 @@ int main(int argc, char **argv)
world->propCount = 0; world->propCount = 0;
world->props = M_ArenaPush(arena, World_Prop, .count=WORLD_PROP_MAX); world->props = M_ArenaPush(arena, World_Prop, .count=WORLD_PROP_MAX);
world->hitboxes = M_ArenaPush(arena, AABB, .count=WORLD_HITBOX_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); GenerateNavMesh(arena, world);
} }
game->editor.enabled = false; game->editor.enabled = true;
game->editor.mode = G_EDITOR_MODE_TILE; game->editor.mode = G_EDITOR_MODE_TILE;
game->editor.currentLevel = WORLD_AREA_OUTSIDE; game->editor.currentLevel = WORLD_AREA_OUTSIDE;
game->editor.selectedNode = 0;
bool running = true; bool running = true;
@@ -274,11 +277,12 @@ int main(int argc, char **argv)
break; break;
} }
case G_EDITOR_MODE_HITBOX: { case G_EDITOR_MODE_HITBOX: {
case G_EDITOR_MODE_PORTAL: {
game->editor.dragStart = game->editor.cursor; game->editor.dragStart = game->editor.cursor;
break; 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 // Add hitbox
V2f topLeft = V2F(Min(game->editor.cursor.x, game->editor.dragStart.x), Min(game->editor.cursor.y, game->editor.dragStart.y)); 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; game->world->hitboxes[game->world->hitboxCount].pos = topLeft;
@@ -288,6 +292,17 @@ int main(int argc, char **argv)
); );
game->world->hitboxCount++; game->world->hitboxCount++;
GenerateNavMesh(game->arena, game->world); 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: { case SDLK_RIGHT: {
game->editor.currentAsset = Min(game->editor.currentAsset+1, 64); game->editor.currentAsset = Min(game->editor.currentAsset+1, 64);
printf("editing with %d\n", game->editor.currentAsset);
break; break;
} }
case SDLK_LEFT: { case SDLK_LEFT: {
game->editor.currentAsset = Max(game->editor.currentAsset-1, 0); game->editor.currentAsset = Max(game->editor.currentAsset-1, 0);
printf("editing with %d\n", game->editor.currentAsset);
break; break;
} }
case SDLK_A: { case SDLK_A: {
@@ -331,6 +348,16 @@ int main(int argc, char **argv)
game->world->player.currentArea--; game->world->player.currentArea--;
break; 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: { case SDLK_SPACE: {
game->editor.mode = (game->editor.mode + 1) % 4; game->editor.mode = (game->editor.mode + 1) % 4;
printf("EDITOR MODE %d\n", game->editor.mode); printf("EDITOR MODE %d\n", game->editor.mode);
@@ -398,6 +425,39 @@ int main(int argc, char **argv)
G_Editor editor = game->editor; G_Editor editor = game->editor;
F32 tilex = cast(F32) floor(editor.cursor.x+TILE_SIZE/2); F32 tilex = cast(F32) floor(editor.cursor.x+TILE_SIZE/2);
F32 tiley = cast(F32) floor(editor.cursor.y+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) { switch(game->editor.mode) {
case G_EDITOR_MODE_TILE: { case G_EDITOR_MODE_TILE: {
World_Tile asset = game->world->tileTypes[editor.currentAsset]; World_Tile asset = game->world->tileTypes[editor.currentAsset];
@@ -424,7 +484,19 @@ int main(int argc, char **argv)
} }
break; 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; break;
} }
} }

View File

@@ -21,7 +21,7 @@ enum G_EDITOR_MODE {
G_EDITOR_MODE_TILE, G_EDITOR_MODE_TILE,
G_EDITOR_MODE_PROP, G_EDITOR_MODE_PROP,
G_EDITOR_MODE_HITBOX, G_EDITOR_MODE_HITBOX,
G_EDITOR_MODE_POI, G_EDITOR_MODE_PORTAL,
}; };
typedef struct G_Editor G_Editor; typedef struct G_Editor G_Editor;
@@ -32,6 +32,7 @@ struct G_Editor {
G_EDITOR_MODE mode; G_EDITOR_MODE mode;
V2f cursor; V2f cursor;
V2f dragStart; V2f dragStart;
U32 selectedNode;
}; };
typedef struct G_State G_State; typedef struct G_State G_State;

View File

@@ -11,7 +11,7 @@ bool AABB_Collide(AABB a, AABB b)
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.y + a.size.y >= v.y && a.pos.y <= v.y;
return collision_x && collision_y; return collision_x && collision_y;
} }

View File

@@ -10,6 +10,11 @@ V2f shootTowards(Bandit *bandit, V2f target, Random* r)
} }
void UpdateBandit(F32 delta, Bandit *bandit, World *world) { 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 ( if (
world->player.controls.shot && AABB_Slab(world->player.collision.pos, world->player.shotPos, bandit->collision) && bandit->currentArea == world->player.currentArea) world->player.controls.shot && AABB_Slab(world->player.collision.pos, world->player.shotPos, bandit->collision) && bandit->currentArea == world->player.currentArea)
{ {

View File

@@ -4,6 +4,11 @@
#include "core/math.h" #include "core/math.h"
void UpdateNPC(F32 delta, NPC *npc, World *world) { 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) { switch (npc->mode) {
case NPC_ACTION_WAITING: case NPC_ACTION_WAITING:
npc->waitTime+=delta; npc->waitTime+=delta;

View File

@@ -165,6 +165,11 @@ void PlayerUpdate(F32 delta, Player *player) {
dir = V2f_Scale(NormaliseV2F(dir), PLAYER_SPEED*delta); dir = V2f_Scale(NormaliseV2F(dir), PLAYER_SPEED*delta);
player->collision.pos.x += dir.x; player->collision.pos.x += dir.x;
player->collision.pos.y += dir.y; 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) { void PlayerDraw(D_Context *draw, Player *player) {

View File

@@ -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++) { for(U32 i = 0; i < world->npcCount; i++) {
NPC npc = world->npcs[i]; NPC npc = world->npcs[i];
if(npc.currentArea == world->player.currentArea) { 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); FS_FileWrite(file, world->map, sizeof(U32)*WORLD_MAP_MAX, offset);
offset += sizeof(U32)*WORLD_MAP_MAX; 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); FS_FileClose(file);
printf("Saved world :)\n"); 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); FS_FileRead(file, world->map, sizeof(U32)*WORLD_MAP_MAX, offset);
offset += sizeof(U32)*WORLD_MAP_MAX; 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); FS_FileClose(file);
printf("loaded world\n"); printf("loaded world\n");
} }
@@ -195,14 +196,10 @@ void GenerateNavMesh(M_Arena *arena, World *world) {
if(skip) {continue;} if(skip) {continue;}
world->navMesh->nodes[world->navMesh->nodeCount].pos.x = x; world->navMesh->nodes[world->navMesh->nodeCount].pos.x = x;
world->navMesh->nodes[world->navMesh->nodeCount].pos.y = y; 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; world->navMesh->nodes[world->navMesh->nodeCount].connectionCount = 0;
for(int nx = -1; nx < 2; nx++){ for(int nx = -1; nx < 2; nx++){
for(int ny = -1; ny < 2; ny++){ 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) { if(x+nx < 0 || x+nx > 95) {
continue; continue;
} }
@@ -216,14 +213,42 @@ void GenerateNavMesh(M_Arena *arena, World *world) {
break; break;
} }
} }
if(skip) {continue;}
U32 index = x+nx + (y+ny)*96;
U32 nCount = world->navMesh->nodeCount; 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].NodeIndex = index;
world->navMesh->nodes[nCount].connections[world->navMesh->nodes[nCount].connectionCount].Cost = cost; 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[nCount].connectionCount++;
world->navMesh->nodes[index].connectionCount++;
} }
} }
world->navMesh->nodeCount++; 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;
}
} }

View File

@@ -2,19 +2,21 @@
#define LD_GAME_WORLD_H_ #define LD_GAME_WORLD_H_
#include "../core/math.h" #include "../core/math.h"
#include "./aabb.h"
#define WORLD_HITBOX_MAX 4096 #define WORLD_HITBOX_MAX 4096
#define WORLD_PROP_MAX 2048 #define WORLD_PROP_MAX 2048
#define WORLD_PROP_TYPE_MAX 64 #define WORLD_PROP_TYPE_MAX 64
#define WORLD_TILE_TYPE_MAX 64 #define WORLD_TILE_TYPE_MAX 64
#define WORLD_MAP_MAX 4800 #define WORLD_MAP_MAX 4800
#define WORLD_PORTAL_MAX 64
// Areas are which // Areas are which
typedef U32 World_Area; typedef U32 World_Area;
enum World_Area enum World_Area
{ {
WORLD_AREA_OUTSIDE = (1 << 0), WORLD_AREA_OUTSIDE = 1,
WORLD_AREA_SALOON = (1 << 1), WORLD_AREA_SALOON = 2,
}; };
typedef struct World_Tile World_Tile; typedef struct World_Tile World_Tile;
@@ -37,6 +39,13 @@ struct World_Prop
V2f pos; V2f pos;
}; };
typedef struct World_Portal World_Portal;
struct World_Portal
{
AABB box;
World_Area area;
};
typedef struct World World; typedef struct World World;
#include "player.h" #include "player.h"
#include "npc.h" #include "npc.h"
@@ -54,11 +63,14 @@ struct World {
World_PropType *propTypes; World_PropType *propTypes;
World_Prop *props; World_Prop *props;
AABB *hitboxes; AABB *hitboxes;
World_Portal *portals;
U32 *map; U32 *map;
U32 propCount; U32 propCount;
U32 portalCount;
U32 hitboxCount; U32 hitboxCount;
//// Player //// Player
Player player; Player player;

Binary file not shown.