mirror of
https://github.com/risingPhil/PokeMe64.git
synced 2026-03-22 02:14:20 -05:00
This implements functionality to teach Pikachu Surf and/or Fly and extends existing widgets to help provide this functionality. When a gen1 game is inserted, the user gets the choice to teach Pikachu these moves. This commit also restructures some of the Data and Style structs.
354 lines
11 KiB
C++
Executable File
354 lines
11 KiB
C++
Executable File
#include "menu/MenuFunctions.h"
|
|
#include "menu/MenuEntries.h"
|
|
#include "core/RDPQGraphics.h"
|
|
#include "scenes/DistributionPokemonListScene.h"
|
|
#include "scenes/SceneManager.h"
|
|
#include "gen2/Gen2GameReader.h"
|
|
#include "transferpak/TransferPakManager.h"
|
|
#include "transferpak/TransferPakRomReader.h"
|
|
#include "transferpak/TransferPakSaveManager.h"
|
|
|
|
#define POKEMON_CRYSTAL_ITEM_ID_GS_BALL 0x73
|
|
|
|
const Move MOVE_SURF = Move::SURF;
|
|
const Move MOVE_FLY = Move::FLY;
|
|
|
|
typedef struct Gen1TeachPikachuParams
|
|
{
|
|
Move moveType;
|
|
uint8_t partyIndex;
|
|
uint8_t moveIndex;
|
|
Gen1TrainerPokemon poke;
|
|
} Gen1TeachPikachuParams;
|
|
|
|
// here are a few structs I use to pass around parameters to the gen1TeachPikachu() function
|
|
// i want to avoid dynamic allocation for this purpose.
|
|
// we need 4 of them because we will have 4 menu options, each with a different moveIndex
|
|
static Gen1TeachPikachuParams teachParamsMove1 = {
|
|
.moveType = Move::SURF, // dummy value. Will be replaced later
|
|
.moveIndex = 0
|
|
};
|
|
static Gen1TeachPikachuParams teachParamsMove2 = {
|
|
.moveType = Move::SURF, // dummy value. Will be replaced later
|
|
.moveIndex = 1
|
|
};
|
|
static Gen1TeachPikachuParams teachParamsMove3 = {
|
|
.moveType = Move::SURF, // dummy value. Will be replaced later
|
|
.moveIndex = 2
|
|
};
|
|
static Gen1TeachPikachuParams teachParamsMove4 = {
|
|
.moveType = Move::SURF, // dummy value. Will be replaced later
|
|
.moveIndex = 3
|
|
};
|
|
|
|
static void goToDistributionPokemonListMenu(void* context, DistributionPokemonListType type)
|
|
{
|
|
auto sceneContext = new DistributionPokemonListSceneContext;
|
|
sceneContext->listType = type;
|
|
|
|
MenuScene* scene = static_cast<MenuScene*>(context);
|
|
SceneManager& sceneManager = scene->getDependencies().sceneManager;
|
|
|
|
sceneManager.switchScene(SceneType::DISTRIBUTION_POKEMON_LIST, deleteDistributionPokemonListSceneContext, sceneContext);
|
|
}
|
|
|
|
static uint8_t gen1FindPikachuInParty(Gen1Party& party)
|
|
{
|
|
const uint8_t PIKACHU_INDEX_CODE = 0x54;
|
|
uint8_t foundIndex = 0xFF;
|
|
const uint8_t numberOfPokemon = party.getNumberOfPokemon();
|
|
|
|
for(uint8_t i=0; i < numberOfPokemon; ++i)
|
|
{
|
|
const uint8_t speciesIndex = party.getSpeciesAtIndex(i);
|
|
if(speciesIndex == PIKACHU_INDEX_CODE)
|
|
{
|
|
// debugf("Found Pikachu at index %hu\r\n", i);
|
|
foundIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
return foundIndex;
|
|
}
|
|
|
|
void printMessage(void* context, const void*)
|
|
{
|
|
debugf((const char*)context);
|
|
}
|
|
|
|
void activateFrameLog(void* context, const void*)
|
|
{
|
|
MenuScene* scene = static_cast<MenuScene*>(context);
|
|
RDPQGraphics& gfx = scene->getDependencies().gfx;
|
|
debugf("Triggering RDPQ log for 1 frame!\r\n");
|
|
gfx.triggerDebugFrame();
|
|
}
|
|
|
|
void goToTestScene(void* context, const void* param)
|
|
{
|
|
MenuScene* scene = static_cast<MenuScene*>(context);
|
|
SceneManager& sceneManager = scene->getDependencies().sceneManager;
|
|
|
|
sceneManager.switchScene(SceneType::TEST);
|
|
}
|
|
|
|
void goToGen1DistributionPokemonMenu(void* context, const void*)
|
|
{
|
|
goToDistributionPokemonListMenu(context, DistributionPokemonListType::GEN1);
|
|
|
|
}
|
|
|
|
void goToGen2DistributionPokemonMenu(void* context, const void*)
|
|
{
|
|
goToDistributionPokemonListMenu(context, DistributionPokemonListType::GEN2);
|
|
}
|
|
|
|
void goToGen2PCNYDistributionPokemonMenu(void* context, const void* param)
|
|
{
|
|
goToDistributionPokemonListMenu(context, DistributionPokemonListType::GEN2_POKEMON_CENTER_NEW_YORK);
|
|
}
|
|
|
|
void gen1PrepareToTeachPikachu(void* context, const void* param)
|
|
{
|
|
MenuScene* scene = static_cast<MenuScene*>(context);
|
|
TransferPakManager& tpakManager = scene->getDependencies().tpakManager;
|
|
TransferPakRomReader romReader(tpakManager);
|
|
TransferPakSaveManager saveManager(tpakManager);
|
|
Gen1GameReader gameReader(romReader, saveManager, static_cast<Gen1GameType>(scene->getDependencies().specificGenVersion));
|
|
DialogData* msg1 = nullptr;
|
|
DialogData* msg2 = nullptr;
|
|
uint8_t foundIndex;
|
|
const Move moveType = *static_cast<const Move*>(param);
|
|
|
|
tpakManager.setRAMEnabled(true);
|
|
|
|
Gen1TrainerPokemon poke;
|
|
Gen1Party party = gameReader.getParty();
|
|
|
|
foundIndex = gen1FindPikachuInParty(party);
|
|
|
|
if(foundIndex == 0xFF)
|
|
{
|
|
tpakManager.setRAMEnabled(false);
|
|
msg1 = new DialogData{
|
|
.shouldDeleteWhenDone = true
|
|
};
|
|
setDialogDataText(*msg1, "I see you don't have Pikachu in your party. Please come back once you do!");
|
|
scene->showDialog(msg1);
|
|
return;
|
|
}
|
|
|
|
if(!party.getPokemon(foundIndex, poke, false))
|
|
{
|
|
tpakManager.setRAMEnabled(false);
|
|
debugf("%s: ERROR while retrieving pokemon from party at index %hu\r\n", __FUNCTION__, foundIndex);
|
|
return;
|
|
}
|
|
|
|
tpakManager.setRAMEnabled(false);
|
|
|
|
if(poke.index_move1 == (uint8_t)moveType || poke.index_move2 == (uint8_t)moveType || poke.index_move3 == (uint8_t)moveType || poke.index_move4 == (uint8_t)moveType)
|
|
{
|
|
msg1 = new DialogData{
|
|
.shouldDeleteWhenDone = true
|
|
};
|
|
setDialogDataText(*msg1, "Your Pikachu already knows %s!", getMoveString(moveType));
|
|
scene->showDialog(msg1);
|
|
return;
|
|
}
|
|
|
|
teachParamsMove1.moveType = teachParamsMove2.moveType = teachParamsMove3.moveType = teachParamsMove4.moveType = moveType;
|
|
teachParamsMove1.partyIndex = teachParamsMove2.partyIndex = teachParamsMove3.partyIndex = teachParamsMove4.partyIndex = foundIndex;
|
|
teachParamsMove1.poke = teachParamsMove2.poke = teachParamsMove3.poke = teachParamsMove4.poke = poke;
|
|
|
|
if(!poke.index_move1)
|
|
{
|
|
gen1TeachPikachu(context, &teachParamsMove1);
|
|
return;
|
|
}
|
|
else if(!poke.index_move2)
|
|
{
|
|
gen1TeachPikachu(context, &teachParamsMove2);
|
|
return;
|
|
}
|
|
else if(!poke.index_move3)
|
|
{
|
|
gen1TeachPikachu(context, &teachParamsMove3);
|
|
return;
|
|
}
|
|
else if(!poke.index_move4)
|
|
{
|
|
gen1TeachPikachu(context, &teachParamsMove4);
|
|
return;
|
|
}
|
|
|
|
msg2 = new DialogData {
|
|
.options = {
|
|
.items = new MenuItemData[4]{
|
|
{
|
|
.title = getMoveString(static_cast<Move>(poke.index_move1)),
|
|
.onConfirmAction = gen1TeachPikachu,
|
|
.context = context,
|
|
.itemParam = &teachParamsMove1
|
|
},
|
|
{
|
|
.title = getMoveString(static_cast<Move>(poke.index_move2)),
|
|
.onConfirmAction = gen1TeachPikachu,
|
|
.context = context,
|
|
.itemParam = &teachParamsMove2
|
|
},
|
|
{
|
|
.title = getMoveString(static_cast<Move>(poke.index_move3)),
|
|
.onConfirmAction = gen1TeachPikachu,
|
|
.context = context,
|
|
.itemParam = &teachParamsMove3
|
|
},
|
|
{
|
|
.title = getMoveString(static_cast<Move>(poke.index_move4)),
|
|
.onConfirmAction = gen1TeachPikachu,
|
|
.context = context,
|
|
.itemParam = &teachParamsMove4
|
|
}
|
|
},
|
|
.number = 4,
|
|
.shouldDeleteWhenDone = true
|
|
},
|
|
.shouldDeleteWhenDone = true
|
|
};
|
|
|
|
msg1 = new DialogData{
|
|
.next = msg2,
|
|
.shouldDeleteWhenDone = true
|
|
};
|
|
setDialogDataText(*msg1, "Pikachu is trying to learn %s. But Pikachu already knows 4 moves.", getMoveString(moveType));
|
|
setDialogDataText(*msg2, "Delete an older move to make room for %s?", getMoveString(moveType));
|
|
scene->showDialog(msg1);
|
|
}
|
|
|
|
void gen1TeachPikachu(void* context, const void* param)
|
|
{
|
|
MenuScene* scene = static_cast<MenuScene*>(context);
|
|
TransferPakManager& tpakManager = scene->getDependencies().tpakManager;
|
|
TransferPakRomReader romReader(tpakManager);
|
|
TransferPakSaveManager saveManager(tpakManager);
|
|
Gen1GameReader gameReader(romReader, saveManager, static_cast<Gen1GameType>(scene->getDependencies().specificGenVersion));
|
|
|
|
const Gen1TeachPikachuParams* params = static_cast<const Gen1TeachPikachuParams*>(param);
|
|
Gen1TrainerPokemon poke = params->poke;
|
|
uint8_t previousMove;
|
|
uint8_t newMove = static_cast<uint8_t>(params->moveType);
|
|
|
|
// debugf("%s: newMove %hu, moveIndex %hu, partyIndex %hu\r\n", __FUNCTION__, newMove, params->moveIndex, params->partyIndex);
|
|
|
|
switch(params->moveIndex)
|
|
{
|
|
case 0:
|
|
previousMove = poke.index_move1;
|
|
poke.index_move1 = newMove;
|
|
poke.pp_move1 = 0;
|
|
break;
|
|
case 1:
|
|
previousMove = poke.index_move2;
|
|
poke.index_move2 = newMove;
|
|
poke.pp_move2 = 0;
|
|
break;
|
|
case 2:
|
|
previousMove = poke.index_move3;
|
|
poke.index_move3 = newMove;
|
|
poke.pp_move3 = 0;
|
|
break;
|
|
case 3:
|
|
previousMove = poke.index_move4;
|
|
poke.index_move4 = newMove;
|
|
poke.pp_move4 = 0;
|
|
break;
|
|
default:
|
|
scene->advanceDialog();
|
|
return;
|
|
}
|
|
|
|
tpakManager.setRAMEnabled(true);
|
|
|
|
Gen1Party party = gameReader.getParty();
|
|
if(!party.setPokemon(params->partyIndex, poke))
|
|
{
|
|
tpakManager.setRAMEnabled(false);
|
|
debugf("%s: ERROR: can't update Pikachu at partyIndex %hu\r\n", __FUNCTION__, params->partyIndex);
|
|
return;
|
|
}
|
|
gameReader.updateMainChecksum();
|
|
tpakManager.finishWrites();
|
|
tpakManager.setRAMEnabled(false);
|
|
|
|
DialogData* msg1 = new DialogData{
|
|
.shouldDeleteWhenDone = true
|
|
};
|
|
|
|
if(previousMove)
|
|
{
|
|
DialogData* msg3 = new DialogData{
|
|
.shouldDeleteWhenDone = true
|
|
};
|
|
DialogData* msg2 = new DialogData{
|
|
.next = msg3,
|
|
.shouldDeleteWhenDone = true
|
|
};
|
|
msg1->next = msg2;
|
|
|
|
setDialogDataText(*msg1, "1..2..3..Poof!");
|
|
setDialogDataText(*msg2, "Pikachu forgot %s!", getMoveString(static_cast<Move>(previousMove)));
|
|
setDialogDataText(*msg3, "Pikachu learned %s!", getMoveString(params->moveType));
|
|
}
|
|
else
|
|
{
|
|
setDialogDataText(*msg1, "Pikachu learned %s!", getMoveString(params->moveType));
|
|
}
|
|
|
|
scene->showDialog(msg1);
|
|
}
|
|
|
|
void gen2ReceiveGSBall(void* context, const void* param)
|
|
{
|
|
MenuScene* scene = static_cast<MenuScene*>(context);
|
|
TransferPakManager& tpakManager = scene->getDependencies().tpakManager;
|
|
TransferPakRomReader romReader(tpakManager);
|
|
TransferPakSaveManager saveManager(tpakManager);
|
|
Gen2GameReader gameReader(romReader, saveManager, Gen2GameType::CRYSTAL);
|
|
DialogData* messageData = new DialogData{
|
|
.shouldDeleteWhenDone = true
|
|
};
|
|
bool alreadyHasOne = false;
|
|
|
|
tpakManager.setRAMEnabled(true);
|
|
|
|
const char* trainerName = gameReader.getTrainerName();
|
|
Gen2ItemList keyItemPocket = gameReader.getItemList(Gen2ItemListType::GEN2_ITEMLISTTYPE_KEYITEMPOCKET);
|
|
if(keyItemPocket.getCount() > 0)
|
|
{
|
|
uint8_t itemId;
|
|
uint8_t itemCount;
|
|
bool gotEntry = keyItemPocket.getEntry(0, itemId, itemCount);
|
|
|
|
while(gotEntry)
|
|
{
|
|
if(itemId == POKEMON_CRYSTAL_ITEM_ID_GS_BALL)
|
|
{
|
|
alreadyHasOne = true;
|
|
break;
|
|
}
|
|
gotEntry = keyItemPocket.getNextEntry(itemId, itemCount);
|
|
}
|
|
}
|
|
|
|
if(alreadyHasOne)
|
|
{
|
|
setDialogDataText(*messageData, "It appears you already have one!");
|
|
}
|
|
else
|
|
{
|
|
keyItemPocket.add(POKEMON_CRYSTAL_ITEM_ID_GS_BALL, 1);
|
|
gameReader.finishSave();
|
|
setDialogDataText(*messageData, "%s obtained a GS Ball!", trainerName);
|
|
}
|
|
scene->showDialog(messageData);
|
|
} |