Entity
This commit is contained in:
@@ -35,7 +35,6 @@ add_subdirectory(engine)
|
|||||||
add_subdirectory(error)
|
add_subdirectory(error)
|
||||||
add_subdirectory(input)
|
add_subdirectory(input)
|
||||||
# add_subdirectory(locale)
|
# add_subdirectory(locale)
|
||||||
add_subdirectory(physics)
|
|
||||||
add_subdirectory(rpg)
|
add_subdirectory(rpg)
|
||||||
add_subdirectory(scene)
|
add_subdirectory(scene)
|
||||||
add_subdirectory(thread)
|
add_subdirectory(thread)
|
||||||
|
|||||||
@@ -7,7 +7,8 @@
|
|||||||
target_sources(${DUSK_TARGET_NAME}
|
target_sources(${DUSK_TARGET_NAME}
|
||||||
PRIVATE
|
PRIVATE
|
||||||
entity.c
|
entity.c
|
||||||
|
entityanim.c
|
||||||
npc.c
|
npc.c
|
||||||
player.c
|
player.c
|
||||||
direction.c
|
entitydir.c
|
||||||
)
|
)
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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 directionGetVec2(const direction_t dir, vec2 out) {
|
|
||||||
assertNotNull(out, "Output vector cannot be NULL");
|
|
||||||
|
|
||||||
switch(dir) {
|
|
||||||
case DIRECTION_NORTH:
|
|
||||||
out[0] = 0.0f;
|
|
||||||
out[1] = 1.0f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DIRECTION_SOUTH:
|
|
||||||
out[0] = 0.0f;
|
|
||||||
out[1] = -1.0f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DIRECTION_EAST:
|
|
||||||
out[0] = 1.0f;
|
|
||||||
out[1] = 0.0f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DIRECTION_WEST:
|
|
||||||
out[0] = -1.0f;
|
|
||||||
out[1] = 0.0f;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
assertUnreachable("Invalid direction");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a direction to a vec2 unit vector.
|
|
||||||
*
|
|
||||||
* @param dir The direction to convert.
|
|
||||||
* @param out Pointer to the vec2 array to populate.
|
|
||||||
*/
|
|
||||||
void directionGetVec2(const direction_t dir, vec2 out);
|
|
||||||
@@ -46,8 +46,66 @@ void entityUpdate(entity_t *entity) {
|
|||||||
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type");
|
assertTrue(entity->type < ENTITY_TYPE_COUNT, "Invalid entity type");
|
||||||
assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type");
|
assertTrue(entity->type != ENTITY_TYPE_NULL, "Cannot have NULL entity type");
|
||||||
|
|
||||||
|
// What state is the entity in?
|
||||||
|
if(entity->animation != ENTITY_ANIM_IDLE) {
|
||||||
|
// Entity is mid animation, tick it (down).
|
||||||
|
entity->animFrame--;
|
||||||
|
if(entity->animFrame == 0) entity->animation = ENTITY_ANIM_IDLE;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Movement code.
|
// Movement code.
|
||||||
if(entity->type == ENTITY_TYPE_PLAYER) {
|
if(entity->type == ENTITY_TYPE_PLAYER) {
|
||||||
playerMovement(entity);
|
playerMovement(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void entityTurn(entity_t *entity, const entitydir_t direction) {
|
||||||
|
entity->direction = direction;
|
||||||
|
entity->animation = ENTITY_ANIM_TURN;
|
||||||
|
entity->animFrame = ENTITY_ANIM_TURN_DURATION;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void entityWalk(entity_t *entity, const entitydir_t direction) {
|
||||||
|
// TODO: Animation, delay, etc.
|
||||||
|
entity->direction = direction;
|
||||||
|
|
||||||
|
// Where are we moving?
|
||||||
|
uint8_t newX, newY;
|
||||||
|
newX = entity->position.x;
|
||||||
|
newY = entity->position.y;
|
||||||
|
{
|
||||||
|
int8_t relX, relY;
|
||||||
|
entityDirGetRelative(direction, &relX, &relY);
|
||||||
|
newX += relX;
|
||||||
|
newY += relY;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Tile in way?
|
||||||
|
// TODO: Map bounds in way?
|
||||||
|
|
||||||
|
// Entity in way?
|
||||||
|
// entity_t *start = GAME.overworld.map.entities;
|
||||||
|
// entity_t *end = start + MAP_ENTITY_COUNT;
|
||||||
|
// while(start < end) {
|
||||||
|
// if(
|
||||||
|
// start == entity ||
|
||||||
|
// entity->type == ENTITY_TYPE_NULL ||
|
||||||
|
// start->position.x != newX ||
|
||||||
|
// start->position.y != newY
|
||||||
|
// ) {
|
||||||
|
// start++;
|
||||||
|
// continue;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return;// Blocked
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Move.
|
||||||
|
entity->position.x = newX;
|
||||||
|
entity->position.y = newY;
|
||||||
|
|
||||||
|
entity->animation = ENTITY_ANIM_WALK;
|
||||||
|
entity->animFrame = ENTITY_ANIM_WALK_DURATION;// TODO: Running vs walking
|
||||||
|
}
|
||||||
@@ -6,9 +6,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "direction.h"
|
#include "entitydir.h"
|
||||||
|
#include "entityanim.h"
|
||||||
#include "rpg/entity/player.h"
|
#include "rpg/entity/player.h"
|
||||||
#include "npc.h"
|
#include "npc.h"
|
||||||
|
#include "rpg/world/worldpos.h"
|
||||||
|
|
||||||
#define ENTITY_COUNT 256
|
#define ENTITY_COUNT 256
|
||||||
|
|
||||||
@@ -18,15 +20,19 @@ typedef enum {
|
|||||||
ENTITY_TYPE_NULL,
|
ENTITY_TYPE_NULL,
|
||||||
ENTITY_TYPE_PLAYER,
|
ENTITY_TYPE_PLAYER,
|
||||||
ENTITY_TYPE_NPC,
|
ENTITY_TYPE_NPC,
|
||||||
|
|
||||||
ENTITY_TYPE_COUNT
|
ENTITY_TYPE_COUNT
|
||||||
} entitytype_t;
|
} entitytype_t;
|
||||||
|
|
||||||
typedef struct entity_s {
|
typedef struct entity_s {
|
||||||
uint8_t id;
|
uint8_t id;
|
||||||
entitytype_t type;
|
entitytype_t type;
|
||||||
direction_t direction;
|
|
||||||
uint32_t position;// Tile index
|
// Movement
|
||||||
|
entitydir_t direction;
|
||||||
|
worldpos_t position;
|
||||||
|
|
||||||
|
entityanim_t animation;
|
||||||
|
uint8_t animFrame;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
player_t player;
|
player_t player;
|
||||||
@@ -50,3 +56,19 @@ void entityInit(entity_t *entity, const entitytype_t type);
|
|||||||
* @param entity Pointer to the entity structure to update.
|
* @param entity Pointer to the entity structure to update.
|
||||||
*/
|
*/
|
||||||
void entityUpdate(entity_t *entity);
|
void entityUpdate(entity_t *entity);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn an entity to face a new direction.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to turn.
|
||||||
|
* @param direction The direction to face.
|
||||||
|
*/
|
||||||
|
void entityTurn(entity_t *entity, const entitydir_t direction);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make an entity walk in a direction.
|
||||||
|
*
|
||||||
|
* @param entity Pointer to the entity to make walk.
|
||||||
|
* @param direction The direction to walk in.
|
||||||
|
*/
|
||||||
|
void entityWalk(entity_t *entity, const entitydir_t direction);
|
||||||
|
|||||||
9
src/rpg/entity/entityanim.c
Normal file
9
src/rpg/entity/entityanim.c
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "entityanim.h"
|
||||||
|
|
||||||
18
src/rpg/entity/entityanim.h
Normal file
18
src/rpg/entity/entityanim.h
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
#define ENTITY_ANIM_TURN_DURATION 1
|
||||||
|
#define ENTITY_ANIM_WALK_DURATION 10
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ENTITY_ANIM_IDLE,
|
||||||
|
ENTITY_ANIM_TURN,
|
||||||
|
ENTITY_ANIM_WALK,
|
||||||
|
} entityanim_t;
|
||||||
37
src/rpg/entity/entitydir.c
Normal file
37
src/rpg/entity/entitydir.c
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "entitydir.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
|
||||||
|
void entityDirGetRelative(const entitydir_t from, int8_t *outX, int8_t *outY) {
|
||||||
|
assertValidEntityDir(from, "Invalid direction provided");
|
||||||
|
assertNotNull(outX, "Output X pointer cannot be NULL");
|
||||||
|
assertNotNull(outY, "Output Y pointer cannot be NULL");
|
||||||
|
|
||||||
|
switch(from) {
|
||||||
|
case ENTITY_DIR_NORTH:
|
||||||
|
*outX = 0;
|
||||||
|
*outY = -1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENTITY_DIR_EAST:
|
||||||
|
*outX = 1;
|
||||||
|
*outY = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENTITY_DIR_SOUTH:
|
||||||
|
*outX = 0;
|
||||||
|
*outY = 1;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ENTITY_DIR_WEST:
|
||||||
|
*outX = -1;
|
||||||
|
*outY = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/rpg/entity/entitydir.h
Normal file
45
src/rpg/entity/entitydir.h
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
ENTITY_DIR_SOUTH = 0,
|
||||||
|
ENTITY_DIR_EAST = 1,
|
||||||
|
ENTITY_DIR_WEST = 2,
|
||||||
|
ENTITY_DIR_NORTH = 3,
|
||||||
|
|
||||||
|
ENTITY_DIR_UP = ENTITY_DIR_NORTH,
|
||||||
|
ENTITY_DIR_DOWN = ENTITY_DIR_SOUTH,
|
||||||
|
ENTITY_DIR_LEFT = ENTITY_DIR_WEST,
|
||||||
|
ENTITY_DIR_RIGHT = ENTITY_DIR_EAST,
|
||||||
|
} entitydir_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Asserts a given direction is valid.
|
||||||
|
*
|
||||||
|
* @param dir The direction to validate.
|
||||||
|
* @param msg The message to display if the assertion fails.
|
||||||
|
*/
|
||||||
|
#define assertValidEntityDir(dir, msg) \
|
||||||
|
assertTrue( \
|
||||||
|
(dir) == ENTITY_DIR_NORTH || \
|
||||||
|
(dir) == ENTITY_DIR_EAST || \
|
||||||
|
(dir) == ENTITY_DIR_SOUTH || \
|
||||||
|
(dir) == ENTITY_DIR_WEST, \
|
||||||
|
msg \
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the relative x and y offsets for a given direction.
|
||||||
|
*
|
||||||
|
* @param dir The direction to get offsets for.
|
||||||
|
* @param relX Pointer to store the relative x offset.
|
||||||
|
* @param relY Pointer to store the relative y offset.
|
||||||
|
*/
|
||||||
|
void entityDirGetRelative(const entitydir_t dir, int8_t *relX, int8_t *relY);
|
||||||
@@ -12,6 +12,20 @@
|
|||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
#include "time/time.h"
|
#include "time/time.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
inputaction_t action;
|
||||||
|
entitydir_t direction;
|
||||||
|
} playerinputdirmap_t;
|
||||||
|
|
||||||
|
static const playerinputdirmap_t PLAYER_INPUT_DIR_MAP[] = {
|
||||||
|
{ INPUT_ACTION_UP, ENTITY_DIR_NORTH },
|
||||||
|
{ INPUT_ACTION_DOWN, ENTITY_DIR_SOUTH },
|
||||||
|
{ INPUT_ACTION_LEFT, ENTITY_DIR_WEST },
|
||||||
|
{ INPUT_ACTION_RIGHT, ENTITY_DIR_EAST },
|
||||||
|
|
||||||
|
{ 0xFF, 0xFF }
|
||||||
|
};
|
||||||
|
|
||||||
void playerInit(entity_t *entity) {
|
void playerInit(entity_t *entity) {
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
}
|
}
|
||||||
@@ -19,47 +33,23 @@ void playerInit(entity_t *entity) {
|
|||||||
void playerMovement(entity_t *entity) {
|
void playerMovement(entity_t *entity) {
|
||||||
assertNotNull(entity, "Entity pointer cannot be NULL");
|
assertNotNull(entity, "Entity pointer cannot be NULL");
|
||||||
|
|
||||||
// Get movement angle as 0-> normalized vector.
|
// Turn
|
||||||
vec2 dir = {
|
const playerinputdirmap_t *dirMap = PLAYER_INPUT_DIR_MAP;
|
||||||
inputAxis(INPUT_ACTION_LEFT, INPUT_ACTION_RIGHT),
|
do {
|
||||||
inputAxis(INPUT_ACTION_UP, INPUT_ACTION_DOWN)
|
if(!inputPressed(dirMap->action)) continue;
|
||||||
};
|
if(entity->direction == dirMap->direction) continue;
|
||||||
if(dir[0] == 0 && dir[1] == 0) return;
|
return entityTurn(entity, dirMap->direction);
|
||||||
glm_vec2_normalize(dir);
|
} while((++dirMap)->action != 0xFF);
|
||||||
|
|
||||||
entity->velocity[0] += PLAYER_SPEED * dir[0] * TIME.fixedDelta;
|
// Walk
|
||||||
entity->velocity[1] += PLAYER_SPEED * dir[1] * TIME.fixedDelta;
|
dirMap = PLAYER_INPUT_DIR_MAP;
|
||||||
|
do {
|
||||||
|
if(!inputIsDown(dirMap->action)) continue;
|
||||||
|
if(entity->direction != dirMap->direction) continue;
|
||||||
|
return entityWalk(entity, dirMap->direction);
|
||||||
|
} while((++dirMap)->action != 0xFF);
|
||||||
|
|
||||||
// Update direction.
|
// Interaction
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void playerInteraction(entity_t *entity) {
|
void playerInteraction(entity_t *entity) {
|
||||||
@@ -67,50 +57,4 @@ void playerInteraction(entity_t *entity) {
|
|||||||
|
|
||||||
if(!inputPressed(INPUT_ACTION_ACCEPT)) return;
|
if(!inputPressed(INPUT_ACTION_ACCEPT)) return;
|
||||||
|
|
||||||
physicsbox_t interactBox;
|
|
||||||
|
|
||||||
// Get direction vector
|
|
||||||
directionGetVec2(entity->direction, interactBox.min);
|
|
||||||
|
|
||||||
// Scale by interact range
|
|
||||||
glm_vec2_scale(interactBox.min, PLAYER_INTERACTION_RANGE, interactBox.min);
|
|
||||||
|
|
||||||
// Add entity position, this makes the center of the box.
|
|
||||||
glm_vec2_add(interactBox.min, entity->position, interactBox.min);
|
|
||||||
|
|
||||||
// Copy to max
|
|
||||||
glm_vec2_copy(interactBox.min, interactBox.max);
|
|
||||||
|
|
||||||
// // Size of the hitbox
|
|
||||||
// vec2 halfSize = {
|
|
||||||
// 1 * PLAYER_INTERACTION_SIZE * 0.5f,
|
|
||||||
// 1 * PLAYER_INTERACTION_SIZE * 0.5f
|
|
||||||
// };
|
|
||||||
|
|
||||||
// // Subtract from min, add to max.
|
|
||||||
// glm_vec2_sub(interactBox.min, halfSize, interactBox.min);
|
|
||||||
// glm_vec2_add(interactBox.max, halfSize, interactBox.max);
|
|
||||||
|
|
||||||
// // For each entity
|
|
||||||
// entity_t *start = ENTITIES;
|
|
||||||
// entity_t *end = &ENTITIES[ENTITY_COUNT];
|
|
||||||
// vec2 otherSize = { 1, 1 };
|
|
||||||
// physicsbox_t otherBox;
|
|
||||||
// physicsboxboxresult_t result;
|
|
||||||
|
|
||||||
// do {
|
|
||||||
// if(start->type != ENTITY_TYPE_NPC) continue;
|
|
||||||
|
|
||||||
// // Setup other box.
|
|
||||||
// glm_vec2_copy(start->position, otherBox.min);
|
|
||||||
// glm_vec2_copy(start->position, otherBox.max);
|
|
||||||
// glm_vec2_sub(otherBox.min, otherSize, otherBox.min);
|
|
||||||
// glm_vec2_add(otherBox.min, otherSize, otherBox.max);
|
|
||||||
|
|
||||||
// physicsBoxCheckBox(interactBox, otherBox, &result);
|
|
||||||
// if(!result.hit) continue;
|
|
||||||
|
|
||||||
// printf("Interacted with entity at (%.2f, %.2f)\n", start->position[0], start->position[1]);
|
|
||||||
// break;
|
|
||||||
// } while(++start != end);
|
|
||||||
}
|
}
|
||||||
@@ -27,11 +27,11 @@ errorret_t rpgInit(void) {
|
|||||||
entityInit(ent, ENTITY_TYPE_PLAYER);
|
entityInit(ent, ENTITY_TYPE_PLAYER);
|
||||||
RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY;
|
RPG_CAMERA.mode = RPG_CAMERA_MODE_FOLLOW_ENTITY;
|
||||||
RPG_CAMERA.followEntity.followEntityId = ent->id;
|
RPG_CAMERA.followEntity.followEntityId = ent->id;
|
||||||
ent->position[0] = 4, ent->position[1] = 4;
|
ent->position.x = 4, ent->position.y = 4;
|
||||||
|
|
||||||
ent = &ENTITIES[1];
|
ent = &ENTITIES[1];
|
||||||
entityInit(ent, ENTITY_TYPE_NPC);
|
entityInit(ent, ENTITY_TYPE_NPC);
|
||||||
ent->position[0] = 6, ent->position[1] = 6;
|
ent->position.x = 6, ent->position.y = 6;
|
||||||
|
|
||||||
// All Good!
|
// All Good!
|
||||||
errorOk();
|
errorOk();
|
||||||
@@ -51,9 +51,6 @@ void rpgUpdate(void) {
|
|||||||
if(ent->type == ENTITY_TYPE_NULL) continue;
|
if(ent->type == ENTITY_TYPE_NULL) continue;
|
||||||
entityUpdate(ent);
|
entityUpdate(ent);
|
||||||
} while(++ent < &ENTITIES[ENTITY_COUNT]);
|
} while(++ent < &ENTITIES[ENTITY_COUNT]);
|
||||||
|
|
||||||
// Update the camera.
|
|
||||||
rpgCameraUpdate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpgDispose(void) {
|
void rpgDispose(void) {
|
||||||
|
|||||||
@@ -14,19 +14,3 @@ rpgcamera_t RPG_CAMERA;
|
|||||||
void rpgCameraInit(void) {
|
void rpgCameraInit(void) {
|
||||||
memoryZero(&RPG_CAMERA, sizeof(rpgcamera_t));
|
memoryZero(&RPG_CAMERA, sizeof(rpgcamera_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpgCameraUpdate(void) {
|
|
||||||
switch(RPG_CAMERA.mode) {
|
|
||||||
case RPG_CAMERA_MODE_FOLLOW_ENTITY:
|
|
||||||
if(RPG_CAMERA.followEntity.followEntityId >= ENTITY_COUNT) break;
|
|
||||||
entity_t *ent = &ENTITIES[RPG_CAMERA.followEntity.followEntityId];
|
|
||||||
if(ent->type == ENTITY_TYPE_NULL) break;
|
|
||||||
memoryCopy(
|
|
||||||
&RPG_CAMERA.position, ent->position, sizeof(RPG_CAMERA.position)
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "dusk.h"
|
#include "rpg/world/worldpos.h"
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
RPG_CAMERA_MODE_FREE,
|
RPG_CAMERA_MODE_FREE,
|
||||||
@@ -14,10 +14,10 @@ typedef enum {
|
|||||||
} rpgcameramode_t;
|
} rpgcameramode_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
vec3 position;
|
|
||||||
rpgcameramode_t mode;
|
rpgcameramode_t mode;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
|
worldpos_t free;
|
||||||
struct {
|
struct {
|
||||||
uint8_t followEntityId;
|
uint8_t followEntityId;
|
||||||
} followEntity;
|
} followEntity;
|
||||||
@@ -30,8 +30,3 @@ extern rpgcamera_t RPG_CAMERA;
|
|||||||
* Initializes the RPG camera.
|
* Initializes the RPG camera.
|
||||||
*/
|
*/
|
||||||
void rpgCameraInit(void);
|
void rpgCameraInit(void);
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates the RPG camera.
|
|
||||||
*/
|
|
||||||
void rpgCameraUpdate(void);
|
|
||||||
13
src/rpg/world/worldpos.h
Normal file
13
src/rpg/world/worldpos.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2025 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "dusk.h"
|
||||||
|
|
||||||
|
typedef struct worldpos_s {
|
||||||
|
uint32_t x, y, z;
|
||||||
|
} worldpos_t;
|
||||||
@@ -40,18 +40,68 @@ void sceneMapUpdate(scenedata_t *data) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sceneMapGetWorldPosition(const worldpos_t pos, vec3 outPosition) {
|
||||||
|
assertNotNull(outPosition, "Output position cannot be NULL");
|
||||||
|
|
||||||
|
outPosition[0] = pos.x * TILE_SIZE;
|
||||||
|
outPosition[1] = pos.y * TILE_SIZE;
|
||||||
|
outPosition[2] = pos.z * TILE_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void sceneMapEntityGetPosition(const entity_t *entity, vec3 outPosition) {
|
||||||
|
assertNotNull(entity, "Entity cannot be NULL");
|
||||||
|
assertNotNull(outPosition, "Output position cannot be NULL");
|
||||||
|
|
||||||
|
sceneMapGetWorldPosition(entity->position, outPosition);
|
||||||
|
|
||||||
|
// Add animation offset(s)
|
||||||
|
switch(entity->animation) {
|
||||||
|
case ENTITY_ANIM_WALK:
|
||||||
|
float_t animPercentage = (
|
||||||
|
(float_t)entity->animFrame / (float_t)ENTITY_ANIM_WALK_DURATION
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get facing rel, we know we moved from the inverse direction.
|
||||||
|
int8_t x, y;
|
||||||
|
entityDirGetRelative(entity->direction, &x, &y);
|
||||||
|
x = -x, y = -y;
|
||||||
|
|
||||||
|
// Add tile size times percentage to posMin/max
|
||||||
|
vec3 offset = {
|
||||||
|
x * TILE_SIZE * animPercentage,
|
||||||
|
y * TILE_SIZE * animPercentage,
|
||||||
|
0.0f
|
||||||
|
};
|
||||||
|
glm_vec3_add(outPosition, offset, outPosition);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void sceneMapRender(scenedata_t *data) {
|
void sceneMapRender(scenedata_t *data) {
|
||||||
// Look at target.
|
// Look at target.
|
||||||
glm_vec3_scale(
|
vec3 cameraTarget;
|
||||||
RPG_CAMERA.position,
|
switch(RPG_CAMERA.mode) {
|
||||||
TILE_SIZE,
|
case RPG_CAMERA_MODE_FREE:
|
||||||
data->sceneMap.camera.lookatPixelPerfect.target
|
sceneMapGetWorldPosition(RPG_CAMERA.free, cameraTarget);
|
||||||
);
|
break;
|
||||||
|
|
||||||
// Center within tile
|
case RPG_CAMERA_MODE_FOLLOW_ENTITY: {
|
||||||
glm_vec3_add(
|
const entity_t *ent = &ENTITIES[RPG_CAMERA.followEntity.followEntityId];
|
||||||
data->sceneMap.camera.lookatPixelPerfect.target,
|
sceneMapEntityGetPosition(ent, cameraTarget);
|
||||||
(vec3){TILE_SIZE / 2.0f, TILE_SIZE / 2.0f, TILE_SIZE / 2.0f },
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
glm_vec3_zero(cameraTarget);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm_vec3_copy(
|
||||||
|
cameraTarget,
|
||||||
data->sceneMap.camera.lookatPixelPerfect.target
|
data->sceneMap.camera.lookatPixelPerfect.target
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -74,14 +124,36 @@ void sceneMapRenderEntity(entity_t *entity) {
|
|||||||
|
|
||||||
if(entity->type == ENTITY_TYPE_NULL) return;
|
if(entity->type == ENTITY_TYPE_NULL) return;
|
||||||
|
|
||||||
vec3 posMin, posMax;
|
vec3 posCenter, posMin, posMax;
|
||||||
glm_vec3_scale(entity->position, TILE_SIZE, posMin);
|
vec3 halfSize = { TILE_SIZE / 2.0f, TILE_SIZE / 2.0f, TILE_SIZE / 2.0f };
|
||||||
glm_vec3_add(posMin, (vec3){TILE_SIZE, TILE_SIZE, TILE_SIZE }, posMax);
|
sceneMapEntityGetPosition(entity, posCenter);
|
||||||
|
glm_vec3_sub(posCenter, halfSize, posMin);
|
||||||
|
glm_vec3_add(posCenter, halfSize, posMax);
|
||||||
|
|
||||||
|
// TEST: Change color depending on dir.
|
||||||
|
color_t testColor;
|
||||||
|
switch(entity->direction) {
|
||||||
|
case ENTITY_DIR_NORTH:
|
||||||
|
testColor = COLOR_BLUE;
|
||||||
|
break;
|
||||||
|
case ENTITY_DIR_EAST:
|
||||||
|
testColor = COLOR_GREEN;
|
||||||
|
break;
|
||||||
|
case ENTITY_DIR_SOUTH:
|
||||||
|
testColor = COLOR_CYAN;
|
||||||
|
break;
|
||||||
|
case ENTITY_DIR_WEST:
|
||||||
|
testColor = COLOR_YELLOW;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
testColor = COLOR_WHITE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
vec2 uv0 = { 0.0f, 0.0f };
|
vec2 uv0 = { 0.0f, 0.0f };
|
||||||
vec2 uv1 = { 1.0f, 1.0f };
|
vec2 uv1 = { 1.0f, 1.0f };
|
||||||
|
|
||||||
spriteBatchPush3D(NULL, posMin, posMax, COLOR_RED, uv0, uv1);
|
spriteBatchPush3D(NULL, posMin, posMax, testColor, uv0, uv1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sceneMapDispose(scenedata_t *data) {
|
void sceneMapDispose(scenedata_t *data) {
|
||||||
|
|||||||
Reference in New Issue
Block a user