# 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
# include "Item.h"
# include "safemap.h"
# include "DEFINES.h"
# include "Crawler.h"
# include "Menu.h"
# include "Ability.h"
INCLUDE_game
INCLUDE_DATA
INCLUDE_GFX
safemap < std : : string , ItemInfo > ITEM_DATA ;
safemap < std : : string , ItemScript > ITEM_SCRIPTS ;
safemap < std : : string , std : : set < std : : string > > ITEM_CATEGORIES ;
Item Item : : BLANK ;
std : : map < IT , Item > Inventory : : _inventory ;
std : : map < ITCategory , std : : vector < Item > > Inventory : : sortedInv ;
std : : vector < ItemOverlay > ItemOverlay : : items ;
std : : map < std : : string , ItemSet > ItemSet : : sets ;
std : : map < EquipSlot , Item * > Inventory : : equipment ;
std : : map < std : : string , EquipSlot > ItemInfo : : nameToEquipSlot ;
ItemInfo : : ItemInfo ( )
: customProps ( { nullptr , nullptr } ) , img ( nullptr ) { }
void ItemInfo : : InitializeItems ( ) {
for ( int i = int ( EquipSlot : : HELMET ) ; i < = int ( EquipSlot : : RING2 ) ; i < < = 1 ) {
Inventory : : equipment [ EquipSlot ( i ) ] = & Item : : BLANK ;
}
nameToEquipSlot [ " Helmet " ] = EquipSlot : : HELMET ;
nameToEquipSlot [ " Weapon " ] = EquipSlot : : WEAPON ;
nameToEquipSlot [ " Armor " ] = EquipSlot : : ARMOR ;
nameToEquipSlot [ " Gloves " ] = EquipSlot : : GLOVES ;
nameToEquipSlot [ " Pants " ] = EquipSlot : : PANTS ;
nameToEquipSlot [ " Shoes " ] = EquipSlot : : SHOES ;
nameToEquipSlot [ " Ring1 " ] = EquipSlot : : RING1 ;
nameToEquipSlot [ " Ring2 " ] = EquipSlot : : RING2 ;
InitializeScripts ( ) ;
//Setup Strings to Attribute map.
for ( ItemAttribute attr = ItemAttribute ( int ( ItemAttribute : : ENUM_START ) + 1 ) ; attr < ItemAttribute : : ENUM_END ; attr = ItemAttribute ( int ( attr ) + 1 ) ) {
ItemAttributable : : stringToAttribute [ ItemAttributable : : GetDisplayInfo ( attr ) . name ] = attr ;
}
InitializeSets ( ) ;
for ( auto & [ key , value ] : DATA [ " ItemCategory " ] . GetKeys ( ) ) {
ITEM_CATEGORIES [ key ] ;
Inventory : : sortedInv [ key ] ;
Menu : : inventoryListeners [ key ] ;
}
auto ReadItems = [ & ] ( datafile & data ) {
for ( auto & [ key , value ] : data . GetKeys ( ) ) {
if ( key = = " " ) ERR ( " Failed to read an item block ,no name specified! " ) ;
std : : string imgPath = " assets/ " + " item_img_directory " _S + key + " .png " ;
Renderable & img = GFX [ " item_img_directory " _S + key + " .png " ] ;
img . Load ( imgPath ) ;
std : : string scriptName = " " , description = " " , category = " " ;
std : : string setName = " " ;
float castTime = 0 ;
std : : vector < std : : string > slot ;
float cooldownTime = " Item.Item Cooldown Time " _F ;
std : : vector < ItemAttribute > statValueList ;
for ( auto & [ itemKey , itemValue ] : data [ key ] . GetKeys ( ) ) {
std : : string keyName = itemKey ;
if ( keyName = = " Description " ) {
description = data [ key ] [ keyName ] . GetString ( ) ;
} else
if ( keyName = = " ItemCategory " ) {
category = data [ key ] [ keyName ] . GetString ( ) ;
} else
if ( keyName = = " ItemScript " ) {
scriptName = data [ key ] [ keyName ] . GetString ( ) ;
} else
if ( keyName = = " Cast Time " ) {
castTime = float ( data [ key ] [ keyName ] . GetReal ( ) ) ;
} else
if ( keyName = = " Cooldown Time " ) {
castTime = float ( data [ key ] [ keyName ] . GetReal ( ) ) ;
} else
if ( keyName = = " Slot " ) {
for ( auto & val : data [ key ] [ keyName ] . GetValues ( ) ) {
slot . push_back ( val ) ;
}
} else
if ( keyName = = " StatValues " ) {
for ( int i = 0 ; i < data [ key ] [ " StatValues " ] . GetValueCount ( ) ; i + + ) {
statValueList . push_back ( ItemAttributable : : GetAttributeFromString ( data [ key ] [ " StatValues " ] . GetString ( i ) ) ) ;
}
} else
if ( keyName = = " PartofSet " ) {
setName = data [ key ] [ keyName ] . GetString ( ) ;
} else { //THis is a custom override modifier for a script. NO-OP
}
}
ItemInfo & it = ITEM_DATA [ key ] ;
if ( statValueList . size ( ) > 0 ) {
EnhancementInfo enhancementStats ;
for ( int enhancementLevel = 0 ; enhancementLevel < = 10 ; enhancementLevel + + ) {
datafile & dat = data [ key ] [ " StatValues[ " + std : : to_string ( enhancementLevel ) + " ] " ] ;
int attrIndex = 0 ;
for ( ItemAttribute & attr : statValueList ) {
enhancementStats . SetAttribute ( enhancementLevel , attr , dat . GetInt ( attrIndex ) ) ;
attrIndex + + ;
}
}
it . enhancement = enhancementStats ;
}
if ( scriptName ! = " " ) {
if ( ! ITEM_SCRIPTS . count ( scriptName ) ) {
ERR ( " Could not load script " < < scriptName < < " for Item " < < key < < " ! " )
}
}
it . name = key ;
it . description = description ;
it . category = category ;
it . castTime = castTime ;
it . cooldownTime = cooldownTime ;
it . slot = EquipSlot : : NONE ;
it . set = setName ;
if ( slot . size ( ) > 0 ) {
for ( std : : string & s : slot ) {
if ( ! nameToEquipSlot . count ( s ) ) ERR ( " WARNING! Tried to add item " < < it . name < < " to slot " < < s < < " which doesn't exist! " ) ;
it . slot | = nameToEquipSlot [ s ] ;
}
}
if ( ! ITEM_CATEGORIES . count ( it . category ) ) {
ERR ( " WARNING! Tried to add item " < < it . name < < " to category " < < it . category < < " which does not exist! " )
}
ITEM_CATEGORIES . at ( it . category ) . insert ( it . name ) ;
it . img = img . Decal ( ) ;
ItemProps & props = it . customProps ;
if ( scriptName ! = " " ) {
props . scriptProps = & DATA [ " ItemScript " ] [ scriptName ] ;
props . customProps = & data [ key ] ;
}
it . useFunc = scriptName ;
}
} ;
ReadItems ( DATA [ " ItemDatabase " ] ) ;
ReadItems ( DATA [ " Equipment " ] ) ;
ITEM_DATA . SetInitialized ( ) ;
ITEM_CATEGORIES . SetInitialized ( ) ;
Menu : : inventoryListeners . SetInitialized ( ) ;
for ( auto & [ name , info ] : ITEM_DATA ) {
Item tempItem { 1 , name } ;
if ( tempItem . Description ( ) . length ( ) = = 0 ) ERR ( " WARNING! Item " < < info . name < < " does not have a description! " ) ;
}
std : : cout < < ITEM_DATA . size ( ) < < " items have been loaded. " < < std : : endl ;
std : : cout < < ITEM_CATEGORIES . size ( ) < < " item categories have been loaded. " < < std : : endl ;
}
ItemProps : : ItemProps ( utils : : datafile * scriptProps , utils : : datafile * customProps )
: scriptProps ( scriptProps ) , customProps ( customProps ) { }
int ItemProps : : GetIntProp ( std : : string prop , size_t index = 0 ) {
if ( customProps - > HasProperty ( prop ) ) return ( * customProps ) [ prop ] . GetInt ( index ) ;
else return ( * scriptProps ) [ prop ] . GetInt ( index ) ;
} ;
float ItemProps : : GetFloatProp ( std : : string prop , size_t index = 0 ) {
if ( customProps - > HasProperty ( prop ) ) return float ( ( * customProps ) [ prop ] . GetReal ( index ) ) ;
else return float ( ( * scriptProps ) [ prop ] . GetReal ( index ) ) ;
} ;
std : : string ItemProps : : GetStringProp ( std : : string prop , size_t index = 0 ) {
if ( customProps - > HasProperty ( prop ) ) return ( * customProps ) [ prop ] . GetString ( index ) ;
else return ( * scriptProps ) [ prop ] . GetString ( index ) ;
} ;
void ItemInfo : : InitializeScripts ( ) {
ITEM_SCRIPTS [ " Restore " ] = [ ] ( Crawler * game , ItemProps props ) {
game - > GetPlayer ( ) - > Heal ( props . GetIntProp ( " HP Restore " ) ) ;
game - > GetPlayer ( ) - > Heal ( int ( game - > GetPlayer ( ) - > GetMaxHealth ( ) * props . GetIntProp ( " HP % Restore " ) / 100.f ) ) ;
game - > GetPlayer ( ) - > RestoreMana ( props . GetIntProp ( " MP Restore " ) ) ;
game - > GetPlayer ( ) - > RestoreMana ( int ( game - > GetPlayer ( ) - > GetMaxMana ( ) * props . GetIntProp ( " MP % Restore " ) / 100.f ) ) ;
return true ;
} ;
ITEM_SCRIPTS [ " Buff " ] = [ ] ( Crawler * game , ItemProps props ) {
game - > GetPlayer ( ) - > AddBuff ( BuffType : : ATTACK_PCT_UP , props . GetFloatProp ( " Attack % " , 1 ) , props . GetFloatProp ( " Attack % " , 0 ) / 100 ) ;
return true ;
} ;
ITEM_SCRIPTS . SetInitialized ( ) ;
std : : cout < < ITEM_SCRIPTS . size ( ) < < " item scripts have been loaded. " < < std : : endl ;
}
Item : : Item ( )
: amt ( 0 ) , it ( nullptr ) , enhancementLevel ( 0 ) { }
Item : : Item ( uint32_t amt , IT item , uint8_t enhancementLevel )
: amt ( amt ) , it ( & ITEM_DATA . at ( item ) ) , enhancementLevel ( enhancementLevel ) { }
void Inventory : : AddItem ( IT it , uint32_t amt , bool monsterDrop ) {
if ( ! ITEM_DATA . count ( it ) ) ERR ( " Item " < < std : : quoted ( it ) < < " does not exist in Item Database! " ) ;
//There are two places to manipulate items in (Both the sorted inventory and the actual inventory)
if ( ! _inventory . count ( it ) ) {
_inventory [ it ] = Item { amt , it } ;
InsertIntoSortedInv ( it ) ;
} else {
_inventory . at ( it ) . amt + = amt ;
}
InsertIntoStageInventoryCategory ( it , amt , monsterDrop ) ;
}
Item Inventory : : CopyItem ( IT it ) {
if ( ! _inventory . count ( it ) ) return Item : : BLANK ;
return _inventory . at ( it ) ;
}
Item & Inventory : : GetItem ( IT it ) {
if ( ! _inventory . count ( it ) ) return Item : : BLANK ;
return _inventory . at ( it ) ;
}
uint32_t Inventory : : GetItemCount ( IT it ) {
if ( ! _inventory . count ( it ) ) {
return 0 ;
} else {
return _inventory . at ( it ) . Amt ( ) ;
}
}
//Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory.
bool Inventory : : UseItem ( IT it , uint32_t amt ) {
if ( ! _inventory . count ( it ) ) return false ;
//There are two places to manipulate items in (Both the sorted inventory and the actual inventory)
for ( uint32_t i = 0 ; i < amt ; i + + ) {
if ( ExecuteAction ( it ) ) {
return RemoveItem ( it ) ;
}
}
return false ;
}
//Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory.
bool Inventory : : RemoveItem ( IT it , ITCategory inventory , uint32_t amt ) {
# pragma region Calculate Inventory to Manipulate
std : : vector < Item > & inv = sortedInv . at ( inventory ) ;
bool eraseFromLootWindow = false ;
if ( inventory = = " Monster Loot " ) {
inv = sortedInv . at ( " Monster Loot " ) ;
eraseFromLootWindow = true ;
} else
if ( inventory = = " Stage Loot " ) {
inv = sortedInv . at ( " Stage Loot " ) ;
eraseFromLootWindow = true ;
}
int count = 0 ;
for ( Item & item : inv ) {
if ( item . Name ( ) = = it ) break ;
count + + ;
}
# pragma endregion
uint32_t itemAmt = GetItemCount ( it ) ;
if ( inventory = = " Monster Loot " | | inventory = = " Stage Loot " ) {
itemAmt = inv . at ( count ) . Amt ( ) ;
}
//There are two places to manipulate items in (Both the sorted inventory and the actual inventory)
if ( ! itemAmt ) return false ;
if ( amt > = itemAmt ) {
inv . erase ( inv . begin ( ) + count ) ;
if ( ! eraseFromLootWindow ) {
_inventory . erase ( it ) ;
}
//Callback for GUI inventories.
Menu : : InventorySlotsUpdated ( inventory ) ;
return true ;
} else {
inv . at ( count ) . amt - = amt ;
if ( ! eraseFromLootWindow ) {
_inventory . at ( it ) . amt - = amt ;
}
return false ;
}
}
//Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory.
bool Inventory : : RemoveItem ( IT it , uint32_t amt ) {
ITCategory cat = ITEM_DATA [ it ] . category ;
return RemoveItem ( it , cat , amt ) ;
}
std : : vector < Item > & Inventory : : get ( ITCategory itemCategory ) {
return sortedInv . at ( itemCategory ) ;
}
void Inventory : : InsertIntoSortedInv ( IT item ) {
sortedInv . at ( ITEM_DATA [ item ] . category ) . push_back ( Item { 1 , item } ) ;
//This should be a callback to menus that we need to update the interface with another item slot since a new one has appeared.
Menu : : InventorySlotsUpdated ( ITEM_DATA [ item ] . category ) ;
}
void Inventory : : InsertIntoStageInventoryCategory ( IT item , uint32_t amt , bool monsterDrop ) {
std : : string stageInventoryCategory = " Stage Loot " ;
if ( monsterDrop ) {
stageInventoryCategory = " Monster Loot " ;
}
std : : vector < Item > & inv = sortedInv . at ( stageInventoryCategory ) ;
std : : vector < Item > : : iterator it = std : : find ( inv . begin ( ) , inv . end ( ) , Item { amt , item } ) ;
if ( it ! = inv . end ( ) ) {
( * it ) . amt + = amt ;
} else {
inv . push_back ( Item { amt , item } ) ;
}
Menu : : InventorySlotsUpdated ( stageInventoryCategory ) ;
}
bool Inventory : : ExecuteAction ( IT item ) {
if ( ITEM_SCRIPTS . count ( ITEM_DATA . at ( item ) . useFunc ) ) {
return ITEM_SCRIPTS . at ( ITEM_DATA . at ( item ) . useFunc ) ( game , ITEM_DATA [ item ] . customProps ) ;
} else {
return false ;
}
}
bool Inventory : : SwapItems ( ITCategory itemCategory , uint32_t slot1 , uint32_t slot2 ) {
std : : vector < Item > & inv = sortedInv . at ( itemCategory ) ;
int largestSlot = std : : max ( slot1 , slot2 ) ;
if ( inv . size ( ) < = largestSlot ) {
//The inventory is too small, so expand out blank slots to accomodate.
inv . resize ( largestSlot + size_t ( 1 ) ) ;
}
Item & item1 = inv . at ( slot1 ) ;
Item & item2 = inv . at ( slot2 ) ;
std : : swap ( item1 , item2 ) ;
return true ;
}
uint32_t Item : : Amt ( ) {
return amt ;
} ;
std : : string Item : : Name ( ) {
if ( IsBlank ( ) ) return " " ;
std : : string name = it - > Name ( ) ;
if ( IsEquippable ( ) & & EnhancementLevel ( ) > 0 ) {
name + = " [+ " + std : : to_string ( EnhancementLevel ( ) ) + " ] " ;
}
return name ;
} ;
bool Item : : IsEquippable ( ) const {
return Category ( ) = = " Equipment " | | Category ( ) = = " Accessories " ;
}
std : : string Item : : Description ( CompactText compact ) {
std : : string description = it - > Description ( ) ;
if ( IsEquippable ( ) ) {
description + = ' \n ' ;
description + = GetStats ( ) . GetStatsString ( compact ) ;
if ( ItemSet ( ) ) {
const : : ItemSet * const set = ItemSet ( ) . value ( ) ;
if ( compact = = COMPACT ) {
description + = " \n " + set - > GetSetName ( ) + " Set - " ;
} else {
description + = " \n \n " + set - > GetSetName ( ) + " Set \n " ;
}
const std : : map < : : ItemSet , int > & itemSetCounts = Inventory : : GetEquippedItemSets ( ) ;
for ( bool first = true ; const auto & [ pieceCount , bonuses ] : set - > GetSetBonusBreakpoints ( ) ) {
if ( itemSetCounts . count ( * set ) & & pieceCount < = itemSetCounts . at ( * set ) ) {
description + = " #56E346 " ;
}
if ( ! first ) {
if ( compact = = COMPACT ) {
description + = " " ;
} else {
description + = " \n " ;
}
}
description + = " ( " + std : : to_string ( pieceCount ) + " ): " + bonuses . GetStatsString ( compact ) + " #FFFFFF " ;
first = false ;
}
}
}
return description ;
} ;
const ITCategory Item : : Category ( ) const {
return it - > Category ( ) ;
} ;
: : Decal * Item : : Decal ( ) {
return it - > Decal ( ) ;
} ;
ItemScript & Item : : OnUseAction ( ) {
return it - > OnUseAction ( ) ;
} ;
std : : string ItemInfo : : Name ( ) {
return name ;
} ;
std : : string ItemInfo : : Description ( ) {
return description ;
} ;
ITCategory ItemInfo : : Category ( ) {
return category ;
} ;
: : Decal * ItemInfo : : Decal ( ) {
return img ;
} ;
ItemScript & ItemInfo : : OnUseAction ( ) {
return ITEM_SCRIPTS . at ( useFunc ) ;
} ;
bool Item : : IsBlank ( ) {
return amt = = 0 | | it = = nullptr ;
}
void Inventory : : Clear ( ITCategory itemCategory ) {
std : : vector < Item > itemList = get ( itemCategory ) ; //We have to make a copy here because RemoveItem() will modify the list provided by get() inline.
for ( Item & item : itemList ) {
size_t itemQuantity = GetItemCount ( item . Name ( ) ) ; //Normally we want to clear all the items that are actually in our inventory...But...
if ( itemCategory = = " Monster Loot " | | itemCategory = = " Stage Loot " ) { //These do not affect the actual inventory, we just clear the lists.
itemQuantity = item . Amt ( ) ;
}
RemoveItem ( item . Name ( ) , itemCategory , uint32_t ( itemQuantity ) ) ;
}
}
bool Item : : operator = = ( const Item & rhs ) const {
return it = = rhs . it ;
}
ItemOverlay : : ItemOverlay ( ItemInfo item )
: it ( item ) , width ( " ItemDrop.Item Drop Scale " _F * 24 + 4 + game - > GetTextSizeProp ( item . Name ( ) ) . x * 0.5f ) {
xOffset = - width ;
}
void ItemOverlay : : Update ( ) {
for ( ItemOverlay & item : items ) {
item . timer + = game - > GetElapsedTime ( ) ;
item . xOffset = std : : min ( item . xOffset + game - > GetElapsedTime ( ) * " ItemOverlay.Item Overlay Speed " _F , 0.f ) ;
}
std : : erase_if ( items , [ ] ( ItemOverlay & item ) { return item . timer > " ItemOverlay.Item Overlay Time " _F ; } ) ;
}
void ItemOverlay : : Draw ( ) {
float itemScale = " ItemDrop.Item Drop Scale " _F ;
for ( int counter = 0 ; ItemOverlay & item : items ) {
vf2d pos = { item . xOffset , 96.f + counter * 10 } ;
Pixel darkCol = Menu : : GetCurrentTheme ( ) . GetButtonCol ( ) ;
Pixel lightCol = Menu : : GetCurrentTheme ( ) . GetButtonCol ( ) * 1.2f ;
game - > GradientFillRectDecal ( pos , { item . width , 8 } , darkCol , darkCol , darkCol , lightCol ) ;
game - > DrawRectDecal ( pos , { item . width , 8 } , Menu : : GetCurrentTheme ( ) . GetHighlightCol ( ) ) ;
game - > DrawDecal ( pos , item . it . Decal ( ) , { itemScale , itemScale } ) ;
game - > DrawShadowStringPropDecal ( pos + vf2d { itemScale * 24 + 2 , 2 } , item . it . Name ( ) , WHITE , BLACK , { 0.5f , 0.7f } ) ;
counter + + ;
}
}
void ItemOverlay : : AddToItemOverlay ( const ItemInfo & it ) {
items . push_back ( ItemOverlay { it } ) ;
std : : for_each ( items . begin ( ) , items . end ( ) , [ ] ( ItemOverlay & it ) { it . ResetTimer ( ) ; } ) ;
}
float ItemInfo : : CastTime ( ) {
return castTime ;
}
float ItemInfo : : CooldownTime ( ) {
return cooldownTime ;
}
void ItemOverlay : : ResetTimer ( ) {
timer = 0 ;
}
float Item : : CastTime ( ) {
return it - > CastTime ( ) ;
}
float Item : : CooldownTime ( ) {
return it - > CooldownTime ( ) ;
}
const Stats & EnhancementInfo : : operator [ ] ( int level ) const {
return enhancementStats [ level ] ;
}
const std : : optional < const ItemSet * const > ItemInfo : : ItemSet ( ) const {
if ( ItemSet : : sets . count ( set ) ) {
return & ItemSet : : sets [ set ] ;
}
return { } ;
} ;
const Stats & ItemSet : : operator [ ] ( int setPieces ) const {
if ( setPieces < = 0 | | setPieces > = 9 ) ERR ( " Piece count is invalid! Expecting a value (1-8) but got " < < setPieces ) ;
return setBonuses [ setPieces ] ;
} ;
void ItemSet : : AddSetBonus ( std : : string setName , int pieceCount , Stats & bonuses ) {
if ( pieceCount < = 0 | | pieceCount > = 9 ) ERR ( " Piece count is invalid! Expecting a value (1-8) but got " < < pieceCount ) ;
sets [ setName ] . name = setName ;
for ( int i = pieceCount ; i < sets [ setName ] . setBonuses . size ( ) ; i + + ) {
sets [ setName ] . setBonuses [ i ] + = bonuses ;
}
sets [ setName ] . setBonusBreakpoints . push_back ( { pieceCount , bonuses } ) ;
}
void Inventory : : EquipItem ( Item & it , EquipSlot slot ) {
if ( ! ( it . it - > slot & slot ) ) return ;
EquipSlot equippedSlot = GetSlotEquippedIn ( it ) ;
if ( equippedSlot ! = EquipSlot : : NONE ) UnequipItem ( equippedSlot ) ;
if ( GetEquip ( slot ) ! = nullptr ) UnequipItem ( slot ) ;
Inventory : : equipment [ slot ] = & it ;
PlayerStats : : RecalculateEquipStats ( ) ;
} ;
void Inventory : : UnequipItem ( EquipSlot slot ) {
Inventory : : equipment [ slot ] = & Item : : BLANK ;
PlayerStats : : RecalculateEquipStats ( ) ;
} ;
EquipSlot Inventory : : GetSlotEquippedIn ( Item & it ) {
for ( int i = int ( EquipSlot : : HELMET ) ; i < = int ( EquipSlot : : RING2 ) ; i < < = 1 ) {
EquipSlot slot = EquipSlot ( i ) ;
Item * equip = GetEquip ( slot ) ;
if ( equip = = nullptr ) continue ;
if ( equip - > Name ( ) = = it . Name ( ) ) return slot ;
}
return EquipSlot : : NONE ;
} ;
Item * Inventory : : GetEquip ( EquipSlot slot ) {
return Inventory : : equipment [ slot ] ;
}
EquipSlot Item : : GetEquipSlot ( ) {
return it - > Slot ( ) ;
}
EquipSlot ItemInfo : : Slot ( ) {
return slot ;
}
void EnhancementInfo : : SetAttribute ( int enhanceLevel , ItemAttribute attribute , int value ) {
while ( enhancementStats . size ( ) < = enhanceLevel ) {
enhancementStats . push_back ( { } ) ;
}
enhancementStats [ enhanceLevel ] . A ( attribute ) = value ;
}
void ItemInfo : : InitializeSets ( ) {
for ( auto & [ key , value ] : DATA [ " ItemSet " ] . GetKeys ( ) ) {
std : : string setName = key ;
datafile & setInfo = DATA [ " ItemSet " ] [ setName ] ;
for ( int pieceCount = 1 ; pieceCount < = 8 ; pieceCount + + ) {
if ( setInfo . HasProperty ( std : : to_string ( pieceCount ) ) ) {
datafile & statInfo = setInfo [ std : : to_string ( pieceCount ) ] ;
Stats bonuses ;
for ( auto & [ key , value ] : statInfo . GetKeys ( ) ) {
ItemAttribute attr = bonuses . GetAttributeFromString ( key ) ;
bonuses . A ( attr ) = statInfo [ key ] . GetInt ( 0 ) ;
}
ItemSet : : AddSetBonus ( setName , pieceCount , bonuses ) ;
}
}
}
}
Stats ItemInfo : : GetStats ( int enhancementLevel ) {
if ( enhancement . size ( ) < = enhancementLevel ) {
return { } ;
}
return enhancement [ enhancementLevel ] ;
}
const Stats Item : : GetStats ( ) const {
if ( it = = nullptr ) return { } ;
return it - > GetStats ( enhancementLevel ) ;
} ;
const size_t EnhancementInfo : : size ( ) const {
return enhancementStats . size ( ) ;
} ;
const std : : optional < const ItemSet * const > Item : : ItemSet ( ) const {
return it - > ItemSet ( ) ;
} ;
uint8_t Item : : EnhancementLevel ( ) const {
return enhancementLevel ;
} ;
void Item : : EnhanceItem ( ) {
if ( enhancementLevel + 1 > " Item.Item Max Enhancement Level " _I ) ERR ( " WARNING! Attempted to enhance " < < Name ( ) < < " beyond the cap of " < < " Item.Item Max Enhancement Level " _I ) ;
enhancementLevel + + ;
} ;
const std : : vector < std : : pair < int , Stats > > & ItemSet : : GetSetBonusBreakpoints ( ) const {
return setBonusBreakpoints ;
} ;
const std : : map < ItemSet , int > Inventory : : GetEquippedItemSets ( ) {
std : : map < ItemSet , int > setCounts ;
for ( int i = int ( EquipSlot : : HELMET ) ; i < = int ( EquipSlot : : RING2 ) ; i < < = 1 ) {
EquipSlot slot = EquipSlot ( i ) ;
Item * equip = Inventory : : GetEquip ( slot ) ;
if ( equip - > IsBlank ( ) ) continue ;
if ( equip - > ItemSet ( ) ) {
setCounts [ * equip - > ItemSet ( ) . value ( ) ] + + ;
}
}
return setCounts ;
}
const std : : string Stats : : GetStatsString ( CompactText compact ) const {
std : : string description = " " ;
for ( bool first = true ; const auto & [ attr , val ] : attributes ) {
if ( ! first ) {
if ( compact = = COMPACT ) {
description + = " " ;
} else {
description + = " \n " ;
}
}
description + = ItemAttributable : : GetAttributeName ( attr ) + " : " + std : : to_string ( val ) + ( ItemAttributable : : GetDisplayInfo ( attr ) . displayAsPercent ? " % " : " " ) ;
first = false ;
}
return description ;
}
ItemInfo & ItemInfo : : operator [ ] ( const IT & item ) {
if ( ! ITEM_DATA . count ( item ) ) ERR ( " Item " < < std : : quoted ( item ) < < " does not exist in the item database! " ) ;
return ITEM_DATA [ item ] ;
}