mirror of
https://github.com/risingPhil/PokeMe64.git
synced 2026-03-21 18:04:15 -05:00
Add menu options to teach Pikachu Surf and Fly
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.
This commit is contained in:
parent
fdc6f90415
commit
eaf9224b31
|
|
@ -1,6 +1,12 @@
|
|||
#ifndef _MENUFUNCTIONS_H
|
||||
#define _MENUFUNCTIONS_H
|
||||
|
||||
#include "Moves.h"
|
||||
|
||||
// these are used to pass as a pointer to the gen1PrepareToTeachPikachu
|
||||
extern const Move MOVE_SURF;
|
||||
extern const Move MOVE_FLY;
|
||||
|
||||
void printMessage(void* context, const void* param);
|
||||
void activateFrameLog(void* context, const void* param);
|
||||
|
||||
|
|
@ -10,6 +16,8 @@ void goToGen1DistributionPokemonMenu(void* context, const void* param);
|
|||
void goToGen2DistributionPokemonMenu(void* context, const void* param);
|
||||
void goToGen2PCNYDistributionPokemonMenu(void* context, const void* param);
|
||||
|
||||
void gen1PrepareToTeachPikachu(void* context, const void* param);
|
||||
void gen1TeachPikachu(void* context, const void* param);
|
||||
void gen2ReceiveGSBall(void* context, const void* param);
|
||||
|
||||
#endif
|
||||
|
|
@ -38,18 +38,11 @@ public:
|
|||
|
||||
SceneDependencies& getDependencies();
|
||||
|
||||
/**
|
||||
* This is a helper function to show a single message to the user.
|
||||
* It should only be used for simple situations. (feedback on a function executed directly in the menu for example)
|
||||
*/
|
||||
void showSingleMessage(const DialogData& messageData);
|
||||
virtual void showDialog(DialogData* diagData);
|
||||
protected:
|
||||
virtual void setupMenu();
|
||||
void setupFonts() override;
|
||||
void setupDialog(DialogWidgetStyle& style) override;
|
||||
|
||||
virtual void showDialog(DialogData* diagData);
|
||||
|
||||
MenuSceneContext* context_;
|
||||
sprite_t* menu9SliceSprite_;
|
||||
sprite_t* cursorSprite_;
|
||||
|
|
@ -57,10 +50,8 @@ protected:
|
|||
CursorWidget cursorWidget_;
|
||||
ListItemFiller<VerticalList, MenuItemData, MenuItemWidget, MenuItemStyle> menuListFiller_;
|
||||
WidgetFocusChainSegment listFocusChainSegment_;
|
||||
uint8_t fontStyleYellowId_;
|
||||
bool bButtonPressed_;
|
||||
private:
|
||||
DialogData singleMessageDialog_;
|
||||
};
|
||||
|
||||
void deleteMenuSceneContext(void* context);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ public:
|
|||
void destroy() override;
|
||||
|
||||
void render(RDPQGraphics& gfx, const Rectangle& sceneBounds) override;
|
||||
|
||||
void advanceDialog();
|
||||
protected:
|
||||
virtual void setupFonts();
|
||||
virtual void setupDialog(DialogWidgetStyle& style);
|
||||
|
|
@ -22,6 +24,7 @@ protected:
|
|||
WidgetFocusChainSegment dialogFocusChainSegment_;
|
||||
uint8_t arialId_;
|
||||
uint8_t fontStyleWhiteId_;
|
||||
uint8_t fontStyleYellowId_;
|
||||
private:
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
#ifndef _DIALOGWIDGET_H
|
||||
#define _DIALOGWIDGET_H
|
||||
|
||||
#include "widget/IWidget.h"
|
||||
#include "widget/VerticalList.h"
|
||||
#include "widget/MenuItemWidget.h"
|
||||
#include "core/Sprite.h"
|
||||
#include "core/RDPQGraphics.h"
|
||||
|
||||
|
|
@ -13,32 +14,52 @@ typedef struct DialogData
|
|||
{
|
||||
char text[DIALOG_TEXT_SIZE];
|
||||
// optional sprite of a character that is saying the dialog text
|
||||
sprite_t* characterSprite;
|
||||
SpriteRenderSettings characterSpriteSettings;
|
||||
// bounds of the character sprite relative to the widget
|
||||
Rectangle characterSpriteBounds;
|
||||
bool characterSpriteVisible;
|
||||
sprite_t* buttonSprite;
|
||||
SpriteRenderSettings buttonSpriteSettings;
|
||||
// bounds of the button sprite relative to the widget
|
||||
Rectangle buttonSpriteBounds;
|
||||
bool buttonSpriteVisible;
|
||||
struct {
|
||||
sprite_t* sprite;
|
||||
SpriteRenderSettings spriteSettings;
|
||||
// bounds of the character sprite relative to the widget
|
||||
Rectangle spriteBounds;
|
||||
bool spriteVisible;
|
||||
} character;
|
||||
// optional button sprite
|
||||
struct {
|
||||
sprite_t* sprite;
|
||||
SpriteRenderSettings spriteSettings;
|
||||
// bounds of the button sprite relative to the widget
|
||||
Rectangle spriteBounds;
|
||||
bool spriteVisible;
|
||||
} button;
|
||||
// use this struct if you want to provide dialog options
|
||||
struct {
|
||||
MenuItemData* items;
|
||||
uint8_t number;
|
||||
bool shouldDeleteWhenDone;
|
||||
} options;
|
||||
|
||||
// The next Dialog
|
||||
struct DialogData* next;
|
||||
bool shouldReleaseWhenDone;
|
||||
bool shouldDeleteWhenDone;
|
||||
bool userAdvanceBlocked;
|
||||
//TODO: dialog sound
|
||||
} DialogData;
|
||||
|
||||
typedef struct DialogWidgetStyle
|
||||
{
|
||||
sprite_t* backgroundSprite;
|
||||
SpriteRenderSettings backgroundSpriteSettings;
|
||||
struct {
|
||||
sprite_t* sprite;
|
||||
SpriteRenderSettings spriteSettings;
|
||||
} background;
|
||||
struct {
|
||||
Rectangle bounds;
|
||||
MenuItemStyle style;
|
||||
} dialogOptions;
|
||||
TextRenderSettings textSettings;
|
||||
int marginLeft;
|
||||
int marginRight;
|
||||
int marginTop;
|
||||
int marginBottom;
|
||||
struct {
|
||||
int left;
|
||||
int right;
|
||||
int top;
|
||||
int bottom;
|
||||
} margin;
|
||||
} DialogWidgetStyle;
|
||||
|
||||
/**
|
||||
|
|
@ -95,6 +116,7 @@ private:
|
|||
*/
|
||||
bool isAdvanceAllowed() const;
|
||||
|
||||
VerticalList dialogOptionList_;
|
||||
AnimationManager& animationManager_;
|
||||
Rectangle bounds_;
|
||||
DialogWidgetStyle style_;
|
||||
|
|
|
|||
|
|
@ -40,30 +40,34 @@ typedef struct MenuItemStyle
|
|||
* width and height for the MenuItemWidget
|
||||
*/
|
||||
Dimensions size;
|
||||
/**
|
||||
* (optional) background sprite
|
||||
*/
|
||||
sprite_t* backgroundSprite;
|
||||
/*
|
||||
* RenderSettings that influence how the backgroundSprite is
|
||||
* being rendered
|
||||
*/
|
||||
SpriteRenderSettings backgroundSpriteSettings;
|
||||
struct {
|
||||
/**
|
||||
* (optional) background sprite
|
||||
*/
|
||||
sprite_t* sprite;
|
||||
/*
|
||||
* RenderSettings that influence how the backgroundSprite is
|
||||
* being rendered
|
||||
*/
|
||||
SpriteRenderSettings spriteSettings;
|
||||
} background;
|
||||
|
||||
/**
|
||||
* (optional) icon sprite
|
||||
*/
|
||||
sprite_t* iconSprite;
|
||||
struct {
|
||||
/**
|
||||
* (optional) icon sprite
|
||||
*/
|
||||
sprite_t* sprite;
|
||||
|
||||
/**
|
||||
* RenderSettings that influence how the iconSprite is being rendered
|
||||
*/
|
||||
SpriteRenderSettings iconSpriteSettings;
|
||||
|
||||
/**
|
||||
* relative bounds of the icon sprite in relation to the MenuItem widget
|
||||
*/
|
||||
Rectangle iconSpriteBounds;
|
||||
/**
|
||||
* RenderSettings that influence how the iconSprite is being rendered
|
||||
*/
|
||||
SpriteRenderSettings spriteSettings;
|
||||
|
||||
/**
|
||||
* relative bounds of the icon sprite in relation to the MenuItem widget
|
||||
*/
|
||||
Rectangle spriteBounds;
|
||||
} icon;
|
||||
/**
|
||||
* These are the text settings for when the MenuItemWidget is NOT focused by the user
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -53,39 +53,62 @@ private:
|
|||
*/
|
||||
typedef struct VerticalListStyle
|
||||
{
|
||||
/**
|
||||
* @brief (optional) a background sprite to render the background of
|
||||
* the widget
|
||||
*/
|
||||
sprite_t* backgroundSprite;
|
||||
/**
|
||||
* @brief (optional) render settings for rendering the background sprite (if any)
|
||||
*
|
||||
*/
|
||||
SpriteRenderSettings backgroundSpriteSettings;
|
||||
/**
|
||||
* @brief left margin -> the widgets will start rendering after this x spacing offset from the left edge of the list
|
||||
*/
|
||||
int marginLeft;
|
||||
/**
|
||||
* @brief right margin -> the widgets will stop rendering x pixels from the right of this list
|
||||
*/
|
||||
int marginRight;
|
||||
/**
|
||||
* @brief top margin -> the widgets will start rendering after this y spacing offset from the top edge of the list
|
||||
*
|
||||
*/
|
||||
int marginTop;
|
||||
/**
|
||||
* @brief bottom margin -> the widgets will stop rendering y pixels from the bottom edge of this list.
|
||||
*
|
||||
*/
|
||||
int marginBottom;
|
||||
struct {
|
||||
/**
|
||||
* @brief (optional) a background sprite to render the background of
|
||||
* the widget
|
||||
*/
|
||||
sprite_t* sprite;
|
||||
/**
|
||||
* @brief (optional) render settings for rendering the background sprite (if any)
|
||||
*
|
||||
*/
|
||||
SpriteRenderSettings spriteSettings;
|
||||
} background;
|
||||
|
||||
struct {
|
||||
/**
|
||||
* @brief left margin -> the widgets will start rendering after this x spacing offset from the left edge of the list
|
||||
*/
|
||||
int left;
|
||||
/**
|
||||
* @brief right margin -> the widgets will stop rendering x pixels from the right of this list
|
||||
*/
|
||||
int right;
|
||||
/**
|
||||
* @brief top margin -> the widgets will start rendering after this y spacing offset from the top edge of the list
|
||||
*/
|
||||
int top;
|
||||
/**
|
||||
* @brief bottom margin -> the widgets will stop rendering y pixels from the bottom edge of this list.
|
||||
*/
|
||||
int bottom;
|
||||
} margin;
|
||||
|
||||
/**
|
||||
* @brief the amount of spacing (in pixels) between 2 list widgets (default: 0)
|
||||
*/
|
||||
int verticalSpacingBetweenWidgets;
|
||||
|
||||
/**
|
||||
* @brief should grow automaticaly with each item
|
||||
*/
|
||||
struct {
|
||||
/**
|
||||
* Whether the list should grow its bounds whenever items are added
|
||||
*/
|
||||
bool enabled;
|
||||
|
||||
/**
|
||||
* defines the direction the list should grow in. true for upwards, false for downwards
|
||||
*/
|
||||
bool shouldGrowUpWards;
|
||||
|
||||
/**
|
||||
* Maximum height the list should grow towards
|
||||
*/
|
||||
uint16_t maxHeight;
|
||||
} autogrow;
|
||||
} VerticalListStyle;
|
||||
|
||||
/**
|
||||
|
|
@ -117,7 +140,9 @@ public:
|
|||
void addWidget(IWidget* widget);
|
||||
void clearWidgets();
|
||||
|
||||
VerticalListStyle& getStyle();
|
||||
void setStyle(const VerticalListStyle& style);
|
||||
|
||||
void setViewWindowStartY(uint32_t windowStartY);
|
||||
|
||||
bool isFocused() const override;
|
||||
|
|
@ -145,6 +170,7 @@ private:
|
|||
int32_t scrollWindowToFocusedWidget();
|
||||
void moveWindow(int32_t yAmount);
|
||||
void notifyFocusListeners(const FocusChangeStatus& status);
|
||||
void autoGrowBounds();
|
||||
|
||||
MoveVerticalListWindowAnimation moveWindowAnimation_;
|
||||
IWidgetList widgetList_;
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 747bdd4b4b27c8d1bd9c44f0d651d4c8c439b90c
|
||||
Subproject commit b275d12a974bb1df92edf38e5bb32267339469ad
|
||||
|
|
@ -5,6 +5,16 @@ MenuItemData gen1MenuEntries[] = {
|
|||
{
|
||||
.title = "Event Pokémon",
|
||||
.onConfirmAction = goToGen1DistributionPokemonMenu
|
||||
},
|
||||
{
|
||||
.title = "Teach Pikachu Surf",
|
||||
.onConfirmAction = gen1PrepareToTeachPikachu,
|
||||
.itemParam = &MOVE_SURF
|
||||
},
|
||||
{
|
||||
.title = "Teach Pikachu Fly",
|
||||
.onConfirmAction = gen1PrepareToTeachPikachu,
|
||||
.itemParam = &MOVE_FLY
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -10,20 +10,36 @@
|
|||
|
||||
#define POKEMON_CRYSTAL_ITEM_ID_GS_BALL 0x73
|
||||
|
||||
#if 0
|
||||
static void goToMenu(void* context, MenuItemData* menuEntries, uint32_t numMenuEntries)
|
||||
const Move MOVE_SURF = Move::SURF;
|
||||
const Move MOVE_FLY = Move::FLY;
|
||||
|
||||
typedef struct Gen1TeachPikachuParams
|
||||
{
|
||||
MenuScene* scene = static_cast<MenuScene*>(context);
|
||||
SceneManager& sceneManager = scene->getDependencies().sceneManager;
|
||||
Move moveType;
|
||||
uint8_t partyIndex;
|
||||
uint8_t moveIndex;
|
||||
Gen1TrainerPokemon poke;
|
||||
} Gen1TeachPikachuParams;
|
||||
|
||||
MenuSceneContext* sceneContext = new MenuSceneContext{
|
||||
.menuEntries = menuEntries,
|
||||
.numMenuEntries = numMenuEntries
|
||||
};
|
||||
|
||||
sceneManager.switchScene(SceneType::MENU, deleteMenuSceneContext, sceneContext);
|
||||
}
|
||||
#endif
|
||||
// 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)
|
||||
{
|
||||
|
|
@ -36,6 +52,25 @@ static void goToDistributionPokemonListMenu(void* context, DistributionPokemonLi
|
|||
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);
|
||||
|
|
@ -73,6 +108,205 @@ 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);
|
||||
|
|
@ -80,7 +314,9 @@ void gen2ReceiveGSBall(void* context, const void* param)
|
|||
TransferPakRomReader romReader(tpakManager);
|
||||
TransferPakSaveManager saveManager(tpakManager);
|
||||
Gen2GameReader gameReader(romReader, saveManager, Gen2GameType::CRYSTAL);
|
||||
DialogData messageData = {0};
|
||||
DialogData* messageData = new DialogData{
|
||||
.shouldDeleteWhenDone = true
|
||||
};
|
||||
bool alreadyHasOne = false;
|
||||
|
||||
tpakManager.setRAMEnabled(true);
|
||||
|
|
@ -106,15 +342,13 @@ void gen2ReceiveGSBall(void* context, const void* param)
|
|||
|
||||
if(alreadyHasOne)
|
||||
{
|
||||
setDialogDataText(messageData, "It appears you already have one!");
|
||||
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);
|
||||
setDialogDataText(*messageData, "%s obtained a GS Ball!", trainerName);
|
||||
}
|
||||
scene->showSingleMessage(messageData);
|
||||
|
||||
tpakManager.setRAMEnabled(false);
|
||||
scene->showDialog(messageData);
|
||||
}
|
||||
|
|
@ -162,12 +162,16 @@ void DistributionPokemonListScene::onDialogDone()
|
|||
void DistributionPokemonListScene::setupMenu()
|
||||
{
|
||||
const VerticalListStyle listStyle = {
|
||||
.backgroundSprite = menu9SliceSprite_,
|
||||
.backgroundSpriteSettings = {
|
||||
.renderMode = SpriteRenderMode::NINESLICE,
|
||||
.srcRect = { 6, 6, 6, 6 }
|
||||
.background = {
|
||||
.sprite = menu9SliceSprite_,
|
||||
.spriteSettings = {
|
||||
.renderMode = SpriteRenderMode::NINESLICE,
|
||||
.srcRect = { 6, 6, 6, 6 }
|
||||
}
|
||||
},
|
||||
.marginTop = 5,
|
||||
.margin = {
|
||||
.top = 5
|
||||
}
|
||||
};
|
||||
|
||||
menuList_.setStyle(listStyle);
|
||||
|
|
|
|||
|
|
@ -149,8 +149,8 @@ void InitTransferPakScene::setupTPakDetectWidget()
|
|||
|
||||
void InitTransferPakScene::setupDialog(DialogWidgetStyle& style)
|
||||
{
|
||||
style.backgroundSprite = menu9SliceSprite_;
|
||||
style.backgroundSpriteSettings = {
|
||||
style.background.sprite = menu9SliceSprite_;
|
||||
style.background.spriteSettings = {
|
||||
.renderMode = SpriteRenderMode::NINESLICE,
|
||||
.srcRect = { 6, 6, 6, 6 }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -21,9 +21,7 @@ MenuScene::MenuScene(SceneDependencies& deps, void* context)
|
|||
, listFocusChainSegment_(WidgetFocusChainSegment{
|
||||
.current = &menuList_
|
||||
})
|
||||
, fontStyleYellowId_(1)
|
||||
, bButtonPressed_(false)
|
||||
, singleMessageDialog_({0})
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -131,33 +129,19 @@ SceneDependencies& MenuScene::getDependencies()
|
|||
return deps_;
|
||||
}
|
||||
|
||||
void MenuScene::showSingleMessage(const DialogData& messageData)
|
||||
{
|
||||
singleMessageDialog_ = messageData;
|
||||
showDialog(&singleMessageDialog_);
|
||||
}
|
||||
|
||||
void MenuScene::setupFonts()
|
||||
{
|
||||
SceneWithDialogWidget::setupFonts();
|
||||
|
||||
const rdpq_fontstyle_t arialYellow = {
|
||||
.color = RGBA32(0xFF, 0xFF, 0x00, 0xFF),
|
||||
.outline_color = RGBA32(0, 0, 0, 0xFF)
|
||||
};
|
||||
|
||||
deps_.fontManager.registerFontStyle(arialId_, fontStyleYellowId_, arialYellow);
|
||||
}
|
||||
|
||||
void MenuScene::setupMenu()
|
||||
{
|
||||
const VerticalListStyle listStyle = {
|
||||
.backgroundSprite = menu9SliceSprite_,
|
||||
.backgroundSpriteSettings = {
|
||||
.renderMode = SpriteRenderMode::NINESLICE,
|
||||
.srcRect = { 6, 6, 6, 6 }
|
||||
.background = {
|
||||
.sprite = menu9SliceSprite_,
|
||||
.spriteSettings = {
|
||||
.renderMode = SpriteRenderMode::NINESLICE,
|
||||
.srcRect = { 6, 6, 6, 6 }
|
||||
}
|
||||
},
|
||||
.marginTop = 5
|
||||
.margin = {
|
||||
.top = 5
|
||||
}
|
||||
};
|
||||
|
||||
const CursorStyle cursorStyle = {
|
||||
|
|
@ -201,8 +185,8 @@ void MenuScene::setupMenu()
|
|||
|
||||
void MenuScene::setupDialog(DialogWidgetStyle& style)
|
||||
{
|
||||
style.backgroundSprite = menu9SliceSprite_;
|
||||
style.backgroundSpriteSettings = {
|
||||
style.background.sprite = menu9SliceSprite_;
|
||||
style.background.spriteSettings = {
|
||||
.renderMode = SpriteRenderMode::NINESLICE,
|
||||
.srcRect = { 6, 6, 6, 6 }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ SceneWithDialogWidget::SceneWithDialogWidget(SceneDependencies& deps)
|
|||
})
|
||||
, arialId_(1)
|
||||
, fontStyleWhiteId_(0)
|
||||
, fontStyleYellowId_(1)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -20,14 +21,32 @@ SceneWithDialogWidget::~SceneWithDialogWidget()
|
|||
void SceneWithDialogWidget::init()
|
||||
{
|
||||
DialogWidgetStyle style = {
|
||||
.dialogOptions = {
|
||||
.bounds = Rectangle{190, 179, 120, 0},
|
||||
.style = {
|
||||
.size = {140, 16},
|
||||
.titleNotFocused = {
|
||||
.fontId = arialId_,
|
||||
.fontStyleId = fontStyleWhiteId_
|
||||
},
|
||||
.titleFocused = {
|
||||
.fontId = arialId_,
|
||||
.fontStyleId = fontStyleYellowId_
|
||||
},
|
||||
.leftMargin = 5,
|
||||
.topMargin = 1
|
||||
}
|
||||
},
|
||||
.textSettings = {
|
||||
.fontId = arialId_,
|
||||
.fontStyleId = fontStyleWhiteId_
|
||||
},
|
||||
.marginLeft = 10,
|
||||
.marginRight = 10,
|
||||
.marginTop = 10,
|
||||
.marginBottom = 10
|
||||
.margin = {
|
||||
.left = 10,
|
||||
.right = 10,
|
||||
.top = 10,
|
||||
.bottom = 10
|
||||
}
|
||||
};
|
||||
|
||||
setupFonts();
|
||||
|
|
@ -49,15 +68,26 @@ void SceneWithDialogWidget::render(RDPQGraphics& gfx, const Rectangle& sceneBoun
|
|||
dialogWidget_.render(gfx, sceneBounds);
|
||||
}
|
||||
|
||||
void SceneWithDialogWidget::advanceDialog()
|
||||
{
|
||||
dialogWidget_.advanceDialog();
|
||||
}
|
||||
|
||||
void SceneWithDialogWidget::setupFonts()
|
||||
{
|
||||
arialId_ = deps_.fontManager.getFont("rom://Arial.font64");
|
||||
|
||||
const rdpq_fontstyle_t arialWhite = {
|
||||
.color = RGBA32(0xFF, 0xFF, 0xFF, 0xFF),
|
||||
.outline_color = RGBA32(0, 0, 0, 0xFF)
|
||||
};
|
||||
const rdpq_fontstyle_t arialYellow = {
|
||||
.color = RGBA32(0xFF, 0xFF, 0x00, 0xFF),
|
||||
.outline_color = RGBA32(0, 0, 0, 0xFF)
|
||||
};
|
||||
|
||||
deps_.fontManager.registerFontStyle(arialId_, fontStyleWhiteId_, arialWhite);
|
||||
deps_.fontManager.registerFontStyle(arialId_, fontStyleYellowId_, arialYellow);
|
||||
}
|
||||
|
||||
void SceneWithDialogWidget::setupDialog(DialogWidgetStyle& style)
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ TransferPakSaveManager::TransferPakSaveManager(TransferPakManager& pakManager)
|
|||
: pakManager_(pakManager)
|
||||
, sramOffset_(0)
|
||||
{
|
||||
// reset back to sram bank 0
|
||||
pakManager_.switchGBSRAMBank(0);
|
||||
}
|
||||
|
||||
TransferPakSaveManager::~TransferPakSaveManager()
|
||||
|
|
|
|||
|
|
@ -1,10 +1,39 @@
|
|||
#include "widget/DialogWidget.h"
|
||||
#include "widget/ListItemFiller.h"
|
||||
#include "core/RDPQGraphics.h"
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
/**
|
||||
* @brief Releases either a single DialogData entry or its entire chain.
|
||||
*/
|
||||
static void releaseEntry(DialogData* data, bool releaseAllEntries)
|
||||
{
|
||||
DialogData* cur = data;
|
||||
DialogData* next = nullptr;
|
||||
do
|
||||
{
|
||||
if(cur)
|
||||
{
|
||||
next = cur->next;
|
||||
if(cur->options.shouldDeleteWhenDone && cur->options.items)
|
||||
{
|
||||
delete[] cur->options.items;
|
||||
cur->options.items = nullptr;
|
||||
}
|
||||
if(cur->shouldDeleteWhenDone)
|
||||
{
|
||||
delete cur;
|
||||
}
|
||||
}
|
||||
cur = next;
|
||||
|
||||
} while(releaseAllEntries && cur);
|
||||
}
|
||||
|
||||
DialogWidget::DialogWidget(AnimationManager& animationManager)
|
||||
: animationManager_(animationManager)
|
||||
: dialogOptionList_(animationManager)
|
||||
, animationManager_(animationManager)
|
||||
, bounds_({0})
|
||||
, style_({0})
|
||||
, data_(nullptr)
|
||||
|
|
@ -14,10 +43,13 @@ DialogWidget::DialogWidget(AnimationManager& animationManager)
|
|||
, visible_(true)
|
||||
, btnAPressedOnPrevCheck_(false)
|
||||
{
|
||||
dialogOptionList_.setVisible(false);
|
||||
}
|
||||
|
||||
DialogWidget::~DialogWidget()
|
||||
{
|
||||
releaseEntry(data_, true);
|
||||
data_ = nullptr;
|
||||
}
|
||||
|
||||
const DialogWidgetStyle& DialogWidget::getStyle() const
|
||||
|
|
@ -28,11 +60,36 @@ const DialogWidgetStyle& DialogWidget::getStyle() const
|
|||
void DialogWidget::setStyle(const DialogWidgetStyle& style)
|
||||
{
|
||||
style_ = style;
|
||||
|
||||
VerticalListStyle& listStyle = dialogOptionList_.getStyle();
|
||||
listStyle.background.sprite = style.background.sprite;
|
||||
listStyle.background.spriteSettings = style.background.spriteSettings;
|
||||
listStyle.margin.left = style.margin.left;
|
||||
listStyle.margin.right = style.margin.right;
|
||||
listStyle.margin.top = style.margin.top;
|
||||
listStyle.margin.bottom = style.margin.bottom;
|
||||
listStyle.autogrow.enabled = true;
|
||||
listStyle.autogrow.shouldGrowUpWards = true;
|
||||
dialogOptionList_.setBounds(style.dialogOptions.bounds);
|
||||
}
|
||||
|
||||
void DialogWidget::setData(DialogData* data)
|
||||
{
|
||||
dialogOptionList_.clearWidgets();
|
||||
dialogOptionList_.setVisible(false);
|
||||
dialogOptionList_.setFocused(false);
|
||||
releaseEntry(data_, true);
|
||||
|
||||
data_ = data;
|
||||
|
||||
if(data_ && data->options.items)
|
||||
{
|
||||
ListItemFiller<VerticalList, MenuItemData, MenuItemWidget, MenuItemStyle> filler(dialogOptionList_);
|
||||
|
||||
filler.addItems(data->options.items, data->options.number, style_.dialogOptions.style);
|
||||
dialogOptionList_.setVisible(true);
|
||||
dialogOptionList_.setFocused(focused_);
|
||||
}
|
||||
}
|
||||
|
||||
void DialogWidget::appendDialogData(DialogData* data)
|
||||
|
|
@ -59,6 +116,11 @@ bool DialogWidget::isFocused() const
|
|||
void DialogWidget::setFocused(bool focused)
|
||||
{
|
||||
focused_ = focused;
|
||||
|
||||
if(data_ && data_->options.number)
|
||||
{
|
||||
dialogOptionList_.setFocused(focused);
|
||||
}
|
||||
}
|
||||
|
||||
bool DialogWidget::isVisible() const
|
||||
|
|
@ -102,18 +164,33 @@ void DialogWidget::advanceDialog()
|
|||
}
|
||||
return;
|
||||
}
|
||||
const DialogData* oldEntry = data_;
|
||||
|
||||
DialogData* oldEntry = data_;
|
||||
data_ = data_->next;
|
||||
|
||||
releaseEntry(oldEntry, false);
|
||||
|
||||
if(oldEntry->shouldReleaseWhenDone)
|
||||
dialogOptionList_.clearWidgets();
|
||||
dialogOptionList_.setFocused(false);
|
||||
dialogOptionList_.setVisible(false);
|
||||
if(data_->options.items)
|
||||
{
|
||||
delete oldEntry;
|
||||
oldEntry = nullptr;
|
||||
ListItemFiller<VerticalList, MenuItemData, MenuItemWidget, MenuItemStyle> filler(dialogOptionList_);
|
||||
|
||||
filler.addItems(data_->options.items, data_->options.number, style_.dialogOptions.style);
|
||||
dialogOptionList_.setVisible(true);
|
||||
dialogOptionList_.setFocused(focused_);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool DialogWidget::handleUserInput(const joypad_inputs_t& userInput)
|
||||
{
|
||||
// if dialog options are displayed and focused, the VerticalList widget needs to handle the key events
|
||||
if(dialogOptionList_.isFocused())
|
||||
{
|
||||
return dialogOptionList_.handleUserInput(userInput);
|
||||
}
|
||||
// make sure the user needs to release the button before handling the A button again
|
||||
// if we don't do that, a different component might react to the same a button press
|
||||
if(btnAPressedOnPrevCheck_)
|
||||
|
|
@ -141,9 +218,9 @@ void DialogWidget::render(RDPQGraphics& gfx, const Rectangle& parentBounds)
|
|||
|
||||
const Rectangle myBounds = addOffset(bounds_, parentBounds);
|
||||
// render the background first, if any.
|
||||
if(style_.backgroundSprite)
|
||||
if(style_.background.sprite)
|
||||
{
|
||||
gfx.drawSprite(myBounds, style_.backgroundSprite, style_.backgroundSpriteSettings);
|
||||
gfx.drawSprite(myBounds, style_.background.sprite, style_.background.spriteSettings);
|
||||
}
|
||||
|
||||
if(!data_)
|
||||
|
|
@ -151,29 +228,31 @@ void DialogWidget::render(RDPQGraphics& gfx, const Rectangle& parentBounds)
|
|||
return;
|
||||
}
|
||||
|
||||
if(data_->characterSprite && data_->characterSpriteVisible)
|
||||
if(data_->character.sprite && data_->character.spriteVisible)
|
||||
{
|
||||
const Rectangle absoluteCharBounds = addOffset(data_->characterSpriteBounds, myBounds);
|
||||
gfx.drawSprite(absoluteCharBounds, data_->characterSprite, data_->characterSpriteSettings);
|
||||
const Rectangle absoluteCharBounds = addOffset(data_->character.spriteBounds, myBounds);
|
||||
gfx.drawSprite(absoluteCharBounds, data_->character.sprite, data_->character.spriteSettings);
|
||||
}
|
||||
|
||||
if(data_->buttonSprite && data_->buttonSpriteVisible)
|
||||
if(data_->button.sprite && data_->button.spriteVisible)
|
||||
{
|
||||
const Rectangle absoluteButtonSpriteBounds = addOffset(data_->buttonSpriteBounds, myBounds);
|
||||
gfx.drawSprite(absoluteButtonSpriteBounds, data_->buttonSprite, data_->buttonSpriteSettings);
|
||||
const Rectangle absoluteButtonSpriteBounds = addOffset(data_->button.spriteBounds, myBounds);
|
||||
gfx.drawSprite(absoluteButtonSpriteBounds, data_->button.sprite, data_->button.spriteSettings);
|
||||
}
|
||||
|
||||
if(data_->text[0] != '\0')
|
||||
{
|
||||
const Rectangle textBounds = {
|
||||
.x = myBounds.x + style_.marginLeft,
|
||||
.y = myBounds.y + style_.marginTop,
|
||||
.width = myBounds.width - style_.marginLeft - style_.marginRight,
|
||||
.height = myBounds.height - style_.marginTop - style_.marginBottom
|
||||
.x = myBounds.x + style_.margin.left,
|
||||
.y = myBounds.y + style_.margin.top,
|
||||
.width = myBounds.width - style_.margin.left - style_.margin.right,
|
||||
.height = myBounds.height - style_.margin.top - style_.margin.bottom
|
||||
};
|
||||
|
||||
gfx.drawText(textBounds, data_->text, style_.textSettings);
|
||||
}
|
||||
|
||||
dialogOptionList_.render(gfx, parentBounds);
|
||||
}
|
||||
|
||||
bool DialogWidget::isAdvanceAllowed() const
|
||||
|
|
|
|||
|
|
@ -84,15 +84,15 @@ void MenuItemWidget::render(RDPQGraphics& gfx, const Rectangle& parentBounds)
|
|||
return;
|
||||
}
|
||||
Rectangle myBounds = {.x = parentBounds.x, .y = parentBounds.y, .width = style_.size.width, .height = style_.size.height};
|
||||
if(style_.backgroundSprite)
|
||||
if(style_.background.sprite)
|
||||
{
|
||||
gfx.drawSprite(myBounds, style_.backgroundSprite, style_.backgroundSpriteSettings);
|
||||
gfx.drawSprite(myBounds, style_.background.sprite, style_.background.spriteSettings);
|
||||
}
|
||||
|
||||
if(style_.iconSprite)
|
||||
if(style_.icon.sprite)
|
||||
{
|
||||
const Rectangle iconSpriteBounds = addOffset(style_.iconSpriteBounds, myBounds);
|
||||
gfx.drawSprite(iconSpriteBounds, style_.iconSprite, style_.iconSpriteSettings);
|
||||
const Rectangle iconSpriteBounds = addOffset(style_.icon.spriteBounds, myBounds);
|
||||
gfx.drawSprite(iconSpriteBounds, style_.icon.sprite, style_.icon.spriteSettings);
|
||||
}
|
||||
|
||||
myBounds.x += style_.leftMargin;
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ bool VerticalList::focusNext()
|
|||
++focusedWidgetIndex_;
|
||||
|
||||
changeStatus.curFocus = widgetList_[focusedWidgetIndex_];
|
||||
changeStatus.focusBounds = calculateListWidgetBounds(widgetBoundsList_[focusedWidgetIndex_], windowMinY_, bounds_.x + listStyle_.marginLeft, bounds_.y + listStyle_.marginTop);
|
||||
changeStatus.focusBounds = calculateListWidgetBounds(widgetBoundsList_[focusedWidgetIndex_], windowMinY_, bounds_.x + listStyle_.margin.left, bounds_.y + listStyle_.margin.top);
|
||||
widgetList_[focusedWidgetIndex_]->setFocused(true);
|
||||
|
||||
const int32_t scrollAmountY = scrollWindowToFocusedWidget();
|
||||
|
|
@ -165,7 +165,7 @@ bool VerticalList::focusPrevious()
|
|||
--focusedWidgetIndex_;
|
||||
|
||||
changeStatus.curFocus = widgetList_[focusedWidgetIndex_];
|
||||
changeStatus.focusBounds = calculateListWidgetBounds(widgetBoundsList_[focusedWidgetIndex_], windowMinY_, bounds_.x + listStyle_.marginLeft, bounds_.y + listStyle_.marginTop);
|
||||
changeStatus.focusBounds = calculateListWidgetBounds(widgetBoundsList_[focusedWidgetIndex_], windowMinY_, bounds_.x + listStyle_.margin.left, bounds_.y + listStyle_.margin.top);
|
||||
widgetList_[focusedWidgetIndex_]->setFocused(true);
|
||||
|
||||
const int32_t scrollAmountY = scrollWindowToFocusedWidget();
|
||||
|
|
@ -187,7 +187,7 @@ void VerticalList::addWidget(IWidget *widget)
|
|||
widget->setFocused(focused_);
|
||||
|
||||
FocusChangeStatus changeStatus = {
|
||||
.focusBounds = calculateListWidgetBounds(widgetBoundsList_[focusedWidgetIndex_], windowMinY_, bounds_.x + listStyle_.marginLeft, bounds_.y + listStyle_.marginTop),
|
||||
.focusBounds = calculateListWidgetBounds(widgetBoundsList_[focusedWidgetIndex_], windowMinY_, bounds_.x + listStyle_.margin.left, bounds_.y + listStyle_.margin.top),
|
||||
.prevFocus = nullptr,
|
||||
.curFocus = widget
|
||||
};
|
||||
|
|
@ -198,12 +198,19 @@ void VerticalList::addWidget(IWidget *widget)
|
|||
const Rectangle lastWidgetBounds = widgetBoundsList_.back();
|
||||
widgetBoundsList_.push_back(Rectangle{.x = 0, .y = lastWidgetBounds.y + lastWidgetBounds.height + listStyle_.verticalSpacingBetweenWidgets, .width = widgetSize.width, .height = widgetSize.height});
|
||||
}
|
||||
autoGrowBounds();
|
||||
}
|
||||
|
||||
void VerticalList::clearWidgets()
|
||||
{
|
||||
widgetList_.clear();
|
||||
widgetBoundsList_.clear();
|
||||
focusedWidgetIndex_ = 0;
|
||||
}
|
||||
|
||||
VerticalListStyle& VerticalList::getStyle()
|
||||
{
|
||||
return listStyle_;
|
||||
}
|
||||
|
||||
void VerticalList::setStyle(const VerticalListStyle& style)
|
||||
|
|
@ -232,7 +239,7 @@ void VerticalList::setFocused(bool isFocused)
|
|||
widgetList_[focusedWidgetIndex_]->setFocused(focused_);
|
||||
|
||||
FocusChangeStatus changeStatus = {
|
||||
.focusBounds = calculateListWidgetBounds(widgetBoundsList_[focusedWidgetIndex_], windowMinY_, bounds_.x + listStyle_.marginLeft, bounds_.y + listStyle_.marginTop)
|
||||
.focusBounds = calculateListWidgetBounds(widgetBoundsList_[focusedWidgetIndex_], windowMinY_, bounds_.x + listStyle_.margin.left, bounds_.y + listStyle_.margin.top)
|
||||
};
|
||||
|
||||
if(isFocused)
|
||||
|
|
@ -318,11 +325,11 @@ void VerticalList::render(RDPQGraphics& gfx, const Rectangle& parentBounds)
|
|||
return;
|
||||
}
|
||||
|
||||
const uint32_t innerListHeight = getInnerListHeight(bounds_, listStyle_.marginTop, listStyle_.marginBottom);
|
||||
const uint32_t innerListHeight = getInnerListHeight(bounds_, listStyle_.margin.top, listStyle_.margin.bottom);
|
||||
uint32_t i;
|
||||
const Rectangle myBounds = addOffset(bounds_, parentBounds);
|
||||
const int topX = myBounds.x + listStyle_.marginLeft;
|
||||
const int topY = myBounds.y + listStyle_.marginTop;
|
||||
const int topX = myBounds.x + listStyle_.margin.left;
|
||||
const int topY = myBounds.y + listStyle_.margin.top;
|
||||
|
||||
// store previous clipping rectangle to restore later
|
||||
// const Rectangle prevClipRect = gfx.getClippingRectangle();
|
||||
|
|
@ -330,9 +337,9 @@ void VerticalList::render(RDPQGraphics& gfx, const Rectangle& parentBounds)
|
|||
// gfx.setClippingRectangle(myBounds);
|
||||
|
||||
// render the background first, if any.
|
||||
if(listStyle_.backgroundSprite)
|
||||
if(listStyle_.background.sprite)
|
||||
{
|
||||
gfx.drawSprite(myBounds, listStyle_.backgroundSprite, listStyle_.backgroundSpriteSettings);
|
||||
gfx.drawSprite(myBounds, listStyle_.background.sprite, listStyle_.background.spriteSettings);
|
||||
}
|
||||
|
||||
if(widgetList_.empty())
|
||||
|
|
@ -416,7 +423,7 @@ int32_t VerticalList::scrollWindowToFocusedWidget()
|
|||
// the reason is the use of isWidgetInsideWindow() inside the render() function to cull entries from the render window.
|
||||
// We could potentially eliminate this by expanding the check to allow a partially visible entry to be rendered.
|
||||
// But for my goals, this is currently not needed, so I claim YAGNI for now.
|
||||
const uint32_t innerListHeight = getInnerListHeight(bounds_, listStyle_.marginTop, listStyle_.marginBottom);
|
||||
const uint32_t innerListHeight = getInnerListHeight(bounds_, listStyle_.margin.top, listStyle_.margin.bottom);
|
||||
const int32_t windowScrollYNeeded = getVerticalWindowScrollNeededToMakeWidgetFullyVisible(widgetBoundsList_[focusedWidgetIndex_], innerListHeight, windowMinY_);
|
||||
if(windowScrollYNeeded != 0)
|
||||
{
|
||||
|
|
@ -436,4 +443,27 @@ void VerticalList::notifyFocusListeners(const FocusChangeStatus& status)
|
|||
{
|
||||
listener->focusChanged(status);
|
||||
}
|
||||
}
|
||||
|
||||
void VerticalList::autoGrowBounds()
|
||||
{
|
||||
if(!listStyle_.autogrow.enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const Rectangle& lastWidgetBounds = widgetBoundsList_.back();
|
||||
int heightToConsider = lastWidgetBounds.y + lastWidgetBounds.height + listStyle_.margin.top + listStyle_.margin.bottom;
|
||||
if(listStyle_.autogrow.maxHeight && listStyle_.autogrow.maxHeight < heightToConsider)
|
||||
{
|
||||
heightToConsider = listStyle_.autogrow.maxHeight;
|
||||
}
|
||||
|
||||
if(listStyle_.autogrow.shouldGrowUpWards)
|
||||
{
|
||||
const int heightDiff = abs(heightToConsider - bounds_.height);
|
||||
bounds_.y -= heightDiff;
|
||||
}
|
||||
|
||||
bounds_.height = heightToConsider;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user