|
|
|
#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.
|
|
|
|
*/
|
|
|
|
#pragma endregion
|
|
|
|
#include "Item.h"
|
|
|
|
#include "safemap.h"
|
|
|
|
#include "DEFINES.h"
|
|
|
|
#include "Crawler.h"
|
|
|
|
#include "Menu.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<IT>>Inventory::sortedInv;
|
|
|
|
|
|
|
|
ItemInfo::ItemInfo()
|
|
|
|
:customProps({nullptr,nullptr}),img(nullptr){}
|
|
|
|
|
|
|
|
void ItemInfo::InitializeItems(){
|
|
|
|
|
|
|
|
InitializeScripts();
|
|
|
|
|
|
|
|
for(auto&key:DATA["ItemCategory"].GetKeys()){
|
|
|
|
ITEM_CATEGORIES[key.first];
|
|
|
|
Inventory::sortedInv[key.first];
|
|
|
|
Menu::inventoryListeners[key.first];
|
|
|
|
}
|
|
|
|
|
|
|
|
for(auto&key:DATA["ItemDatabase"].GetKeys()){
|
|
|
|
std::string imgPath="assets/"+"item_img_directory"_S+key.first+".png";
|
|
|
|
Renderable&img=GFX["item_img_directory"_S+key.first+".png"];
|
|
|
|
img.Load(imgPath);
|
|
|
|
|
|
|
|
std::string scriptName="",description="",category="";
|
|
|
|
for(auto&itemKey:DATA["ItemDatabase"][key.first].GetKeys()){
|
|
|
|
std::string keyName=itemKey.first;
|
|
|
|
if(keyName=="Description"){
|
|
|
|
description=DATA["ItemDatabase"][key.first][keyName].GetString();
|
|
|
|
}else
|
|
|
|
if(keyName=="ItemCategory"){
|
|
|
|
category=DATA["ItemDatabase"][key.first][keyName].GetString();
|
|
|
|
}else
|
|
|
|
if(keyName=="ItemScript"){
|
|
|
|
scriptName=DATA["ItemDatabase"][key.first][keyName].GetString();
|
|
|
|
}else{ //THis is a custom override modifier for a script. NO-OP
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(scriptName!=""){
|
|
|
|
if(!ITEM_SCRIPTS.count(scriptName)){
|
|
|
|
ERR("Could not load script "<<scriptName<<" for Item "<<key.first<<"!")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ItemInfo&it=ITEM_DATA[key.first];
|
|
|
|
it.name=key.first;
|
|
|
|
it.description=description;
|
|
|
|
it.category=category;
|
|
|
|
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["ItemDatabase"][key.first];
|
|
|
|
}
|
|
|
|
it.useFunc=scriptName;
|
|
|
|
}
|
|
|
|
|
|
|
|
ITEM_DATA.SetInitialized();
|
|
|
|
ITEM_CATEGORIES.SetInitialized();
|
|
|
|
Menu::inventoryListeners.SetInitialized();
|
|
|
|
|
|
|
|
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){
|
|
|
|
if(customProps->HasProperty(prop)) return (*customProps)[prop].GetInt();
|
|
|
|
else return (*scriptProps)[prop].GetInt();
|
|
|
|
};
|
|
|
|
float ItemProps::GetFloatProp(std::string prop){
|
|
|
|
if(customProps->HasProperty(prop)) return (*customProps)[prop].GetReal();
|
|
|
|
else return (*scriptProps)[prop].GetReal();
|
|
|
|
};
|
|
|
|
std::string ItemProps::GetStringProp(std::string prop){
|
|
|
|
if(customProps->HasProperty(prop)) return (*customProps)[prop].GetString();
|
|
|
|
else return (*scriptProps)[prop].GetString();
|
|
|
|
};
|
|
|
|
|
|
|
|
void ItemInfo::InitializeScripts(){
|
|
|
|
ITEM_SCRIPTS["Restore"]=[](Crawler*game,ItemProps props){
|
|
|
|
game->GetPlayer()->Heal(props.GetIntProp("HP Restore"));
|
|
|
|
game->GetPlayer()->Heal(game->GetPlayer()->GetMaxHealth()*props.GetIntProp("HP % Restore")/100.f);
|
|
|
|
game->GetPlayer()->RestoreMana(props.GetIntProp("MP Restore"));
|
|
|
|
game->GetPlayer()->RestoreMana(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){}
|
|
|
|
|
|
|
|
Item::Item(uint32_t amt,IT item)
|
|
|
|
:amt(amt),it(&ITEM_DATA.at(item)){}
|
|
|
|
|
|
|
|
void Inventory::AddItem(IT it,uint32_t amt){
|
|
|
|
//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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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(int 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,uint32_t amt){
|
|
|
|
//There are two places to manipulate items in (Both the sorted inventory and the actual inventory)
|
|
|
|
if(!_inventory.count(it))return false;
|
|
|
|
if(amt>=_inventory.at(it).Amt()){
|
|
|
|
int count=0;
|
|
|
|
std::vector<IT>&sortedList=sortedInv.at(_inventory.at(it).Category());
|
|
|
|
for(std::string&itemName:sortedList){
|
|
|
|
if(itemName==it)break;
|
|
|
|
count++;
|
|
|
|
}
|
|
|
|
sortedList.erase(sortedList.begin()+count);
|
|
|
|
_inventory.erase(it);
|
|
|
|
ITCategory cat=ITEM_DATA[it].category;
|
|
|
|
//Callback for GUI inventories.
|
|
|
|
Menu::InventorySlotsUpdated(cat);
|
|
|
|
return true;
|
|
|
|
}else{
|
|
|
|
_inventory.at(it).amt-=amt;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<IT>&Inventory::get(ITCategory itemCategory){
|
|
|
|
return sortedInv.at(itemCategory);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Inventory::InsertIntoSortedInv(IT item){
|
|
|
|
sortedInv.at(ITEM_DATA[item].category).push_back(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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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<IT>&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));
|
|
|
|
}
|
|
|
|
IT&item1=inv.at(slot1);
|
|
|
|
IT&item2=inv.at(slot2);
|
|
|
|
std::swap(item1,item2);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t Item::Amt(){
|
|
|
|
return amt;
|
|
|
|
};
|
|
|
|
std::string Item::Name(){
|
|
|
|
return it->Name();
|
|
|
|
};
|
|
|
|
std::string Item::Description(){
|
|
|
|
return it->Description();
|
|
|
|
};
|
|
|
|
ITCategory Item::Category(){
|
|
|
|
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<IT>itemList=get(itemCategory); //We have to make a copy here because RemoveItem() will modify the list provided by get() inline.
|
|
|
|
for(IT&item:itemList){
|
|
|
|
RemoveItem(item,GetItemCount(item));
|
|
|
|
}
|
|
|
|
}
|