npc and bandit dressup, bits of UI added, WIP NPC interaction

This commit is contained in:
2025-10-06 23:08:15 +01:00
parent f18d9d2b0e
commit 7f77d7ad52
13 changed files with 166 additions and 58 deletions

BIN
assets/heart.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 289 B

BIN
assets/poster.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@@ -24,6 +24,7 @@
#include "game/impl/world.c"
#include "game/impl/npc.c"
#include "game/impl/bandit.c"
#include "game/impl/outfit.c"
int main(int argc, char **argv)
{
@@ -101,7 +102,7 @@ int main(int argc, char **argv)
game->world = world;
world->arena = arena;
world->random = Random_Seed(29237489723847);
world->npcCount = 127;
world->npcCount = 12;
for(U32 i = 0; i < world->npcCount; i++) {
NPC *npc1 = &world->npcs[i];
npc1->collision.pos.x = 0;
@@ -114,6 +115,7 @@ int main(int argc, char **argv)
npc1->waitTime = 0;
npc1->maxWaitTime = 1;
npc1->currentNavNode = 0;
GenOutfit(&npc1->outfit,world,game);
}
Bandit *badman = &world->bandit;
@@ -127,7 +129,7 @@ int main(int argc, char **argv)
badman->maxWaitTime = 2;
badman->poiCount = 2;
badman->shootoutTimer = 1.5;
badman->agroRadius = 600.0;
badman->agroRadius = 6.0;
badman->bullets = 6;
badman->shootDelay = 1;
badman->accuracyRange = 0.25;
@@ -135,6 +137,8 @@ int main(int argc, char **argv)
badman->reloadTimer = 0;
badman->pointsOfInterest[0] = 937;
badman->pointsOfInterest[1] = 12;
badman->outfitChoices = GenOutfit(&badman->outfit, world, game);
badman->currentArea = WORLD_AREA_OUTSIDE;
world->npcPOI[0] = 100;
@@ -247,6 +251,7 @@ int main(int argc, char **argv)
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;
}
}
@@ -420,6 +425,12 @@ int main(int argc, char **argv)
D_Begin(&game->draw, frame, D_MAX_RECTS);
RenderWorld(game->world, &game->draw);
if(game->world->showPoster){
D_Rect(&game->draw, G_CameraBounds(&game->camera).min.x+4.8, G_CameraBounds(&game->camera).min.y+6.4, .texture=D_ImageHandle(&game->draw, S("poster")), .scale=12.0);
}
for(int i = 0; i < game->world->player.health; i++){
D_Rect(&game->draw, (G_CameraBounds(&game->camera).max.x-3)+i, G_CameraBounds(&game->camera).min.y+1.0, .texture=D_ImageHandle(&game->draw, S("heart")), .scale=1.0);
}
//D_Text(&game->draw, game->draw.fonts, S("Small Test"), 0, 0);

View File

@@ -1,5 +1,6 @@
#if !defined(LD_GAME_BANDIT_H_)
#define LD_GAME_BANDIT_H_
#include "outfit.h"
typedef enum BANDIT_ACTION BANDIT_ACTION;
enum BANDIT_ACTION
@@ -62,8 +63,12 @@ struct Bandit {
F32 accuracyRange;
// A the circle around the bandit where they will trigger the quicktime reaction scene
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 V2f ShootTowards(Bandit* bandit, V2f target, Random* r);
function void BanditDraw(D_Context *draw, Bandit *bandit);
#endif // LD_GAME_BANDIT_H_

View File

@@ -1,7 +1,7 @@
#include "game/world.h"
#include "game/bandit.h"
V2f shootTowards(Bandit *bandit, V2f target, Random* r)
V2f ShootTowards(Bandit *bandit, V2f target, Random* r)
{
V2f shooterV2 = bandit->collision.pos;
F32 randX = Random_F32(r, -bandit->accuracyRange, bandit->accuracyRange);
@@ -71,17 +71,17 @@ void UpdateBandit(F32 delta, Bandit *bandit, World *world) {
bandit->reloadTimer -= delta;
if (bandit->shootCooldownTimer < 0 && bandit->reloadTimer < 0)
{
printf("shoot at player\n");
printf("\nshoot at player");
bandit->bullets--;
bandit->shootCooldownTimer = bandit->shootDelay;
V2f banditShot = shootTowards(bandit, world->player.collision.pos, &world->random);
V2f banditShot = ShootTowards(bandit, world->player.collision.pos, &world->random);
if(AABB_Slab(bandit->collision.pos, banditShot, world->player.collision)){
// gets shot lmao
printf("hit\n");
printf("\nhit");
world->player.health--;
}
if(bandit->bullets == 0){
printf("enemy reload\n");
printf("\nenemy reload");
bandit->bullets = 6;
bandit->reloadTimer = bandit->reloadTime;
}
@@ -90,3 +90,23 @@ void UpdateBandit(F32 delta, Bandit *bandit, World *world) {
// 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);
}
}
}

View File

@@ -46,3 +46,23 @@ void UpdateNPC(F32 delta, NPC *npc, World *world) {
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
code/game/impl/outfit.c Normal file
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;
}

View File

@@ -2,47 +2,9 @@
#include <SDL3/SDL_events.h>
#include <SDL3/SDL_keycode.h>
#include <stdio.h>
#include "outfit.h"
#include <npc.h>
// @Todo: move/extern these so the npc/bandit can use them
//
#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))
void PlayerInit(G_State *game, Player *player) {
World *world = game->world;
@@ -105,6 +67,16 @@ void PlayerInput(SDL_Event *event, Player *player)
player->controls.downDown = val;
break;
}
case SDLK_E:
{
for(int i = 0; player->world->npmCount; 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 (

View File

@@ -67,15 +67,11 @@ void RenderWorld(World *world, D_Context *draw) {
for(U32 i = 0; i < world->npcCount; i++) {
NPC npc = world->npcs[i];
if(npc.currentArea == world->player.currentArea) {
V2f drawPos = AABB_Centre(npc.collision);
D_Rect(draw, drawPos.x, drawPos.y, .texture = 1);
NPCDraw(draw, &world->npcs[i]);
}
}
if(world->bandit.currentArea == world->player.currentArea) {
V2f drawPos = AABB_Centre(world->bandit.collision);
D_Rect(draw, drawPos.x, drawPos.y, .texture = 9);
}
BanditDraw(draw, &world->bandit);
PlayerDraw(draw, &world->player);
}

View File

@@ -46,10 +46,15 @@ struct NPC {
U32 targetNavNode;
// How long the npc has been walking to the next index
F32 walkTimer;
// Space within you can interact with the NPC.
F32 interationRadius;
//// Knowledge
// What the NPC knows about the bandit.
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_

45
code/game/outfit.h Normal file
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

View File

@@ -34,6 +34,9 @@ struct Player
U32 health;
F32 reloadTimer;
U32 *knownDetails;
U32 detailCount;
};
function void PlayerInit(G_State *game, Player *player);

View File

@@ -67,7 +67,7 @@ struct World {
World_Hitbox *hitboxes;
World_Portal *portals;
U32 *map;
bool showPoster;
U32 propCount;
U32 portalCount;
U32 hitboxCount;