The open source repository for the action RPG game in development by Sig Productions titled 'Adventures in Lestoria'!
https://forums.lestoria.net
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
256 lines
11 KiB
256 lines
11 KiB
#pragma region License
|
|
/*
|
|
License (OLC-3)
|
|
~~~~~~~~~~~~~~~
|
|
|
|
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
|
|
|
|
Redistribution and use in source and binary forms, with or without modification,
|
|
are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions or derivations of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
2. Redistributions or derivative works in binary form must reproduce the above
|
|
copyright notice. This list of conditions and the following disclaimer must be
|
|
reproduced in the documentation and/or other materials provided with the distribution.
|
|
|
|
3. Neither the name of the copyright holder nor the names of its contributors may
|
|
be used to endorse or promote products derived from this software without specific
|
|
prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
|
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
|
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
SUCH DAMAGE.
|
|
|
|
Portions of this software are copyright © 2023 The FreeType
|
|
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
|
All rights reserved.
|
|
*/
|
|
#pragma endregion
|
|
#pragma once
|
|
#include "Item.h"
|
|
#include <stack>
|
|
#include "safemap.h"
|
|
#include "Theme.h"
|
|
#include "Attributable.h"
|
|
#include "olcUTIL_Geometry2D.h"
|
|
#include "olcPGEX_ViewPort.h"
|
|
#include "Key.h"
|
|
|
|
class AiL;
|
|
class MenuComponent;
|
|
class ScrollableWindowComponent;
|
|
|
|
struct Navigation;
|
|
|
|
class Menu;
|
|
|
|
struct MenuFuncData{
|
|
Menu&menu;
|
|
AiL*const game;
|
|
const std::weak_ptr<MenuComponent> component;
|
|
const std::weak_ptr<ScrollableWindowComponent> parentComponent={};
|
|
MenuFuncData(Menu&menu,AiL*const game,std::weak_ptr<MenuComponent> component,std::weak_ptr<ScrollableWindowComponent>parentComponent={});
|
|
};
|
|
|
|
using MenuFunc=std::function<bool(MenuFuncData)>;
|
|
//Add a component to a menu using this macro. Follow-up with END at the end of it.
|
|
#define ADD(key,componentType) _AddComponent<componentType>(key,std::make_shared<componentType>
|
|
#define END )
|
|
#define DEPTH ,
|
|
|
|
#define DEFAULT_DEPTH -999999
|
|
#define STARTING_DEPTH 999999
|
|
|
|
enum MenuType{
|
|
///////////////////////////////////////////////////////////
|
|
/*DO NOT REMOVE!!*/ENUM_START,///////////////////////////////
|
|
///////////////////////////////////////////////////////////
|
|
INVENTORY_CONSUMABLES,
|
|
CLASS_INFO,
|
|
CLASS_SELECTION,
|
|
MAIN_MENU,
|
|
OVERWORLD_LEVEL_SELECT,
|
|
ITEM_LOADOUT,
|
|
LEVEL_COMPLETE,
|
|
OVERWORLD_MENU,
|
|
CHARACTER_MENU,
|
|
INVENTORY,
|
|
MERCHANT,
|
|
BUY_ITEM,
|
|
SELL_ITEM,
|
|
BLACKSMITH,
|
|
CRAFT_ITEM,
|
|
CRAFT_CONSUMABLE,
|
|
CONSUMABLE_CRAFT_ITEM,
|
|
SAVE_FILE_NAME,
|
|
LOAD_GAME,
|
|
USER_ID,
|
|
///////////////////////////////////////////////////////////
|
|
/*DO NOT REMOVE!!*/ENUM_END////////////////////////////////
|
|
///////////////////////////////////////////////////////////
|
|
};
|
|
|
|
using MenuDataFunc=std::function<void(MenuType)>;
|
|
|
|
using InputGroupActionDisplayName=std::string;
|
|
using ButtonName=std::string;
|
|
using MenuInputGroups=std::map<InputGroup,std::pair<InputGroupActionDisplayName,std::variant<ButtonName,MenuDataFunc>>>;
|
|
using ButtonNavigationGroups=std::map<ButtonName,Navigation>;
|
|
|
|
|
|
struct Navigation{
|
|
std::variant<std::string,MenuDataFunc>up;
|
|
std::variant<std::string,MenuDataFunc>down;
|
|
std::variant<std::string,MenuDataFunc>left;
|
|
std::variant<std::string,MenuDataFunc>right;
|
|
};
|
|
|
|
class Menu:public IAttributable{
|
|
static void InitializeConsumableInventoryWindow();
|
|
static void InitializeClassInfoWindow();
|
|
static void InitializeClassSelectionWindow();
|
|
static void InitializeMainMenuWindow();
|
|
static void InitializeOverworldMapLevelWindow();
|
|
static void InitializeItemLoadoutWindow();
|
|
static void InitializeLevelCompleteWindow();
|
|
static void InitializeOverworldMenuWindow();
|
|
static void InitializeCharacterMenuWindow();
|
|
static void InitializeInventoryWindow();
|
|
static void InitializeMerchantWindow();
|
|
static void InitializeBuyItemWindow();
|
|
static void InitializeSellItemWindow();
|
|
static void InitializeBlacksmithCraftingWindow();
|
|
static void InitializeCraftItemWindow();
|
|
static void InitializeConsumableCraftingWindow();
|
|
static void InitializeConsumableCraftItemWindow();
|
|
static void InitializeSaveFileWindow();
|
|
static void InitializeLoadGameWindow();
|
|
static void InitializeUserIDWindow();
|
|
|
|
friend class AiL;
|
|
friend class ItemInfo;
|
|
friend class EntityStats;
|
|
|
|
float buttonHoldTime=0;
|
|
std::weak_ptr<MenuComponent>selection;
|
|
vi2d lastActiveMousePos={};
|
|
int componentCount=0;
|
|
|
|
std::unique_ptr<MenuComponent>draggingComponent;
|
|
ViewPort window;
|
|
static safemap<ITCategory,std::vector<std::weak_ptr<MenuComponent>>>inventoryListeners; //All menu components that care about inventory updates subscribe to this list indirectly (See Menu::AddInventoryListener()).
|
|
static safemap<ITCategory,std::vector<std::weak_ptr<MenuComponent>>>merchantInventoryListeners; //All menu components that care about merchant inventory updates subscribe to this list indirectly (See Menu::AddMerchantInventoryListener()).
|
|
static std::vector<std::weak_ptr<MenuComponent>>equipStatListeners; //All menu components that care about stat/equip updates subscribe to this list indirectly (See Menu::AddStatListener()).
|
|
static std::vector<std::weak_ptr<MenuComponent>>chapterListeners; //All menu components that care about story chapter updates subscribe to this list indirectly (See Menu::AddChapterListener()).
|
|
public:
|
|
//The constructor is private. Use CreateMenu() instead!
|
|
Menu()=default;
|
|
//DO NOT USE DIRECTLY! You should be utilizing the ADD macro for adding components.
|
|
template<class T>
|
|
std::shared_ptr<T>_AddComponent(std::string componentKey,std::shared_ptr<T>component,int depth=DEFAULT_DEPTH){
|
|
component->parentMenu=type;
|
|
if(depth==DEFAULT_DEPTH){
|
|
component->depth=STARTING_DEPTH-componentCount;
|
|
}else{
|
|
component->depth=depth;
|
|
}
|
|
|
|
if(components.count(componentKey)){
|
|
ERR("WARNING! Key "<<componentKey<<" for this sub-menu already exists! Key names must be unique!")
|
|
}
|
|
component->name=componentKey;
|
|
components.Unlock(); //It's possible we can add a component later on, so we will make sure we remove the lock first.
|
|
components[componentKey]=component;
|
|
components.SetInitialized();
|
|
lastRegisteredComponent=componentKey;
|
|
|
|
RecalculateComponentCount();
|
|
|
|
return component;
|
|
}
|
|
void Update(AiL*game);
|
|
void Draw(AiL*game);
|
|
static void InitializeMenuListenerCategory(const std::string&category);
|
|
static void InitializeMenus();
|
|
static void LockInListeners();
|
|
static void OpenMenu(MenuType menu,bool cover=true);
|
|
static void CloseMenu();
|
|
static void CloseAllMenus();
|
|
static void CleanupAllMenus();
|
|
static std::vector<Menu*>stack;
|
|
static std::string themeSelection;
|
|
static safeunorderedmap<std::string,Theme>themes;
|
|
static const vf2d CENTERED;
|
|
static bool IsMenuOpen();
|
|
const MenuType GetType()const;
|
|
safemap<std::string,std::shared_ptr<MenuComponent>>components; //A friendly way to interrogate any component we are interested in.
|
|
static std::map<MenuType,Menu*>menus;
|
|
vf2d pos; //Specify the upper-left corner of the window. Using CENTERED will always put this where the upper-left corner would center the window.
|
|
vf2d size; //Size in tiles (24x24), every menu will be tile-based
|
|
|
|
static Theme&GetCurrentTheme();
|
|
static bool UsingMouseNavigation();
|
|
void SetMouseNavigation(bool mouseNavigation);
|
|
static void InventorySlotsUpdated(ITCategory cat); //Called whenever the player's inventory gets modified.
|
|
static void MerchantInventorySlotsUpdated(ITCategory cat); //Called whenever a traveling merchant's inventory item gets updated.
|
|
static void AddInventoryListener(std::weak_ptr<MenuComponent>component,ITCategory category); //Adds a component to be in a given listener category.
|
|
static void AddMerchantInventoryListener(std::weak_ptr<MenuComponent>component,ITCategory category); //Adds a component to be in a given listener category.
|
|
static void AddEquipStatListener(std::weak_ptr<MenuComponent>component); //Adds a component to be in an equip stat listener. Will receive updates whenever stats are updated via equips.
|
|
static void AddChapterListener(std::weak_ptr<MenuComponent>component); //Adds a component to be in a chapter listener. Will receive updates anytime the chapter in-game changes.
|
|
vf2d center();
|
|
//Returns the last menu type created and last registered component, in case a component is detected as memory leaking, provides this information to each component for safety.
|
|
static std::pair<MenuType,std::string>GetMemoryLeakReportInfo();
|
|
virtual void Cleanup();
|
|
|
|
static void DrawThemedWindow(vf2d menuPos,vf2d size,Pixel renderColor=WHITE);
|
|
|
|
//X (0-2), Y (0-2) for specific 9-patch tile (tiled version).
|
|
static Renderable&GetPatchPart(int x,int y);
|
|
void RecalculateComponentCount();
|
|
|
|
void SetDefaultButton(std::weak_ptr<MenuComponent>button);
|
|
void SetSelection(std::weak_ptr<MenuComponent>button);
|
|
const std::weak_ptr<MenuComponent>GetSelection()const;
|
|
private:
|
|
Menu(vf2d pos,vf2d size);
|
|
std::weak_ptr<MenuComponent>defaultButton;
|
|
static MenuType lastMenuTypeCreated;
|
|
static std::string lastRegisteredComponent;
|
|
void HoverMenuSelect(AiL*game);
|
|
void MenuSelect(AiL*game);
|
|
void CheckClickAndPerformMenuSelect(AiL*game);
|
|
//Mandatory before any menu operations! This creates and sets up the menu in memory.
|
|
static Menu*CreateMenu(MenuType type,vf2d pos,vf2d size);
|
|
|
|
void SetupKeyboardNavigation(MenuInputGroups inputGroups,ButtonNavigationGroups navigationGroups);
|
|
void KeyboardButtonNavigation(AiL*game,vf2d menuPos);
|
|
static void DrawScaledWindowBackground(AiL*game,vf2d menuPos,vf2d size,Pixel renderColor);
|
|
static void DrawTiledWindowBackground(AiL*game,vf2d menuPos,vf2d size,Pixel renderColor);
|
|
static void DrawScaledWindowBorder(AiL*game,vf2d menuPos,vf2d size,Pixel renderColor);
|
|
static void DrawTiledWindowBorder(AiL*game,vf2d menuPos,vf2d size,Pixel renderColor);
|
|
|
|
//This triggers if we use a keyboard/controller input to try and select some off-screen menu item. We should ideally follow the menu cursor.
|
|
bool HandleOutsideDisabledButtonSelection(std::weak_ptr<MenuComponent>disabledButton);
|
|
|
|
Pixel GetRenderColor();
|
|
MenuType type;
|
|
MenuInputGroups inputGroups;
|
|
ButtonNavigationGroups navigationGroups;
|
|
|
|
static bool MOUSE_NAVIGATION;
|
|
bool cover; //A black cover for when a menu pops up to fade out the stuff behind it.
|
|
};
|
|
|
|
template<typename T>
|
|
std::shared_ptr<T>Component(MenuType menu,std::string componentName){
|
|
return DYNAMIC_POINTER_CAST<T>(Menu::menus[menu]->components[componentName]);
|
|
} |