1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-21 17:17:06 +02:00

IMarket suggestions

Co-authored-by: Ivan Savenko <saven.ivan@gmail.com>
This commit is contained in:
SoundSSGood 2024-08-17 22:06:48 +03:00
parent cd7ebea9e3
commit 58bb2b58e3
37 changed files with 128 additions and 94 deletions

View File

@ -526,7 +526,7 @@ void AIGateway::heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonu
NET_EVENT_HANDLER;
}
void AIGateway::showMarketWindow(const IMarket * market, const ObjectInstanceID & marketId, const CGHeroInstance * visitor, QueryID queryID)
void AIGateway::showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID)
{
LOG_TRACE(logAi);
NET_EVENT_HANDLER;

View File

@ -152,7 +152,7 @@ public:
void objectPropertyChanged(const SetObjectProperty * sop) override;
void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override;
void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
void showMarketWindow(const IMarket * market, const ObjectInstanceID & marketId, const CGHeroInstance * visitor, QueryID queryID) override;
void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override;

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

@ -594,7 +594,7 @@ void VCAI::heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bo
NET_EVENT_HANDLER;
}
void VCAI::showMarketWindow(const IMarket * market, const ObjectInstanceID & marketId, const CGHeroInstance * visitor, QueryID queryID)
void VCAI::showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID)
{
LOG_TRACE(logAi);
NET_EVENT_HANDLER;

View File

@ -184,7 +184,7 @@ public:
void objectPropertyChanged(const SetObjectProperty * sop) override;
void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override;
void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
void showMarketWindow(const IMarket * market, const ObjectInstanceID & marketId, const CGHeroInstance * visitor, QueryID queryID) override;
void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
void battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, BattleSide side, bool replayAllowed) override;

View File

@ -1632,7 +1632,7 @@ void CPlayerInterface::battleNewRoundFirst(const BattleID & battleID)
battleInt->newRoundFirst();
}
void CPlayerInterface::showMarketWindow(const IMarket * market, const ObjectInstanceID & marketId, 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](){
@ -1649,13 +1649,13 @@ void CPlayerInterface::showMarketWindow(const IMarket * market, const ObjectInst
}
if(market->allowsTrade(EMarketMode::ARTIFACT_EXP) && visitor->getAlignment() != EAlignment::EVIL)
GH.windows().createAndPushWindow<CMarketWindow>(market, visitor, marketId, onWindowClosed, EMarketMode::ARTIFACT_EXP);
GH.windows().createAndPushWindow<CMarketWindow>(market, visitor, onWindowClosed, EMarketMode::ARTIFACT_EXP);
else if(market->allowsTrade(EMarketMode::CREATURE_EXP) && visitor->getAlignment() != EAlignment::GOOD)
GH.windows().createAndPushWindow<CMarketWindow>(market, visitor, marketId, onWindowClosed, EMarketMode::CREATURE_EXP);
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(vstd::contains(market->availableModes(), EMarketMode::RESOURCE_RESOURCE))
GH.windows().createAndPushWindow<CMarketWindow>(market, visitor, marketId, onWindowClosed, EMarketMode::RESOURCE_RESOURCE);
GH.windows().createAndPushWindow<CMarketWindow>(market, visitor, onWindowClosed, EMarketMode::RESOURCE_RESOURCE);
}
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 ObjectInstanceID & marketId, 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

@ -1010,7 +1010,7 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
const auto *market = dynamic_cast<const IMarket*>(obj);
callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showMarketWindow, market, pack.object, hero, pack.queryID);
callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showMarketWindow, market, hero, pack.queryID);
}
break;
case EOpenWindowMode::HILL_FORT_WINDOW:

View File

@ -432,7 +432,7 @@ void AdventureMapShortcuts::showMarketplace()
}
if(townWithMarket) //if any town has marketplace, open window
GH.windows().createAndPushWindow<CMarketWindow>(townWithMarket, nullptr, townWithMarket->id, nullptr, EMarketMode::RESOURCE_RESOURCE);
GH.windows().createAndPushWindow<CMarketWindow>(townWithMarket, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
else //if not - complain
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket"));
}

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,9 +476,9 @@ 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, town->id, nullptr, EMarketMode::RESOURCE_RESOURCE);
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
return;
}
}

