Compare commits

...
Author SHA1 Message Date
bulmanator 874b2b59f7 Merge branch 'main' of yibble.dev:bulmanator/ld58 2025-10-07 14:14:51 +01:00
bulmanator dbff261805 Merge branch 'main' of yibble.dev:bulmanator/ld58 2025-10-07 14:09:59 +01:00
matt 8363560fd2 fix: make it compile 2025-10-06 23:13:46 +01:00
matt 31863b0d31 fix: import 2025-10-06 23:09:53 +01:00
declan 7f77d7ad52 npc and bandit dressup, bits of UI added, WIP NPC interaction 2025-10-06 23:08:15 +01:00
bulmanator df2c02a6a9 VERY hacky positional audio 2025-10-06 22:17:41 +01:00
matt f18d9d2b0e feat: made hitboxes area dependent 2025-10-06 21:57:06 +01:00
bulmanator 59f643b72c Added audio playback
Playing music
2025-10-06 21:54:48 +01:00
matt c01a6be4e5 feat: Initial prop placement 2025-10-06 21:35:32 +01:00
matt f93b543924 chore: fix \n 2025-10-06 21:30:13 +01:00
matt 72a86ddb41 Merge remote-tracking branch 'origin' 2025-10-06 21:19:44 +01:00
matt 680d375b8c feat: Added portals 2025-10-06 21:07:55 +01:00
bulmanator 87d3c9087e Merge branch 'debug'
Fixed some warnings
2025-10-06 19:52:28 +01:00
bulmanator 0c33944721 Merge branch 'debug/world_change' of yibble.dev:bulmanator/ld58 into debug 2025-10-06 19:38:14 +01:00
bulmanator c1fb705f68 Fixed level saving and loading 2025-10-06 19:36:41 +01:00
matt be49003a8d Merge remote-tracking branch 'origin' 2025-10-06 19:32:12 +01:00
matt 7b7f088116 fix: save/load 2025-10-06 19:27:45 +01:00
bulmanator cb10bd10b6 Merge branch 'main' of yibble.dev:bulmanator/ld58 2025-10-06 17:55:32 +01:00
bulmanator a6577f520b Fixed ???
Some files have been renamed
2025-10-06 17:49:08 +01:00
declan d54b4df4c2 player health 2025-10-06 17:45:58 +01:00
declan fcc7adfb22 added badman shooting and reloading 2025-10-06 17:30:49 +01:00
79 changed files with 985 additions and 234 deletions

No files matched your search

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 204 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 483 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 243 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 319 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 298 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 560 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 337 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file not shown.

After

