1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-17 20:58:07 +02:00

Merge pull request #4463 from SoundSSGood/IMarket-refactoring

IMarket refactoring
This commit is contained in:
Ivan Savenko 2024-08-26 22:48:23 +03:00 committed by GitHub
commit 11e8b04ac5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
63 changed files with 388 additions and 303 deletions

View File

@ -1491,7 +1491,7 @@ void AIGateway::tryRealize(Goals::Trade & g) //trade
//TODO trade only as much as needed
if (toGive) //don't try to sell 0 resources
{
cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, GameResID(g.resID), toGive);
cb->trade(m->getObjInstanceID(), EMarketMode::RESOURCE_RESOURCE, res, GameResID(g.resID), toGive);
acquiredResources = static_cast<int>(toGet * (it->resVal / toGive));
logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, acquiredResources, g.resID, obj->getObjectName());
}

View File

@ -196,7 +196,7 @@ bool BuildingManager::getBuildingOptions(const CGTownInstance * t)
return true;
//workaround for mantis #2696 - build capitol with separate algorithm if it is available
if(vstd::contains(t->builtBuildings, BuildingID::CITY_HALL) && getMaxPossibleGoldBuilding(t) == BuildingID::CAPITOL)
if(t->hasBuilt(BuildingID::CITY_HALL) && getMaxPossibleGoldBuilding(t) == BuildingID::CAPITOL)
{
if(tryBuildNextStructure(t, capitolAndRequirements))
return true;

View File

@ -2130,7 +2130,7 @@ void VCAI::tryRealize(Goals::Trade & g) //trade
//TODO trade only as much as needed
if (toGive) //don't try to sell 0 resources
{
cb->trade(m, EMarketMode::RESOURCE_RESOURCE, res, GameResID(g.resID), toGive);
cb->trade(m->getObjInstanceID(), EMarketMode::RESOURCE_RESOURCE, res, GameResID(g.resID), toGive);
acquiredResources = static_cast<int>(toGet * (it->resVal / toGive));
logAi->debug("Traded %d of %s for %d of %s at %s", toGive, res, acquiredResources, g.resID, obj->getObjectName());
}

View File

@ -266,15 +266,15 @@ void CCallback::buyArtifact(const CGHeroInstance *hero, ArtifactID aid)
sendRequest(&pack);
}
void CCallback::trade(const IMarket * market, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero)
void CCallback::trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero)
{
trade(market, mode, std::vector(1, id1), std::vector(1, id2), std::vector(1, val1), hero);
trade(marketId, mode, std::vector(1, id1), std::vector(1, id2), std::vector(1, val1), hero);
}
void CCallback::trade(const IMarket * market, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero)
void CCallback::trade(const ObjectInstanceID marketId, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero)
{
TradeOnMarketplace pack;
pack.marketId = dynamic_cast<const CGObjectInstance *>(market)->id;
pack.marketId = marketId;
pack.heroId = hero ? hero->id : ObjectInstanceID();
pack.mode = mode;
pack.r1 = id1;

View File

@ -34,7 +34,6 @@ class IBattleEventsReceiver;
class IGameEventsReceiver;
struct ArtifactLocation;
class BattleStateInfoForRetreat;
class IMarket;
VCMI_LIB_NAMESPACE_END
@ -81,8 +80,8 @@ public:
virtual bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE)=0; //if newID==-1 then best possible upgrade will be made
virtual void swapGarrisonHero(const CGTownInstance *town)=0;
virtual void trade(const IMarket * market, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero = nullptr)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce
virtual void trade(const IMarket * market, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr)=0;
virtual void trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero)=0; //mode==0: sell val1 units of id1 resource for id2 resiurce
virtual void trade(const ObjectInstanceID marketId, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero)=0;
virtual int selectionMade(int selection, QueryID queryID) =0;
virtual int sendQueryReply(std::optional<int32_t> reply, QueryID queryID) =0;
@ -190,8 +189,8 @@ public:
void endTurn() override;
void swapGarrisonHero(const CGTownInstance *town) override;
void buyArtifact(const CGHeroInstance *hero, ArtifactID aid) override;
void trade(const IMarket * market, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero = nullptr) override;
void trade(const IMarket * market, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr) override;
void trade(const ObjectInstanceID marketId, EMarketMode mode, TradeItemSell id1, TradeItemBuy id2, ui32 val1, const CGHeroInstance * hero = nullptr) override;
void trade(const ObjectInstanceID marketId, EMarketMode mode, const std::vector<TradeItemSell> & id1, const std::vector<TradeItemBuy> & id2, const std::vector<ui32> & val1, const CGHeroInstance * hero = nullptr) override;
void setFormation(const CGHeroInstance * hero, EArmyFormation mode) override;
void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero=HeroTypeID::NONE) override;
void save(const std::string &fname) override;

View File

@ -1633,14 +1633,14 @@ void CPlayerInterface::battleNewRoundFirst(const BattleID & battleID)
battleInt->newRoundFirst();
}
void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID)
void CPlayerInterface::showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID)
{
EVENT_HANDLER_CALLED_BY_CLIENT;
auto onWindowClosed = [this, queryID](){
cb->selectionMade(0, queryID);
};
if (market->allowsTrade(EMarketMode::ARTIFACT_EXP) && dynamic_cast<const CGArtifactsAltar*>(market) == nullptr)
if (market->allowsTrade(EMarketMode::ARTIFACT_EXP) && market->getArtifactsStorage() == nullptr)
{
// compatibility check, safe to remove for 1.6
// 1.4 saves loaded in 1.5 will not be able to visit Altar of Sacrifice due to Altar now requiring different map object class
@ -1655,8 +1655,17 @@ void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInsta
GH.windows().createAndPushWindow<CMarketWindow>(market, visitor, onWindowClosed, EMarketMode::CREATURE_EXP);
else if(market->allowsTrade(EMarketMode::CREATURE_UNDEAD))
GH.windows().createAndPushWindow<CTransformerWindow>(market, visitor, onWindowClosed);
else if(!market->availableModes().empty())
GH.windows().createAndPushWindow<CMarketWindow>(market, visitor, onWindowClosed, market->availableModes().front());
else if (!market->availableModes().empty())
for(auto mode = EMarketMode::RESOURCE_RESOURCE; mode != EMarketMode::MARKET_AFTER_LAST_PLACEHOLDER; mode = vstd::next(mode, 1))
{
if(vstd::contains(market->availableModes(), mode))
{
GH.windows().createAndPushWindow<CMarketWindow>(market, visitor, onWindowClosed, mode);
break;
}
}
else
onWindowClosed();
}
void CPlayerInterface::showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID)

View File

@ -120,7 +120,7 @@ protected: // Call-ins from server, should not be called directly, but only via
void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID) override;
void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID) override;
void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor) override;
void advmapSpellCast(const CGHeroInstance * caster, SpellID spellID) override; //called when a hero casts a spell

View File

@ -999,7 +999,7 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
case EOpenWindowMode::UNIVERSITY_WINDOW:
{
//displays University window (when hero enters University on adventure map)
const auto * market = dynamic_cast<const IMarket*>(cl.getObj(ObjectInstanceID(pack.object)));
const auto * market = cl.getMarket(ObjectInstanceID(pack.object));
const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
callInterfaceIfPresent(cl, hero->tempOwner, &IGameEventsReceiver::showUniversityWindow, market, hero, pack.queryID);
}
@ -1009,7 +1009,7 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
//displays Thieves' Guild window (when hero enters Den of Thieves)
const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
const auto *market = dynamic_cast<const IMarket*>(obj);
const auto market = cl.getMarket(pack.object);
callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showMarketWindow, market, hero, pack.queryID);
}
break;

View File