View File

@ -26,7 +26,7 @@
#include "../../../lib/mapObjects/CGHeroInstance.h"
#include "../../../lib/mapObjects/CGMarket.h"
CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance * hero, const ObjectInstanceID & marketId)
CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance * hero)
: CMarketBase(market, hero)
{
OBJECT_CONSTRUCTION;
@ -50,7 +50,9 @@ CAltarArtifacts::CAltarArtifacts(const IMarket * market, const CGHeroInstance *
// Hero's artifacts
heroArts = std::make_shared<CArtifactsOfHeroAltar>(Point(-365, -11));
heroArts->setHero(hero);
heroArts->altarId = marketId;
const auto mapObj = dynamic_cast<const CGObjectInstance*>(market);
assert(mapObj);
heroArts->altarId = mapObj->id;
// Altar
offerTradePanel = std::make_shared<ArtifactsAltarPanel>([this](const std::shared_ptr<CTradeableItem> & altarSlot)

View File

@ -15,7 +15,7 @@
class CAltarArtifacts : public CExperienceAltar
{
public:
CAltarArtifacts(const IMarket * market, const CGHeroInstance * hero, const ObjectInstanceID & marketId);
CAltarArtifacts(const IMarket * market, const CGHeroInstance * hero);
TExpType calcExpAltarForHero() override;
void deselect() override;
void makeDeal() override;

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())
@ -729,7 +729,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
case BuildingID::MARKETPLACE:
// can't use allied marketplace
if (town->getOwner() == LOCPLINT->playerID)
GH.windows().createAndPushWindow<CMarketWindow>(town, town->visitingHero, town->id, nullptr, EMarketMode::RESOURCE_RESOURCE);
GH.windows().createAndPushWindow<CMarketWindow>(town, town->visitingHero, nullptr, EMarketMode::RESOURCE_RESOURCE);
else
enterBuilding(building);
break;
@ -758,7 +758,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
case BuildingSubID::ARTIFACT_MERCHANT:
if(town->visitingHero)
GH.windows().createAndPushWindow<CMarketWindow>(town, town->visitingHero, town->id, nullptr, EMarketMode::RESOURCE_ARTIFACT);
GH.windows().createAndPushWindow<CMarketWindow>(town, town->visitingHero, nullptr, EMarketMode::RESOURCE_ARTIFACT);
else
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->getNameTranslated())); //Only visiting heroes may use the %s.
break;
@ -769,7 +769,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
case BuildingSubID::FREELANCERS_GUILD:
if(getHero())
GH.windows().createAndPushWindow<CMarketWindow>(town, getHero(), town->id, nullptr, EMarketMode::CREATURE_RESOURCE);
GH.windows().createAndPushWindow<CMarketWindow>(town, getHero(), nullptr, EMarketMode::CREATURE_RESOURCE);
else
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->getNameTranslated())); //Only visiting heroes may use the %s.
break;
@ -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,18 +1059,18 @@ void CCastleBuildings::enterBank()
void CCastleBuildings::enterAnyMarket()
{
if(town->builtBuildings.count(BuildingID::MARKETPLACE))
if(town->hasBuilt(BuildingID::MARKETPLACE))
{
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, town->id, nullptr, EMarketMode::RESOURCE_RESOURCE);
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
return;
}
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, town->id, nullptr, EMarketMode::RESOURCE_RESOURCE);
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,9 +833,9 @@ 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, town->id, nullptr, EMarketMode::RESOURCE_RESOURCE);
GH.windows().createAndPushWindow<CMarketWindow>(town, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
return;
}
}

View File

