tmc/src/npcUtils.c

512 lines
14 KiB
C

#include "global.h"
#include "asm.h"
#include "entity.h"
#include "functions.h"
#include "definitions.h"
#include "save.h"
#include "npc.h"
#include "kinstone.h"
#include "hitbox.h"
extern const NPCDefinition gNPCDefinitions[];
typedef struct {
u16 textIndex;
u16 cancelledTextIndex;
u16 fusingTextIndex;
} NPCData;
extern NPCData* gUnk_08001A7C[];
u32 sub_0806EF88(Entity*);
u32 sub_0806EE70(Entity*);
u32 sub_0806EED0(Entity*);
u32 sub_0806EEF4(Entity*);
u32 sub_0806EFAC(Entity*, u16*);
u32 sub_0806EFBC(Entity*, u16*);
u32 sub_0806EFCC(Entity*, u16*);
u32 sub_0806EFDC(Entity*, u16*);
u32 sub_0806F014(Entity*, u16*);
u32 sub_0806F02C(Entity*, u16*);
u32 sub_0806F048(Entity*, u16*);
u32 sub_0806F050(Entity*, u16*);
u32 sub_0806F064(Entity*, u16*);
static void sub_0806EF14(Entity*);
static void sub_0806EF4C(Entity*, u16*);
static u32 sub_0806EF74(Entity*, u32);
void sub_0806EC20(Entity* ent) {
Entity* e = CreateNPC(NPC_UNK_58, 0, 0);
if (e != NULL) {
e->parent = ent;
}
}
void sub_0806EC38(void) {
Entity* e = FindEntityByID(NPC, NPC_UNK_58, 7);
if (e != NULL)
DeleteEntity(e);
}
static const NPCDefinition* GetNPCDefinition(Entity* this) {
const NPCDefinition* definition = &gNPCDefinitions[this->id];
if (definition->bitfield.type == 2) {
definition = &definition->data.definition[this->type];
}
return definition;
}
void NPCInit(Entity* this) {
static const Hitbox* const gNPCHitboxes[] = {
NULL, &gHitbox_2, &gHitbox_30, &gHitbox_2, &gHitbox_3, &gHitbox_31,
};
u32 tmp;
u32 tmp2;
if ((this->flags & ENT_DID_INIT) == 0) {
const NPCDefinition* definition = GetNPCDefinition(this);
if (definition->bitfield.type == 0) {
// No sprite for this NPC
this->flags |= ENT_DID_INIT;
} else {
tmp = definition->bitfield.gfx;
switch (definition->bitfield.gfx_type) {
case 2:
this->spriteVramOffset = definition->bitfield.gfx;
break;
case 1:
LoadSwapGFX(this, tmp, 0);
break;
default:
LoadFixedGFX(this, tmp);
break;
}
tmp = definition->data.sprite.paletteIndex;
LoadObjPalette(this, tmp);
this->spriteIndex = definition->data.sprite.spriteIndex;
this->spriteSettings.shadow = definition->data.sprite.shadow;
this->spritePriority.b1 = definition->data.sprite.spritePriority;
this->spriteSettings.draw = definition->data.sprite.draw;
this->hitbox = (Hitbox*)gNPCHitboxes[definition->bitfield.hitbox];
this->flags |= ENT_DID_INIT;
tmp2 = 0xff;
this->animIndex = tmp2;
this->frameIndex = tmp2;
UpdateSpriteForCollisionLayer(this);
}
}
}
Entity* CreateNPC(u32 subtype, u32 form, u32 parameter) {
Entity* entity = GetEmptyEntity();
if (entity != NULL) {
entity->kind = NPC;
entity->id = subtype;
entity->type = form;
entity->type2 = parameter;
AppendEntityToList(entity, 7);
}
return entity;
}
u32 sub_0806ED78(Entity* ent) {
u32 result = sub_0800445C(ent);
if (result) {
if (gPlayerState.dash_state & 0x40) {
PutAwayItems();
}
}
return result;
}
s32 GetAnimationStateInRectRadius(Entity* ent, u32 x, u32 y) {
s32 anim = -1;
if (EntityInRectRadius(ent, &gPlayerEntity, x, y))
anim = GetAnimationState(ent);
return anim;
}
u32 GetAnimationState(Entity* ent) {
u32 direction = GetFacingDirection(ent, &gPlayerEntity);
return GetAnimationStateForDirection4(direction);
}
s32 GetFacingDirectionInRectRadius(Entity* ent, u32 x, u32 y) {
s32 dir = -1;
if (EntityInRectRadius(ent, &gPlayerEntity, x, y))
dir = GetFacingDirection(ent, &gPlayerEntity);
return dir;
}
void sub_0806EE04(Entity* ent, void* a2, u32 a3) {
ent->child = a2;
ent->collisionFlags = a3;
ent->hitType = 0;
ent->knockbackSpeed = 0;
ent->carryFlags = 0;
}
u32 sub_0806EE20(Entity* ent) {
static u32 (*const gUnk_08114EFC[])(Entity*) = {
sub_0806EF88,
sub_0806EE70,
sub_0806EED0,
sub_0806EEF4,
};
u32 v3;
if (!ent->interactType) {
if (ent->child != NULL)
return gUnk_08114EFC[ent->carryFlags](ent);
} else {
ent->knockbackSpeed = 8;
v3 = GetFacingDirection(ent, &gPlayerEntity);
ent->knockbackDirection = GetAnimationStateForDirection4(v3);
}
return 0;
}
u32 sub_0806EE70(Entity* ent) {
s32 tmp1;
s32 tmp2;
u32 result;
u16 xy[2];
if (++ent->knockbackSpeed > 8) {
ent->knockbackSpeed = 0;
sub_0806EF14(ent);
}
LinearMoveUpdate(ent);
sub_0806EF4C(ent, xy);
tmp1 = sub_080041DC(ent, xy[0], xy[1]);
tmp2 = ent->speed;
if (tmp2 < 0)
tmp2 = -tmp2;
if ((u32)tmp2 / 8 <= tmp1)
result = 0;
else
result = sub_0806EF74(ent, 3);
return result;
}
u32 sub_0806EED0(Entity* ent) {
if (!--ent->knockbackSpeed)
return sub_0806EF74(ent, 2);
return 0;
}
u32 sub_0806EEF4(Entity* ent) {
if (ent->frame & ANIM_DONE)
return sub_0806EF74(ent, 1);
return 0;
}
static void sub_0806EF14(Entity* ent) {
u16 xy[2];
sub_0806EF4C(ent, xy);
ent->direction = sub_080045B4(ent, xy[0], xy[1]);
if ((ent->collisionFlags & 1) == 0)
ent->knockbackDirection = GetAnimationStateForDirection4(ent->direction);
}
static void sub_0806EF4C(Entity* ent, u16* xy) {
u16* src = &((u16*)ent->child)[ent->hitType];
xy[0] = gRoomControls.origin_x + src[1];
xy[1] = gRoomControls.origin_y + src[2];
}
static u32 sub_0806EF74(Entity* ent, u32 a2) {
ent->hitType += a2;
return sub_0806EF88(ent);
}
u32 sub_0806EF88(Entity* ent) {
static u32 (*const gUnk_08114F0C[])(Entity*, u16*) = {
sub_0806EFAC, sub_0806EFBC, sub_0806EFCC, sub_0806EFDC, sub_0806F014,
sub_0806F02C, sub_0806F048, sub_0806F050, sub_0806F064,
};
u8* v1 = (u8*)&((u16*)ent->child)[ent->hitType];
return gUnk_08114F0C[*v1](ent, (u16*)v1);
}
u32 sub_0806EFAC(Entity* ent, u16* a2) {
ent->hitType = 0;
return sub_0806EF88(ent);
}
u32 sub_0806EFBC(Entity* ent, u16* a2) {
ent->carryFlags = 1;
ent->knockbackSpeed = 8;
return 0;
}
u32 sub_0806EFCC(Entity* ent, u16* a2) {
ent->carryFlags = 2;
ent->knockbackSpeed = a2[1];
return 0;
}
u32 sub_0806EFDC(Entity* ent, u16* a2) {
if (*a2 >> 8) {
ent->collisionFlags &= ~1;
} else {
ent->collisionFlags |= 1;
}
ent->hitType++;
return sub_0806EF88(ent);
}
u32 sub_0806F014(Entity* ent, u16* a2) {
ent->speed = a2[1];
ent->hitType += 2;
return sub_0806EF88(ent);
}
u32 sub_0806F02C(Entity* ent, u16* a2) {
ent->knockbackDirection = *a2 >> 8;
ent->hitType++;
return sub_0806EF88(ent);
}
u32 sub_0806F048(Entity* ent, u16* a2) {
ent->carryFlags = 3;
return 0;
}
u32 sub_0806F050(Entity* ent, u16* a2) {
(u16*)ent->child = a2 + 1;
ent->hitType = 0;
return sub_0806EF88(ent);
}
u32 sub_0806F064(Entity* ent, u16* a2) {
ent->carryFlags = 0;
ent->hitType++;
return *a2 >> 8;
}
s32 sub_0806F078(Entity* ent, s32 a2) {
if (a2 != ent->animIndex) {
if (ent->spriteAnimation[0])
InitAnimationForceUpdate(ent, a2);
else
InitializeAnimation(ent, a2);
return 1;
}
return 0;
}
void CollideFollowers(void) {
LinkedList* entityList = gEntityLists + 7;
Entity* currentEntity;
u32 val;
for (currentEntity = entityList->first, val = 0; currentEntity != (Entity*)entityList;
currentEntity = currentEntity->next) {
val++;
}
if (val <= 1)
return;
for (currentEntity = entityList->first; currentEntity != (Entity*)entityList; currentEntity = currentEntity->next) {
Entity* nextEnt;
if ((currentEntity->flags & ENT_DID_INIT) == 0)
continue;
if ((currentEntity->followerFlag & 1) == 0)
continue;
for (nextEnt = currentEntity->next; nextEnt != (Entity*)entityList; nextEnt = nextEnt->next) {
if ((nextEnt->flags & ENT_DID_INIT) == 0)
continue;
if ((nextEnt->followerFlag & 1) == 0)
continue;
sub_08004484(currentEntity, nextEnt);
}
}
}
void InitializeNPCFusion(Entity* entity) {
u32 fuserId = GetFuserId(entity);
NPCData* data = gUnk_08001A7C[fuserId];
InitializeFuseInfo(entity, data->textIndex, data->cancelledTextIndex, data->fusingTextIndex);
gPlayerState.controlMode = CONTROL_DISABLED;
}
// Returns -1, 0, or 1
u32 UpdateFuseInteraction(Entity* entity) {
u32 result;
PerformFuseAction();
result = -1;
switch (gFuseInfo.fusionState) {
default:
result = 0;
break;
case FUSION_STATE_2:
gPlayerState.controlMode = CONTROL_DISABLED;
result = 1;
case FUSION_STATE_1:
PlayerResetStateFromFusion();
gPlayerState.controlMode = CONTROL_1;
break;
}
return result;
}
void MarkFuserDone(Entity* entity) {
u32 fuserId = GetFuserId(entity);
if (fuserId != 0)
gSave.kinstones.fuserOffers[fuserId] = KINSTONE_FUSER_DONE;
}
void ShowNPCDialogue(Entity* ent, const Dialog* dia) {
u32 fromSelf;
s32 temp;
u32 uVar2;
u32 uVar3;
u32 flagType;
s32 uVar4;
bool32 isFlagSet;
temp = *(((u16*)dia) + 1);
fromSelf = (temp >> 4) & 1;
temp &= 0xf;
switch (temp) {
case DIALOG_SET_FLAG:
uVar3 = (int)*(u32*)dia;
temp = (s32)uVar3 >> 0xc & 0xf;
uVar3 = uVar3 & 0xfff;
isFlagSet = FALSE;
switch (temp) {
case DIALOG_ROOM_FLAG:
isFlagSet = CheckRoomFlag(uVar3);
SetRoomFlag(uVar3);
break;
case DIALOG_LOCAL_FLAG:
isFlagSet = CheckLocalFlag(uVar3);
SetLocalFlag(uVar3);
break;
case DIALOG_GLOBAL_FLAG:
isFlagSet = CheckGlobalFlag(uVar3);
SetGlobalFlag(uVar3);
break;
}
if (!isFlagSet) {
uVar2 = dia->data.indices.b;
} else {
uVar2 = dia->data.indices.a;
}
break;
case DIALOG_TOGGLE_FLAG:
uVar3 = (int)*(u32*)dia;
uVar4 = (s32)uVar3 >> 0xc & 0xf;
uVar3 = uVar3 & 0xfff;
isFlagSet = 0;
switch (uVar4) {
case DIALOG_ROOM_FLAG:
isFlagSet = CheckRoomFlag(uVar3);
if (!isFlagSet) {
SetRoomFlag(uVar3);
} else {
ClearRoomFlag(uVar3);
}
break;
case DIALOG_LOCAL_FLAG:
isFlagSet = CheckLocalFlag(uVar3);
if (!isFlagSet) {
SetLocalFlag(uVar3);
} else {
ClearLocalFlag(uVar3);
}
break;
case DIALOG_GLOBAL_FLAG:
isFlagSet = CheckGlobalFlag(uVar3);
if (!isFlagSet) {
SetGlobalFlag(uVar3);
} else {
ClearGlobalFlag(uVar3);
}
break;
}
if (!isFlagSet) {
uVar2 = dia->data.indices.b;
} else {
uVar2 = dia->data.indices.a;
}
break;
case DIALOG_CHECK_FLAG:
uVar3 = (int)*(u32*)dia;
uVar4 = (s32)uVar3 >> 0xc & 0xf;
uVar3 = uVar3 & 0xfff;
isFlagSet = FALSE;
{
bool32 local;
switch (uVar4) {
case DIALOG_ROOM_FLAG:
isFlagSet = CheckRoomFlag(uVar3);
break;
case DIALOG_LOCAL_FLAG:
isFlagSet = CheckLocalFlag(uVar3);
break;
case DIALOG_GLOBAL_FLAG:
isFlagSet = CheckGlobalFlag(uVar3);
break;
case DIALOG_KINSTONE:
isFlagSet = CheckKinstoneFused(uVar3);
break;
case DIALOG_INVENTORY:
local = GetInventoryValue(uVar3);
if (local != 0) {
local = 1;
}
isFlagSet = local;
break;
}
}
if (!isFlagSet) {
uVar2 = dia->data.indices.b;
} else {
uVar2 = dia->data.indices.a;
}
break;
case DIALOG_CALL_FUNC:
if (dia->data.func != 0) {
dia->data.func(ent);
return;
}
default:
case DIALOG_NONE:
uVar2 = 0;
break;
case DIALOG_NORMAL:
uVar2 = dia->data.indices.b;
break;
case DIALOG_MINISH:
if ((gPlayerState.flags & PL_MINISH) != 0) {
uVar2 = dia->data.indices.b;
} else {
uVar2 = dia->data.indices.a;
}
break;
}
if (fromSelf) {
MessageNoOverlap(uVar2, ent);
} else {
MessageFromTarget(uVar2);
}
}
const u8 gUnk_08114F30[] = {
0,
0,
1,
2,
};
const u8 gUnk_08114F34[] = {
1,
2,
3,
3,
};