mirror of
https://github.com/risingPhil/PokeMe64.git
synced 2026-04-25 15:42:41 -05:00
Add StatsScene for showing obtained pokemon
This commit is contained in:
parent
4a8ecb6017
commit
f516c6d138
1
Makefile
1
Makefile
|
|
@ -37,6 +37,7 @@ filesystem/%.sprite: assets/%.png
|
|||
$(N64_MKSPRITE) $(MKSPRITE_FLAGS) -o filesystem "$<"
|
||||
|
||||
filesystem/Arial.font64: MKFONT_FLAGS+=--size 11 --outline 1.0 --char-spacing 1.0 --range 20-E9
|
||||
filesystem/Arial-small.font64: MKFONT_FLAGS+=--size 10 --outline 1.0 --char-spacing 1.0 --range 20-E9
|
||||
|
||||
filesystem/menu-bg-9slice.sprite: MKSPRITE_FLAGS += -f RGBA16
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ typedef struct TextRenderSettings
|
|||
} TextRenderSettings;
|
||||
|
||||
typedef struct SpriteRenderSettings SpriteRenderSettings;
|
||||
typedef struct SurfaceRenderSettings SurfaceRenderSettings;
|
||||
|
||||
/**
|
||||
* @brief This class abstracts operations done with the RDPQ graphics API in libdragon
|
||||
|
|
@ -64,6 +65,11 @@ public:
|
|||
*/
|
||||
void drawSprite(const Rectangle& dstRect, sprite_t* sprite, const SpriteRenderSettings& renderSettings);
|
||||
|
||||
/**
|
||||
* Same as drawSprite() but then for surface_t objects
|
||||
*/
|
||||
void drawSurface(const Rectangle& dstRect, const surface_t* surface, const SurfaceRenderSettings& renderSettings);
|
||||
|
||||
const Rectangle& getClippingRectangle() const;
|
||||
void setClippingRectangle(const Rectangle& clipRect);
|
||||
void resetClippingRectangle();
|
||||
|
|
|
|||
|
|
@ -60,4 +60,17 @@ typedef struct SpriteRenderSettings
|
|||
float rotationAngle;
|
||||
} SpriteRenderSettings;
|
||||
|
||||
typedef struct SurfaceRenderSettings
|
||||
{
|
||||
/**
|
||||
* Source region within the sprite that needs to be rendered (partial rendering)
|
||||
*/
|
||||
Rectangle srcRect;
|
||||
|
||||
/**
|
||||
* @brief Rotation angle in radians
|
||||
*/
|
||||
float rotationAngle;
|
||||
} SurfaceRenderSettings;
|
||||
|
||||
#endif
|
||||
|
|
@ -12,4 +12,7 @@ extern const uint32_t gen2MenuEntriesSize;
|
|||
extern MenuItemData gen2CrystalMenuEntries[];
|
||||
extern const uint32_t gen2CrystalMenuEntriesSize;
|
||||
|
||||
extern MenuItemData gen2DecorationMenuEntries[];
|
||||
extern const uint32_t gen2DecorationMenuEntriesSize;
|
||||
|
||||
#endif
|
||||
|
|
@ -21,6 +21,7 @@ void goToTestScene(void* context, const void* param);
|
|||
void goToGen1DistributionPokemonMenu(void* context, const void* param);
|
||||
void goToGen2DistributionPokemonMenu(void* context, const void* param);
|
||||
void goToGen2PCNYDistributionPokemonMenu(void* context, const void* param);
|
||||
void goToGen2DecorationMenu(void* context, const void* param);
|
||||
|
||||
void gen1PrepareToTeachPikachu(void* context, const void* param);
|
||||
void gen1TeachPikachu(void* context, const void* param);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ enum class SceneType
|
|||
INIT_TRANSFERPAK,
|
||||
MENU,
|
||||
DISTRIBUTION_POKEMON_LIST,
|
||||
STATS,
|
||||
TEST
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -62,6 +62,7 @@ private:
|
|||
|
||||
std::vector<SceneHistorySegment> sceneHistory_;
|
||||
SceneDependencies sceneDeps_;
|
||||
uint64_t blockInputStartTime_;
|
||||
IScene* scene_;
|
||||
SceneType newSceneType_;
|
||||
void* newSceneContext_;
|
||||
|
|
|
|||
68
include/scenes/StatsScene.h
Normal file
68
include/scenes/StatsScene.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#ifndef _STATSSCENE_H
|
||||
#define _STATSSCENE_H
|
||||
|
||||
#include "scenes/SceneWithDialogWidget.h"
|
||||
#include "gen1/Gen1Common.h"
|
||||
#include "gen2/Gen2Common.h"
|
||||
#include "core/Sprite.h"
|
||||
#include "transferpak/TransferPakRomReader.h"
|
||||
#include "transferpak/TransferPakSaveManager.h"
|
||||
#include "gen1/Gen1GameReader.h"
|
||||
#include "gen2/Gen2GameReader.h"
|
||||
|
||||
typedef struct StatsSceneContext
|
||||
{
|
||||
union {
|
||||
Gen1TrainerPokemon poke_g1;
|
||||
Gen2TrainerPokemon poke_g2;
|
||||
};
|
||||
bool showReceivedPokemonDialog;
|
||||
bool isEgg;
|
||||
} StatsSceneContext;
|
||||
|
||||
/**
|
||||
* Shows the stats for a pokemon
|
||||
*/
|
||||
class StatsScene : public SceneWithDialogWidget
|
||||
{
|
||||
public:
|
||||
StatsScene(SceneDependencies& deps, void* context);
|
||||
virtual ~StatsScene();
|
||||
|
||||
void init() override;
|
||||
void destroy() override;
|
||||
|
||||
void render(RDPQGraphics& gfx, const Rectangle& sceneBounds) override;
|
||||
|
||||
void onDialogFinished();
|
||||
protected:
|
||||
void setupDialog(DialogWidgetStyle& style) override;
|
||||
private:
|
||||
void loadPokemonSprite(uint8_t pokeIndex, bool shiny);
|
||||
|
||||
DialogData diag_;
|
||||
TransferPakRomReader romReader_;
|
||||
TransferPakSaveManager saveManager_;
|
||||
Gen1GameReader gen1GameReader_;
|
||||
Gen2GameReader gen2GameReader_;
|
||||
sprite_t* menu9SliceSprite_;
|
||||
SpriteRenderSettings backgroundRenderSettings_;
|
||||
uint8_t fontArialSmallId_;
|
||||
uint8_t fontArialSmallWhiteId_;
|
||||
TextRenderSettings textSettings_;
|
||||
TextRenderSettings smallTextSettings_;
|
||||
TextRenderSettings statsSettings_;
|
||||
Rectangle spriteBounds_;
|
||||
char nameBuffer_[15];
|
||||
char levelAndNumberBuffer_[40];
|
||||
char pokeStatsString_[150];
|
||||
char otInfoString_[40];
|
||||
char movesString[256];
|
||||
StatsSceneContext* context_;
|
||||
surface_t pokeSpriteSurface_;
|
||||
|
||||
};
|
||||
|
||||
void deleteStatsSceneContext(void* context);
|
||||
|
||||
#endif
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 7b96cae992f827921032a4c60e773efd213039f5
|
||||
Subproject commit 47ec404aac43c46bf1adf65e49c7da10946ad587
|
||||
|
|
@ -16,6 +16,7 @@ uint8_t FontManager::getFont(const char* fontUri)
|
|||
.fontId = nextFontId_
|
||||
};
|
||||
fontMap_.emplace(fontUri, entry);
|
||||
++nextFontId_;
|
||||
|
||||
rdpq_text_register_font(entry.fontId, entry.font);
|
||||
return entry.fontId;
|
||||
|
|
|
|||
|
|
@ -167,6 +167,35 @@ static void render_sprite_ninegrid(const Rectangle &dstRect, sprite_t *sprite, c
|
|||
render_sprite_normal(curDest, sprite, {.renderMode = SpriteRenderMode::NORMAL, .srcRect = curSrc});
|
||||
}
|
||||
|
||||
// same as render_sprite_normal, but for surface_t
|
||||
static void render_surface(const Rectangle &dstRect, const surface_t *surface, const SurfaceRenderSettings &renderSettings)
|
||||
{
|
||||
if (!isZeroSizeRectangle(renderSettings.srcRect))
|
||||
{
|
||||
const rdpq_blitparms_t blitParams = {
|
||||
.s0 = renderSettings.srcRect.x,
|
||||
.t0 = renderSettings.srcRect.y,
|
||||
.width = renderSettings.srcRect.width,
|
||||
.height = renderSettings.srcRect.height,
|
||||
.scale_x = static_cast<float>(dstRect.width) / renderSettings.srcRect.width,
|
||||
.scale_y = static_cast<float>(dstRect.height) / renderSettings.srcRect.height,
|
||||
.theta = renderSettings.rotationAngle
|
||||
};
|
||||
|
||||
rdpq_tex_blit(surface, dstRect.x, dstRect.y, &blitParams);
|
||||
}
|
||||
else
|
||||
{
|
||||
const rdpq_blitparms_t blitParams = {
|
||||
.scale_x = static_cast<float>(dstRect.width) / surface->width,
|
||||
.scale_y = static_cast<float>(dstRect.height) / surface->height,
|
||||
.theta = renderSettings.rotationAngle
|
||||
};
|
||||
|
||||
rdpq_tex_blit(surface, dstRect.x, dstRect.y, &blitParams);
|
||||
}
|
||||
}
|
||||
|
||||
RDPQGraphics::RDPQGraphics()
|
||||
: clipRect_({0})
|
||||
, initialized_(false)
|
||||
|
|
@ -282,6 +311,27 @@ void RDPQGraphics::drawSprite(const Rectangle &dstRect, sprite_t *sprite, const
|
|||
}
|
||||
}
|
||||
|
||||
void RDPQGraphics::drawSurface(const Rectangle& dstRect, const surface_t* surface, const SurfaceRenderSettings& renderSettings)
|
||||
{
|
||||
rdpq_mode_begin();
|
||||
rdpq_set_mode_standard();
|
||||
rdpq_mode_alphacompare(1); // colorkey (draw pixel with alpha >= 1)
|
||||
switch(surface_get_format(surface))
|
||||
{
|
||||
case FMT_RGBA32:
|
||||
case FMT_IA8:
|
||||
case FMT_IA16:
|
||||
rdpq_mode_blender(RDPQ_BLENDER_MULTIPLY);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
rdpq_mode_filter(FILTER_BILINEAR);
|
||||
rdpq_mode_end();
|
||||
|
||||
render_surface(dstRect, surface, renderSettings);
|
||||
}
|
||||
|
||||
const Rectangle& RDPQGraphics::getClippingRectangle() const
|
||||
{
|
||||
return clipRect_;
|
||||
|
|
|
|||
|
|
@ -27,22 +27,11 @@ MenuItemData gen2MenuEntries[] = {
|
|||
},
|
||||
{
|
||||
.title = "PCNY Pokémon",
|
||||
.onConfirmAction = goToGen2PCNYDistributionPokemonMenu,
|
||||
.onConfirmAction = goToGen2PCNYDistributionPokemonMenu
|
||||
},
|
||||
{
|
||||
.title = "Unlock Pikachu Bed",
|
||||
.onConfirmAction = gen2SetEventFlag,
|
||||
.itemParam = &GEN2_EVENTFLAG_DECORATION_PIKACHU_BED
|
||||
},
|
||||
{
|
||||
.title = "Unlock Unown Doll",
|
||||
.onConfirmAction = gen2SetEventFlag,
|
||||
.itemParam = &GEN2_EVENTFLAG_DECORATION_UNOWN_DOLL
|
||||
},
|
||||
{
|
||||
.title = "Unlock Tentacool Doll",
|
||||
.onConfirmAction = gen2SetEventFlag,
|
||||
.itemParam = &GEN2_EVENTFLAG_DECORATION_TENTACOOL_DOLL
|
||||
.title = "Unlock Decoration",
|
||||
.onConfirmAction = goToGen2DecorationMenu
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -62,20 +51,29 @@ MenuItemData gen2CrystalMenuEntries[] = {
|
|||
.onConfirmAction = gen2ReceiveGSBall
|
||||
},
|
||||
{
|
||||
.title = "Unlock Pikachu Bed",
|
||||
.title = "Unlock Decoration",
|
||||
.onConfirmAction = goToGen2DecorationMenu
|
||||
}
|
||||
};
|
||||
|
||||
const uint32_t gen2CrystalMenuEntriesSize = sizeof(gen2CrystalMenuEntries);
|
||||
|
||||
MenuItemData gen2DecorationMenuEntries[] = {
|
||||
{
|
||||
.title = "Pikachu Bed",
|
||||
.onConfirmAction = gen2SetEventFlag,
|
||||
.itemParam = &GEN2_EVENTFLAG_DECORATION_PIKACHU_BED
|
||||
},
|
||||
{
|
||||
.title = "Unlock Unown Doll",
|
||||
.title = "Unown Doll",
|
||||
.onConfirmAction = gen2SetEventFlag,
|
||||
.itemParam = &GEN2_EVENTFLAG_DECORATION_UNOWN_DOLL
|
||||
},
|
||||
{
|
||||
.title = "Unlock Tentacool Doll",
|
||||
.title = "Tentacool Doll",
|
||||
.onConfirmAction = gen2SetEventFlag,
|
||||
.itemParam = &GEN2_EVENTFLAG_DECORATION_TENTACOOL_DOLL
|
||||
}
|
||||
};
|
||||
|
||||
const uint32_t gen2CrystalMenuEntriesSize = sizeof(gen2CrystalMenuEntries);
|
||||
const uint32_t gen2DecorationMenuEntriesSize = sizeof(gen2DecorationMenuEntries);
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
#include "menu/MenuEntries.h"
|
||||
#include "core/RDPQGraphics.h"
|
||||
#include "scenes/DistributionPokemonListScene.h"
|
||||
#include "scenes/StatsScene.h"
|
||||
#include "scenes/MenuScene.h"
|
||||
#include "scenes/SceneManager.h"
|
||||
#include "gen2/Gen2GameReader.h"
|
||||
#include "transferpak/TransferPakManager.h"
|
||||
|
|
@ -114,7 +116,6 @@ void goToTestScene(void* context, const void* param)
|
|||
void goToGen1DistributionPokemonMenu(void* context, const void*)
|
||||
{
|
||||
goToDistributionPokemonListMenu(context, DistributionPokemonListType::GEN1);
|
||||
|
||||
}
|
||||
|
||||
void goToGen2DistributionPokemonMenu(void* context, const void*)
|
||||
|
|
@ -127,6 +128,17 @@ void goToGen2PCNYDistributionPokemonMenu(void* context, const void* param)
|
|||
goToDistributionPokemonListMenu(context, DistributionPokemonListType::GEN2_POKEMON_CENTER_NEW_YORK);
|
||||
}
|
||||
|
||||
void goToGen2DecorationMenu(void* context, const void* param)
|
||||
{
|
||||
MenuScene* scene = static_cast<MenuScene*>(context);
|
||||
auto newSceneContext = new MenuSceneContext{
|
||||
.menuEntries = gen2DecorationMenuEntries,
|
||||
.numMenuEntries = gen2DecorationMenuEntriesSize / sizeof(gen2DecorationMenuEntries[0])
|
||||
};
|
||||
|
||||
scene->getDependencies().sceneManager.switchScene(SceneType::MENU, deleteMenuSceneContext, newSceneContext);
|
||||
}
|
||||
|
||||
void gen1PrepareToTeachPikachu(void* context, const void* param)
|
||||
{
|
||||
MenuScene* scene = static_cast<MenuScene*>(context);
|
||||
|
|
@ -382,4 +394,4 @@ void gen2SetEventFlag(void* context, const void* param)
|
|||
|
||||
tpakManager.setRAMEnabled(false);
|
||||
scene->showDialog(messageData);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "scenes/DistributionPokemonListScene.h"
|
||||
#include "scenes/SceneManager.h"
|
||||
#include "scenes/StatsScene.h"
|
||||
#include "transferpak/TransferPakManager.h"
|
||||
|
||||
static DistributionPokemonListSceneContext* convert(void* context)
|
||||
|
|
@ -68,27 +69,38 @@ void DistributionPokemonListScene::triggerPokemonInjection(const void* data)
|
|||
|
||||
void DistributionPokemonListScene::injectPokemon(const void* data)
|
||||
{
|
||||
StatsSceneContext* statsContext;
|
||||
const Gen1DistributionPokemon* g1Poke;
|
||||
const Gen2DistributionPokemon* g2Poke;
|
||||
const char* trainerName;
|
||||
const char* pokeName;
|
||||
|
||||
deps_.tpakManager.setRAMEnabled(true);
|
||||
|
||||
statsContext = new StatsSceneContext{
|
||||
.showReceivedPokemonDialog = true
|
||||
};
|
||||
|
||||
switch(convert(context_)->listType)
|
||||
{
|
||||
case DistributionPokemonListType::GEN1:
|
||||
g1Poke = static_cast<const Gen1DistributionPokemon*>(data);
|
||||
trainerName = gen1Reader_.getTrainerName();
|
||||
pokeName = gen1Reader_.getPokemonName(g1Poke->poke.poke_index);
|
||||
gen1Reader_.addDistributionPokemon((*g1Poke));
|
||||
statsContext->poke_g1 = g1Poke->poke;
|
||||
// I have not used gen1Reader_->addDistributionPokemon here because I want to show the resulting pokemon in a stats screen
|
||||
// gen1_prepareDistributionPokemon() + addPokemon() gives me access to the resulting Gen1TrainerPokemon instance
|
||||
// in which things are done like IV generation, OT name decision, OT id
|
||||
gen1_prepareDistributionPokemon(gen1Reader_, (*g1Poke), statsContext->poke_g1, trainerName);
|
||||
gen1Reader_.addPokemon(statsContext->poke_g1, trainerName);
|
||||
break;
|
||||
case DistributionPokemonListType::GEN2:
|
||||
case DistributionPokemonListType::GEN2_POKEMON_CENTER_NEW_YORK:
|
||||
g2Poke = static_cast<const Gen2DistributionPokemon*>(data);
|
||||
trainerName = gen2Reader_.getTrainerName();
|
||||
pokeName = gen2Reader_.getPokemonName(g2Poke->poke.poke_index);
|
||||
gen2Reader_.addDistributionPokemon((*g2Poke));
|
||||
statsContext->poke_g2 = g2Poke->poke;
|
||||
statsContext->isEgg = g2Poke->isEgg;
|
||||
// I have not used gen2Reader_->addDistributionPokemon here because I want to show the resulting pokemon in a stats screen
|
||||
// gen2_prepareDistributionPokemon() + addPokemon() gives me access to the resulting Gen2TrainerPokemon instance
|
||||
// in which things are done like IV generation, OT name decision, OT id, shininess
|
||||
gen2_prepareDistributionPokemon(gen2Reader_, (*g2Poke), statsContext->poke_g2, trainerName);
|
||||
gen2Reader_.addPokemon(statsContext->poke_g2, trainerName);
|
||||
gen2Reader_.finishSave();
|
||||
break;
|
||||
default:
|
||||
|
|
@ -104,6 +116,8 @@ void DistributionPokemonListScene::injectPokemon(const void* data)
|
|||
* to MBC3
|
||||
*/
|
||||
deps_.tpakManager.setRAMEnabled(false);
|
||||
delete statsContext;
|
||||
statsContext = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -112,12 +126,9 @@ void DistributionPokemonListScene::injectPokemon(const void* data)
|
|||
// The reason is the same as previous setRAMEnabled(false) statement above
|
||||
deps_.tpakManager.setRAMEnabled(false);
|
||||
|
||||
// operation done. Now the dialog can be advanced and we can show confirmation that the user got the pokémon
|
||||
dialogWidget_.advanceDialog();
|
||||
deps_.sceneManager.switchScene(SceneType::STATS, deleteStatsSceneContext, statsContext);
|
||||
|
||||
setDialogDataText(diag_, "%s received %s!", trainerName, pokeName);
|
||||
diag_.userAdvanceBlocked = false;
|
||||
showDialog(&diag_);
|
||||
// operation done. Now the dialog can be advanced and we can show confirmation that the user got the pokémon
|
||||
}
|
||||
|
||||
void DistributionPokemonListScene::onDialogDone()
|
||||
|
|
|
|||
|
|
@ -158,6 +158,10 @@ void MenuScene::setupMenu()
|
|||
},
|
||||
.margin = {
|
||||
.top = 5
|
||||
},
|
||||
.autogrow = {
|
||||
.enabled = true,
|
||||
.maxHeight = 150
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -169,7 +173,7 @@ void MenuScene::setupMenu()
|
|||
};
|
||||
|
||||
menuList_.setStyle(listStyle);
|
||||
menuList_.setBounds(Rectangle{100, 30, 150, 150});
|
||||
menuList_.setBounds(Rectangle{100, 30, 150, 0});
|
||||
menuList_.setVisible(true);
|
||||
cursorWidget_.setStyle(cursorStyle);
|
||||
cursorWidget_.setVisible(true);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
#include "scenes/SceneManager.h"
|
||||
#include "scenes/TestScene.h"
|
||||
#include "scenes/StatsScene.h"
|
||||
#include "scenes/InitTransferPakScene.h"
|
||||
#include "scenes/DistributionPokemonListScene.h"
|
||||
|
||||
#include <libdragon.h>
|
||||
|
||||
static const uint16_t INPUT_BLOCK_TIME_AFTER_NEW_SCENE_IN_MS = 500;
|
||||
|
||||
SceneManager::SceneManager(RDPQGraphics& gfx, AnimationManager& animationManager, FontManager& fontManager, TransferPakManager& tpakManager)
|
||||
: sceneHistory_()
|
||||
, sceneDeps_(SceneDependencies{
|
||||
|
|
@ -44,6 +47,8 @@ void SceneManager::switchScene(SceneType type, void (*deleteContextFunc)(void*),
|
|||
.context = sceneContext,
|
||||
.deleteContextFunc = deleteContextFunc
|
||||
});
|
||||
|
||||
blockInputStartTime_ = get_ticks();
|
||||
}
|
||||
|
||||
void SceneManager::goBackToPreviousScene()
|
||||
|
|
@ -80,6 +85,18 @@ void SceneManager::handleUserInput()
|
|||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if(blockInputStartTime_)
|
||||
{
|
||||
const uint64_t now = get_ticks();
|
||||
if(TICKS_TO_MS(now - blockInputStartTime_) < INPUT_BLOCK_TIME_AFTER_NEW_SCENE_IN_MS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// enough time has passed. reset the blockInputStartTime_
|
||||
blockInputStartTime_ = 0;
|
||||
}
|
||||
|
||||
scene_->processUserInput();
|
||||
}
|
||||
|
||||
|
|
@ -112,6 +129,9 @@ void SceneManager::loadScene()
|
|||
case SceneType::DISTRIBUTION_POKEMON_LIST:
|
||||
scene_ = new DistributionPokemonListScene(sceneDeps_, newSceneContext_);
|
||||
break;
|
||||
case SceneType::STATS:
|
||||
scene_ = new StatsScene(sceneDeps_, newSceneContext_);
|
||||
break;
|
||||
case SceneType::TEST:
|
||||
scene_ = new TestScene(sceneDeps_, newSceneContext_);
|
||||
break;
|
||||
|
|
|
|||
313
src/scenes/StatsScene.cpp
Normal file
313
src/scenes/StatsScene.cpp
Normal file
|
|
@ -0,0 +1,313 @@
|
|||
#include "scenes/StatsScene.h"
|
||||
#include "SpriteRenderer.h"
|
||||
#include "core/FontManager.h"
|
||||
#include "scenes/SceneManager.h"
|
||||
#include "transferpak/TransferPakManager.h"
|
||||
#include "Moves.h"
|
||||
|
||||
using OutputFormat = SpriteRenderer::OutputFormat;
|
||||
|
||||
static const Rectangle backgroundBounds = {30, 25, 260, 140};
|
||||
static const Rectangle spriteBounds = {37, 32, 0, 0};
|
||||
static const Rectangle nameBounds = {90, 37, 150, 16};
|
||||
static const Rectangle levelAndNumberBounds = {90, 53, 100, 32};
|
||||
static const Rectangle otInfoBounds = {185, 53, 100, 32};
|
||||
static const Rectangle statsBounds = {42, 96, 150, 80};
|
||||
static const Rectangle movesBounds = {185, 96, 150, 80};
|
||||
|
||||
static uint8_t getNumBytesPerColorFor(OutputFormat format)
|
||||
{
|
||||
switch(format)
|
||||
{
|
||||
case OutputFormat::RGB:
|
||||
return 3;
|
||||
case OutputFormat::RGBA16:
|
||||
return 2;
|
||||
case OutputFormat::RGBA32:
|
||||
return 4;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static Gen1GameType getGen1TypeFor(uint8_t generation, uint8_t specificGenVersion)
|
||||
{
|
||||
if(generation != 1)
|
||||
{
|
||||
return Gen1GameType::INVALID;
|
||||
}
|
||||
return static_cast<Gen1GameType>(specificGenVersion);
|
||||
}
|
||||
|
||||
static Gen2GameType getGen2TypeFor(uint8_t generation, uint8_t specificGenVersion)
|
||||
{
|
||||
if(generation != 2)
|
||||
{
|
||||
return Gen2GameType::INVALID;
|
||||
}
|
||||
return static_cast<Gen2GameType>(specificGenVersion);
|
||||
}
|
||||
|
||||
static void dialogFinishedCallback(void* context)
|
||||
{
|
||||
StatsScene* scene = (StatsScene*)context;
|
||||
scene->onDialogFinished();
|
||||
}
|
||||
|
||||
StatsScene::StatsScene(SceneDependencies& deps, void* context)
|
||||
: SceneWithDialogWidget(deps)
|
||||
, diag_({0})
|
||||
, romReader_(deps.tpakManager)
|
||||
, saveManager_(deps.tpakManager)
|
||||
, gen1GameReader_(romReader_, saveManager_, getGen1TypeFor(deps.generation, deps.specificGenVersion))
|
||||
, gen2GameReader_(romReader_, saveManager_, getGen2TypeFor(deps.generation, deps.specificGenVersion))
|
||||
, menu9SliceSprite_(nullptr)
|
||||
, backgroundRenderSettings_({
|
||||
.renderMode = SpriteRenderMode::NINESLICE,
|
||||
.srcRect = Rectangle{6, 6, 6, 6}
|
||||
})
|
||||
, fontArialSmallId_(2)
|
||||
, fontArialSmallWhiteId_(0)
|
||||
, textSettings_(TextRenderSettings{
|
||||
.fontId = arialId_,
|
||||
.fontStyleId = fontStyleWhiteId_
|
||||
})
|
||||
, smallTextSettings_(TextRenderSettings{
|
||||
.fontId = fontArialSmallId_,
|
||||
.fontStyleId = fontArialSmallWhiteId_
|
||||
})
|
||||
, statsSettings_(TextRenderSettings{
|
||||
.fontId = arialId_,
|
||||
.fontStyleId = fontStyleWhiteId_,
|
||||
})
|
||||
, spriteBounds_(spriteBounds)
|
||||
, nameBuffer_()
|
||||
, levelAndNumberBuffer_()
|
||||
, pokeStatsString_()
|
||||
, otInfoString_()
|
||||
, context_(static_cast<StatsSceneContext*>(context))
|
||||
, pokeSpriteSurface_({0})
|
||||
{
|
||||
nameBuffer_[0] = '\0';
|
||||
nameBuffer_[sizeof(nameBuffer_) - 1] = '\0';
|
||||
}
|
||||
|
||||
StatsScene::~StatsScene()
|
||||
{
|
||||
}
|
||||
|
||||
void StatsScene::init()
|
||||
{
|
||||
uint8_t pokeIndex;
|
||||
uint8_t level;
|
||||
uint8_t pokeNumber;
|
||||
uint16_t hp;
|
||||
uint16_t trainerID;
|
||||
uint16_t atk;
|
||||
uint16_t def;
|
||||
uint16_t specAtk;
|
||||
uint16_t specDef;
|
||||
uint16_t speed;
|
||||
const char* move1Str;
|
||||
const char* move2Str;
|
||||
const char* move3Str;
|
||||
const char* move4Str;
|
||||
const char* pokeName;
|
||||
const char* trainerName;
|
||||
bool shiny;
|
||||
|
||||
menu9SliceSprite_ = sprite_load("rom://menu-bg-9slice.sprite");
|
||||
fontArialSmallId_ = deps_.fontManager.getFont("rom://Arial-small.font64");
|
||||
|
||||
SceneWithDialogWidget::init();
|
||||
|
||||
const rdpq_fontstyle_t arialWhite = {
|
||||
.color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF),
|
||||
.outline_color = RGBA32(0, 0, 0, 0xFF)
|
||||
};
|
||||
deps_.fontManager.registerFontStyle(fontArialSmallId_, fontArialSmallWhiteId_, arialWhite);
|
||||
deps_.tpakManager.setRAMEnabled(true);
|
||||
switch(deps_.generation)
|
||||
{
|
||||
case 1:
|
||||
gen1_recalculatePokeStats(gen1GameReader_, context_->poke_g1);
|
||||
pokeIndex = context_->poke_g1.poke_index;
|
||||
level = context_->poke_g1.level;
|
||||
pokeNumber = gen1GameReader_.getPokemonNumber(pokeIndex);
|
||||
hp = context_->poke_g1.max_hp;
|
||||
trainerID = context_->poke_g1.original_trainer_ID;
|
||||
atk = context_->poke_g1.atk;
|
||||
def = context_->poke_g1.def;
|
||||
specAtk = context_->poke_g1.special;
|
||||
speed = context_->poke_g1.speed;
|
||||
move1Str = getMoveString(static_cast<Move>(context_->poke_g1.index_move1));
|
||||
move2Str = getMoveString(static_cast<Move>(context_->poke_g1.index_move2));
|
||||
move3Str = getMoveString(static_cast<Move>(context_->poke_g1.index_move3));
|
||||
move4Str = getMoveString(static_cast<Move>(context_->poke_g1.index_move4));
|
||||
pokeName = gen1GameReader_.getPokemonName(pokeIndex);
|
||||
trainerName = gen1GameReader_.getTrainerName();
|
||||
shiny = false;
|
||||
snprintf(pokeStatsString_, sizeof(pokeStatsString_), "ATK: %u\nDEF: %u\nSPEC: %u\nSPEED: %u", atk, def, specAtk, speed);
|
||||
break;
|
||||
case 2:
|
||||
gen2_recalculatePokeStats(gen2GameReader_, context_->poke_g2);
|
||||
pokeIndex = context_->poke_g2.poke_index;
|
||||
level = context_->poke_g2.level;
|
||||
pokeNumber = pokeIndex;
|
||||
hp = context_->poke_g2.max_hp;
|
||||
trainerID = context_->poke_g2.original_trainer_ID;
|
||||
atk = context_->poke_g2.atk;
|
||||
def = context_->poke_g2.def;
|
||||
specAtk = context_->poke_g2.special_atk;
|
||||
specDef = context_->poke_g2.special_def;
|
||||
speed = context_->poke_g2.speed;
|
||||
move1Str = getMoveString(static_cast<Move>(context_->poke_g2.index_move1));
|
||||
move2Str = getMoveString(static_cast<Move>(context_->poke_g2.index_move2));
|
||||
move3Str = getMoveString(static_cast<Move>(context_->poke_g2.index_move3));
|
||||
move4Str = getMoveString(static_cast<Move>(context_->poke_g2.index_move4));
|
||||
pokeName = gen2GameReader_.getPokemonName(pokeIndex);
|
||||
trainerName = gen2GameReader_.getTrainerName();
|
||||
shiny = gen2_isPokemonShiny(context_->poke_g2);
|
||||
snprintf(pokeStatsString_, sizeof(pokeStatsString_), "ATK: %u\nDEF: %u\nSPEC. ATK: %u\nSPEC. DEF: %u\nSPEED: %u", atk, def, specAtk, specDef, speed);
|
||||
break;
|
||||
default:
|
||||
deps_.tpakManager.setRAMEnabled(false);
|
||||
return;
|
||||
}
|
||||
deps_.tpakManager.setRAMEnabled(false);
|
||||
|
||||
loadPokemonSprite(pokeIndex, shiny);
|
||||
|
||||
strncpy(nameBuffer_, pokeName, sizeof(nameBuffer_) - 1);
|
||||
snprintf(levelAndNumberBuffer_, sizeof(levelAndNumberBuffer_), "L %hu No. %hu\nHP: %u/%u", level, pokeNumber, hp, hp);
|
||||
snprintf(otInfoString_, sizeof(otInfoString_), "TID: %u\nOT: %s", trainerID, trainerName);
|
||||
snprintf(movesString, sizeof(movesString), "%s\n%s\n%s\n%s", move1Str, move2Str, move3Str, move4Str);
|
||||
|
||||
if(context_->showReceivedPokemonDialog)
|
||||
{
|
||||
if(context_->isEgg)
|
||||
{
|
||||
setDialogDataText(diag_, "%s received a %s EGG!Take good care of it!", trainerName, pokeName);
|
||||
}
|
||||
else
|
||||
{
|
||||
setDialogDataText(diag_, "%s received %s!\nTake good care of it!", trainerName, pokeName);
|
||||
}
|
||||
showDialog(&diag_);
|
||||
}
|
||||
}
|
||||
|
||||
void StatsScene::destroy()
|
||||
{
|
||||
SceneWithDialogWidget::destroy();
|
||||
|
||||
sprite_free(menu9SliceSprite_);
|
||||
menu9SliceSprite_ = nullptr;
|
||||
|
||||
if(pokeSpriteSurface_.buffer)
|
||||
{
|
||||
surface_free(&pokeSpriteSurface_);
|
||||
}
|
||||
}
|
||||
|
||||
void StatsScene::render(RDPQGraphics& gfx, const Rectangle& sceneBounds)
|
||||
{
|
||||
SceneWithDialogWidget::render(gfx, sceneBounds);
|
||||
|
||||
gfx.drawSprite(backgroundBounds, menu9SliceSprite_, backgroundRenderSettings_);
|
||||
gfx.drawSurface(spriteBounds_, &pokeSpriteSurface_, {0});
|
||||
|
||||
gfx.drawText(nameBounds, nameBuffer_, textSettings_);
|
||||
gfx.drawText(levelAndNumberBounds, levelAndNumberBuffer_, smallTextSettings_);
|
||||
gfx.drawText(otInfoBounds, otInfoString_, smallTextSettings_);
|
||||
gfx.drawText(statsBounds, pokeStatsString_, smallTextSettings_);
|
||||
gfx.drawText(movesBounds, movesString, smallTextSettings_);
|
||||
}
|
||||
|
||||
void StatsScene::onDialogFinished()
|
||||
{
|
||||
deps_.sceneManager.goBackToPreviousScene();
|
||||
}
|
||||
|
||||
void StatsScene::setupDialog(DialogWidgetStyle& style)
|
||||
{
|
||||
style.background.sprite = menu9SliceSprite_;
|
||||
style.background.spriteSettings = {
|
||||
.renderMode = SpriteRenderMode::NINESLICE,
|
||||
.srcRect = { 6, 6, 6, 6 }
|
||||
};
|
||||
|
||||
SceneWithDialogWidget::setupDialog(style);
|
||||
|
||||
dialogWidget_.setOnDialogFinishedCallback(dialogFinishedCallback, this);
|
||||
dialogWidget_.setVisible(false);
|
||||
}
|
||||
|
||||
void StatsScene::loadPokemonSprite(uint8_t pokeIndex, bool shiny)
|
||||
{
|
||||
SpriteRenderer renderer;
|
||||
uint8_t* spriteBuffer;
|
||||
uint8_t* outputBuffer;
|
||||
uint16_t colorPalette[4];
|
||||
uint8_t spriteWidthInTiles;
|
||||
uint8_t spriteHeightInTiles;
|
||||
uint8_t spriteWidthInPixels;
|
||||
uint8_t spriteHeightInPixels;
|
||||
const OutputFormat outputFormat = OutputFormat::RGBA16;
|
||||
const uint8_t numBytesPerColor = getNumBytesPerColorFor(outputFormat);
|
||||
|
||||
switch(deps_.generation)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
Gen1PokeStats pokeStats;
|
||||
gen1GameReader_.readPokemonStatsForIndex(pokeIndex, pokeStats);
|
||||
gen1GameReader_.readColorPalette(gen1GameReader_.getColorPaletteIndexByPokemonNumber(pokeStats.pokedex_number), colorPalette);
|
||||
spriteBuffer = gen1GameReader_.decodeSprite(pokeStats.sprite_bank, pokeStats.pointer_to_frontsprite);
|
||||
spriteWidthInTiles = MAX_SPRITE_TILES_WIDTH;
|
||||
spriteHeightInTiles = MAX_SPRITE_TILES_HEIGHT;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
Gen2PokeStats pokeStats;
|
||||
uint8_t bankIndex;
|
||||
uint16_t bankPointer;
|
||||
gen2GameReader_.readPokemonStatsForIndex(pokeIndex, pokeStats);
|
||||
gen2GameReader_.readFrontSpritePointer(pokeIndex, bankIndex, bankPointer);
|
||||
gen2GameReader_.readSpriteDimensions(pokeStats, spriteWidthInTiles, spriteHeightInTiles);
|
||||
gen2GameReader_.readColorPaletteForPokemon(pokeIndex, shiny, colorPalette);
|
||||
spriteBuffer = gen2GameReader_.decodeSprite(bankIndex, bankPointer);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
outputBuffer = renderer.draw(spriteBuffer, outputFormat, colorPalette, spriteWidthInTiles, spriteHeightInTiles, true);
|
||||
|
||||
spriteWidthInPixels = spriteWidthInTiles * 8;
|
||||
spriteHeightInPixels = spriteHeightInTiles * 8;
|
||||
|
||||
// due to alignment constraints, we should use the surface_alloc function to ensure that it is done correctly
|
||||
// and respect its resulting stride field
|
||||
pokeSpriteSurface_ = surface_alloc(FMT_RGBA16, spriteWidthInPixels, spriteHeightInPixels);
|
||||
spriteBounds_.width = spriteWidthInPixels;
|
||||
spriteBounds_.height = spriteHeightInPixels;
|
||||
|
||||
// now copy the sprite to the surface
|
||||
const uint32_t actualStride = spriteWidthInPixels * numBytesPerColor;
|
||||
for(uint8_t i=0; i < spriteHeightInPixels; ++i)
|
||||
{
|
||||
uint8_t* src = outputBuffer + (i * actualStride);
|
||||
uint8_t* dst = ((uint8_t*)(pokeSpriteSurface_.buffer)) + (i * pokeSpriteSurface_.stride);
|
||||
// copy the source buffer to the surface line by line, but advancing with the surface stride on every new row
|
||||
memcpy(dst, src, actualStride);
|
||||
}
|
||||
}
|
||||
|
||||
void deleteStatsSceneContext(void* context)
|
||||
{
|
||||
auto sceneContext = static_cast<StatsSceneContext*>(context);
|
||||
delete sceneContext;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user