entity dir

This commit is contained in:
2025-09-17 18:38:14 -05:00
parent f799690d3c
commit 08221af3f8
16 changed files with 369 additions and 58 deletions

View File

@@ -9,4 +9,5 @@ target_sources(${DUSK_TARGET_NAME}
entity.c
npc.c
player.c
direction.c
)

View File

@@ -0,0 +1,53 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#include "direction.h"
#include "assert/assert.h"
float_t directionToAngle(const direction_t dir) {
switch(dir) {
case DIRECTION_NORTH: return (M_PI_2);
case DIRECTION_SOUTH: return -(M_PI_2);
case DIRECTION_EAST: return 0;
case DIRECTION_WEST: return (M_PI);
default: return 0; // Should never happen
}
}
void directionGetCoordinates(
const direction_t dir,
int8_t *x, int8_t *y
) {
assertNotNull(x, "X coordinate pointer cannot be NULL");
assertNotNull(y, "Y coordinate pointer cannot be NULL");
switch(dir) {
case DIRECTION_NORTH:
*x = 0;
*y = -1;
break;
case DIRECTION_SOUTH:
*x = 0;
*y = 1;
break;
case DIRECTION_EAST:
*x = 1;
*y = 0;
break;
case DIRECTION_WEST:
*x = -1;
*y = 0;
break;
default:
assertUnreachable("Invalid direction");
break;
}
}

View File

@@ -0,0 +1,41 @@
/**
* Copyright (c) 2025 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef enum {
DIRECTION_SOUTH = 0,
DIRECTION_EAST = 1,
DIRECTION_WEST = 2,
DIRECTION_NORTH = 3,
DIRECTION_UP = DIRECTION_NORTH,
DIRECTION_DOWN = DIRECTION_SOUTH,
DIRECTION_LEFT = DIRECTION_WEST,
DIRECTION_RIGHT = DIRECTION_EAST,
} direction_t;
/**
* Converts a direction to an angle in float_t format.
*
* @param dir The direction to convert.
* @return The angle corresponding to the direction.
*/
float_t directionToAngle(const direction_t dir);
/**
* Gets the relative coordinates for a given direction.
*
* @param dir The direction to get coordinates for.
* @param x Pointer to store the x coordinate.
* @param y Pointer to store the y coordinate.
*/
void directionGetCoordinates(
const direction_t dir,
int8_t *x, int8_t *y
);

View File

@@ -6,33 +6,98 @@
*/
#include "entity.h"
#include "rpg/world/map.h"
#include "assert/assert.h"
#include "util/memory.h"
#include "display/tileset/tileset_entities.h"
#include "time/time.h"
#include "util/math.h"
entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT] = {
{ NULL, NULL }, // ENTITY_TYPE_NULL
{ playerInit, playerUpdate }, // ENTITY_TYPE_PLAYER
{ npcInit, npcUpdate}, // ENTITY_TYPE_NPC
};
void entityInit(entity_t *entity, const entitytype_t type) {
void entityInit(entity_t *entity, const entitytype_t type, map_t *map) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertNotNull(map, "Map pointer cannot be NULL");
assertTrue(type < ENTITY_TYPE_COUNT, "Invalid entity type");
assertTrue(type != ENTITY_TYPE_NULL, "Cannot have NULL entity type");
assertNotNull(ENTITY_CALLBACKS[type].init, "Entity type has no init function");
memoryZero(entity, sizeof(entity_t));
entity->type = type;
entity->map = map;
ENTITY_CALLBACKS[type].init(entity);
// Init. I did use a callback struct but it was not flexible enough.
switch(type) {
case ENTITY_TYPE_PLAYER:
playerInit(entity);
break;
case ENTITY_TYPE_NPC:
npcInit(entity);
break;
default:
break;
}
}
void entityUpdate(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type");
assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type");
assertNotNull(ENTITY_CALLBACKS[entity->type].update, "enttype lacks update");
ENTITY_CALLBACKS[entity->type].update(entity);
// Handle movement logic
switch(entity->type) {
case ENTITY_TYPE_PLAYER:
playerMovement(entity);
break;
case ENTITY_TYPE_NPC:
npcUpdate(entity);
break;
default:
break;
}
// Apply velocity
if(entity->velocity[0] != 0.0f || entity->velocity[1] != 0.0f) {
entity->position[0] += entity->velocity[0] * TIME.delta;
entity->position[1] += entity->velocity[1] * TIME.delta;
// Hit test on other entities.
entity_t *start = entity->map->entities;
entity_t *end = &entity->map->entities[entity->map->entityCount];
// Our hitbox
physicscircle_t self;
glm_vec2_copy(entity->position, self.position);
self.radius = TILESET_ENTITIES.tileWidth / 2.0f;
physicscircle_t other;
other.radius = self.radius;
// TODO: what if multiple collisions?
do {
if(start == entity) continue;
if(start->type == ENTITY_TYPE_NULL) continue;
glm_vec2_copy(start->position, other.position);
physicscirclecircleresult_t result;
physicsCircleCheckCircle(self, other, &result);
if(result.hit) {
entity->position[0] -= result.normal[0] * result.depth;
entity->position[1] -= result.normal[1] * result.depth;
break;
}
} while((start++) != end);
// Friction (and dampening)
entity->velocity[0] *= ENTITY_FRICTION * TIME.delta;
entity->velocity[1] *= ENTITY_FRICTION * TIME.delta;
if(mathAbs(entity->velocity[0]) < ENTITY_MIN_VELOCITY) {
entity->velocity[0] = 0.0f;
}
if(mathAbs(entity->velocity[1]) < ENTITY_MIN_VELOCITY) {
entity->velocity[1] = 0.0f;
}
}
}