@ -464,7 +464,7 @@ void CInteractableTownTooltip::init(const CGTownInstance * town)
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
for(auto & town : towns)
{
if(town->id == townId && town->builtBuildings.count(BuildingID::TAVERN))
if(town->id == townId && town->hasBuilt(BuildingID::TAVERN))
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
}
}, [town]{
@ -476,7 +476,7 @@ void CInteractableTownTooltip::init(const CGTownInstance * town)
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
for(auto & town : towns)
{
if(town->builtBuildings.count(BuildingID::MARKETPLACE))
if(town->hasBuilt(BuildingID::MARKETPLACE))
{
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
return;

View File

@ -24,16 +24,15 @@
#include "../../../lib/networkPacks/ArtifactLocation.h"
#include "../../../lib/texts/CGeneralTextHandler.h"
#include "../../../lib/mapObjects/CGHeroInstance.h"
#include "../../../lib/mapObjects/CGMarket.h"
#include "../../../lib/mapObjects/IMarket.h"
CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance * hero)
: CMarketBase(market, hero)
{
OBJECT_CONSTRUCTION;
assert(dynamic_cast<const CGArtifactsAltar*>(market));
auto altarObj = dynamic_cast<const CGArtifactsAltar*>(market);
altarArtifacts = altarObj;
assert(market->getArtifactsStorage());
altarArtifactsStorage = market->getArtifactsStorage();
deal = std::make_shared<CButton>(Point(269, 520), AnimationPath::builtin("ALTSACR.DEF"),
CGI->generaltexth->zelp[585], [this]() {CAltarArtifacts::makeDeal(); }, EShortcut::MARKET_DEAL);
@ -51,7 +50,7 @@ CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance *
// Hero's artifacts
heroArts = std::make_shared<CArtifactsOfHeroAltar>(Point(-365, -11));
heroArts->setHero(hero);
heroArts->altarId = altarObj->id;
heroArts->altarId = market->getObjInstanceID();
// Altar
offerTradePanel = std::make_shared<ArtifactsAltarPanel>([this](const std::shared_ptr<CTradeableItem> & altarSlot)
@ -104,7 +103,7 @@ void CAltarArtifacts::makeDeal()
{
positions.push_back(artInst->getId());
}
LOCPLINT->cb->trade(market, EMarketMode::ARTIFACT_EXP, positions, std::vector<TradeItemBuy>(), std::vector<ui32>(), hero);
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::ARTIFACT_EXP, positions, std::vector<TradeItemBuy>(), std::vector<ui32>(), hero);
deselect();
}
@ -125,7 +124,7 @@ std::shared_ptr<CArtifactsOfHeroAltar> CAltarArtifacts::getAOHset() const
void CAltarArtifacts::updateAltarSlots()
{
assert(altarArtifacts->artifactsInBackpack.size() <= GameConstants::ALTAR_ARTIFACTS_SLOTS);
assert(altarArtifactsStorage->artifactsInBackpack.size() <= GameConstants::ALTAR_ARTIFACTS_SLOTS);
assert(tradeSlotsMap.size() <= GameConstants::ALTAR_ARTIFACTS_SLOTS);
auto tradeSlotsMapNewArts = tradeSlotsMap;
@ -146,12 +145,12 @@ void CAltarArtifacts::updateAltarSlots()
for(auto & tradeSlot : tradeSlotsMapNewArts)
{
assert(tradeSlot.first->id == -1);
assert(altarArtifacts->getArtPos(tradeSlot.second) != ArtifactPosition::PRE_FIRST);
assert(altarArtifactsStorage->getArtPos(tradeSlot.second) != ArtifactPosition::PRE_FIRST);
tradeSlot.first->setID(tradeSlot.second->getTypeId().num);
tradeSlot.first->subtitle->setText(std::to_string(calcExpCost(tradeSlot.second->getTypeId())));
}
auto newArtsFromBulkMove = altarArtifacts->artifactsInBackpack;
auto newArtsFromBulkMove = altarArtifactsStorage->artifactsInBackpack;
for(const auto & [altarSlot, art] : tradeSlotsMap)
{
newArtsFromBulkMove.erase(std::remove_if(newArtsFromBulkMove.begin(), newArtsFromBulkMove.end(), [artForRemove = art](auto & slotInfo)
@ -179,7 +178,7 @@ void CAltarArtifacts::putBackArtifacts()
{
// TODO: If the backpack capacity limit is enabled, artifacts may remain on the altar.
// Perhaps should be erased in CGameHandler::objectVisitEnded if id of visited object will be available
if(!altarArtifacts->artifactsInBackpack.empty())
if(!altarArtifactsStorage->artifactsInBackpack.empty())
LOCPLINT->cb->bulkMoveArtifacts(heroArts->altarId, heroArts->getHero()->id, false, true, true);
}
@ -200,7 +199,7 @@ void CAltarArtifacts::onSlotClickPressed(const std::shared_ptr<CTradeableItem> &
if(const auto pickedArtInst = heroArts->getPickedArtifact())
{
if(pickedArtInst->canBePutAt(altarArtifacts))
if(pickedArtInst->canBePutAt(altarArtifactsStorage.get()))
{
if(pickedArtInst->artType->isTradable())
{
@ -221,7 +220,7 @@ void CAltarArtifacts::onSlotClickPressed(const std::shared_ptr<CTradeableItem> &
else if(altarSlot->id != -1)
{
assert(tradeSlotsMap.at(altarSlot));
const auto slot = altarArtifacts->getArtPos(tradeSlotsMap.at(altarSlot));
const auto slot = altarArtifactsStorage->getArtPos(tradeSlotsMap.at(altarSlot));
assert(slot != ArtifactPosition::PRE_FIRST);
LOCPLINT->cb->swapArtifacts(ArtifactLocation(heroArts->altarId, slot),
ArtifactLocation(hero->id, GH.isKeyboardCtrlDown() ? ArtifactPosition::FIRST_AVAILABLE : ArtifactPosition::TRANSITION_POS));

View File

@ -26,7 +26,7 @@ public:
void putBackArtifacts();
private:
const CArtifactSet * altarArtifacts;
std::shared_ptr<CArtifactSet> altarArtifactsStorage;
std::shared_ptr<CButton> sacrificeBackpackButton;
std::shared_ptr<CArtifactsOfHeroAltar> heroArts;
std::map<std::shared_ptr<CTradeableItem>, const CArtifactInstance*> tradeSlotsMap;

View File

@ -23,7 +23,7 @@
#include "../../../lib/texts/CGeneralTextHandler.h"
#include "../../../lib/mapObjects/CGHeroInstance.h"
#include "../../../lib/mapObjects/CGMarket.h"
#include "../../../lib/mapObjects/IMarket.h"
CAltarCreatures::CAltarCreatures(const IMarket * market, const CGHeroInstance * hero)
: CMarketBase(market, hero)
@ -157,7 +157,7 @@ void CAltarCreatures::makeDeal()
}
}
LOCPLINT->cb->trade(market, EMarketMode::CREATURE_EXP, ids, {}, toSacrifice, hero);
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::CREATURE_EXP, ids, {}, toSacrifice, hero);
for(int & units : unitsOnAltar)
units = 0;

View File

@ -11,7 +11,6 @@
#include "StdInc.h"
#include "CArtifactsBuying.h"
#include "../../gui/CGuiHandler.h"
#include "../../gui/Shortcut.h"
#include "../../widgets/Buttons.h"
#include "../../widgets/TextControls.h"
@ -21,24 +20,16 @@
#include "../../../CCallback.h"
#include "../../../lib/entities/building/CBuilding.h"
#include "../../../lib/entities/faction/CTownHandler.h"
#include "../../../lib/mapObjects/CGHeroInstance.h"
#include "../../../lib/mapObjects/CGMarket.h"
#include "../../../lib/mapObjects/CGTownInstance.h"
#include "../../../lib/mapObjects/IMarket.h"
#include "../../../lib/texts/CGeneralTextHandler.h"
CArtifactsBuying::CArtifactsBuying(const IMarket * market, const CGHeroInstance * hero)
CArtifactsBuying::CArtifactsBuying(const IMarket * market, const CGHeroInstance * hero, const std::string & title)
: CMarketBase(market, hero)
, CResourcesSelling([this](const std::shared_ptr<CTradeableItem> & heroSlot){CArtifactsBuying::onSlotClickPressed(heroSlot, bidTradePanel);})
{
OBJECT_CONSTRUCTION;
std::string title;
if(auto townMarket = dynamic_cast<const CGTownInstance*>(market))
title = (*CGI->townh)[townMarket->getFaction()]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated();
else
title = CGI->generaltexth->allTexts[349];
labels.emplace_back(std::make_shared<CLabel>(titlePos.x, titlePos.y, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, title));
deal = std::make_shared<CButton>(dealButtonPos, AnimationPath::builtin("TPMRKB.DEF"),
CGI->generaltexth->zelp[595], [this](){CArtifactsBuying::makeDeal();}, EShortcut::MARKET_DEAL);
@ -77,7 +68,7 @@ void CArtifactsBuying::makeDeal()
{
if(ArtifactID(offerTradePanel->getSelectedItemId()).toArtifact()->canBePutAt(hero))
{
LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_ARTIFACT, GameResID(bidTradePanel->getSelectedItemId()),
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::RESOURCE_ARTIFACT, GameResID(bidTradePanel->getSelectedItemId()),
ArtifactID(offerTradePanel->getSelectedItemId()), offerQty, hero);
CMarketTraderText::makeDeal();
deselect();

View File

@ -14,7 +14,7 @@
class CArtifactsBuying : public CResourcesSelling, public CMarketTraderText
{
public:
CArtifactsBuying(const IMarket * market, const CGHeroInstance * hero);
CArtifactsBuying(const IMarket * market, const CGHeroInstance * hero, const std::string & title);
void deselect() override;
void makeDeal() override;

View File

@ -22,14 +22,11 @@
#include "../../../CCallback.h"
#include "../../../lib/CArtifactInstance.h"
#include "../../../lib/entities/building/CBuilding.h"
#include "../../../lib/entities/faction/CTownHandler.h"
#include "../../../lib/mapObjects/CGHeroInstance.h"
#include "../../../lib/mapObjects/CGMarket.h"
#include "../../../lib/mapObjects/CGTownInstance.h"
#include "../../../lib/mapObjects/IMarket.h"
#include "../../../lib/texts/CGeneralTextHandler.h"
CArtifactsSelling::CArtifactsSelling(const IMarket * market, const CGHeroInstance * hero)
CArtifactsSelling::CArtifactsSelling(const IMarket * market, const CGHeroInstance * hero, const std::string & title)
: CMarketBase(market, hero)
, CResourcesBuying(
[this](const std::shared_ptr<CTradeableItem> & resSlot){CArtifactsSelling::onSlotClickPressed(resSlot, offerTradePanel);},
@ -37,12 +34,6 @@ CArtifactsSelling::CArtifactsSelling(const IMarket * market, const CGHeroInstanc
{
OBJECT_CONSTRUCTION;
std::string title;
if(const auto townMarket = dynamic_cast<const CGTownInstance*>(market))
title = (*CGI->townh)[townMarket->getFaction()]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated();
else if(const auto mapMarket = dynamic_cast<const CGMarket*>(market))
title = mapMarket->title;
labels.emplace_back(std::make_shared<CLabel>(titlePos.x, titlePos.y, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, title));
labels.push_back(std::make_shared<CLabel>(155, 56, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, boost::str(boost::format(CGI->generaltexth->allTexts[271]) % hero->getNameTranslated())));
deal = std::make_shared<CButton>(dealButtonPos, AnimationPath::builtin("TPMRKB.DEF"),
@ -87,7 +78,8 @@ void CArtifactsSelling::makeDeal()
{
const auto art = hero->getArt(selectedHeroSlot);
assert(art);
LOCPLINT->cb->trade(market, EMarketMode::ARTIFACT_RESOURCE, art->getId(), GameResID(offerTradePanel->getSelectedItemId()), offerQty, hero);
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::ARTIFACT_RESOURCE, art->getId(),
GameResID(offerTradePanel->getSelectedItemId()), offerQty, hero);
CMarketTraderText::makeDeal();
}

View File

@ -15,7 +15,7 @@
class CArtifactsSelling : public CResourcesBuying, public CMarketTraderText
{
public:
CArtifactsSelling(const IMarket * market, const CGHeroInstance * hero);
CArtifactsSelling(const IMarket * market, const CGHeroInstance * hero, const std::string & title);
void deselect() override;
void makeDeal() override;
void updateShowcases() override;

View File

@ -23,7 +23,7 @@
#include "../../../lib/texts/CGeneralTextHandler.h"
#include "../../../lib/mapObjects/CGHeroInstance.h"
#include "../../../lib/mapObjects/CGMarket.h"
#include "../../../lib/mapObjects/IMarket.h"
CFreelancerGuild::CFreelancerGuild(const IMarket * market, const CGHeroInstance * hero)
: CMarketBase(market, hero)
@ -69,7 +69,7 @@ void CFreelancerGuild::makeDeal()
{
if(auto toTrade = offerSlider->getValue(); toTrade != 0)
{
LOCPLINT->cb->trade(market, EMarketMode::CREATURE_RESOURCE, SlotID(bidTradePanel->highlightedSlot->serial), GameResID(offerTradePanel->getSelectedItemId()), bidQty * toTrade, hero);
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::CREATURE_RESOURCE, SlotID(bidTradePanel->highlightedSlot->serial), GameResID(offerTradePanel->getSelectedItemId()), bidQty * toTrade, hero);
CMarketTraderText::makeDeal();
deselect();
}

View File

@ -22,7 +22,7 @@
#include "../../../CCallback.h"
#include "../../../lib/texts/CGeneralTextHandler.h"
#include "../../../lib/mapObjects/CGMarket.h"
#include "../../../lib/mapObjects/IMarket.h"
CMarketResources::CMarketResources(const IMarket * market, const CGHeroInstance * hero)
: CMarketBase(market, hero)
@ -60,7 +60,7 @@ void CMarketResources::makeDeal()
{
if(auto toTrade = offerSlider->getValue(); toTrade != 0)
{
LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_RESOURCE, GameResID(bidTradePanel->getSelectedItemId()),
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::RESOURCE_RESOURCE, GameResID(bidTradePanel->getSelectedItemId()),
GameResID(offerTradePanel->highlightedSlot->id), bidQty * toTrade, hero);
CMarketTraderText::makeDeal();
deselect();

View File

@ -22,6 +22,7 @@
#include "../../../CCallback.h"
#include "../../../lib/texts/CGeneralTextHandler.h"
#include "../../../lib/mapObjects/IMarket.h"
#include "../../../lib/texts/MetaString.h"
CTransferResources::CTransferResources(const IMarket * market, const CGHeroInstance * hero)
@ -63,7 +64,7 @@ void CTransferResources::makeDeal()
{
if(auto toTrade = offerSlider->getValue(); toTrade != 0)
{
LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_PLAYER, GameResID(bidTradePanel->getSelectedItemId()),
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::RESOURCE_PLAYER, GameResID(bidTradePanel->getSelectedItemId()),
PlayerColor(offerTradePanel->getSelectedItemId()), toTrade, hero);
CMarketTraderText::makeDeal();
deselect();

View File

@ -586,9 +586,9 @@ void CCastleBuildings::recreate()
//Generate buildings list
auto buildingsCopy = town->builtBuildings;// a bit modified copy of built buildings
auto buildingsCopy = town->getBuildings();// a bit modified copy of built buildings
if(vstd::contains(town->builtBuildings, BuildingID::SHIPYARD))
if(town->hasBuilt(BuildingID::SHIPYARD))
{
auto bayPos = town->bestLocation();
if(!bayPos.valid())
@ -996,7 +996,7 @@ void CCastleBuildings::enterMagesGuild()
void CCastleBuildings::enterTownHall()
{
if(town->visitingHero && town->visitingHero->hasArt(ArtifactID::GRAIL) &&
!vstd::contains(town->builtBuildings, BuildingID::GRAIL)) //hero has grail, but town does not have it
!town->hasBuilt(BuildingID::GRAIL)) //hero has grail, but town does not have it
{
if(!vstd::contains(town->forbiddenBuildings, BuildingID::GRAIL))
{
@ -1033,7 +1033,7 @@ void CCastleBuildings::enterAnyThievesGuild()
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
for(auto & ownedTown : towns)
{
if(ownedTown->builtBuildings.count(BuildingID::TAVERN))
if(ownedTown->hasBuilt(BuildingID::TAVERN))
{
LOCPLINT->showThievesGuildWindow(ownedTown);
return;
@ -1059,7 +1059,7 @@ void CCastleBuildings::enterBank()
void CCastleBuildings::enterAnyMarket()
{
if(town->builtBuildings.count(BuildingID::MARKETPLACE))
if(town->hasBuilt(BuildingID::MARKETPLACE))
{
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
return;
@ -1068,7 +1068,7 @@ void CCastleBuildings::enterAnyMarket()
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
for(auto & town : towns)
{
if(town->builtBuildings.count(BuildingID::MARKETPLACE))
if(town->hasBuilt(BuildingID::MARKETPLACE))
{
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
return;
@ -1385,7 +1385,7 @@ void CCastleInterface::recreateIcons()
fastMarket = std::make_shared<LRClickableArea>(Rect(163, 410, 64, 42), [this]() { builds->enterAnyMarket(); });
fastTavern = std::make_shared<LRClickableArea>(Rect(15, 387, 58, 64), [&]()
{
if(town->builtBuildings.count(BuildingID::TAVERN))
if(town->hasBuilt(BuildingID::TAVERN))
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
}, [this]{
if(!town->town->faction->getDescriptionTranslated().empty())
@ -1563,7 +1563,7 @@ CHallInterface::CHallInterface(const CGTownInstance * Town):
}
const CBuilding * current = town->town->buildings.at(buildingID);
if(vstd::contains(town->builtBuildings, buildingID))
if(town->hasBuilt(buildingID))
{
building = current;
}
@ -1776,7 +1776,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
{
BuildingID dwelling = BuildingID::getDwellingFromLevel(i, 1);
if(vstd::contains(town->builtBuildings, dwelling))
if(town->hasBuilt(dwelling))
buildingID = BuildingID(BuildingID::getDwellingFromLevel(i, 1));
else
buildingID = BuildingID(BuildingID::getDwellingFromLevel(i, 0));
@ -1841,7 +1841,7 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *
buildingIcon = std::make_shared<CAnimImage>(town->town->clientInfo.buildingsIcons, getMyBuilding()->bid, 0, 4, 21);
buildingName = std::make_shared<CLabel>(78, 101, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, getMyBuilding()->getNameTranslated(), 152);
if(vstd::contains(town->builtBuildings, getMyBuilding()->bid))
if(town->hasBuilt(getMyBuilding()->bid))
{
ui32 available = town->creatures[level].first;
std::string availableText = CGI->generaltexth->allTexts[217]+ std::to_string(available);

View File

@ -822,7 +822,7 @@ CTownItem::CTownItem(const CGTownInstance * Town)
fastTavern = std::make_shared<LRClickableArea>(Rect(5, 6, 58, 64), [&]()
{
if(town->builtBuildings.count(BuildingID::TAVERN))
if(town->hasBuilt(BuildingID::TAVERN))
LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
}, [&]{
if(!town->town->faction->getDescriptionTranslated().empty())
@ -833,7 +833,7 @@ CTownItem::CTownItem(const CGTownInstance * Town)
std::vector<const CGTownInstance*> towns = LOCPLINT->cb->getTownsInfo(true);
for(auto & town : towns)
{
if(town->builtBuildings.count(BuildingID::MARKETPLACE))
if(town->hasBuilt(BuildingID::MARKETPLACE))
{
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
return;

View File

@ -27,11 +27,14 @@
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../../lib/entities/building/CBuilding.h"
#include "../../lib/texts/CGeneralTextHandler.h"
#include "../../lib/mapObjects/CGTownInstance.h"
#include "../../lib/mapObjects/CGMarket.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../CCallback.h"
CMarketWindow::CMarketWindow(const IMarket * market, const CGHeroInstance * hero, const std::function<void()> & onWindowClosed, EMarketMode mode)
: CWindowObject(PLAYER_COLORED)
, windowClosedCallback(onWindowClosed)
@ -111,6 +114,11 @@ void CMarketWindow::createChangeModeButtons(EMarketMode currentMode, const IMark
if(!market->allowsTrade(modeButton))
return false;
if(currentMode == EMarketMode::ARTIFACT_EXP && modeButton != EMarketMode::CREATURE_EXP)
return false;
if(currentMode == EMarketMode::CREATURE_EXP && modeButton != EMarketMode::ARTIFACT_EXP)
return false;
if(modeButton == EMarketMode::RESOURCE_RESOURCE || modeButton == EMarketMode::RESOURCE_PLAYER)
{
if(const auto town = dynamic_cast<const CGTownInstance*>(market))
@ -175,12 +183,28 @@ void CMarketWindow::initWidgetInternals(const EMarketMode mode, const std::pair<
redraw();
}
std::string CMarketWindow::getMarketTitle(const ObjectInstanceID marketId, const EMarketMode mode) const
{
assert(LOCPLINT->cb->getMarket(marketId));
assert(vstd::contains(LOCPLINT->cb->getMarket(marketId)->availableModes(), mode));
if(const auto town = LOCPLINT->cb->getTown(marketId))
{
for(const auto & buildingId : town->getBuildings())
{
if(const auto building = town->town->buildings.at(buildingId); vstd::contains(building->marketModes, mode))
return building->getNameTranslated();
}
}
return LOCPLINT->cb->getObj(marketId)->getObjectName();
}
void CMarketWindow::createArtifactsBuying(const IMarket * market, const CGHeroInstance * hero)
{
OBJECT_CONSTRUCTION;
background = createBg(ImagePath::builtin("TPMRKABS.bmp"), PLAYER_COLORED);
marketWidget = std::make_shared<CArtifactsBuying>(market, hero);
marketWidget = std::make_shared<CArtifactsBuying>(market, hero, getMarketTitle(market->getObjInstanceID(), EMarketMode::RESOURCE_ARTIFACT));
initWidgetInternals(EMarketMode::RESOURCE_ARTIFACT, CGI->generaltexth->zelp[600]);
}
@ -192,13 +216,13 @@ void CMarketWindow::createArtifactsSelling(const IMarket * market, const CGHeroI
// Create image that copies part of background containing slot MISC_1 into position of slot MISC_5
artSlotBack = std::make_shared<CPicture>(background->getSurface(), Rect(20, 187, 47, 47), 0, 0);
artSlotBack->moveTo(pos.topLeft() + Point(18, 339));
auto artsSellingMarket = std::make_shared<CArtifactsSelling>(market, hero);
auto artsSellingMarket = std::make_shared<CArtifactsSelling>(market, hero, getMarketTitle(market->getObjInstanceID(), EMarketMode::ARTIFACT_RESOURCE));
artSets.clear();
const auto heroArts = artsSellingMarket->getAOHset();
heroArts->showPopupCallback = [this, heroArts](CArtPlace & artPlace, const Point & cursorPosition){showArifactInfo(*heroArts, artPlace, cursorPosition);};
addSet(heroArts);
marketWidget = artsSellingMarket;
initWidgetInternals(EMarketMode::ARTIFACT_RESOURCE, CGI->generaltexth->zelp[600]);
initWidgetInternals(EMarketMode::ARTIFACT_RESOURCE, CGI->generaltexth->zelp[600]);
}
void CMarketWindow::createMarketResources(const IMarket * market, const CGHeroInstance * hero)
@ -233,10 +257,10 @@ void CMarketWindow::createAltarArtifacts(const IMarket * market, const CGHeroIns
OBJECT_CONSTRUCTION;
background = createBg(ImagePath::builtin("ALTRART2.bmp"), PLAYER_COLORED);
auto altarArtifacts = std::make_shared<CAltarArtifacts>(market, hero);
marketWidget = altarArtifacts;
auto altarArtifactsStorage = std::make_shared<CAltarArtifacts>(market, hero);
marketWidget = altarArtifactsStorage;
artSets.clear();
const auto heroArts = altarArtifacts->getAOHset();
const auto heroArts = altarArtifactsStorage->getAOHset();
heroArts->clickPressedCallback = [this, heroArts](const CArtPlace & artPlace, const Point & cursorPosition)
{
clickPressedOnArtPlace(heroArts->getHero(), artPlace.slot, true, true, false);
@ -252,7 +276,7 @@ void CMarketWindow::createAltarArtifacts(const IMarket * market, const CGHeroIns
addSet(heroArts);
initWidgetInternals(EMarketMode::ARTIFACT_EXP, CGI->generaltexth->zelp[568]);
updateExperience();
quitButton->addCallback([altarArtifacts](){altarArtifacts->putBackArtifacts();});
quitButton->addCallback([altarArtifactsStorage](){altarArtifactsStorage->putBackArtifacts();});
}
void CMarketWindow::createAltarCreatures(const IMarket * market, const CGHeroInstance * hero)

View File

@ -27,6 +27,7 @@ public:
private:
void createChangeModeButtons(EMarketMode currentMode, const IMarket * market, const CGHeroInstance * hero);
void initWidgetInternals(const EMarketMode mode, const std::pair<std::string, std::string> & quitButtonHelpContainer);
std::string getMarketTitle(const ObjectInstanceID marketId, const EMarketMode mode) const;
void createArtifactsBuying(const IMarket * market, const CGHeroInstance * hero);
void createArtifactsSelling(const IMarket * market, const CGHeroInstance * hero);

View File

@ -819,7 +819,7 @@ void CTransformerWindow::makeDeal()
for(auto & elem : items)
{
if(!elem->left)
LOCPLINT->cb->trade(market, EMarketMode::CREATURE_UNDEAD, SlotID(elem->id), {}, {}, hero);
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::CREATURE_UNDEAD, SlotID(elem->id), {}, {}, hero);
}
}
@ -1005,7 +1005,7 @@ void CUniversityWindow::updateSecondarySkills()
void CUniversityWindow::makeDeal(SecondarySkill skill)
{
LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_SKILL, GameResID(GameResID::GOLD), skill, 1, hero);
LOCPLINT->cb->trade(market->getObjInstanceID(), EMarketMode::RESOURCE_SKILL, GameResID(GameResID::GOLD), skill, 1, hero);
}
CUnivConfirmWindow::CUnivConfirmWindow(CUniversityWindow * owner_, SecondarySkill SKILL, bool available)

View File

@ -166,7 +166,7 @@
"townHall": { },
"cityHall": { },
"capitol": { },
"marketplace": { },
"marketplace": { "marketModes" : ["resource-resource", "resource-player"] },
"resourceSilo": { "produce": { "ore": 1, "wood": 1 } },
"blacksmith": { },

View File

@ -171,11 +171,11 @@
"townHall": { },
"cityHall": { },
"capitol": { },
"marketplace": { },
"marketplace": { "marketModes" : ["resource-resource", "resource-player"] },
"resourceSilo": { "produce": { "mercury": 1 } },
"blacksmith": { },
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ], "marketModes" : ["resource-artifact", "artifact-resource"] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
"ship": { "id" : 20, "upgrades" : "shipyard" },

View File

@ -167,11 +167,11 @@
"townHall": { },
"cityHall": { },
"capitol": { },
"marketplace": { },
"marketplace": { "marketModes" : ["resource-resource", "resource-player"] },
"resourceSilo": { "produce": { "sulfur": 1 } },
"blacksmith": { },
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ], "marketModes" : ["resource-artifact", "artifact-resource"] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
"special2": {

View File

@ -165,7 +165,7 @@
"townHall": { },
"cityHall": { },
"capitol": { },
"marketplace": { },
"marketplace": { "marketModes" : ["resource-resource", "resource-player"] },
"resourceSilo": { "produce": { "wood": 1, "ore": 1 } },
"blacksmith": { },

View File

@ -167,7 +167,7 @@
"townHall": { },
"cityHall": { },
"capitol": { },
"marketplace": { },
"marketplace": { "marketModes" : ["resource-resource", "resource-player"] },
"resourceSilo": { "produce": { "mercury": 1 } },
"blacksmith": { },

View File

@ -172,7 +172,7 @@
"townHall": { },
"cityHall": { },
"capitol": { },
"marketplace": { },
"marketplace": { "marketModes" : ["resource-resource", "resource-player"] },
"resourceSilo": { "produce": { "ore": 1, "wood": 1 } },
"blacksmith": { },
@ -182,7 +182,7 @@
"ship": { "id" : 20, "upgrades" : "shipyard" },
"special2": { "requires" : [ "mageGuild1" ],
"bonuses": [ { "type": "UNDEAD_RAISE_PERCENTAGE", "val": 10, "propagator": "PLAYER_PROPAGATOR" } ] },
"special3": { "type" : "creatureTransformer", "requires" : [ "dwellingLvl1" ] },
"special3": { "type" : "creatureTransformer", "requires" : [ "dwellingLvl1" ], "marketModes" : ["creature-undead"] },
"grail": { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 },
"bonuses": [ { "type": "UNDEAD_RAISE_PERCENTAGE", "val": 20, "propagator": "PLAYER_PROPAGATOR" } ] },

View File

@ -170,7 +170,7 @@
"townHall": { },
"cityHall": { },
"capitol": { },
"marketplace": { },
"marketplace": { "marketModes" : ["resource-resource", "resource-player"] },
"resourceSilo": { "produce": { "crystal": 1 } },
"blacksmith": { },

View File

@ -162,14 +162,14 @@
"townHall": { },
"cityHall": { },
"capitol": { },
"marketplace": { },
"marketplace": { "marketModes" : ["resource-resource", "resource-player"] },
"resourceSilo": { "produce": { "ore": 1, "wood": 1 } },
"blacksmith": { },
"special1": { "type" : "escapeTunnel", "requires" : [ "fort" ] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl1" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
"special2": { "type" : "freelancersGuild", "requires" : [ "marketplace" ] },
"special2": { "type" : "freelancersGuild", "requires" : [ "marketplace" ], "marketModes" : ["creature-resource"] },
"special3": { "type" : "ballistaYard", "requires" : [ "blacksmith" ] },
"special4": {
"requires" : [ "fort" ],

View File

@ -165,11 +165,11 @@
"townHall": { },
"cityHall": { },
"capitol": { },
"marketplace": { },
"marketplace": { "marketModes" : ["resource-resource", "resource-player"] },
"resourceSilo": { "produce" : { "gems": 1 } },
"blacksmith": { },
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ], "marketModes" : ["resource-artifact", "artifact-resource"] },
"horde1": { "id" : 18, "upgrades" : "dwellingLvl2" },
"horde1Upgr": { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" },
"special2": { "height" : "high", "requires" : [ "fort" ] },

View File

@ -97,6 +97,11 @@
"type" : "array",
"description" : "Bonuses that are provided by this building in any town where this building has been built. Only affects town itself (including siege), to propagate effect to player or team please use bonus propagators",
"items" : { "$ref" : "bonus.json" }
},
"marketModes" : {
"type" : "array",
"enum" : [ "resource-resource", "resource-player", "creature-resource", "resource-artifact", "artifact-resource", "artifact-experience", "creature-experience", "creature-undead", "resource-skill"],
"description" : "List of modes available in this market"
}
}
}

View File

@ -171,6 +171,9 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config
// If set to true, this building will replace all bonuses from base building, leaving only bonuses defined by this building"
"upgradeReplacesBonuses" : false,
// If the building is a market, it requires market mode.
"marketModes" : [ "resource-resource", "resource-player" ],
}
```
@ -217,7 +220,6 @@ Following HotA buildings can be used as unique building for a town. Functionalit
#### Custom buildings
In addition to above, it is possible to use same format as [Rewardable](../Map_Objects/Rewardable.md) map objects for town buildings. In order to do that, configuration of a rewardable object must be placed into `configuration` json node in building config.
```
### Town Structure node
@ -248,3 +250,17 @@ In addition to above, it is possible to use same format as [Rewardable](../Map_O
"hidden" : false
}
```
#### Markets in towns
Market buildings require list of available [modes](../Map_Objects/Market.md)
##### Marketplace
```jsonc
"marketplace": { "marketModes" : ["resource-resource", "resource-player"] },
```
##### Artifact merchant
```jsonc
"special1": { "type" : "artifactMerchant", "requires" : [ "marketplace" ], "marketModes" : ["resource-artifact", "artifact-resource"] },
```

View File

@ -174,6 +174,15 @@ const CGTownInstance* CGameInfoCallback::getTown(ObjectInstanceID objid) const
return nullptr;
}
const IMarket * CGameInfoCallback::getMarket(ObjectInstanceID objid) const
{
const CGObjectInstance * obj = getObj(objid, false);
if(obj)
return dynamic_cast<const IMarket*>(obj);
else
return nullptr;
}
void CGameInfoCallback::fillUpgradeInfo(const CArmedInstance *obj, SlotID stackPos, UpgradeInfo &out) const
{
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);

View File

@ -48,6 +48,7 @@ class CGHeroInstance;
class CGDwelling;
class CGTeleport;
class CGTownInstance;
class IMarket;
class DLL_LINKAGE IGameInfoCallback : boost::noncopyable
{
@ -189,6 +190,7 @@ public:
virtual std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
virtual const CGObjectInstance * getTopObj (int3 pos) const;
virtual PlayerColor getOwner(ObjectInstanceID heroID) const;
virtual const IMarket * getMarket(ObjectInstanceID objid) const;
//map
virtual int3 guardingCreaturePosition (int3 pos) const;

View File

@ -287,18 +287,16 @@ CArtifactSet * CNonConstInfoCallback::getArtSet(const ArtifactLocation & loc)
return hero;
}
}
else if(auto market = getMarket(loc.artHolder))
{
if(auto artSet = market->getArtifactsStorage())
return artSet.get();
}
else if(auto army = getArmyInstance(loc.artHolder))
{
return army->getStackPtr(loc.creature.value());
}
else if(auto market = dynamic_cast<CGArtifactsAltar*>(getObjInstance(loc.artHolder)))
{
return market;
}
else
{
return nullptr;
}
return nullptr;
}
bool IGameCallback::isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, const CGHeroInstance *hero)

View File

@ -109,7 +109,7 @@ public:
virtual void showPuzzleMap(){};
virtual void viewWorldMap(){};
virtual void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID){};
virtual void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID){};
virtual void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID){};
virtual void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor){};
virtual void showThievesGuildWindow (const CGObjectInstance * obj){};

View File

@ -34,6 +34,7 @@ public:
TResources resources;
TResources produce;
TRequired requirements;
std::set<EMarketMode> marketModes;
BuildingID bid; //structure ID
BuildingID upgrade; /// indicates that building "upgrade" can be improved by this, -1 = empty
@ -85,7 +86,7 @@ public:
STRONG_INLINE
bool IsTradeBuilding() const
{
return bid == BuildingID::MARKETPLACE || subId == BuildingSubID::ARTIFACT_MERCHANT || subId == BuildingSubID::FREELANCERS_GUILD;
return !marketModes.empty();
}
void addNewBonus(const std::shared_ptr<Bonus> & b, BonusList & bonusList) const;

View File

@ -342,6 +342,11 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
ret->upgrade = BuildingID::NONE;
ret->town->buildings[ret->bid] = ret;
for(const auto & element : source["marketModes"].Vector())
{
if(MappedKeys::MARKET_NAMES_TO_TYPES.count(element.String()))
ret->marketModes.insert(MappedKeys::MARKET_NAMES_TO_TYPES.at(element.String()));
}
registerObject(source.getModScope(), ret->town->getBuildingScope(), ret->identifier, ret->bid.getNum());
}

View File

@ -808,12 +808,12 @@ void CGameState::initTowns()
constexpr std::array hordes = { BuildingID::HORDE_PLACEHOLDER1, BuildingID::HORDE_PLACEHOLDER2, BuildingID::HORDE_PLACEHOLDER3, BuildingID::HORDE_PLACEHOLDER4, BuildingID::HORDE_PLACEHOLDER5, BuildingID::HORDE_PLACEHOLDER6, BuildingID::HORDE_PLACEHOLDER7, BuildingID::HORDE_PLACEHOLDER8 };
//init buildings
if(vstd::contains(vti->builtBuildings, BuildingID::DEFAULT)) //give standard set of buildings
if(vti->hasBuilt(BuildingID::DEFAULT)) //give standard set of buildings
{
vti->builtBuildings.erase(BuildingID::DEFAULT);
vti->builtBuildings.insert(BuildingID::VILLAGE_HALL);
vti->removeBuilding(BuildingID::DEFAULT);
vti->addBuilding(BuildingID::VILLAGE_HALL);
if(vti->tempOwner != PlayerColor::NEUTRAL)
vti->builtBuildings.insert(BuildingID::TAVERN);
vti->addBuilding(BuildingID::TAVERN);
auto definesBuildingsChances = VLC->settings()->getVector(EGameSettings::TOWNS_STARTING_DWELLING_CHANCES);
@ -821,48 +821,49 @@ void CGameState::initTowns()
{
if((getRandomGenerator().nextInt(1,100) <= definesBuildingsChances[i]))
{
vti->builtBuildings.insert(basicDwellings[i]);
vti->addBuilding(basicDwellings[i]);
}
}
}
// village hall must always exist
vti->builtBuildings.insert(BuildingID::VILLAGE_HALL);
vti->addBuilding(BuildingID::VILLAGE_HALL);
//init hordes
for (int i = 0; i < vti->town->creatures.size(); i++)
{
if (vstd::contains(vti->builtBuildings, hordes[i])) //if we have horde for this level
if(vti->hasBuilt(hordes[i])) //if we have horde for this level
{
vti->builtBuildings.erase(hordes[i]);//remove old ID
vti->removeBuilding(hordes[i]);//remove old ID
if (vti->getTown()->hordeLvl.at(0) == i)//if town first horde is this one
{
vti->builtBuildings.insert(BuildingID::HORDE_1);//add it
vti->addBuilding(BuildingID::HORDE_1);//add it
//if we have upgraded dwelling as well
if (vstd::contains(vti->builtBuildings, upgradedDwellings[i]))
vti->builtBuildings.insert(BuildingID::HORDE_1_UPGR);//add it as well
if(vti->hasBuilt(upgradedDwellings[i]))
vti->addBuilding(BuildingID::HORDE_1_UPGR);//add it as well
}
if (vti->getTown()->hordeLvl.at(1) == i)//if town second horde is this one
{
vti->builtBuildings.insert(BuildingID::HORDE_2);
if (vstd::contains(vti->builtBuildings, upgradedDwellings[i]))
vti->builtBuildings.insert(BuildingID::HORDE_2_UPGR);
vti->addBuilding(BuildingID::HORDE_2);
if(vti->hasBuilt(upgradedDwellings[i]))
vti->addBuilding(BuildingID::HORDE_2_UPGR);
}
}
}
//#1444 - remove entries that don't have buildings defined (like some unused extra town hall buildings)
//But DO NOT remove horde placeholders before they are replaced
vstd::erase_if(vti->builtBuildings, [vti](const BuildingID & bid)
{
return !vti->getTown()->buildings.count(bid) || !vti->getTown()->buildings.at(bid);
});
for(const auto & building : vti->getBuildings())
{
if(!vti->getTown()->buildings.count(building) || !vti->getTown()->buildings.at(building))
vti->removeBuilding(building);
}
if (vstd::contains(vti->builtBuildings, BuildingID::SHIPYARD) && vti->shipyardStatus()==IBoatGenerator::TILE_BLOCKED)
vti->builtBuildings.erase(BuildingID::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour)
if(vti->hasBuilt(BuildingID::SHIPYARD) && vti->shipyardStatus()==IBoatGenerator::TILE_BLOCKED)
vti->removeBuilding(BuildingID::SHIPYARD);//if we have harbor without water - erase it (this is H3 behaviour)
//Early check for #1444-like problems
for([[maybe_unused]] const auto & building : vti->builtBuildings)
for([[maybe_unused]] const auto & building : vti->getBuildings())
{
assert(vti->getTown()->buildings.at(building) != nullptr);
}

View File

@ -656,7 +656,7 @@ void CGameStateCampaign::initTowns()
if(gameState->scenarioOps->campState->formatVCMI())
newBuilding = BuildingID(chosenBonus->info1);
else
newBuilding = CBuildingHandler::campToERMU(chosenBonus->info1, town->getFaction(), town->builtBuildings);
newBuilding = CBuildingHandler::campToERMU(chosenBonus->info1, town->getFaction(), town->getBuildings());
// Build granted building & all prerequisites - e.g. Mages Guild Lvl 3 should also give Mages Guild Lvl 1 & 2
while(true)
@ -664,10 +664,10 @@ void CGameStateCampaign::initTowns()
if (newBuilding == BuildingID::NONE)
break;
if (town->builtBuildings.count(newBuilding) != 0)
if(town->hasBuilt(newBuilding))
break;
town->builtBuildings.insert(newBuilding);
town->addBuilding(newBuilding);
auto building = town->town->buildings.at(newBuilding);
newBuilding = building->upgrade;

View File

@ -372,7 +372,7 @@ float Statistic::getTownBuiltRatio(const PlayerState * ps)
for(const auto & t : ps->towns)
{
built += t->builtBuildings.size();
built += t->getBuildings().size();
for(const auto & b : t->town->buildings)
if(!t->forbiddenBuildings.count(b.first))
total += 1;

View File

@ -33,7 +33,7 @@ HighScoreParameter HighScore::prepareHighScores(const CGameState * gs, PlayerCol
if(h->hasArt(ArtifactID::GRAIL))
param.hasGrail = true;
for(const CGTownInstance * t : playerState->towns)
if(t->builtBuildings.count(BuildingID::GRAIL))
if(t->hasBuilt(BuildingID::GRAIL))
param.hasGrail = true;
param.allEnemiesDefeated = true;
for (PlayerColor otherPlayer(0); otherPlayer < PlayerColor::PLAYER_LIMIT; ++otherPlayer)

View File

@ -238,25 +238,23 @@ CGMarket * MarketInstanceConstructor::createObject(IGameCallback * cb) const
return new CGUniversity(cb);
}
}
else if(marketModes.size() == 2)
{
if(vstd::contains(marketModes, EMarketMode::ARTIFACT_EXP))
return new CGArtifactsAltar(cb);
}
return new CGMarket(cb);
}
void MarketInstanceConstructor::initializeObject(CGMarket * market) const
{
market->marketModes = marketModes;
market->addMarketMode(marketModes);
market->marketEfficiency = marketEfficiency;
market->title = market->getObjectName();
if(!title.empty())
market->title = VLC->generaltexth->translate(title);
if (!speech.empty())
market->speech = VLC->generaltexth->translate(speech);
if(auto university = dynamic_cast<CGUniversity*>(market))
{
university->title = market->getObjectName();
if(!title.empty())
university->title = VLC->generaltexth->translate(title);
if(!speech.empty())
university->speech = VLC->generaltexth->translate(speech);
}
}
void MarketInstanceConstructor::randomizeObject(CGMarket * object, vstd::RNG & rng) const

View File

@ -23,6 +23,11 @@
VCMI_LIB_NAMESPACE_BEGIN
ObjectInstanceID CGMarket::getObjInstanceID() const
{
return id;
}
void CGMarket::initObj(vstd::RNG & rand)
{
getObjectHandler()->configureObject(this, rand);
@ -38,23 +43,11 @@ int CGMarket::getMarketEfficiency() const
return marketEfficiency;
}
bool CGMarket::allowsTrade(EMarketMode mode) const
{
return marketModes.count(mode);
}
int CGMarket::availableUnits(EMarketMode mode, int marketItemSerial) const
{
return -1;
}
std::vector<TradeItemBuy> CGMarket::availableItemsIds(EMarketMode mode) const
{
if(allowsTrade(mode))
return IMarket::availableItemsIds(mode);
return std::vector<TradeItemBuy>();
}
CGMarket::CGMarket(IGameCallback *cb):
CGObjectInstance(cb)
{}
@ -63,8 +56,6 @@ std::vector<TradeItemBuy> CGBlackMarket::availableItemsIds(EMarketMode mode) con
{
switch(mode)
{
case EMarketMode::ARTIFACT_RESOURCE:
return IMarket::availableItemsIds(mode);
case EMarketMode::RESOURCE_ARTIFACT:
{
std::vector<TradeItemBuy> ret;
@ -113,9 +104,4 @@ void CGUniversity::onHeroVisit(const CGHeroInstance * h) const
cb->showObjectWindow(this, EOpenWindowMode::UNIVERSITY_WINDOW, h, true);
}
ArtBearer::ArtBearer CGArtifactsAltar::bearerType() const
{
return ArtBearer::ALTAR;
}
VCMI_LIB_NAMESPACE_END

View File

@ -18,32 +18,23 @@ VCMI_LIB_NAMESPACE_BEGIN
class DLL_LINKAGE CGMarket : public CGObjectInstance, public IMarket
{
public:
std::set<EMarketMode> marketModes;
int marketEfficiency;
//window variables
std::string title;
std::string speech; //currently shown only in university
CGMarket(IGameCallback *cb);
///IObjectInterface
void onHeroVisit(const CGHeroInstance * h) const override; //open trading window
void initObj(vstd::RNG & rand) override;//set skills for trade
///IMarket
ObjectInstanceID getObjInstanceID() const override;
int getMarketEfficiency() const override;
bool allowsTrade(EMarketMode mode) const override;
int availableUnits(EMarketMode mode, int marketItemSerial) const override; //-1 if unlimited
std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const override;
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<CGObjectInstance&>(*this);
h & marketModes;
h & static_cast<IMarket&>(*this);
h & marketEfficiency;
h & title;
h & speech;
}
};
@ -68,6 +59,8 @@ class DLL_LINKAGE CGUniversity : public CGMarket
{
public:
using CGMarket::CGMarket;
std::string speech; //currently shown only in university
std::string title;
std::vector<TradeItemBuy> skills; //available skills
@ -78,20 +71,8 @@ public:
{
h & static_cast<CGMarket&>(*this);
h & skills;
}
};
class DLL_LINKAGE CGArtifactsAltar : public CGMarket, public CArtifactSet
{
public:
using CGMarket::CGMarket;
ArtBearer::ArtBearer bearerType() const override;
template <typename Handler> void serialize(Handler & h)
{
h & static_cast<CGMarket&>(*this);
h & static_cast<CArtifactSet&>(*this);
h & speech;
h & title;
}
};

View File

@ -690,35 +690,6 @@ int CGTownInstance::getMarketEfficiency() const
return marketCount;
}
bool CGTownInstance::allowsTrade(EMarketMode mode) const
{
switch(mode)
{
case EMarketMode::RESOURCE_RESOURCE:
case EMarketMode::RESOURCE_PLAYER:
return hasBuilt(BuildingID::MARKETPLACE);
case EMarketMode::ARTIFACT_RESOURCE:
case EMarketMode::RESOURCE_ARTIFACT:
return hasBuilt(BuildingSubID::ARTIFACT_MERCHANT);
case EMarketMode::CREATURE_RESOURCE:
return hasBuilt(BuildingSubID::FREELANCERS_GUILD);
case EMarketMode::CREATURE_UNDEAD:
return hasBuilt(BuildingSubID::CREATURE_TRANSFORMER);
case EMarketMode::RESOURCE_SKILL:
return hasBuilt(BuildingSubID::MAGIC_UNIVERSITY);
case EMarketMode::CREATURE_EXP:
case EMarketMode::ARTIFACT_EXP:
return false;
default:
assert(0);
return false;
}
}
std::vector<TradeItemBuy> CGTownInstance::availableItemsIds(EMarketMode mode) const
{
if(mode == EMarketMode::RESOURCE_ARTIFACT)
@ -739,6 +710,11 @@ std::vector<TradeItemBuy> CGTownInstance::availableItemsIds(EMarketMode mode) co
return IMarket::availableItemsIds(mode);
}
ObjectInstanceID CGTownInstance::getObjInstanceID() const
{
return id;
}
void CGTownInstance::updateAppearance()
{
auto terrain = cb->gameState()->getTile(visitablePos())->terType->getId();
@ -932,12 +908,7 @@ const CArmedInstance * CGTownInstance::getUpperArmy() const
bool CGTownInstance::hasBuiltSomeTradeBuilding() const
{
for(const auto & bid : builtBuildings)
{
if(town->buildings.at(bid)->IsTradeBuilding())
return true;
}
return false;
return availableModes().empty() ? false : true;
}
bool CGTownInstance::hasBuilt(BuildingSubID::EBuildingSubID buildingID) const
@ -962,6 +933,42 @@ bool CGTownInstance::hasBuilt(const BuildingID & buildingID, FactionID townID) c
return false;
}
void CGTownInstance::addBuilding(const BuildingID & buildingID)
{
if(buildingID == BuildingID::NONE)
return;
const auto townType = (*VLC->townh)[getFaction()]->town;
if(const auto & building = townType->buildings.find(buildingID); building != townType->buildings.end())
{
builtBuildings.insert(buildingID);
addMarketMode(building->second->marketModes);
}
}
void CGTownInstance::removeBuilding(const BuildingID & buildingID)
{
if(!vstd::contains(builtBuildings, buildingID))
return;
if(const auto & building = town->buildings.find(buildingID); building != town->buildings.end())
{
builtBuildings.erase(buildingID);
removeMarketMode(building->second->marketModes);
}
}
void CGTownInstance::removeAllBuildings()
{
builtBuildings.clear();
removeAllMarketModes();
}
std::set<BuildingID> CGTownInstance::getBuildings() const
{
return builtBuildings;
}
TResources CGTownInstance::getBuildingCost(const BuildingID & buildingID) const
{
if (vstd::contains(town->buildings, buildingID))
@ -1132,23 +1139,23 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
{
handler.serializeLIC("buildings", buildingsLIC);
builtBuildings.insert(BuildingID::VILLAGE_HALL);
addBuilding(BuildingID::VILLAGE_HALL);
if(buildingsLIC.none.empty() && buildingsLIC.all.empty())
{
builtBuildings.insert(BuildingID::DEFAULT);
addBuilding(BuildingID::DEFAULT);
bool hasFort = false;
handler.serializeBool("hasFort",hasFort);
if(hasFort)
builtBuildings.insert(BuildingID::FORT);
addBuilding(BuildingID::FORT);
}
else
{
for(const si32 item : buildingsLIC.none)
forbiddenBuildings.insert(BuildingID(item));
for(const si32 item : buildingsLIC.all)
builtBuildings.insert(BuildingID(item));
addBuilding(BuildingID(item));
}
}
}

View File

@ -52,6 +52,8 @@ class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public I
std::string nameTextId; // name of town
std::map<BuildingID, TownRewardableBuildingInstance*> convertOldBuildings(std::vector<TownRewardableBuildingInstance*> oldVector);
std::set<BuildingID> builtBuildings;
public:
using CGDwelling::getPosition;
@ -65,7 +67,6 @@ public:
ui32 identifier; //special identifier from h3m (only > RoE maps)
PlayerColor alignmentToPlayer; // if set to non-neutral, random town will have same faction as specified player
std::set<BuildingID> forbiddenBuildings;
std::set<BuildingID> builtBuildings;
std::map<BuildingID, TownRewardableBuildingInstance*> rewardableBuildings;
std::vector<SpellID> possibleSpells, obligatorySpells;
std::vector<std::vector<SpellID> > spells; //spells[level] -> vector of spells, first will be available in guild
@ -76,6 +77,7 @@ public:
template <typename Handler> void serialize(Handler &h)
{
h & static_cast<CGDwelling&>(*this);
h & static_cast<IMarket&>(*this);
h & nameTextId;
h & built;
h & destroyed;
@ -152,9 +154,8 @@ public:
EGeneratorState shipyardStatus() const override;
const IObjectInterface * getObject() const override;
int getMarketEfficiency() const override; //=market count
bool allowsTrade(EMarketMode mode) const override;
std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const override;
ObjectInstanceID getObjInstanceID() const override;
void updateAppearance();
//////////////////////////////////////////////////////////////////////////
@ -174,6 +175,10 @@ public:
//checks if building is constructed and town has same subID
bool hasBuilt(const BuildingID & buildingID) const;
bool hasBuilt(const BuildingID & buildingID, FactionID townID) const;
void addBuilding(const BuildingID & buildingID);
void removeBuilding(const BuildingID & buildingID);
void removeAllBuildings();
std::set<BuildingID> getBuildings() const;
TResources getBuildingCost(const BuildingID & buildingID) const;
TResources dailyIncome() const; //calculates daily income of this town

View File

@ -20,6 +20,11 @@
VCMI_LIB_NAMESPACE_BEGIN
bool IMarket::allowsTrade(const EMarketMode mode) const
{
return vstd::contains(marketModes, mode);
}
bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) const
{
switch(mode)
@ -122,12 +127,7 @@ bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode)
return true;
}
bool IMarket::allowsTrade(EMarketMode mode) const
{
return false;
}
int IMarket::availableUnits(EMarketMode mode, int marketItemSerial) const
int IMarket::availableUnits(const EMarketMode mode, const int marketItemSerial) const
{
switch(mode)
{
@ -140,7 +140,45 @@ int IMarket::availableUnits(EMarketMode mode, int marketItemSerial) const
}
}
std::vector<TradeItemBuy> IMarket::availableItemsIds(EMarketMode mode) const
void IMarket::addMarketMode(const EMarketMode mode)
{
marketModes.insert(mode);
if(mode == EMarketMode::ARTIFACT_EXP)
altarArtifactsStorage = std::make_shared<CArtifactSetAltar>();
}
void IMarket::addMarketMode(const std::set<EMarketMode> & modes)
{
for(const auto & mode : modes)
addMarketMode(mode);
}
void IMarket::removeMarketMode(const EMarketMode mode)
{
marketModes.erase(mode);
if(mode == EMarketMode::ARTIFACT_EXP)
altarArtifactsStorage.reset();
}
void IMarket::removeMarketMode(const std::set<EMarketMode> & modes)
{
for(const auto & mode : modes)
removeMarketMode(mode);
}
void IMarket::removeAllMarketModes()
{
marketModes.clear();
}
std::shared_ptr<CArtifactSet> IMarket::getArtifactsStorage() const
{
return altarArtifactsStorage;
}
std::vector<TradeItemBuy> IMarket::availableItemsIds(const EMarketMode mode) const
{
std::vector<TradeItemBuy> ret;
switch(mode)
@ -148,24 +186,15 @@ std::vector<TradeItemBuy> IMarket::availableItemsIds(EMarketMode mode) const
case EMarketMode::RESOURCE_RESOURCE:
case EMarketMode::ARTIFACT_RESOURCE:
case EMarketMode::CREATURE_RESOURCE:
for (auto res : GameResID::ALL_RESOURCES())
for(const auto & res : GameResID::ALL_RESOURCES())
ret.push_back(res);
}
return ret;
}
IMarket::IMarket()
std::set<EMarketMode> IMarket::availableModes() const
{
}
std::vector<EMarketMode> IMarket::availableModes() const
{
std::vector<EMarketMode> ret;
for (EMarketMode i = static_cast<EMarketMode>(0); i < EMarketMode::MARKET_AFTER_LAST_PLACEHOLDER; i = vstd::next(i, 1))
if(allowsTrade(i))
ret.push_back(i);
return ret;
return marketModes;
}
VCMI_LIB_NAMESPACE_END

View File

@ -11,24 +11,44 @@
#include "../networkPacks/TradeItem.h"
#include "../constants/Enumerations.h"
#include "../CArtHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
class CGObjectInstance;
class DLL_LINKAGE IMarket
class DLL_LINKAGE IMarket : public virtual Serializeable
{
public:
IMarket();
virtual ~IMarket() {}
class CArtifactSetAltar : public CArtifactSet
{
public:
ArtBearer::ArtBearer bearerType() const override {return ArtBearer::ALTAR;};
};
virtual ObjectInstanceID getObjInstanceID() const = 0; // The market is always an object on the map
virtual int getMarketEfficiency() const = 0;
virtual bool allowsTrade(EMarketMode mode) const;
virtual int availableUnits(EMarketMode mode, int marketItemSerial) const; //-1 if unlimited
virtual std::vector<TradeItemBuy> availableItemsIds(EMarketMode mode) const;
virtual bool allowsTrade(const EMarketMode mode) const;
virtual int availableUnits(const EMarketMode mode, const int marketItemSerial) const; //-1 if unlimited
virtual std::vector<TradeItemBuy> availableItemsIds(const EMarketMode mode) const;
void addMarketMode(const EMarketMode mode);
void addMarketMode(const std::set<EMarketMode> & modes);
void removeMarketMode(const EMarketMode mode);
void removeMarketMode(const std::set<EMarketMode> & modes);
void removeAllMarketModes();
std::set<EMarketMode> availableModes() const;
std::shared_ptr<CArtifactSet> getArtifactsStorage() const;
bool getOffer(int id1, int id2, int &val1, int &val2, EMarketMode mode) const; //val1 - how many units of id1 player has to give to receive val2 units
std::vector<EMarketMode> availableModes() const;
template <typename Handler> void serialize(Handler & h)
{
h & marketModes;
if(h.loadingGamestate && vstd::contains(marketModes, EMarketMode::ARTIFACT_EXP))
altarArtifactsStorage = std::make_shared<CArtifactSetAltar>();
}
private:
std::shared_ptr<CArtifactSetAltar> altarArtifactsStorage;
std::set<EMarketMode> marketModes;
};
VCMI_LIB_NAMESPACE_END

View File

@ -2198,7 +2198,11 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
bool hasCustomBuildings = reader->readBool();
if(hasCustomBuildings)
{
reader->readBitmaskBuildings(object->builtBuildings, faction);
object->subID = faction.value();
std::set<BuildingID> builtBuildings;
reader->readBitmaskBuildings(builtBuildings, faction);
for(const auto & building : builtBuildings)
object->addBuilding(building);
reader->readBitmaskBuildings(object->forbiddenBuildings, faction);
}
// Standard buildings
@ -2206,10 +2210,10 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
{
bool hasFort = reader->readBool();
if(hasFort)
object->builtBuildings.insert(BuildingID::FORT);
object->addBuilding(BuildingID::FORT);
//means that set of standard building should be included
object->builtBuildings.insert(BuildingID::DEFAULT);
object->addBuilding(BuildingID::DEFAULT);
}
if(features.levelAB)

View File

@ -1332,7 +1332,7 @@ void NewStructures::applyGs(CGameState *gs)
for(const auto & id : bid)
{
assert(t->town->buildings.at(id) != nullptr);
t->builtBuildings.insert(id);
t->addBuilding(id);
}
t->updateAppearance();
t->built = built;
@ -1344,7 +1344,7 @@ void RazeStructures::applyGs(CGameState *gs)
CGTownInstance *t = gs->getTown(tid);
for(const auto & id : bid)
{
t->builtBuildings.erase(id);
t->removeBuilding(id);
t->updateAppearance();
}

View File

@ -132,8 +132,7 @@ void registerTypesMapObjects(Serializer &s)
//s.template registerType<CObstacleInstance>();
s.template registerType<CObstacleInstance, SpellCreatedObstacle>();
s.template registerType<CGMarket, CGArtifactsAltar>();
s.template registerType<IMarket, CGMarket>();
}
VCMI_LIB_NAMESPACE_END

View File

@ -79,8 +79,8 @@ void TownPlacer::placeTowns(ObjectManager & manager)
CGTownInstance * town = dynamic_cast<CGTownInstance *>(townFactory->create(map.mapInstance->cb, nullptr));
town->tempOwner = player;
town->builtBuildings.insert(BuildingID::FORT);
town->builtBuildings.insert(BuildingID::DEFAULT);
town->addBuilding(BuildingID::FORT);
town->addBuilding(BuildingID::DEFAULT);
for(auto spellID : VLC->spellh->getDefaultAllowed()) //add all regular spells to town
@ -203,8 +203,8 @@ void TownPlacer::addNewTowns(int count, bool hasFort, const PlayerColor & player
town->tempOwner = player;
if (hasFort)
town->builtBuildings.insert(BuildingID::FORT);
town->builtBuildings.insert(BuildingID::DEFAULT);
town->addBuilding(BuildingID::FORT);
town->addBuilding(BuildingID::DEFAULT);
for(auto spellID : VLC->spellh->getDefaultAllowed()) //add all regular spells to town
town->possibleSpells.push_back(spellID);

View File

@ -166,12 +166,12 @@ void Initializer::initialize(CGTownInstance * o)
const std::vector<std::string> castleLevels{"village", "fort", "citadel", "castle", "capitol"};
int lvl = vstd::find_pos(castleLevels, o->appearance->stringID);
o->builtBuildings.insert(BuildingID::DEFAULT);
if(lvl > -1) o->builtBuildings.insert(BuildingID::TAVERN);
if(lvl > 0) o->builtBuildings.insert(BuildingID::FORT);
if(lvl > 1) o->builtBuildings.insert(BuildingID::CITADEL);
if(lvl > 2) o->builtBuildings.insert(BuildingID::CASTLE);
if(lvl > 3) o->builtBuildings.insert(BuildingID::CAPITOL);
o->addBuilding(BuildingID::DEFAULT);
if(lvl > -1) o->addBuilding(BuildingID::TAVERN);
if(lvl > 0) o->addBuilding(BuildingID::FORT);
if(lvl > 1) o->addBuilding(BuildingID::CITADEL);
if(lvl > 2) o->addBuilding(BuildingID::CASTLE);
if(lvl > 3) o->addBuilding(BuildingID::CAPITOL);
for(auto const & spell : VLC->spellh->objects) //add all regular spells to town
{

View File

@ -166,7 +166,7 @@ QStandardItem * TownBuildingsWidget::addBuilding(const CTown & ctown, int bId, s
checks << new QStandardItem;
checks.back()->setCheckable(true);
checks.back()->setCheckState(town.builtBuildings.count(buildingId) ? Qt::Checked : Qt::Unchecked);
checks.back()->setCheckState(town.hasBuilt(buildingId) ? Qt::Checked : Qt::Unchecked);
checks.back()->setData(bId, MapEditorRoles::BuildingIDRole);
if(building->getBase() == buildingId)
@ -350,7 +350,9 @@ void TownBuildingsDelegate::setModelData(QWidget *editor, QAbstractItemModel *mo
if(auto * ed = qobject_cast<TownBuildingsWidget *>(editor))
{
town.forbiddenBuildings = ed->getForbiddenBuildings();
town.builtBuildings = ed->getBuiltBuildings();
town.removeAllBuildings();
for(const auto & building : ed->getBuiltBuildings())
town.addBuilding(building);
}
else
{

View File

@ -174,10 +174,11 @@ void MapController::repairMap(CMap * map) const
{
if(tnh->getTown())
{
vstd::erase_if(tnh->builtBuildings, [tnh](BuildingID bid)
for(const auto & building : tnh->getBuildings())
{
return !tnh->getTown()->buildings.count(bid);
});
if(!tnh->getTown()->buildings.count(building))
tnh->removeBuilding(building);
}
vstd::erase_if(tnh->forbiddenBuildings, [tnh](BuildingID bid)
{
return !tnh->getTown()->buildings.count(bid);

View File

@ -2525,7 +2525,7 @@ bool CGameHandler::razeStructure (ObjectInstanceID tid, BuildingID bid)
{
///incomplete, simply erases target building
const CGTownInstance * t = getTown(tid);
if (!vstd::contains(t->builtBuildings, bid))
if(!t->hasBuilt(bid))
return false;
RazeStructures rs;
rs.tid = tid;
@ -3565,9 +3565,9 @@ bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2)
return true;
}
auto market = dynamic_cast<const IMarket*>(o1);
auto market = getMarket(id1);
if(market == nullptr)
market = dynamic_cast<const IMarket*>(o2);
market = getMarket(id2);
if(market)
return market->allowsTrade(EMarketMode::ARTIFACT_EXP);
@ -3916,15 +3916,15 @@ bool CGameHandler::sacrificeCreatures(const IMarket * market, const CGHeroInstan
return true;
}
bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector<ArtifactInstanceID> & arts)
bool CGameHandler::sacrificeArtifact(const IMarket * market, const CGHeroInstance * hero, const std::vector<ArtifactInstanceID> & arts)
{
if (!hero)
COMPLAIN_RET("You need hero to sacrifice artifact!");
if(hero->getAlignment() == EAlignment::EVIL)
COMPLAIN_RET("Evil hero can't sacrifice artifact!");
assert(m);
auto altarObj = dynamic_cast<const CGArtifactsAltar*>(m);
assert(market);
const auto artSet = market->getArtifactsStorage();
int expSum = 0;
auto finish = [this, &hero, &expSum]()
@ -3934,15 +3934,15 @@ bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * h
for(const auto & artInstId : arts)
{
if(auto art = altarObj->getArtByInstanceId(artInstId))
if(auto art = artSet->getArtByInstanceId(artInstId))
{
if(art->artType->isTradable())
{
int dmp;
int expToGive;
m->getOffer(art->getTypeId(), 0, dmp, expToGive, EMarketMode::ARTIFACT_EXP);
market->getOffer(art->getTypeId(), 0, dmp, expToGive, EMarketMode::ARTIFACT_EXP);
expSum += expToGive;
removeArtifact(ArtifactLocation(altarObj->id, altarObj->getArtPos(art)));
removeArtifact(ArtifactLocation(market->getObjInstanceID(), artSet->getArtPos(art)));
}
else
{

View File

@ -282,7 +282,7 @@ public:
void start(bool resume);
void tick(int millisecondsPassed);
bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, const std::vector<ArtifactInstanceID> & arts);
bool sacrificeArtifact(const IMarket * market, const CGHeroInstance * hero, const std::vector<ArtifactInstanceID> & arts);
void spawnWanderingMonsters(CreatureID creatureID);
// Check for victory and loss conditions

View File

@ -184,7 +184,7 @@ void ApplyGhNetPackVisitor::visitExchangeArtifacts(ExchangeArtifacts & pack)
void ApplyGhNetPackVisitor::visitBulkExchangeArtifacts(BulkExchangeArtifacts & pack)
{
if(dynamic_cast<const IMarket*>(gh.getObj(pack.srcHero)) == nullptr)
if(gh.getMarket(pack.srcHero) == nullptr)
gh.throwIfWrongOwner(&pack, pack.srcHero);
if(pack.swap)
gh.throwIfWrongOwner(&pack, pack.dstHero);
@ -250,7 +250,7 @@ void ApplyGhNetPackVisitor::visitTradeOnMarketplace(TradeOnMarketplace & pack)
{
const CGObjectInstance * object = gh.getObj(pack.marketId);
const CGHeroInstance * hero = gh.getHero(pack.heroId);
const auto * market = dynamic_cast<const IMarket*>(object);
const auto * market = gh.getMarket(pack.marketId);
gh.throwIfWrongPlayer(&pack);
gh.throwIfPlayerNotActive(&pack);

View File

@ -64,7 +64,7 @@ TEST_F(CatapultTest, NotApplicableInVillage)
TEST_F(CatapultTest, NotApplicableForDefenderIfSmart)
{
auto fakeTown = std::make_shared<CGTownInstance>(nullptr);
fakeTown->builtBuildings.insert(BuildingID::FORT);
fakeTown->addBuilding(BuildingID::FORT);
mechanicsMock.casterSide = BattleSide::DEFENDER;
EXPECT_CALL(*battleFake, getDefendedTown()).WillRepeatedly(Return(fakeTown.get()));
@ -78,7 +78,7 @@ TEST_F(CatapultTest, NotApplicableForDefenderIfSmart)
TEST_F(CatapultTest, DISABLED_ApplicableInTown)
{
auto fakeTown = std::make_shared<CGTownInstance>(nullptr);
fakeTown->builtBuildings.insert(BuildingID::FORT);
fakeTown->addBuilding(BuildingID::FORT);
EXPECT_CALL(*battleFake, getDefendedTown()).WillRepeatedly(Return(fakeTown.get()));
EXPECT_CALL(mechanicsMock, adaptProblem(_, _)).Times(0);
@ -108,7 +108,7 @@ protected:
{
EffectFixture::setUp();
fakeTown = std::make_shared<CGTownInstance>(nullptr);
fakeTown->builtBuildings.insert(BuildingID::FORT);
fakeTown->addBuilding(BuildingID::FORT);
}
private:
std::shared_ptr<CGTownInstance> fakeTown;