/** * Copyright (c) 2025 Dominic Masters * * This software is released under the MIT License. * https://opensource.org/licenses/MIT */ #include "entity.h" #include "assert/assert.h" #include "util/memory.h" #include "time/time.h" #include "util/math.h" #include "rpg/cutscene/cutscenemode.h" #include "rpg/world/map.h" entity_t ENTITIES[ENTITY_COUNT]; void entityInit(entity_t *entity, const entitytype_t type) { assertNotNull(entity, "Entity pointer cannot be NULL"); assertTrue(type < ENTITY_TYPE_COUNT, "Invalid entity type"); assertTrue(type != ENTITY_TYPE_NULL, "Cannot have NULL entity type"); assertTrue( entity >= ENTITIES && entity < ENTITIES + ENTITY_COUNT, "Entity pointer is out of bounds" ); memoryZero(entity, sizeof(entity_t)); entity->id = (uint8_t)(entity - ENTITIES); entity->type = type; if(ENTITY_CALLBACKS[type].init != NULL) ENTITY_CALLBACKS[type].init(entity); } 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"); // What state is the entity in? if(entity->animation != ENTITY_ANIM_IDLE) { // Entity is mid animation, tick it (down). entity->animTime -= TIME.delta; if(entity->animTime <= 0) { entity->animation = ENTITY_ANIM_IDLE; entity->animTime = 0; } return; } // Movement code. if( cutsceneModeIsInputAllowed() && ENTITY_CALLBACKS[entity->type].movement != NULL ) { ENTITY_CALLBACKS[entity->type].movement(entity); } } void entityTurn(entity_t *entity, const entitydir_t direction) { entity->direction = direction; entity->animation = ENTITY_ANIM_TURN; entity->animTime = ENTITY_ANIM_TURN_DURATION; } void entityWalk(entity_t *entity, const entitydir_t direction) { // TODO: Animation, delay, etc. entity->direction = direction; // Where are we moving? worldunit_t newX, newY; newX = entity->position.x; newY = entity->position.y; { worldunits_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 *other = ENTITIES; do { if(other == entity) continue; if(other->type == ENTITY_TYPE_NULL) continue; if(other->position.x != newX) continue; if(other->position.y != newY) continue; if(other->position.z != entity->position.z) continue; return;// Blocked } while(++other, other < &ENTITIES[ENTITY_COUNT]); // Move. entity->position.x = newX; entity->position.y = newY; entity->animation = ENTITY_ANIM_WALK; entity->animTime = ENTITY_ANIM_WALK_DURATION;// TODO: Running vs walking } entity_t * entityGetAt(const worldpos_t position) { entity_t *ent = ENTITIES; do { if(ent->type == ENTITY_TYPE_NULL) continue; if(ent->position.x != position.x) continue; if(ent->position.y != position.y) continue; if(ent->position.z != position.z) continue; return ent; } while(++ent, ent < &ENTITIES[ENTITY_COUNT]); return NULL; }