View File

@@ -6,15 +6,15 @@
*/
#pragma once
// #include "direction.h"
#include "direction.h"
#include "rpg/entity/player.h"
#include "npc.h"
#include "physics/physics.h"
typedef struct {
void (*init)(entity_t *entity);
void (*update)(entity_t *entity);
} entitycallback_t;
#define ENTITY_FRICTION 0.9f
#define ENTITY_MIN_VELOCITY 0.05f
typedef struct map_s map_t;
typedef enum {
ENTITY_TYPE_NULL,
@@ -25,9 +25,9 @@ typedef enum {
} entitytype_t;
typedef struct entity_s {
// uint32_t id;// Completely unique ID for this entity.
map_t *map;
entitytype_t type;
// direction_t dir;
direction_t direction;
vec2 position;
vec2 velocity;
@@ -38,15 +38,14 @@ typedef struct entity_s {
};
} entity_t;
extern entitycallback_t ENTITY_CALLBACKS[ENTITY_TYPE_COUNT];
/**
* Initializes an entity structure.
*
* @param entity Pointer to the entity structure to initialize.
* @param type The type of the entity.
* @param map Pointer to the map the entity belongs to.
*/
void entityInit(entity_t *entity, const entitytype_t type);
void entityInit(entity_t *entity, const entitytype_t type, map_t *map);
/**
* Updates an entity.

View File

@@ -7,9 +7,7 @@
#include "entity.h"
#include "assert/assert.h"
#include "time/time.h"
#include "input/input.h"
#include "display/scene/overworld/sceneoverworld.h"
#include "display/tileset/tileset_entities.h"
@@ -17,36 +15,48 @@ void playerInit(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
}
void playerUpdate(entity_t *entity) {
void playerMovement(entity_t *entity) {
assertNotNull(entity, "Entity pointer cannot be NULL");
// testing only
float_t move = TIME.delta * 64.0f; // tiles per second
// Update velocity.
vec2 dir = {
inputAxis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT),
inputAxis(INPUT_ACTION_UP, INPUT_ACTION_DOWN)
inputAxis(INPUT_ACTION_DOWN, INPUT_ACTION_UP)
};
if(dir[0] == 0 && dir[1] == 0) return;
glm_vec2_normalize(dir);
entity->position[0] += move * dir[0];
entity->position[1] -= move * dir[1];
SCENE_OVERWORLD.camera.lookat.target[0] = entity->position[0];
SCENE_OVERWORLD.camera.lookat.target[1] = entity->position[1];
// Can we move?
physicscircle_t npc = {
.position = { 32.0f, 32.0f },
.radius = TILESET_ENTITIES.tileWidth / 2.0f
};
physicscircle_t self;
glm_vec2_copy(entity->position, self.position);
self.radius = npc.radius;
physicscirclecircleresult_t result;
physicsCircleCheckCircle(self, npc, &result);
if(result.hit) {
entity->position[0] -= result.normal[0] * result.depth;
entity->position[1] -= result.normal[1] * result.depth;
entity->velocity[0] += PLAYER_SPEED * dir[0];
entity->velocity[1] += PLAYER_SPEED * dir[1];
// Update direction.
if(dir[0] > 0) {
if(entity->direction == DIRECTION_RIGHT) {
entity->direction = DIRECTION_RIGHT;
} else {
if(dir[1] < 0) {
entity->direction = DIRECTION_UP;
} else if(dir[1] > 0) {
entity->direction = DIRECTION_DOWN;
} else {
entity->direction = DIRECTION_RIGHT;
}
}
} else if(dir[0] < 0) {
if(entity->direction == DIRECTION_LEFT) {
entity->direction = DIRECTION_LEFT;
} else {
if(dir[1] < 0) {
entity->direction = DIRECTION_UP;
} else if(dir[1] > 0) {
entity->direction = DIRECTION_DOWN;
} else {
entity->direction = DIRECTION_LEFT;
}
}
} else if(dir[1] < 0) {
entity->direction = DIRECTION_UP;
} else if(dir[1] > 0) {
entity->direction = DIRECTION_DOWN;
}
}

View File

@@ -8,6 +8,8 @@
#pragma once
#include "dusk.h"
#define PLAYER_SPEED 64.0f
typedef struct entity_s entity_t;
typedef struct {
@@ -22,8 +24,8 @@ typedef struct {
void playerInit(entity_t *entity);
/**
* Updates a player entity.
* Handles movement logic for the player entity.
*
* @param entity Pointer to the entity structure to update.
* @param entity Pointer to the player entity structure.
*/
void playerUpdate(entity_t *entity);
void playerMovement(entity_t *entity);