/* * CGMarket.cpp, part of VCMI engine * * Authors: listed in file AUTHORS in main folder * * License: GNU General Public License v2.0 or later * Full text of license available in license.txt file, in main folder * */ #include "StdInc.h" #include "CGMarket.h" #include "../NetPacks.h" #include "../CGeneralTextHandler.h" #include "../IGameCallback.h" #include "../CCreatureHandler.h" #include "../CGameState.h" #include "CGTownInstance.h" #include "../CModHandler.h" #include "../CSkillHandler.h" ///helpers static void openWindow(const OpenWindow::EWindow type, const int id1, const int id2 = -1) { OpenWindow ow; ow.window = type; ow.id1 = id1; ow.id2 = id2; IObjectInterface::cb->sendAndApply(&ow); } bool IMarket::getOffer(int id1, int id2, int &val1, int &val2, EMarketMode::EMarketMode mode) const { switch(mode) { case EMarketMode::RESOURCE_RESOURCE: { double effectiveness = std::min((getMarketEfficiency() + 1.0) / 20.0, 0.5); double r = VLC->objh->resVals[id1], //value of given resource g = VLC->objh->resVals[id2] / effectiveness; //value of wanted resource if(r>g) //if given resource is more expensive than wanted { val2 = ceil(r / g); val1 = 1; } else //if wanted resource is more expensive { val1 = (g / r) + 0.5; val2 = 1; } } break; case EMarketMode::CREATURE_RESOURCE: { const double effectivenessArray[] = {0.0, 0.3, 0.45, 0.50, 0.65, 0.7, 0.85, 0.9, 1.0}; double effectiveness = effectivenessArray[std::min(getMarketEfficiency(), 8)]; double r = VLC->creh->creatures[id1]->cost[6], //value of given creature in gold g = VLC->objh->resVals[id2] / effectiveness; //value of wanted resource if(r>g) //if given resource is more expensive than wanted { val2 = ceil(r / g); val1 = 1; } else //if wanted resource is more expensive { val1 = (g / r) + 0.5; val2 = 1; } } break; case EMarketMode::RESOURCE_PLAYER: val1 = 1; val2 = 1; break; case EMarketMode::RESOURCE_ARTIFACT: { double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6); double r = VLC->objh->resVals[id1], //value of offered resource g = VLC->arth->artifacts[id2]->price / effectiveness; //value of bought artifact in gold if(id1 != 6) //non-gold prices are doubled r /= 2; val1 = std::max(1, (int)((g / r) + 0.5)); //don't sell arts for less than 1 resource val2 = 1; } break; case EMarketMode::ARTIFACT_RESOURCE: { double effectiveness = std::min((getMarketEfficiency() + 3.0) / 20.0, 0.6); double r = VLC->arth->artifacts[id1]->price * effectiveness, g = VLC->objh->resVals[id2]; // if(id2 != 6) //non-gold prices are doubled // r /= 2; val1 = 1; val2 = std::max(1, (int)((r / g) + 0.5)); //at least one resource is given in return } break; case EMarketMode::CREATURE_EXP: { val1 = 1; val2 = (VLC->creh->creatures[id1]->AIValue / 40) * 5; } break; case EMarketMode::ARTIFACT_EXP: { val1 = 1; int givenClass = VLC->arth->artifacts[id1]->getArtClassSerial(); if(givenClass < 0 || givenClass > 3) { val2 = 0; return false; } static const int expPerClass[] = {1000, 1500, 3000, 6000}; val2 = expPerClass[givenClass]; } break; default: assert(0); return false; } return true; } bool IMarket::allowsTrade(EMarketMode::EMarketMode mode) const { return false; } int IMarket::availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const { switch(mode) { case EMarketMode::RESOURCE_RESOURCE: case EMarketMode::ARTIFACT_RESOURCE: case EMarketMode::CREATURE_RESOURCE: return -1; default: return 1; } } std::vector IMarket::availableItemsIds(EMarketMode::EMarketMode mode) const { std::vector ret; switch(mode) { case EMarketMode::RESOURCE_RESOURCE: case EMarketMode::ARTIFACT_RESOURCE: case EMarketMode::CREATURE_RESOURCE: for (int i = 0; i < 7; i++) ret.push_back(i); } return ret; } const IMarket * IMarket::castFrom(const CGObjectInstance *obj, bool verbose) { switch(obj->ID) { case Obj::TOWN: return static_cast(obj); case Obj::ALTAR_OF_SACRIFICE: case Obj::BLACK_MARKET: case Obj::TRADING_POST: case Obj::TRADING_POST_SNOW: case Obj::FREELANCERS_GUILD: return static_cast(obj); case Obj::UNIVERSITY: return static_cast(obj); default: if(verbose) logGlobal->error("Cannot cast to IMarket object with ID %d", obj->ID); return nullptr; } } IMarket::IMarket(const CGObjectInstance *O) :o(O) { } std::vector IMarket::availableModes() const { std::vector ret; for (int i = 0; i < EMarketMode::MARTKET_AFTER_LAST_PLACEHOLDER; i++) if(allowsTrade((EMarketMode::EMarketMode)i)) ret.push_back((EMarketMode::EMarketMode)i); return ret; } void CGMarket::onHeroVisit(const CGHeroInstance * h) const { openWindow(OpenWindow::MARKET_WINDOW,id.getNum(),h->id.getNum()); } int CGMarket::getMarketEfficiency() const { return 5; } bool CGMarket::allowsTrade(EMarketMode::EMarketMode mode) const { switch(mode) { case EMarketMode::RESOURCE_RESOURCE: case EMarketMode::RESOURCE_PLAYER: switch(ID) { case Obj::TRADING_POST: case Obj::TRADING_POST_SNOW: return true; default: return false; } case EMarketMode::CREATURE_RESOURCE: return ID == Obj::FREELANCERS_GUILD; //case ARTIFACT_RESOURCE: case EMarketMode::RESOURCE_ARTIFACT: return ID == Obj::BLACK_MARKET; case EMarketMode::ARTIFACT_EXP: case EMarketMode::CREATURE_EXP: return ID == Obj::ALTAR_OF_SACRIFICE; //TODO? check here for alignment of visiting hero? - would not be coherent with other checks here case EMarketMode::RESOURCE_SKILL: return ID == Obj::UNIVERSITY; default: return false; } } int CGMarket::availableUnits(EMarketMode::EMarketMode mode, int marketItemSerial) const { return -1; } std::vector CGMarket::availableItemsIds(EMarketMode::EMarketMode mode) const { switch(mode) { case EMarketMode::RESOURCE_RESOURCE: case EMarketMode::RESOURCE_PLAYER: return IMarket::availableItemsIds(mode); default: return std::vector(); } } CGMarket::CGMarket() :IMarket(this) { } std::vector CGBlackMarket::availableItemsIds(EMarketMode::EMarketMode mode) const { switch(mode) { case EMarketMode::ARTIFACT_RESOURCE: return IMarket::availableItemsIds(mode); case EMarketMode::RESOURCE_ARTIFACT: { std::vector ret; for(const CArtifact *a : artifacts) if(a) ret.push_back(a->id); else ret.push_back(-1); return ret; } default: return std::vector(); } } void CGBlackMarket::newTurn(CRandomGenerator & rand) const { if(!VLC->modh->settings.BLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE) //check if feature changing OH3 behavior is enabled return; if(cb->getDate(Date::DAY_OF_MONTH) != 1) //new month return; SetAvailableArtifacts saa; saa.id = id.getNum(); cb->pickAllowedArtsSet(saa.arts, rand); cb->sendAndApply(&saa); } void CGUniversity::initObj(CRandomGenerator & rand) { std::vector toChoose; for(int i = 0; i < VLC->skillh->size(); ++i) { if(cb->isAllowed(2, i)) { toChoose.push_back(i); } } if(toChoose.size() < 4) { logGlobal->warn("Warning: less then 4 available skills was found by University initializer!"); return; } // get 4 skills for(int i = 0; i < 4; ++i) { // move randomly one skill to selected and remove from list auto it = RandomGeneratorUtil::nextItem(toChoose, rand); skills.push_back(*it); toChoose.erase(it); } } std::vector CGUniversity::availableItemsIds(EMarketMode::EMarketMode mode) const { switch (mode) { case EMarketMode::RESOURCE_SKILL: return skills; default: return std::vector (); } } void CGUniversity::onHeroVisit(const CGHeroInstance * h) const { openWindow(OpenWindow::UNIVERSITY_WINDOW,id.getNum(),h->id.getNum()); }