Feature/add about screen (#1)

Adds the "About" scene, which displays the version info and credits. The user can scroll up/down through this screen.
This commit is contained in:
Philippe Symons 2024-08-20 23:57:35 +02:00 committed by GitHub
parent 0ac60b6c02
commit bc7733cc36
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 387 additions and 11 deletions

View File

@ -39,6 +39,13 @@ filesystem/%.sprite: assets/%.png
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/logo-libdragon.sprite: MKSPRITE_FLAGS += -f RGBA32
filesystem/logo-bulbagarden.sprite: MKSPRITE_FLAGS += -f RGBA32
filesystem/logo-retrogamemechanicsexplained.sprite: MKSPRITE_FLAGS += -f RGBA32
filesystem/logo-datacrystal.sprite: MKSPRITE_FLAGS += -f RGBA32
filesystem/logo-pkhex.sprite: MKSPRITE_FLAGS += -f RGBA32
filesystem/logo-gbdevio.sprite: MKSPRITE_FLAGS += -f RGBA32
filesystem/logo-nesdevwiki.sprite: MKSPRITE_FLAGS += -f RGBA32
filesystem/menu-bg-9slice.sprite: MKSPRITE_FLAGS += -f RGBA16
pokemegb:

BIN
assets/logo-bulbagarden.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
assets/logo-datacrystal.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
assets/logo-gbdevio.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
assets/logo-libdragon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
assets/logo-nesdevwiki.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

BIN
assets/logo-pkhex.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 600 B

View File

@ -8,10 +8,24 @@
typedef struct TextRenderSettings
{
/**
* @brief The libdragon font id (used as handle) as known by libdragon. FontManager manages the various fonts
*/
uint8_t fontId;
/**
* @brief The style id used to render the text. (this is a handle/id to a previously registered rdpq_fontstyle_t struct for the fontId specified earlier)
*/
uint8_t fontStyleId;
int16_t charSpacing; ///< Extra spacing between chars (in addition to glyph width and kerning)
int16_t lineSpacing; ///< Extra spacing between lines (in addition to font height)
/**
* @brief The horizontal alignment of the text
*/
rdpq_align_t halign;
/**
* @brief The vertical alignment of the text
*/
rdpq_valign_t valign;
} TextRenderSettings;
typedef struct SpriteRenderSettings SpriteRenderSettings;

View File

@ -17,6 +17,7 @@ void activateFrameLog(void* context, const void* param);
void advanceDialog(void* context, const void* param);
void goToTestScene(void* context, const void* param);
void goToAboutScene(void* context, const void* param);
void goToGen1DistributionPokemonMenu(void* context, const void* param);
void goToGen2DistributionPokemonMenu(void* context, const void* param);

View File

@ -0,0 +1,52 @@
#ifndef _ABOUTSCENE_H
#define _ABOUTSCENE_H
#include "scenes/AbstractUIScene.h"
#include "widget/ScrollWidget.h"
#include "widget/ImageWidget.h"
#include "widget/TextWidget.h"
/**
* @brief This scene shows the version number and credits of the PokeMe64 project.
*/
class AboutScene : public AbstractUIScene
{
public:
AboutScene(SceneDependencies& deps, void* context);
virtual ~AboutScene();
void init() override;
void destroy() override;
bool handleUserInput(joypad_port_t port, const joypad_inputs_t& inputs) override;
void render(RDPQGraphics& gfx, const Rectangle& sceneBounds) override;
protected:
private:
uint8_t fontIdArial_;
uint8_t fontArialStyleWhiteId_;
sprite_t* logoLibDragon_;
sprite_t* logoBulbagarden_;
sprite_t* logoRetroGameMechanicsExplained_;
sprite_t* logoDataCrystal_;
sprite_t* logoPKHEX_;
sprite_t* logoGBDevIO_;
sprite_t* logoNESDevWiki_;
ScrollWidget scrollWidget_;
TextWidget headerText_;
ImageWidget imgDragonWidget_;
ImageWidget imgBulbagardenWidget_;
ImageWidget imgRetroGameMechanicsExplained_;
ImageWidget imgDataCrystal_;
TextWidget dataCrystalText_;
ImageWidget imgPKHEX_;
TextWidget PKHEXText_;
TextWidget pretPokeCrystalText_;
ImageWidget imgGBDevIO_;
TextWidget gbDevIOText_;
ImageWidget imgNESDevWiki_;
TextWidget otherCreditsText_;
WidgetFocusChainSegment scrollFocusSegment_;
bool bButtonPressed_;
};
#endif

View File

@ -18,7 +18,8 @@ enum class SceneType
MENU,
DISTRIBUTION_POKEMON_LIST,
STATS,
TEST
TEST,
ABOUT
};
typedef struct SceneDependencies

