#include "global.h" #include "event_data.h" #include "event_object_movement.h" #include "field_player_avatar.h" #include "fieldmap.h" #include "rfu_union_tool.h" #include "script.h" #include "task.h" #include "constants/flags.h" #include "constants/event_object_movement.h" #include "constants/union_room.h" #include "constants/event_objects.h" static EWRAM_DATA struct UnionObj * UnionObjWork = NULL; static EWRAM_DATA u32 sUnionObjRefreshTimer = 0; static u8 StartUnionObjAnimTask(void); static u32 RfuUnionGroupMemberIsInvisible(u32 group, u32 member); static void UnionPartnerObjectSetFacing(s32 member, s32 group, u8 direction); static const u8 sUnionObjectEventGfxIds[][10] = { [MALE] = { OBJ_EVENT_GFX_COOLTRAINER_M, OBJ_EVENT_GFX_BLACKBELT, OBJ_EVENT_GFX_CAMPER, OBJ_EVENT_GFX_YOUNGSTER, OBJ_EVENT_GFX_BOY, OBJ_EVENT_GFX_BUG_CATCHER, OBJ_EVENT_GFX_MAN, OBJ_EVENT_GFX_ROCKER }, [FEMALE] = { OBJ_EVENT_GFX_COOLTRAINER_F, OBJ_EVENT_GFX_CHANNELER, OBJ_EVENT_GFX_PICNICKER, OBJ_EVENT_GFX_LASS, OBJ_EVENT_GFX_WOMAN_1, OBJ_EVENT_GFX_BATTLE_GIRL, OBJ_EVENT_GFX_WOMAN_2, OBJ_EVENT_GFX_BEAUTY } }; static const s16 sUnionPartnerCoords[][2] = { { 4, 6}, {13, 8}, {10, 6}, { 1, 8}, {13, 4}, { 7, 4}, { 1, 4}, { 7, 8} }; static const s8 sFacingDirectionOffsets[][2] = { [DIR_NONE] = { 0, 0}, [DIR_SOUTH] = { 1, 0}, [DIR_NORTH] = { 0, -1}, [DIR_WEST] = {-1, 0}, [DIR_EAST] = { 0, 1} }; static const u8 sOppositeFacingDirection[] = { [DIR_NONE] = DIR_NONE, [DIR_SOUTH] = DIR_NORTH, [DIR_NORTH] = DIR_SOUTH, [DIR_WEST] = DIR_EAST, [DIR_EAST] = DIR_WEST }; static const u8 sUnionGroupMemberFacings[] = { DIR_SOUTH, DIR_WEST, DIR_SOUTH, DIR_EAST, DIR_NORTH }; static const u8 sUnionRoomLocalIds[] = { 9, 8, 7, 2, 6, 5, 4, 3 }; static const u16 sUnref_8457128[] = { 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A }; static bool32 is_walking_or_running(void) { if (gPlayerAvatar.tileTransitionState == 2 || gPlayerAvatar.tileTransitionState == 0) { return TRUE; } else { return FALSE; } } static u8 GetUnionRoomPlayerGraphicsId(u32 gender, u32 id) { return sUnionObjectEventGfxIds[gender][id % 8]; } static void GetUnionRoomPlayerFacingCoords(u32 group, u32 member, s32 * xp, s32 * yp) { *xp = sUnionPartnerCoords[group][0] + sFacingDirectionOffsets[member][0] + 7; *yp = sUnionPartnerCoords[group][1] + sFacingDirectionOffsets[member][1] + 7; } static bool32 IsUnionRoomPlayerFacingTileAt(u32 group, u32 member, s32 x, s32 y) { if (sUnionPartnerCoords[group][0] + sFacingDirectionOffsets[member][0] + 7 != x) { return FALSE; } else if (sUnionPartnerCoords[group][1] + sFacingDirectionOffsets[member][1] + 7 != y) { return FALSE; } else { return TRUE; } } static bool32 IsUnionRoomPlayerHidden(u32 player_idx) { return FlagGet(FLAG_HIDE_UNION_ROOM_PLAYER_1 + player_idx); } static void HideUnionRoomPlayer(u32 player_idx) { FlagSet(FLAG_HIDE_UNION_ROOM_PLAYER_1 + player_idx); } static void ShowUnionRoomPlayer(u32 player_idx) { FlagClear(FLAG_HIDE_UNION_ROOM_PLAYER_1 + player_idx); } static void SetUnionRoomPlayerGfx(u32 playerIdx, u32 gfxId) { VarSet(VAR_OBJ_GFX_ID_0 + playerIdx, gfxId); } static void CreateUnionRoomPlayerObjectEvent(u32 playerIdx) { TrySpawnObjectEvent(sUnionRoomLocalIds[playerIdx], gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); } static void RemoveUnionRoomPlayerObjectEvent(u32 playerIdx) { RemoveObjectEventByLocalIdAndMap(sUnionRoomLocalIds[playerIdx], gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup); } static bool32 SetUnionRoomPlayerEnterExitMovement(u32 playerIdx, const u8 * movement) { u8 objectId; struct ObjectEvent * object; if (TryGetObjectEventIdByLocalIdAndMap(sUnionRoomLocalIds[playerIdx], gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &objectId)) { return FALSE; } object = &gObjectEvents[objectId]; if (ObjectEventIsMovementOverridden(object)) { return FALSE; } if (ObjectEventSetHeldMovement(object, *movement)) { AGB_ASSERT_EX(0, ABSPATH("rfu_union_tool.c"), 387); return FALSE; } return TRUE; } static bool32 TryReleaseUnionRoomPlayerObjectEvent(u32 playerIdx) { u8 objectId; struct ObjectEvent * object; if (TryGetObjectEventIdByLocalIdAndMap(sUnionRoomLocalIds[playerIdx], gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &objectId)) { return TRUE; } object = &gObjectEvents[objectId]; if (!ObjectEventClearHeldMovementIfFinished(object)) { return FALSE; } if (!ScriptContext2_IsEnabled()) { UnfreezeObjectEvent(object); } else { FreezeObjectEvent(object); } return TRUE; } u8 ZeroUnionObjWork(struct UnionObj * ptr) { s32 i; sUnionObjRefreshTimer = 0; UnionObjWork = ptr; AGB_ASSERT_EX(UnionObjWork != NULL, ABSPATH("rfu_union_tool.c"), 442) for (i = 0; i < 8; i++) { ptr[i].state = 0; ptr[i].gfxId = 0; ptr[i].animState = 0; ptr[i].schedAnim = 0; } return StartUnionObjAnimTask(); } static const u8 sMovement_UnionPlayerExit[2] = { MOVEMENT_ACTION_FLY_UP, MOVEMENT_ACTION_STEP_END }; static bool32 AnimateUnionRoomPlayerDespawn(s8 * a0, u32 playerIdx, struct UnionObj * ptr) { switch (*a0) { case 0: if (SetUnionRoomPlayerEnterExitMovement(playerIdx, sMovement_UnionPlayerExit) == TRUE) { HideUnionRoomPlayer(playerIdx); (*a0)++; } break; case 1: if (TryReleaseUnionRoomPlayerObjectEvent(playerIdx)) { RemoveUnionRoomPlayerObjectEvent(playerIdx); HideUnionRoomPlayer(playerIdx); *a0 = 0; return TRUE; } break; } return FALSE; } static const u8 sMovement_UnionPlayerEnter[2] = { MOVEMENT_ACTION_FLY_DOWN, MOVEMENT_ACTION_STEP_END }; static bool32 AnimateUnionRoomPlayerSpawn(s8 * state_p, u32 playerIdx, struct UnionObj * ptr) { s16 x, y; switch (*state_p) { case 0: if (!is_walking_or_running()) { break; } PlayerGetDestCoords(&x, &y); if (IsUnionRoomPlayerFacingTileAt(playerIdx, 0, x, y) == TRUE) { break; } player_get_pos_including_state_based_drift(&x, &y); if (IsUnionRoomPlayerFacingTileAt(playerIdx, 0, x, y) == TRUE) { break; } SetUnionRoomPlayerGfx(playerIdx, ptr->gfxId); CreateUnionRoomPlayerObjectEvent(playerIdx); ShowUnionRoomPlayer(playerIdx); (*state_p)++; // fallthrough case 3: // incorrect? if (SetUnionRoomPlayerEnterExitMovement(playerIdx, sMovement_UnionPlayerEnter) == 1) { (*state_p)++; } break; case 2: if (TryReleaseUnionRoomPlayerObjectEvent(playerIdx)) { *state_p = 0; return TRUE; } break; } return FALSE; } static bool32 SpawnGroupLeader(u32 playerIdx, u32 gender, u32 idMod256) { struct UnionObj * ptr = &UnionObjWork[playerIdx]; AGB_ASSERT_EX(UnionObjWork != NULL, ABSPATH("rfu_union_tool.c"), 561) ptr->schedAnim = UNION_ROOM_SPAWN_IN; ptr->gfxId = GetUnionRoomPlayerGraphicsId(gender, idMod256); if (ptr->state == 0) { return TRUE; } else { return FALSE; } } static bool32 DespawnGroupLeader(u32 playerIdx) { struct UnionObj * ptr = &UnionObjWork[playerIdx]; AGB_ASSERT_EX(UnionObjWork != NULL, ABSPATH("rfu_union_tool.c"), 577) ptr->schedAnim = UNION_ROOM_SPAWN_OUT; if (ptr->state == 1) { return TRUE; } else { return FALSE; } } static void AnimateUnionObj(u32 playerIdx, struct UnionObj * ptr) { switch (ptr->state) { case 0: if (ptr->schedAnim == UNION_ROOM_SPAWN_IN) { ptr->state = 2; ptr->animState = 0; } else { break; } // fallthrough case 2: if (!RfuUnionGroupMemberIsInvisible(playerIdx, 0) && ptr->schedAnim == 2) { ptr->state = 0; ptr->animState = 0; RemoveUnionRoomPlayerObjectEvent(playerIdx); HideUnionRoomPlayer(playerIdx); } else if (AnimateUnionRoomPlayerSpawn(&ptr->animState, playerIdx, ptr) == TRUE) { ptr->state = 1; } break; case 1: if (ptr->schedAnim == UNION_ROOM_SPAWN_OUT) { ptr->state = 3; ptr->animState = 0; } else { break; } // fallthrough case 3: if (AnimateUnionRoomPlayerDespawn(&ptr->animState, playerIdx, ptr) == TRUE) { ptr->state = 0; } break; } ptr->schedAnim = UNION_ROOM_SPAWN_NONE; } static void Task_AnimateUnionObjs(u8 taskId) { s32 i; AGB_ASSERT_EX(UnionObjWork != NULL, ABSPATH("rfu_union_tool.c"), 643) for (i = 0; i < 8; i++) { AnimateUnionObj(i, &UnionObjWork[i]); } } static u8 StartUnionObjAnimTask(void) { if (FuncIsActiveTask(Task_AnimateUnionObjs) == TRUE) { AGB_ASSERT_EX(0, ABSPATH("rfu_union_tool.c"), 655) return NUM_TASKS; } else { return CreateTask(Task_AnimateUnionObjs, 5); } } static void DestroyAnimateUnionObjsTask(void) { u8 taskId = FindTaskIdByFunc(Task_AnimateUnionObjs); if (taskId < NUM_TASKS) { DestroyTask(taskId); } } void DeleteUnionObjWorkAndStopTask(void) { s32 i; for (i = 0; i < 8; i++) { if (!IsUnionRoomPlayerHidden(i)) { RemoveUnionRoomPlayerObjectEvent(i); HideUnionRoomPlayer(i); } } UnionObjWork = NULL; DestroyAnimateUnionObjsTask(); } void CreateGroupMemberObjectsInvisible(u8 * sprite_ids, s32 group) { s32 i; for (i = 0; i < 5; i++) { s32 obj_id = 5 * group + i; sprite_ids[obj_id] = sprite_new(OBJ_EVENT_GFX_MAN, obj_id - 0x38, sUnionPartnerCoords[group][0] + sFacingDirectionOffsets[i][0], sUnionPartnerCoords[group][1] + sFacingDirectionOffsets[i][1], 3, 1); RfuUnionObjectToggleInvisibility(obj_id - 0x38, TRUE); } } void DestroyGroupMemberObjects(u8 *spriteIds) { s32 i; for (i = 0; i < 40; i++) { DestroySprite(&gSprites[spriteIds[i]]); } } void MakeGroupAssemblyAreasPassable(void) { s32 i, j, x, y; for (i = 0; i < 8; i++) { for (j = 0; j < 5; j++) { GetUnionRoomPlayerFacingCoords(i, j, &x, &y); MapGridSetMetatileImpassabilityAt(x, y, FALSE); } } } static u8 UnionPartnerObjectGetFacing(u32 member, u32 group, struct GFtgtGname * gname) { if (member != 0) { return sUnionGroupMemberFacings[member]; } else if (gname->activity == 0x45) { return DIR_SOUTH; } else { return DIR_EAST; } } static u32 RfuUnionGroupMemberIsInvisible(u32 group, u32 member) { return RfuUnionObjectIsInvisible(5 * group + member - 0x38); } static void SpawnGroupMember(u32 groupNo, u32 memberNo, u8 direction, struct GFtgtGname * gname) { s32 x, y; s32 objId = 5 * groupNo + memberNo; if (RfuUnionGroupMemberIsInvisible(groupNo, memberNo) == TRUE) { RfuUnionObjectToggleInvisibility(objId - 0x38, FALSE); RfuUnionObjectStartWarp(objId - 0x38, UNION_ROOM_SPAWN_IN); } RfuUnionObjectSetFacingDirection(objId - 0x38, direction); UnionPartnerObjectSetFacing(memberNo, groupNo, UnionPartnerObjectGetFacing(memberNo, groupNo, gname)); GetUnionRoomPlayerFacingCoords(groupNo, memberNo, &x, &y); MapGridSetMetatileImpassabilityAt(x, y, TRUE); } static void DespawnGroupMember(u32 group, u32 member) { s32 x, y; RfuUnionObjectStartWarp(5 * group + member - 0x38, UNION_ROOM_SPAWN_OUT); GetUnionRoomPlayerFacingCoords(group, member, &x, &y); MapGridSetMetatileImpassabilityAt(x, y, FALSE); } static void AssembleGroup(u32 group, struct GFtgtGname * gname) { s16 x, y, x2, y2; s32 i; PlayerGetDestCoords(&x, &y); player_get_pos_including_state_based_drift(&x2, &y2); if (RfuUnionObjectIsInvisible(5 * group - 0x38) == TRUE) { if (IsUnionRoomPlayerFacingTileAt(group, 0, x, y) == TRUE || IsUnionRoomPlayerFacingTileAt(group, 0, x2, y2) == TRUE) { return; } SpawnGroupMember(group, 0, GetUnionRoomPlayerGraphicsId(gname->playerGender, gname->unk_00.playerTrainerId[0]), gname); } for (i = 1; i < 5; i++) { if (gname->child_sprite_gender[i - 1] == 0) { DespawnGroupMember(group, i); } else if (IsUnionRoomPlayerFacingTileAt(group, i, x, y) == FALSE && IsUnionRoomPlayerFacingTileAt(group, i, x2, y2) == FALSE) { SpawnGroupMember(group, i, GetUnionRoomPlayerGraphicsId((gname->child_sprite_gender[i - 1] >> 3) & 1, gname->child_sprite_gender[i - 1] & 7), gname); } } } static void SpawnGroupLeaderAndMembers(u32 group, struct GFtgtGname * gname) { u32 i; switch (gname->activity) { case 0x40: case 0x54: SpawnGroupLeader(group, gname->playerGender, gname->unk_00.playerTrainerId[0]); for (i = 0; i < 5; i++) { DespawnGroupMember(group, i); } break; case 0x41: case 0x44: case 0x45: case 0x48: case 0x51: case 0x52: case 0x53: DespawnGroupLeader(group); AssembleGroup(group, gname); break; default: AGB_ASSERT_EX(0, ABSPATH("rfu_union_tool.c"), 979) } } static void DespawnGroupLeaderAndMembers(u32 group, struct GFtgtGname * gname) { s32 i; DespawnGroupLeader(group); for (i = 0; i < 5; i++) { DespawnGroupMember(group, i); } } static void UpdateUnionRoomPlayerSprites(struct UnkStruct_URoom * groups) { s32 i; struct UnkStruct_x20 * x20_p; sUnionObjRefreshTimer = 0; for (i = 0, x20_p = groups->field_0->arr; i < 8; i++) { if (x20_p[i].groupScheduledAnim == UNION_ROOM_SPAWN_IN) { SpawnGroupLeaderAndMembers(i, &x20_p[i].gname_uname.gname); } else if (x20_p[i].groupScheduledAnim == UNION_ROOM_SPAWN_OUT) { DespawnGroupLeaderAndMembers(i, &x20_p[i].gname_uname.gname); } } } void ScheduleUnionRoomPlayerRefresh(struct UnkStruct_URoom *uroom_p) { sUnionObjRefreshTimer = 300; } void HandleUnionRoomPlayerRefresh(struct UnkStruct_URoom *uroom_p) { if (++sUnionObjRefreshTimer > 300) { UpdateUnionRoomPlayerSprites(uroom_p); } } bool32 RfuUnionTool_GetGroupAndMemberInFrontOfPlayer(struct UnkStruct_Main0 *main0_p, s16 *member_p, s16 *group_p, u8 *spriteIds) { s16 x, y; s32 i, j; struct UnkStruct_x20 * x20_p; if (!is_walking_or_running()) { return FALSE; } GetXYCoordsOneStepInFrontOfPlayer(&x, &y); for (i = 0, x20_p = main0_p->arr; i < 8; i++) { for (j = 0; j < 5; j++) { s32 objId = 5 * i + j; if (x != sUnionPartnerCoords[i][0] + sFacingDirectionOffsets[j][0] + 7) { continue; } if (y != sUnionPartnerCoords[i][1] + sFacingDirectionOffsets[j][1] + 7) { continue; } if (RfuUnionObjectIsInvisible(objId - 0x38) != 0) { continue; } if (RfuUnionObjectIsWarping(objId - 0x38) != 0) { continue; } if (x20_p[i].groupScheduledAnim != UNION_ROOM_SPAWN_IN) { continue; } UnionPartnerObjectSetFacing(j, i, sOppositeFacingDirection[GetPlayerFacingDirection()]); *member_p = j; *group_p = i; return TRUE; } } return FALSE; } static void UnionPartnerObjectSetFacing(s32 member, s32 group, u8 direction) { TurnObjectEvent(5 * group - 0x38 + member, direction); } void UpdateUnionGroupMemberFacing(u32 member, u32 group, struct UnkStruct_Main0 *main0_p) { return UnionPartnerObjectSetFacing(member, group, UnionPartnerObjectGetFacing(member, group, &main0_p->arr[group].gname_uname.gname)); }