# 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 "AttributableStat.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 ;
std : : shared_ptr < Item > Item : : BLANK = std : : make_shared < Item > ( ) ;
std : : map < IT , std : : shared_ptr < Item > > Inventory : : _inventory ;
std : : map < ITCategory , std : : vector < std : : shared_ptr < Item > > > Inventory : : sortedInv ;
std : : vector < ItemOverlay > ItemOverlay : : items ;
std : : map < std : : string , ItemSet > ItemSet : : sets ;
std : : map < EquipSlot , std : : weak_ptr < Item > > Inventory : : equipment ;
std : : map < std : : string , EquipSlot > ItemInfo : : nameToEquipSlot ;
int Item : : IsBlankStaticCallCounter = 0 ;
safemap < int , float > Stats : : maxDamageReductionTable ;
ItemEnhancementFunctionPrimingData Item : : enhanceFunctionPrimed ( " CanEnhanceItem() " ) ;
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 ( ) ;
InitializeSets ( ) ;
for ( auto & [ key , value ] : DATA [ " ItemCategory " ] . GetKeys ( ) ) {
ITEM_CATEGORIES [ key ] ;
Inventory : : sortedInv [ key ] ;
Menu : : InitializeMenuListenerCategory ( 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 ;
uint32_t sellValue = 0 ;
uint32_t buyValue = 0 ;
bool useDuringCast = false ;
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 ( ItemAttribute : : Get ( data [ key ] [ " StatValues " ] . GetString ( i ) ) ) ;
}
} else
if ( keyName = = " PartofSet " ) {
setName = data [ key ] [ keyName ] . GetString ( ) ;
}
if ( keyName = = " BuyValue " ) {
buyValue = data [ key ] [ keyName ] . GetInt ( ) ;
} else
if ( keyName = = " SellValue " ) {
sellValue = data [ key ] [ keyName ] . GetInt ( ) ;
} 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 . GetReal ( attrIndex ) ) ;
attrIndex + + ;
}
std : : vector < Item > itemsRequired ;
uint32_t goldCost = 0 ;
if ( enhancementLevel ! = 0 ) { //The first level does not require any crafting, skip this level.
while ( data [ key ] [ " Crafting " ] [ std : : format ( " Level[{}] " , enhancementLevel ) ] . HasProperty ( std : : format ( " Item[{}] " , itemsRequired . size ( ) ) ) ) {
datafile & item = data [ key ] [ " Crafting " ] [ std : : format ( " Level[{}] " , enhancementLevel ) ] [ std : : format ( " Item[{}] " , itemsRequired . size ( ) ) ] ;
itemsRequired . push_back ( Item ( item . GetInt ( 1 ) , item . GetString ( 0 ) ) ) ;
}
goldCost = data [ key ] [ " Crafting " ] [ std : : format ( " Level[{}] " , enhancementLevel ) ] [ " Gold " ] . GetInt ( ) ;
}
enhancementStats . SetCraftingRequirements ( enhancementLevel , itemsRequired , goldCost ) ;
}
it . enhancement = enhancementStats ;
}
if ( scriptName ! = " " ) {
if ( scriptName = = " RestoreDuringCast " ) {
useDuringCast = true ;
}
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 ;
it . buyValue = buyValue ;
it . sellValue = sellValue ;
it . useDuringCast = useDuringCast ;
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 ;
# pragma region Equipment Category Verification Tests
int equipmentCategories = 0 ;
equipmentCategories + = it . IsWeapon ( ) ;
equipmentCategories + = it . IsArmor ( ) ;
equipmentCategories + = it . IsAccessory ( ) ;
if ( it . IsEquippable ( ) & & equipmentCategories = = 0 ) ERR ( std : : format ( " WARNING! {} is not considered a weapon, armor, or accessory but is considered equippable! " , it . Name ( ) ) )
if ( it . IsEquippable ( ) & & equipmentCategories ! = 1 ) ERR ( std : : format ( " WARNING! {} is considered in {} categories among the [weapon, armor, or accessory] set but is considered equippable! " , it . Name ( ) , equipmentCategories ) )
if ( it . IsEquippable ( ) & & it . Category ( ) ! = " Equipment " & & it . Category ( ) ! = " Accessories " ) ERR ( std : : format ( " WARNING! {} is considered equippable, but is not in either the Equipment category or the Accessories category! " , it . Name ( ) ) )
if ( it . Category ( ) = = " Equipment " & & ! ( it . IsWeapon ( ) | | it . IsArmor ( ) ) ) ERR ( std : : format ( " WARNING! {} is in the Equipment Category, but is not considered a Weapon or Armor! " , it . Name ( ) ) )
if ( it . Category ( ) = = " Accessories " & & it . IsWeapon ( ) ) ERR ( std : : format ( " WARNING! {} is in the Accessories Category, but is considered a Weapon! " , it . Name ( ) ) )
if ( it . Category ( ) = = " Accessories " & & it . IsArmor ( ) ) ERR ( std : : format ( " WARNING! {} is in the Accessories Category, but is considered Armor! " , it . Name ( ) ) )
if ( it . Category ( ) = = " Accessories " & & ! it . IsAccessory ( ) ) ERR ( std : : format ( " WARNING! {} is in the Accessories Category, but not considered an Accessory! " , it . Name ( ) ) )
# pragma endregion
}
} ;
ReadItems ( DATA [ " ItemDatabase " ] ) ;
ReadItems ( DATA [ " Equipment " ] ) ;
ITEM_DATA . SetInitialized ( ) ;
ITEM_CATEGORIES . SetInitialized ( ) ;
Menu : : LockInListeners ( ) ;
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 ( const std : : string & prop , size_t index ) const {
if ( customProps - > HasProperty ( prop ) ) return ( * customProps ) [ prop ] . GetInt ( index ) ;
else return ( * scriptProps ) [ prop ] . GetInt ( index ) ;
} ;
float ItemProps : : GetFloatProp ( const std : : string & prop , size_t index ) const {
if ( customProps - > HasProperty ( prop ) ) return float ( ( * customProps ) [ prop ] . GetReal ( index ) ) ;
else return float ( ( * scriptProps ) [ prop ] . GetReal ( index ) ) ;
} ;
std : : string ItemProps : : GetStringProp ( const std : : string & prop , size_t index ) const {
if ( customProps - > HasProperty ( prop ) ) return ( * customProps ) [ prop ] . GetString ( index ) ;
else return ( * scriptProps ) [ prop ] . GetString ( index ) ;
} ;
const uint32_t ItemProps : : PropCount ( const std : : string & prop ) const {
if ( customProps - > HasProperty ( prop ) ) return ( * customProps ) [ prop ] . GetValueCount ( ) ;
else return ( * scriptProps ) [ prop ] . GetValueCount ( ) ;
}
void ItemInfo : : InitializeScripts ( ) {
ITEM_SCRIPTS [ " Restore " ] = [ ] ( Crawler * game , ItemProps props ) {
auto ParseItemScriptData = [ & ] ( const std : : string & propName , std : : function < void ( Crawler * , int ) > action ) {
int restoreAmt = props . GetIntProp ( propName ) ;
action ( game , restoreAmt ) ;
if ( restoreAmt > 0 & & props . PropCount ( propName ) = = 3 ) {
game - > GetPlayer ( ) - > AddBuff ( RESTORATION , props . GetFloatProp ( propName , 2 ) , restoreAmt , props . GetFloatProp ( propName , 1 ) , action ) ;
}
} ;
ParseItemScriptData ( " HP Restore " , [ & ] ( Crawler * game , int restoreAmt ) {
game - > GetPlayer ( ) - > Heal ( restoreAmt ) ;
} ) ;
ParseItemScriptData ( " HP % Restore " , [ & ] ( Crawler * game , int restoreAmt ) {
game - > GetPlayer ( ) - > Heal ( int ( game - > GetPlayer ( ) - > GetMaxHealth ( ) * restoreAmt / 100.0f ) ) ;
} ) ;
ParseItemScriptData ( " MP Restore " , [ & ] ( Crawler * game , int restoreAmt ) {
game - > GetPlayer ( ) - > RestoreMana ( restoreAmt ) ;
} ) ;
ParseItemScriptData ( " MP % Restore " , [ & ] ( Crawler * game , int restoreAmt ) {
game - > GetPlayer ( ) - > RestoreMana ( int ( game - > GetPlayer ( ) - > GetMaxMana ( ) * props . GetIntProp ( " MP % Restore " ) / 100.f ) ) ;
} ) ;
return true ;
} ;
for ( auto & [ key , value ] : ItemAttribute : : attributes ) {
if ( ! DATA . GetProperty ( " ItemScript.Buff " ) . HasProperty ( key ) ) {
ERR ( " WARNING! Buff Item Script does not support Buff " < < std : : quoted ( value . Name ( ) ) < < " ! " ) ;
}
}
ITEM_SCRIPTS [ " Buff " ] = [ ] ( Crawler * game , ItemProps props ) {
for ( auto & [ key , value ] : ItemAttribute : : attributes ) {
float intensity = props . GetFloatProp ( key , 0 ) ;
if ( ItemAttribute : : Get ( key ) . DisplayAsPercent ( ) ) intensity / = 100 ;
game - > GetPlayer ( ) - > AddBuff ( BuffType : : STAT_UP , props . GetFloatProp ( key , 1 ) , intensity , { ItemAttribute : : Get ( key ) } ) ;
}
return true ;
} ;
ITEM_SCRIPTS [ " RestoreDuringCast " ] = [ & ] ( Crawler * game , ItemProps props ) {
auto ParseItemScriptData = [ & ] ( const std : : string & propName , std : : function < void ( Crawler * , int ) > action ) {
int restoreAmt = props . GetIntProp ( propName ) ;
action ( game , restoreAmt ) ;
if ( restoreAmt > 0 & & props . PropCount ( propName ) = = 3 ) {
game - > GetPlayer ( ) - > AddBuff ( RESTORATION_DURING_CAST , props . GetFloatProp ( propName , 2 ) , restoreAmt , props . GetFloatProp ( propName , 1 ) , action ) ;
}
} ;
ParseItemScriptData ( " HP Restore " , [ & ] ( Crawler * game , int restoreAmt ) {
game - > GetPlayer ( ) - > Heal ( restoreAmt ) ;
} ) ;
ParseItemScriptData ( " HP % Restore " , [ & ] ( Crawler * game , int restoreAmt ) {
game - > GetPlayer ( ) - > Heal ( int ( game - > GetPlayer ( ) - > GetMaxHealth ( ) * restoreAmt / 100.0f ) ) ;
} ) ;
ParseItemScriptData ( " MP Restore " , [ & ] ( Crawler * game , int restoreAmt ) {
game - > GetPlayer ( ) - > RestoreMana ( restoreAmt ) ;
} ) ;
ParseItemScriptData ( " MP % Restore " , [ & ] ( Crawler * game , int restoreAmt ) {
game - > GetPlayer ( ) - > RestoreMana ( int ( game - > GetPlayer ( ) - > GetMaxMana ( ) * props . GetIntProp ( " MP % Restore " ) / 100.f ) ) ;
} ) ;
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 ] = std : : make_shared < Item > ( amt , it ) ;
InsertIntoSortedInv ( it ) ;
} else {
_inventory . at ( it ) - > amt + = amt ;
}
InsertIntoStageInventoryCategory ( it , amt , monsterDrop ) ;
}
std : : shared_ptr < Item > Inventory : : CopyItem ( IT it ) {
if ( ! _inventory . count ( it ) ) return std : : make_shared < Item > ( * Item : : BLANK ) ;
return std : : make_shared < Item > ( * _inventory . at ( it ) ) ;
}
std : : weak_ptr < 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 < std : : shared_ptr < 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 ( std : : shared_ptr < Item > item : inv ) {
if ( item = = 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 ) ; //Clears it from the detected sorted inventory as well!
if ( ! eraseFromLootWindow ) { //We must clear out the item AFTER we've updated context-sensitive inventories because they may be borrowing a ref from this structure!!!
_inventory . erase ( it ) ;
}
//Callback for GUI inventories.
Menu : : InventorySlotsUpdated ( inventory ) ;
return true ;
} else {
if ( ! eraseFromLootWindow ) {
_inventory . at ( it ) - > amt - = amt ;
} else {
inv . at ( count ) - > amt - = amt ; //Don't touch the sorted inventory unless this is monster loot or stage loot because there's only "1" of this item in the entry list.
}
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 ) ;
}
const std : : vector < std : : shared_ptr < Item > > & Inventory : : get ( ITCategory itemCategory ) {
return sortedInv . at ( itemCategory ) ;
}
void Inventory : : InsertIntoSortedInv ( IT item ) {
sortedInv . at ( ITEM_DATA [ item ] . category ) . push_back ( _inventory [ 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 < std : : shared_ptr < Item > > & inv = sortedInv . at ( stageInventoryCategory ) ;
std : : vector < std : : shared_ptr < Item > > : : iterator it = std : : find ( inv . begin ( ) , inv . end ( ) , std : : make_shared < Item > ( amt , item ) ) ;
if ( it ! = inv . end ( ) ) {
( * it ) - > amt + = amt ;
} else {
inv . push_back ( std : : make_shared < 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 < std : : shared_ptr < 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 ) ) ;
}
std : : shared_ptr < Item > item1 = inv . at ( slot1 ) ;
std : : shared_ptr < Item > item2 = inv . at ( slot2 ) ;
std : : swap ( item1 , item2 ) ;
return true ;
}
uint32_t Item : : Amt ( ) const {
return amt ;
} ;
const std : : string & Item : : ActualName ( ) const {
if ( _IsBlank ( ) ) return " " ;
return it - > Name ( ) ;
} ;
const std : : string Item : : DisplayName ( ) const {
if ( _IsBlank ( ) ) return " " ;
std : : string name = ActualName ( ) ;
if ( IsEquippable ( ) & & EnhancementLevel ( ) > 0 ) {
name + = " [+ " + std : : to_string ( EnhancementLevel ( ) ) + " ] " ;
}
return name ;
}
const bool ItemInfo : : IsEquippable ( ) const {
return slot ! = EquipSlot : : NONE & & ( category = = " Equipment " | | category = = " Accessories " ) ;
}
const bool Item : : IsEquippable ( ) const {
return it - > IsEquippable ( ) ;
}
const std : : string Item : : Description ( CompactText compact ) const {
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 ( ) ;
} ;
const : : Decal * const Item : : Decal ( ) const {
return it - > Decal ( ) ;
} ;
const ItemScript & Item : : OnUseAction ( ) const {
return it - > OnUseAction ( ) ;
} ;
const std : : string & ItemInfo : : Name ( ) const {
return name ;
} ;
const std : : string & ItemInfo : : Description ( ) const {
return description ;
} ;
const ITCategory ItemInfo : : Category ( ) const {
return category ;
} ;
const : : Decal * const ItemInfo : : Decal ( ) const {
return img ;
} ;
const ItemScript & ItemInfo : : OnUseAction ( ) const {
return ITEM_SCRIPTS . at ( useFunc ) ;
} ;
const bool Item : : IsBlank ( ) const {
if ( Item : : IsBlankStaticCallCounter ! = 1 ) ERR ( " WARNING! You should not call the IsBlank() function directly! Use ISBLANK() macro instead! " )
Item : : IsBlankStaticCallCounter - - ;
return amt = = 0 | | it = = nullptr ;
}
void Inventory : : Clear ( ITCategory itemCategory ) {
std : : vector < std : : shared_ptr < Item > > itemList = get ( itemCategory ) ; //We have to make a copy here because RemoveItem() will modify the list provided by get() inline.
for ( std : : shared_ptr < Item > & item : itemList ) {
size_t itemQuantity = GetItemCount ( item - > ActualName ( ) ) ; //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 - > ActualName ( ) , itemCategory , uint32_t ( itemQuantity ) ) ;
}
}
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 , const_cast < Decal * > ( 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 ( ) ; } ) ;
}
const float ItemInfo : : CastTime ( ) const {
return castTime ;
}
const float ItemInfo : : CooldownTime ( ) const {
return cooldownTime ;
}
void ItemOverlay : : ResetTimer ( ) {
timer = 0 ;
}
const float Item : : CastTime ( ) const {
return it - > CastTime ( ) ;
}
const float Item : : CooldownTime ( ) const {
return it - > CooldownTime ( ) ;
}
const EnhancementLevelInfo EnhancementInfo : : operator [ ] ( int level ) const {
return EnhancementLevelInfo { const_cast < Stats & > ( enhancementStats . at ( level ) ) , const_cast < CraftingRequirement & > ( craftingRequirements . at ( level ) ) } ;
}
const std : : optional < const ItemSet * const > ItemInfo : : ItemSet ( ) const {
if ( ItemSet : : sets . count ( set ) ) {
return & ItemSet : : sets . at ( 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 ( const std : : weak_ptr < Item > it , EquipSlot slot ) {
if ( ! ( it . lock ( ) - > it - > slot & slot ) ) return ;
EquipSlot equippedSlot = GetSlotEquippedIn ( it ) ;
if ( equippedSlot ! = EquipSlot : : NONE ) UnequipItem ( equippedSlot ) ;
if ( ! GetEquip ( slot ) . expired ( ) ) UnequipItem ( slot ) ;
Inventory : : equipment [ slot ] = it ;
game - > GetPlayer ( ) - > RecalculateEquipStats ( ) ;
} ;
void Inventory : : UnequipItem ( EquipSlot slot ) {
Inventory : : equipment [ slot ] = Item : : BLANK ;
game - > GetPlayer ( ) - > RecalculateEquipStats ( ) ;
} ;
EquipSlot Inventory : : GetSlotEquippedIn ( const std : : weak_ptr < Item > it ) {
for ( int i = int ( EquipSlot : : HELMET ) ; i < = int ( EquipSlot : : RING2 ) ; i < < = 1 ) {
EquipSlot slot = EquipSlot ( i ) ;
std : : weak_ptr < Item > equip = GetEquip ( slot ) ;
if ( equip . expired ( ) ) continue ;
if ( equip . lock ( ) - > ActualName ( ) = = it . lock ( ) - > ActualName ( ) ) return slot ;
}
return EquipSlot : : NONE ;
} ;
std : : weak_ptr < Item > Inventory : : GetEquip ( EquipSlot slot ) {
return Inventory : : equipment [ slot ] ;
}
const EquipSlot Item : : GetEquipSlot ( ) const {
return it - > Slot ( ) ;
}
const EquipSlot ItemInfo : : Slot ( ) const {
return slot ;
}
void EnhancementInfo : : SetAttribute ( int enhanceLevel , ItemAttribute attribute , float 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 ( ) ) {
const ItemAttribute & attr = ItemAttribute : : Get ( key ) ;
bonuses . A ( attr ) = statInfo [ key ] . GetInt ( 0 ) ;
}
ItemSet : : AddSetBonus ( setName , pieceCount , bonuses ) ;
}
}
}
}
const Stats & ItemInfo : : GetStats ( int enhancementLevel ) const {
if ( enhancement . size ( ) < = enhancementLevel ) {
return { } ;
}
return enhancement [ enhancementLevel ] . stats ;
}
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 ( ) ;
} ;
const uint8_t Item : : EnhancementLevel ( ) const {
return enhancementLevel ;
} ;
void Item : : EnhanceItem ( ) {
enhanceFunctionPrimed . Validate ( ActualName ( ) , EnhancementLevel ( ) ) ;
if ( enhancementLevel + 1 > " Item.Item Max Enhancement Level " _I ) ERR ( " WARNING! Attempted to enhance " < < DisplayName ( ) < < " beyond the cap of " < < " Item.Item Max Enhancement Level " _I ) ;
enhancementLevel + + ;
const CraftingRequirement & consumedResources = GetEnhancementInfo ( ) [ EnhancementLevel ( ) ] . craftingRequirement ;
for ( const Item & it : consumedResources . GetItems ( ) ) {
Inventory : : RemoveItem ( it . ActualName ( ) , it . Amt ( ) ) ;
}
game - > GetPlayer ( ) - > SetMoney ( game - > GetPlayer ( ) - > GetMoney ( ) - consumedResources . GetCost ( ) ) ;
} ;
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 ) ;
std : : weak_ptr < Item > equip = Inventory : : GetEquip ( slot ) ;
if ( ISBLANK ( equip ) ) continue ;
if ( equip . lock ( ) - > ItemSet ( ) ) {
setCounts [ * equip . lock ( ) - > 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 " ;
}
}
std : : string statNumber = std : : to_string ( int ( val ) ) ;
if ( attr . ShowAsDecimal ( ) ) {
statNumber = std : : format ( " {:.2f} " , val ) ;
}
description + = std : : string ( attr . Name ( ) ) + " : " + statNumber + ( 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 ] ;
}
const uint32_t ItemInfo : : GetBuyValue ( ) const {
return buyValue ;
}
const uint32_t ItemInfo : : GetSellValue ( ) const {
return sellValue ;
}
const bool ItemInfo : : CanBeSold ( ) const {
return GetSellValue ( ) > 0 ;
}
const bool ItemInfo : : CanBePurchased ( ) const {
return GetBuyValue ( ) > 0 ;
}
const uint32_t Item : : BuyValue ( ) const {
return it - > GetBuyValue ( ) ;
}
const uint32_t Item : : SellValue ( ) const {
return it - > GetSellValue ( ) ;
}
const bool Item : : CanBeSold ( ) const {
return it - > CanBeSold ( ) ;
}
const bool Item : : CanBePurchased ( ) const {
return it - > CanBePurchased ( ) ;
}
void Item : : SetAmt ( uint32_t newAmt ) {
amt = newAmt ;
}
const std : : weak_ptr < Item > Inventory : : GetInventorySlot ( ITCategory itemCategory , size_t slot ) {
return GetItem ( get ( itemCategory ) . at ( slot ) - > ActualName ( ) ) ;
}
bool Item : : IsBlank ( std : : shared_ptr < Item > item ) {
Item : : IsBlankStaticCallCounter + + ;
return item - > IsBlank ( ) ;
} ;
bool Item : : IsBlank ( std : : weak_ptr < Item > item ) {
if ( ! item . expired ( ) ) Item : : IsBlankStaticCallCounter + + ; else return true ;
return item . lock ( ) - > IsBlank ( ) ;
} ;
const bool Item : : _IsBlank ( ) const {
Item : : IsBlankStaticCallCounter + + ;
return IsBlank ( ) ;
} ;
const bool Item : : UseDuringCast ( ) const {
return it - > UseDuringCast ( ) ;
}
const bool ItemInfo : : UseDuringCast ( ) const {
return useDuringCast ;
}
void Stats : : InitializeDamageReductionTable ( ) {
float totalReduction = 0 ;
maxDamageReductionTable [ 0 ] = 0 ;
for ( int i = 1 ; i < = 1000 ; i + + ) {
if ( i < = 10 ) {
totalReduction + = 1. _Pct ;
} else
if ( i < = 30 ) {
totalReduction + = 0.5 _Pct ;
} else
if ( i < = 70 ) {
totalReduction + = 0.25 _Pct ;
} else
if ( i < = 150 ) {
totalReduction + = 0.125 _Pct ;
} else
if ( i < = 310 ) {
totalReduction + = 0.0625 _Pct ;
} else
if ( i < = 950 ) {
totalReduction + = 0.03125 _Pct ;
} else {
totalReduction + = 0.1 _Pct ;
}
maxDamageReductionTable [ i ] = totalReduction ;
}
}
void EnhancementInfo : : SetCraftingRequirements ( const int enhanceLevel , const std : : vector < Item > & requiredItems , const uint32_t goldCost ) {
while ( craftingRequirements . size ( ) < = enhanceLevel ) {
craftingRequirements . push_back ( CraftingRequirement ( { } , 0 ) ) ;
}
craftingRequirements [ enhanceLevel ] = CraftingRequirement ( requiredItems , goldCost ) ;
}
EnhancementLevelInfo : : EnhancementLevelInfo ( const Stats & stats , const CraftingRequirement & craftingRequirement )
: stats ( stats ) , craftingRequirement ( craftingRequirement ) { }
const bool Item : : CanEnhanceItem ( ) const {
if ( ! GetEnhancementInfo ( ) . CanBeEnhanced ( ) ) return false ;
const EnhancementLevelInfo & enhanceInfo = GetEnhancementInfo ( ) [ EnhancementLevel ( ) + 1 ] ;
if ( EnhancementLevel ( ) > = " Item.Item Max Enhancement Level " _I ) return false ;
if ( game - > GetPlayer ( ) - > GetMoney ( ) < enhanceInfo . craftingRequirement . GetCost ( ) ) return false ;
for ( const Item & it : enhanceInfo . craftingRequirement . GetItems ( ) ) {
if ( Inventory : : GetItemCount ( ActualName ( ) ) < it . Amt ( ) ) {
return false ;
}
}
enhanceFunctionPrimed . enhancementLevel = EnhancementLevel ( ) ;
enhanceFunctionPrimed . item = ActualName ( ) ;
return enhanceFunctionPrimed = true ;
}
const EnhancementInfo & ItemInfo : : GetEnhancementInfo ( ) const {
return enhancement ;
}
const EnhancementInfo & Item : : GetEnhancementInfo ( ) const {
return it - > GetEnhancementInfo ( ) ;
}
const bool EnhancementInfo : : CanBeEnhanced ( ) const {
return enhancementStats . size ( ) = = " Item.Item Max Enhancement Level " _I + 1 & & craftingRequirements . size ( ) = = " Item.Item Max Enhancement Level " _I + 1 ;
} ;
const bool ItemInfo : : IsWeapon ( ) const {
return slot = = EquipSlot : : WEAPON ;
}
const bool ItemInfo : : IsArmor ( ) const {
return IsEquippable ( ) & & ! IsWeapon ( ) & & ! IsAccessory ( ) ;
}
const bool ItemInfo : : IsAccessory ( ) const {
return slot = = EquipSlot : : RING1 | | slot = = EquipSlot : : RING2 ;
}
const bool Item : : IsWeapon ( ) const {
return it - > IsWeapon ( ) ;
}
const bool Item : : IsArmor ( ) const {
return it - > IsArmor ( ) ;
}
const bool Item : : IsAccessory ( ) const {
return it - > IsAccessory ( ) ;
}