mirror of
https://github.com/zeldaret/tmc
synced 2026-06-23 09:20:02 -04:00
210 lines
6.7 KiB
C
210 lines
6.7 KiB
C
/**
|
|
* @file zeldaFollower.c
|
|
* @ingroup NPCs
|
|
*
|
|
* @brief Zelda Follower NPC
|
|
*/
|
|
#include "npc/zelda.h"
|
|
#include "common.h"
|
|
#include "entity.h"
|
|
#include "functions.h"
|
|
|
|
extern s32 sub_080041E8(s32 x1, s32 y1, s32 x2, s32 y2);
|
|
|
|
typedef union {
|
|
struct {
|
|
u16 x;
|
|
u16 y;
|
|
s16 z;
|
|
u8 framestate;
|
|
u8 animationState : 6;
|
|
u8 collisionLayer : 2;
|
|
} FIELDS;
|
|
u64 DWORD;
|
|
} ZeldaFollowerItem;
|
|
|
|
#define ZELDA_FOLLOWER_HEAP_LEN 20
|
|
|
|
#define ZELDA_FOLLOWER_HEAP ((ZeldaFollowerItem*)super->myHeap)
|
|
#define ZELDA_FOLLOWER_HEAP_END ((ZeldaFollowerItem*)super->myHeap + (ZELDA_FOLLOWER_HEAP_LEN - 1))
|
|
|
|
#define ZELDA_FOLLOWER_HEAP_SHIFT_RIGHT(this, heapPtr) \
|
|
do { \
|
|
int i; \
|
|
heapPtr = ((ZeldaFollowerItem*)this->myHeap); \
|
|
heapPtr += (ZELDA_FOLLOWER_HEAP_LEN - 2); \
|
|
for (i = 0; i < (ZELDA_FOLLOWER_HEAP_LEN - 1); i++) { \
|
|
heapPtr[1] = heapPtr[0]; \
|
|
heapPtr--; \
|
|
} \
|
|
} while (0)
|
|
|
|
void sub_08068318(ZeldaFollowerEntity*);
|
|
void sub_0806854C(ZeldaFollowerEntity*, u32*);
|
|
void sub_08068578(ZeldaFollowerEntity* this);
|
|
|
|
void ZeldaFollower(ZeldaFollowerEntity* this) {
|
|
if (super->action == 0) {
|
|
super->action++;
|
|
super->spriteSettings.draw = TRUE;
|
|
super->animationState = 4;
|
|
this->unk_68 = 0;
|
|
this->unk_69 = 0;
|
|
SetEntityPriority(super, PRIO_MESSAGE);
|
|
InitAnimationForceUpdate(super, 0);
|
|
sub_0806854C(this, NULL);
|
|
}
|
|
if ((s8)this->unk_68 != 0) {
|
|
sub_08068318(this);
|
|
} else {
|
|
super->spriteSettings.draw = FALSE;
|
|
}
|
|
}
|
|
|
|
void sub_08068318(ZeldaFollowerEntity* this) {
|
|
s32 dist;
|
|
|
|
u32 animIndex;
|
|
u32 animIndexTmp;
|
|
|
|
ZeldaFollowerItem* heapPtr;
|
|
ZeldaFollowerItem item;
|
|
|
|
item.FIELDS.x = gPlayerEntity.base.x.HALF_U.HI;
|
|
item.FIELDS.y = gPlayerEntity.base.y.HALF_U.HI;
|
|
item.FIELDS.z = gPlayerEntity.base.z.HALF_U.HI;
|
|
item.FIELDS.framestate = gPlayerState.framestate;
|
|
item.FIELDS.animationState = gPlayerEntity.base.animationState;
|
|
item.FIELDS.collisionLayer = gPlayerEntity.base.collisionLayer;
|
|
|
|
heapPtr = super->myHeap;
|
|
|
|
if ((heapPtr->FIELDS.framestate == 0xa && item.FIELDS.framestate != 0xa) ||
|
|
(heapPtr->FIELDS.framestate == 0x16 && item.FIELDS.framestate != 0x16)) {
|
|
super->x.HALF.HI = gPlayerEntity.base.x.HALF.HI;
|
|
super->y.HALF.HI = gPlayerEntity.base.y.HALF.HI;
|
|
super->spriteSettings.draw = 1;
|
|
sub_08068578(this);
|
|
}
|
|
|
|
animIndex = 0;
|
|
if (item.DWORD != heapPtr->DWORD || item.FIELDS.framestate == 0x16 || item.FIELDS.framestate == 0xa) {
|
|
ZELDA_FOLLOWER_HEAP_SHIFT_RIGHT(super, heapPtr);
|
|
heapPtr = ZELDA_FOLLOWER_HEAP;
|
|
heapPtr[0] = item;
|
|
animIndex = 0x4;
|
|
|
|
if ((s8)this->unk_69 > 0) {
|
|
this->unk_69 = this->unk_69 - 1;
|
|
}
|
|
} else {
|
|
heapPtr += ZELDA_FOLLOWER_HEAP_LEN - 1;
|
|
|
|
if (heapPtr->FIELDS.z < 0) {
|
|
ZELDA_FOLLOWER_HEAP_SHIFT_RIGHT(super, heapPtr);
|
|
animIndex = 0x4;
|
|
} else {
|
|
dist = sub_080041E8(gPlayerEntity.base.x.HALF.HI, gPlayerEntity.base.y.HALF.HI, (u16)heapPtr->FIELDS.x,
|
|
(u16)heapPtr->FIELDS.y);
|
|
dist = ((u32)dist) >> 0x4;
|
|
if (dist > 0x18) {
|
|
ZELDA_FOLLOWER_HEAP_SHIFT_RIGHT(super, heapPtr);
|
|
animIndex = 0x4;
|
|
}
|
|
}
|
|
}
|
|
|
|
heapPtr = ZELDA_FOLLOWER_HEAP;
|
|
heapPtr += ZELDA_FOLLOWER_HEAP_LEN - 1;
|
|
super->x.HALF.HI = heapPtr->FIELDS.x;
|
|
super->y.HALF.HI = heapPtr->FIELDS.y;
|
|
super->z.HALF.HI = heapPtr->FIELDS.z;
|
|
super->animationState = heapPtr->FIELDS.animationState;
|
|
super->collisionLayer = heapPtr->FIELDS.collisionLayer;
|
|
|
|
if (heapPtr->FIELDS.framestate == 0x16 || heapPtr->FIELDS.framestate == 0xa) {
|
|
super->spriteSettings.draw = 0;
|
|
}
|
|
|
|
if (((s8)this->unk_69) > 0) {
|
|
this->unk_69 = this->unk_69 - 1;
|
|
}
|
|
|
|
animIndexTmp = animIndex;
|
|
animIndex += super->animationState >> 1;
|
|
|
|
if (!(animIndex == super->animIndex || (animIndexTmp == 0 && ((s8)this->unk_69) > 0))) {
|
|
InitAnimationForceUpdate(super, animIndex);
|
|
this->unk_69 = 0x1e;
|
|
} else {
|
|
UpdateAnimationSingleFrame(super);
|
|
}
|
|
|
|
sub_0800451C(super);
|
|
if (super->z.HALF.HI < 0) {
|
|
sub_0806F854(super, 0x0, -0xc);
|
|
}
|
|
}
|
|
|
|
void sub_0806854C(ZeldaFollowerEntity* this, u32* none) {
|
|
super->myHeap = zMalloc(sizeof(ZeldaFollowerItem[ZELDA_FOLLOWER_HEAP_LEN]));
|
|
if (super->myHeap != NULL) {
|
|
this->unk_68 = 1;
|
|
RemoveInteractableObject(super);
|
|
super->hitbox = NULL;
|
|
sub_08068578(this);
|
|
}
|
|
}
|
|
|
|
void sub_08068578(ZeldaFollowerEntity* this) {
|
|
s32 dx, dy;
|
|
s32 i;
|
|
|
|
ZeldaFollowerItem *heapPtr, item;
|
|
|
|
// Copy from the player's position/state.
|
|
item.FIELDS.x = gPlayerEntity.base.x.HALF_U.HI;
|
|
item.FIELDS.y = gPlayerEntity.base.y.HALF_U.HI;
|
|
item.FIELDS.z = gPlayerEntity.base.z.HALF_U.HI;
|
|
item.FIELDS.framestate = gPlayerState.framestate;
|
|
item.FIELDS.animationState = gPlayerEntity.base.animationState;
|
|
item.FIELDS.collisionLayer = gPlayerEntity.base.collisionLayer;
|
|
|
|
// Compute the distance between zelda and the player.
|
|
dx = gPlayerEntity.base.x.HALF.HI - super->x.HALF.HI;
|
|
dy = gPlayerEntity.base.y.HALF.HI - super->y.HALF.HI;
|
|
|
|
// Divide it into ZELDA_FOLLOWER_HEAP_LEN increments.
|
|
dx = FixedDiv(dx, ZELDA_FOLLOWER_HEAP_LEN);
|
|
dy = FixedDiv(dy, ZELDA_FOLLOWER_HEAP_LEN);
|
|
|
|
heapPtr = ZELDA_FOLLOWER_HEAP;
|
|
for (i = 0; i < ZELDA_FOLLOWER_HEAP_LEN; i++) {
|
|
heapPtr->FIELDS.x = item.FIELDS.x - ((i * dx) >> 8);
|
|
heapPtr->FIELDS.y = item.FIELDS.y - ((i * dy) >> 8);
|
|
heapPtr->FIELDS.z = item.FIELDS.z;
|
|
heapPtr->FIELDS.framestate = item.FIELDS.framestate;
|
|
heapPtr->FIELDS.animationState = super->animationState;
|
|
heapPtr->FIELDS.collisionLayer = super->collisionLayer;
|
|
heapPtr++;
|
|
}
|
|
}
|
|
|
|
void ZeldaFollower_Hide(Entity* zelda, ZeldaFollowerEntity* follower) {
|
|
follower->unk_68 = 0;
|
|
follower->base.spriteSettings.draw = 0;
|
|
}
|
|
|
|
void ZeldaFollower_Show(Entity* zelda, ZeldaFollowerEntity* follower) {
|
|
follower->unk_68 = 1;
|
|
follower->base.spriteSettings.draw = 1;
|
|
follower->base.animationState = zelda->animationState;
|
|
sub_08068578(follower);
|
|
InitAnimationForceUpdate(&follower->base, follower->base.animationState / 2);
|
|
}
|
|
|
|
void sub_080686C4(Entity* zelda, ZeldaFollowerEntity* follower) {
|
|
follower->base.y.HALF.HI -= 0x10;
|
|
sub_08068578(follower);
|
|
}
|