@ -34,11 +34,9 @@
#include "../../CCallback.h"
CMarketWindow::CMarketWindow(const IMarket * market, const CGHeroInstance * hero, const ObjectInstanceID & marketId,
const std::function<void()> & onWindowClosed, EMarketMode mode)
CMarketWindow::CMarketWindow(const IMarket * market, const CGHeroInstance * hero, const std::function<void()> & onWindowClosed, EMarketMode mode)
: CWindowObject(PLAYER_COLORED)
, windowClosedCallback(onWindowClosed)
, marketId(marketId)
{
assert(mode == EMarketMode::RESOURCE_RESOURCE || mode == EMarketMode::RESOURCE_PLAYER || mode == EMarketMode::CREATURE_RESOURCE ||
mode == EMarketMode::RESOURCE_ARTIFACT || mode == EMarketMode::ARTIFACT_RESOURCE || mode == EMarketMode::ARTIFACT_EXP ||
@ -115,6 +113,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))
@ -184,7 +187,9 @@ void CMarketWindow::createArtifactsBuying(const IMarket * market, const CGHeroIn
OBJECT_CONSTRUCTION;
background = createBg(ImagePath::builtin("TPMRKABS.bmp"), PLAYER_COLORED);
marketWidget = std::make_shared<CArtifactsBuying>(market, hero, LOCPLINT->cb->getTown(marketId) ?
const auto mapObj = dynamic_cast<const CGObjectInstance*>(market);
assert(market);
marketWidget = std::make_shared<CArtifactsBuying>(market, hero, LOCPLINT->cb->getTown(mapObj->id) ?
VLC->generaltexth->translate("building.core.conflux.special1.name") : CGI->generaltexth->allTexts[349]);
initWidgetInternals(EMarketMode::RESOURCE_ARTIFACT, CGI->generaltexth->zelp[600]);
}
@ -197,8 +202,10 @@ 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, LOCPLINT->cb->getTown(marketId) ?
VLC->generaltexth->translate("building.core.conflux.special1.name") : LOCPLINT->cb->getObj(marketId)->getObjectName());
const auto mapObj = dynamic_cast<const CGObjectInstance*>(market);
assert(market);
auto artsSellingMarket = std::make_shared<CArtifactsSelling>(market, hero, LOCPLINT->cb->getTown(mapObj->id) ?
VLC->generaltexth->translate("building.core.conflux.special1.name") : mapObj->getObjectName());
artSets.clear();
const auto heroArts = artsSellingMarket->getAOHset();
heroArts->showPopupCallback = [this, heroArts](CArtPlace & artPlace, const Point & cursorPosition){showArifactInfo(*heroArts, artPlace, cursorPosition);};
@ -239,7 +246,7 @@ void CMarketWindow::createAltarArtifacts(const IMarket * market, const CGHeroIns
OBJECT_CONSTRUCTION;
background = createBg(ImagePath::builtin("ALTRART2.bmp"), PLAYER_COLORED);
auto altarArtifactsStorage = std::make_shared<CAltarArtifacts>(market, hero, marketId);
auto altarArtifactsStorage = std::make_shared<CAltarArtifacts>(market, hero);
marketWidget = altarArtifactsStorage;
artSets.clear();
const auto heroArts = altarArtifactsStorage->getAOHset();

View File

@ -15,8 +15,7 @@
class CMarketWindow final : public CStatusbarWindow, public CWindowWithArtifacts, public IGarrisonHolder, public IMarketHolder
{
public:
CMarketWindow(const IMarket * market, const CGHeroInstance * hero, const ObjectInstanceID & marketId,
const std::function<void()> & onWindowClosed, EMarketMode mode);
CMarketWindow(const IMarket * market, const CGHeroInstance * hero, const std::function<void()> & onWindowClosed, EMarketMode mode);
void updateResources() override;
void updateArtifacts() override;
void updateGarrisons() override;
@ -43,7 +42,6 @@ private:
std::function<void()> windowClosedCallback;
const Point quitButtonPos = Point(516, 520);
std::shared_ptr<CMarketBase> marketWidget;
const ObjectInstanceID marketId;
// This is workaround for bug in H3 files where this slot for ragdoll on this screen is missing
std::shared_ptr<CPicture> artSlotBack;

View File

@ -287,15 +287,15 @@ CArtifactSet * CNonConstInfoCallback::getArtSet(const ArtifactLocation & loc)
return hero;
}
}
else if(auto army = getArmyInstance(loc.artHolder))
{
return army->getStackPtr(loc.creature.value());
}
else if(auto market = dynamic_cast<IMarket*>(getObjInstance(loc.artHolder)))
{
if(auto artSet = market->getArtifactsStorage())
return artSet.get();
}
else if(auto army = getArmyInstance(loc.artHolder))
{
return army->getStackPtr(loc.creature.value());
}
return nullptr;
}

