diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index 38481869bc..11a25d8966 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -11,6 +11,7 @@ #define DAY 1 #define NIGHT 2 +#define FUSION_TERMINATOR 0xFF #define FORM_CHANGE_TERMINATOR 0 // Form change that activates when the specified item is given to or taken from the selected Pokémon. diff --git a/include/constants/party_menu.h b/include/constants/party_menu.h index 93d5e5d57b..c860b425d4 100644 --- a/include/constants/party_menu.h +++ b/include/constants/party_menu.h @@ -80,6 +80,7 @@ #define PARTY_MSG_DO_WHAT_WITH_MAIL 25 #define PARTY_MSG_ALREADY_HOLDING_ONE 26 #define PARTY_MSG_WHICH_APPLIANCE 27 +#define PARTY_MSG_CHOOSE_SECOND_FUSION 28 #define PARTY_MSG_NONE 127 // IDs for DisplayPartyPokemonDescriptionText, to display a message in the party pokemon's box diff --git a/include/item_use.h b/include/item_use.h index a4dd92663d..5abe87f057 100644 --- a/include/item_use.h +++ b/include/item_use.h @@ -31,6 +31,7 @@ void ItemUseOutOfBattle_FormChange(u8); void ItemUseOutOfBattle_FormChange_ConsumedOnUse(u8); void ItemUseOutOfBattle_RotomCatalog(u8); void ItemUseOutOfBattle_ZygardeCube(u8); +void ItemUseOutOfBattle_Fusion(u8); void ItemUseOutOfBattle_Honey(u8); void ItemUseOutOfBattle_CannotUse(u8); void ItemUseOutOfBattle_ExpShare(u8); diff --git a/include/party_menu.h b/include/party_menu.h index 304eae1266..c50abc276a 100644 --- a/include/party_menu.h +++ b/include/party_menu.h @@ -68,6 +68,7 @@ void ItemUseCB_FormChange(u8 taskId, TaskFunc task); void ItemUseCB_FormChange_ConsumedOnUse(u8 taskId, TaskFunc task); void ItemUseCB_RotomCatalog(u8 taskId, TaskFunc task); void ItemUseCB_ZygardeCube(u8 taskId, TaskFunc task); +void ItemUseCB_Fusion(u8 taskId, TaskFunc task); const u8* GetItemEffect(u16 item); u8 GetItemEffectType(u16 item); void CB2_PartyMenuFromStartMenu(void); diff --git a/include/pokemon.h b/include/pokemon.h index 30abf8a037..ff78373fa5 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -426,6 +426,19 @@ struct FormChange u16 param3; }; +struct Fusion +{ + u16 fusionStorageIndex; + u16 itemId; + u16 targetSpecies1; + u16 targetSpecies2; + u16 fusingIntoMon; + u16 fusionMove; + u16 unfuseForgetMove; +}; + +extern const struct Fusion *const gFusionTablePointers[NUM_SPECIES]; + #define NUM_UNOWN_FORMS 28 #define GET_UNOWN_LETTER(personality) (( \ diff --git a/include/pokemon_storage_system.h b/include/pokemon_storage_system.h index eb5f9a992d..5ab1b4cb8f 100644 --- a/include/pokemon_storage_system.h +++ b/include/pokemon_storage_system.h @@ -6,6 +6,7 @@ #define IN_BOX_COLUMNS 6 // Number of columns, 5 Pokémon per column #define IN_BOX_COUNT (IN_BOX_ROWS * IN_BOX_COLUMNS) #define BOX_NAME_LENGTH 8 +#define MAX_FUSION_STORAGE 4 /* COLUMNS @@ -22,6 +23,7 @@ struct PokemonStorage /*0x0001*/ struct BoxPokemon boxes[TOTAL_BOXES_COUNT][IN_BOX_COUNT]; /*0x8344*/ u8 boxNames[TOTAL_BOXES_COUNT][BOX_NAME_LENGTH + 1]; /*0x83C2*/ u8 boxWallpapers[TOTAL_BOXES_COUNT]; + /*0x8432*/ struct Pokemon fusions[MAX_FUSION_STORAGE]; }; extern struct PokemonStorage *gPokemonStoragePtr; diff --git a/include/strings.h b/include/strings.h index cd56d71e36..9184b4663a 100644 --- a/include/strings.h +++ b/include/strings.h @@ -944,6 +944,7 @@ extern const u8 gText_UsedVar2WildLured[]; extern const u8 gText_UsedVar2WildRepelled[]; extern const u8 gText_BoxFull[]; extern const u8 gText_WontHaveEffect[]; +extern const u8 gText_NextFusionMon[]; extern const u8 gText_LevelSymbol[]; extern const u8 gText_PkmnInfo[]; diff --git a/src/data/items.h b/src/data/items.h index 984c611ca6..670d7dd2e5 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -8502,6 +8502,7 @@ const struct Item gItems[] = { .name = _("Gracidea"), .price = 0, + .importance = 1, .description = sGracideaDesc, .pocket = POCKET_KEY_ITEMS, .type = ITEM_USE_PARTY_MENU, @@ -8512,6 +8513,7 @@ const struct Item gItems[] = { .name = _("Reveal Glass"), .price = 0, + .importance = 1, .description = sRevealGlassDesc, .pocket = POCKET_KEY_ITEMS, .type = ITEM_USE_PARTY_MENU, @@ -8522,10 +8524,11 @@ const struct Item gItems[] = { .name = _("DNA Splicers"), .price = 0, + .importance = 1, .description = sDNASplicersDesc, .pocket = POCKET_KEY_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo: ItemUseOutOfBattle_FormChange_Fusion + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Fusion, }, [ITEM_ZYGARDE_CUBE] = @@ -8543,6 +8546,7 @@ const struct Item gItems[] = { .name = _("Prison Bottle"), .price = 0, + .importance = 1, .description = sPrisonBottleDesc, .pocket = POCKET_KEY_ITEMS, .type = ITEM_USE_PARTY_MENU, @@ -8553,30 +8557,33 @@ const struct Item gItems[] = { .name = _("N-Solarizer"), .price = 0, + .importance = 1, .description = sNSolarizerDesc, .pocket = POCKET_KEY_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo: ItemUseOutOfBattle_FormChange_Fusion + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Fusion, }, [ITEM_N_LUNARIZER] = { .name = _("N-Lunarizer"), .price = 0, + .importance = 1, .description = sNLunarizerDesc, .pocket = POCKET_KEY_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo: ItemUseOutOfBattle_FormChange_Fusion + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Fusion, }, [ITEM_REINS_OF_UNITY] = { .name = _("ReinsOfUnity"), .price = 0, + .importance = 1, .description = sReinsOfUnityDesc, .pocket = POCKET_KEY_ITEMS, - .type = ITEM_USE_BAG_MENU, - .fieldUseFunc = ItemUseOutOfBattle_CannotUse, // Todo: ItemUseOutOfBattle_FormChange_Fusion + .type = ITEM_USE_PARTY_MENU, + .fieldUseFunc = ItemUseOutOfBattle_Fusion, }, // Battle Mechanic Key Items diff --git a/src/data/party_menu.h b/src/data/party_menu.h index b12ca40651..168520a48c 100644 --- a/src/data/party_menu.h +++ b/src/data/party_menu.h @@ -658,6 +658,7 @@ static const u8 *const sActionStringTable[] = [PARTY_MSG_DO_WHAT_WITH_MAIL] = gText_DoWhatWithMail, [PARTY_MSG_ALREADY_HOLDING_ONE] = gText_AlreadyHoldingOne, [PARTY_MSG_WHICH_APPLIANCE] = gText_WhichAppliance, + [PARTY_MSG_CHOOSE_SECOND_FUSION] = gText_NextFusionMon, }; static const u8 *const sDescriptionStringTable[] = diff --git a/src/data/pokemon/form_change_table_pointers.h b/src/data/pokemon/form_change_table_pointers.h index 42be2a904f..0180c16ca1 100644 --- a/src/data/pokemon/form_change_table_pointers.h +++ b/src/data/pokemon/form_change_table_pointers.h @@ -375,3 +375,28 @@ const struct FormChange *const gFormChangeTablePointers[NUM_SPECIES] = [SPECIES_OGERPON_CORNERSTONE_MASK_TERA] = sOgerponFormChangeTable, #endif }; + +const struct Fusion *const gFusionTablePointers[NUM_SPECIES] = +{ +#if P_GEN_5_POKEMON == TRUE + [SPECIES_KYUREM] = sKyuremFusionTable, + [SPECIES_KYUREM_BLACK] = sKyuremFusionTable, + [SPECIES_KYUREM_WHITE] = sKyuremFusionTable, + [SPECIES_RESHIRAM] = sKyuremFusionTable, + [SPECIES_ZEKROM] = sKyuremFusionTable, +#endif +#if P_GEN_7_POKEMON == TRUE + [SPECIES_NECROZMA] = sNecrozmaFusionTable, + [SPECIES_NECROZMA_DAWN_WINGS] = sNecrozmaFusionTable, + [SPECIES_NECROZMA_DUSK_MANE] = sNecrozmaFusionTable, + [SPECIES_SOLGALEO] = sNecrozmaFusionTable, + [SPECIES_LUNALA] = sNecrozmaFusionTable, +#endif +#if P_GEN_8_POKEMON == TRUE + [SPECIES_CALYREX] = sCalyrexFusionTable, + [SPECIES_CALYREX_ICE_RIDER] = sCalyrexFusionTable, + [SPECIES_CALYREX_SHADOW_RIDER] = sCalyrexFusionTable, + [SPECIES_SPECTRIER] = sCalyrexFusionTable, + [SPECIES_GLASTRIER] = sCalyrexFusionTable, +#endif +}; diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index ccb736bba8..7080ad2444 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -407,6 +407,12 @@ static const struct FormChange sLandorusFormChangeTable[] = { {FORM_CHANGE_TERMINATOR}, }; +static const struct Fusion sKyuremFusionTable[] = { + {0, ITEM_DNA_SPLICERS, SPECIES_KYUREM, SPECIES_RESHIRAM, SPECIES_KYUREM_WHITE}, + {0, ITEM_DNA_SPLICERS, SPECIES_KYUREM, SPECIES_ZEKROM, SPECIES_KYUREM_BLACK}, + {FUSION_TERMINATOR}, +}; + static const struct FormChange sKeldeoFormChangeTable[] = { {FORM_CHANGE_MOVE, SPECIES_KELDEO_RESOLUTE, MOVE_SECRET_SWORD, WHEN_LEARNED}, {FORM_CHANGE_MOVE, SPECIES_KELDEO_ORDINARY, MOVE_SECRET_SWORD, WHEN_FORGOTTEN}, @@ -588,6 +594,13 @@ static const struct FormChange sMiniorYellowFormChangeTable[] = { {FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_YELLOW}, {FORM_CHANGE_TERMINATOR}, }; + +static const struct Fusion sNecrozmaFusionTable[] = { + {1, ITEM_N_SOLARIZER, SPECIES_NECROZMA, SPECIES_SOLGALEO, SPECIES_NECROZMA_DUSK_MANE, MOVE_SUNSTEEL_STRIKE, MOVE_CONFUSION}, + {2, ITEM_N_LUNARIZER, SPECIES_NECROZMA, SPECIES_LUNALA, SPECIES_NECROZMA_DAWN_WINGS, MOVE_MOONGEIST_BEAM, MOVE_CONFUSION}, + {FUSION_TERMINATOR}, +}; + static const struct FormChange sNecrozmaDuskManeFormChangeTable[] = { {FORM_CHANGE_BATTLE_ULTRA_BURST, SPECIES_NECROZMA_ULTRA, ITEM_ULTRANECROZIUM_Z}, {FORM_CHANGE_TERMINATOR}, @@ -635,6 +648,12 @@ static const struct FormChange sZamazentaFormChangeTable[] = { {FORM_CHANGE_TERMINATOR}, }; +static const struct Fusion sCalyrexFusionTable[] = { + {3, ITEM_REINS_OF_UNITY, SPECIES_CALYREX, SPECIES_GLASTRIER, SPECIES_CALYREX_ICE_RIDER, MOVE_GLACIAL_LANCE, MOVE_CONFUSION}, + {3, ITEM_REINS_OF_UNITY, SPECIES_CALYREX, SPECIES_SPECTRIER, SPECIES_CALYREX_SHADOW_RIDER, MOVE_ASTRAL_BARRAGE, MOVE_CONFUSION}, + {FUSION_TERMINATOR}, +}; + static const struct FormChange sEnamorusFormChangeTable[] = { {FORM_CHANGE_ITEM_USE, SPECIES_ENAMORUS_INCARNATE, ITEM_REVEAL_GLASS}, {FORM_CHANGE_ITEM_USE, SPECIES_ENAMORUS_THERIAN, ITEM_REVEAL_GLASS}, diff --git a/src/item_use.c b/src/item_use.c index 602bda27af..11d92e7427 100644 --- a/src/item_use.c +++ b/src/item_use.c @@ -1344,6 +1344,13 @@ void ItemUseOutOfBattle_ZygardeCube(u8 taskId) } } +void ItemUseOutOfBattle_Fusion(u8 taskId) +{ + gItemUseCB = ItemUseCB_Fusion; + gTasks[taskId].data[0] = FALSE; + SetUpItemUseCallback(taskId); +} + void Task_UseHoneyOnField(u8 taskId) { //ResetInitialPlayerAvatarState(); diff --git a/src/party_menu.c b/src/party_menu.c index 798f9541d2..37e042a96a 100644 --- a/src/party_menu.c +++ b/src/party_menu.c @@ -247,7 +247,9 @@ void (*gItemUseCB)(u8, TaskFunc); static void ResetPartyMenu(void); static void CB2_InitPartyMenu(void); +static void CB2_ReloadPartyMenu(void); static bool8 ShowPartyMenu(void); +static bool8 ReloadPartyMenu(void); static void SetPartyMonsAllowedInMinigame(void); static void ExitPartyMenu(void); static bool8 AllocPartyMenuBg(void); @@ -552,6 +554,25 @@ static void InitPartyMenu(u8 menuType, u8 layout, u8 partyAction, bool8 keepCurs } } +static void RefreshPartyMenu(void) //Refreshes the party menu without restarting tasks +{ + u16 i; + + sPartyMenuInternal->exitCallback = NULL; + sPartyMenuInternal->lastSelectedSlot = 0; + sPartyMenuInternal->spriteIdConfirmPokeball = 0x7F; + sPartyMenuInternal->spriteIdCancelPokeball = 0x7F; + + for (i = 0; i < ARRAY_COUNT(sPartyMenuInternal->data); i++) + sPartyMenuInternal->data[i] = 0; + for (i = 0; i < ARRAY_COUNT(sPartyMenuInternal->windowId); i++) + sPartyMenuInternal->windowId[i] = WINDOW_NONE; + + gTextFlags.autoScroll = 0; + CalculatePlayerPartyCount(); + SetMainCallback2(CB2_ReloadPartyMenu); +} + static void CB2_UpdatePartyMenu(void) { RunTasks(); @@ -577,6 +598,15 @@ static void CB2_InitPartyMenu(void) } } +static void CB2_ReloadPartyMenu(void) +{ + while (TRUE) + { + if (MenuHelpers_ShouldWaitForLinkRecv() == TRUE || ReloadPartyMenu() == TRUE || MenuHelpers_IsLinkActive() == TRUE) + break; + } +} + static bool8 ShowPartyMenu(void) { switch (gMain.state) @@ -701,6 +731,119 @@ static bool8 ShowPartyMenu(void) return FALSE; } +static bool8 ReloadPartyMenu(void) +{ + switch (gMain.state) + { + case 0: + SetVBlankHBlankCallbacksToNull(); + ClearScheduledBgCopiesToVram(); + gMain.state++; + break; + case 1: + ScanlineEffect_Stop(); + gMain.state++; + break; + case 2: + ResetPaletteFade(); + gPaletteFade.bufferTransferDisabled = TRUE; + gMain.state++; + break; + case 3: + ResetSpriteData(); + gMain.state++; + break; + case 4: + FreeAllSpritePalettes(); + gMain.state++; + break; + case 5: + SetPartyMonsAllowedInMinigame(); + gMain.state++; + break; + case 6: + if (!AllocPartyMenuBg()) + { + ExitPartyMenu(); + return TRUE; + } + else + { + sPartyMenuInternal->data[0] = 0; + gMain.state++; + } + break; + case 7: + if (AllocPartyMenuBgGfx()) + gMain.state++; + break; + case 8: + InitPartyMenuWindows(gPartyMenu.layout); + gMain.state++; + break; + case 9: + InitPartyMenuBoxes(gPartyMenu.layout); + sPartyMenuInternal->data[0] = 0; + gMain.state++; + break; + case 10: + LoadHeldItemIcons(); + gMain.state++; + break; + case 11: + LoadPartyMenuPokeballGfx(); + gMain.state++; + break; + case 12: + LoadPartyMenuAilmentGfx(); + gMain.state++; + break; + case 13: + LoadMonIconPalettes(); + gMain.state++; + break; + case 14: + if (CreatePartyMonSpritesLoop()) + { + sPartyMenuInternal->data[0] = 0; + gMain.state++; + } + break; + case 15: + if (RenderPartyMenuBoxes()) + { + sPartyMenuInternal->data[0] = 0; + gMain.state++; + } + break; + case 16: + CreateCancelConfirmPokeballSprites(); + gMain.state++; + break; + case 17: + CreateCancelConfirmWindows(sPartyMenuInternal->chooseHalf); + gMain.state++; + break; + case 18: + gMain.state++; + break; + case 19: + BlendPalettes(PALETTES_ALL, 16, RGB_WHITEALPHA); + gPaletteFade.bufferTransferDisabled = FALSE; + gMain.state++; + break; + case 20: + BeginNormalPaletteFade(PALETTES_ALL, 0, 16, 0, RGB_WHITEALPHA); + gMain.state++; + break; + default: + SetVBlankCallback(VBlankCB_PartyMenu); + SetMainCallback2(CB2_UpdatePartyMenu); + return TRUE; + } + return FALSE; +} + static void ExitPartyMenu(void) { BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_BLACK); @@ -5707,11 +5850,44 @@ void ItemUseCB_EvolutionStone(u8 taskId, TaskFunc task) } } +#define FUSE_MON 1 +#define UNFUSE_MON 2 +#define SECOND_FUSE_MON 3 + #define tState data[0] #define tTargetSpecies data[1] #define tAnimWait data[2] #define tNextFunc 3 +#define fusionType data[7] +#define firstFusion data[8] +#define firstFusionSlot data[9] +#define fusionResult data[10] +#define secondFusionSlot data[11] +#define unfuseSecondMon data[12] +#define moveToLearn data[13] +#define forgetMove data[14] +#define storageIndex data[15] + +static void Task_TryItemUseFusionChange(u8 taskId); +static void SpriteCB_FormChangeIconMosaic(struct Sprite *sprite); + +u8 IsFusionMon(u16 species) +{ + u16 i; + const struct Fusion *itemFusion = gFusionTablePointers[species]; + for (i = 0; itemFusion[i].fusionStorageIndex != FUSION_TERMINATOR; i++) + { + if (itemFusion[i].fusingIntoMon == species) + return UNFUSE_MON; + else if (itemFusion[i].targetSpecies1 == species) + return FUSE_MON; + else if (itemFusion[i].targetSpecies2 == species) + return SECOND_FUSE_MON; + } + return FALSE; +} + void FormChangeTeachMove(u8 taskId, u32 move, u32 slot) { struct Pokemon *mon; @@ -5771,6 +5947,250 @@ bool32 DoesMonHaveAnyMoves(struct Pokemon *mon) return FALSE; } +bool32 TryItemUseFusionChange(u8 taskId, TaskFunc task) +{ + u16 targetSpecies = gTasks[taskId].fusionResult; + s8 *slotPtr = GetCurrentPartySlotPtr(); + *slotPtr = gTasks[taskId].firstFusionSlot; + if (gTasks[taskId].fusionType == FUSE_MON) + AnimatePartySlot(gTasks[taskId].secondFusionSlot, 0); + AnimatePartySlot(*slotPtr, 1); + + if (targetSpecies != SPECIES_NONE) + { + gPartyMenuUseExitCallback = TRUE; + SetWordTaskArg(taskId, tNextFunc, (u32)task); + gTasks[taskId].func = Task_TryItemUseFusionChange; + gTasks[taskId].tState = 0; + gTasks[taskId].tTargetSpecies = targetSpecies; + gTasks[taskId].tAnimWait = 0; + return TRUE; + } + else + { + gPartyMenuUseExitCallback = FALSE; + PlaySE(SE_SELECT); + DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE); + ScheduleBgCopyTilemapToVram(2); + gTasks[taskId].func = task; + return FALSE; + } +} + +static void Task_TryItemUseFusionChange(u8 taskId) +{ + struct Pokemon *mon = &gPlayerParty[gTasks[taskId].firstFusionSlot]; + struct Sprite *icon = &gSprites[sPartyMenuBoxes[gTasks[taskId].firstFusionSlot].monSpriteId]; + struct Pokemon *mon2; + struct Sprite *icon2 = &gSprites[sPartyMenuBoxes[gTasks[taskId].secondFusionSlot].monSpriteId]; + u16 targetSpecies; + + switch (gTasks[taskId].tState) + { + case 0: + if (gTasks[taskId].fusionType == FUSE_MON) + { + mon2 = &gPlayerParty[gTasks[taskId].secondFusionSlot]; + CopyMon(&gPokemonStoragePtr->fusions[gTasks[taskId].storageIndex], mon2, sizeof(*mon2)); + ZeroMonData(&gPlayerParty[gTasks[taskId].secondFusionSlot]); + } + else + { + mon2 = &gPokemonStoragePtr->fusions[gTasks[taskId].storageIndex]; + GiveMonToPlayer(mon2); + ZeroMonData(&gPokemonStoragePtr->fusions[gTasks[taskId].storageIndex]); + } + targetSpecies = gTasks[taskId].tTargetSpecies; + SetMonData(mon, MON_DATA_SPECIES, &targetSpecies); + CalculateMonStats(mon); + CompactPartySlots(); + CalculatePlayerPartyCount(); + gTasks[taskId].tState++; + PlaySE(SE_M_TELEPORT); + break; + case 1: + targetSpecies = gTasks[taskId].tTargetSpecies; + if (gTasks[taskId].tAnimWait == 0) + { + icon->oam.mosaic = TRUE; + icon->data[0] = 10; + icon->data[1] = 1; + icon->data[2] = taskId; + icon->callback = SpriteCB_FormChangeIconMosaic; + SetGpuReg(REG_OFFSET_MOSAIC, (icon->data[0] << 12) | (icon->data[1] << 8)); + if (gTasks[taskId].fusionType == FUSE_MON) + { + icon2->oam.mosaic = TRUE; + icon2->data[0] = 10; + icon2->data[1] = 1; + icon2->data[2] = taskId; + icon2->callback = SpriteCB_FormChangeIconMosaic; + SetGpuReg(REG_OFFSET_MOSAIC, (icon2->data[0] << 12) | (icon2->data[1] << 8)); + } + } + + if (++gTasks[taskId].tAnimWait == 60) + { + BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, RGB_WHITEALPHA); + gTasks[taskId].tState++; + } + break; + case 2: + if (gPaletteFade.active) + break; + if (gTasks[taskId].fusionType == FUSE_MON && gTasks[taskId].firstFusionSlot > gTasks[taskId].secondFusionSlot) + { + gTasks[taskId].firstFusionSlot--; + gPartyMenu.slotId--; + } + RefreshPartyMenu(); + gTasks[taskId].tState++; + break; + case 3: + BeginNormalPaletteFade(PALETTES_ALL, 16, 0, 0, RGB_WHITEALPHA); + gTasks[taskId].tState++; + break; + case 4: + targetSpecies = gTasks[taskId].tTargetSpecies; + PlayCry_Normal(targetSpecies, 0); + gTasks[taskId].tState++; + break; + case 5: + if (IsCryFinished()) + { + GetMonNickname(mon, gStringVar1); + StringExpandPlaceholders(gStringVar4, gText_PkmnTransformed); + DisplayPartyMenuMessage(gStringVar4, FALSE); + ScheduleBgCopyTilemapToVram(2); + gTasks[taskId].tState++; + } + break; + case 6: + if (!IsPartyMenuTextPrinterActive()) + { + if (gTasks[taskId].moveToLearn != 0) + { + if (gTasks[taskId].fusionType == FUSE_MON) + FormChangeTeachMove(taskId, gTasks[taskId].moveToLearn, gTasks[taskId].firstFusionSlot); + else + { + DeleteMove(mon, gTasks[taskId].forgetMove); + if (!DoesMonHaveAnyMoves(mon)) + FormChangeTeachMove(taskId, gTasks[taskId].moveToLearn, gTasks[taskId].firstFusionSlot); + } + } + gTasks[taskId].tState++; + } + break; + case 7: + gTasks[taskId].func = (void *)GetWordTaskArg(taskId, tNextFunc); + break; + } +} + +void ItemUseCB_Fusion(u8 taskId, TaskFunc taskFunc) +{ + u16 i; + struct Task *task = &gTasks[taskId]; + u16 species = GetMonData(&gPlayerParty[gPartyMenu.slotId], MON_DATA_SPECIES); + const struct Fusion *itemFusion = gFusionTablePointers[species]; + + PlaySE(SE_SELECT); + switch (IsFusionMon(species)) + { + case FALSE: // Cancel if Not a Fuse Mon + break; + case UNFUSE_MON: + if (task->fusionType == FUSE_MON) // Cancel if An already Fused Mon Is Chosen For The Second Fusion Mon + break; + if (gPlayerPartyCount == PARTY_SIZE) + { + gPartyMenuUseExitCallback = FALSE; + DisplayPartyMenuMessage(gText_YourPartysFull, TRUE); + ScheduleBgCopyTilemapToVram(2); + task->func = taskFunc; + return; + } + for (i = 0; itemFusion[i].fusionStorageIndex != FUSION_TERMINATOR; i++) // Loops through fusion table and checks if the mon can be unfused + { + if (gPokemonStoragePtr->fusions[itemFusion[i].fusionStorageIndex].level == 0) + continue; + if (itemFusion[i].itemId == gSpecialVar_ItemId && GetMonData(&gPokemonStoragePtr->fusions[itemFusion[i].fusionStorageIndex], MON_DATA_SPECIES) == itemFusion[i].targetSpecies2) + { + task->fusionType = UNFUSE_MON; + task->firstFusion = species; + task->firstFusionSlot = gPartyMenu.slotId; + task->storageIndex = itemFusion[i].fusionStorageIndex; + task->fusionResult = itemFusion[i].targetSpecies1; + task->unfuseSecondMon = itemFusion[i].targetSpecies2; + task->moveToLearn = itemFusion[i].unfuseForgetMove; + task->forgetMove = itemFusion[i].fusionMove; + TryItemUseFusionChange(taskId, taskFunc); + return; + } + } + break; + case FUSE_MON: + if (task->fusionType == FUSE_MON) // Cancel If Second Mon is Another First Fusion Mon + break; + for (i = 0; itemFusion[i].fusionStorageIndex != FUSION_TERMINATOR; i++) // Run through the Fusion table for each species and check if the item matches one of the entries + { + if (itemFusion[i].itemId == gSpecialVar_ItemId) + { + task->fusionType = FUSE_MON; + task->firstFusion = species; + task->firstFusionSlot = gPartyMenu.slotId; + task->storageIndex = itemFusion[i].fusionStorageIndex; + task->func = Task_HandleChooseMonInput; + gPartyMenuUseExitCallback = FALSE; + sPartyMenuInternal->exitCallback = NULL; + PartyMenuRemoveWindow(&sPartyMenuInternal->windowId[0]); + DisplayPartyMenuStdMessage(PARTY_MSG_CHOOSE_SECOND_FUSION); + return; + } + } + break; + case SECOND_FUSE_MON: + if (task->fusionType != FUSE_MON) // Cancel if Secondary Fusion Mon Chosen First + break; + for (i = 0; itemFusion[i].fusionStorageIndex != FUSION_TERMINATOR; i++) // run through fusion table and check if the fusion works + { + if (gPokemonStoragePtr->fusions[itemFusion[i].fusionStorageIndex].level != 0) + continue; + if (itemFusion[i].itemId == gSpecialVar_ItemId && itemFusion[i].targetSpecies1 == task->firstFusion) + { + task->storageIndex = itemFusion[i].fusionStorageIndex; + task->fusionResult = itemFusion[i].fusingIntoMon; + task->secondFusionSlot = gPartyMenu.slotId; + task->moveToLearn = itemFusion[i].fusionMove; + // Start Fusion + TryItemUseFusionChange(taskId, taskFunc); + return; + } + } + break; + } + // No Effect Exit + gPartyMenuUseExitCallback = FALSE; + DisplayPartyMenuMessage(gText_WontHaveEffect, TRUE); + ScheduleBgCopyTilemapToVram(2); + task->func = taskFunc; + return; +} + +#undef FUSE_MON +#undef UNFUSE_MON +#undef SECOND_FUSE_MON + +#undef fusionType +#undef firstFusion +#undef firstFusionSlot +#undef fusionResult +#undef secondFusionSlot +#undef unfuseSecondMon +#undef moveToLearn +#undef forgetMove +#undef storageIndex static void SpriteCB_FormChangeIconMosaic(struct Sprite *sprite) { diff --git a/src/strings.c b/src/strings.c index 72d6ff3e0a..f23ad3fca5 100644 --- a/src/strings.c +++ b/src/strings.c @@ -377,6 +377,7 @@ const u8 gText_HP3[] = _("HP"); const u8 gText_SpAtk3[] = _("SP. ATK"); const u8 gText_SpDef3[] = _("SP. DEF"); const u8 gText_WontHaveEffect[] = _("It won't have any effect.{PAUSE_UNTIL_PRESS}"); +const u8 gText_NextFusionMon[] = _("Choose {PKMN} to fuse with."); const u8 gText_CantBeUsedOnPkmn[] = _("This can't be used on\nthat POKéMON.{PAUSE_UNTIL_PRESS}"); const u8 gText_PkmnCantSwitchOut[] = _("{STR_VAR_1} can't be switched\nout!{PAUSE_UNTIL_PRESS}"); const u8 gText_PkmnAlreadyInBattle[] = _("{STR_VAR_1} is already\nin battle!{PAUSE_UNTIL_PRESS}"); @@ -881,7 +882,7 @@ const u8 gText_PkmnWasReleased[] = _("{DYNAMIC 0} was released."); const u8 gText_ByeByePkmn[] = _("Bye-bye, {DYNAMIC 0}!"); const u8 gText_MarkYourPkmn[] = _("Mark your POKéMON."); const u8 gText_ThatsYourLastPkmn[] = _("That's your last POKéMON!"); -const u8 gText_YourPartysFull[] = _("Your party's full!"); +const u8 gText_YourPartysFull[] = _("Your party's full!{PAUSE_UNTIL_PRESS}"); const u8 gText_YoureHoldingAPkmn[] = _("You're holding a POKéMON!"); const u8 gText_WhichOneWillYouTake[] = _("Which one will you take?"); const u8 gText_YouCantReleaseAnEgg[] = _("You can't release an EGG.");