// Copyright (c) 2023 Alex Diener. All rights reserved. #include "PROJECT_NAME/EntityComponent_movement.h" #include "PROJECT_NAME/EntityComponent_pushable.h" #include "PROJECT_NAME/GameEntity.h" #include "PROJECT_NAME/RoomState.h" #include #define stemobject_implementation EntityComponent_movement stemobject_vtable_begin(); stemobject_vtable_entry(dispose); stemobject_vtable_entry(initCopy); stemobject_vtable_entry(update); stemobject_vtable_end(); EntityComponent_movement * EntityComponent_movement_create(bool canExitRoom) { stemobject_create_implementation(init, canExitRoom) } bool EntityComponent_movement_init(EntityComponent_movement * self, bool canExitRoom) { call_super(init, self); self->lastMoveResult = MOVE_RESULT_IDLE; self->canExitRoom = canExitRoom; return true; } void EntityComponent_movement_dispose(EntityComponent_movement * self) { call_super_virtual(dispose, self); } void EntityComponent_movement_initCopy(EntityComponent_movement * self, compat_type(GameEntityComponent *) originalUntyped) { call_super_virtual(initCopy, self, originalUntyped); EntityComponent_movement * original = originalUntyped; self->lastMoveResult = original->lastMoveResult; self->canExitRoom = original->canExitRoom; } void EntityComponent_movement_update(EntityComponent_movement * self, struct GameEntity * entity) { EntityComponent_position * positionComponent = call_virtual(getComponent, entity, COMPONENT_POSITION); EntityComponent_steering * steeringComponent = call_virtual(getComponent, entity, COMPONENT_STEERING); assert(positionComponent != NULL); assert(steeringComponent != NULL); Vector2i move = call_virtual(getMoveVector, steeringComponent, entity); if (move.x == 0 && move.y == 0) { self->lastMoveResult = MOVE_RESULT_IDLE; } else { Vector2i newPosition = Vector2i_add(positionComponent->position, move); EntityComponent_pusher * pusherComponent = call_virtual(getComponent, entity, COMPONENT_PUSHER); TilePropertyBits tileProperties = RoomState_getTilePropertiesAtPosition(entity->roomState, newPosition); bool traversible = !!(tileProperties & TILE_STATIC_FLOOR); if (self->canExitRoom && (tileProperties & TILE_OUT_OF_BOUNDS)) { traversible = true; } bool blocked = !!(tileProperties & TILE_STATIC_WALL); unsigned int entityCount = RoomState_getEntityCountAtPosition(entity->roomState, newPosition); // Pushing may affect entities at target position; need to list all before mutating GameEntity * entities[entityCount]; for (unsigned int entityIndex = 0; entityIndex < entityCount; entityIndex++) { entities[entityIndex] = RoomState_getEntityAtPositionAtIndex(entity->roomState, newPosition, entityIndex); } for (unsigned int entityIndex = 0; entityIndex < entityCount; entityIndex++) { GameEntity * entityAtTargetPosition = entities[entityIndex]; if (!entityAtTargetPosition->markedForRemoval) { EntityComponent_collidable * collidableComponent = call_virtual(getComponent, entityAtTargetPosition, COMPONENT_COLLIDABLE); if (collidableComponent != NULL && call_virtual(collision, collidableComponent, entity, move)) { if (pusherComponent != NULL) { EntityComponent_pushable * pushableComponent = call_virtual(getComponent, entityAtTargetPosition, COMPONENT_PUSHABLE); if (pushableComponent == NULL || !call_virtual(push, pushableComponent, entityAtTargetPosition, entity, move)) { blocked = true; } } else { blocked = true; } } } } if (blocked) { positionComponent->lastBumpDirection = move; self->lastMoveResult = MOVE_RESULT_BLOCKED; } else if (!traversible) { self->lastMoveResult = MOVE_RESULT_REFUSED; } else { call_virtual(moveTo, positionComponent, newPosition); self->lastMoveResult = MOVE_RESULT_MOVED; } } }