mirror of https://github.com/zeldaret/tmc.git
429 lines
9.6 KiB
C
429 lines
9.6 KiB
C
/**
|
|
* @file lakitu.c
|
|
* @ingroup Enemies
|
|
*
|
|
* @brief Lakitu enemy
|
|
*/
|
|
#include "enemy.h"
|
|
#include "physics.h"
|
|
#include "player.h"
|
|
|
|
typedef struct {
|
|
/*0x00*/ Entity base;
|
|
/*0x68*/ u8 unused1[12];
|
|
/*0x74*/ u16 unk_74;
|
|
/*0x76*/ u16 unk_76;
|
|
/*0x78*/ u8 unk_78;
|
|
/*0x79*/ u8 unk_79;
|
|
} LakituEntity;
|
|
|
|
extern void (*const LakituActionFuncs[])(LakituEntity*);
|
|
|
|
// Forward references to functions in lakitu.c
|
|
extern void sub_0803CAD0(LakituEntity* this);
|
|
extern void sub_0803CBAC(LakituEntity* this);
|
|
extern void sub_0803CA84(LakituEntity* this, u32);
|
|
extern bool32 sub_0803CA4C(LakituEntity* this);
|
|
extern bool32 sub_0803CB04(LakituEntity* this);
|
|
extern void Lakitu_SpawnLightning(LakituEntity* this);
|
|
extern void sub_0803CB34(LakituEntity* this);
|
|
extern void sub_0803CC08(LakituEntity* this);
|
|
|
|
enum {
|
|
INIT,
|
|
HIDDEN,
|
|
END_HIDE,
|
|
IDLE,
|
|
BEGIN_HIDE,
|
|
LIGHTNING_THROW,
|
|
LIGHTNING_DELAY,
|
|
CLOUDLESS,
|
|
};
|
|
|
|
typedef struct {
|
|
s8 x;
|
|
s8 y;
|
|
} PACKED OffsetCoords;
|
|
|
|
// Variables
|
|
extern void (*const Lakitu_Functions[])(LakituEntity*);
|
|
extern void (*const gUnk_080D0128[])(LakituEntity*);
|
|
extern void (*const gUnk_080D0148[])(LakituEntity*);
|
|
extern const OffsetCoords gUnk_080D0154[];
|
|
|
|
void Lakitu(LakituEntity* this) {
|
|
EnemyFunctionHandler(super, (EntityActionArray)Lakitu_Functions);
|
|
|
|
SetChildOffset(super, 0, 1, -16);
|
|
}
|
|
|
|
void Lakitu_OnTick(LakituEntity* this) {
|
|
if (super->action != 0 && super->action != 7) {
|
|
sub_0803CAD0(this);
|
|
}
|
|
|
|
LakituActionFuncs[super->action](this);
|
|
}
|
|
|
|
void Lakitu_OnCollision(LakituEntity* this) {
|
|
if ((super->contactFlags & 0x7f) == 0x1d) {
|
|
super->zVelocity = Q_16_16(2.0);
|
|
|
|
sub_0803CBAC(this);
|
|
} else {
|
|
if (super->hitType == 0x43) {
|
|
Entity* fx = CreateFx(super, FX_DEATH, 0);
|
|
|
|
if (fx != NULL) {
|
|
u32 angle = (super->knockbackDirection ^ 0x10) << 3;
|
|
s32 sine;
|
|
|
|
sine = gSineTable[angle];
|
|
if (sine < 0) {
|
|
sine += 0x1f;
|
|
}
|
|
|
|
fx->x.HALF.HI += sine >> 5;
|
|
|
|
sine = gSineTable[angle + 0x40];
|
|
if (sine < 0) {
|
|
sine += 0x1f;
|
|
}
|
|
|
|
fx->y.HALF.HI -= sine >> 5;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (super->confusedTime) {
|
|
Create0x68FX(super, FX_STARS);
|
|
}
|
|
|
|
EnemyFunctionHandlerAfterCollision(super, Lakitu_Functions);
|
|
}
|
|
|
|
void Lakitu_OnGrabbed(LakituEntity* this) {
|
|
if (sub_0806F520(super)) {
|
|
gUnk_080D0148[super->subAction](this);
|
|
}
|
|
}
|
|
|
|
void sub_0803C844(LakituEntity* this) {
|
|
super->subAction = 1;
|
|
super->gustJarTolerance = 0x3c;
|
|
}
|
|
|
|
void sub_0803C850(LakituEntity* this) {
|
|
Entity* cloud = super->child;
|
|
if (cloud != NULL) {
|
|
cloud->spriteOffsetX = super->spriteOffsetX;
|
|
}
|
|
|
|
sub_0806F4E8(super);
|
|
}
|
|
|
|
void sub_0803C86C(LakituEntity* this) {
|
|
sub_0803CBAC(this);
|
|
super->child = NULL;
|
|
}
|
|
|
|
void Lakitu_Initialize(LakituEntity* this) {
|
|
Entity* cloud = CreateProjectileWithParent(super, 17, 0);
|
|
if (cloud == NULL) {
|
|
return;
|
|
}
|
|
|
|
cloud->parent = super;
|
|
super->child = cloud;
|
|
|
|
sub_0804A720(super);
|
|
|
|
super->action = HIDDEN;
|
|
|
|
super->z.HALF.HI = -2;
|
|
|
|
super->spriteOffsetY = 0xff;
|
|
|
|
this->unk_74 = super->x.HALF.HI;
|
|
this->unk_76 = super->y.HALF.HI;
|
|
}
|
|
|
|
void Lakitu_Hide(LakituEntity* this) {
|
|
sub_0803CA84(this, 0);
|
|
|
|
if (sub_0803CA4C(this)) {
|
|
super->action = END_HIDE;
|
|
super->spriteSettings.draw = 1;
|
|
}
|
|
}
|
|
|
|
void Lakitu_EndHide(LakituEntity* this) {
|
|
UpdateAnimationSingleFrame(super);
|
|
|
|
if (super->frame & ANIM_DONE) {
|
|
super->action = IDLE;
|
|
super->timer = 60;
|
|
|
|
super->hitType = 0x42;
|
|
|
|
InitAnimationForceUpdate(super, super->animationState + 4);
|
|
}
|
|
}
|
|
|
|
void Lakitu_Idle(LakituEntity* this) {
|
|
if (sub_0803CB04(this)) {
|
|
return;
|
|
}
|
|
|
|
if (!sub_0803CA4C(this)) {
|
|
super->action = BEGIN_HIDE;
|
|
|
|
super->hitType = 0x43;
|
|
InitAnimationForceUpdate(super, super->animationState + 0xc);
|
|
} else {
|
|
sub_0803CA84(this, 4);
|
|
}
|
|
}
|
|
|
|
void Lakitu_BeginHide(LakituEntity* this) {
|
|
UpdateAnimationSingleFrame(super);
|
|
|
|
if (super->frame & ANIM_DONE) {
|
|
super->action = HIDDEN;
|
|
super->spriteSettings.draw = 0;
|
|
|
|
InitAnimationForceUpdate(super, super->animationState);
|
|
}
|
|
}
|
|
|
|
void Lakitu_Lightning(LakituEntity* this) {
|
|
UpdateAnimationSingleFrame(super);
|
|
|
|
if (!(super->frame & ANIM_DONE)) {
|
|
return;
|
|
}
|
|
|
|
Lakitu_SpawnLightning(this);
|
|
|
|
super->action = LIGHTNING_DELAY;
|
|
super->hitType = 0x42;
|
|
|
|
if ((Random() & 1) && !this->unk_79) {
|
|
super->timer = 15;
|
|
|
|
this->unk_79 = TRUE;
|
|
} else {
|
|
super->timer = 30;
|
|
|
|
this->unk_79 = FALSE;
|
|
|
|
InitAnimationForceUpdate(super->child, super->animationState);
|
|
}
|
|
}
|
|
|
|
void Lakitu_LightningDelay(LakituEntity* this) {
|
|
super->timer--;
|
|
|
|
if (super->timer != 0) {
|
|
return;
|
|
}
|
|
|
|
if (this->unk_79 == 1) {
|
|
sub_0803CB34(this);
|
|
} else {
|
|
super->action = IDLE;
|
|
super->timer = 180;
|
|
|
|
InitAnimationForceUpdate(super, super->animationState + 4);
|
|
}
|
|
}
|
|
|
|
void Lakitu_Cloudless(LakituEntity* this) {
|
|
if (GravityUpdate(super, Q_8_8(24.0)) == 0 && super->animIndex <= 19) {
|
|
InitAnimationForceUpdate(super, super->animationState + 20);
|
|
|
|
super->spritePriority.b1 = 0;
|
|
}
|
|
|
|
UpdateAnimationSingleFrame(super);
|
|
sub_0803CC08(this);
|
|
}
|
|
|
|
bool32 sub_0803CA4C(LakituEntity* this) {
|
|
if (EntityWithinDistance(super, gPlayerEntity.base.x.HALF.HI, gPlayerEntity.base.y.HALF.HI, 0x28) == 0) {
|
|
if (EntityInRectRadius(super, &gPlayerEntity.base, 0x70, 0x50)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
void sub_0803CA84(LakituEntity* this, u32 unkParameter) {
|
|
u32 altAnimState = GetFacingDirection(super, &gPlayerEntity.base);
|
|
|
|
if (((altAnimState - 3) & 7) > 2 || ((super->animationState - (altAnimState >> 3)) & 3) > 1) {
|
|
altAnimState = DirectionRoundUp(altAnimState) >> 3;
|
|
|
|
if (altAnimState != super->animationState) {
|
|
super->animationState = altAnimState;
|
|
|
|
InitAnimationForceUpdate(super, altAnimState + unkParameter);
|
|
InitAnimationForceUpdate(super->child, altAnimState);
|
|
}
|
|
}
|
|
}
|
|
|
|
void sub_0803CAD0(LakituEntity* this) {
|
|
if (!EntityWithinDistance(super, this->unk_74, this->unk_76, 1)) {
|
|
super->direction = CalculateDirectionTo(super->x.HALF.HI, super->y.HALF.HI, this->unk_74, this->unk_76);
|
|
|
|
ProcessMovement2(super);
|
|
}
|
|
}
|
|
|
|
bool32 sub_0803CB04(LakituEntity* this) {
|
|
bool32 ret;
|
|
u8 delay;
|
|
|
|
delay = --super->timer;
|
|
if (delay != 0) {
|
|
ret = FALSE;
|
|
} else {
|
|
sub_0803CB34(this);
|
|
this->unk_79 = delay;
|
|
|
|
InitAnimationForceUpdate(super->child, super->animationState + 4);
|
|
ret = TRUE;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
void sub_0803CB34(LakituEntity* this) {
|
|
super->action = LIGHTNING_THROW;
|
|
super->hitType = 0xa6;
|
|
|
|
this->unk_78 = GetFacingDirection(super, &gPlayerEntity.base);
|
|
|
|
InitAnimationForceUpdate(super, super->animationState + 8);
|
|
}
|
|
|
|
void Lakitu_SpawnLightning(LakituEntity* this) {
|
|
Entity* lightning;
|
|
const OffsetCoords* offset;
|
|
|
|
lightning = CreateProjectileWithParent(super, LAKITU_LIGHTNING, 0);
|
|
|
|
if (lightning == NULL) {
|
|
return;
|
|
}
|
|
|
|
offset = &gUnk_080D0154[super->animationState];
|
|
|
|
lightning->direction = this->unk_78;
|
|
|
|
PositionRelative(super, lightning, Q_16_16(offset->x), Q_16_16(offset->y));
|
|
|
|
EnqueueSFX(SFX_193);
|
|
}
|
|
|
|
void sub_0803CBAC(LakituEntity* this) {
|
|
Entity* cloud;
|
|
|
|
cloud = super->child;
|
|
if (cloud != NULL) {
|
|
cloud->flags |= ENT_COLLIDE;
|
|
cloud->hitType = 0x43;
|
|
}
|
|
|
|
super->action = CLOUDLESS;
|
|
super->spriteSettings.draw = 1;
|
|
|
|
super->spritePriority.b1 = 1;
|
|
|
|
super->flags2 &= 0x7b;
|
|
|
|
super->hitType = 0x42;
|
|
|
|
InitAnimationForceUpdate(super, super->animationState + 16);
|
|
}
|
|
|
|
void sub_0803CC08(LakituEntity* this) {
|
|
Entity* cloud;
|
|
Entity* fx;
|
|
|
|
const s32 diff = Q_8_8(3.0 / 128.0);
|
|
|
|
cloud = super->child;
|
|
if (cloud == NULL) {
|
|
return;
|
|
}
|
|
|
|
if ((u32)(cloud->z.HALF.HI - super->z.HALF.HI) > 2) {
|
|
return;
|
|
}
|
|
|
|
if (super->zVelocity >= 0) {
|
|
return;
|
|
}
|
|
|
|
if (!EntityWithinDistance(super, cloud->x.HALF.HI, cloud->y.HALF.HI, 6)) {
|
|
return;
|
|
}
|
|
|
|
fx = CreateFx(super, FX_DEATH, 0);
|
|
if (fx != NULL) {
|
|
fx->x.HALF.HI += diff;
|
|
fx->y.HALF.HI += diff;
|
|
}
|
|
|
|
fx = CreateFx(super, FX_DEATH, 0);
|
|
if (fx != NULL) {
|
|
fx->x.HALF.HI -= diff;
|
|
fx->y.HALF.HI += diff;
|
|
}
|
|
|
|
fx = CreateFx(super, FX_DEATH, 0);
|
|
if (fx != NULL) {
|
|
fx->x.HALF.HI += diff;
|
|
fx->y.HALF.HI -= diff;
|
|
}
|
|
|
|
fx = CreateFx(super, FX_DEATH, 0);
|
|
if (fx != NULL) {
|
|
fx->x.HALF.HI -= diff;
|
|
fx->y.HALF.HI -= diff;
|
|
}
|
|
|
|
super->child = NULL;
|
|
DeleteEntity(cloud);
|
|
}
|
|
|
|
void (*const Lakitu_Functions[])(LakituEntity*) = {
|
|
Lakitu_OnTick,
|
|
Lakitu_OnCollision,
|
|
(void (*)(LakituEntity*))GenericKnockback,
|
|
(void (*)(LakituEntity*))GenericDeath,
|
|
(void (*)(LakituEntity*))GenericConfused,
|
|
Lakitu_OnGrabbed,
|
|
};
|
|
|
|
void (*const LakituActionFuncs[])(LakituEntity*) = {
|
|
Lakitu_Initialize, Lakitu_Hide, Lakitu_EndHide, Lakitu_Idle,
|
|
Lakitu_BeginHide, Lakitu_Lightning, Lakitu_LightningDelay, Lakitu_Cloudless,
|
|
};
|
|
|
|
void (*const gUnk_080D0148[])(LakituEntity*) = {
|
|
sub_0803C844,
|
|
sub_0803C850,
|
|
sub_0803C86C,
|
|
};
|
|
|
|
const OffsetCoords gUnk_080D0154[] = {
|
|
{ 0x00, 0xEE },
|
|
{ 0x12, 0x00 },
|
|
{ 0x00, 0x08 },
|
|
{ 0xEE, 0x00 },
|
|
};
|