# pragma region License
/*
License ( OLC - 3 )
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
Copyright 2018 - 2023 OneLoneCoder . 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 "Menu.h"
# include "MenuComponent.h"
# include "MenuItemButton.h"
# include "Crawler.h"
# include "ScrollableWindowComponent.h"
using A = Attribute ;
struct InventoryWindowOptions {
int padding = 8 ;
vf2d size = { 24 , 24 } ;
} ;
//This class can easily be derived with different component types by overriding non-final virtual functions in this class.
//Functions currently include:
// virtual inline void AddButtonOnSlotUpdate(ITCategory cat)
// virtual inline void SetCompactDescriptions(bool compact)
//
// Please ensure these are overwritten, otherwise you will get errors complaining about dynamic casts unable to convert classes successfully.
class InventoryScrollableWindowComponent : public ScrollableWindowComponent {
protected :
ITCategory inventoryType ;
std : : function < bool ( MenuFuncData ) > inventoryButtonClickAction ;
std : : function < bool ( MenuFuncData ) > inventoryButtonHoverAction ;
std : : function < bool ( MenuFuncData ) > inventoryButtonMouseOutAction ;
CompactText compact = COMPACT ;
InventoryWindowOptions options ;
std : : string itemNameLabelName ;
std : : string itemDescriptionLabelName ;
bool inventoryButtonsActive = true ;
public :
inline InventoryScrollableWindowComponent ( geom2d : : rect < float > rect , ITCategory invType , std : : string itemNameLabelName , std : : string itemDescriptionLabelName , std : : function < bool ( MenuFuncData ) > inventoryButtonClickAction , InventoryWindowOptions options = { . padding = 8 , . size = { 24 , 24 } } , bool inventoryButtonsActive = true , ComponentAttr attributes = ComponentAttr : : BACKGROUND | ComponentAttr : : OUTLINE )
: InventoryScrollableWindowComponent ( rect , invType , itemNameLabelName , itemDescriptionLabelName , inventoryButtonClickAction , DO_NOTHING , DO_NOTHING , options , inventoryButtonsActive , attributes ) {
}
inline InventoryScrollableWindowComponent ( geom2d : : rect < float > rect , ITCategory invType , std : : string itemNameLabelName , std : : string itemDescriptionLabelName , std : : function < bool ( MenuFuncData ) > inventoryButtonClickAction , std : : function < bool ( MenuFuncData ) > inventoryButtonHoverAction , std : : function < bool ( MenuFuncData ) > inventoryButtonMouseOutAction , InventoryWindowOptions options = { . padding = 8 , . size = { 24 , 24 } } , bool inventoryButtonsActive = true , ComponentAttr attributes = ComponentAttr : : BACKGROUND | ComponentAttr : : OUTLINE )
: ScrollableWindowComponent ( rect , attributes ) , inventoryButtonHoverAction ( inventoryButtonHoverAction ) , inventoryButtonMouseOutAction ( inventoryButtonMouseOutAction ) , inventoryType ( invType ) , itemNameLabelName ( itemNameLabelName ) , itemDescriptionLabelName ( itemDescriptionLabelName ) ,
options ( options ) , inventoryButtonClickAction ( inventoryButtonClickAction ) , inventoryButtonsActive ( inventoryButtonsActive ) {
Menu : : AddInventoryListener ( this , invType ) ;
}
virtual inline void Update ( Crawler * game ) override {
ScrollableWindowComponent : : Update ( game ) ;
bool noneHovered = true ;
for ( MenuComponent * component : components ) {
if ( component - > hovered ) {
noneHovered = false ;
break ;
}
}
if ( noneHovered ) {
if ( itemNameLabelName . length ( ) > 0 ) Component < MenuLabel > ( parentMenu , itemNameLabelName ) - > SetLabel ( " " ) ;
if ( itemDescriptionLabelName . length ( ) > 0 ) Component < MenuLabel > ( parentMenu , itemDescriptionLabelName ) - > SetLabel ( " " ) ;
}
}
virtual inline void SetCompactDescriptions ( bool compact ) {
if ( compact ) this - > compact = COMPACT ;
else this - > compact = NON_COMPACT ;
for ( MenuComponent * component : components ) {
MenuItemButton * itemButton = dynamic_cast < MenuItemButton * > ( component ) ;
if ( itemButton ) {
itemButton - > SetCompactDescriptions ( compact ) ;
} else {
ERR ( " WARNING! Attempting to cast a button that isn't a MenuItemButton! " ) ;
}
}
}
protected :
inline void RemoveEmptySlots ( ) {
//Algorithm will iterate through all slots, finding blank slots. Each time a blank slot is found, all items will shift over by one, and then the last item will be removed. Repeat until all slots iterated through.
for ( int i = 0 ; i < components . size ( ) ; i + + ) {
MenuComponent * button = components [ i ] ;
button - > Update ( game ) ; //We have to call update to update the validation state.
//HACK ALERT!! This only gets called on inventories...And only on inventory items, which would have the valid flag set. We only care about components that are inventory slots.
if ( ! button - > valid ) {
for ( int j = i ; j < components . size ( ) - 1 ; j + + ) {
//Take the item in the next slot and move it to this slot.
Menu : : menus [ components [ j ] - > parentMenu ] - > components . at ( components [ j ] - > name ) = components [ size_t ( j + 1 ) ] ;
components [ j ] = components [ size_t ( j + 1 ) ] ;
}
MenuComponent * lastButton = Menu : : menus [ components [ components . size ( ) - 1 ] - > parentMenu ] - > components . at ( components [ components . size ( ) - 1 ] - > name ) ;
//Now we have to fix up the keyboard button list.
RemoveButton ( lastButton ) ;
i - - ; //Subtract one from the index so we don't accidently skip slots.
}
}
CalculateBounds ( ) ; //Recalculate the bounds as it's possible the width/height of the component has changed.
}
virtual inline void OnInventorySlotsUpdate ( ITCategory cat ) override final {
size_t invSize = Inventory : : get ( cat ) . size ( ) ;
//We only want to refresh the inventory slots if the component count no longer matches what's actually in our inventory.
if ( components . size ( ) < invSize ) { //We need more space to display our items.
AddButtonOnSlotUpdate ( cat ) ;
} else
if ( components . size ( ) > invSize ) { //There are empty spots, so let's clean up.
RemoveEmptySlots ( ) ;
}
}
virtual inline void Cleanup ( ) override final {
}
virtual inline void AddButtonOnSlotUpdate ( ITCategory cat ) {
size_t invSize = Inventory : : get ( cat ) . size ( ) ;
int invWidth = int ( rect . size . x / ( float ( options . size . x ) + options . padding ) ) ;
int x = int ( ( invSize - 1 ) % invWidth ) ;
int y = int ( ( invSize - 1 ) / invWidth ) ;
int itemIndex = y * invWidth + x ;
vf2d buttonSize = options . size ;
int totalSpacing = options . padding + buttonSize . x ;
ADD ( " item_ " + cat + " _ " + std : : to_string ( itemIndex ) , MenuItemButton ) ( { { float ( totalSpacing * x ) , float ( totalSpacing * y ) } , buttonSize } , Inventory : : get ( cat ) , itemIndex , inventoryButtonClickAction , inventoryButtonHoverAction , inventoryButtonMouseOutAction , parentMenu , itemNameLabelName , itemDescriptionLabelName , inventoryButtonsActive ? IconButtonAttr : : SELECTABLE : IconButtonAttr : : NOT_SELECTABLE ) END
- > SetCompactDescriptions ( compact = = COMPACT ) ;
}
} ;