Files
tmc/src/projectile/winder.c
T
2022-03-24 22:46:23 +00:00

134 lines
3.8 KiB
C

#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_TAIL = 4,
} WinderType;
void Winder_Init(WinderEntity* this);
void Winder_Move(WinderEntity* this);
void Winder_SetPositions(WinderEntity*);
bool32 Winder_CheckForRailings(WinderEntity* this, u32 dir);
void Winder(Entity* thisx) {
static void (*const Winder_Actions[])(WinderEntity*) = {
Winder_Init,
Winder_Move,
};
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 < WINDER_TYPE_TAIL) {
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 Winder_Move(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 == WINDER_TYPE_HEAD) {
u8 dir;
ProcessMovement0(super);
dir = super->direction >> 3;
if ((collisionChecks[dir] & super->collisions) || Winder_CheckForRailings(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 Winder_Move.
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_CheckForRailings(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;
}