// Copyright (c) 2023 Alex Diener. All rights reserved. #include "PROJECT_NAME/Atoms.h" #include "PROJECT_NAME/EntityComponent_attack.h" #include "PROJECT_NAME/EntityWallCrawler.h" #include "PROJECT_NAME/RoomState.h" #include "PROJECT_NAME/Sprites.h" #include "PROJECT_NAME/Utilities.h" #define stemobject_implementation EntityWallCrawler stemobject_vtable_begin(); stemobject_vtable_entry(createWithEncodedState); stemobject_vtable_entry(initWithEncodedState); stemobject_vtable_entry(copy); stemobject_vtable_entry(initCopy); stemobject_vtable_entry(dispose); stemobject_vtable_entry(postinit); stemobject_vtable_entry(getComponent); stemobject_vtable_entry(roomEntered); stemobject_vtable_entry(advanceTurn); stemobject_vtable_entry(isEqual); stemobject_vtable_entry(enumerateProperties); stemobject_vtable_entry(undoActionPerformed); stemobject_vtable_end(); EntityWallCrawler * EntityWallCrawler_create(Vector2i position, FacingDirection facing, struct RoomState * roomState) { stemobject_create_implementation(init, position, facing, roomState) } static bool collisionCallback(struct GameEntity * collidingEntity, Vector2i fromDirection, void * context) { EntityWallCrawler * self = context; EntityComponent_attack * attackComponent = call_virtual(getComponent, collidingEntity, COMPONENT_ATTACK); if (attackComponent != NULL && (attackComponent->type & ATTACK_HURT_ENEMY)) { self->markedForRemoval = true; EventDispatcher_dispatchEvent(self->roomState->eventDispatcher, ATOM_event_enemy_killed, self); if (attackComponent->type & ATTACK_OVERRIDE_COLLISION) { return false; } } return true; } static void updateSprite(EntityWallCrawler * self) { FacingDirection wallFacing = rotateFacingCCW(self->facingComponent.facing); Vector2i wallDirection = facingToVector2i(wallFacing); if (!isMoveBlockedAtPosition(self->roomState, Vector2i_add(self->positionComponent.position, wallDirection))) { switch (self->facingComponent.facing) { case FACING_NORTH: self->spriteComponent.spriteID = SPRITE_ID_WALL_CRAWLER_DETACHED_NORTH; break; case FACING_EAST: self->spriteComponent.spriteID = SPRITE_ID_WALL_CRAWLER_DETACHED_EAST; break; case FACING_SOUTH: self->spriteComponent.spriteID = SPRITE_ID_WALL_CRAWLER_DETACHED_SOUTH; break; case FACING_WEST: self->spriteComponent.spriteID = SPRITE_ID_WALL_CRAWLER_DETACHED_WEST; break; } } else { switch (self->facingComponent.facing) { case FACING_NORTH: self->spriteComponent.spriteID = SPRITE_ID_WALL_CRAWLER_NORTH; break; case FACING_EAST: self->spriteComponent.spriteID = SPRITE_ID_WALL_CRAWLER_EAST; break; case FACING_SOUTH: self->spriteComponent.spriteID = SPRITE_ID_WALL_CRAWLER_SOUTH; break; case FACING_WEST: self->spriteComponent.spriteID = SPRITE_ID_WALL_CRAWLER_WEST; break; } } } static void initComponents(EntityWallCrawler * self, Vector2i position, FacingDirection facing) { stemobject_assign_vtable(self->positionComponent, EntityComponent_position); stemobject_assign_vtable(self->spriteComponent, EntityComponent_sprite); stemobject_assign_vtable(self->facingComponent, EntityComponent_facing); stemobject_assign_vtable(self->movementComponent, EntityComponent_movement); stemobject_assign_vtable(self->steeringComponent, EntityComponent_facingSteering); stemobject_assign_vtable(self->collidableComponent, EntityComponent_collidable); stemobject_assign_vtable(self->attackComponent, EntityComponent_attack); EntityComponent_position_init(&self->positionComponent, position); EntityComponent_sprite_init(&self->spriteComponent, SPRITE_ID_WALL_CRAWLER_NORTH, DRAW_LAYER_ENTITY, 0); EntityComponent_facing_init(&self->facingComponent, facing); EntityComponent_movement_init(&self->movementComponent, false); EntityComponent_facingSteering_init(&self->steeringComponent); EntityComponent_collidable_init(&self->collidableComponent, collisionCallback, self); EntityComponent_attack_init(&self->attackComponent, ATTACK_HURT_PLAYER | ATTACK_OVERRIDE_COLLISION); } bool EntityWallCrawler_init(EntityWallCrawler * self, Vector2i position, FacingDirection facing, struct RoomState * roomState) { call_super(init, self, roomState); self->initialPosition = position; self->initialFacing = facing; initComponents(self, position, facing); return true; } EntityWallCrawler * EntityWallCrawler_createWithEncodedState(struct memreadContext * memreadContext, struct RoomState * roomState) { GameEntity_createWithEncodedState_implementation(); } void EntityWallCrawler_initWithEncodedState(EntityWallCrawler * self, struct memreadContext * memreadContext, struct RoomState * roomState) { initComponents(self, VECTOR2i_ZERO, FACING_NORTH); call_super_virtual(initWithEncodedState, self, memreadContext, roomState); self->positionComponent.lastPosition = self->positionComponent.position; } EntityWallCrawler * EntityWallCrawler_copy(EntityWallCrawler * self, struct RoomState * roomState) { stemobject_copy_implementation(initCopy, roomState) } void EntityWallCrawler_initCopy(EntityWallCrawler * self, compat_type(EntityWallCrawler *) originalUntyped, struct RoomState * roomState) { call_super_virtual(initCopy, self, originalUntyped, roomState); EntityWallCrawler * original = originalUntyped; self->initialPosition = original->initialPosition; self->initialFacing = original->initialFacing; initComponents(self, original->positionComponent.position, original->facingComponent.facing); call_virtual(initCopy, &self->positionComponent, &original->positionComponent); call_virtual(initCopy, &self->spriteComponent, &original->spriteComponent); call_virtual(initCopy, &self->facingComponent, &original->facingComponent); call_virtual(initCopy, &self->movementComponent, &original->movementComponent); call_virtual(initCopy, &self->steeringComponent, &original->steeringComponent); call_virtual(initCopy, &self->collidableComponent, &original->collidableComponent); call_virtual(initCopy, &self->attackComponent, &original->attackComponent); self->collidableComponent.callbackContext = self; } void EntityWallCrawler_dispose(EntityWallCrawler * self) { call_super_virtual(dispose, self); } void EntityWallCrawler_postinit(EntityWallCrawler * self) { updateSprite(self); } compat_type(GameEntityComponent *) EntityWallCrawler_getComponent(EntityWallCrawler * self, ComponentType type) { switch (type) { case COMPONENT_ATTACK: return &self->attackComponent; case COMPONENT_COLLIDABLE: return &self->collidableComponent; case COMPONENT_FACING: return &self->facingComponent; case COMPONENT_MOVEMENT: return &self->movementComponent; case COMPONENT_POSITION: return &self->positionComponent; case COMPONENT_SPRITE: return &self->spriteComponent; case COMPONENT_STEERING: return &self->steeringComponent; default: break; } return NULL; } void EntityWallCrawler_roomEntered(EntityWallCrawler * self) { self->positionComponent.position = self->initialPosition; self->facingComponent.facing = self->initialFacing; updateSprite(self); } void EntityWallCrawler_advanceTurn(EntityWallCrawler * self) { Vector2i lastPosition = self->positionComponent.position; call_virtual(update, &self->positionComponent, (GameEntity *) self); call_virtual(update, &self->movementComponent, (GameEntity *) self); if (self->positionComponent.position.x == lastPosition.x && self->positionComponent.position.y == lastPosition.y) { self->facingComponent.facing = rotateFacingCW(self->facingComponent.facing); } else { FacingDirection wallFacing = rotateFacingCCW(self->facingComponent.facing); Vector2i wallDirection = facingToVector2i(wallFacing); if (!isMoveBlockedAtPosition(self->roomState, Vector2i_add(self->positionComponent.position, wallDirection))) { self->facingComponent.facing = wallFacing; } } updateSprite(self); } bool EntityWallCrawler_isEqual(EntityWallCrawler * self, EntityWallCrawler * compare) { return self->positionComponent.position.x == compare->positionComponent.position.x && self->positionComponent.position.y == compare->positionComponent.position.y && self->facingComponent.facing == compare->facingComponent.facing; } void EntityWallCrawler_enumerateProperties(EntityWallCrawler * self, bool transient, GameEntity_enumeratePropertiesCallback callback, void * context) { call_super_virtual(enumerateProperties, self, transient, callback, context); if (!transient) { callback(&self->initialPosition, PROPERTY_TYPE_Vector2i_int8_t, context); callback(&self->initialFacing, PROPERTY_TYPE_uint8_t, context); } callback(&self->positionComponent.position, PROPERTY_TYPE_Vector2i_int8_t, context); callback(&self->facingComponent.facing, PROPERTY_TYPE_uint8_t, context); } void EntityWallCrawler_undoActionPerformed(EntityWallCrawler * self) { updateSprite(self); }