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.
 
 
FiestaCraftingCalculator/FiestaCraftingCalculator/FiestaCraftingCalculator.cpp

526 lines
20 KiB

#define OLC_PGE_APPLICATION
#include "olcPixelGameEngine.h"
#define OLC_PGEX_QUICKGUI
#include "olcPGEX_QuickGUI.h"
using namespace olc;
using namespace olc::QuickGUI;
#define PROGRAM_WIDTH 1600
int PANEL_WIDTH=620;
int PANEL_HEIGHT=240;
class FiestaCraftingCalculator : public olc::PixelGameEngine
{
struct ItemData{
std::string name;
Pixel borderCol;
std::pair<std::string,int> img;
std::pair<std::string,int> grade;
void DrawIcon(PixelGameEngine*pge,vf2d pos){
Renderable*r=FilePathToImage[img.first];
pge->DrawPartialDecal(pos,{32,32},r->Decal(),vi2d{img.second%8,img.second/8}*32,{32,32});
if(grade.first!="-"){
pge->DrawPartialDecal(pos,FiestaCraftingCalculator::GradeIcons.Decal(),vi2d{grade.second%8,grade.second/8}*32,{32,32});
}
pge->DrawRectDecal(pos,{32,32},borderCol);
}
};
static std::map<std::string,ItemData>IDToName;
static std::map<std::string,Renderable*>FilePathToImage;
class Recipe{
public:
std::string ProduceIndex;
std::string DisplayName;
std::string Product;
int amt;
std::vector<std::pair<std::string,int>>requiredItems;
int expGained;
int requiredExp;
Recipe(std::vector<std::string>&data){
ProduceIndex=data[1];
DisplayName=data[2];
Product=data[3];
if(IDToName.find(Product)==IDToName.end()){
IDToName[Product]=ItemData();
}
amt=stoi(data[4]);
for(int i=5;i<=20;i+=2){
if(data[i].size()>1){
requiredItems.push_back({data[i],stoi(data[i+1])});
if(IDToName.find(data[i])==IDToName.end()){
IDToName[data[i]]=ItemData();
}
}
}
expGained=stoi(data[22]);
requiredExp=stoi(data[24]);
}
};
static std::vector<Recipe>PotionProdRecipes;
static std::vector<Recipe>StoneProdRecipes;
static std::vector<Recipe>ScrollProdRecipes;
static std::vector<Recipe>CompRecipes;
static std::vector<Recipe>DecompRecipes;
static std::map<int,Recipe*> PotionProdRecipeLink;
static std::map<int,Recipe*> StoneProdRecipeLink;
static std::map<int,Recipe*> ScrollProdRecipeLink;
static std::map<int,Recipe*> CompRecipeLink;
static std::map<int,Recipe*> DecompRecipeLink;
class Calculator{
Manager manager;
vf2d displayPos={10,10};
ImageCheckBox*PotionProd;
ImageCheckBox*StoneProd;
ImageCheckBox*ScrollProd;
ImageCheckBox*CompProd;
ImageCheckBox*DecompProd;
CustomButton*CloseButton;
ListBox*RecipeList;
TextBox*Amount;
std::string DisplayProdText;
std::string SelectedProdText;
std::vector<std::string> recipeItems;
Recipe*selectedItem;
static bool disabledAllCalculators;
public:
Calculator(){
PotionProd=new ImageCheckBox(manager,FiestaCraftingCalculator::ProductionIcons,false,displayPos+vi2d{10,10},{40,40},{192,128},{32,32});
StoneProd=new ImageCheckBox(manager,FiestaCraftingCalculator::ProductionIcons,false,displayPos+vi2d{55,10},{40,40},{0,160},{32,32});
ScrollProd=new ImageCheckBox(manager,FiestaCraftingCalculator::ProductionIcons,false,displayPos+vi2d{100,10},{40,40},{224,128},{32,32});
CompProd=new ImageCheckBox(manager,FiestaCraftingCalculator::ProductionIcons,false,displayPos+vi2d{145,10},{40,40},{96,160},{32,32});
DecompProd=new ImageCheckBox(manager,FiestaCraftingCalculator::ProductionIcons,false,displayPos+vi2d{190,10},{40,40},{64,160},{32,32});
CloseButton=new CustomButton(manager,FiestaCraftingCalculator::CloseIcon,FiestaCraftingCalculator::CloseBackIcon,displayPos+vf2d{PANEL_WIDTH-30.f,0},{26,26},{0,0},{26,26});
RecipeList=new ListBox(manager,recipeItems,displayPos+vi2d{10,90},{300,140},16);
Amount=new TextBox(manager,"1",displayPos+vi2d{356,10},{68,24},{2,2});
Amount->bVisible=false;
RecipeList->bVisible=false;
}
void OnTextEntryComplete(const std::string& sText){
//Amount->m_bTextEdit=false;
}
bool Update(FiestaCraftingCalculator*pge,int panelNumber){
displayPos=vf2d{float(10+panelNumber%2*PANEL_WIDTH),float(10+panelNumber/2*PANEL_HEIGHT)};
PotionProd->vPos=displayPos+vi2d{10,10};
StoneProd->vPos=displayPos+vi2d{55,10};
ScrollProd->vPos=displayPos+vi2d{100,10};
CompProd->vPos=displayPos+vi2d{145,10};
DecompProd->vPos=displayPos+vi2d{190,10};
CloseButton->vPos=displayPos+vf2d{PANEL_WIDTH-30.f,0};
RecipeList->vPos=displayPos+vi2d{10,90};
RecipeList->m_pSlider->vPosMin=displayPos+vi2d{10,90}+vf2d{300 - manager.fGrabRad - 1,manager.fGrabRad + 1 };
RecipeList->m_pSlider->vPosMax=displayPos+vi2d{10,90}+vf2d{300 - manager.fGrabRad - 1,140-manager.fGrabRad - 1 };
Amount->vPos=displayPos+vi2d{356,10};
manager.Update(pge);
DisplayProdText=SelectedProdText;
if(CloseButton->bPressed){
return false;
}
if(PotionProd->bPressed){
StoneProd->bChecked=ScrollProd->bChecked=CompProd->bChecked=DecompProd->bChecked=false;
SelectedProdText="Potion Production";
RecipeList->bVisible=Amount->bVisible=true;
recipeItems.clear();
RecipeList->m_pSlider->fValue=0;
RecipeList->nSelectedItem=RecipeList->nPreviouslySelectedItem=0;
for(Recipe&r:PotionProdRecipes){
recipeItems.push_back(IDToName[r.Product].name);
}
selectedItem=&PotionProdRecipes[0];
Amount->sText="1";
}
if(PotionProd->bHovered){
DisplayProdText="Potion Production";
}
if(StoneProd->bPressed){
PotionProd->bChecked=ScrollProd->bChecked=CompProd->bChecked=DecompProd->bChecked=false;
SelectedProdText="Stone Production";
RecipeList->bVisible=Amount->bVisible=true;
recipeItems.clear();
RecipeList->m_pSlider->fValue=0;
RecipeList->nSelectedItem=RecipeList->nPreviouslySelectedItem=0;
for(Recipe&r:StoneProdRecipes){
recipeItems.push_back(IDToName[r.Product].name);
}
selectedItem=&StoneProdRecipes[0];
Amount->sText="1";
}
if(StoneProd->bHovered){
DisplayProdText="Stone Production";
}
if(ScrollProd->bPressed){
StoneProd->bChecked=PotionProd->bChecked=CompProd->bChecked=DecompProd->bChecked=false;
SelectedProdText="Scroll Production";
RecipeList->bVisible=Amount->bVisible=true;
recipeItems.clear();
RecipeList->m_pSlider->fValue=0;
RecipeList->nSelectedItem=RecipeList->nPreviouslySelectedItem=0;
for(Recipe&r:ScrollProdRecipes){
recipeItems.push_back(IDToName[r.Product].name);
}
selectedItem=&ScrollProdRecipes[0];
Amount->sText="1";
}
if(ScrollProd->bHovered){
DisplayProdText="Scroll Production";
}
if(CompProd->bPressed){
StoneProd->bChecked=ScrollProd->bChecked=PotionProd->bChecked=DecompProd->bChecked=false;
SelectedProdText="Material Composition";
RecipeList->bVisible=Amount->bVisible=true;
recipeItems.clear();
RecipeList->m_pSlider->fValue=0;
RecipeList->nSelectedItem=RecipeList->nPreviouslySelectedItem=0;
for(Recipe&r:CompRecipes){
recipeItems.push_back(IDToName[r.Product].name);
}
selectedItem=&CompRecipes[0];
Amount->sText="1";
}
if(CompProd->bHovered){
DisplayProdText="Material Composition";
}
if(DecompProd->bPressed){
StoneProd->bChecked=ScrollProd->bChecked=CompProd->bChecked=PotionProd->bChecked=false;
SelectedProdText="Material Decomposition";
RecipeList->bVisible=Amount->bVisible=true;
recipeItems.clear();
RecipeList->m_pSlider->fValue=0;
RecipeList->nSelectedItem=RecipeList->nPreviouslySelectedItem=0;
for(Recipe&r:DecompRecipes){
recipeItems.push_back(IDToName[r.Product].name);
}
selectedItem=&DecompRecipes[0];
Amount->sText="1";
}
if(DecompProd->bHovered){
DisplayProdText="Material Decomposition";
}
if(RecipeList->bSelectionChanged){
if(PotionProd->bChecked){
selectedItem=&PotionProdRecipes[RecipeList->nSelectedItem];
}
if(StoneProd->bChecked){
selectedItem=&StoneProdRecipes[RecipeList->nSelectedItem];
}
if(ScrollProd->bChecked){
selectedItem=&ScrollProdRecipes[RecipeList->nSelectedItem];
}
if(CompProd->bChecked){
selectedItem=&CompRecipes[RecipeList->nSelectedItem];
}
if(DecompProd->bChecked){
selectedItem=&DecompRecipes[RecipeList->nSelectedItem];
}
}
pge->GradientFillRectDecal(displayPos-vf2d{2,2},{float(PANEL_WIDTH),float(PANEL_HEIGHT)},{49, 32, 61,216},{0,0,0,216},{0,0,0,216},{49, 32, 61,216});
pge->DrawRectDecal(displayPos-vf2d{1,1},{float(PANEL_WIDTH),float(PANEL_HEIGHT)},{131, 90, 161});
pge->DrawStringPropDecal(displayPos+vf2d{10,10+54},DisplayProdText,WHITE,{2,2});
if(RecipeList->bVisible){
ItemData data = IDToName[selectedItem->Product];
int quantityRequested=1;
if(Amount->sText.size()>0&&Amount->sText[0]>='0'&&Amount->sText[0]<='9'){
quantityRequested=stoi(Amount->sText);
}
data.DrawIcon(pge,displayPos+vf2d{320,40});
float stringWidth=pge->GetTextSizeProp(data.name).x*2;
if(stringWidth>PANEL_WIDTH-356){
float scale=(PANEL_WIDTH-356)/stringWidth;
pge->DrawStringPropDecal(displayPos+vf2d{356,40},data.name,WHITE,{2*scale,2});
} else {
pge->DrawStringPropDecal(displayPos+vf2d{356,40},data.name,WHITE,{2,2});
}
pge->DrawStringPropDecal(displayPos+vf2d{264,16},"Crafts: ",WHITE,{1.75,1.75});
pge->DrawStringPropDecal(displayPos+vf2d{360,58},"x "+std::to_string(selectedItem->amt*quantityRequested),WHITE,{1.75,1.75});
pge->DrawStringDecal(displayPos+vf2d{320,80},"Requires:");
float yOffset=0;
for(std::pair<std::string,int>&item:selectedItem->requiredItems){
data = IDToName[item.first];
data.DrawIcon(pge,displayPos+vf2d{320,92+yOffset});
float stringWidth=pge->GetTextSizeProp(data.name).x*2;
if(stringWidth>PANEL_WIDTH-356){
float scale=(PANEL_WIDTH-356)/stringWidth;
pge->DrawStringPropDecal(displayPos+vf2d{356,92+yOffset},data.name,WHITE,{2*scale,2});
} else {
pge->DrawStringPropDecal(displayPos+vf2d{356,92+yOffset},data.name,WHITE,{2,2});
}
pge->DrawStringPropDecal(displayPos+vf2d{360,110+yOffset},"x "+std::to_string(item.second*quantityRequested),WHITE,{1.75,1.75});
yOffset+=36;
}
if(Amount->m_bTextEdit){
for(Calculator*c:calculators){
if(c!=this){
c->Amount->Enable(false);
}
}
disabledAllCalculators=true;
} else
if (disabledAllCalculators){
for(Calculator*c:calculators){
c->Amount->Enable(true);
}
}
if(Amount->sText.size()>4){
Amount->sText=Amount->sText.substr(0,4);
pge->sTextEntryString=Amount->sText;
pge->nTextEntryCursor=4;
}
}
manager.DrawDecal(pge);
return true;
}
};
Renderable Karen;
public:
FiestaCraftingCalculator()
{
sAppName = "Fiesta Crafting Calculator";
}
static std::vector<Calculator*>calculators;
Manager manager;
Button*AddButton;
static Renderable CloseIcon,CloseBackIcon,ProductionIcons,CollectibleIcons,CollectibleIcons2,GradeIcons,PlusIcon,PlusBackIcon;
public:
std::string slurp(std::ifstream& in) {
std::ostringstream sstr;
sstr << in.rdbuf();
return sstr.str();
}
void OnTextEntryComplete(const std::string& sText)override{
for(auto it=calculators.begin();it!=calculators.end();++it){
Calculator*c=*it;
c->OnTextEntryComplete(sText);
}
}
bool OnUserCreate() override
{
// Called once at the start, so create things here
std::ifstream file=std::ifstream("assets/Produce.txt");
std::string fileContents=slurp(file);
fileContents+='\n';//HACK ALERT! Our parser does not deal with data until a '\n' character occurs. If the file has no \n on the last line, we won't process it. This hack fixes that.
std::string column="";
std::vector<std::string>data;
//HACK ALERT! If the number of recipes for any of these exceeds 300, a reallocation occurs and all pointers move...
//We need to keep this number high enough to avoid ever reallocating.
StoneProdRecipes.reserve(300);
PotionProdRecipes.reserve(300);
ScrollProdRecipes.reserve(300);
CompRecipes.reserve(300);
DecompRecipes.reserve(300);
for(int i=0;i<fileContents.size();i++){
if(fileContents[i]=='\t'){
data.push_back(column);
column="";
} else
if(fileContents[i]=='\n'){
data.push_back(column);
column="";
if(data.size()!=25){
std::cout<<"Data size was "<<data.size()<<"! Expected 25 columns..."<<std::endl;
for(int i=0;i<data.size();i++){
std::cout<<"Col "<<i+1<<":"<<data[i]<<std::endl;
}
return false;
} else {
Recipe r(data);
switch(stoi(data[21])){
case 0:{
StoneProdRecipes.push_back(r);
StoneProdRecipeLink[stoi(data[0])]=&(*std::prev(StoneProdRecipes.end()));
}break;
case 1:{
PotionProdRecipes.push_back(r);
PotionProdRecipeLink[stoi(data[0])]=&(*std::prev(PotionProdRecipes.end()));
}break;
case 2:{
ScrollProdRecipes.push_back(r);
ScrollProdRecipeLink[stoi(data[0])]=&(*std::prev(ScrollProdRecipes.end()));
}break;
//For some reason 3 is skipped in the game's data.
case 4:{
CompRecipes.push_back(r);
CompRecipeLink[stoi(data[0])]=&(*std::prev(CompRecipes.end()));
}break;
case 5:{
DecompRecipes.push_back(r);
DecompRecipeLink[stoi(data[0])]=&(*std::prev(DecompRecipes.end()));
}break;
}
}
data.clear();
} else {
column+=fileContents[i];
}
}
file=std::ifstream("assets/ItemViewinfo.txt");
fileContents=slurp(file);
fileContents+='\n';//HACK ALERT! Our parser does not deal with data until a '\n' character occurs. If the file has no \n on the last line, we won't process it. This hack fixes that.
column="";
data.clear();
for(int i=0;i<fileContents.size();i++){
if(fileContents[i]=='\t'){
data.push_back(column);
column="";
} else
if(fileContents[i]=='\n'){
data.push_back(column);
column="";
if(data.size()!=27){
std::cout<<"Data size was "<<data.size()<<"! Expected 27 columns..."<<std::endl;
for(int i=0;i<data.size();i++){
std::cout<<"Col "<<i+1<<":"<<data[i]<<std::endl;
}
return false;
} else {
//Recipe r(data);
std::string product=data[1];
if(IDToName.find(product)!=IDToName.end()){
IDToName[product]=ItemData{product,{uint8_t(stoi(data[8])),uint8_t(stoi(data[9])),uint8_t(stoi(data[10]))},{data[3],stoi(data[2])},{data[5],stoi(data[4])}};
goto quitLoop;
}
}
quitLoop:
data.clear();
} else {
column+=fileContents[i];
}
}
file=std::ifstream("assets/ItemInfo.txt");
fileContents=slurp(file);
fileContents+='\n';//HACK ALERT! Our parser does not deal with data until a '\n' character occurs. If the file has no \n on the last line, we won't process it. This hack fixes that.
column="";
data.clear();
for(int i=0;i<fileContents.size();i++){
if(fileContents[i]=='\t'){
data.push_back(column);
column="";
} else
if(fileContents[i]=='\n'){
data.push_back(column);
column="";
if(data.size()!=57){
std::cout<<"Data size was "<<data.size()<<"! Expected 57 columns..."<<std::endl;
for(int i=0;i<data.size();i++){
std::cout<<"Col "<<i+1<<":"<<data[i]<<std::endl;
}
return false;
} else {
//Recipe r(data);
std::string product=data[1];
if(IDToName.find(product)!=IDToName.end()){
IDToName[product].name=data[2];
}
}
data.clear();
} else {
column+=fileContents[i];
}
}
std::sort(StoneProdRecipes.begin(),StoneProdRecipes.end(),[](Recipe&r1,Recipe&r2){return r1.requiredExp<r2.requiredExp;});
std::sort(PotionProdRecipes.begin(),PotionProdRecipes.end(),[](Recipe&r1,Recipe&r2){return r1.requiredExp<r2.requiredExp;});
std::sort(ScrollProdRecipes.begin(),ScrollProdRecipes.end(),[](Recipe&r1,Recipe&r2){return r1.requiredExp<r2.requiredExp;});
std::sort(CompRecipes.begin(),CompRecipes.end(),[](Recipe&r1,Recipe&r2){return r1.requiredExp<r2.requiredExp;});
std::sort(DecompRecipes.begin(),DecompRecipes.end(),[](Recipe&r1,Recipe&r2){return r1.requiredExp<r2.requiredExp;});
std::cout<<"Loaded:"<<std::endl;
std::cout<<" "<<PotionProdRecipes.size()<<" Potion Recipes"<<std::endl;
std::cout<<" "<<StoneProdRecipes.size()<<" Stone Recipes"<<std::endl;
std::cout<<" "<<ScrollProdRecipes.size()<<" Scroll Recipes"<<std::endl;
std::cout<<" "<<CompRecipes.size()<<" Composition Recipes"<<std::endl;
std::cout<<" "<<DecompRecipes.size()<<" Decomposition Recipes"<<std::endl;
ProductionIcons.Load("assets/Prdct000.png",nullptr,true);
Karen.Load("assets/AdlF_Karen.png",nullptr,true);
CloseIcon.Load("assets/close_button.png",nullptr,true);
CloseBackIcon.Load("assets/button_mask.png",nullptr,true);
ProductionIcons.Load("assets/production_icons.png",nullptr,true);
CollectibleIcons.Load("assets/Cllct000.png",nullptr,true);
CollectibleIcons2.Load("assets/QstItem000.png",nullptr,true);
GradeIcons.Load("assets/ItemGrade.png",nullptr,true);
PlusIcon.Load("assets/plus_button.png",nullptr,true);
PlusBackIcon.Load("assets/button_plus_mask.png",nullptr,true);
calculators.push_back(new Calculator());
FilePathToImage["Cllct000"]=&CollectibleIcons;
FilePathToImage["QstItem000"]=&CollectibleIcons2;
FilePathToImage["Prdct000"]=&ProductionIcons;
FilePathToImage["ItemGrade"]=&GradeIcons;
AddButton=new Button(manager,"Add Panel",{PROGRAM_WIDTH-256,float(ScreenHeight()-Karen.Sprite()->height-240)},{172,36},{2,2});
return true;
}
void DrawCenteredStringDecal(const vf2d&pos,const std::string&text,Pixel col,const vf2d&scale){
DrawStringDecal(pos-GetTextSize(text)*scale/2,text,col,scale);
}
bool OnUserUpdate(float fElapsedTime) override
{
/*if(GetKey(SPACE).bPressed){
calculators.push_back(new Calculator());
}*/
DrawCenteredStringDecal({float(ScreenWidth()-175),64},"Fiesta Online",WHITE,{2,3});
DrawCenteredStringDecal({float(ScreenWidth()-175),64+24},"Crafting Calculator",WHITE,{2,3});
DrawCenteredStringDecal({float(ScreenWidth()-175),64+24*4},"Made using the",WHITE,{2,3});
DrawCenteredStringDecal({float(ScreenWidth()-175),64+24*5},"olcPixelGameEngine",WHITE,{2,3});
DrawDecal({float(ScreenWidth()-Karen.Sprite()->width),float(ScreenHeight()-Karen.Sprite()->height)},Karen.Decal());
int panelNumber=0;
manager.Update(this);
if(AddButton->bPressed){
calculators.push_back(new Calculator());
}
for(auto it=calculators.begin();it!=calculators.end();++it){
Calculator*c=*it;
if (!c->Update(this,panelNumber)){
it=calculators.erase(it);
if(it==calculators.end()){
break;
}
}
++panelNumber;
}
manager.DrawDecal(this);
return true;
}
};
Renderable FiestaCraftingCalculator::CloseIcon,FiestaCraftingCalculator::CloseBackIcon,FiestaCraftingCalculator::ProductionIcons,FiestaCraftingCalculator::CollectibleIcons,FiestaCraftingCalculator::CollectibleIcons2,FiestaCraftingCalculator::GradeIcons,FiestaCraftingCalculator::PlusIcon,FiestaCraftingCalculator::PlusBackIcon;
std::vector<FiestaCraftingCalculator::Recipe> FiestaCraftingCalculator::PotionProdRecipes;
std::vector<FiestaCraftingCalculator::Recipe> FiestaCraftingCalculator::StoneProdRecipes;
std::vector<FiestaCraftingCalculator::Recipe> FiestaCraftingCalculator::ScrollProdRecipes;
std::vector<FiestaCraftingCalculator::Recipe> FiestaCraftingCalculator::CompRecipes;
std::vector<FiestaCraftingCalculator::Recipe> FiestaCraftingCalculator::DecompRecipes;
std::map<int,FiestaCraftingCalculator::Recipe*> FiestaCraftingCalculator::PotionProdRecipeLink;
std::map<int,FiestaCraftingCalculator::Recipe*> FiestaCraftingCalculator::StoneProdRecipeLink;
std::map<int,FiestaCraftingCalculator::Recipe*> FiestaCraftingCalculator::ScrollProdRecipeLink;
std::map<int,FiestaCraftingCalculator::Recipe*> FiestaCraftingCalculator::CompRecipeLink;
std::map<int,FiestaCraftingCalculator::Recipe*> FiestaCraftingCalculator::DecompRecipeLink;
std::map<std::string,Renderable*>FiestaCraftingCalculator::FilePathToImage;
std::map<std::string,FiestaCraftingCalculator::ItemData>FiestaCraftingCalculator::IDToName;
std::vector<FiestaCraftingCalculator::Calculator*>FiestaCraftingCalculator::calculators;
bool FiestaCraftingCalculator::Calculator::disabledAllCalculators=false;
int main()
{
FiestaCraftingCalculator calculator;
if (calculator.Construct(PROGRAM_WIDTH, 980, 1, 1))
calculator.Start();
return 0;
}