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.
AdventuresInLestoria/Crawler/Item.cpp

730 lines
23 KiB

#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
1 year ago
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;
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;
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::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;
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();
}
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.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;
it.buyValue=buyValue;
it.sellValue=sellValue;
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::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(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]=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);
}
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 Item::IsEquippable()const{
return Category()=="Equipment"||Category()=="Accessories";
}
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 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.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;
PlayerStats::RecalculateEquipStats();
};
void Inventory::UnequipItem(EquipSlot slot){
Inventory::equipment[slot]=Item::BLANK;
PlayerStats::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,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);
}
}
}
}
const Stats&ItemInfo::GetStats(int enhancementLevel)const{
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();
};
const uint8_t Item::EnhancementLevel()const{
return enhancementLevel;
};
void Item::EnhanceItem(){
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 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";
}
}
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];
}
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();
};