#define NENT_DEPRECATED #include "collision.h" #include "entity.h" #include "asm.h" #include "functions.h" #include "common.h" #include "projectile.h" #include "projectile/winder.h" extern s16 gUnk_080B4488[]; typedef enum { /* 0 */ WINDER_TYPE_HEAD, /* 4 */ WINDER_TYPE_4 = 4, } WinderType; void Winder_Init(WinderEntity* this); void sub_080AB950(WinderEntity* this); void Winder_SetPositions(WinderEntity*); bool32 Winder_CheckNextTileCollision(WinderEntity* this, u32 dir); void Winder(Entity* thisx) { static void (*const Winder_Actions[])(WinderEntity*) = { Winder_Init, sub_080AB950, }; WinderEntity* this = (WinderEntity*)thisx; Winder_Actions[super->action](this); Winder_SetPositions(this); } void Winder_Init(WinderEntity* this) { Entity* nextSegment; u16* posPtr; s32 i; super->action++; super->speed = 0x140; super->z.WORD = 0; if (super->type == 0) { super->direction = Random() & 0x18; super->parent = super; } InitializeAnimation(super, 0); if (super->type < 4) { nextSegment = CreateProjectile(WINDER); nextSegment->type = super->type + 1; nextSegment->parent = super->parent; nextSegment->child = super; CopyPosition(super, nextSegment); } posPtr = this->positions; for (i = 0; i < WINDER_NUM_SEGMENTS; i++) { *posPtr++ = super->x.HALF.HI; *posPtr++ = super->y.HALF.HI; } } void sub_080AB950(WinderEntity* this) { static const u8 nextDirections[][2] = { { DirectionEast, DirectionWest }, { DirectionNorth, DirectionSouth }, { DirectionEast, DirectionWest }, { DirectionNorth, DirectionSouth }, }; static const u16 collisionChecks[] = { COL_NORTH_ANY, COL_EAST_ANY, COL_SOUTH_ANY, COL_WEST_ANY }; if (super->type == 0) { u8 dir; ProcessMovement0(super); dir = super->direction >> 3; if ((collisionChecks[dir] & super->collisions) || Winder_CheckNextTileCollision(this, super->direction)) { super->direction = nextDirections[dir][Random() & 0x1]; } } else { WinderEntity* child; if (super->parent == NULL) { DeleteThisEntity(); } if (super->parent->next == NULL) { DeleteThisEntity(); } child = (WinderEntity*)super->child; if ((child != NULL) && (child->base.next != NULL)) { super->x.HALF.HI = child->positions[0]; super->y.HALF.HI = child->positions[1]; } else { DeleteThisEntity(); } } GetNextFrame(super); } void Winder_SetPositions(WinderEntity* this) { //! @bug Undefined behaviour for source and destination to overlap in a memcpy. In this case it is okay because the //! copy will always be sequential, incremental and in chunks of <= 4 bytes, so it will copy the contents of //! positions[0] and positions[1] to positions[2] and positions[3], then that of positions[2] and positions[3] to //! positions[4] and positions[5], and so on. A safer way to do this would be a manual loop as in sub_080AB950. MemCopy(&this->positions[2], &this->positions[0], sizeof(u16) * (ARRAY_COUNT(this->positions) - 2)); this->positions[2 * (WINDER_NUM_SEGMENTS - 1)] = super->x.HALF.HI; this->positions[2 * (WINDER_NUM_SEGMENTS - 1) + 1] = super->y.HALF.HI; } bool32 Winder_CheckNextTileCollision(WinderEntity* this, u32 dir) { u32 tile; u32 val; LayerStruct* layer = GetLayerByIndex(super->collisionLayer); u32 collisionData; tile = TILE(super->x.HALF.HI, super->y.HALF.HI); tile += gUnk_080B4488[dir >> 3]; collisionData = layer->collisionData[tile]; if (collisionData <= 0x1F) { return FALSE; } if (collisionData > 0x3F) { return FALSE; } return TRUE; }