Add player money functions. Implement Merchant transaction functions. Add Function Priming classes.

pull/28/head
sigonasr2 12 months ago
parent 2b3c1ea1b4
commit f1aa872717
  1. 2
      Crawler/CharacterMenuWindow.cpp
  2. 8
      Crawler/Crawler.cpp
  3. 4
      Crawler/Crawler.vcxproj
  4. 3
      Crawler/Crawler.vcxproj.filters
  5. 2
      Crawler/EquipSlotButton.h
  6. 63
      Crawler/FunctionPriming.h
  7. 2
      Crawler/InventoryConsumableWindow.cpp
  8. 111
      Crawler/Item.cpp
  9. 61
      Crawler/Item.h
  10. 4
      Crawler/ItemDrop.cpp
  11. 16
      Crawler/MenuItemButton.h
  12. 10
      Crawler/MenuItemItemButton.h
  13. 98
      Crawler/Merchant.cpp
  14. 20
      Crawler/Merchant.h
  15. 9
      Crawler/Player.cpp
  16. 4
      Crawler/Player.h
  17. 6
      Crawler/RowItemDisplay.h
  18. 2
      Crawler/Version.h
  19. 18
      Crawler/assets/config/shops/Chapter 1 Merchants.txt

@ -134,7 +134,7 @@ void Menu::InitializeCharacterMenuWindow(){
ScrollableWindowComponent*equipList=Component<ScrollableWindowComponent>(data.component->parentMenu,"Equip List");
equipList->RemoveAllComponents();
for(int counter=0;Item&it:availableEquipment){
Item&itemInvRef=Inventory::GetItem(it.Name());
Item&itemInvRef=Inventory::GetItem(it.ActualName());
auto equip=equipList->ADD("Equip Item "+std::to_string(counter),RowItemDisplay)({{2,2+counter*28.f},{120-15,28}},itemInvRef,
[](MenuFuncData data){
RowItemDisplay*comp=dynamic_cast<RowItemDisplay*>(data.component);

@ -202,6 +202,10 @@ bool Crawler::OnUserCreate(){
utils::datafile::INITIAL_SETUP_COMPLETE=true;
ValidateGameStatus(); //Checks to make sure everything has been initialized properly.
Merchant::RandomizeTravelingMerchant();
Merchant&myMerchant=Merchant::GetCurrentTravelingMerchant();
const std::vector<Item>&itemsAvailable=myMerchant.GetShopItems();
return true;
}
@ -1725,6 +1729,7 @@ void Crawler::ChangePlayerClass(Class cl){
Ability itemAbility1=player->useItem1;
Ability itemAbility2=player->useItem2;
Ability itemAbility3=player->useItem3;
uint32_t oldMoney=player->money;
switch(cl){
case WARRIOR:{
player.reset(NEW Warrior(player.get()));
@ -1750,6 +1755,7 @@ void Crawler::ChangePlayerClass(Class cl){
player->SetBaseStat(ItemAttribute::attack,DATA.GetProperty(player->GetClassName()+".BaseAtk").GetInt());
player->hpGrowthRate=float(DATA.GetProperty(player->GetClassName()+".HealthGrowthRate").GetReal());
player->atkGrowthRate=float(DATA.GetProperty(player->GetClassName()+".AtkGrowthRate").GetReal());
player->money=oldMoney;
sig::Animation::SetupPlayerAnimations();
GetPlayer()->UpdateIdleAnimation(DOWN);
GetPlayer()->SetItem1UseFunc(itemAbility1);
@ -2239,7 +2245,7 @@ void Crawler::SetLoadoutItem(int slot,std::string itemName){
bool Crawler::UseLoadoutItem(int slot){
if(slot<0||slot>loadout.size()-1)ERR("Invalid inventory slot "+std::to_string(slot)+", please choose a slot in range (0-"+std::to_string(loadout.size()-1)+").");
if(GetLoadoutItem(slot).Amt()>0){
Inventory::UseItem(loadout[slot].Name());
Inventory::UseItem(loadout[slot].ActualName());
GetLoadoutItem(slot).OnUseAction();
GetLoadoutItem(slot).amt--;
return true;

@ -316,6 +316,10 @@
</SubType>
</ClInclude>
<ClInclude Include="Error.h" />
<ClInclude Include="FunctionPriming.h">
<SubType>
</SubType>
</ClInclude>
<ClInclude Include="GameState.h" />
<ClInclude Include="Item.h" />
<ClInclude Include="ItemDrop.h">

@ -309,6 +309,9 @@
<ClInclude Include="Merchant.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="FunctionPriming.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Player.cpp">

@ -52,7 +52,7 @@ public:
inline void OnEquipStatsUpdate()override{
Item&equip=*Inventory::GetEquip(slot);
if(!equip.IsBlank()){
icon=equip.Decal();
icon=const_cast<Decal*>(equip.Decal());
SetItem(equip);
}else{
icon=nullptr;

@ -0,0 +1,63 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2018 - 2022 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
#pragma once
#include "Error.h"
#include <format>
using IT=std::string;
struct FunctionPrimingData{
bool primed=false;
std::string dependentFunction="";
inline FunctionPrimingData(std::string dependentFunction)
:dependentFunction(dependentFunction){}
};
struct MerchantFunctionPrimingData:public FunctionPrimingData{
IT item="";
uint32_t amt=0;
inline MerchantFunctionPrimingData(std::string dependentFunction)
:FunctionPrimingData(dependentFunction){}
virtual inline void Validate(IT item,uint32_t amt)const{
if(!primed)ERR(std::format("WARNING! {} should be called before running this function! Priming Requirement!",dependentFunction));
if(this->item!=item)ERR(std::format("WARNING! Primed items are not matching! {}!={}",this->item,item));
if(this->amt!=amt)ERR(std::format("WARNING! Primed amounts are not matching! {}!={}",this->amt,amt));
}
inline bool operator=(bool rhs){
return primed=rhs;
}
};

@ -75,7 +75,7 @@ void Menu::InitializeConsumableInventoryWindow(){
}
}
button->selected=data.menu.I(A::LOADOUT_SLOT);
data.game->SetLoadoutItem(button->selected,button->GetItem().Name());
data.game->SetLoadoutItem(button->selected,button->GetItem().ActualName());
return true;
})END;

@ -102,6 +102,8 @@ void ItemInfo::InitializeItems(){
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"){
@ -131,6 +133,12 @@ void ItemInfo::InitializeItems(){
}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
}
}
@ -162,6 +170,8 @@ void ItemInfo::InitializeItems(){
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!");
@ -294,7 +304,7 @@ bool Inventory::RemoveItem(IT it,ITCategory inventory,uint32_t amt){
}
int count=0;
for(Item&item:inv){
if(item.Name()==it)break;
if(item==it)break;
count++;
}
#pragma endregion
@ -376,27 +386,31 @@ bool Inventory::SwapItems(ITCategory itemCategory,uint32_t slot1,uint32_t slot2)
return true;
}
uint32_t Item::Amt(){
uint32_t Item::Amt()const{
return amt;
};
std::string Item::Name(){
const std::string&Item::ActualName()const{
if(IsBlank())return "";
return it->Name();
};
const std::string Item::DisplayName()const{
if(IsBlank())return "";
std::string name=it->Name();
std::string name=ActualName();
if(IsEquippable()&&EnhancementLevel()>0){
name+=" [+"+std::to_string(EnhancementLevel())+"]";
}
return name;
};
bool Item::IsEquippable()const{
}
const bool Item::IsEquippable()const{
return Category()=="Equipment"||Category()=="Accessories";
}
std::string Item::Description(CompactText compact){
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();
const::ItemSet*const set=ItemSet().value();
if(compact==COMPACT){
description+="\n"+set->GetSetName()+" Set - ";
}else{
@ -424,48 +438,52 @@ std::string Item::Description(CompactText compact){
const ITCategory Item::Category()const{
return it->Category();
};
::Decal*Item::Decal(){
const::Decal*const Item::Decal()const{
return it->Decal();
};
ItemScript&Item::OnUseAction(){
const ItemScript&Item::OnUseAction()const{
return it->OnUseAction();
};
std::string ItemInfo::Name(){
const std::string&ItemInfo::Name()const{
return name;
};
std::string ItemInfo::Description(){
const std::string&ItemInfo::Description()const{
return description;
};
ITCategory ItemInfo::Category(){
const ITCategory ItemInfo::Category()const{
return category;
};
::Decal*ItemInfo::Decal(){
const::Decal*const ItemInfo::Decal()const{
return img;
};
ItemScript&ItemInfo::OnUseAction(){
const ItemScript&ItemInfo::OnUseAction()const{
return ITEM_SCRIPTS.at(useFunc);
};
bool Item::IsBlank(){
const bool Item::IsBlank()const{
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...
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.Name(),itemCategory,uint32_t(itemQuantity));
RemoveItem(item.ActualName(),itemCategory,uint32_t(itemQuantity));
}
}
bool Item::operator==(const Item&rhs)const{
const bool Item::operator==(const Item&rhs)const{
return it==rhs.it;
}
const bool Item::operator==(const IT&rhs)const{
return it->Name()==rhs;
}
ItemOverlay::ItemOverlay(ItemInfo item)
:it(item),width("ItemDrop.Item Drop Scale"_F*24+4+game->GetTextSizeProp(item.Name()).x*0.5f){
xOffset=-width;
@ -487,7 +505,7 @@ void ItemOverlay::Draw(){
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->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++;
}
@ -498,11 +516,11 @@ void ItemOverlay::AddToItemOverlay(const ItemInfo&it){
std::for_each(items.begin(),items.end(),[](ItemOverlay&it){it.ResetTimer();});
}
float ItemInfo::CastTime(){
const float ItemInfo::CastTime()const{
return castTime;
}
float ItemInfo::CooldownTime(){
const float ItemInfo::CooldownTime()const{
return cooldownTime;
}
@ -510,11 +528,11 @@ void ItemOverlay::ResetTimer(){
timer=0;
}
float Item::CastTime(){
const float Item::CastTime()const{
return it->CastTime();
}
float Item::CooldownTime(){
const float Item::CooldownTime()const{
return it->CooldownTime();
}
@ -524,7 +542,7 @@ const Stats&EnhancementInfo::operator[](int level)const{
const std::optional<const ItemSet *const>ItemInfo::ItemSet()const{
if(ItemSet::sets.count(set)){
return &ItemSet::sets[set];
return &ItemSet::sets.at(set);
}
return {};
};
@ -560,17 +578,17 @@ EquipSlot Inventory::GetSlotEquippedIn(Item&it){
EquipSlot slot=EquipSlot(i);
Item*equip=GetEquip(slot);
if(equip==nullptr)continue;
if(equip->Name()==it.Name())return slot;
if(equip->ActualName()==it.ActualName())return slot;
}
return EquipSlot::NONE;
};
Item*Inventory::GetEquip(EquipSlot slot){
return Inventory::equipment[slot];
}
EquipSlot Item::GetEquipSlot(){
const EquipSlot Item::GetEquipSlot()const{
return it->Slot();
}
EquipSlot ItemInfo::Slot(){
const EquipSlot ItemInfo::Slot()const{
return slot;
}
@ -600,7 +618,7 @@ void ItemInfo::InitializeSets(){
}
}
Stats ItemInfo::GetStats(int enhancementLevel){
const Stats&ItemInfo::GetStats(int enhancementLevel)const{
if(enhancement.size()<=enhancementLevel){
return {};
}
@ -620,11 +638,11 @@ const std::optional<const ItemSet*const>Item::ItemSet()const{
return it->ItemSet();
};
uint8_t Item::EnhancementLevel()const{
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 "<<Name()<<" beyond the cap of "<<"Item.Item Max Enhancement Level"_I);
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++;
};
@ -664,4 +682,35 @@ const std::string Stats::GetStatsString(CompactText compact)const{
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;
}

@ -44,6 +44,7 @@ All rights reserved.
#include "AttributableStat.h"
#include "BitwiseEnum.h"
#include <optional>
#include "Merchant.h"
class Crawler;
class ItemInfo;
@ -129,31 +130,42 @@ class Item{
friend class Inventory;
friend class Crawler;
friend class Menu;
friend void Merchant::PurchaseItem(IT item,uint32_t amt);
friend void Merchant::SellItem(IT item,uint32_t amt);
private:
//The amount in the current item stack.
uint32_t amt;
uint8_t enhancementLevel;
ItemInfo*it;
void SetAmt(uint32_t newAmt);
public:
Item();
Item(uint32_t amt,IT item,uint8_t enhancementLevel=0);
uint32_t Amt();
std::string Name();
std::string Description(CompactText compact=COMPACT);
uint32_t Amt()const;
//Use this for places where the item name is used for item tracking. NOT for drawing. Hooks directly into item info's base item name.
const std::string&ActualName()const;
//Use for places where the item name is actually displayed. Provides modified text that shows additional information like enhancement levels.
const std::string DisplayName()const;
const std::string Description(CompactText compact=COMPACT)const;
const ITCategory Category()const;
EquipSlot GetEquipSlot();
::Decal*Decal();
const EquipSlot GetEquipSlot()const;
const::Decal*const Decal()const;
const Stats GetStats()const;
ItemScript&OnUseAction();
float CastTime();
float CooldownTime();
bool IsBlank();
uint8_t EnhancementLevel()const;
const ItemScript&OnUseAction()const;
const float CastTime()const;
const float CooldownTime()const;
const bool IsBlank()const;
const uint8_t EnhancementLevel()const;
void EnhanceItem();
static Item BLANK;
bool operator==(const Item&rhs)const;
const bool operator==(const Item&rhs)const;
const bool operator==(const IT&rhs)const;
const std::optional<const ::ItemSet *const>ItemSet()const;
bool IsEquippable()const;
const bool IsEquippable()const;
const uint32_t BuyValue()const;
const uint32_t SellValue()const;
const bool CanBeSold()const;
const bool CanBePurchased()const;
};
class Inventory{
@ -220,7 +232,8 @@ class ItemInfo{
//Custom properties for this specific item's script.
static utils::datafile NOPROPS;
ItemProps customProps;
uint32_t buyValue=0;
uint32_t sellValue=0;
private:
static void InitializeScripts();
static void InitializeSets();
@ -228,20 +241,24 @@ private:
public:
static void InitializeItems();
ItemInfo();
std::string Name();
std::string Description();
ITCategory Category();
::Decal*Decal();
const std::string&Name()const;
const std::string&Description()const;
const ITCategory Category()const;
const::Decal*const Decal()const;
/*
For the useFunc, return true if the item can be used, false otherwise.
*/
ItemScript&OnUseAction();
Stats GetStats(int enhancementLevel);
float CastTime();
float CooldownTime();
EquipSlot Slot();
const ItemScript&OnUseAction()const;
const Stats&GetStats(int enhancementLevel)const;
const float CastTime()const;
const float CooldownTime()const;
const EquipSlot Slot()const;
const std::optional<const ::ItemSet *const>ItemSet()const;
ItemInfo&operator[](const IT&item);
const uint32_t GetBuyValue()const;
const uint32_t GetSellValue()const;
const bool CanBeSold()const;
const bool CanBePurchased()const;
};
class ItemOverlay{

@ -78,9 +78,9 @@ void ItemDrop::Draw(){
yOffset=sin((game->levelTime+randomSpinOffset)*3)*0.5f;
}
game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},GFX["skill_overlay_icon_overlay.png"].Decal(),0,GFX["skill_overlay_icon_overlay.png"].Decal()->sprite->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},YELLOW);
game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},item->Decal(),0,item->Decal()->sprite->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},{255,255,255,128});
game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},const_cast<Decal*>(item->Decal()),0,item->Decal()->sprite->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},{255,255,255,128});
game->SetDecalMode(DecalMode::ADDITIVE);
game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},item->Decal(),0,item->Decal()->sprite->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},{uint8_t(abs(sin(game->levelTime*1.5)*255.f)),uint8_t(abs(sin(game->levelTime*1.5)*255.f)),uint8_t(abs(sin(game->levelTime*1.5)*255.f)),128});
game->view.DrawRotatedDecal(pos-vf2d{0,GetZ()+yOffset},const_cast<Decal*>(item->Decal()),0,item->Decal()->sprite->Size()/2,{"ItemDrop.Item Drop Scale"_F,"ItemDrop.Item Drop Scale"_F},{uint8_t(abs(sin(game->levelTime*1.5)*255.f)),uint8_t(abs(sin(game->levelTime*1.5)*255.f)),uint8_t(abs(sin(game->levelTime*1.5)*255.f)),128});
game->SetDecalMode(DecalMode::NORMAL);
}

@ -60,12 +60,12 @@ private:
public:
int selected=-1; //0-2 representing which loadout slot this item consumes. -1 means not selected.
inline MenuItemButton(geom2d::rect<float>rect,std::vector<Item>&invRef,int invIndex,MenuFunc onClick,MenuType itemDescriptionMenu,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE)
:MenuIconButton(rect,invRef.size()>invIndex?invRef[invIndex].Decal():nullptr,onClick,attributes),invRef(invRef),inventoryIndex(invIndex),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
:MenuIconButton(rect,invRef.size()>invIndex?const_cast<Decal*>(invRef[invIndex].Decal()):nullptr,onClick,attributes),invRef(invRef),inventoryIndex(invIndex),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
draggable=false;
valid=invRef.size()>invIndex;
}
inline MenuItemButton(geom2d::rect<float>rect,std::vector<Item>&invRef,int invIndex,MenuFunc onClick,MenuFunc onHover,MenuFunc onMouseOut,MenuType itemDescriptionMenu,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE)
:MenuIconButton(rect,invRef.size()>invIndex?invRef[invIndex].Decal():nullptr,onClick,attributes),invRef(invRef),inventoryIndex(invIndex),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
:MenuIconButton(rect,invRef.size()>invIndex?const_cast<Decal*>(invRef[invIndex].Decal()):nullptr,onClick,attributes),invRef(invRef),inventoryIndex(invIndex),itemDescriptionMenu(itemDescriptionMenu),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
draggable=false;
runHoverFunctions=true;
valid=invRef.size()>invIndex;
@ -73,12 +73,12 @@ public:
SetMouseOutFunc(onMouseOut);
}
inline Item&GetItem(){
return Inventory::GetItem(invRef.at(inventoryIndex).Name());
return Inventory::GetItem(invRef.at(inventoryIndex).ActualName());
}
//Returns true if the item has been consumed completely and there are 0 remaining of that type in our inventory.
inline bool UseItem(uint32_t amt=1){
if(invRef.size()<=inventoryIndex)return false;
return Inventory::UseItem(invRef.at(inventoryIndex).Name(),amt);
return Inventory::UseItem(invRef.at(inventoryIndex).ActualName(),amt);
}
inline void SetCompactDescriptions(bool compact){
if(compact)this->compact=COMPACT;
@ -98,8 +98,8 @@ protected:
std::string labelNameText;
std::string labelDescriptionText;
if(valid){
icon=invRef[inventoryIndex].Decal();
labelNameText=invRef[inventoryIndex].Name();
icon=const_cast<Decal*>(invRef[inventoryIndex].Decal());
labelNameText=invRef[inventoryIndex].DisplayName();
labelDescriptionText=invRef[inventoryIndex].Description(compact);
}else{
icon=nullptr;
@ -120,7 +120,7 @@ protected:
}
virtual inline void Update(Crawler*game)override{
MenuIconButton::Update(game);
valid=invRef.size()>inventoryIndex&&ITEM_DATA.count(invRef[inventoryIndex].Name());
valid=invRef.size()>inventoryIndex&&ITEM_DATA.count(invRef[inventoryIndex].ActualName());
if(hovered){
UpdateLabel();
@ -132,7 +132,7 @@ protected:
drawutil::DrawCrosshairDecalViewPort(window,{rect.pos,rect.size},0);
}
if(valid){
int itemQuantity=Inventory::GetItemCount(invRef.at(inventoryIndex).Name()); //Normally we'd retrieve how many of this item we have from our inventory...However Monster Loot and Stage Loot inventories are special and hold their own inventory counts...
int itemQuantity=Inventory::GetItemCount(invRef.at(inventoryIndex).ActualName()); //Normally we'd retrieve how many of this item we have from our inventory...However Monster Loot and Stage Loot inventories are special and hold their own inventory counts...
if(&invRef==&Inventory::get("Monster Loot")||&invRef==&Inventory::get("Stage Loot")){
itemQuantity=invRef.at(inventoryIndex).Amt(); //So the item quantity comes from the stack itself and not our main inventory.
}

@ -57,12 +57,12 @@ private:
CompactText compact=COMPACT;
public:
inline MenuItemItemButton(geom2d::rect<float>rect,Item&itemRef,MenuType menuDest,MenuFunc onClick,std::string itemNameLabelName,std::string itemDescriptionLabelName,IconButtonAttr attributes=IconButtonAttr::SELECTABLE)
:MenuIconButton(rect,(!itemRef.IsBlank())?itemRef.Decal():nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
:MenuIconButton(rect,(!itemRef.IsBlank())?const_cast<Decal*>(itemRef.Decal()):nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
draggable=false;
valid=!itemRef.IsBlank();
}
inline MenuItemItemButton(geom2d::rect<float>rect,Item&itemRef,MenuType menuDest,MenuFunc onClick,MenuFunc onHover,MenuFunc onMouseOut,std::string itemNameLabelName="",std::string itemDescriptionLabelName="",IconButtonAttr attributes=IconButtonAttr::SELECTABLE)
:MenuIconButton(rect,(!itemRef.IsBlank())?itemRef.Decal():nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
:MenuIconButton(rect,(!itemRef.IsBlank())?const_cast<Decal*>(itemRef.Decal()):nullptr,menuDest,onClick,attributes),itemRef(itemRef),itemNameLabelName(itemNameLabelName),itemDescriptionLabelName(itemDescriptionLabelName){
runHoverFunctions=true;
draggable=false;
valid=!itemRef.IsBlank();
@ -87,7 +87,7 @@ public:
icon=nullptr;
return;
}
icon=itemRef.get().Decal();
icon=const_cast<Decal*>(itemRef.get().Decal());
}
protected:
virtual inline void OnMouseOut()override{
@ -112,8 +112,8 @@ protected:
return;
}
icon=itemRef.get().Decal();
labelNameText=itemRef.get().Name();
icon=const_cast<Decal*>(itemRef.get().Decal());
labelNameText=itemRef.get().DisplayName();
labelDescriptionText=itemRef.get().Description(compact);
if(itemNameLabelName!=""){
Component<MenuLabel>(parentMenu,itemNameLabelName)->label=labelNameText;

@ -37,10 +37,16 @@ All rights reserved.
#pragma endregion
#include "Merchant.h"
#include "Crawler.h"
INCLUDE_game
std::map<Chapter,std::vector<Merchant>>Merchant::merchants;
MerchantFunctionPrimingData Merchant::purchaseFunctionPrimed("CanPurchaseItem()");
MerchantFunctionPrimingData Merchant::sellFunctionPrimed("CanSellItem()");
Merchant Merchant::travelingMerchant;
const Merchant&Merchant::GetRandomMerchant(Chapter chapter)const{
const Merchant&Merchant::GetRandomMerchant(Chapter chapter){
return merchants[chapter][rand()%(merchants[chapter].size()-1)];
}
@ -57,8 +63,8 @@ Merchant&Merchant::AddMerchant(Chapter chapter){
return merchants[chapter].back();
}
void Merchant::AddItem(IT item,uint8_t enhancementLevel){
shopItems.push_back(Item{1,item,enhancementLevel});
void Merchant::AddItem(IT item,uint32_t amt,uint8_t enhancementLevel){
shopItems.push_back(Item{amt,item,enhancementLevel});
}
INCLUDE_DATA
@ -82,7 +88,12 @@ void Merchant::Initialize(){
std::string itemKey=std::format("Item[{}]",itemNumber);
if(data.HasProperty(itemKey)){
IT itemName=data[itemKey].GetString();
newMerchant.AddItem(itemName);
if(data[itemKey].GetValueCount()>1){
int qty=data[itemKey].GetInt(1);
newMerchant.AddItem(itemName,qty);
}else{
newMerchant.AddItem(itemName,INFINITE);
}
}else{
ERR("Could not find item "<<itemNumber<<" in Merchant "<<merchantCount<<" of Chapter "<<chapter<<"!");
}
@ -95,4 +106,81 @@ void Merchant::Initialize(){
}
std::cout<<std::format("Added {} merchants to Chapter {}",merchantCount,chapter)<<std::endl;
}
}
}
bool Merchant::CanPurchaseItem(IT item,uint32_t amt)const{
bool itemAvailable=false;
const Item*foundItem=nullptr;
for(const Item&it:shopItems){
if(it==item&&it.Amt()>=amt&&it.CanBePurchased()){
itemAvailable=true;
foundItem=&it;
break;
}
}
purchaseFunctionPrimed.amt=amt;
purchaseFunctionPrimed.item=item;
return purchaseFunctionPrimed=
itemAvailable&&
foundItem!=nullptr&&
game->GetPlayer()->GetMoney()>=foundItem->BuyValue()*amt;
};
bool Merchant::CanSellItem(IT item,uint32_t amt)const{
ItemInfo&it=ITEM_DATA[item];
sellFunctionPrimed.amt=amt;
sellFunctionPrimed.item=item;
return sellFunctionPrimed=
it.CanBeSold()&&
Inventory::GetItemCount(item)>=amt;
};
void Merchant::PurchaseItem(IT item,uint32_t amt){
purchaseFunctionPrimed.Validate(item,amt);
uint32_t totalCost=0U;
for(Item&it:shopItems){
if(it==item){
if(it.Amt()!=INFINITE){
it.SetAmt(it.Amt()-amt);
}
totalCost=it.BuyValue()*amt;
break;
}
}
Inventory::AddItem(item,amt);
game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()-totalCost);
purchaseFunctionPrimed=false;
};
void Merchant::SellItem(IT item,uint32_t amt){
sellFunctionPrimed.Validate(item,amt);
uint32_t totalCost=0U;
bool itemFound=false;
for(Item&it:shopItems){
if(it==item){
if(it.Amt()!=INFINITE){
it.SetAmt(it.Amt()+amt);
}
itemFound=true;
break;
}
}
if(!itemFound){
AddItem(item);
}
totalCost=ITEM_DATA[item].GetSellValue()*amt;
Inventory::RemoveItem(item,amt);
game->GetPlayer()->SetMoney(game->GetPlayer()->GetMoney()+totalCost);
sellFunctionPrimed=false;
};
void Merchant::RandomizeTravelingMerchant(){
travelingMerchant=GetRandomMerchant(game->GetCurrentChapter());
};
Merchant&Merchant::GetCurrentTravelingMerchant(){
return travelingMerchant;
};

@ -35,21 +35,33 @@ Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#include "Crawler.h"
#pragma once
#include "olcPixelGameEngine.h"
#include "FunctionPriming.h"
class Item;
using Chapter=int;
using IT=std::string;
class Merchant{
public:
const Merchant&GetRandomMerchant(Chapter chapter)const;
static void RandomizeTravelingMerchant();
static Merchant&GetCurrentTravelingMerchant();
const std::string&GetDisplayName()const;
const std::vector<Item>&GetShopItems()const;
void AddItem(IT item,uint8_t enhancementLevel=0U);
void AddItem(IT item,uint32_t amt=1,uint8_t enhancementLevel=0U);
bool CanPurchaseItem(IT item,uint32_t amt=1U)const;
bool CanSellItem(IT item,uint32_t amt=1U)const;
void PurchaseItem(IT item,uint32_t amt=1U);
void SellItem(IT item,uint32_t amt=1U);
public:
static void Initialize();
static Merchant&AddMerchant(Chapter chapter);
private:
static MerchantFunctionPrimingData purchaseFunctionPrimed;
static MerchantFunctionPrimingData sellFunctionPrimed;
static Merchant travelingMerchant;
static const Merchant&GetRandomMerchant(Chapter chapter);
static std::map<Chapter,std::vector<Merchant>>merchants;
std::string displayName;
std::vector<Item>shopItems;

@ -908,4 +908,11 @@ void Player::SetBaseStat(ItemAttribute a,int val){
const std::string&ItemSet::GetSetName()const{
return name;
}
}
uint32_t Player::GetMoney()const{
return money;
};
void Player::SetMoney(uint32_t newMoney){
money=newMoney;
};

@ -115,6 +115,7 @@ private:
Ability useItem1;
Ability useItem2;
Ability useItem3;
uint32_t money=9999;
protected:
const float ATTACK_COOLDOWN="Warrior.Auto Attack.Cooldown"_F;
const float MAGIC_ATTACK_COOLDOWN="Wizard.Auto Attack.Cooldown"_F;
@ -251,6 +252,9 @@ public:
void SetItem3UseFunc(Ability a);
static InputGroup KEY_ABILITY1, KEY_ABILITY2, KEY_ABILITY3, KEY_ABILITY4, KEY_DEFENSIVE, KEY_ITEM1, KEY_ITEM2, KEY_ITEM3;
uint32_t GetMoney()const;
void SetMoney(uint32_t newMoney);
};
struct Warrior:Player{

@ -57,10 +57,10 @@ public:
MenuComponent::DrawDecal(window,focused);
float scaleFactor=(rect.size.y-4)/24;
vf2d iconSize=vf2d{scaleFactor,scaleFactor}*24.f;
window.DrawDecal(rect.pos+vf2d{2,2},itemRef.get().Decal(),{scaleFactor,scaleFactor});
window.DrawDecal(rect.pos+vf2d{2,2},const_cast<Decal*>(itemRef.get().Decal()),{scaleFactor,scaleFactor});
window.DrawRectDecal(rect.pos+vf2d{2,2},iconSize);
std::string itemName=itemRef.get().Name();
std::string itemName=itemRef.get().DisplayName();
vf2d scaledSize={std::min(1.f,(rect.size.x-6-iconSize.x)/game->GetTextSizeProp(itemName).x),1};
window.DrawShadowStringPropDecal(rect.pos+vf2d{4,4}+vf2d{iconSize.x,iconSize.y/2-4},itemName,WHITE,BLACK,scaledSize);
@ -109,7 +109,7 @@ public:
std::string labelNameText;
std::string labelDescriptionText;
if(valid){
labelNameText=itemRef.get().Name();
labelNameText=itemRef.get().DisplayName();
labelDescriptionText=itemRef.get().Description(compact);
}else{
labelNameText="";

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 0
#define VERSION_MINOR 2
#define VERSION_PATCH 1
#define VERSION_BUILD 4086
#define VERSION_BUILD 4125
#define stringify(a) stringify_(a)
#define stringify_(a) #a

@ -5,25 +5,29 @@ Merchant
Chapter1_A
{
DisplayName = Merchant
Item[1] = Minor Health Potion
Item[2] = Minor Mana Potion
# Specify items this merchant sells. Add an optional quantity as a second argument. Not specifying a second argument will give the shop infinite supply.
Item[1] = Minor Health Potion,5
Item[2] = Minor Mana Potion,5
}
Chapter1_B
{
DisplayName = Merchant
Item[1] = Minor Health Potion
# Specify items this merchant sells. Add an optional quantity as a second argument. Not specifying a second argument will give the shop infinite supply.
Item[1] = Minor Health Potion,5
}
Chapter1_C
{
DisplayName = Merchant
Item[1] = Minor Mana Potion
# Specify items this merchant sells. Add an optional quantity as a second argument. Not specifying a second argument will give the shop infinite supply.
Item[1] = Minor Mana Potion,5
}
Chapter1_D
{
DisplayName = Merchant
Item[1] = Minor Health Potion
Item[2] = Minor Mana Potion
Item[3] = Bandages
# Specify items this merchant sells. Add an optional quantity as a second argument. Not specifying a second argument will give the shop infinite supply.
Item[1] = Minor Health Potion,5
Item[2] = Minor Mana Potion,5
Item[3] = Bandages,5
}
}
}
Loading…
Cancel
Save