View File

@ -109,7 +109,7 @@ public:
virtual void showPuzzleMap(){};
virtual void viewWorldMap(){};
virtual void showMarketWindow(const IMarket * market, const ObjectInstanceID & marketId, 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

@ -802,7 +802,7 @@ 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->removeBuilding(BuildingID::DEFAULT);
vti->addBuilding(BuildingID::VILLAGE_HALL);
@ -826,20 +826,20 @@ void CGameState::initTowns()
//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->removeBuilding(hordes[i]);//remove old ID
if (vti->getTown()->hordeLvl.at(0) == i)//if town first horde is this one
{
vti->addBuilding(BuildingID::HORDE_1);//add it
//if we have upgraded dwelling as well
if (vstd::contains(vti->builtBuildings, upgradedDwellings[i]))
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->addBuilding(BuildingID::HORDE_2);
if (vstd::contains(vti->builtBuildings, upgradedDwellings[i]))
if(vti->hasBuilt(upgradedDwellings[i]))
vti->addBuilding(BuildingID::HORDE_2_UPGR);
}
}
@ -847,16 +847,17 @@ void CGameState::initTowns()
//#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)
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)

View File

@ -349,7 +349,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

@ -246,14 +246,14 @@ void MarketInstanceConstructor::initializeObject(CGMarket * market) const
market->addMarketMode(marketModes);
market->marketEfficiency = marketEfficiency;
if(auto univercity = dynamic_cast<CGUniversity*>(market))
if(auto university = dynamic_cast<CGUniversity*>(market))
{
univercity->title = market->getObjectName();
university->title = market->getObjectName();
if(!title.empty())
univercity->title = VLC->generaltexth->translate(title);
university->title = VLC->generaltexth->translate(title);
if(!speech.empty())
univercity->speech = VLC->generaltexth->translate(speech);
university->speech = VLC->generaltexth->translate(speech);
}
}

View File

@ -951,6 +951,17 @@ void CGTownInstance::removeBuilding(const BuildingID & buildingID)
marketBuildingModeMapper(buildingID, [this](const EMarketMode mode) {removeMarketMode(mode);});
}
void CGTownInstance::removeAllBuildings()
{
builtBuildings.clear();
removeAllMarketModes();
}
std::set<BuildingID> CGTownInstance::getBuildings() const
{
return builtBuildings;
}
void CGTownInstance::marketBuildingModeMapper(const BuildingID & buildingID, const std::function<void(const EMarketMode)> & func)
{
const auto townType = (*VLC->townh)[getFaction()]->town;

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
@ -176,6 +177,8 @@ public:
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

@ -162,6 +162,11 @@ void IMarket::removeMarketMode(const EMarketMode mode)
altarArtifactsStorage.reset();
}
void IMarket::removeAllMarketModes()
{
marketModes.clear();
}
std::shared_ptr<CArtifactSet> IMarket::getArtifactsStorage() const
{
return altarArtifactsStorage;

View File

@ -31,6 +31,7 @@ public:
void addMarketMode(const EMarketMode mode);
void addMarketMode(const std::set<EMarketMode> & modes);
void removeMarketMode(const EMarketMode mode);
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

View File

@ -2199,9 +2199,11 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
if(hasCustomBuildings)
{
object->subID = faction.value();
for(const auto & building : reader->readBitmaskBuildings(faction))
std::set<BuildingID> builtBuildings;
reader->readBitmaskBuildings(builtBuildings, faction);
for(const auto & building : builtBuildings)
object->addBuilding(building);
object->forbiddenBuildings = reader->readBitmaskBuildings(faction);
reader->readBitmaskBuildings(object->forbiddenBuildings, faction);
}
// Standard buildings
else
@ -2259,7 +2261,7 @@ CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_pt
reader->skipZero(17);
// New buildings
event.buildings = reader->readBitmaskBuildings(faction);
reader->readBitmaskBuildings(event.buildings, faction);
event.creatures.resize(7);
for(int i = 0; i < 7; ++i)

View File

@ -257,10 +257,9 @@ PlayerColor MapReaderH3M::readPlayer32()
return PlayerColor(value);
}
std::set<BuildingID> MapReaderH3M::readBitmaskBuildings(std::optional<FactionID> faction)
void MapReaderH3M::readBitmaskBuildings(std::set<BuildingID> & dest, std::optional<FactionID> faction)
{
std::set<BuildingID> h3m;
std::set<BuildingID> dest;
readBitmask(h3m, features.buildingsBytes, features.buildingsCount, false);
for (auto const & h3mEntry : h3m)
@ -270,7 +269,6 @@ std::set<BuildingID> MapReaderH3M::readBitmaskBuildings(std::optional<FactionID>
if (mapped != BuildingID::NONE) // artifact merchant may be set in random town, but not present in actual town
dest.insert(mapped);
}
return dest;
}
void MapReaderH3M::readBitmaskFactions(std::set<FactionID> & dest, bool invert)