Width:  |  Height:  |  Size: 636 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 639 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 328 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 259 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.
+5
View File
@@ -47,6 +47,11 @@ V3f V3f_Sub(V3f a, V3f b) {
return result; return result;
} }
F32 V2f_Dot(V2f a, V2f b) {
F32 result = (a.x * b.x) + (a.y * b.y);
return result;
}
F32 V3f_Dot(V3f a, V3f b) { F32 V3f_Dot(V3f a, V3f b) {
F32 result = (a.x * b.x) + (a.y * b.y) + (a.z * b.z); F32 result = (a.x * b.x) + (a.y * b.y) + (a.z * b.z);
return result; return result;
+1
View File
@@ -118,6 +118,7 @@ function V3f V3f_Neg(V3f x);
function V3f V3f_Scale(V3f x, F32 s); function V3f V3f_Scale(V3f x, F32 s);
function V3f V3f_Sub(V3f a, V3f b); function V3f V3f_Sub(V3f a, V3f b);
function F32 V2f_Dot(V2f a, V2f b);
function F32 V3f_Dot(V3f a, V3f b); function F32 V3f_Dot(V3f a, V3f b);
function F32 V4f_Dot(V4f a, V4f b); function F32 V4f_Dot(V4f a, V4f b);
+1
View File
@@ -3,6 +3,7 @@
#define S(x) Str8_Wrap(sizeof(x) - sizeof(*(x)), (U8 *) (x)) #define S(x) Str8_Wrap(sizeof(x) - sizeof(*(x)), (U8 *) (x))
#define Sz(x) Str8_WrapZ((U8 *) (x)) #define Sz(x) Str8_WrapZ((U8 *) (x))
#define Sl(x) { sizeof(x) - sizeof(*(x)), (U8 *) (x) }
#define Sv(x) (int) (x).count, (x).data #define Sv(x) (int) (x).count, (x).data
#define Sf(arena, fmt, ...) Str8_Format(arena, fmt, ##__VA_ARGS__) #define Sf(arena, fmt, ...) Str8_Format(arena, fmt, ##__VA_ARGS__)
+44 -41
View File
@@ -151,6 +151,8 @@ void _D_Rect(D_Context *draw, D_RectOpts *opts) {
rect->texture = opts->texture; rect->texture = opts->texture;
F32 w, h;
rect->x = opts->p.x; rect->x = opts->p.x;
rect->y = opts->p.y; rect->y = opts->p.y;
@@ -177,11 +179,19 @@ void _D_Rect(D_Context *draw, D_RectOpts *opts) {
} }
if (opts->flags & D_RECT_IGNORE_ASPECT) { if (opts->flags & D_RECT_IGNORE_ASPECT) {
rect->w = opts->dim.w; w = opts->dim.w;
rect->h = opts->dim.h; h = opts->dim.h;
} }
else { else {
Vk_Image *image = &draw->images[opts->texture].image; Vk_Image *image = 0;
if (opts->texture >= draw->n_images) {
// @hack: first font texture
//
image = &draw->fonts->image;
}
else {
image = &draw->images[opts->texture].image;
}
F32 width = cast(F32) image->width; F32 width = cast(F32) image->width;
F32 height = cast(F32) image->height; F32 height = cast(F32) image->height;
@@ -191,53 +201,42 @@ void _D_Rect(D_Context *draw, D_RectOpts *opts) {
height *= (opts->uv.max.y - opts->uv.min.y); height *= (opts->uv.max.y - opts->uv.min.y);
} }
F32 aspect_w = (width > height) ? (width / height) : 1.0f; F32 aspect_w = (width > height) ? 1.0f : (width / height);
F32 aspect_h = (width > height) ? 1.0f : (height / width); F32 aspect_h = (width > height) ? (height / width) : 1.0f;
rect->w = opts->scale * aspect_w; w = opts->scale * aspect_w;
rect->h = opts->scale * aspect_h; h = opts->scale * aspect_h;
} }
if (opts->flags & D_RECT_FLIP_X) { w *= -1.0f; }
if (opts->flags & D_RECT_FLIP_Y) { h *= -1.0f; }
rect->w = w;
rect->h = h;
draw->n_rects += 1; draw->n_rects += 1;
} }
} }
void D_Text(D_Context *draw, D_Font *font, Str8 text, F32 x, F32 y) { void D_Text(D_Context *draw, D_Font *font, Str8 text, F32 x, F32 y) {
F32 xoff = x; F32 xoff = x;
local_persist B32 done = 0;
// @Todo: control scale
F32 scale = 1.0f / (font->ascent - font->descent);
for (S64 it = 0; it < text.count; ++it) { for (S64 it = 0; it < text.count; ++it) {
if (text.data[it] >= ' ' && text.data[it] <= '~') { if (text.data[it] >= ' ' && text.data[it] <= '~') {
D_Glyph *glyph = &font->glyphs[text.data[it] - ' ']; D_Glyph *glyph = &font->glyphs[text.data[it] - ' '];
V2f size; F32 image_scale = scale * Max(glyph->dim.w, glyph->dim.h);
size.w = (glyph->box.max.x - glyph->box.min.x); F32 advance = scale * glyph->advance;
size.h = (glyph->box.max.y - glyph->box.min.y);
V2f dim; V2f offset = V2f_Scale(glyph->offset, scale);
F32 scale = 150.0f / (font->ascent - font->descent); D_Rect(draw, xoff + offset.x, y + offset.y, .texture = font->id, .uv = glyph->box, .scale = image_scale, .flags = D_RECT_UV_ASPECT);
xoff += advance;
if (size.w > size.h) {
dim.w = scale * (size.w / size.h);
dim.h = scale;
}
else {
dim.w = scale;
dim.h = scale * (size.h / size.w);
}
// @Hardcode: need font index in font struct
D_Rect(draw, xoff, y, .texture = 0, .c = V4F(0, 1, 1, 1), .dim = dim, .flags = D_RECT_IGNORE_ASPECT);
D_Rect(draw, xoff, y, .texture = draw->n_images, .uv = glyph->box, .dim = dim, .flags = D_RECT_IGNORE_ASPECT);
xoff += (1.05f * dim.w); // glyph->advance; //+ glyph->offset.x;
if (!done) { printf(" %f (%f, %f)", xoff, dim.w, dim.h); }
} }
} }
if (!done) { printf("\n"); }
done = true;
} }
void D_FontLoad(D_Context *draw, Str8 name, F32 size) { void D_FontLoad(D_Context *draw, Str8 name, F32 size) {
@@ -252,7 +251,7 @@ void D_FontLoad(D_Context *draw, Str8 name, F32 size) {
stbtt_fontinfo info = { 0 }; stbtt_fontinfo info = { 0 };
stbtt_InitFont(&info, font_data.data, 0); stbtt_InitFont(&info, font_data.data, 0);
F32 scale = stbtt_ScaleForPixelHeight(&info, 20); F32 scale = stbtt_ScaleForPixelHeight(&info, 256);
U32 w = 512; U32 w = 512;
U32 h = 512; U32 h = 512;
@@ -268,14 +267,12 @@ void D_FontLoad(D_Context *draw, Str8 name, F32 size) {
D_Font *font = M_ArenaPush(draw->arena, D_Font); D_Font *font = M_ArenaPush(draw->arena, D_Font);
font->px = size;
S32 asc, desc, line; S32 asc, desc, line;
stbtt_GetFontVMetrics(&info, &asc, &desc, &line); stbtt_GetFontVMetrics(&info, &asc, &desc, &line);
font->line_advance = cast(F32) line; font->line_advance = cast(F32) (scale * line);
font->ascent = cast(F32) asc; font->ascent = cast(F32) (scale * asc);
font->descent = cast(F32) desc; font->descent = cast(F32) (scale * desc);
font->glyphs = M_ArenaPush(draw->arena, D_Glyph, .count = count); font->glyphs = M_ArenaPush(draw->arena, D_Glyph, .count = count);
@@ -286,10 +283,14 @@ void D_FontLoad(D_Context *draw, Str8 name, F32 size) {
S32 left, width; S32 left, width;
stbtt_GetCodepointHMetrics(&info, ' ' + it, &width, &left); stbtt_GetCodepointHMetrics(&info, ' ' + it, &width, &left);
glyph->box = R2F(V2F(chr->x0 / (F32) w, chr->y0 / (F32) h), V2F(chr->x1 / (F32) w, chr->y1 / (F32) h)); glyph->box.min = V2F(chr->x0 / (F32) w, chr->y0 / (F32) h);
glyph->box.max = V2F(chr->x1 / (F32) w, chr->y1 / (F32) h);
glyph->advance = glyph->box.max.x - glyph->box.min.x; //chr->xadvance * scale; glyph->advance = chr->xadvance;
glyph->offset = V2F(chr->xoff * scale, chr->yoff * scale); glyph->dim.w = cast(F32) (chr->x1 - chr->x0);
glyph->dim.h = cast(F32) (chr->y1 - chr->y0);
glyph->offset.x = (0.5f * glyph->dim.w) + chr->xoff;
glyph->offset.y = (0.5f * glyph->dim.h) + chr->yoff;
} }
Vk_Buffer *staging = &draw->staging; Vk_Buffer *staging = &draw->staging;
@@ -302,6 +303,8 @@ void D_FontLoad(D_Context *draw, Str8 name, F32 size) {
Vk_CommandBuffer *cmds = Vk_CommandBufferPush(); Vk_CommandBuffer *cmds = Vk_CommandBufferPush();
font->id = draw->n_images + draw->n_fonts;
font->image.width = w; font->image.width = w;
font->image.height = h; font->image.height = h;
+7 -2
View File
@@ -6,7 +6,10 @@
typedef struct D_Glyph D_Glyph; typedef struct D_Glyph D_Glyph;
struct D_Glyph { struct D_Glyph {
F32 advance; F32 advance;
V2f offset; V2f offset;
V2f dim;
R2f box; R2f box;
}; };
@@ -14,13 +17,13 @@ typedef struct D_Font D_Font;
struct D_Font { struct D_Font {
D_Font *next; D_Font *next;
F32 px; U32 id;
F32 line_advance; F32 line_advance;
F32 ascent; F32 ascent;
F32 descent; F32 descent;
F32 *kerning; F32 *kerning; // @incomplete: we don't load this
D_Glyph *glyphs; D_Glyph *glyphs;
Vk_Image image; Vk_Image image;
@@ -108,6 +111,8 @@ enum D_RectFlags {
D_RECT_IGNORE_ASPECT = (1 << 0), // by default only width is used as a "dimension" D_RECT_IGNORE_ASPECT = (1 << 0), // by default only width is used as a "dimension"
D_RECT_PER_VERTEX_COLOUR = (1 << 1), // split colours per vertex D_RECT_PER_VERTEX_COLOUR = (1 << 1), // split colours per vertex
D_RECT_UV_ASPECT = (1 << 2), // get the aspect from the uv rect rather than the full image D_RECT_UV_ASPECT = (1 << 2), // get the aspect from the uv rect rather than the full image
D_RECT_FLIP_X = (1 << 3),
D_RECT_FLIP_Y = (1 << 4)
}; };
typedef struct D_RectOpts D_RectOpts; typedef struct D_RectOpts D_RectOpts;
+157 -73
View File
@@ -24,6 +24,7 @@
#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/impl/bandit.c"
#include "game/impl/outfit.c"
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@@ -43,30 +44,6 @@ int main(int argc, char **argv)
return 1; return 1;
} }
SDL_AudioStream *austream;
Str8 audio_data;
M_TempScope(0, 0) {
SDL_AudioSpec spec;
spec.format = SDL_AUDIO_S16LE;
spec.channels = 2;
spec.freq = 44100;
austream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, 0, 0);
if (!austream) {
printf("Failed to open audio stream (%s)\n", SDL_GetError());
}
Str8 exec = FS_SystemPath(temp.arena, FS_SYSTEM_PATH_EXE);
Str8 path = Sf(temp.arena, "%.*s/assets/outside_ambience.wav", Sv(exec));
SDL_AudioSpec wav_spec;
U32 count;
SDL_LoadWAV((const char *) path.data, &wav_spec, &audio_data.data, &count);
audio_data.count = count;
}
Vk_Setup(window); Vk_Setup(window);
G_State *game = 0; G_State *game = 0;
@@ -78,9 +55,19 @@ int main(int argc, char **argv)
game->draw.arena = arena; game->draw.arena = arena;
G_ImagesLoad(game); G_ImagesLoad(game);
//D_FontLoad(&game->draw, S("ubuntu"), 60);
G_PipelinesLoad(game); G_PipelinesLoad(game);
G_AudioLoad(game); G_AudioLoad(game);
Audio_Context *audio = M_ArenaPush(game->arena, Audio_Context);
Audio_Init(game->arena, audio, 0.0f); // disabled audio
U32 bgm = Audio_Play(audio, 0);
U32 saloon_music = Audio_Play(audio, 1);
SDL_ResumeAudioStreamDevice(audio->stream);
G_Camera *camera = &game->camera; G_Camera *camera = &game->camera;
camera->x = V3F(1, 0, 0); camera->x = V3F(1, 0, 0);
@@ -95,12 +82,16 @@ int main(int argc, char **argv)
game->draw.camera = camera; game->draw.camera = camera;
World *world = M_ArenaPush(arena, World); World *world = M_ArenaPush(arena, World);
//LoadWorld(arena, world); LoadWorld(arena, world);
game->world = world; game->world = world;
world->audio = audio;
world->audio_handles[0] = bgm;
world->audio_handles[1] = saloon_music;
world->arena = arena; world->arena = arena;
//world->navMesh = &TestNavMesh;
world->random = Random_Seed(29237489723847); world->random = Random_Seed(29237489723847);
world->npcCount = 127; world->npcCount = 12;
for(U32 i = 0; i < world->npcCount; i++) { for(U32 i = 0; i < world->npcCount; i++) {
NPC *npc1 = &world->npcs[i]; NPC *npc1 = &world->npcs[i];
npc1->collision.pos.x = 0; npc1->collision.pos.x = 0;
@@ -111,8 +102,9 @@ 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;
GenOutfit(&npc1->outfit,world,game);
} }
Bandit *badman = &world->bandit; Bandit *badman = &world->bandit;
@@ -125,17 +117,21 @@ int main(int argc, char **argv)
badman->waitTime = 0; badman->waitTime = 0;
badman->maxWaitTime = 2; badman->maxWaitTime = 2;
badman->poiCount = 2; badman->poiCount = 2;
badman->shootoutTimer = 1.5;
badman->agroRadius = 6.0;
badman->bullets = 6;
badman->shootDelay = 1;
badman->accuracyRange = 0.25;
badman->reloadTime = 2.5;
badman->reloadTimer = 0;
badman->pointsOfInterest[0] = 937; badman->pointsOfInterest[0] = 937;
badman->pointsOfInterest[1] = 12; badman->pointsOfInterest[1] = 12;
badman->outfitChoices = GenOutfit(&badman->outfit, world, game);
badman->currentArea = WORLD_AREA_OUTSIDE;
world->npcPOI[0] = 100; world->npcPOI[0] = 100;
world->player.world = world;
world->player.pos.x = 0; PlayerInit(game, &world->player);
world->player.pos.y = 0;
world->player.bulletsLoaded = PLAYER_BULLET_COUNT;
world->player.reloadTimer = 0;
world->player.currentArea = WORLD_AREA_OUTSIDE;
world->map = map;
world->tileTypes = M_ArenaPush(arena, World_Tile, .count=WORLD_TILE_TYPE_MAX); world->tileTypes = M_ArenaPush(arena, World_Tile, .count=WORLD_TILE_TYPE_MAX);
world->tileTypes[0].rotation=0; world->tileTypes[0].rotation=0;
@@ -171,9 +167,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");
@@ -181,17 +177,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");
@@ -199,7 +195,7 @@ int main(int argc, char **argv)
world->propTypes[13].tag = S("house"); world->propTypes[13].tag = S("house");
world->propTypes[13].scale=6.875f; world->propTypes[13].scale=6.875f;
world->propTypes[14].tag = S("house_int"); world->propTypes[14].tag = S("house_int");
world->propTypes[14].scale=6.875f; world->propTypes[14].scale=12.875f;
world->propTypes[15].tag=S("tile_detail_0"); world->propTypes[15].tag=S("tile_detail_0");
world->propTypes[15].scale=1; world->propTypes[15].scale=1;
world->propTypes[16].tag = S("tile_detail_1"); world->propTypes[16].tag = S("tile_detail_1");
@@ -216,19 +212,15 @@ int main(int argc, char **argv)
world->propTypes[21].scale=1; world->propTypes[21].scale=1;
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, World_Hitbox, .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;
D_Animation animation;
{
U32 id = D_ImageHandle(&game->draw, S("npc_front_base_white"));
D_AnimationInit(&animation, id, 1, 4, 1.0f / 20.0f);
}
bool running = true; bool running = true;
@@ -244,6 +236,14 @@ int main(int argc, char **argv)
{ {
running = false; running = false;
} }
else if (e.type == SDL_EVENT_KEY_DOWN) {
Player *player = &game->world->player;
switch (e.key.key) {
case SDLK_R: { PlayerInit(game, player); } break;
case SDLK_F: {game->world->showPoster=!game->world->showPoster;}break;
}
}
V3f projection = G_CameraUnproject(&game->camera, V2f_Clip( V3f projection = G_CameraUnproject(&game->camera, V2f_Clip(
V2F(e.button.x, e.button.y), V2F(e.button.x, e.button.y),
V2F((F32) width, (F32) height) V2F((F32) width, (F32) height)
@@ -258,8 +258,8 @@ int main(int argc, char **argv)
if(e.type==SDL_EVENT_MOUSE_BUTTON_DOWN && e.button.button == SDL_BUTTON_LEFT) { if(e.type==SDL_EVENT_MOUSE_BUTTON_DOWN && e.button.button == SDL_BUTTON_LEFT) {
switch(game->editor.mode){ switch(game->editor.mode){
case G_EDITOR_MODE_TILE: { case G_EDITOR_MODE_TILE: {
F32 tilex = (S32)(game->editor.cursor.x+TILE_SIZE/2); F32 tilex = (F32) (S32)(game->editor.cursor.x+TILE_SIZE/2);
F32 tiley = (S32)(game->editor.cursor.y+TILE_SIZE/2); F32 tiley = (F32) (S32)(game->editor.cursor.y+TILE_SIZE/2);
game->world->map[(S32)tilex + (S32)tiley * 96] = game->editor.currentAsset; game->world->map[(S32)tilex + (S32)tiley * 96] = game->editor.currentAsset;
break; break;
} }
@@ -271,19 +271,33 @@ 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].box.pos = topLeft;
game->world->hitboxes[game->world->hitboxCount].size = V2F( game->world->hitboxes[game->world->hitboxCount].box.size = V2F(
Abs(game->editor.cursor.x-game->editor.dragStart.x), Abs(game->editor.cursor.x-game->editor.dragStart.x),
Abs(game->editor.cursor.y-game->editor.dragStart.y) Abs(game->editor.cursor.y-game->editor.dragStart.y)
); );
game->world->hitboxes[game->world->hitboxCount].area = game->editor.currentAsset;
game->world->hitboxCount++; 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);
} }
} }
@@ -295,10 +309,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: {
@@ -327,6 +343,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);
@@ -336,6 +362,7 @@ int main(int argc, char **argv)
switch(game->editor.mode) { switch(game->editor.mode) {
case G_EDITOR_MODE_PROP: {game->world->propCount--;} case G_EDITOR_MODE_PROP: {game->world->propCount--;}
case G_EDITOR_MODE_HITBOX: {game->world->hitboxCount--;} case G_EDITOR_MODE_HITBOX: {game->world->hitboxCount--;}
case G_EDITOR_MODE_PORTAL: {game->world->portalCount--;}
} }
break; break;
} }
@@ -344,11 +371,11 @@ int main(int argc, char **argv)
} }
if(!game->editor.enabled) { if(!game->editor.enabled) {
UpdateWorld(1.0f / 60.0f, game->world); UpdateWorld(1.0f / 250.0f, game->world);
game->camera.p.x = game->world->player.pos.x;
game->camera.p.y = game->world->player.pos.y; game->camera.p.x = game->world->player.collision.pos.x;
} game->camera.p.y = game->world->player.collision.pos.y;
D_AnimationUpdate(&animation, 1.0f / 250.0f); }
int w, h; int w, h;
SDL_GetWindowSizeInPixels(window, &w, &h); SDL_GetWindowSizeInPixels(window, &w, &h);
@@ -386,15 +413,58 @@ 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); RenderWorld(game->world, &game->draw);
R2f aframe = D_AnimationFrame(&animation); R3f bounds = G_CameraBounds(&game->camera);
D_Rect(&game->draw, 0, 0, .texture = animation.id, .uv = aframe, .flags = D_RECT_UV_ASPECT);
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);
}
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")));
}
//D_Text(&game->draw, game->draw.fonts, S("Small Test"), 0, 0);
if(game->editor.enabled) { if(game->editor.enabled) {
G_Editor editor = game->editor; G_Editor editor = game->editor;
F32 tilex = floor(editor.cursor.x+TILE_SIZE/2); F32 tilex = cast(F32) floor(editor.cursor.x+TILE_SIZE/2);
F32 tiley = floor(editor.cursor.y+TILE_SIZE/2); F32 tiley = cast(F32) floor(editor.cursor.y+TILE_SIZE/2);
for (U32 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 (U32 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];
@@ -407,23 +477,37 @@ int main(int argc, char **argv)
break; break;
} }
case G_EDITOR_MODE_HITBOX: { case G_EDITOR_MODE_HITBOX: {
for(int i = 0; i < game->world->hitboxCount; i++) { for(U32 i = 0; i < game->world->hitboxCount; i++) {
V2f centre = AABB_Centre(game->world->hitboxes[i]); 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),
);
}
}
break;
}
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( D_Rect(
&game->draw, &game->draw,
centre.x, centre.x,
centre.y, centre.y,
.texture=0, .texture=0,
.dim=game->world->hitboxes[i].size, .dim=game->world->portals[i].box.size,
.flags=D_RECT_IGNORE_ASPECT, .flags=D_RECT_IGNORE_ASPECT,
.c=V4F(100,0,0,0.7), .c=V4F(0,0,100,0.7f),
); );
} }
break; break;
} }
case G_EDITOR_MODE_POI: {
break;
}
} }
} }
D_End(&game->draw, frame); D_End(&game->draw, frame);
+1
View File
@@ -14,5 +14,6 @@ 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); function bool AABB_Slab(V2f origin, V2f point, AABB a);
function V2f AABB_Centre(AABB a); function V2f AABB_Centre(AABB a);
function bool AABB_Circle(F32 rad, V2f radOrigin, AABB a);
#endif // LD_GAME_AABB_H_ #endif // LD_GAME_AABB_H_
+14 -1
View File
@@ -1,12 +1,15 @@
#if !defined(LD_GAME_BANDIT_H_) #if !defined(LD_GAME_BANDIT_H_)
#define LD_GAME_BANDIT_H_ #define LD_GAME_BANDIT_H_
#include "outfit.h"
typedef enum BANDIT_ACTION BANDIT_ACTION; typedef enum BANDIT_ACTION BANDIT_ACTION;
enum BANDIT_ACTION { enum BANDIT_ACTION
{
BANDIT_WAITING, BANDIT_WAITING,
BANDIT_WALKING, BANDIT_WALKING,
BANDIT_RUNNING, BANDIT_RUNNING,
BANDIT_SHOOTING, BANDIT_SHOOTING,
BANDIT_SHOOTOUT,
}; };
typedef struct Bandit Bandit; typedef struct Bandit Bandit;
@@ -50,12 +53,22 @@ struct Bandit {
F32 shootDelay; F32 shootDelay;
// After each shot this is set to shootDelay; // After each shot this is set to shootDelay;
F32 shootCooldownTimer; F32 shootCooldownTimer;
// Countdown to shootout
F32 shootoutTimer;
// How long it takes them to reload. // How long it takes them to reload.
F32 reloadTime; F32 reloadTime;
// When gun is empty this is set to reloadTime.
F32 reloadTimer;
// Accuracy, their shots can vary between this angle either side (rads) // Accuracy, their shots can vary between this angle either side (rads)
F32 accuracyRange; F32 accuracyRange;
// A the circle around the bandit where they will trigger the quicktime reaction scene // A the circle around the bandit where they will trigger the quicktime reaction scene
F32 agroRadius; F32 agroRadius;
// What the bandit is wearing
G_Outfit outfit;
// What the bandit's outfit id's are
U32 *outfitChoices;
}; };
function V2f ShootTowards(Bandit* bandit, V2f target, Random* r);
function void BanditDraw(D_Context *draw, Bandit *bandit);
#endif // LD_GAME_BANDIT_H_ #endif // LD_GAME_BANDIT_H_
+47 -2
View File
@@ -1,6 +1,7 @@
#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 World World;
typedef struct G_Camera G_Camera; typedef struct G_Camera G_Camera;
struct G_Camera { struct G_Camera {
@@ -20,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;
@@ -31,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;
@@ -44,6 +46,48 @@ struct G_State {
World *world; World *world;
}; };
typedef U32 G_OutfitDirection;
enum {
G_OUTFIT_DIR_FRONT = 0,
G_OUTFIT_DIR_SIDE,
G_OUTFIT_DIR_BACK,
G_OUTFIT_DIR_FLIPPED = (1 << 16)
};
typedef union G_OutfitSet G_OutfitSet;
union G_OutfitSet {
struct {
U32 base;
U32 eyes;
U32 face;
U32 hair;
U32 hat;
U32 shirt;
U32 shoes;
U32 trousers;
};
U32 e[8];
};
typedef struct G_Outfit G_Outfit;
struct G_Outfit {
D_Animation state; // .id in here isn't used for drawing
G_OutfitDirection dir;
union {
struct {
G_OutfitSet front;
G_OutfitSet side;
G_OutfitSet back;
};
G_OutfitSet e[3];
};
};
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_AudioLoad(G_State *game); function void G_AudioLoad(G_State *game);
@@ -56,6 +100,7 @@ function V3f G_CameraUnproject(G_Camera *camera, V2f clip);
function R3f G_CameraBounds(G_Camera *camera); function R3f G_CameraBounds(G_Camera *camera);
#include "world.h"
#include "aabb.h" #include "aabb.h"
#include "player.h" #include "player.h"
#include "nav.h" #include "nav.h"
+9 -1
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;
} }
@@ -38,3 +38,11 @@ bool AABB_Slab(V2f origin, V2f point, AABB a)
V2f AABB_Centre(AABB a) { V2f AABB_Centre(AABB a) {
return V2F(a.pos.x + a.size.x/2, a.pos.y + a.size.y/2); return V2F(a.pos.x + a.size.x/2, a.pos.y + a.size.y/2);
} }
bool AABB_Circle(F32 rad, V2f radOrigin, AABB a)
{
V2f aCentre = AABB_Centre(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;
}
+63 -3
View File
@@ -1,13 +1,30 @@
#include "game/world.h" #include "game/world.h"
#include "game/bandit.h" #include "game/bandit.h"
V2f ShootTowards(Bandit *bandit, V2f target, Random* r)
{
V2f shooterV2 = bandit->collision.pos;
F32 randX = Random_F32(r, -bandit->accuracyRange, bandit->accuracyRange);
F32 randY = Random_F32(r, -bandit->accuracyRange, bandit->accuracyRange);
return V2F(shooterV2.x + (target.x - shooterV2.x) * (1 + randX), shooterV2.x + (target.y - shooterV2.y) * (1 + randY));
}
void UpdateBandit(F32 delta, Bandit *bandit, World *world) { void UpdateBandit(F32 delta, Bandit *bandit, World *world) {
for(U32 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.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)
{ {
printf("You shot the bandit %*.s\n", Sv(bandit->name));
bandit->health--; bandit->health--;
} }
if (AABB_Circle(bandit->agroRadius, AABB_Centre(bandit->collision), world->player.collision) && !(bandit->mode == BANDIT_SHOOTING || bandit->mode == BANDIT_SHOOTOUT))
{
// shootout time o.o
bandit->mode = BANDIT_SHOOTOUT;
}
switch (bandit->mode) { switch (bandit->mode) {
case BANDIT_WAITING: case BANDIT_WAITING:
bandit->waitTime+=delta; bandit->waitTime+=delta;
@@ -41,7 +58,50 @@ void UpdateBandit(F32 delta, Bandit *bandit, World *world) {
bandit->collision.pos.x = cNav.pos.x * (1 - bandit->walkTimer/NPC_SPEED) + tNav.pos.x * bandit->walkTimer/NPC_SPEED; 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; bandit->collision.pos.y = cNav.pos.y * (1 - bandit->walkTimer/NPC_SPEED) + tNav.pos.y * bandit->walkTimer/NPC_SPEED;
break; break;
// TODO Shooting case BANDIT_SHOOTOUT:
bandit->shootoutTimer-=delta;
if(bandit->shootoutTimer < 0){
bandit->mode=BANDIT_SHOOTING;
}
break;
case BANDIT_SHOOTING:
bandit->shootCooldownTimer -= delta;
bandit->reloadTimer -= delta;
if (bandit->shootCooldownTimer < 0 && bandit->reloadTimer < 0)
{
bandit->bullets--;
bandit->shootCooldownTimer = bandit->shootDelay;
V2f banditShot = ShootTowards(bandit, world->player.collision.pos, &world->random);
if(AABB_Slab(bandit->collision.pos, banditShot, world->player.collision)){
// gets shot lmao
world->player.health--;
}
if(bandit->bullets == 0){
bandit->bullets = 6;
bandit->reloadTimer = bandit->reloadTime;
}
}
break;
// TODO Running away // TODO Running away
} }
} }
void BanditDraw(D_Context *draw, Bandit *bandit)
{
G_Outfit *outfit = &bandit->outfit;
R2f pframe = D_AnimationFrame(&outfit->state);
for (U32 it = 0; it < G_OUTFIT_COMPONENT_COUNT; ++it)
{
U32 flipped = (outfit->dir & G_OUTFIT_DIR_FLIPPED) != 0;
U32 dir = (outfit->dir & ~G_OUTFIT_DIR_FLIPPED);
U32 tid = outfit->e[dir].e[it];
if (tid != 0)
{
U32 flags = D_RECT_UV_ASPECT | (flipped ? D_RECT_FLIP_X : 0);
D_Rect(draw, bandit->collision.pos.x, bandit->collision.pos.y, .texture = tid, .uv = pframe, .flags = flags);
}
}
}
+25
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(U32 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;
@@ -41,3 +46,23 @@ void UpdateNPC(F32 delta, NPC *npc, World *world) {
break; break;
} }
} }
void NPCDraw(D_Context *draw, NPC *npc)
{
G_Outfit *outfit = &npc->outfit;
R2f pframe = D_AnimationFrame(&outfit->state);
for (U32 it = 0; it < G_OUTFIT_COMPONENT_COUNT; ++it)
{
U32 flipped = (outfit->dir & G_OUTFIT_DIR_FLIPPED) != 0;
U32 dir = (outfit->dir & ~G_OUTFIT_DIR_FLIPPED);
U32 tid = outfit->e[dir].e[it];
if (tid != 0)
{
U32 flags = D_RECT_UV_ASPECT | (flipped ? D_RECT_FLIP_X : 0);
D_Rect(draw, npc->collision.pos.x, npc->collision.pos.y, .texture = tid, .uv = pframe, .flags = flags);
}
}
}
+31
View File
@@ -0,0 +1,31 @@
U32* GenOutfit(G_Outfit *o, World *world, G_State *game)
{
U32 *outfitChoices = M_ArenaPush(world->arena, U32, .count = 8);
G_Outfit *outfit = o;
D_AnimationInit(&outfit->state, 0, 1, 4, 1.0f / 6.0f);
M_TempScope(0, 0)
{
for (U32 it = 0; it < G_OUTFIT_COMPONENT_COUNT; ++it)
{
U32 idx = Random_Next(&world->random) % __outfit_counts[it];
// We just allow face, hair and hat to default to 0 meaning the player doesn't have one
if (idx != 0 || it < 2 || it > 4)
{
outfit->front.e[it] = OUTFIT_IMG(front, idx);
outfit->side.e[it] = OUTFIT_IMG(side, idx);
if ((idx + 1) <= __outfit_counts[it])
{
outfit->back.e[it] = OUTFIT_IMG(back, idx);
}
}
outfitChoices[it] = idx;
}
}
return outfitChoices;
}
+99 -4
View File
@@ -2,6 +2,42 @@
#include <SDL3/SDL_events.h> #include <SDL3/SDL_events.h>
#include <SDL3/SDL_keycode.h> #include <SDL3/SDL_keycode.h>
#include <stdio.h> #include <stdio.h>
#include "../outfit.h"
#include "../npc.h"
void PlayerInit(G_State *game, Player *player) {
World *world = game->world;
player->world = world;
player->bulletsLoaded = PLAYER_BULLET_COUNT;
player->currentArea = WORLD_AREA_OUTSIDE;
player->health = 3;
player->collision.size.x = 1;
player->collision.size.x = 2;
G_Outfit *outfit = &player->outfit;
D_AnimationInit(&outfit->state, 0, 1, 4, 1.0f / 6.0f);
M_TempScope(0, 0) {
for (U32 it = 0; it < G_OUTFIT_COMPONENT_COUNT; ++it) {
U32 idx = Random_Next(&world->random) % __outfit_counts[it];
// We just allow face, hair and hat to default to 0 meaning the player doesn't have one
if (idx != 0 || it < 2 || it > 4) {
outfit->front.e[it] = OUTFIT_IMG(front, idx);
outfit->side.e[it] = OUTFIT_IMG(side, idx);
if ((idx + 1) <= __outfit_counts[it]) {
outfit->back.e[it] = OUTFIT_IMG(back, idx);
}
}
}
}
}
void PlayerInput(SDL_Event *event, Player *player) void PlayerInput(SDL_Event *event, Player *player)
{ {
@@ -31,10 +67,20 @@ void PlayerInput(SDL_Event *event, Player *player)
player->controls.downDown = val; player->controls.downDown = val;
break; break;
} }
case SDLK_E:
{
for(int i = 0; player->world->npcCount; i++){
NPC *npc = &player->world->npcs[i];
if(AABB_Circle(npc->interationRadius, npc->collision.pos, player->collision) && !npc->infoGiven){
npc->infoGiven=true;
player->knownDetails[player->detailCount] = player->world->bandit.outfitChoices[player->detailCount];
}
}
}
} }
} }
if ( if (
event->type == SDL_EVENT_MOUSE_BUTTON_DOWN event->type == SDL_EVENT_MOUSE_BUTTON_DOWN
&& mouseBtn.button == SDL_BUTTON_LEFT && mouseBtn.button == SDL_BUTTON_LEFT
) { ) {
if(player->bulletsLoaded > 0) { if(player->bulletsLoaded > 0) {
@@ -47,24 +93,39 @@ void PlayerInput(SDL_Event *event, Player *player)
player->reloadTimer = PLAYER_RELOAD_TIME; player->reloadTimer = PLAYER_RELOAD_TIME;
printf("reloading\n"); printf("reloading\n");
}; };
} }
} }
void PlayerUpdate(F32 delta, Player *player) { void PlayerUpdate(F32 delta, Player *player) {
player->controls.shot = false; player->controls.shot = false;
V2f dir = V2F(0, 0); V2f dir = V2F(0, 0);
if(player->health == 0){
player->health = 3;
}
if(player->controls.upDown) { if(player->controls.upDown) {
dir.y -= 1; dir.y -= 1;
player->outfit.dir = G_OUTFIT_DIR_BACK;
} }
if(player->controls.downDown) { if(player->controls.downDown) {
dir.y += 1; dir.y += 1;
player->outfit.dir = G_OUTFIT_DIR_FRONT;
} }
if(player->controls.leftDown) { if(player->controls.leftDown) {
dir.x -= 1; dir.x -= 1;
player->outfit.dir = G_OUTFIT_DIR_SIDE | G_OUTFIT_DIR_FLIPPED;
} }
if(player->controls.rightDown) { if(player->controls.rightDown) {
dir.x += 1; dir.x += 1;
player->outfit.dir = G_OUTFIT_DIR_SIDE;
} }
if (dir.x != 0 || dir.y != 0) {
D_AnimationUpdate(&player->outfit.state, delta);
}
else {
player->outfit.state.index = 0;
}
if(player->reloadTimer > 0) { if(player->reloadTimer > 0) {
player->reloadTimer-=delta; player->reloadTimer-=delta;
if(player->reloadTimer <= 0) { if(player->reloadTimer <= 0) {
@@ -73,6 +134,40 @@ void PlayerUpdate(F32 delta, Player *player) {
} }
} }
dir = V2f_Scale(NormaliseV2F(dir), PLAYER_SPEED*delta); dir = V2f_Scale(NormaliseV2F(dir), PLAYER_SPEED*delta);
player->pos.x += dir.x; player->collision.pos.x += dir.x;
player->pos.y += dir.y; player->collision.pos.y += dir.y;
for(U32 i = 0; i < player->world->portalCount; i++) {
if(AABB_Collide(player->world->portals[i].box, player->collision)) {
player->currentArea = player->world->portals[i].area;
}
}
V2f test;
test.x = player->collision.pos.x - 14;
test.y = player->collision.pos.y - 14;
F32 len = V2f_Dot(test, test);
F32 vol = Max(0.0f, 1.0f - (len / 25.0f));
Audio_ChangeVolume(player->world->audio, player->world->audio_handles[1], vol);
Audio_ChangeVolume(player->world->audio, player->world->audio_handles[0], 1.0f - vol);
}
void PlayerDraw(D_Context *draw, Player *player) {
G_Outfit *outfit = &player->outfit;
R2f pframe = D_AnimationFrame(&outfit->state);
for (U32 it = 0; it < G_OUTFIT_COMPONENT_COUNT; ++it) {
U32 flipped = (outfit->dir & G_OUTFIT_DIR_FLIPPED) != 0;
U32 dir = (outfit->dir & ~G_OUTFIT_DIR_FLIPPED);
U32 tid = outfit->e[dir].e[it];
if (tid != 0) {
U32 flags = D_RECT_UV_ASPECT | (flipped ? D_RECT_FLIP_X : 0);
D_Rect(draw, player->collision.pos.x, player->collision.pos.y, .texture = tid, .uv = pframe, .flags = flags);
}
}
} }
+191 -97
View File
@@ -9,6 +9,9 @@
void UpdateWorld(F32 delta, World *world) void UpdateWorld(F32 delta, World *world)
{ {
if(world->bandit.mode == BANDIT_SHOOTOUT){
delta = delta/4;
}
UpdateBandit(delta, &world->bandit, world); UpdateBandit(delta, &world->bandit, world);
UpdateNPCs(delta, world); UpdateNPCs(delta, world);
PlayerUpdate(delta, &world->player); PlayerUpdate(delta, &world->player);
@@ -21,7 +24,7 @@ void UpdateNPCs(F32 delta, World *world)
NPC *npc = &world->npcs[i]; NPC *npc = &world->npcs[i];
UpdateNPC(delta, npc, world); UpdateNPC(delta, npc, world);
if ( if (
world->player.controls.shot && AABB_Slab(world->player.pos, world->player.shotPos, npc->collision) && npc->currentArea == world->player.currentArea) world->player.controls.shot && AABB_Slab(world->player.collision.pos, world->player.shotPos, npc->collision) && npc->currentArea == world->player.currentArea)
{ {
printf("You shot %*.s\n", Sv(world->npcs[i].name)); printf("You shot %*.s\n", Sv(world->npcs[i].name));
} }
@@ -32,7 +35,7 @@ void ProcessEvents(SDL_Event *event, World *world)
{ {
PlayerInput(event, &world->player); PlayerInput(event, &world->player);
if(event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_F5){ if(event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_F5){
SaveWorld(world->arena, world); SaveWorld(world);
} }
if(event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_F6){ if(event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_F6){
LoadWorld(world->arena, world); LoadWorld(world->arena, world);
@@ -50,7 +53,7 @@ void RenderWorld(World *world, D_Context *draw) {
); );
} }
} }
for (int i = 0; i < world->propCount; i++) { for (U32 i = 0; i < world->propCount; i++) {
if(world->props[i].area == world->player.currentArea) { if(world->props[i].area == world->player.currentArea) {
D_Rect( D_Rect(
draw, draw,
@@ -61,119 +64,170 @@ 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) {
V2f drawPos = AABB_Centre(npc.collision); NPCDraw(draw, &world->npcs[i]);
D_Rect(draw, drawPos.x, drawPos.y, .texture = 1);
} }
} }
if(world->bandit.currentArea == world->player.currentArea) {
V2f drawPos = AABB_Centre(world->bandit.collision); BanditDraw(draw, &world->bandit);
D_Rect(draw, drawPos.x, drawPos.y, .texture = 9); PlayerDraw(draw, &world->player);
}
D_Rect(draw, world->player.pos.x, world->player.pos.y, .texture = 1);
} }
void SaveWorld(M_Arena *arena, World *world) { #define WRITE(v) FS_FileWrite(file, &(v), sizeof(v), offset); offset += sizeof(v)
printf("Saving world\n"); #define WRITEN(v, n) FS_FileWrite(file, v, (n) * sizeof(*(v)), offset); offset += ((n) * sizeof(*(v)))
void SaveWorld(World *world) {
printf("--- Saving World ---\n");
printf(" - %d props\n", world->propCount);
printf(" - %d hitboxes\n", world->hitboxCount);
OS_Handle file = FS_FileOpen(S("world.sgdat"), FS_ACCESS_WRITE); OS_Handle file = FS_FileOpen(S("world.sgdat"), FS_ACCESS_WRITE);
U32 offset = 0; U64 offset = 0;
for(int i = 0; i < WORLD_TILE_TYPE_MAX; i++) {
FS_FileWrite(file, &world->tileTypes[i].tag.count, sizeof(S64), offset); for (U32 i = 0; i < WORLD_TILE_TYPE_MAX; i++) {
offset += sizeof(S64); World_Tile *tile = &world->tileTypes[i];
FS_FileWrite(file, world->tileTypes[i].tag.data, world->tileTypes[i].tag.count, offset);
offset += sizeof(U8) * world->tileTypes[i].tag.count; WRITE(tile->tag.count);
FS_FileWrite(file, &world->tileTypes[i].rotation, sizeof(F32), offset); WRITEN(tile->tag.data, tile->tag.count);
offset += sizeof(F32); WRITE(tile->rotation);
}
for(int i = 0; i < WORLD_PROP_TYPE_MAX; i++) {
FS_FileWrite(file, &world->propTypes[i].tag.count, sizeof(S64), offset);
offset += sizeof(S64);
FS_FileWrite(file, world->propTypes[i].tag.data, world->propTypes[i].tag.count, offset);
offset += sizeof(U8) * world->propTypes[i].tag.count;
FS_FileWrite(file, &world->propTypes[i].scale, sizeof(F32), offset);
offset += sizeof(F32);
} }
FS_FileWrite(file, world->propTypes, sizeof(World_PropType)*WORLD_PROP_TYPE_MAX, offset); for (U32 i = 0; i < WORLD_PROP_TYPE_MAX; i++) {
offset += sizeof(World_PropType)*WORLD_PROP_TYPE_MAX; World_PropType *type = &world->propTypes[i];
FS_FileWrite(file, &world->propCount, sizeof(U32), offset); WRITE(type->tag.count);
offset += sizeof(U32); WRITEN(type->tag.data, type->tag.count);
WRITE(type->scale);
}
FS_FileWrite(file, world->props, sizeof(World_Prop)*WORLD_PROP_MAX, offset); WRITE(world->propCount);
offset += sizeof(World_Prop)*WORLD_PROP_MAX; WRITEN(world->props, WORLD_PROP_MAX);
FS_FileWrite(file, &world->hitboxCount, sizeof(U32), offset); WRITE(world->hitboxCount);
offset += sizeof(U32); WRITEN(world->hitboxes, WORLD_HITBOX_MAX);
FS_FileWrite(file, world->hitboxes, sizeof(AABB)*WORLD_HITBOX_MAX, offset); WRITEN(world->map, WORLD_MAP_MAX);
offset += sizeof(AABB)*WORLD_HITBOX_MAX;
FS_FileWrite(file, world->map, sizeof(U32)*WORLD_MAP_MAX, offset);
offset += sizeof(U32)*WORLD_MAP_MAX;
WRITE(world->portalCount);
WRITEN(world->portals, WORLD_PORTAL_MAX);
FS_FileClose(file); FS_FileClose(file);
printf("Saved world :)\n");
printf("--- World Saved ---\n");
} }
void LoadWorld(M_Arena *arena, World *world) { void LoadWorld(M_Arena *arena, World *world) {
printf("loading world\n"); printf("--- Loading World ---\n");
OS_Handle file = FS_FileOpen(S("world.sgdat"), FS_ACCESS_READ);
U32 offset = 0;
world->tileTypes = M_ArenaPush(arena, World_Tile, .count=WORLD_TILE_TYPE_MAX); M_TempScope(0, 0) {
for(int i = 0; i < WORLD_TILE_TYPE_MAX; i++) { Str8 data = FS_ReadEntireFile(temp.arena, S("world.sgdat"));
FS_FileRead(file, &world->tileTypes[i].tag.count, sizeof(S64), offset);
offset += sizeof(S64);
world->tileTypes[i].tag.data = M_ArenaPush(arena, U8, .count=world->tileTypes[i].tag.count);
FS_FileRead(file, world->tileTypes[i].tag.data, world->tileTypes[i].tag.count, offset);
offset += sizeof(U8) * world->tileTypes[i].tag.count;
FS_FileRead(file, &world->tileTypes[i].rotation, sizeof(F32), offset);
offset += sizeof(F32);
}
world->propTypes = M_ArenaPush(arena, World_PropType, .count=WORLD_PROP_TYPE_MAX);
for(int i = 0; i < WORLD_PROP_TYPE_MAX; i++) {
FS_FileRead(file, &world->propTypes[i].tag.count, sizeof(S64), offset);
offset += sizeof(S64);
world->propTypes[i].tag.data = M_ArenaPush(arena, U8, .count=world->propTypes[i].tag.count);
FS_FileRead(file, world->propTypes[i].tag.data, world->propTypes[i].tag.count, offset);
offset += sizeof(U8) * world->propTypes[i].tag.count;
FS_FileRead(file, &world->propTypes[i].scale, sizeof(F32), offset);
offset += sizeof(F32);
}
FS_FileRead(file, &world->propCount, sizeof(U32), offset); U8 *base = data.data;
offset += sizeof(U32);
world->props = M_ArenaPush(arena, World_Prop, .count=WORLD_PROP_MAX); // Load tile types
FS_FileRead(file, world->props, sizeof(World_Prop)*WORLD_PROP_MAX, offset); //
offset += sizeof(World_Prop)*world->propCount; world->tileTypes = M_ArenaPush(arena, World_Tile, .count = WORLD_TILE_TYPE_MAX);
for (U32 it = 0; it < WORLD_TILE_TYPE_MAX; ++it) {
World_Tile *type = &world->tileTypes[it];
FS_FileRead(file, &world->hitboxCount, sizeof(U32), offset); Str8 name;
offset += sizeof(U32); name.count = *(S64 *) base;
name.data = base + 8;
world->hitboxes = M_ArenaPush(arena, AABB, .count=WORLD_HITBOX_MAX); base += 8;
FS_FileRead(file, world->hitboxes, sizeof(AABB)*WORLD_HITBOX_MAX, offset); base += name.count;
offset += sizeof(AABB)*world->hitboxCount;
world->map = M_ArenaPush(arena, U32, .count=WORLD_MAP_MAX); if (name.count != 0) {
FS_FileRead(file, world->map, sizeof(U32)*WORLD_MAP_MAX, offset); // Means we likely got a vaild tag
offset += sizeof(U32)*WORLD_MAP_MAX; type->tag = Str8_Copy(arena, name);
type->rotation = *(F32 *) base;
}
FS_FileClose(file); base += 4;
printf("loaded world\n"); }
// Load prop types
//
world->propTypes = M_ArenaPush(arena, World_PropType, .count = WORLD_PROP_TYPE_MAX);
for (U32 it = 0; it < WORLD_PROP_TYPE_MAX; ++it) {
World_PropType *type = &world->propTypes[it];
Str8 name;
name.count = *(S64 *) base;
name.data = base + 8;
base += 8;
base += name.count;
if (name.count != 0) {
type->tag = Str8_Copy(arena, name);
type->scale = *(F32 *) base;
}
base += 4;
}
// Prop locations
{
U32 n_props = *(U32 *) base; base += 4;
World_Prop *props = (World_Prop *) base;
world->propCount = n_props;
world->props = M_ArenaPush(arena, World_Prop, .count = WORLD_PROP_MAX);
M_CopySize(world->props, props, n_props * sizeof(World_Prop));
base += (WORLD_PROP_MAX * sizeof(World_Prop));
}
// Hitboxes
//
{
U32 n_hitbox = *(U32 *) base; base += 4;
World_Hitbox *boxes = (World_Hitbox *) base;
world->hitboxCount = n_hitbox;
world->hitboxes = M_ArenaPush(arena, World_Hitbox, .count = WORLD_HITBOX_MAX);
M_CopySize(world->hitboxes, boxes, n_hitbox * sizeof(World_Hitbox));
base += (WORLD_HITBOX_MAX * sizeof(World_Hitbox));
}
// Map
//
{
U32 *__map = (U32 *) base;
world->map = M_ArenaPush(arena, U32, .count = WORLD_MAP_MAX);
M_CopySize(world->map, __map, WORLD_MAP_MAX * sizeof(U32));
base += (WORLD_MAP_MAX * sizeof(U32));
}
{
U32 n_portals = *(U32 *) base; base += 4;
World_Portal *portals = (World_Portal *) base;
world->portalCount = n_portals;
world->portals = M_ArenaPush(arena, World_Portal, .count = WORLD_PORTAL_MAX);
M_CopySize(world->portals, portals, n_portals * sizeof(World_Portal));
base += (WORLD_PORTAL_MAX * sizeof(World_Portal));
}
if (base != (data.data + data.count)) {
printf("--- OFFSET MISMATCH ---\n");
}
printf("loaded world\n");
printf(" - %d props\n", world->propCount);
printf(" - %d hitboxes\n", world->hitboxCount);
printf("--- Loaded World ---\n");
}
} }
void GenerateNavMesh(M_Arena *arena, World *world) { void GenerateNavMesh(M_Arena *arena, World *world) {
@@ -182,29 +236,69 @@ void GenerateNavMesh(M_Arena *arena, World *world) {
for(int i = 0; i < WORLD_MAP_MAX; i++) { for(int i = 0; i < WORLD_MAP_MAX; i++) {
U32 x = (i % 96); U32 x = (i % 96);
U32 y = (i / 96); U32 y = (i / 96);
world->navMesh->nodes[world->navMesh->nodeCount].pos.x = x; bool skip = false;
world->navMesh->nodes[world->navMesh->nodeCount].pos.y = y; for(U32 hi = 0; hi < world->hitboxCount; hi++) {
U32 cost = 20; if(AABB_Point(world->hitboxes[hi].box, V2F((F32) x, (F32) y))) {
if(Str8_Equal(world->tileTypes[world->map[i]].tag, S("path_middle"), STR8_EQUAL_IGNORE_CASE)) { skip = true;
cost = 10; break;
}
} }
if(skip) {continue;}
world->navMesh->nodes[world->navMesh->nodeCount].pos.x = cast(F32) x;
world->navMesh->nodes[world->navMesh->nodeCount].pos.y = cast(F32) y;
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;
} }
if(y+ny < 0 || y+ny > 49) { if(y+ny < 0 || y+ny > 49) {
continue; continue;
} }
U32 index = x+nx + (y+ny)*96; // It's quad for loop time :D
for(U32 hi = 0; hi < world->hitboxCount; hi++) {
if(AABB_Point(world->hitboxes[hi].box, V2F((F32) (x + nx), (F32) (y + ny)))) {
skip = true;
break;
}
}
U32 nCount = world->navMesh->nodeCount; U32 nCount = world->navMesh->nodeCount;
S32 index = -1;
for (U32 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 > (S32) 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(U32 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;
}
} }
+7 -2
View File
@@ -46,10 +46,15 @@ struct NPC {
U32 targetNavNode; U32 targetNavNode;
// How long the npc has been walking to the next index // How long the npc has been walking to the next index
F32 walkTimer; F32 walkTimer;
// Space within you can interact with the NPC.
F32 interationRadius;
//// Knowledge //// Knowledge
// What the NPC knows about the bandit. // What the NPC knows about the bandit.
NPC_LOOK banditKnowledge; NPC_LOOK banditKnowledge;
// NPC clothes
G_Outfit outfit;
// if the NPC has given info
bool infoGiven;
}; };
function void NPCDraw(D_Context *draw, NPC *npc);
#endif // LD_GAME_NPC_H_ #endif // LD_GAME_NPC_H_
+45
View File
@@ -0,0 +1,45 @@
#if !defined(LD_GAME_OUTFIT_H)
#define LD_GAME_OUTFIT_H
#define G_OUTFIT_COMPONENT_COUNT 8
global_var Str8 __outfit_names[] = {
Sl("npc_%s_base_%d"),
Sl("npc_%s_eyes_%d"),
Sl("npc_%s_face_%d"),
Sl("npc_%s_hair_%d"),
Sl("npc_%s_hat_%d"),
Sl("npc_%s_shirt_%d"),
Sl("npc_%s_shoes_%d"),
Sl("npc_%s_trousers_%d")};
global_var U32 __outfit_counts[] = {
2, // base
3, // eyes
5, // face
4, // hair
3, // hat
2, // shirt
1, // shoes
2 // trousers
};
global_var U32 __outfit_back_counts[] = {
2, // base
0, // eyes
3, // face
4, // hair
3, // hat
2, // shirt
1, // shoes
2 // trousers
};
StaticAssert(ArraySize(__outfit_names) == ArraySize(__outfit_counts));
#define OUTFIT_IMG(dir, n) D_ImageHandle(&game->draw, Sf(temp.arena, (const char *)__outfit_names[it].data, #dir, n))
function U32* GenOutfit(G_Outfit *o, World *world, G_State *game);
#endif // LD_GAME_OUTFIT_H
+11 -1
View File
@@ -5,6 +5,7 @@
#include "../core/macros.h" #include "../core/macros.h"
#include <SDL3/SDL_events.h> #include <SDL3/SDL_events.h>
#include "aabb.h"
#define PLAYER_SPEED 10.0f #define PLAYER_SPEED 10.0f
#define PLAYER_RELOAD_TIME 1.5f #define PLAYER_RELOAD_TIME 1.5f
@@ -23,15 +24,24 @@ typedef struct Player Player;
struct Player struct Player
{ {
World *world; World *world;
V2f pos; AABB collision;
World_Area currentArea; World_Area currentArea;
U32 bulletsLoaded; U32 bulletsLoaded;
ControlState controls; ControlState controls;
V2f shotPos; V2f shotPos;
G_Outfit outfit;
U32 health;
F32 reloadTimer; F32 reloadTimer;
U32 *knownDetails;
U32 detailCount;
}; };
function void PlayerInit(G_State *game, Player *player);
function void PlayerDraw(D_Context *draw, Player *player);
function void PlayerInput(SDL_Event *event, Player *player); function void PlayerInput(SDL_Event *event, Player *player);
function void PlayerUpdate(F32 delta, Player *player); function void PlayerUpdate(F32 delta, Player *player);
+22 -6
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,15 @@ struct World_Prop
V2f pos; V2f pos;
}; };
typedef struct World_Portal World_Portal;
struct World_Portal
{
AABB box;
World_Area area;
};
typedef World_Portal World_Hitbox;
typedef struct World World; typedef struct World World;
#include "player.h" #include "player.h"
#include "npc.h" #include "npc.h"
@@ -52,13 +63,18 @@ struct World {
//// Loaded from world file //// Loaded from world file
World_Tile *tileTypes; World_Tile *tileTypes;
World_PropType *propTypes; World_PropType *propTypes;
World_Prop *props; World_Prop *props;
AABB *hitboxes; World_Hitbox *hitboxes;
World_Portal *portals;
U32 *map; U32 *map;
bool showPoster;
U32 propCount; U32 propCount;
U32 portalCount;
U32 hitboxCount; U32 hitboxCount;
Audio_Context *audio;
U32 audio_handles[2];
//// Player //// Player
Player player; Player player;
@@ -82,7 +98,7 @@ function void UpdateNPC(F32 delta, NPC *npc, World *world);
function void UpdateBandit(F32 delta, Bandit *bandit, World *world); function void UpdateBandit(F32 delta, Bandit *bandit, World *world);
function void LoadWorld(M_Arena *arena, World *world); function void LoadWorld(M_Arena *arena, World *world);
function void SaveWorld(M_Arena *arena, World *world); function void SaveWorld(World *world);
function void GenerateNavMesh(M_Arena *arena, World *world); function void GenerateNavMesh(M_Arena *arena, World *world);
#endif // LD_GAME_WORLD_H_ #endif // LD_GAME_WORLD_H_
+43
View File
@@ -0,0 +1,43 @@
#if !defined(LD_OS_AUDIO_H_)
#define LD_OS_AUDIO_H_
typedef struct Audio_Data Audio_Data;
struct Audio_Data {
S16 *samples;
U32 n_frames;
};
typedef struct Audio_Track Audio_Track;
struct Audio_Track {
B32 playing;
Audio_Data *data;
F32 volume;
U32 n_played;
U32 next; // to play if playing, free otherwise
};
typedef struct Audio_Context Audio_Context;
struct Audio_Context {
SDL_AudioStream *stream;
U32 n_tracks;
Audio_Track tracks[16];
U32 head;
U32 free;
F32 volume;
U32 n_sounds;
Audio_Data *sounds;
};
function void Audio_Init(M_Arena *arena, Audio_Context *audio, F32 volume);
function U32 Audio_Play(Audio_Context *audio, U32 index);
function void Audio_ChangeVolume(Audio_Context *audio, U32 handle, F32 volume);
#endif // LD_OS_AUDIO_H_
+161 -1
View File
@@ -4,7 +4,6 @@
#include "impl/linux/core.c" #include "impl/linux/core.c"
#endif #endif
Str8 FS_ReadEntireFile(M_Arena *arena, Str8 path) { Str8 FS_ReadEntireFile(M_Arena *arena, Str8 path) {
Str8 result = { 0 }; Str8 result = { 0 };
@@ -19,3 +18,164 @@ Str8 FS_ReadEntireFile(M_Arena *arena, Str8 path) {
return result; return result;
} }
internal void SDL_SubmitAudio(void *user, SDL_AudioStream *stream, int needed, int total) {
Audio_Context *audio = cast(Audio_Context *) user;
(void) total;
M_TempScope(0, 0) {
U32 prev = 0;
U32 it = audio->head;
U32 n_samples = needed >> 1;
U32 n_frames = n_samples >> 1;
F32 *left_f32 = M_ArenaPush(temp.arena, F32, .count = n_frames);
F32 *right_f32 = M_ArenaPush(temp.arena, F32, .count = n_frames);
while (it != 0) {
Audio_Track *track = &audio->tracks[it];
U32 next = track->next;
F32 *l0 = left_f32;
F32 *r0 = right_f32;
Assert(track->playing);
U32 remain = track->data->n_frames - track->n_played;
U32 play = Min(n_frames, remain);
U32 off = track->n_played << 1; // played n frames, thus double to n samples
for (U32 f = 0; f < play; ++f) {
*l0++ += cast(F32) (track->data->samples[off + (2 * f) + 0]) * track->volume * audio->volume;
*r0++ += cast(F32) (track->data->samples[off + (2 * f) + 1]) * track->volume * audio->volume;
}
track->n_played += play;
if (track->n_played == track->data->n_frames) {
if (prev == 0) {
// Head has finished playing
//
audio->head = track->next;
track->next = audio->free;
audio->free = it;
}
else {
// We're in the middle somewhere so prev->next == it->next
//
audio->tracks[prev].next = track->next;
track->next = audio->free;
audio->free = it;
}
track->playing = false;
}
it = next;
}
F32 *l0 = left_f32;
F32 *r0 = right_f32;
S16 *interleaved = M_ArenaPush(temp.arena, S16, .count = n_samples);
S16 *i0 = interleaved;
for (U32 n = 0; n < n_frames; ++n) {
*i0++ = cast(S16) (l0[n] + 0.5f);
*i0++ = cast(S16) (r0[n] + 0.5f);
}
SDL_PutAudioStreamData(stream, interleaved, needed);
}
}
void Audio_Init(M_Arena *arena, Audio_Context *audio, F32 volume) {
SDL_AudioSpec spec = { 0 };
spec.format = SDL_AUDIO_S16LE;
spec.channels = 2;
spec.freq = 44100;
audio->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, SDL_SubmitAudio, audio);
if (audio->stream) {
M_TempScope(0, 0) {
Str8 exec = FS_SystemPath(temp.arena, FS_SYSTEM_PATH_EXE);
Str8 path = Sf(temp.arena, "%.*s/assets", Sv(exec));
FS_List files = FS_PathList(temp.arena, path);
U32 n_audio = 0;
for (FS_Entry *it = files.first; it != 0; it = it->next) {
if (Str8_EndsWith(it->basename, S("wav"))) {
n_audio += 1;
}
}
audio->n_sounds = 0;
audio->sounds = M_ArenaPush(arena, Audio_Data, .count = n_audio);
for (FS_Entry *it = files.first; it != 0; it = it->next) {
if (Str8_EndsWith(it->basename, S("wav"))) {
Audio_Data *sound = &audio->sounds[audio->n_sounds];
U8 *data;
U32 count;
SDL_AudioSpec wav;
SDL_LoadWAV((const char *) it->path.data, &wav, &data, &count);
Assert(wav.freq == 44100);
sound->samples = (S16 *) data;
sound->n_frames = (count >> 2);
audio->n_sounds += 1;
}
}
audio->n_tracks = 16;
for (U32 it = 1; it < audio->n_tracks; ++it) {
audio->tracks[it].next = it + 1;
}
audio->tracks[audio->n_tracks - 1].next = 0;
audio->volume = volume;
audio->head = 0;
audio->free = 1;
printf("--- Loaded %d sounds ---\n", audio->n_sounds);
}
}
}
U32 Audio_Play(Audio_Context *audio, U32 index) {
U32 result = 0;
if (audio->free != 0 && SDL_LockAudioStream(audio->stream)) {
result = audio->free;
Audio_Track *track = &audio->tracks[result];
audio->free = track->next;
track->playing = true;
track->data = &audio->sounds[index];
track->n_played = 0;
track->volume = 1.0f;
track->next = audio->head;
audio->head = result;
SDL_UnlockAudioStream(audio->stream);
}
return result;
}
void Audio_ChangeVolume(Audio_Context *audio, U32 handle, F32 volume) {
if (SDL_LockAudioStream(audio->stream)) {
audio->tracks[handle].volume = volume;
SDL_UnlockAudioStream(audio->stream);
}
}
+1
View File
@@ -17,5 +17,6 @@ function void VM_Decommit(void *base, U64 size);
function void VM_Release(void *base, U64 size); function void VM_Release(void *base, U64 size);
#include "filesystem.h" #include "filesystem.h"
#include "audio.h"
#endif // LD_OS_CORE_H_ #endif // LD_OS_CORE_H_
BIN
View File
Binary file not shown.