View File

@ -7,7 +7,6 @@
typedef struct ImageWidgetStyle
{
Dimensions size;
struct {
/**
* (optional) background sprite

View File

@ -6,9 +6,9 @@
typedef struct TextWidgetStyle
{
Dimensions size;
TextRenderSettings renderSettingsFocused;
TextRenderSettings renderSettingsNotFocused;
uint16_t backgroundColor_; // RGBA16
} TextWidgetStyle;
/**

View File

@ -261,8 +261,8 @@ void RDPQGraphics::drawText(const Rectangle &dstRect, const char *text, const Te
.style_id = renderSettings.fontStyleId,
.width = static_cast<int16_t>(dstRect.width),
.height = static_cast<int16_t>(dstRect.height),
.align = ALIGN_LEFT,
.valign = VALIGN_TOP,
.align = renderSettings.halign,
.valign = renderSettings.valign,
.char_spacing = renderSettings.charSpacing,
.line_spacing = renderSettings.lineSpacing,
.wrap = WRAP_WORD,

View File

@ -15,6 +15,10 @@ MenuItemData gen1MenuEntries[] = {
.title = "Teach Pikachu Fly",
.onConfirmAction = gen1PrepareToTeachPikachu,
.itemParam = &MOVE_FLY
},
{
.title = "About",
.onConfirmAction = goToAboutScene
}
};
@ -32,6 +36,10 @@ MenuItemData gen2MenuEntries[] = {
{
.title = "Unlock Decoration",
.onConfirmAction = goToGen2DecorationMenu
},
{
.title = "About",
.onConfirmAction = goToAboutScene
}
};
@ -53,6 +61,10 @@ MenuItemData gen2CrystalMenuEntries[] = {
{
.title = "Unlock Decoration",
.onConfirmAction = goToGen2DecorationMenu
},
{
.title = "About",
.onConfirmAction = goToAboutScene
}
};

View File

@ -113,6 +113,14 @@ void goToTestScene(void* context, const void* param)
sceneManager.switchScene(SceneType::TEST);
}
void goToAboutScene(void* context, const void* param)
{
MenuScene* scene = static_cast<MenuScene*>(context);
SceneManager& sceneManager = scene->getDependencies().sceneManager;
sceneManager.switchScene(SceneType::ABOUT);
}
void goToGen1DistributionPokemonMenu(void* context, const void*)
{
goToDistributionPokemonListMenu(context, DistributionPokemonListType::GEN1);

272
src/scenes/AboutScene.cpp Normal file
View File

@ -0,0 +1,272 @@
#include "scenes/AboutScene.h"
#include "core/FontManager.h"
#include "scenes/SceneManager.h"
static const ScrollWidgetStyle scrollWidgetStyle = {
.scrollStep = 15,
.marginRight = 0,
.marginBottom = 0
};
static const Rectangle scrollWidgetBounds = {0, 0, 320, 240};
static const Rectangle headerTextBounds = {10, 10, 300, 70};
static const Rectangle imgDragonBounds = {5, 80, 133, 78};
static const Rectangle imgBulbagardenBounds = {163, 121, 134, 17};
static const Rectangle imgRetroGameMechanicsExplainedBounds = {5, 173, 133, 39};
static const Rectangle imgDataCrystalBounds = {153, 163, 60, 60};
static const Rectangle dataCrystalTextBounds = {215, 185, 100, 16};
static const Rectangle imgPKHEXBounds = {25, 233, 38, 37};
static const Rectangle PKHEXTextBounds = {68, 248, 50, 16};
static const Rectangle pretPokeCrystalTextBounds = {153, 248, 175, 16};
static const Rectangle imgGBDevIoBounds = {28, 280, 34, 55};
static const Rectangle gbDevIOTextBounds = {68, 300, 75, 16};
static const Rectangle imgNESDevWikiBounds = {163, 280, 96, 60};
static const Rectangle otherCreditsTextBounds = {5, 350, 310, 80};
static const char* otherCreditsString = R"delim(github.com/magical/pokemon-sprites-rby
glitchcity.wiki
github.com/seanmorris/pokemon-parser
github.com/LinusU/pokemon-sprite-compression
github.com/xvillaneau/poke-sprite-python
)delim";
static const char* headerTextString = R"delim(PokeMe64 Version 0.1
by risingPhil
SPECIAL THANKS TO:
(For docs, tools and/or inspiration)
)delim";
AboutScene::AboutScene(SceneDependencies& deps, void*)
: AbstractUIScene(deps)
, fontIdArial_(1)
, fontArialStyleWhiteId_(0)
, logoLibDragon_(nullptr)
, logoBulbagarden_(nullptr)
, logoRetroGameMechanicsExplained_(nullptr)
, logoDataCrystal_(nullptr)
, logoPKHEX_(nullptr)
, logoGBDevIO_(nullptr)
, logoNESDevWiki_(nullptr)
, scrollWidget_(deps.animationManager)
, headerText_()
, imgDragonWidget_()
, imgBulbagardenWidget_()
, imgRetroGameMechanicsExplained_()
, imgDataCrystal_()
, dataCrystalText_()
, imgPKHEX_()
, PKHEXText_()
, pretPokeCrystalText_()
, imgGBDevIO_()
, gbDevIOText_()
, imgNESDevWiki_()
, otherCreditsText_()
, scrollFocusSegment_(WidgetFocusChainSegment{
.current = &scrollWidget_
})
, bButtonPressed_(false)
{
fontIdArial_ = deps.fontManager.getFont("rom://Arial.font64");
const rdpq_fontstyle_t arialWhite = {
.color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF),
.outline_color = RGBA32(0, 0, 0, 0xFF)
};
deps.fontManager.registerFontStyle(fontIdArial_, fontArialStyleWhiteId_, arialWhite);
}
AboutScene::~AboutScene()
{
}
void AboutScene::init()
{
logoLibDragon_ = sprite_load("rom://logo-libdragon.sprite");
logoBulbagarden_ = sprite_load("rom://logo-bulbagarden.sprite");
logoRetroGameMechanicsExplained_ = sprite_load("rom://logo-retrogamemechanicsexplained.sprite");
logoDataCrystal_ = sprite_load("rom://logo-datacrystal.sprite");
logoPKHEX_ = sprite_load("rom://logo-pkhex.sprite");
logoGBDevIO_ = sprite_load("rom://logo-gbdevio.sprite");
logoNESDevWiki_ = sprite_load("rom://logo-nesdevwiki.sprite");
scrollWidget_.setBounds(scrollWidgetBounds);
scrollWidget_.setStyle(scrollWidgetStyle);
const TextWidgetStyle headerTextStyle = {
.renderSettingsNotFocused = {
.fontId = fontIdArial_,
.fontStyleId = fontArialStyleWhiteId_,
.halign = ALIGN_CENTER
}
// ,.backgroundColor_ = 0xF801
};
const TextWidgetStyle commonLeftAlignedTextStyle = {
.renderSettingsNotFocused = {
.fontId = fontIdArial_,
.fontStyleId = fontArialStyleWhiteId_,
.halign = ALIGN_LEFT
}
};
const TextWidgetStyle commonCenterAlignedTextStyle = {
.renderSettingsNotFocused = {
.fontId = fontIdArial_,
.fontStyleId = fontArialStyleWhiteId_,
.halign = ALIGN_CENTER
}
};
headerText_.setBounds(headerTextBounds);
headerText_.setStyle(headerTextStyle);
headerText_.setData(headerTextString);
scrollWidget_.addWidget(&headerText_);
const ImageWidgetStyle imgDragonStyle = {
.image = {
.sprite = logoLibDragon_,
.spriteBounds = Rectangle{0, 0, imgDragonBounds.width, imgDragonBounds.height}
}
};
imgDragonWidget_.setBounds(imgDragonBounds);
imgDragonWidget_.setStyle(imgDragonStyle);
scrollWidget_.addWidget(&imgDragonWidget_);
const ImageWidgetStyle imgBulbaStyle = {
.image = {
.sprite = logoBulbagarden_,
.spriteBounds = Rectangle{0, 0, imgBulbagardenBounds.width, imgBulbagardenBounds.height}
}
};
imgBulbagardenWidget_.setBounds(imgBulbagardenBounds);
imgBulbagardenWidget_.setStyle(imgBulbaStyle);
scrollWidget_.addWidget(&imgBulbagardenWidget_);
const ImageWidgetStyle imgRetroGameMechanicsExplainedStyle = {
.image = {
.sprite = logoRetroGameMechanicsExplained_,
.spriteBounds = Rectangle{0, 0, imgRetroGameMechanicsExplainedBounds.width, imgRetroGameMechanicsExplainedBounds.height}
}
};
imgRetroGameMechanicsExplained_.setBounds(imgRetroGameMechanicsExplainedBounds);
imgRetroGameMechanicsExplained_.setStyle(imgRetroGameMechanicsExplainedStyle);
scrollWidget_.addWidget(&imgRetroGameMechanicsExplained_);
const ImageWidgetStyle imgDataCrystalStyle = {
.image = {
.sprite = logoDataCrystal_,
.spriteBounds = Rectangle{0, 0, imgDataCrystalBounds.width, imgDataCrystalBounds.height}
}
};
imgDataCrystal_.setBounds(imgDataCrystalBounds);
imgDataCrystal_.setStyle(imgDataCrystalStyle);
scrollWidget_.addWidget(&imgDataCrystal_);
dataCrystalText_.setBounds(dataCrystalTextBounds);
dataCrystalText_.setStyle(commonLeftAlignedTextStyle);
dataCrystalText_.setData("Datacrystal");
scrollWidget_.addWidget(&dataCrystalText_);
const ImageWidgetStyle imgPKHEXStyle = {
.image = {
.sprite = logoPKHEX_,
.spriteBounds = Rectangle{0, 0, imgPKHEXBounds.width, imgPKHEXBounds.height}
}
};
imgPKHEX_.setBounds(imgPKHEXBounds);
imgPKHEX_.setStyle(imgPKHEXStyle);
scrollWidget_.addWidget(&imgPKHEX_);
PKHEXText_.setBounds(PKHEXTextBounds);
PKHEXText_.setStyle(commonLeftAlignedTextStyle);
PKHEXText_.setData("PKHeX");
scrollWidget_.addWidget(&PKHEXText_);
pretPokeCrystalText_.setBounds(pretPokeCrystalTextBounds);
pretPokeCrystalText_.setStyle(commonLeftAlignedTextStyle);
pretPokeCrystalText_.setData("github.com/pret/pokecrystal");
scrollWidget_.addWidget(&pretPokeCrystalText_);
const ImageWidgetStyle imgGBDevIOStyle = {
.image = {
.sprite = logoGBDevIO_,
.spriteBounds = Rectangle{0, 0, imgGBDevIoBounds.width, imgGBDevIoBounds.height}
}
};
imgGBDevIO_.setBounds(imgGBDevIoBounds);
imgGBDevIO_.setStyle(imgGBDevIOStyle);
scrollWidget_.addWidget(&imgGBDevIO_);
gbDevIOText_.setBounds(gbDevIOTextBounds);
gbDevIOText_.setStyle(commonLeftAlignedTextStyle);
gbDevIOText_.setData("gbdev.io");
scrollWidget_.addWidget(&gbDevIOText_);
const ImageWidgetStyle imgNESDevWikiStyle = {
.image = {
.sprite = logoNESDevWiki_,
.spriteBounds = Rectangle{0, 0, imgNESDevWikiBounds.width, imgNESDevWikiBounds.height}
}
};
imgNESDevWiki_.setBounds(imgNESDevWikiBounds);
imgNESDevWiki_.setStyle(imgNESDevWikiStyle);
scrollWidget_.addWidget(&imgNESDevWiki_);
otherCreditsText_.setBounds(otherCreditsTextBounds);
otherCreditsText_.setStyle(commonCenterAlignedTextStyle);
otherCreditsText_.setData(otherCreditsString);
scrollWidget_.addWidget(&otherCreditsText_);
setFocusChain(&scrollFocusSegment_);
}
void AboutScene::destroy()
{
sprite_free(logoLibDragon_);
logoLibDragon_ = nullptr;
sprite_free(logoBulbagarden_);
logoBulbagarden_ = nullptr;
sprite_free(logoRetroGameMechanicsExplained_);
logoRetroGameMechanicsExplained_ = nullptr;
sprite_free(logoDataCrystal_);
logoDataCrystal_ = nullptr;
sprite_free(logoPKHEX_);
logoPKHEX_ = nullptr;
sprite_free(logoGBDevIO_);
logoGBDevIO_ = nullptr;
sprite_free(logoNESDevWiki_);
logoNESDevWiki_ = nullptr;
}
bool AboutScene::handleUserInput(joypad_port_t port, const joypad_inputs_t& inputs)
{
if(AbstractUIScene::handleUserInput(port, inputs))
{
return true;
}
if(inputs.btn.b && !bButtonPressed_)
{
bButtonPressed_ = true;
return true;
}
else if(!inputs.btn.b && bButtonPressed_)
{
deps_.sceneManager.goBackToPreviousScene();
return true;
}
return false;
}
void AboutScene::render(RDPQGraphics& gfx, const Rectangle& sceneBounds)
{
scrollWidget_.render(gfx, sceneBounds);
}

View File

@ -90,13 +90,13 @@ bool MenuScene::handleUserInput(joypad_port_t port, const joypad_inputs_t& input
return true;
}
if(inputs.btn.b)
if(inputs.btn.b && !bButtonPressed_)
{
// we will only handle b button release.
bButtonPressed_ = true;
return true;
}
else if(bButtonPressed_)
else if(!inputs.btn.b && bButtonPressed_)
{
// b button release occurred. Switch back to previous scene.
bButtonPressed_ = false;

View File

@ -1,6 +1,7 @@
#include "scenes/SceneManager.h"
#include "scenes/TestScene.h"
#include "scenes/StatsScene.h"
#include "scenes/AboutScene.h"
#include "scenes/InitTransferPakScene.h"
#include "scenes/DistributionPokemonListScene.h"
@ -135,6 +136,9 @@ void SceneManager::loadScene()
case SceneType::TEST:
scene_ = new TestScene(sceneDeps_, newSceneContext_);
break;
case SceneType::ABOUT:
scene_ = new AboutScene(sceneDeps_, newSceneContext_);
break;
default:
break;
}

View File

@ -62,14 +62,12 @@ void TestScene::init()
const Dimensions pokeballDimensions = {.width = pokeballSprite_->width, .height = pokeballSprite_->height};
TextWidgetStyle type1Style = {
.size = {textDimensions.width, textDimensions.height},
.renderSettingsNotFocused = {
.fontId = fontId
}
};
ImageWidgetStyle type2Style = {
.size = oakDimensions,
.image = {
.sprite = oakSprite_,
.spriteBounds = {0, 0, oakDimensions.width, oakDimensions.height}
@ -77,7 +75,6 @@ void TestScene::init()
};
ImageWidgetStyle type3Style = {
.size = pokeballDimensions,
.image = {
.sprite = pokeballSprite_,
.spriteBounds = {0, 0, pokeballDimensions.width, pokeballDimensions.height}

View File

@ -57,7 +57,10 @@ void ImageWidget::setBounds(const Rectangle& bounds)
Dimensions ImageWidget::getSize() const
{
return style_.size;
return Dimensions{
.width = bounds_.width,
.height = bounds_.height
};
}
void ImageWidget::setConfirmAction(void (*onConfirmAction)(void*), void* context)

View File

@ -105,5 +105,11 @@ void TextWidget::render(RDPQGraphics& gfx, const Rectangle& parentBounds)
TextRenderSettings& renderSettings = (focused_) ? style_.renderSettingsFocused : style_.renderSettingsNotFocused;
const Rectangle absoluteBounds = addOffset(bounds_, parentBounds);
if((style_.backgroundColor_ & 0x1))
{
gfx.fillRectangle(absoluteBounds, color_from_packed16(style_.backgroundColor_));
}
gfx.drawText(absoluteBounds, text_, renderSettings);
}