View File

@ -48,7 +48,7 @@ public:
PlayerColor readPlayer();
PlayerColor readPlayer32();
std::set<BuildingID> readBitmaskBuildings(std::optional<FactionID> faction);
void readBitmaskBuildings(std::set<BuildingID> & dest, std::optional<FactionID> faction);
void readBitmaskFactions(std::set<FactionID> & dest, bool invert);
void readBitmaskPlayers(std::set<PlayerColor> & dest, bool invert);
void readBitmaskResources(std::set<GameResID> & dest, bool invert);

View File

@ -51,10 +51,11 @@ void registerTypesMapObjects(Serializer &s)
s.template registerType<CGObjectInstance, CGLighthouse>();
s.template registerType<CGObjectInstance, CGTerrainPatch>();
s.template registerType<CGObjectInstance, HillFort>();
s.template registerType<CGObjectInstance, CGMarket>(); s.template registerType<IMarket, CGMarket>();
s.template registerType<CGObjectInstance, CGMarket>();
s.template registerType<CGMarket, CGBlackMarket>();
s.template registerType<CGMarket, CGUniversity>();
s.template registerType<CGObjectInstance, CGHeroPlaceholder>();
s.template registerType<IMarket, CGMarket>();
s.template registerType<CGObjectInstance, CArmedInstance>(); s.template registerType<CBonusSystemNode, CArmedInstance>(); s.template registerType<CCreatureSet, CArmedInstance>();

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

@ -2521,7 +2521,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;
@ -3909,7 +3909,7 @@ bool CGameHandler::sacrificeCreatures(const IMarket * market, const CGHeroInstan
return true;
}
bool CGameHandler::sacrificeArtifact(const IMarket * market, const CGHeroInstance * hero, const ObjectInstanceID & marketId, 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!");
@ -3917,7 +3917,9 @@ bool CGameHandler::sacrificeArtifact(const IMarket * market, const CGHeroInstanc
COMPLAIN_RET("Evil hero can't sacrifice artifact!");
assert(market);
const auto artSet = getArtSet(marketId);
const auto mapObj = dynamic_cast<const CGObjectInstance*>(market);
assert(mapObj);
const auto artSet = getArtSet(mapObj->id);
int expSum = 0;
auto finish = [this, &hero, &expSum]()
@ -3935,7 +3937,7 @@ bool CGameHandler::sacrificeArtifact(const IMarket * market, const CGHeroInstanc
int expToGive;
market->getOffer(art->getTypeId(), 0, dmp, expToGive, EMarketMode::ARTIFACT_EXP);
expSum += expToGive;
removeArtifact(ArtifactLocation(marketId, artSet->getArtPos(art)));
removeArtifact(ArtifactLocation(mapObj->id, artSet->getArtPos(art)));
}
else
{

View File

@ -281,7 +281,7 @@ public:
void start(bool resume);
void tick(int millisecondsPassed);
bool sacrificeArtifact(const IMarket * market, const CGHeroInstance * hero, const ObjectInstanceID & marketId, 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

@ -347,7 +347,7 @@ void ApplyGhNetPackVisitor::visitTradeOnMarketplace(TradeOnMarketplace & pack)
for(auto const & artInstId : pack.r1)
positions.push_back(artInstId.as<ArtifactInstanceID>());
result = gh.sacrificeArtifact(market, hero, pack.marketId, positions);
result = gh.sacrificeArtifact(market, hero, positions);
return;
}
default: