mirror of
https://github.com/risingPhil/PokeMe64.git
synced 2026-04-26 08:03:31 -05:00
Various changes
- Add "Pikachu Bed" and "Tentacool Doll" decoration unlock options for gen 2 - bugfix for unsafe behaviour - attempt to make the RESET button work for cartridge switching. It failed though. (not reliable). Therefore I just added a warning message instead. - Work on new ScrollWidget for Credits/About screen (Work In Progress)
This commit is contained in:
parent
6ce9c1ec23
commit
f7a687b147
|
|
@ -16,6 +16,8 @@ public:
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
|
|
||||||
|
void onResetInterrupt();
|
||||||
protected:
|
protected:
|
||||||
private:
|
private:
|
||||||
RDPQGraphics graphics_;
|
RDPQGraphics graphics_;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,21 @@
|
||||||
#ifndef _CORE_COMMON_H
|
#ifndef _CORE_COMMON_H
|
||||||
#define _CORE_COMMON_H
|
#define _CORE_COMMON_H
|
||||||
|
|
||||||
|
typedef struct Point
|
||||||
|
{
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
} Point;
|
||||||
|
|
||||||
|
typedef struct FloatPoint
|
||||||
|
{
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
} FloatPoint;
|
||||||
|
|
||||||
|
typedef Point Vector;
|
||||||
|
typedef FloatPoint FloatVector;
|
||||||
|
|
||||||
typedef struct Dimensions
|
typedef struct Dimensions
|
||||||
{
|
{
|
||||||
int width;
|
int width;
|
||||||
|
|
@ -27,6 +42,10 @@ bool isZeroSizeRectangle(const Rectangle& rect);
|
||||||
*/
|
*/
|
||||||
Rectangle addOffset(const Rectangle& a, const Rectangle& b);
|
Rectangle addOffset(const Rectangle& a, const Rectangle& b);
|
||||||
|
|
||||||
|
bool isPointInsideRectangle(const Rectangle& a, const Point& p);
|
||||||
|
|
||||||
|
bool doRectanglesOverlap(const Rectangle& a, const Rectangle& b);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Extract a Dimensions struct from a Rectangle that only contains the width and height
|
* @brief Extract a Dimensions struct from a Rectangle that only contains the width and height
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -3,12 +3,17 @@
|
||||||
|
|
||||||
#include "Moves.h"
|
#include "Moves.h"
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
// these are used to pass as a pointer to the gen1PrepareToTeachPikachu
|
// these are used to pass as a pointer to the gen1PrepareToTeachPikachu
|
||||||
extern const Move MOVE_SURF;
|
extern const Move MOVE_SURF;
|
||||||
extern const Move MOVE_FLY;
|
extern const Move MOVE_FLY;
|
||||||
|
|
||||||
void printMessage(void* context, const void* param);
|
extern const uint16_t GEN2_EVENTFLAG_DECORATION_PIKACHU_BED;
|
||||||
|
extern const uint16_t GEN2_EVENTFLAG_DECORATION_TENTACOOL_DOLL;
|
||||||
|
|
||||||
void activateFrameLog(void* context, const void* param);
|
void activateFrameLog(void* context, const void* param);
|
||||||
|
void advanceDialog(void* context, const void* param);
|
||||||
|
|
||||||
void goToTestScene(void* context, const void* param);
|
void goToTestScene(void* context, const void* param);
|
||||||
|
|
||||||
|
|
@ -19,5 +24,7 @@ void goToGen2PCNYDistributionPokemonMenu(void* context, const void* param);
|
||||||
void gen1PrepareToTeachPikachu(void* context, const void* param);
|
void gen1PrepareToTeachPikachu(void* context, const void* param);
|
||||||
void gen1TeachPikachu(void* context, const void* param);
|
void gen1TeachPikachu(void* context, const void* param);
|
||||||
void gen2ReceiveGSBall(void* context, const void* param);
|
void gen2ReceiveGSBall(void* context, const void* param);
|
||||||
|
void gen2SetEventFlag(void* context, const void* param);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -49,6 +49,8 @@ typedef struct WidgetFocusChainSegment
|
||||||
class AbstractUIScene : public IScene
|
class AbstractUIScene : public IScene
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static const uint16_t MINIMUM_TIME_BETWEEN_INPUT_EVENTS;
|
||||||
|
|
||||||
AbstractUIScene(SceneDependencies& deps);
|
AbstractUIScene(SceneDependencies& deps);
|
||||||
virtual ~AbstractUIScene();
|
virtual ~AbstractUIScene();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ typedef struct MenuSceneContext
|
||||||
{
|
{
|
||||||
MenuItemData* menuEntries;
|
MenuItemData* menuEntries;
|
||||||
uint32_t numMenuEntries;
|
uint32_t numMenuEntries;
|
||||||
|
bool bButtonMeansUserWantsToSwitchCartridge;
|
||||||
} MenuSceneContext;
|
} MenuSceneContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -38,7 +39,7 @@ public:
|
||||||
|
|
||||||
SceneDependencies& getDependencies();
|
SceneDependencies& getDependencies();
|
||||||
|
|
||||||
virtual void showDialog(DialogData* diagData);
|
void showDialog(DialogData* diagData) override;
|
||||||
protected:
|
protected:
|
||||||
virtual void setupMenu();
|
virtual void setupMenu();
|
||||||
void setupDialog(DialogWidgetStyle& style) override;
|
void setupDialog(DialogWidgetStyle& style) override;
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ public:
|
||||||
* WARNING: If you specify a sceneContext, you MUST also specify a deleteContextFunc callback function pointer.
|
* WARNING: If you specify a sceneContext, you MUST also specify a deleteContextFunc callback function pointer.
|
||||||
* This is needed because you can't call delete() on a void*. And we need to keep the context around in the sceneHistory for as long as it needs to
|
* This is needed because you can't call delete() on a void*. And we need to keep the context around in the sceneHistory for as long as it needs to
|
||||||
*/
|
*/
|
||||||
void switchScene(SceneType sceneType, void (*deleteContextFunc)(void*) = nullptr, void* sceneContext = nullptr);
|
void switchScene(SceneType sceneType, void (*deleteContextFunc)(void*) = nullptr, void* sceneContext = nullptr, bool deleteHistory = false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Switch back to the previous scene in the history stack
|
* @brief Switch back to the previous scene in the history stack
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ public:
|
||||||
void render(RDPQGraphics& gfx, const Rectangle& sceneBounds) override;
|
void render(RDPQGraphics& gfx, const Rectangle& sceneBounds) override;
|
||||||
|
|
||||||
void advanceDialog();
|
void advanceDialog();
|
||||||
|
virtual void showDialog(DialogData* diagData);
|
||||||
protected:
|
protected:
|
||||||
virtual void setupFonts();
|
virtual void setupFonts();
|
||||||
virtual void setupDialog(DialogWidgetStyle& style);
|
virtual void setupDialog(DialogWidgetStyle& style);
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ public:
|
||||||
void setPort(joypad_port_t port);
|
void setPort(joypad_port_t port);
|
||||||
|
|
||||||
bool hasTransferPak();
|
bool hasTransferPak();
|
||||||
|
bool isPoweredOn() const;
|
||||||
bool setPower(bool on);
|
bool setPower(bool on);
|
||||||
uint8_t getStatus();
|
uint8_t getStatus();
|
||||||
|
|
||||||
|
|
@ -92,7 +93,7 @@ public:
|
||||||
protected:
|
protected:
|
||||||
private:
|
private:
|
||||||
joypad_port_t port_;
|
joypad_port_t port_;
|
||||||
bool wasPoweredAtLeastOnce_;
|
bool isPoweredOn_;
|
||||||
uint8_t currentSRAMBank_;
|
uint8_t currentSRAMBank_;
|
||||||
uint16_t readBufferBankOffset_;
|
uint16_t readBufferBankOffset_;
|
||||||
uint16_t writeBufferSRAMBankOffset_;
|
uint16_t writeBufferSRAMBankOffset_;
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,7 @@
|
||||||
|
|
||||||
#include "widget/VerticalList.h"
|
#include "widget/VerticalList.h"
|
||||||
#include "widget/MenuItemWidget.h"
|
#include "widget/MenuItemWidget.h"
|
||||||
#include "core/Sprite.h"
|
#include "widget/ListItemFiller.h"
|
||||||
#include "core/RDPQGraphics.h"
|
|
||||||
|
|
||||||
#define DIALOG_TEXT_SIZE 512
|
#define DIALOG_TEXT_SIZE 512
|
||||||
|
|
||||||
|
|
@ -117,6 +116,7 @@ private:
|
||||||
bool isAdvanceAllowed() const;
|
bool isAdvanceAllowed() const;
|
||||||
|
|
||||||
VerticalList dialogOptionList_;
|
VerticalList dialogOptionList_;
|
||||||
|
ListItemFiller<VerticalList, MenuItemData, MenuItemWidget, MenuItemStyle> dialogOptionListFiller_;
|
||||||
AnimationManager& animationManager_;
|
AnimationManager& animationManager_;
|
||||||
Rectangle bounds_;
|
Rectangle bounds_;
|
||||||
DialogWidgetStyle style_;
|
DialogWidgetStyle style_;
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,7 @@ public:
|
||||||
|
|
||||||
~ListItemFiller()
|
~ListItemFiller()
|
||||||
{
|
{
|
||||||
for(ListItemWidgetType* item : widgets_)
|
deleteWidgets();
|
||||||
{
|
|
||||||
delete item;
|
|
||||||
}
|
|
||||||
widgets_.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void addItems(ListDataType* dataList, size_t dataListSize, const MenuItemStyle& itemStyle)
|
void addItems(ListDataType* dataList, size_t dataListSize, const MenuItemStyle& itemStyle)
|
||||||
|
|
@ -44,6 +40,15 @@ public:
|
||||||
list_.addWidget(itemWidget);
|
list_.addWidget(itemWidget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deleteWidgets()
|
||||||
|
{
|
||||||
|
for(ListItemWidgetType* item : widgets_)
|
||||||
|
{
|
||||||
|
delete item;
|
||||||
|
}
|
||||||
|
widgets_.clear();
|
||||||
|
}
|
||||||
protected:
|
protected:
|
||||||
private:
|
private:
|
||||||
ListType& list_;
|
ListType& list_;
|
||||||
|
|
|
||||||
|
|
@ -115,7 +115,7 @@ protected:
|
||||||
/**
|
/**
|
||||||
* Executes the onConfirmAction callback (if any)
|
* Executes the onConfirmAction callback (if any)
|
||||||
*/
|
*/
|
||||||
void execute();
|
bool execute();
|
||||||
private:
|
private:
|
||||||
MenuItemData data_;
|
MenuItemData data_;
|
||||||
MenuItemStyle style_;
|
MenuItemStyle style_;
|
||||||
|
|
|
||||||
115
include/widget/ScrollWidget.h
Normal file
115
include/widget/ScrollWidget.h
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
#ifndef _SCROLLWIDGET_H
|
||||||
|
#define _SCROLLWIDGET_H
|
||||||
|
|
||||||
|
#include "widget/IWidget.h"
|
||||||
|
#include "animations/IAnimation.h"
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
typedef std::vector<IWidget*> WidgetList;
|
||||||
|
|
||||||
|
class ScrollWidget;
|
||||||
|
class AnimationManager;
|
||||||
|
|
||||||
|
typedef struct ScrollWidgetStyle
|
||||||
|
{
|
||||||
|
// it defines the amount of pixels we should scroll when the analog stick or dpad is used.
|
||||||
|
uint8_t scrollStep;
|
||||||
|
} ScrollWidgetStyle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This Animation implementation is used internal in ScrollWidget
|
||||||
|
* to move the "view window"'s x and y-coordinates.
|
||||||
|
*
|
||||||
|
* It's specific to the ScrollWidget
|
||||||
|
*/
|
||||||
|
class MoveScrollWidgetWindowAnimation : public AbstractAnimation
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MoveScrollWidgetWindowAnimation(ScrollWidget* list);
|
||||||
|
virtual ~MoveScrollWidgetWindowAnimation();
|
||||||
|
|
||||||
|
AnimationDistanceTimeFunctionType getDistanceTimeFunctionType() const override;
|
||||||
|
|
||||||
|
uint32_t getDurationInMs() const override;
|
||||||
|
|
||||||
|
void start(const Point& startPoint, const Point& endPoint);
|
||||||
|
protected:
|
||||||
|
void apply(float pos) override;
|
||||||
|
private:
|
||||||
|
ScrollWidget* list_;
|
||||||
|
Point windowStartPoint_;
|
||||||
|
Point windowEndPoint_;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief This is a widget that contains other widgets and can scroll through it.
|
||||||
|
* It differs from VerticalList in that ScrollWidget manages "unstructured" widgets instead of just a list in a certain direction.
|
||||||
|
*
|
||||||
|
* But this scroll widget does not allow you to select a widget and/or handle events on it. Every widget added will remain unfocused and won't handle keys
|
||||||
|
* display only.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
class ScrollWidget : public IWidget
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScrollWidget(AnimationManager& animManager);
|
||||||
|
virtual ~ScrollWidget();
|
||||||
|
|
||||||
|
bool isFocused() const override;
|
||||||
|
void setFocused(bool isFocused) override;
|
||||||
|
|
||||||
|
bool isVisible() const override;
|
||||||
|
void setVisible(bool visible) override;
|
||||||
|
|
||||||
|
Rectangle getBounds() const override;
|
||||||
|
void setBounds(const Rectangle& bounds) override;
|
||||||
|
Dimensions getSize() const override;
|
||||||
|
|
||||||
|
void setStyle(const ScrollWidgetStyle& style);
|
||||||
|
|
||||||
|
void addWidget(IWidget* widget);
|
||||||
|
void removeWidget(IWidget* widget);
|
||||||
|
|
||||||
|
bool handleUserInput(const joypad_inputs_t& userInput) override;
|
||||||
|
void render(RDPQGraphics& gfx, const Rectangle& parentBounds) override;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets the view window start xy coords.
|
||||||
|
* This is used by MoveScrollWidgetWindowAnimation
|
||||||
|
*/
|
||||||
|
void setWindowStart(const Point& windowStart);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Indicates the percentage at which the current window position is at horizontally
|
||||||
|
* in relation to the entire window
|
||||||
|
*/
|
||||||
|
float getWindowProgressX() const;
|
||||||
|
/**
|
||||||
|
* @brief Indicates the percentage at which the current window position is at vertically
|
||||||
|
* in relation to the entire window
|
||||||
|
*/
|
||||||
|
float getWindowProgressY() const;
|
||||||
|
protected:
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* @brief This function will grow the maximum dimensions of the window
|
||||||
|
* based on the specified IWidget
|
||||||
|
*/
|
||||||
|
void growWindow(IWidget* widget);
|
||||||
|
/**
|
||||||
|
* @brief Recalculates the entire width and height of the view window
|
||||||
|
* This is necessary when removing a widget
|
||||||
|
*/
|
||||||
|
void recalculateWindowSize();
|
||||||
|
|
||||||
|
MoveScrollWidgetWindowAnimation windowAnimation_;
|
||||||
|
WidgetList widgets_;
|
||||||
|
ScrollWidgetStyle style_;
|
||||||
|
AnimationManager& animManager_;
|
||||||
|
Rectangle bounds_;
|
||||||
|
Rectangle windowBounds_;
|
||||||
|
bool visible_;
|
||||||
|
bool focused_;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -1,6 +1,16 @@
|
||||||
#include "core/Application.h"
|
#include "core/Application.h"
|
||||||
#include "scenes/IScene.h"
|
#include "scenes/IScene.h"
|
||||||
|
|
||||||
|
static Application* appInstance = nullptr;
|
||||||
|
static void resetInterruptHandler()
|
||||||
|
{
|
||||||
|
if(!appInstance)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
appInstance->onResetInterrupt();
|
||||||
|
}
|
||||||
|
|
||||||
Application::Application()
|
Application::Application()
|
||||||
: graphics_()
|
: graphics_()
|
||||||
, animationManager_()
|
, animationManager_()
|
||||||
|
|
@ -9,15 +19,21 @@ Application::Application()
|
||||||
, sceneManager_(graphics_, animationManager_, fontManager_, tpakManager_)
|
, sceneManager_(graphics_, animationManager_, fontManager_, tpakManager_)
|
||||||
, sceneBounds_({0})
|
, sceneBounds_({0})
|
||||||
{
|
{
|
||||||
|
appInstance = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application()
|
Application::~Application()
|
||||||
{
|
{
|
||||||
|
tpakManager_.setPower(false);
|
||||||
|
unregister_RESET_handler(resetInterruptHandler);
|
||||||
|
|
||||||
graphics_.destroy();
|
graphics_.destroy();
|
||||||
|
|
||||||
display_close();
|
display_close();
|
||||||
timer_close();
|
timer_close();
|
||||||
joypad_close();
|
joypad_close();
|
||||||
|
|
||||||
|
appInstance = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::init()
|
void Application::init()
|
||||||
|
|
@ -37,6 +53,8 @@ void Application::init()
|
||||||
//display_init(RESOLUTION_320x240, DEPTH_16_BPP, 3, GAMMA_NONE, ANTIALIAS_RESAMPLE_FETCH_ALWAYS);
|
//display_init(RESOLUTION_320x240, DEPTH_16_BPP, 3, GAMMA_NONE, ANTIALIAS_RESAMPLE_FETCH_ALWAYS);
|
||||||
|
|
||||||
sceneManager_.switchScene(SceneType::INIT_TRANSFERPAK);
|
sceneManager_.switchScene(SceneType::INIT_TRANSFERPAK);
|
||||||
|
|
||||||
|
register_RESET_handler(resetInterruptHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::run()
|
void Application::run()
|
||||||
|
|
@ -57,3 +75,8 @@ void Application::run()
|
||||||
graphics_.finishAndShowFrame();
|
graphics_.finishAndShowFrame();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::onResetInterrupt()
|
||||||
|
{
|
||||||
|
tpakManager_.setPower(false);
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,26 @@ Rectangle addOffset(const Rectangle &a, const Rectangle &b)
|
||||||
return Rectangle{.x = a.x + b.x, .y = a.y + b.y, .width = a.width, .height = a.height};
|
return Rectangle{.x = a.x + b.x, .y = a.y + b.y, .width = a.width, .height = a.height};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isPointInsideRectangle(const Rectangle& a, const Point& p)
|
||||||
|
{
|
||||||
|
const bool isInsideHorizontally = (p.x >= a.x) && (p.x < (a.x + a.width));
|
||||||
|
const bool isInsideVertically = (p.y >= a.y) && (p.y < (a.y + a.width));
|
||||||
|
return (isInsideHorizontally && isInsideVertically);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool doRectanglesOverlap(const Rectangle& a, const Rectangle& b)
|
||||||
|
{
|
||||||
|
if (a.x < (b.x + b.width) && (a.x + a.width) > b.x &&
|
||||||
|
a.y + a.height > b.y && a.y < (b.y + b.height) )
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Dimensions getDimensions(const Rectangle &r)
|
Dimensions getDimensions(const Rectangle &r)
|
||||||
{
|
{
|
||||||
return Dimensions{.width = r.width, .height = r.height};
|
return Dimensions{.width = r.width, .height = r.height};
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,16 @@ MenuItemData gen2MenuEntries[] = {
|
||||||
{
|
{
|
||||||
.title = "PCNY Pokémon",
|
.title = "PCNY Pokémon",
|
||||||
.onConfirmAction = goToGen2PCNYDistributionPokemonMenu,
|
.onConfirmAction = goToGen2PCNYDistributionPokemonMenu,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.title = "Unlock Pikachu Bed",
|
||||||
|
.onConfirmAction = gen2SetEventFlag,
|
||||||
|
.itemParam = &GEN2_EVENTFLAG_DECORATION_PIKACHU_BED
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.title = "Unlock Tentacool Doll",
|
||||||
|
.onConfirmAction = gen2SetEventFlag,
|
||||||
|
.itemParam = &GEN2_EVENTFLAG_DECORATION_TENTACOOL_DOLL
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -45,6 +55,16 @@ MenuItemData gen2CrystalMenuEntries[] = {
|
||||||
{
|
{
|
||||||
.title = "Unlock GS Ball",
|
.title = "Unlock GS Ball",
|
||||||
.onConfirmAction = gen2ReceiveGSBall
|
.onConfirmAction = gen2ReceiveGSBall
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.title = "Unlock Pikachu Bed",
|
||||||
|
.onConfirmAction = gen2SetEventFlag,
|
||||||
|
.itemParam = &GEN2_EVENTFLAG_DECORATION_PIKACHU_BED
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.title = "Unlock Tentacool Doll",
|
||||||
|
.onConfirmAction = gen2SetEventFlag,
|
||||||
|
.itemParam = &GEN2_EVENTFLAG_DECORATION_TENTACOOL_DOLL
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,10 @@
|
||||||
const Move MOVE_SURF = Move::SURF;
|
const Move MOVE_SURF = Move::SURF;
|
||||||
const Move MOVE_FLY = Move::FLY;
|
const Move MOVE_FLY = Move::FLY;
|
||||||
|
|
||||||
|
// based on https://github.com/kwsch/PKHeX/blob/master/PKHeX.Core/Resources/text/script/gen2/flags_c_en.txt
|
||||||
|
const uint16_t GEN2_EVENTFLAG_DECORATION_PIKACHU_BED = 679;
|
||||||
|
const uint16_t GEN2_EVENTFLAG_DECORATION_TENTACOOL_DOLL = 715;
|
||||||
|
|
||||||
typedef struct Gen1TeachPikachuParams
|
typedef struct Gen1TeachPikachuParams
|
||||||
{
|
{
|
||||||
Move moveType;
|
Move moveType;
|
||||||
|
|
@ -71,9 +75,17 @@ static uint8_t gen1FindPikachuInParty(Gen1Party& party)
|
||||||
return foundIndex;
|
return foundIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void printMessage(void* context, const void*)
|
static const char* convertGen2EventFlagToString(uint16_t eventFlagIndex)
|
||||||
{
|
{
|
||||||
debugf((const char*)context);
|
switch(eventFlagIndex)
|
||||||
|
{
|
||||||
|
case GEN2_EVENTFLAG_DECORATION_PIKACHU_BED:
|
||||||
|
return "Pikachu Bed";
|
||||||
|
case GEN2_EVENTFLAG_DECORATION_TENTACOOL_DOLL:
|
||||||
|
return "Tentacool doll";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void activateFrameLog(void* context, const void*)
|
void activateFrameLog(void* context, const void*)
|
||||||
|
|
@ -84,6 +96,12 @@ void activateFrameLog(void* context, const void*)
|
||||||
gfx.triggerDebugFrame();
|
gfx.triggerDebugFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void advanceDialog(void* context, const void*)
|
||||||
|
{
|
||||||
|
MenuScene* scene = static_cast<MenuScene*>(context);
|
||||||
|
scene->advanceDialog();
|
||||||
|
}
|
||||||
|
|
||||||
void goToTestScene(void* context, const void* param)
|
void goToTestScene(void* context, const void* param)
|
||||||
{
|
{
|
||||||
MenuScene* scene = static_cast<MenuScene*>(context);
|
MenuScene* scene = static_cast<MenuScene*>(context);
|
||||||
|
|
@ -330,5 +348,37 @@ void gen2ReceiveGSBall(void* context, const void* param)
|
||||||
|
|
||||||
setDialogDataText(*messageData, "GS Ball event unlocked! Please go to the Golden Rod Pokémon Center and try to leave!", trainerName);
|
setDialogDataText(*messageData, "GS Ball event unlocked! Please go to the Golden Rod Pokémon Center and try to leave!", trainerName);
|
||||||
|
|
||||||
|
scene->showDialog(messageData);
|
||||||
|
}
|
||||||
|
|
||||||
|
void gen2SetEventFlag(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, static_cast<Gen2GameType>(scene->getDependencies().specificGenVersion));
|
||||||
|
DialogData* messageData = new DialogData{
|
||||||
|
.shouldDeleteWhenDone = true
|
||||||
|
};
|
||||||
|
const uint16_t eventFlagIndex = *static_cast<const uint16_t*>(param);
|
||||||
|
|
||||||
|
tpakManager.setRAMEnabled(true);
|
||||||
|
|
||||||
|
const char* trainerName = gameReader.getTrainerName();
|
||||||
|
if(gameReader.getEventFlag(eventFlagIndex))
|
||||||
|
{
|
||||||
|
setDialogDataText(*messageData, "%s already has %s!", trainerName, convertGen2EventFlagToString(eventFlagIndex));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gameReader.setEventFlag(eventFlagIndex, true);
|
||||||
|
gameReader.finishSave();
|
||||||
|
tpakManager.finishWrites();
|
||||||
|
|
||||||
|
setDialogDataText(*messageData, "%s has unlocked %s!", trainerName, convertGen2EventFlagToString(eventFlagIndex));
|
||||||
|
}
|
||||||
|
|
||||||
|
tpakManager.setRAMEnabled(false);
|
||||||
scene->showDialog(messageData);
|
scene->showDialog(messageData);
|
||||||
}
|
}
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
#include "core/DragonUtils.h"
|
#include "core/DragonUtils.h"
|
||||||
#include "widget/IWidget.h"
|
#include "widget/IWidget.h"
|
||||||
|
|
||||||
static uint16_t minimumTimeBetweenInputEventsInMs = 150;
|
const uint16_t AbstractUIScene::MINIMUM_TIME_BETWEEN_INPUT_EVENTS = 150;
|
||||||
|
|
||||||
AbstractUIScene::AbstractUIScene(SceneDependencies& deps)
|
AbstractUIScene::AbstractUIScene(SceneDependencies& deps)
|
||||||
: deps_(deps)
|
: deps_(deps)
|
||||||
|
|
@ -23,7 +23,7 @@ void AbstractUIScene::processUserInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint64_t now = get_ticks();
|
const uint64_t now = get_ticks();
|
||||||
if(TICKS_TO_MS(now - lastInputHandleTime_) < minimumTimeBetweenInputEventsInMs)
|
if(TICKS_TO_MS(now - lastInputHandleTime_) < MINIMUM_TIME_BETWEEN_INPUT_EVENTS)
|
||||||
{
|
{
|
||||||
// not enough time has passed since last handled input event. Ignore
|
// not enough time has passed since last handled input event. Ignore
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,8 @@ void InitTransferPakScene::onDialogDone()
|
||||||
{
|
{
|
||||||
menuContext = new MenuSceneContext({
|
menuContext = new MenuSceneContext({
|
||||||
.menuEntries = gen1MenuEntries,
|
.menuEntries = gen1MenuEntries,
|
||||||
.numMenuEntries = static_cast<uint32_t>(gen1MenuEntriesSize / sizeof(gen1MenuEntries[0]))
|
.numMenuEntries = static_cast<uint32_t>(gen1MenuEntriesSize / sizeof(gen1MenuEntries[0])),
|
||||||
|
.bButtonMeansUserWantsToSwitchCartridge = true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else if(gen2Type != Gen2GameType::INVALID)
|
else if(gen2Type != Gen2GameType::INVALID)
|
||||||
|
|
@ -86,14 +87,16 @@ void InitTransferPakScene::onDialogDone()
|
||||||
{
|
{
|
||||||
menuContext = new MenuSceneContext({
|
menuContext = new MenuSceneContext({
|
||||||
.menuEntries = gen2CrystalMenuEntries,
|
.menuEntries = gen2CrystalMenuEntries,
|
||||||
.numMenuEntries = static_cast<uint32_t>(gen2CrystalMenuEntriesSize / sizeof(gen2CrystalMenuEntries[0]))
|
.numMenuEntries = static_cast<uint32_t>(gen2CrystalMenuEntriesSize / sizeof(gen2CrystalMenuEntries[0])),
|
||||||
|
.bButtonMeansUserWantsToSwitchCartridge = true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
menuContext = new MenuSceneContext({
|
menuContext = new MenuSceneContext({
|
||||||
.menuEntries = gen2MenuEntries,
|
.menuEntries = gen2MenuEntries,
|
||||||
.numMenuEntries = static_cast<uint32_t>(gen2MenuEntriesSize / sizeof(gen2MenuEntries[0]))
|
.numMenuEntries = static_cast<uint32_t>(gen2MenuEntriesSize / sizeof(gen2MenuEntries[0])),
|
||||||
|
.bButtonMeansUserWantsToSwitchCartridge = true
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#include "scenes/MenuScene.h"
|
#include "scenes/MenuScene.h"
|
||||||
#include "core/FontManager.h"
|
#include "core/FontManager.h"
|
||||||
#include "scenes/SceneManager.h"
|
#include "scenes/SceneManager.h"
|
||||||
|
#include "transferpak/TransferPakManager.h"
|
||||||
|
#include "menu/MenuFunctions.h"
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
|
|
@ -78,7 +80,7 @@ void MenuScene::render(RDPQGraphics& gfx, const Rectangle& sceneBounds)
|
||||||
.fontId = arialId_,
|
.fontId = arialId_,
|
||||||
.fontStyleId = fontStyleWhiteId_
|
.fontStyleId = fontStyleWhiteId_
|
||||||
};
|
};
|
||||||
gfx.drawText(Rectangle{40, 10, 280, 16}, "PokeMe64 by risingPhil. Very early version...", renderSettings);
|
gfx.drawText(Rectangle{40, 10, 280, 16}, "PokeMe64 by risingPhil. Version 0.1", renderSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MenuScene::handleUserInput(joypad_port_t port, const joypad_inputs_t& inputs)
|
bool MenuScene::handleUserInput(joypad_port_t port, const joypad_inputs_t& inputs)
|
||||||
|
|
@ -98,7 +100,22 @@ bool MenuScene::handleUserInput(joypad_port_t port, const joypad_inputs_t& input
|
||||||
{
|
{
|
||||||
// b button release occurred. Switch back to previous scene.
|
// b button release occurred. Switch back to previous scene.
|
||||||
bButtonPressed_ = false;
|
bButtonPressed_ = false;
|
||||||
deps_.sceneManager.goBackToPreviousScene();
|
|
||||||
|
if(context_->bButtonMeansUserWantsToSwitchCartridge)
|
||||||
|
{
|
||||||
|
DialogData* diag = new DialogData{
|
||||||
|
.shouldDeleteWhenDone = true
|
||||||
|
};
|
||||||
|
|
||||||
|
setDialogDataText(*diag, "Please turn the console off to switch gameboy cartridges!");
|
||||||
|
|
||||||
|
showDialog(diag);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// now do the actual switch back to the previous scene
|
||||||
|
deps_.sceneManager.goBackToPreviousScene();
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -199,10 +216,9 @@ void MenuScene::setupDialog(DialogWidgetStyle& style)
|
||||||
|
|
||||||
void MenuScene::showDialog(DialogData* diagData)
|
void MenuScene::showDialog(DialogData* diagData)
|
||||||
{
|
{
|
||||||
|
SceneWithDialogWidget::showDialog(diagData);
|
||||||
menuList_.setVisible(false);
|
menuList_.setVisible(false);
|
||||||
cursorWidget_.setVisible(false);
|
cursorWidget_.setVisible(false);
|
||||||
dialogWidget_.setVisible(true);
|
|
||||||
dialogWidget_.setData(diagData);
|
|
||||||
setFocusChain(&dialogFocusChainSegment_);
|
setFocusChain(&dialogFocusChainSegment_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,16 @@ SceneManager::~SceneManager()
|
||||||
unloadScene(scene_);
|
unloadScene(scene_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneManager::switchScene(SceneType type, void (*deleteContextFunc)(void*), void* sceneContext)
|
void SceneManager::switchScene(SceneType type, void (*deleteContextFunc)(void*), void* sceneContext, bool deleteHistory)
|
||||||
{
|
{
|
||||||
newSceneType_ = type;
|
newSceneType_ = type;
|
||||||
newSceneContext_ = sceneContext;
|
newSceneContext_ = sceneContext;
|
||||||
|
|
||||||
|
if(deleteHistory)
|
||||||
|
{
|
||||||
|
clearHistory();
|
||||||
|
}
|
||||||
|
|
||||||
sceneHistory_.push_back(SceneHistorySegment{
|
sceneHistory_.push_back(SceneHistorySegment{
|
||||||
.type = type,
|
.type = type,
|
||||||
.context = sceneContext,
|
.context = sceneContext,
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,12 @@ void SceneWithDialogWidget::advanceDialog()
|
||||||
dialogWidget_.advanceDialog();
|
dialogWidget_.advanceDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SceneWithDialogWidget::showDialog(DialogData* diagData)
|
||||||
|
{
|
||||||
|
dialogWidget_.setVisible(true);
|
||||||
|
dialogWidget_.setData(diagData);
|
||||||
|
}
|
||||||
|
|
||||||
void SceneWithDialogWidget::setupFonts()
|
void SceneWithDialogWidget::setupFonts()
|
||||||
{
|
{
|
||||||
arialId_ = deps_.fontManager.getFont("rom://Arial.font64");
|
arialId_ = deps_.fontManager.getFont("rom://Arial.font64");
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ static const uint16_t sramBankStartGBAddress = 0xA000;
|
||||||
|
|
||||||
TransferPakManager::TransferPakManager()
|
TransferPakManager::TransferPakManager()
|
||||||
: port_(JOYPAD_PORT_1)
|
: port_(JOYPAD_PORT_1)
|
||||||
, wasPoweredAtLeastOnce_(false)
|
, isPoweredOn_(false)
|
||||||
, currentSRAMBank_(0)
|
, currentSRAMBank_(0)
|
||||||
, readBufferBankOffset_(0xFFFF)
|
, readBufferBankOffset_(0xFFFF)
|
||||||
, writeBufferSRAMBankOffset_(0xFFFF)
|
, writeBufferSRAMBankOffset_(0xFFFF)
|
||||||
|
|
@ -30,8 +30,11 @@ joypad_port_t TransferPakManager::getPort() const
|
||||||
|
|
||||||
void TransferPakManager::setPort(joypad_port_t port)
|
void TransferPakManager::setPort(joypad_port_t port)
|
||||||
{
|
{
|
||||||
|
if(isPoweredOn_)
|
||||||
|
{
|
||||||
|
setPower(false);
|
||||||
|
}
|
||||||
port_ = port;
|
port_ = port;
|
||||||
wasPoweredAtLeastOnce_ = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TransferPakManager::hasTransferPak()
|
bool TransferPakManager::hasTransferPak()
|
||||||
|
|
@ -47,31 +50,48 @@ bool TransferPakManager::hasTransferPak()
|
||||||
return (type == JOYPAD_ACCESSORY_TYPE_TRANSFER_PAK);
|
return (type == JOYPAD_ACCESSORY_TYPE_TRANSFER_PAK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TransferPakManager::isPoweredOn() const
|
||||||
|
{
|
||||||
|
return isPoweredOn_;
|
||||||
|
}
|
||||||
|
|
||||||
bool TransferPakManager::setPower(bool on)
|
bool TransferPakManager::setPower(bool on)
|
||||||
{
|
{
|
||||||
|
uint8_t status;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if(!wasPoweredAtLeastOnce_)
|
if(on)
|
||||||
{
|
{
|
||||||
if(!on)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
ret = tpak_init(static_cast<int>(port_));
|
ret = tpak_init(static_cast<int>(port_));
|
||||||
if(ret)
|
if(ret)
|
||||||
{
|
{
|
||||||
debugf("[TransferPakManager]: %s: tpak_init got error %d\r\n", __FUNCTION__, ret);
|
debugf("[TransferPakManager]: %s: tpak_init got error %d\r\n", __FUNCTION__, ret);
|
||||||
}
|
}
|
||||||
wasPoweredAtLeastOnce_ = true;
|
|
||||||
|
status = getStatus();
|
||||||
|
while(!(status | TPAK_STATUS_READY))
|
||||||
|
{
|
||||||
|
debugf("[TransferPakManager]: %s: ERROR: transfer pak not ready yet. Current status is %hu\r\n", __FUNCTION__, status);
|
||||||
|
status = getStatus();
|
||||||
|
}
|
||||||
|
isPoweredOn_ = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ret = tpak_set_power(port_, on);
|
ret = tpak_set_access(port_, false);
|
||||||
if(ret)
|
if(ret)
|
||||||
{
|
{
|
||||||
debugf("[TransferPakManager]: %s: tpak_set_power got error %d\r\n", __FUNCTION__, ret);
|
debugf("[TransferPakManager]: %s: tpak_set_access got error %d\r\n", __FUNCTION__, ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ret = tpak_set_power(port_, false);
|
||||||
|
if(ret)
|
||||||
|
{
|
||||||
|
debugf("[TransferPakManager]: %s: tpak_set_access got error %d\r\n", __FUNCTION__, ret);
|
||||||
|
}
|
||||||
|
isPoweredOn_ = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (!ret);
|
return (!ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
#include "widget/DialogWidget.h"
|
#include "widget/DialogWidget.h"
|
||||||
#include "widget/ListItemFiller.h"
|
|
||||||
#include "core/RDPQGraphics.h"
|
#include "core/RDPQGraphics.h"
|
||||||
|
|
||||||
#include <cstdarg>
|
#include <cstdarg>
|
||||||
|
|
@ -33,6 +32,7 @@ static void releaseEntry(DialogData* data, bool releaseAllEntries)
|
||||||
|
|
||||||
DialogWidget::DialogWidget(AnimationManager& animationManager)
|
DialogWidget::DialogWidget(AnimationManager& animationManager)
|
||||||
: dialogOptionList_(animationManager)
|
: dialogOptionList_(animationManager)
|
||||||
|
, dialogOptionListFiller_(dialogOptionList_)
|
||||||
, animationManager_(animationManager)
|
, animationManager_(animationManager)
|
||||||
, bounds_({0})
|
, bounds_({0})
|
||||||
, style_({0})
|
, style_({0})
|
||||||
|
|
@ -76,6 +76,8 @@ void DialogWidget::setStyle(const DialogWidgetStyle& style)
|
||||||
void DialogWidget::setData(DialogData* data)
|
void DialogWidget::setData(DialogData* data)
|
||||||
{
|
{
|
||||||
dialogOptionList_.clearWidgets();
|
dialogOptionList_.clearWidgets();
|
||||||
|
// the filler is the owner of the widgets. They need to be properly destroyed
|
||||||
|
dialogOptionListFiller_.deleteWidgets();
|
||||||
dialogOptionList_.setVisible(false);
|
dialogOptionList_.setVisible(false);
|
||||||
dialogOptionList_.setFocused(false);
|
dialogOptionList_.setFocused(false);
|
||||||
releaseEntry(data_, true);
|
releaseEntry(data_, true);
|
||||||
|
|
@ -84,9 +86,7 @@ void DialogWidget::setData(DialogData* data)
|
||||||
|
|
||||||
if(data_ && data->options.items)
|
if(data_ && data->options.items)
|
||||||
{
|
{
|
||||||
ListItemFiller<VerticalList, MenuItemData, MenuItemWidget, MenuItemStyle> filler(dialogOptionList_);
|
dialogOptionListFiller_.addItems(data->options.items, data->options.number, style_.dialogOptions.style);
|
||||||
|
|
||||||
filler.addItems(data->options.items, data->options.number, style_.dialogOptions.style);
|
|
||||||
dialogOptionList_.setVisible(true);
|
dialogOptionList_.setVisible(true);
|
||||||
dialogOptionList_.setFocused(focused_);
|
dialogOptionList_.setFocused(focused_);
|
||||||
}
|
}
|
||||||
|
|
@ -171,13 +171,13 @@ void DialogWidget::advanceDialog()
|
||||||
releaseEntry(oldEntry, false);
|
releaseEntry(oldEntry, false);
|
||||||
|
|
||||||
dialogOptionList_.clearWidgets();
|
dialogOptionList_.clearWidgets();
|
||||||
|
// the filler is the owner of the widgets. They need to be properly destroyed
|
||||||
|
dialogOptionListFiller_.deleteWidgets();
|
||||||
dialogOptionList_.setFocused(false);
|
dialogOptionList_.setFocused(false);
|
||||||
dialogOptionList_.setVisible(false);
|
dialogOptionList_.setVisible(false);
|
||||||
if(data_->options.items)
|
if(data_->options.items)
|
||||||
{
|
{
|
||||||
ListItemFiller<VerticalList, MenuItemData, MenuItemWidget, MenuItemStyle> filler(dialogOptionList_);
|
dialogOptionListFiller_.addItems(data_->options.items, data_->options.number, style_.dialogOptions.style);
|
||||||
|
|
||||||
filler.addItems(data_->options.items, data_->options.number, style_.dialogOptions.style);
|
|
||||||
dialogOptionList_.setVisible(true);
|
dialogOptionList_.setVisible(true);
|
||||||
dialogOptionList_.setFocused(focused_);
|
dialogOptionList_.setFocused(focused_);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
MenuItemWidget::MenuItemWidget()
|
MenuItemWidget::MenuItemWidget()
|
||||||
: data_()
|
: data_()
|
||||||
, style_()
|
, style_({0})
|
||||||
, focused_(false)
|
, focused_(false)
|
||||||
, visible_(true)
|
, visible_(true)
|
||||||
, aButtonPressed_(false)
|
, aButtonPressed_(false)
|
||||||
|
|
@ -70,9 +70,8 @@ bool MenuItemWidget::handleUserInput(const joypad_inputs_t& userInput)
|
||||||
}
|
}
|
||||||
else if(aButtonPressed_)
|
else if(aButtonPressed_)
|
||||||
{
|
{
|
||||||
execute();
|
|
||||||
aButtonPressed_ = false;
|
aButtonPressed_ = false;
|
||||||
return true;
|
return execute();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -104,7 +103,12 @@ void MenuItemWidget::render(RDPQGraphics& gfx, const Rectangle& parentBounds)
|
||||||
gfx.drawText(myBounds, data_.title, (focused_) ? style_.titleFocused : style_.titleNotFocused);
|
gfx.drawText(myBounds, data_.title, (focused_) ? style_.titleFocused : style_.titleNotFocused);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuItemWidget::execute()
|
bool MenuItemWidget::execute()
|
||||||
{
|
{
|
||||||
data_.onConfirmAction(data_.context, data_.itemParam);
|
if(data_.onConfirmAction)
|
||||||
|
{
|
||||||
|
data_.onConfirmAction(data_.context, data_.itemParam);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
310
src/widget/ScrollWidget.cpp
Normal file
310
src/widget/ScrollWidget.cpp
Normal file
|
|
@ -0,0 +1,310 @@
|
||||||
|
#include "widget/ScrollWidget.h"
|
||||||
|
#include "core/common.h"
|
||||||
|
#include "scenes/AbstractUIScene.h"
|
||||||
|
#include "animations/AnimationManager.h"
|
||||||
|
#include "core/RDPQGraphics.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
static uint8_t ANALOG_STICK_MIN_THRESHOLD = 30;
|
||||||
|
static uint8_t ANALOG_STICK_MAX_THRESHOLD = 60;
|
||||||
|
|
||||||
|
static FloatVector determineScrollDirection(const joypad_inputs_t& userInput)
|
||||||
|
{
|
||||||
|
// this value should end up giving a semi-normalized vector when used for both x and y coords
|
||||||
|
const float DPAD_2_DIRECTIONS_PRESSED_COORD = 0.7071f;
|
||||||
|
const float STICK_RANGE_UNIT = 1.f / (ANALOG_STICK_MAX_THRESHOLD - ANALOG_STICK_MIN_THRESHOLD);
|
||||||
|
|
||||||
|
if(userInput.btn.d_left)
|
||||||
|
{
|
||||||
|
if(userInput.btn.d_up)
|
||||||
|
{
|
||||||
|
return FloatPoint{ -DPAD_2_DIRECTIONS_PRESSED_COORD, -DPAD_2_DIRECTIONS_PRESSED_COORD};
|
||||||
|
}
|
||||||
|
else if(userInput.btn.d_down)
|
||||||
|
{
|
||||||
|
return FloatPoint{ -DPAD_2_DIRECTIONS_PRESSED_COORD, DPAD_2_DIRECTIONS_PRESSED_COORD};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FloatPoint{-1.f, 0.f};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(userInput.btn.d_right)
|
||||||
|
{
|
||||||
|
if(userInput.btn.d_up)
|
||||||
|
{
|
||||||
|
return FloatPoint{ DPAD_2_DIRECTIONS_PRESSED_COORD, -DPAD_2_DIRECTIONS_PRESSED_COORD};
|
||||||
|
}
|
||||||
|
else if(userInput.btn.d_down)
|
||||||
|
{
|
||||||
|
return FloatPoint{ DPAD_2_DIRECTIONS_PRESSED_COORD, DPAD_2_DIRECTIONS_PRESSED_COORD};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return FloatPoint{1.f, 0.f};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(userInput.btn.d_up)
|
||||||
|
{
|
||||||
|
return FloatPoint{0.f, -1.f};
|
||||||
|
}
|
||||||
|
else if(userInput.btn.d_down)
|
||||||
|
{
|
||||||
|
return FloatPoint{0.f, 1.f};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// analog stick
|
||||||
|
int8_t absXVal = static_cast<int8_t>(abs(userInput.stick_x));
|
||||||
|
int8_t absYVal = static_cast<int8_t>(abs(userInput.stick_y));
|
||||||
|
if(absXVal < ANALOG_STICK_MIN_THRESHOLD && absYVal < ANALOG_STICK_MIN_THRESHOLD)
|
||||||
|
{
|
||||||
|
return FloatPoint{0.f, 0.f};
|
||||||
|
}
|
||||||
|
|
||||||
|
// clamp the x and y values to ANALOG_STICK_MAX_THRESHOLD
|
||||||
|
absXVal = (absXVal > ANALOG_STICK_MAX_THRESHOLD) ? ANALOG_STICK_MAX_THRESHOLD : absXVal;
|
||||||
|
absYVal = (absYVal > ANALOG_STICK_MAX_THRESHOLD) ? ANALOG_STICK_MAX_THRESHOLD : absYVal;
|
||||||
|
|
||||||
|
// remove the dead zone and map the resulting range to 0.f-1.f
|
||||||
|
float resultX = static_cast<float>(absXVal - ANALOG_STICK_MIN_THRESHOLD) * STICK_RANGE_UNIT;
|
||||||
|
float resultY = static_cast<float>(absYVal - ANALOG_STICK_MIN_THRESHOLD) * STICK_RANGE_UNIT;
|
||||||
|
|
||||||
|
if(userInput.stick_x < 0)
|
||||||
|
{
|
||||||
|
resultX = -resultX;
|
||||||
|
}
|
||||||
|
if(userInput.stick_y > 0)
|
||||||
|
{
|
||||||
|
// up in the UI means negative y values
|
||||||
|
resultY = -resultY;
|
||||||
|
}
|
||||||
|
return FloatPoint{resultX, resultY};
|
||||||
|
}
|
||||||
|
|
||||||
|
return FloatPoint{0.f, 0.f};
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveScrollWidgetWindowAnimation::MoveScrollWidgetWindowAnimation(ScrollWidget* list)
|
||||||
|
: AbstractAnimation(1.f)
|
||||||
|
, list_(list)
|
||||||
|
, windowStartPoint_({0})
|
||||||
|
, windowEndPoint_({0})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MoveScrollWidgetWindowAnimation::~MoveScrollWidgetWindowAnimation()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
AnimationDistanceTimeFunctionType MoveScrollWidgetWindowAnimation::getDistanceTimeFunctionType() const
|
||||||
|
{
|
||||||
|
return AnimationDistanceTimeFunctionType::LINEAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t MoveScrollWidgetWindowAnimation::getDurationInMs() const
|
||||||
|
{
|
||||||
|
return AbstractUIScene::MINIMUM_TIME_BETWEEN_INPUT_EVENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveScrollWidgetWindowAnimation::start(const Point& startPoint, const Point& endPoint)
|
||||||
|
{
|
||||||
|
windowStartPoint_ = startPoint;
|
||||||
|
windowEndPoint_ = endPoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoveScrollWidgetWindowAnimation::apply(float pos)
|
||||||
|
{
|
||||||
|
const int px = static_cast<int>(ceil(windowStartPoint_.x + (pos * (windowEndPoint_.x - windowStartPoint_.x))));
|
||||||
|
const int py = static_cast<int>(ceil(windowStartPoint_.y + (pos * (windowEndPoint_.y - windowStartPoint_.y))));
|
||||||
|
list_->setWindowStart(Point{px, py});
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollWidget::ScrollWidget(AnimationManager& animManager)
|
||||||
|
: windowAnimation_(this)
|
||||||
|
, widgets_()
|
||||||
|
, style_({0})
|
||||||
|
, animManager_(animManager)
|
||||||
|
, bounds_({0})
|
||||||
|
, windowBounds_({0})
|
||||||
|
, visible_(true)
|
||||||
|
, focused_(false)
|
||||||
|
{
|
||||||
|
animManager_.add(&windowAnimation_);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollWidget::~ScrollWidget()
|
||||||
|
{
|
||||||
|
animManager_.remove(&windowAnimation_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrollWidget::isFocused() const
|
||||||
|
{
|
||||||
|
return focused_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollWidget::setFocused(bool isFocused)
|
||||||
|
{
|
||||||
|
focused_ = isFocused;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrollWidget::isVisible() const
|
||||||
|
{
|
||||||
|
return visible_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollWidget::setVisible(bool visible)
|
||||||
|
{
|
||||||
|
visible_ = visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle ScrollWidget::getBounds() const
|
||||||
|
{
|
||||||
|
return bounds_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollWidget::setBounds(const Rectangle& bounds)
|
||||||
|
{
|
||||||
|
bounds_ = bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dimensions ScrollWidget::getSize() const
|
||||||
|
{
|
||||||
|
return Dimensions{.width = bounds_.width, .height = bounds_.height};
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollWidget::setStyle(const ScrollWidgetStyle& style)
|
||||||
|
{
|
||||||
|
style_ = style;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollWidget::addWidget(IWidget* widget)
|
||||||
|
{
|
||||||
|
widgets_.push_back(widget);
|
||||||
|
growWindow(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollWidget::removeWidget(IWidget* widget)
|
||||||
|
{
|
||||||
|
auto it = std::find(widgets_.begin(), widgets_.end(), widget);
|
||||||
|
if(it != widgets_.end())
|
||||||
|
{
|
||||||
|
widgets_.erase(it);
|
||||||
|
}
|
||||||
|
recalculateWindowSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScrollWidget::handleUserInput(const joypad_inputs_t& userInput)
|
||||||
|
{
|
||||||
|
if(!focused_ || style_.scrollStep == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const FloatVector scrollDirection = determineScrollDirection(userInput);
|
||||||
|
if(scrollDirection.x == 0.f && scrollDirection.y == 0.f)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(!windowAnimation_.isFinished())
|
||||||
|
{
|
||||||
|
windowAnimation_.skipToEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Point windowStartPoint = {.x = windowBounds_.x, .y = windowBounds_.y};
|
||||||
|
Point windowEndPoint = {.x = windowBounds_.x, .y = windowBounds_.y};
|
||||||
|
|
||||||
|
windowEndPoint.x += static_cast<int>(ceil(scrollDirection.x * style_.scrollStep));
|
||||||
|
windowEndPoint.y += static_cast<int>(ceil(scrollDirection.y * style_.scrollStep));
|
||||||
|
windowAnimation_.start(windowStartPoint, windowEndPoint);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollWidget::render(RDPQGraphics& gfx, const Rectangle& parentBounds)
|
||||||
|
{
|
||||||
|
if(!visible_)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Rectangle myAbsoluteBounds = addOffset(bounds_, parentBounds);
|
||||||
|
|
||||||
|
// we "correct" the x/y coords here because the child widget
|
||||||
|
// will use addOffset() to apply its relative bounds (which may have higher xy coords than the width and height of the window) to the parentBounds
|
||||||
|
// by doing this correction, we compensate for the window start offset
|
||||||
|
const Rectangle absoluteWindowStartBounds = Rectangle{
|
||||||
|
.x = myAbsoluteBounds.x - windowBounds_.x,
|
||||||
|
.y = myAbsoluteBounds.y - windowBounds_.y,
|
||||||
|
.width = myAbsoluteBounds.width,
|
||||||
|
.height = myAbsoluteBounds.height
|
||||||
|
};
|
||||||
|
|
||||||
|
// the bounds of the widget instances are all relative to the top of the scroll widget.
|
||||||
|
// so is the windowStart_
|
||||||
|
// so we need to define a rectangle to define the overlap in order to decide whether a widget needs to be rendered at all.
|
||||||
|
const Rectangle relativeWindowRect = {
|
||||||
|
.x = windowBounds_.x,
|
||||||
|
.y = windowBounds_.y,
|
||||||
|
.width = bounds_.width,
|
||||||
|
.height = bounds_.height
|
||||||
|
};
|
||||||
|
|
||||||
|
const Rectangle prevClip = gfx.getClippingRectangle();
|
||||||
|
gfx.setClippingRectangle(myAbsoluteBounds);
|
||||||
|
|
||||||
|
for(IWidget* widget : widgets_)
|
||||||
|
{
|
||||||
|
if(doRectanglesOverlap(relativeWindowRect, widget->getBounds()))
|
||||||
|
{
|
||||||
|
widget->render(gfx, absoluteWindowStartBounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gfx.setClippingRectangle(prevClip);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollWidget::setWindowStart(const Point& windowStart)
|
||||||
|
{
|
||||||
|
windowBounds_.x = windowStart.x;
|
||||||
|
windowBounds_.y = windowStart.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ScrollWidget::getWindowProgressX() const
|
||||||
|
{
|
||||||
|
const float maxX = windowBounds_.width - bounds_.width;
|
||||||
|
return static_cast<float>(windowBounds_.x) / maxX;
|
||||||
|
}
|
||||||
|
|
||||||
|
float ScrollWidget::getWindowProgressY() const
|
||||||
|
{
|
||||||
|
const double maxY = windowBounds_.height - bounds_.height;
|
||||||
|
return static_cast<float>(windowBounds_.y) / maxY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollWidget::growWindow(IWidget* widget)
|
||||||
|
{
|
||||||
|
const Rectangle widgetBounds = widget->getBounds();
|
||||||
|
|
||||||
|
const int widgetMaxX = widgetBounds.x + widgetBounds.width;
|
||||||
|
const int widgetMaxY = widgetBounds.y + widgetBounds.height;
|
||||||
|
|
||||||
|
if(widgetMaxX > windowBounds_.width)
|
||||||
|
{
|
||||||
|
windowBounds_.width = widgetMaxX;
|
||||||
|
}
|
||||||
|
if(widgetMaxY > windowBounds_.height)
|
||||||
|
{
|
||||||
|
windowBounds_.height = widgetMaxY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ScrollWidget::recalculateWindowSize()
|
||||||
|
{
|
||||||
|
windowBounds_.width = 0;
|
||||||
|
windowBounds_.height = 0;
|
||||||
|
for(IWidget* widget : widgets_)
|
||||||
|
{
|
||||||
|
growWindow(widget);
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user