2025-10-03 14:30:36 +01:00
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdbool.h>
|
|
|
|
|
#include <SDL3/SDL.h>
|
2025-10-05 16:49:18 +01:00
|
|
|
#include <SDL3/SDL_keycode.h>
|
2025-10-03 14:30:36 +01:00
|
|
|
|
2025-10-04 17:24:30 +01:00
|
|
|
#define STB_IMAGE_IMPLEMENTATION 1
|
|
|
|
|
#include <stb_image.h>
|
|
|
|
|
|
2025-10-05 21:11:18 +01:00
|
|
|
#define STB_RECT_PACK_IMPLEMENTATION 1
|
|
|
|
|
#include <stb_rect_pack.h>
|
|
|
|
|
|
|
|
|
|
#define STB_TRUETYPE_IMPLEMENTATION 1
|
|
|
|
|
#include <stb_truetype.h>
|
|
|
|
|
|
2025-10-04 00:46:26 +01:00
|
|
|
#include "core/core.h"
|
2025-10-04 14:57:09 +01:00
|
|
|
#include "core/types.h"
|
2025-10-04 00:46:26 +01:00
|
|
|
#include "os/core.h"
|
2025-10-03 20:04:40 +01:00
|
|
|
|
|
|
|
|
#include "vulkan/core.h"
|
2025-10-05 14:27:05 +01:00
|
|
|
#include "draw/core.h"
|
|
|
|
|
|
2025-10-04 17:24:30 +01:00
|
|
|
#include "game/core.h"
|
2025-10-03 20:04:40 +01:00
|
|
|
|
2025-10-05 14:42:19 +01:00
|
|
|
#include "game/impl/world.c"
|
2025-10-05 00:25:37 +01:00
|
|
|
#include "game/impl/npc.c"
|
2025-10-05 21:05:29 +01:00
|
|
|
#include "game/impl/bandit.c"
|
2025-10-06 23:08:15 +01:00
|
|
|
#include "game/impl/outfit.c"
|
2025-10-03 20:04:40 +01:00
|
|
|
|
2025-10-04 17:36:47 +01:00
|
|
|
int main(int argc, char **argv)
|
|
|
|
|
{
|
2025-10-06 01:01:28 +01:00
|
|
|
(void) argc;
|
|
|
|
|
(void) argv;
|
2025-10-04 15:53:55 +01:00
|
|
|
|
2025-10-06 00:52:16 +01:00
|
|
|
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO))
|
2025-10-04 15:53:55 +01:00
|
|
|
{
|
2025-10-03 14:30:36 +01:00
|
|
|
printf("[Error] :: Failed to initialise SDL3 (%s)\n", SDL_GetError());
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDL_Window *window = SDL_CreateWindow("Ludum", 1280, 720, SDL_WINDOW_HIGH_PIXEL_DENSITY);
|
2025-10-04 15:53:55 +01:00
|
|
|
if (!window)
|
|
|
|
|
{
|
2025-10-03 14:30:36 +01:00
|
|
|
printf("[Error] :: Failed to create window (%s)\n", SDL_GetError());
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-03 20:04:40 +01:00
|
|
|
Vk_Setup(window);
|
|
|
|
|
|
2025-10-04 21:42:04 +01:00
|
|
|
G_State *game = 0;
|
|
|
|
|
{
|
|
|
|
|
M_Arena *arena = M_ArenaAlloc(GB(64), .initial = MB(4));
|
|
|
|
|
game = M_ArenaPush(arena, G_State);
|
2025-10-04 17:24:30 +01:00
|
|
|
|
2025-10-06 00:52:16 +01:00
|
|
|
game->arena = arena;
|
2025-10-05 21:11:18 +01:00
|
|
|
game->draw.arena = arena;
|
2025-10-04 21:42:04 +01:00
|
|
|
|
|
|
|
|
G_ImagesLoad(game);
|
2025-10-06 19:32:12 +01:00
|
|
|
//D_FontLoad(&game->draw, S("ubuntu"), 60);
|
2025-10-06 17:49:08 +01:00
|
|
|
|
2025-10-04 21:42:04 +01:00
|
|
|
G_PipelinesLoad(game);
|
2025-10-06 00:52:16 +01:00
|
|
|
G_AudioLoad(game);
|
2025-10-04 21:42:04 +01:00
|
|
|
|
2025-10-06 21:54:48 +01:00
|
|
|
Audio_Context *audio = M_ArenaPush(game->arena, Audio_Context);
|
2025-10-07 14:09:59 +01:00
|
|
|
Audio_Init(game->arena, audio, 0.0f); // disabled audio
|
2025-10-06 21:54:48 +01:00
|
|
|
|
2025-10-06 22:17:41 +01:00
|
|
|
U32 bgm = Audio_Play(audio, 0);
|
|
|
|
|
U32 saloon_music = Audio_Play(audio, 1);
|
2025-10-06 21:54:48 +01:00
|
|
|
|
|
|
|
|
SDL_ResumeAudioStreamDevice(audio->stream);
|
|
|
|
|
|
2025-10-05 02:40:59 +01:00
|
|
|
G_Camera *camera = &game->camera;
|
2025-10-04 23:46:13 +01:00
|
|
|
|
2025-10-05 14:27:05 +01:00
|
|
|
camera->x = V3F(1, 0, 0);
|
|
|
|
|
camera->y = V3F(0, 1, 0);
|
|
|
|
|
camera->z = V3F(0, 0, 1);
|
2025-10-06 21:07:55 +01:00
|
|
|
camera->p = V3F(0, 0, 48);
|
2025-10-04 23:46:13 +01:00
|
|
|
|
2025-10-05 02:40:59 +01:00
|
|
|
camera->fov = 60.0f;
|
2025-10-04 21:42:04 +01:00
|
|
|
|
2025-10-05 02:40:59 +01:00
|
|
|
camera->nearp = 0.01f;
|
2025-10-04 17:36:47 +01:00
|
|
|
camera->farp = 1000.0f;
|
2025-10-04 21:42:04 +01:00
|
|
|
|
2025-10-05 14:27:05 +01:00
|
|
|
game->draw.camera = camera;
|
2025-10-06 16:35:22 +01:00
|
|
|
World *world = M_ArenaPush(arena, World);
|
2025-10-06 19:32:12 +01:00
|
|
|
LoadWorld(arena, world);
|
2025-10-05 15:21:23 +01:00
|
|
|
game->world = world;
|
2025-10-06 22:17:41 +01:00
|
|
|
|
|
|
|
|
world->audio = audio;
|
|
|
|
|
world->audio_handles[0] = bgm;
|
|
|
|
|
world->audio_handles[1] = saloon_music;
|
|
|
|
|
|
2025-10-06 01:10:44 +01:00
|
|
|
world->arena = arena;
|
2025-10-05 15:32:57 +01:00
|
|
|
world->random = Random_Seed(29237489723847);
|
2025-10-06 23:08:15 +01:00
|
|
|
world->npcCount = 12;
|
2025-10-05 21:19:14 +01:00
|
|
|
for(U32 i = 0; i < world->npcCount; i++) {
|
2025-10-06 18:23:47 +01:00
|
|
|
NPC *npc1 = &world->npcs[i];
|
|
|
|
|
npc1->collision.pos.x = 0;
|
|
|
|
|
npc1->collision.pos.y = 0;
|
2025-10-05 21:05:29 +01:00
|
|
|
npc1->collision.size.x = 1;
|
2025-10-05 22:35:34 +01:00
|
|
|
npc1->collision.size.y = 2;
|
2025-10-05 16:49:18 +01:00
|
|
|
npc1->name = S("Matt");
|
|
|
|
|
npc1->mode = NPC_ACTION_WAITING;
|
2025-10-06 18:23:47 +01:00
|
|
|
npc1->currentArea = WORLD_AREA_OUTSIDE;
|
2025-10-05 16:49:18 +01:00
|
|
|
npc1->waitTime = 0;
|
2025-10-06 21:07:55 +01:00
|
|
|
npc1->maxWaitTime = 1;
|
2025-10-05 16:49:18 +01:00
|
|
|
npc1->currentNavNode = 0;
|
2025-10-06 23:08:15 +01:00
|
|
|
GenOutfit(&npc1->outfit,world,game);
|
2025-10-05 16:49:18 +01:00
|
|
|
}
|
2025-10-05 15:21:23 +01:00
|
|
|
|
2025-10-05 21:05:29 +01:00
|
|
|
Bandit *badman = &world->bandit;
|
|
|
|
|
badman->collision.pos.x = 15;
|
|
|
|
|
badman->collision.pos.y = 15;
|
2025-10-05 23:36:50 +01:00
|
|
|
badman->collision.size.x = 1;
|
|
|
|
|
badman->collision.size.y = 2;
|
2025-10-05 21:05:29 +01:00
|
|
|
badman->name = S("Leroy Brown");
|
|
|
|
|
badman->mode = BANDIT_WAITING;
|
|
|
|
|
badman->waitTime = 0;
|
|
|
|
|
badman->maxWaitTime = 2;
|
|
|
|
|
badman->poiCount = 2;
|
2025-10-06 17:30:49 +01:00
|
|
|
badman->shootoutTimer = 1.5;
|
2025-10-06 23:08:15 +01:00
|
|
|
badman->agroRadius = 6.0;
|
2025-10-06 17:30:49 +01:00
|
|
|
badman->bullets = 6;
|
|
|
|
|
badman->shootDelay = 1;
|
|
|
|
|
badman->accuracyRange = 0.25;
|
|
|
|
|
badman->reloadTime = 2.5;
|
|
|
|
|
badman->reloadTimer = 0;
|
2025-10-05 21:05:29 +01:00
|
|
|
badman->pointsOfInterest[0] = 937;
|
|
|
|
|
badman->pointsOfInterest[1] = 12;
|
2025-10-06 23:08:15 +01:00
|
|
|
badman->outfitChoices = GenOutfit(&badman->outfit, world, game);
|
|
|
|
|
badman->currentArea = WORLD_AREA_OUTSIDE;
|
2025-10-04 17:36:47 +01:00
|
|
|
|
|
|
|
|
world->npcPOI[0] = 100;
|
2025-10-06 17:49:08 +01:00
|
|
|
|
|
|
|
|
PlayerInit(game, &world->player);
|
2025-10-06 14:26:00 +01:00
|
|
|
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes = M_ArenaPush(arena, World_Tile, .count=WORLD_TILE_TYPE_MAX);
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[0].rotation=0;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[0].tag=S("tile_dirt_0");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[1].rotation=0,
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[1].tag=S("path_middle");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[2].rotation=0;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[2].tag=S("path_middle_edge");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[3].rotation=PI_F32/2;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[3].tag=S("path_middle_edge");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[4].rotation=PI_F32;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[4].tag=S("path_middle_edge");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[5].rotation=-PI_F32/2;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[5].tag=S("path_middle_edge");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[6].rotation=0;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[6].tag=S("path_middle");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[7].rotation=PI_F32/2;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[7].tag=S("path_middle");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[8].rotation=-PI_F32;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[8].tag=S("path_middle");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[9].rotation=-PI_F32/2;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[9].tag=S("path_middle");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[10].rotation=0;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[10].tag=S("path_corner");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[11].rotation=PI_F32/2;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[11].tag=S("path_corner");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[12].rotation=-PI_F32/2;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[12].tag=S("path_corner");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[13].rotation=PI_F32;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[13].tag=S("path_corner");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->tileTypes[14].rotation=0;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->tileTypes[14].tag=S("tile_dirt_1");
|
2025-10-06 14:26:00 +01:00
|
|
|
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes = M_ArenaPush(arena, World_PropType, .count=WORLD_PROP_TYPE_MAX);
|
|
|
|
|
world->propTypes[0].tag=S("rug0");
|
2025-10-06 21:07:55 +01:00
|
|
|
world->propTypes[0].scale=3;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes[1].tag=S("rug1");
|
2025-10-06 21:07:55 +01:00
|
|
|
world->propTypes[1].scale=3;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes[2].tag=S("skull");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->propTypes[2].scale=1;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes[3].tag = S("table");
|
|
|
|
|
world->propTypes[3].scale=2;
|
|
|
|
|
world->propTypes[4].tag = S("barrel");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->propTypes[4].scale=1;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes[5].tag = S("can");
|
2025-10-06 21:07:55 +01:00
|
|
|
world->propTypes[5].scale=0.5;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes[6].tag = S("candle");
|
2025-10-06 21:07:55 +01:00
|
|
|
world->propTypes[6].scale=0.5;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes[7].tag = S("clock");
|
2025-10-06 21:07:55 +01:00
|
|
|
world->propTypes[7].scale=1.5;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes[8].tag = S("log_pile");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->propTypes[8].scale=1;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes[9].tag = S("nightstand");
|
2025-10-06 14:26:00 +01:00
|
|
|
world->propTypes[9].scale=1;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes[10].tag = S("pool_table");
|
2025-10-06 21:07:55 +01:00
|
|
|
world->propTypes[10].scale=3;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes[11].tag = S("saloon_ext");
|
2025-10-06 14:50:32 +01:00
|
|
|
world->propTypes[11].scale=6.875f;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes[12].tag = S("saloon_int");
|
|
|
|
|
world->propTypes[12].scale=2*6.875f;
|
|
|
|
|
world->propTypes[13].tag = S("house");
|
|
|
|
|
world->propTypes[13].scale=6.875f;
|
|
|
|
|
world->propTypes[14].tag = S("house_int");
|
2025-10-06 21:30:13 +01:00
|
|
|
world->propTypes[14].scale=12.875f;
|
2025-10-06 16:35:22 +01:00
|
|
|
world->propTypes[15].tag=S("tile_detail_0");
|
|
|
|
|
world->propTypes[15].scale=1;
|
|
|
|
|
world->propTypes[16].tag = S("tile_detail_1");
|
|
|
|
|
world->propTypes[16].scale=1;
|
|
|
|
|
world->propTypes[17].tag = S("tile_detail_2");
|
|
|
|
|
world->propTypes[17].scale=1;
|
|
|
|
|
world->propTypes[18].tag= S("tile_detail_3");
|
|
|
|
|
world->propTypes[18].scale=1;
|
|
|
|
|
world->propTypes[19].tag= S("tile_detail_4");
|
|
|
|
|
world->propTypes[19].scale=1;
|
|
|
|
|
world->propTypes[20].tag=S("tile_detail_5");
|
|
|
|
|
world->propTypes[20].scale=1;
|
|
|
|
|
world->propTypes[21].tag=S("tile_detail_6");
|
|
|
|
|
world->propTypes[21].scale=1;
|
|
|
|
|
world->propCount = 0;
|
|
|
|
|
world->props = M_ArenaPush(arena, World_Prop, .count=WORLD_PROP_MAX);
|
2025-10-06 21:57:06 +01:00
|
|
|
world->hitboxes = M_ArenaPush(arena, World_Hitbox, .count=WORLD_HITBOX_MAX);
|
2025-10-06 21:07:55 +01:00
|
|
|
world->portals = M_ArenaPush(arena, World_Portal, .count=64);
|
|
|
|
|
world->portalCount=0;
|
2025-10-06 18:23:47 +01:00
|
|
|
GenerateNavMesh(arena, world);
|
2025-10-05 14:27:05 +01:00
|
|
|
}
|
2025-10-06 21:07:55 +01:00
|
|
|
game->editor.enabled = true;
|
2025-10-06 14:26:00 +01:00
|
|
|
game->editor.mode = G_EDITOR_MODE_TILE;
|
2025-10-06 14:50:32 +01:00
|
|
|
game->editor.currentLevel = WORLD_AREA_OUTSIDE;
|
2025-10-06 21:07:55 +01:00
|
|
|
game->editor.selectedNode = 0;
|
2025-10-04 21:42:04 +01:00
|
|
|
|
2025-10-03 14:30:36 +01:00
|
|
|
bool running = true;
|
2025-10-05 01:31:39 +01:00
|
|
|
|
2025-10-06 01:01:28 +01:00
|
|
|
const int width = 1280;
|
|
|
|
|
const int height = 720;
|
|
|
|
|
|
2025-10-04 15:53:55 +01:00
|
|
|
while (running)
|
|
|
|
|
{
|
2025-10-03 14:30:36 +01:00
|
|
|
SDL_Event e;
|
2025-10-04 15:53:55 +01:00
|
|
|
while (SDL_PollEvent(&e))
|
|
|
|
|
{
|
|
|
|
|
if (e.type == SDL_EVENT_QUIT)
|
|
|
|
|
{
|
|
|
|
|
running = false;
|
|
|
|
|
}
|
2025-10-06 17:49:08 +01:00
|
|
|
else if (e.type == SDL_EVENT_KEY_DOWN) {
|
|
|
|
|
Player *player = &game->world->player;
|
|
|
|
|
switch (e.key.key) {
|
|
|
|
|
case SDLK_R: { PlayerInit(game, player); } break;
|
2025-10-06 23:08:15 +01:00
|
|
|
case SDLK_F: {game->world->showPoster=!game->world->showPoster;}break;
|
2025-10-06 17:49:08 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-05 22:35:34 +01:00
|
|
|
V3f projection = G_CameraUnproject(&game->camera, V2f_Clip(
|
|
|
|
|
V2F(e.button.x, e.button.y),
|
2025-10-06 01:01:28 +01:00
|
|
|
V2F((F32) width, (F32) height)
|
2025-10-05 22:35:34 +01:00
|
|
|
));
|
|
|
|
|
game->world->mouseProjected = V2F(projection.x, projection.y);
|
2025-10-06 14:26:00 +01:00
|
|
|
if(e.type==SDL_EVENT_MOUSE_MOTION) {
|
|
|
|
|
game->editor.cursor = V2F(projection.x, projection.y);
|
|
|
|
|
}
|
|
|
|
|
if (!game->editor.enabled) {
|
|
|
|
|
ProcessEvents(&e, game->world);
|
|
|
|
|
} else {
|
|
|
|
|
if(e.type==SDL_EVENT_MOUSE_BUTTON_DOWN && e.button.button == SDL_BUTTON_LEFT) {
|
|
|
|
|
switch(game->editor.mode){
|
|
|
|
|
case G_EDITOR_MODE_TILE: {
|
2025-10-06 17:49:08 +01:00
|
|
|
F32 tilex = (F32) (S32)(game->editor.cursor.x+TILE_SIZE/2);
|
|
|
|
|
F32 tiley = (F32) (S32)(game->editor.cursor.y+TILE_SIZE/2);
|
2025-10-06 14:26:00 +01:00
|
|
|
game->world->map[(S32)tilex + (S32)tiley * 96] = game->editor.currentAsset;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case G_EDITOR_MODE_PROP: {
|
|
|
|
|
game->world->props[game->world->propCount].propType = game->editor.currentAsset;
|
|
|
|
|
game->world->props[game->world->propCount].area = game->editor.currentLevel;
|
|
|
|
|
game->world->props[game->world->propCount].pos = game->editor.cursor;
|
|
|
|
|
game->world->propCount++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-10-06 14:50:32 +01:00
|
|
|
case G_EDITOR_MODE_HITBOX: {
|
2025-10-06 21:07:55 +01:00
|
|
|
case G_EDITOR_MODE_PORTAL: {
|
2025-10-06 14:50:32 +01:00
|
|
|
game->editor.dragStart = game->editor.cursor;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-10-06 14:26:00 +01:00
|
|
|
}
|
2025-10-06 21:07:55 +01:00
|
|
|
}} else if(e.type==SDL_EVENT_MOUSE_BUTTON_UP && e.button.button == SDL_BUTTON_LEFT && game->editor.mode == G_EDITOR_MODE_HITBOX) {
|
2025-10-06 17:49:08 +01:00
|
|
|
// Add hitbox
|
2025-10-06 14:50:32 +01:00
|
|
|
V2f topLeft = V2F(Min(game->editor.cursor.x, game->editor.dragStart.x), Min(game->editor.cursor.y, game->editor.dragStart.y));
|
2025-10-06 21:57:06 +01:00
|
|
|
game->world->hitboxes[game->world->hitboxCount].box.pos = topLeft;
|
|
|
|
|
game->world->hitboxes[game->world->hitboxCount].box.size = V2F(
|
2025-10-06 14:50:32 +01:00
|
|
|
Abs(game->editor.cursor.x-game->editor.dragStart.x),
|
|
|
|
|
Abs(game->editor.cursor.y-game->editor.dragStart.y)
|
|
|
|
|
);
|
2025-10-06 21:57:06 +01:00
|
|
|
game->world->hitboxes[game->world->hitboxCount].area = game->editor.currentAsset;
|
2025-10-06 14:50:32 +01:00
|
|
|
game->world->hitboxCount++;
|
2025-10-06 19:27:45 +01:00
|
|
|
GenerateNavMesh(game->arena, game->world);
|
2025-10-06 21:07:55 +01:00
|
|
|
} 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);
|
2025-10-06 14:26:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-10-06 14:50:32 +01:00
|
|
|
|
2025-10-06 14:26:00 +01:00
|
|
|
if (e.type == SDL_EVENT_KEY_DOWN) {
|
|
|
|
|
switch(e.key.key) {
|
|
|
|
|
case SDLK_F10: {
|
|
|
|
|
game->editor.enabled = !game->editor.enabled;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SDLK_RIGHT: {
|
|
|
|
|
game->editor.currentAsset = Min(game->editor.currentAsset+1, 64);
|
2025-10-06 21:07:55 +01:00
|
|
|
printf("editing with %d\n", game->editor.currentAsset);
|
2025-10-06 14:26:00 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SDLK_LEFT: {
|
|
|
|
|
game->editor.currentAsset = Max(game->editor.currentAsset-1, 0);
|
2025-10-06 21:07:55 +01:00
|
|
|
printf("editing with %d\n", game->editor.currentAsset);
|
2025-10-06 14:26:00 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SDLK_A: {
|
|
|
|
|
game->camera.p.x -= 5;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SDLK_D: {
|
|
|
|
|
game->camera.p.x += 5;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SDLK_W: {
|
|
|
|
|
game->camera.p.y -= 5;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SDLK_S: {
|
|
|
|
|
game->camera.p.y += 5;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SDLK_UP: {
|
|
|
|
|
game->editor.currentLevel++;
|
|
|
|
|
game->world->player.currentArea++;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case SDLK_DOWN: {
|
|
|
|
|
game->editor.currentLevel--;
|
|
|
|
|
game->world->player.currentArea--;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-10-06 21:07:55 +01:00
|
|
|
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;
|
|
|
|
|
}
|
2025-10-06 14:26:00 +01:00
|
|
|
case SDLK_SPACE: {
|
|
|
|
|
game->editor.mode = (game->editor.mode + 1) % 4;
|
2025-10-06 16:35:22 +01:00
|
|
|
printf("EDITOR MODE %d\n", game->editor.mode);
|
2025-10-06 14:26:00 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2025-10-06 14:50:32 +01:00
|
|
|
case SDLK_U: {
|
|
|
|
|
switch(game->editor.mode) {
|
|
|
|
|
case G_EDITOR_MODE_PROP: {game->world->propCount--;}
|
|
|
|
|
case G_EDITOR_MODE_HITBOX: {game->world->hitboxCount--;}
|
2025-10-06 21:57:06 +01:00
|
|
|
case G_EDITOR_MODE_PORTAL: {game->world->portalCount--;}
|
2025-10-06 14:50:32 +01:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-10-06 14:26:00 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-10-03 14:30:36 +01:00
|
|
|
}
|
2025-10-05 15:17:37 +01:00
|
|
|
|
2025-10-06 14:26:00 +01:00
|
|
|
if(!game->editor.enabled) {
|
2025-10-06 22:17:41 +01:00
|
|
|
UpdateWorld(1.0f / 250.0f, game->world);
|
2025-10-06 17:55:32 +01:00
|
|
|
|
2025-10-06 17:30:49 +01:00
|
|
|
game->camera.p.x = game->world->player.collision.pos.x;
|
|
|
|
|
game->camera.p.y = game->world->player.collision.pos.y;
|
2025-10-06 17:49:08 +01:00
|
|
|
}
|
2025-10-03 20:04:40 +01:00
|
|
|
|
|
|
|
|
int w, h;
|
|
|
|
|
SDL_GetWindowSizeInPixels(window, &w, &h);
|
|
|
|
|
|
2025-10-04 17:36:47 +01:00
|
|
|
game->draw.window_width = w;
|
2025-10-05 14:27:05 +01:00
|
|
|
game->draw.window_height = h;
|
|
|
|
|
|
2025-10-04 17:36:47 +01:00
|
|
|
G_CalculateCamera(&game->camera, (F32)w / (F32)h);
|
2025-10-05 02:40:59 +01:00
|
|
|
|
2025-10-03 20:04:40 +01:00
|
|
|
Vk_Frame *frame = Vk_FrameBegin(window);
|
|
|
|
|
VkCommandBuffer cmd = frame->cmd;
|
|
|
|
|
|
|
|
|
|
VkClearValue clear_colour;
|
2025-10-06 14:50:32 +01:00
|
|
|
clear_colour.color.float32[0] = 0.0f;
|
2025-10-03 20:04:40 +01:00
|
|
|
clear_colour.color.float32[1] = 0.0f;
|
|
|
|
|
clear_colour.color.float32[2] = 0.0f;
|
|
|
|
|
clear_colour.color.float32[3] = 1.0f;
|
|
|
|
|
|
2025-10-04 15:53:55 +01:00
|
|
|
VkRenderingAttachmentInfo colour_attachment = {0};
|
2025-10-04 17:36:47 +01:00
|
|
|
colour_attachment.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO;
|
|
|
|
|
colour_attachment.imageView = vk.swapchain.views[frame->image];
|
2025-10-03 20:04:40 +01:00
|
|
|
colour_attachment.imageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
2025-10-04 17:36:47 +01:00
|
|
|
colour_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
|
|
|
colour_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
|
|
|
colour_attachment.clearValue = clear_colour;
|
2025-10-04 15:53:55 +01:00
|
|
|
|
|
|
|
|
VkRenderingInfo rendering_info = {0};
|
2025-10-04 17:36:47 +01:00
|
|
|
rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO;
|
|
|
|
|
rendering_info.renderArea = (VkRect2D){0, 0, w, h};
|
|
|
|
|
rendering_info.layerCount = 1;
|
2025-10-03 20:04:40 +01:00
|
|
|
rendering_info.colorAttachmentCount = 1;
|
2025-10-04 17:36:47 +01:00
|
|
|
rendering_info.pColorAttachments = &colour_attachment;
|
2025-10-03 20:04:40 +01:00
|
|
|
|
|
|
|
|
vk.CmdBeginRendering(cmd, &rendering_info);
|
|
|
|
|
|
2025-10-05 14:27:05 +01:00
|
|
|
D_Begin(&game->draw, frame, D_MAX_RECTS);
|
2025-10-04 21:42:04 +01:00
|
|
|
|
2025-10-07 14:14:51 +01:00
|
|
|
|
2025-10-06 14:26:00 +01:00
|
|
|
RenderWorld(game->world, &game->draw);
|
2025-10-06 00:52:16 +01:00
|
|
|
|
2025-10-07 14:14:51 +01:00
|
|
|
R3f bounds = G_CameraBounds(&game->camera);
|
|
|
|
|
|
|
|
|
|
if (game->world->showPoster) {
|
|
|
|
|
D_Rect(&game->draw, bounds.min.x + 4.8f, bounds.min.y + 6.4f, .texture = D_ImageHandle(&game->draw, S("poster")), .scale = 12.0f);
|
2025-10-06 23:08:15 +01:00
|
|
|
}
|
2025-10-07 14:14:51 +01:00
|
|
|
|
|
|
|
|
for (U32 i = 0; i < game->world->player.health; i++) {
|
|
|
|
|
D_Rect(&game->draw, (bounds.max.x - 3) + i, bounds.min.y + 1, .texture = D_ImageHandle(&game->draw, S("heart")));
|
2025-10-06 23:08:15 +01:00
|
|
|
}
|
2025-10-06 00:52:16 +01:00
|
|
|
|
2025-10-06 19:32:12 +01:00
|
|
|
//D_Text(&game->draw, game->draw.fonts, S("Small Test"), 0, 0);
|
2025-10-04 21:42:04 +01:00
|
|
|
|
2025-10-06 14:26:00 +01:00
|
|
|
if(game->editor.enabled) {
|
|
|
|
|
G_Editor editor = game->editor;
|
2025-10-06 17:49:08 +01:00
|
|
|
F32 tilex = cast(F32) floor(editor.cursor.x+TILE_SIZE/2);
|
|
|
|
|
F32 tiley = cast(F32) floor(editor.cursor.y+TILE_SIZE/2);
|
2025-10-07 14:09:59 +01:00
|
|
|
for (U32 i = 0; i < game->world->navMesh->nodeCount; i++) {
|
2025-10-06 21:07:55 +01:00
|
|
|
NavNode n = game->world->navMesh->nodes[i];
|
|
|
|
|
D_Rect(
|
|
|
|
|
&game->draw,
|
|
|
|
|
n.pos.x,
|
|
|
|
|
n.pos.y,
|
|
|
|
|
.texture = 0,
|
|
|
|
|
.scale = 0.2f,
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-10-07 14:09:59 +01:00
|
|
|
for (U32 i = 0; i < game->world->navMesh->nodeCount; i++) {
|
2025-10-06 21:07:55 +01:00
|
|
|
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),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-06 14:26:00 +01:00
|
|
|
switch(game->editor.mode) {
|
|
|
|
|
case G_EDITOR_MODE_TILE: {
|
|
|
|
|
World_Tile asset = game->world->tileTypes[editor.currentAsset];
|
2025-10-06 16:35:22 +01:00
|
|
|
D_Rect(&game->draw, tilex, tiley, .texture=D_ImageHandle(&game->draw, asset.tag), .angle=asset.rotation);
|
2025-10-06 14:26:00 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case G_EDITOR_MODE_PROP: {
|
|
|
|
|
World_PropType prop = game->world->propTypes[editor.currentAsset];
|
2025-10-06 16:35:22 +01:00
|
|
|
D_Rect(&game->draw, editor.cursor.x, editor.cursor.y, .texture=D_ImageHandle(&game->draw, prop.tag), .scale=prop.scale);
|
2025-10-06 14:26:00 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case G_EDITOR_MODE_HITBOX: {
|
2025-10-06 17:49:08 +01:00
|
|
|
for(U32 i = 0; i < game->world->hitboxCount; i++) {
|
2025-10-06 21:57:06 +01:00
|
|
|
if(game->world->player.currentArea == game->world->hitboxes[i].area) {
|
|
|
|
|
V2f centre = AABB_Centre(game->world->hitboxes[i].box);
|
|
|
|
|
D_Rect(
|
|
|
|
|
&game->draw,
|
|
|
|
|
centre.x,
|
|
|
|
|
centre.y,
|
|
|
|
|
.texture=0,
|
|
|
|
|
.dim=game->world->hitboxes[i].box.size,
|
|
|
|
|
.flags=D_RECT_IGNORE_ASPECT,
|
|
|
|
|
.c=V4F(100,0,0,0.7f),
|
|
|
|
|
);
|
|
|
|
|
}
|
2025-10-06 14:50:32 +01:00
|
|
|
}
|
2025-10-06 14:26:00 +01:00
|
|
|
break;
|
|
|
|
|
}
|
2025-10-06 21:07:55 +01:00
|
|
|
case G_EDITOR_MODE_PORTAL: {
|
|
|
|
|
for(U32 i = 0; i < game->world->portalCount; i++) {
|
|
|
|
|
V2f centre = AABB_Centre(game->world->portals[i].box);
|
2025-10-06 16:35:22 +01:00
|
|
|
D_Rect(
|
|
|
|
|
&game->draw,
|
|
|
|
|
centre.x,
|
|
|
|
|
centre.y,
|
|
|
|
|
.texture=0,
|
2025-10-06 21:07:55 +01:00
|
|
|
.dim=game->world->portals[i].box.size,
|
2025-10-06 16:35:22 +01:00
|
|
|
.flags=D_RECT_IGNORE_ASPECT,
|
2025-10-07 14:09:59 +01:00
|
|
|
.c=V4F(0,0,100,0.7f),
|
2025-10-06 16:35:22 +01:00
|
|
|
);
|
2025-10-06 14:50:32 +01:00
|
|
|
}
|
2025-10-06 14:26:00 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-10-05 14:27:05 +01:00
|
|
|
D_End(&game->draw, frame);
|
2025-10-04 21:42:04 +01:00
|
|
|
|
2025-10-03 20:04:40 +01:00
|
|
|
vk.CmdEndRendering(cmd);
|
|
|
|
|
|
|
|
|
|
Vk_FrameEnd();
|
2025-10-03 14:30:36 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SDL_DestroyWindow(window);
|
|
|
|
|
SDL_Quit();
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2025-10-03 20:04:40 +01:00
|
|
|
|
2025-10-04 00:46:26 +01:00
|
|
|
#include "core/core.c"
|
|
|
|
|
#include "os/core.c"
|
2025-10-03 20:04:40 +01:00
|
|
|
#include "vulkan/core.c"
|
2025-10-05 14:27:05 +01:00
|
|
|
#include "draw/core.c"
|
2025-10-04 17:24:30 +01:00
|
|
|
#include "game/core.c"
|