From 55ff933b7f2eca86c9096ba5360a2e5fb9e92619 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sun, 13 Jul 2014 16:31:00 +0300 Subject: [PATCH] Reducing size of GUI classes. Split trade windows into a separate file --- client/CAdvmapInterface.cpp | 1 + client/CCastleInterface.cpp | 1 + client/CMakeLists.txt | 1 + client/CPlayerInterface.cpp | 1 + client/GUIClasses.cpp | 1475 ---------------------------------- client/GUIClasses.h | 150 ---- client/gui/CTradeWindow.cpp | 1507 +++++++++++++++++++++++++++++++++++ client/gui/CTradeWindow.h | 166 ++++ 8 files changed, 1677 insertions(+), 1625 deletions(-) create mode 100644 client/gui/CTradeWindow.cpp create mode 100644 client/gui/CTradeWindow.h diff --git a/client/CAdvmapInterface.cpp b/client/CAdvmapInterface.cpp index b0db7a16c..e30724549 100644 --- a/client/CAdvmapInterface.cpp +++ b/client/CAdvmapInterface.cpp @@ -30,6 +30,7 @@ #include "CMusicHandler.h" #include "gui/CGuiHandler.h" #include "gui/CIntObjectClasses.h" +#include "gui/CTradeWindow.h" #include "../lib/UnlockGuard.h" #ifdef _MSC_VER diff --git a/client/CCastleInterface.cpp b/client/CCastleInterface.cpp index 394479ca1..1b4548530 100644 --- a/client/CCastleInterface.cpp +++ b/client/CCastleInterface.cpp @@ -24,6 +24,7 @@ #include "../lib/GameConstants.h" #include "gui/CGuiHandler.h" #include "gui/CIntObjectClasses.h" +#include "gui/CTradeWindow.h" using namespace boost::assign; diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index c22075c8c..31062e5e7 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -20,6 +20,7 @@ set(client_SRCS gui/CGuiHandler.cpp gui/CIntObject.cpp gui/CIntObjectClasses.cpp + gui/CTradeWindow.cpp gui/Fonts.cpp gui/Geometries.cpp gui/CCursorHandler.cpp diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 056f0d607..5c3405081 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -13,6 +13,7 @@ #include "CMessage.h" #include "CPlayerInterface.h" #include "gui/SDL_Extensions.h" +#include "gui/CTradeWindow.h" #include "../lib/CConfigHandler.h" #include "battle/CCreatureAnimation.h" #include "Graphics.h" diff --git a/client/GUIClasses.cpp b/client/GUIClasses.cpp index e80a77d73..364ad64c0 100644 --- a/client/GUIClasses.cpp +++ b/client/GUIClasses.cpp @@ -1089,1481 +1089,6 @@ void CObjectListWindow::keyPressed (const SDL_KeyboardEvent & key) changeSelection(sel); } -CTradeWindow::CTradeableItem::CTradeableItem(Point pos, EType Type, int ID, bool Left, int Serial): - CIntObject(LCLICK | HOVER | RCLICK, pos), - type(EType(-1)),// set to invalid, will be corrected in setType - id(ID), - serial(Serial), - left(Left) -{ - downSelection = false; - hlp = nullptr; - image = nullptr; - setType(Type); -} - -void CTradeWindow::CTradeableItem::setType(EType newType) -{ - if (type != newType) - { - OBJ_CONSTRUCTION_CAPTURING_ALL; - type = newType; - delete image; - - if (getIndex() < 0) - { - image = new CAnimImage(getFilename(), 0); - image->disable(); - } - else - image = new CAnimImage(getFilename(), getIndex()); - } -} - -void CTradeWindow::CTradeableItem::setID(int newID) -{ - if (id != newID) - { - id = newID; - if (image) - { - int index = getIndex(); - if (index < 0) - image->disable(); - else - { - image->enable(); - image->setFrame(index); - } - } - } -} - -std::string CTradeWindow::CTradeableItem::getFilename() -{ - switch(type) - { - case RESOURCE: - return "RESOURCE"; - case PLAYER: - return "CREST58"; - case ARTIFACT_TYPE: - case ARTIFACT_PLACEHOLDER: - case ARTIFACT_INSTANCE: - return "artifact"; - case CREATURE: - return "TWCRPORT"; - default: - return ""; - } -} - -int CTradeWindow::CTradeableItem::getIndex() -{ - if (id < 0) - return -1; - - switch(type) - { - case RESOURCE: - case PLAYER: - return id; - case ARTIFACT_TYPE: - case ARTIFACT_INSTANCE: - case ARTIFACT_PLACEHOLDER: - return VLC->arth->artifacts[id]->iconIndex; - case CREATURE: - return VLC->creh->creatures[id]->iconIndex; - default: - return -1; - } -} - -void CTradeWindow::CTradeableItem::showAll(SDL_Surface * to) -{ - Point posToBitmap; - Point posToSubCenter; - - switch(type) - { - case RESOURCE: - posToBitmap = Point(19,9); - posToSubCenter = Point(36, 59); - break; - case CREATURE_PLACEHOLDER: - case CREATURE: - posToSubCenter = Point(29, 76); - if(downSelection) - posToSubCenter.y += 5; - break; - case PLAYER: - posToSubCenter = Point(31, 76); - break; - case ARTIFACT_PLACEHOLDER: - case ARTIFACT_INSTANCE: - posToSubCenter = Point(19, 55); - if(downSelection) - posToSubCenter.y += 8; - break; - case ARTIFACT_TYPE: - posToSubCenter = Point(19, 58); - break; - } - - if (image) - { - image->moveTo(pos.topLeft() + posToBitmap); - CIntObject::showAll(to); - } - - printAtMiddleLoc(subtitle, posToSubCenter, FONT_SMALL, Colors::WHITE, to); -} - -void CTradeWindow::CTradeableItem::clickLeft(tribool down, bool previousState) -{ - CTradeWindow *mw = dynamic_cast(parent); - assert(mw); - if(down) - { - - if(type == ARTIFACT_PLACEHOLDER) - { - CAltarWindow *aw = static_cast(mw); - if(const CArtifactInstance *movedArt = aw->arts->commonInfo->src.art) - { - aw->moveFromSlotToAltar(aw->arts->commonInfo->src.slotID, this, movedArt); - } - else if(const CArtifactInstance *art = getArtInstance()) - { - aw->arts->commonInfo->src.AOH = aw->arts; - aw->arts->commonInfo->src.art = art; - aw->arts->commonInfo->src.slotID = aw->hero->getArtPos(art); - aw->arts->markPossibleSlots(art); - - //aw->arts->commonInfo->dst.AOH = aw->arts; - CCS->curh->dragAndDropCursor(new CAnimImage("artifact", art->artType->iconIndex)); - - aw->arts->artifactsOnAltar.erase(art); - setID(-1); - subtitle = ""; - aw->deal->block(!aw->arts->artifactsOnAltar.size()); - } - - aw->calcTotalExp(); - return; - } - if(left) - { - if(mw->hLeft != this) - mw->hLeft = this; - else - return; - } - else - { - if(mw->hRight != this) - mw->hRight = this; - else - return; - } - mw->selectionChanged(left); - } -} - -void CTradeWindow::CTradeableItem::showAllAt(const Point &dstPos, const std::string &customSub, SDL_Surface * to) -{ - Rect oldPos = pos; - std::string oldSub = subtitle; - downSelection = true; - - moveTo(dstPos); - subtitle = customSub; - showAll(to); - - downSelection = false; - moveTo(oldPos.topLeft()); - subtitle = oldSub; -} - -void CTradeWindow::CTradeableItem::hover(bool on) -{ - if(!on) - { - GH.statusbar->clear(); - return; - } - - switch(type) - { - case CREATURE: - case CREATURE_PLACEHOLDER: - GH.statusbar->setText(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->creatures[id]->namePl)); - break; - case ARTIFACT_PLACEHOLDER: - if(id < 0) - GH.statusbar->setText(CGI->generaltexth->zelp[582].first); - else - GH.statusbar->setText(CGI->arth->artifacts[id]->Name()); - break; - } -} - -void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState) -{ - if(down) - { - switch(type) - { - case CREATURE: - case CREATURE_PLACEHOLDER: - //GH.statusbar->print(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->creatures[id]->namePl)); - break; - case ARTIFACT_TYPE: - case ARTIFACT_PLACEHOLDER: - if(id >= 0) - adventureInt->handleRightClick(CGI->arth->artifacts[id]->Description(), down); - break; - } - } -} - -std::string CTradeWindow::CTradeableItem::getName(int number /*= -1*/) const -{ - switch(type) - { - case PLAYER: - return CGI->generaltexth->capColors[id]; - case RESOURCE: - return CGI->generaltexth->restypes[id]; - case CREATURE: - if(number == 1) - return CGI->creh->creatures[id]->nameSing; - else - return CGI->creh->creatures[id]->namePl; - case ARTIFACT_TYPE: - case ARTIFACT_INSTANCE: - return CGI->arth->artifacts[id]->Name(); - } - assert(0); - return ""; -} - -const CArtifactInstance * CTradeWindow::CTradeableItem::getArtInstance() const -{ - switch(type) - { - case ARTIFACT_PLACEHOLDER: - case ARTIFACT_INSTANCE: - return (const CArtifactInstance *)hlp; - default: - return nullptr; - } -} - -void CTradeWindow::CTradeableItem::setArtInstance(const CArtifactInstance *art) -{ - assert(type == ARTIFACT_PLACEHOLDER || type == ARTIFACT_INSTANCE); - hlp = art; - if(art) - setID(art->artType->id); - else - setID(-1); -} - -CTradeWindow::CTradeWindow(std::string bgName, const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode): - CWindowObject(PLAYER_COLORED, bgName), - market(Market), - hero(Hero), - arts(nullptr), - hLeft(nullptr), - hRight(nullptr), - readyToTrade(false) -{ - type |= BLOCK_ADV_HOTKEYS; - mode = Mode; - initTypes(); -} - -void CTradeWindow::initTypes() -{ - switch(mode) - { - case EMarketMode::RESOURCE_RESOURCE: - itemsType[1] = RESOURCE; - itemsType[0] = RESOURCE; - break; - case EMarketMode::RESOURCE_PLAYER: - itemsType[1] = RESOURCE; - itemsType[0] = PLAYER; - break; - case EMarketMode::CREATURE_RESOURCE: - itemsType[1] = CREATURE; - itemsType[0] = RESOURCE; - break; - case EMarketMode::RESOURCE_ARTIFACT: - itemsType[1] = RESOURCE; - itemsType[0] = ARTIFACT_TYPE; - break; - case EMarketMode::ARTIFACT_RESOURCE: - itemsType[1] = ARTIFACT_INSTANCE; - itemsType[0] = RESOURCE; - break; - case EMarketMode::CREATURE_EXP: - itemsType[1] = CREATURE; - itemsType[0] = CREATURE_PLACEHOLDER; - break; - case EMarketMode::ARTIFACT_EXP: - itemsType[1] = ARTIFACT_TYPE; - itemsType[0] = ARTIFACT_PLACEHOLDER; - break; - } -} - -void CTradeWindow::initItems(bool Left) -{ - if(Left && (itemsType[1] == ARTIFACT_TYPE || itemsType[1] == ARTIFACT_INSTANCE)) - { - int xOffset = 0, yOffset = 0; - if(mode == EMarketMode::ARTIFACT_RESOURCE) - { - xOffset = -361; - yOffset = +46; - - auto hlp = new CTradeableItem(Point(137, 469), itemsType[Left], -1, 1, 0); - hlp->recActions &= ~(UPDATE | SHOWALL); - items[Left].push_back(hlp); - } - else //ARTIFACT_EXP - { - xOffset = -363; - yOffset = -12; - } - - BLOCK_CAPTURING; - arts = new CArtifactsOfHero(Point(pos.x+xOffset, pos.y+yOffset)); - arts->commonInfo = new CArtifactsOfHero::SCommonPart; - arts->commonInfo->participants.insert(arts); - arts->recActions = 255; - arts->setHero(hero); - arts->allowedAssembling = false; - addChild(arts); - artSets.push_back(arts); - - if(mode == EMarketMode::ARTIFACT_RESOURCE) - arts->highlightModeCallback = boost::bind(&CTradeWindow::artifactSelected, this, _1); - return; - } - - std::vector *ids = getItemsIds(Left); - std::vector pos; - int amount = -1; - - getPositionsFor(pos, Left, itemsType[Left]); - - if(Left || !ids) - amount = 7; - else - amount = ids->size(); - - if(ids) - vstd::amin(amount, ids->size()); - - for(int j=0; jsize()>j) ? (*ids)[j] : j; - if(id < 0 && mode != EMarketMode::ARTIFACT_EXP) //when sacrificing artifacts we need to prepare empty slots - continue; - - auto hlp = new CTradeableItem(pos[j].topLeft(), itemsType[Left], id, Left, j); - hlp->pos = pos[j] + this->pos.topLeft(); - items[Left].push_back(hlp); - } - - initSubs(Left); -} - -std::vector *CTradeWindow::getItemsIds(bool Left) -{ - std::vector *ids = nullptr; - - if(mode == EMarketMode::ARTIFACT_EXP) - return new std::vector(22, -1); - - if(Left) - { - switch(itemsType[1]) - { - case CREATURE: - ids = new std::vector; - for(int i = 0; i < 7; i++) - { - if(const CCreature *c = hero->getCreature(SlotID(i))) - ids->push_back(c->idNumber); - else - ids->push_back(-1); - } - break; - } - } - else - { - switch(itemsType[0]) - { - case PLAYER: - ids = new std::vector; - for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++) - if(PlayerColor(i) != LOCPLINT->playerID && LOCPLINT->cb->getPlayerStatus(PlayerColor(i)) == EPlayerStatus::INGAME) - ids->push_back(i); - break; - - case ARTIFACT_TYPE: - ids = new std::vector(market->availableItemsIds(mode)); - break; - } - } - - return ids; -} - -void CTradeWindow::getPositionsFor(std::vector &poss, bool Left, EType type) const -{ - if(mode == EMarketMode::ARTIFACT_EXP && !Left) - { - //22 boxes, 5 in row, last row: two boxes centered - int h, w, x, y, dx, dy; - h = w = 44; - x = 317; - y = 53; - dx = 54; - dy = 70; - for (int i = 0; i < 4 ; i++) - for (int j = 0; j < 5 ; j++) - poss += Rect(x + dx*j, y + dy*i, w, h); - - poss += Rect(x + dx*1.5, y + dy*4, w, h); - poss += Rect(x + dx*2.5, y + dy*4, w, h); - } - else - { - //seven boxes: - // X X X - // X X X - // X - int h, w, x, y, dx, dy; - int leftToRightOffset; - getBaseForPositions(type, dx, dy, x, y, h, w, !Left, leftToRightOffset); - - poss += genRect(h, w, x, y), genRect(h, w, x + dx, y), genRect(h, w, x + 2*dx, y), - genRect(h, w, x, y + dy), genRect(h, w, x + dx, y + dy), genRect(h, w, x + 2*dx, y + dy), - genRect(h, w, x + dx, y + 2*dy); - - if(!Left) - { - for(Rect &r : poss) - r.x += leftToRightOffset; - } - } -} - -void CTradeWindow::initSubs(bool Left) -{ - for(CTradeableItem *t : items[Left]) - { - if(Left) - { - switch(itemsType[1]) - { - case CREATURE: - t->subtitle = boost::lexical_cast(hero->getStackCount(SlotID(t->serial))); - break; - case RESOURCE: - t->subtitle = boost::lexical_cast(LOCPLINT->cb->getResourceAmount(static_cast(t->serial))); - break; - } - } - else //right side - { - if(itemsType[0] == PLAYER) - { - t->subtitle = CGI->generaltexth->capColors[t->id]; - } - else if(hLeft)//artifact, creature - { - int h1, h2; //hlp variables for getting offer - market->getOffer(hLeft->id, t->id, h1, h2, mode); - if(t->id != hLeft->id || mode != EMarketMode::RESOURCE_RESOURCE) //don't allow exchanging same resources - { - std::ostringstream oss; - oss << h2; - if(h1!=1) - oss << "/" << h1; - t->subtitle = oss.str(); - } - else - t->subtitle = CGI->generaltexth->allTexts[164]; // n/a - } - else - t->subtitle = ""; - } - } -} - -void CTradeWindow::showAll(SDL_Surface * to) -{ - CWindowObject::showAll(to); - - if(hRight) - CSDL_Ext::drawBorder(to,hRight->pos.x-1,hRight->pos.y-1,hRight->pos.w+2,hRight->pos.h+2,int3(255,231,148)); - if(hLeft && hLeft->type != ARTIFACT_INSTANCE) - CSDL_Ext::drawBorder(to,hLeft->pos.x-1,hLeft->pos.y-1,hLeft->pos.w+2,hLeft->pos.h+2,int3(255,231,148)); - - if(readyToTrade) - { - hLeft->showAllAt(pos.topLeft() + selectionOffset(true), selectionSubtitle(true), to); - hRight->showAllAt(pos.topLeft() + selectionOffset(false), selectionSubtitle(false), to); - } -} - -void CTradeWindow::removeItems(const std::set &toRemove) -{ - for(CTradeableItem *t : toRemove) - removeItem(t); -} - -void CTradeWindow::removeItem(CTradeableItem * t) -{ - items[t->left] -= t; - delete t; - - if(hRight == t) - { - hRight = nullptr; - selectionChanged(false); - } -} - -void CTradeWindow::getEmptySlots(std::set &toRemove) -{ - for(CTradeableItem *t : items[1]) - if(!hero->getStackCount(SlotID(t->serial))) - toRemove.insert(t); -} - -void CTradeWindow::setMode(EMarketMode::EMarketMode Mode) -{ - const IMarket *m = market; - const CGHeroInstance *h = hero; - CTradeWindow *nwindow = nullptr; - - GH.popIntTotally(this); - - switch(Mode) - { - case EMarketMode::CREATURE_EXP: - case EMarketMode::ARTIFACT_EXP: - nwindow = new CAltarWindow(m, h, Mode); - break; - default: - nwindow = new CMarketplaceWindow(m, h, Mode); - break; - } - - GH.pushInt(nwindow); -} - -void CTradeWindow::artifactSelected(CArtPlace *slot) -{ - assert(mode == EMarketMode::ARTIFACT_RESOURCE); - items[1][0]->setArtInstance(slot->ourArt); - if(slot->ourArt) - hLeft = items[1][0]; - else - hLeft = nullptr; - - selectionChanged(true); -} - -std::string CMarketplaceWindow::getBackgroundForMode(EMarketMode::EMarketMode mode) -{ - switch(mode) - { - case EMarketMode::RESOURCE_RESOURCE: - return "TPMRKRES.bmp"; - case EMarketMode::RESOURCE_PLAYER: - return "TPMRKPTS.bmp"; - case EMarketMode::CREATURE_RESOURCE: - return "TPMRKCRS.bmp"; - case EMarketMode::RESOURCE_ARTIFACT: - return "TPMRKABS.bmp"; - case EMarketMode::ARTIFACT_RESOURCE: - return "TPMRKASS.bmp"; - } - assert(0); - return ""; -} - -CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode) - : CTradeWindow(getBackgroundForMode(Mode), Market, Hero, Mode) -{ - OBJ_CONSTRUCTION_CAPTURING_ALL; - - madeTransaction = false; - bool sliderNeeded = true; - - new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); - - std::string title; - - if (market->o->ID == Obj::TOWN) - { - switch (mode) - { - break; case EMarketMode::CREATURE_RESOURCE: - title = CGI->townh->factions[ETownType::STRONGHOLD]->town->buildings[BuildingID::FREELANCERS_GUILD]->Name(); - - break; case EMarketMode::RESOURCE_ARTIFACT: - title = CGI->townh->factions[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name(); - sliderNeeded = false; - - break; case EMarketMode::ARTIFACT_RESOURCE: - title = CGI->townh->factions[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name(); - sliderNeeded = false; - - break; default: - title = CGI->generaltexth->allTexts[158]; - } - } - else - { - switch (market->o->ID) - { - break; case Obj::BLACK_MARKET: title = CGI->generaltexth->allTexts[349]; - break; case Obj::TRADING_POST: title = CGI->generaltexth->allTexts[159]; - break; case Obj::TRADING_POST_SNOW: title = CGI->generaltexth->allTexts[159]; - break; default: title = market->o->getObjectName(); - } - } - - new CLabel(300, 27, FONT_BIG, CENTER, Colors::YELLOW, title); - - initItems(false); - initItems(true); - - ok = new CAdventureMapButton(CGI->generaltexth->zelp[600],boost::bind(&CGuiHandler::popIntTotally,&GH,this),516,520,"IOK6432.DEF",SDLK_RETURN); - ok->assignedKeys.insert(SDLK_ESCAPE); - deal = new CAdventureMapButton(CGI->generaltexth->zelp[595],boost::bind(&CMarketplaceWindow::makeDeal,this),307,520,"TPMRKB.DEF"); - deal->block(true); - - if(sliderNeeded) - { - slider = new CSlider(231,490,137,nullptr,0,0); - slider->moved = boost::bind(&CMarketplaceWindow::sliderMoved,this,_1); - max = new CAdventureMapButton(CGI->generaltexth->zelp[596],boost::bind(&CMarketplaceWindow::setMax,this),229,520,"IRCBTNS.DEF"); - max->block(true); - } - else - { - slider = nullptr; - max = nullptr; - deal->moveBy(Point(-30, 0)); - } - - Rect traderTextRect; - - //left side - switch(Mode) - { - case EMarketMode::RESOURCE_RESOURCE: - case EMarketMode::RESOURCE_PLAYER: - case EMarketMode::RESOURCE_ARTIFACT: - new CLabel(154, 148, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[270]); - break; - - case EMarketMode::CREATURE_RESOURCE: - //%s's Creatures - new CLabel(152, 102, FONT_SMALL, CENTER, Colors::WHITE, - boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name)); - break; - case EMarketMode::ARTIFACT_RESOURCE: - //%s's Artifacts - new CLabel(152, 102, FONT_SMALL, CENTER, Colors::WHITE, - boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name)); - break; - } - - //right side - switch(Mode) - { - case EMarketMode::RESOURCE_RESOURCE: - case EMarketMode::CREATURE_RESOURCE: - case EMarketMode::RESOURCE_ARTIFACT: - case EMarketMode::ARTIFACT_RESOURCE: - new CLabel(445, 148, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[168]); - traderTextRect = Rect(316, 48, 260, 75); - break; - case EMarketMode::RESOURCE_PLAYER: - new CLabel(445, 55, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[169]); - traderTextRect = Rect(28, 48, 260, 75); - break; - } - - traderText = new CTextBox("", traderTextRect, 0, FONT_SMALL, CENTER); - int specialOffset = mode == EMarketMode::ARTIFACT_RESOURCE ? 35 : 0; //in selling artifacts mode we need to move res-res and art-res buttons down - - if(printButtonFor(EMarketMode::RESOURCE_PLAYER)) - new CAdventureMapButton(CGI->generaltexth->zelp[612],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_PLAYER), 18, 520,"TPMRKBU1.DEF"); - if(printButtonFor(EMarketMode::RESOURCE_RESOURCE)) - new CAdventureMapButton(CGI->generaltexth->zelp[605],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_RESOURCE), 516, 450 + specialOffset,"TPMRKBU5.DEF"); - if(printButtonFor(EMarketMode::CREATURE_RESOURCE)) - new CAdventureMapButton(CGI->generaltexth->zelp[599],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::CREATURE_RESOURCE), 516, 485,"TPMRKBU4.DEF"); //was y=450, changed to not overlap res-res in some conditions - if(printButtonFor(EMarketMode::RESOURCE_ARTIFACT)) - new CAdventureMapButton(CGI->generaltexth->zelp[598],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_ARTIFACT), 18, 450 + specialOffset,"TPMRKBU2.DEF"); - if(printButtonFor(EMarketMode::ARTIFACT_RESOURCE)) - new CAdventureMapButton(CGI->generaltexth->zelp[613],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::ARTIFACT_RESOURCE), 18, 485,"TPMRKBU3.DEF"); //was y=450, changed to not overlap res-art in some conditions - - updateTraderText(); -} - -CMarketplaceWindow::~CMarketplaceWindow() -{ - hLeft = hRight = nullptr; - for(auto & elem : items[1]) - delete elem; - for(auto & elem : items[0]) - delete elem; - - items[1].clear(); - items[0].clear(); -} - - - -void CMarketplaceWindow::setMax() -{ - slider->moveToMax(); -} - -void CMarketplaceWindow::makeDeal() -{ - int sliderValue = 0; - if(slider) - sliderValue = slider->value; - else - sliderValue = !deal->isBlocked(); //should always be 1 - - if(!sliderValue) - return; - - int leftIdToSend = -1; - switch (mode) - { - case EMarketMode::CREATURE_RESOURCE: - leftIdToSend = hLeft->serial; - break; - case EMarketMode::ARTIFACT_RESOURCE: - leftIdToSend = hLeft->getArtInstance()->id.getNum(); - break; - default: - leftIdToSend = hLeft->id; - break; - } - - if(slider) - { - LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, slider->value*r1, hero); - slider->moveTo(0); - } - else - { - LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, r2, hero); - } - madeTransaction = true; - - hLeft = nullptr; - hRight = nullptr; - selectionChanged(true); -} - -void CMarketplaceWindow::sliderMoved( int to ) -{ - redraw(); -} - -void CMarketplaceWindow::selectionChanged(bool side) -{ - readyToTrade = hLeft && hRight; - if(mode == EMarketMode::RESOURCE_RESOURCE) - readyToTrade = readyToTrade && (hLeft->id != hRight->id); //for resource trade, two DIFFERENT resources must be selected - - if(mode == EMarketMode::ARTIFACT_RESOURCE && !hLeft) - arts->unmarkSlots(false); - - if(readyToTrade) - { - int soldItemId = hLeft->id; - market->getOffer(soldItemId, hRight->id, r1, r2, mode); - - if(slider) - { - int newAmount = -1; - if(itemsType[1] == RESOURCE) - newAmount = LOCPLINT->cb->getResourceAmount(static_cast(soldItemId)); - else if(itemsType[1] == CREATURE) - newAmount = hero->getStackCount(SlotID(hLeft->serial)) - (hero->Slots().size() == 1 && hero->needsLastStack()); - else - assert(0); - - slider->setAmount(newAmount / r1); - slider->moveTo(0); - max->block(false); - deal->block(false); - } - else if(itemsType[1] == RESOURCE) //buying -> check if we can afford transaction - { - deal->block(LOCPLINT->cb->getResourceAmount(static_cast(soldItemId)) < r1); - } - else - deal->block(false); - } - else - { - if(slider) - { - max->block(true); - slider->setAmount(0); - slider->moveTo(0); - } - deal->block(true); - } - - if(side && itemsType[0] != PLAYER) //items[1] selection changed, recalculate offers - initSubs(false); - - updateTraderText(); - redraw(); -} - -bool CMarketplaceWindow::printButtonFor(EMarketMode::EMarketMode M) const -{ - return market->allowsTrade(M) && M != mode && (hero || ( M != EMarketMode::CREATURE_RESOURCE && M != EMarketMode::RESOURCE_ARTIFACT && M != EMarketMode::ARTIFACT_RESOURCE )); -} - -void CMarketplaceWindow::garrisonChanged() -{ - if(mode != EMarketMode::CREATURE_RESOURCE) - return; - - std::set toRemove; - getEmptySlots(toRemove); - - - removeItems(toRemove); - initSubs(true); -} - -void CMarketplaceWindow::artifactsChanged(bool Left) -{ - assert(!Left); - if(mode != EMarketMode::RESOURCE_ARTIFACT) - return; - - std::vector available = market->availableItemsIds(mode); - std::set toRemove; - for(CTradeableItem *t : items[0]) - if(!vstd::contains(available, t->id)) - toRemove.insert(t); - - removeItems(toRemove); - redraw(); -} - -std::string CMarketplaceWindow::selectionSubtitle(bool Left) const -{ - if(Left) - { - switch(itemsType[1]) - { - case RESOURCE: - case CREATURE: - { - int val = slider - ? slider->value * r1 - : (((deal->isBlocked())) ? 0 : r1); - - return boost::lexical_cast(val); - } - case ARTIFACT_INSTANCE: - return ((deal->isBlocked()) ? "0" : "1"); - } - } - else - { - switch(itemsType[0]) - { - case RESOURCE: - if(slider) - return boost::lexical_cast( slider->value * r2 ); - else - return boost::lexical_cast(r2); - case ARTIFACT_TYPE: - return ((deal->isBlocked()) ? "0" : "1"); - case PLAYER: - return (hRight ? CGI->generaltexth->capColors[hRight->id] : ""); - } - } - - return "???"; -} - -Point CMarketplaceWindow::selectionOffset(bool Left) const -{ - if(Left) - { - switch(itemsType[1]) - { - case RESOURCE: - return Point(122, 446); - case CREATURE: - return Point(128, 450); - case ARTIFACT_INSTANCE: - return Point(134, 466); - } - } - else - { - switch(itemsType[0]) - { - case RESOURCE: - if(mode == EMarketMode::ARTIFACT_RESOURCE) - return Point(410, 469); - else - return Point(410, 446); - case ARTIFACT_TYPE: - return Point(425, 447); - case PLAYER: - return Point(417, 451); - } - } - - assert(0); - return Point(0,0); -} - -void CMarketplaceWindow::resourceChanged(int type, int val) -{ - initSubs(true); -} - -void CMarketplaceWindow::getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const -{ - switch(type) - { - case RESOURCE: - dx = 82; - dy = 79; - x = 39; - y = 180; - h = 66; - w = 74; - break; - case PLAYER: - dx = 83; - dy = 118; - h = 64; - w = 58; - x = 44; - y = 83; - assert(Right); - break; - case CREATURE://45,123 - x = 45; - y = 123; - w = 58; - h = 64; - dx = 83; - dy = 98; - assert(!Right); - break; - case ARTIFACT_TYPE://45,123 - x = 340-289; - y = 180; - w = 44; - h = 44; - dx = 83; - dy = 79; - break; - } - - leftToRightOffset = 289; -} - -void CMarketplaceWindow::updateTraderText() -{ - if(readyToTrade) - { - if(mode == EMarketMode::RESOURCE_PLAYER) - { - //I can give %s to the %s player. - traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[165]) % hLeft->getName() % hRight->getName())); - } - else if(mode == EMarketMode::RESOURCE_ARTIFACT) - { - //I can offer you the %s for %d %s of %s. - traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[267]) % hRight->getName() % r1 % CGI->generaltexth->allTexts[160 + (r1==1)] % hLeft->getName())); - } - else if(mode == EMarketMode::RESOURCE_RESOURCE) - { - //I can offer you %d %s of %s for %d %s of %s. - traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[157]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % r1 % CGI->generaltexth->allTexts[160 + (r1==1)] % hLeft->getName())); - } - else if(mode == EMarketMode::CREATURE_RESOURCE) - { - //I can offer you %d %s of %s for %d %s. - traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[269]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % r1 % hLeft->getName(r1))); - } - else if(mode == EMarketMode::ARTIFACT_RESOURCE) - { - //I can offer you %d %s of %s for your %s. - traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[268]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % hLeft->getName(r1))); - } - return; - } - - int gnrtxtnr = -1; - if(madeTransaction) - { - if(mode == EMarketMode::RESOURCE_PLAYER) - gnrtxtnr = 166; //Are there any other resources you'd like to give away? - else - gnrtxtnr = 162; //You have received quite a bargain. I expect to make no profit on the deal. Can I interest you in any of my other wares? - } - else - { - if(mode == EMarketMode::RESOURCE_PLAYER) - gnrtxtnr = 167; //If you'd like to give any of your resources to another player, click on the item you wish to give and to whom. - else - gnrtxtnr = 163; //Please inspect our fine wares. If you feel like offering a trade, click on the items you wish to trade with and for. - } - traderText->setText(CGI->generaltexth->allTexts[gnrtxtnr]); -} - -CAltarWindow::CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero /*= nullptr*/, EMarketMode::EMarketMode Mode) - :CTradeWindow((Mode == EMarketMode::CREATURE_EXP ? "ALTARMON.bmp" : "ALTRART2.bmp"), Market, Hero, Mode) -{ - OBJ_CONSTRUCTION_CAPTURING_ALL; - if(Mode == EMarketMode::CREATURE_EXP) - { - //%s's Creatures - new CLabel(155, 30, FONT_SMALL, CENTER, Colors::YELLOW, - boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name)); - - //Altar of Sacrifice - new CLabel(450, 30, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[479]); - - //To sacrifice creatures, move them from your army on to the Altar and click Sacrifice - new CTextBox(CGI->generaltexth->allTexts[480], Rect(320, 56, 256, 40), 0, FONT_SMALL, CENTER, Colors::YELLOW); - - slider = new CSlider(231,481,137,nullptr,0,0); - slider->moved = boost::bind(&CAltarWindow::sliderMoved,this,_1); - max = new CAdventureMapButton(CGI->generaltexth->zelp[578],boost::bind(&CSlider::moveToMax, slider),147,520,"IRCBTNS.DEF"); - - sacrificedUnits.resize(GameConstants::ARMY_SIZE, 0); - sacrificeAll = new CAdventureMapButton(CGI->generaltexth->zelp[579],boost::bind(&CAltarWindow::SacrificeAll,this),393,520,"ALTARMY.DEF"); - sacrificeBackpack = nullptr; - - initItems(true); - mimicCres(); - artIcon = nullptr; - } - else - { - //Sacrifice artifacts for experience - new CLabel(450, 34, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[477]); - //%s's Creatures - new CLabel(302, 423, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[478]); - - sacrificeAll = new CAdventureMapButton(CGI->generaltexth->zelp[571], boost::bind(&CAltarWindow::SacrificeAll,this),393,520,"ALTFILL.DEF"); - sacrificeAll->block(hero->artifactsInBackpack.empty() && hero->artifactsWorn.empty()); - sacrificeBackpack = new CAdventureMapButton(CGI->generaltexth->zelp[570],boost::bind(&CAltarWindow::SacrificeBackpack,this),147,520,"ALTEMBK.DEF"); - sacrificeBackpack->block(hero->artifactsInBackpack.empty()); - - slider = nullptr; - max = nullptr; - - initItems(true); - initItems(false); - artIcon = new CAnimImage("ARTIFACT", 0, 0, 281, 442); - artIcon->disable(); - } - - //Experience needed to reach next level - new CTextBox(CGI->generaltexth->allTexts[475], Rect(15, 415, 125, 50), 0, FONT_SMALL, CENTER, Colors::YELLOW); - //Total experience on the Altar - new CTextBox(CGI->generaltexth->allTexts[476], Rect(15, 495, 125, 40), 0, FONT_SMALL, CENTER, Colors::YELLOW); - - new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); - - ok = new CAdventureMapButton(CGI->generaltexth->zelp[568],boost::bind(&CGuiHandler::popIntTotally,&GH,this),516,520,"IOK6432.DEF",SDLK_RETURN); - ok->assignedKeys.insert(SDLK_ESCAPE); - - deal = new CAdventureMapButton(CGI->generaltexth->zelp[585],boost::bind(&CAltarWindow::makeDeal,this),269,520,"ALTSACR.DEF"); - - if(Hero->getAlignment() != ::EAlignment::EVIL && Mode == EMarketMode::CREATURE_EXP) - new CAdventureMapButton(CGI->generaltexth->zelp[580], boost::bind(&CTradeWindow::setMode,this, EMarketMode::ARTIFACT_EXP), 516, 421, "ALTART.DEF"); - if(Hero->getAlignment() != ::EAlignment::GOOD && Mode == EMarketMode::ARTIFACT_EXP) - new CAdventureMapButton(CGI->generaltexth->zelp[572], boost::bind(&CTradeWindow::setMode,this, EMarketMode::CREATURE_EXP), 516, 421, "ALTSACC.DEF"); - - expPerUnit.resize(GameConstants::ARMY_SIZE, 0); - getExpValues(); - - expToLevel = new CLabel(73, 475, FONT_SMALL, CENTER); - expOnAltar = new CLabel(73, 543, FONT_SMALL, CENTER); - - setExpToLevel(); - calcTotalExp(); - blockTrade(); -} - -CAltarWindow::~CAltarWindow() -{ - -} - -void CAltarWindow::getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const -{ - leftToRightOffset = 289; - x = 45; - y = 110; - w = 58; - h = 64; - dx = 83; - dy = 98; -} - -void CAltarWindow::sliderMoved(int to) -{ - sacrificedUnits[hLeft->serial] = to; - updateRight(hRight); - deal->block(!to); - calcTotalExp(); - redraw(); -} - -void CAltarWindow::makeDeal() -{ - if(mode == EMarketMode::CREATURE_EXP) - { - blockTrade(); - slider->value = 0; - - std::vector toSacrifice = sacrificedUnits; - for (int i = 0; i < toSacrifice.size(); i++) - { - if(toSacrifice[i]) - LOCPLINT->cb->trade(market->o, mode, i, 0, toSacrifice[i], hero); - } - - for(int& val : sacrificedUnits) - val = 0; - - for(CTradeableItem *t : items[0]) - { - t->setType(CREATURE_PLACEHOLDER); - t->subtitle = ""; - } - } - else - { - for(const CArtifactInstance *art : arts->artifactsOnAltar) //sacrifice each artifact on the list - { - LOCPLINT->cb->trade(market->o, mode, hero->getArtPos(art), -1, 1, hero); - } - arts->artifactsOnAltar.clear(); - - for(CTradeableItem *t : items[0]) - { - t->setID(-1); - t->subtitle = ""; - } - - arts->commonInfo->reset(); - //arts->scrollBackpack(0); - deal->block(true); - } - - calcTotalExp(); -} - -void CAltarWindow::SacrificeAll() -{ - if(mode == EMarketMode::CREATURE_EXP) - { - bool movedAnything = false; - for(CTradeableItem *t : items[1]) - sacrificedUnits[t->serial] = hero->getStackCount(SlotID(t->serial)); - - sacrificedUnits[items[1].front()->serial]--; - - for(CTradeableItem *t : items[0]) - { - updateRight(t); - if(t->type == CREATURE) - movedAnything = true; - } - - deal->block(!movedAnything); - calcTotalExp(); - } - else - { - for(auto i = hero->artifactsWorn.cbegin(); i != hero->artifactsWorn.cend(); i++) - { - if(i->second.artifact->artType->id != ArtifactID::ART_LOCK) //ignore locks from assembled artifacts - moveFromSlotToAltar(i->first, nullptr, i->second.artifact); - } - - SacrificeBackpack(); - } - redraw(); -} - -void CAltarWindow::selectionChanged(bool side) -{ - if(mode != EMarketMode::CREATURE_EXP) - return; - - CTradeableItem *&selected = side ? hLeft : hRight; - CTradeableItem *&theOther = side ? hRight : hLeft; - - theOther = *std::find_if(items[!side].begin(), items[!side].end(), [&](const CTradeableItem * item) - { - return item->serial == selected->serial; - }); - - int stackCount = 0; - for (int i = 0; i < GameConstants::ARMY_SIZE; i++) - if(hero->getStackCount(SlotID(i)) > sacrificedUnits[i]) - stackCount++; - - slider->setAmount(hero->getStackCount(SlotID(hLeft->serial)) - (stackCount == 1)); - slider->block(!slider->amount); - slider->value = sacrificedUnits[hLeft->serial]; - max->block(!slider->amount); - readyToTrade = true; - redraw(); -} - -void CAltarWindow::mimicCres() -{ - std::vector positions; - getPositionsFor(positions, false, CREATURE); - - for(CTradeableItem *t : items[1]) - { - auto hlp = new CTradeableItem(positions[t->serial].topLeft(), CREATURE_PLACEHOLDER, t->id, false, t->serial); - hlp->pos = positions[t->serial] + this->pos.topLeft(); - items[0].push_back(hlp); - } -} - -Point CAltarWindow::selectionOffset(bool Left) const -{ - if(Left) - return Point(150, 421); - else - return Point(396, 421); -} - -std::string CAltarWindow::selectionSubtitle(bool Left) const -{ - if(Left && slider && hLeft) - return boost::lexical_cast(slider->value); - else if(!Left && hRight) - return hRight->subtitle; - else - return ""; -} - -void CAltarWindow::artifactsChanged(bool left) -{ - -} - -void CAltarWindow::garrisonChanged() -{ - if(mode != EMarketMode::CREATURE_EXP) - return; - - std::set empty; - getEmptySlots(empty); - - for(CTradeableItem *t : empty) - { - removeItem(*std::find_if(items[0].begin(), items[0].end(), [&](const CTradeableItem * item) - { - return item->serial == t->serial; - })); - } - - initSubs(true); - getExpValues(); -} - -void CAltarWindow::getExpValues() -{ - int dump; - for(CTradeableItem *t : items[1]) - if(t->id >= 0) - market->getOffer(t->id, 0, dump, expPerUnit[t->serial], EMarketMode::CREATURE_EXP); -} - -void CAltarWindow::calcTotalExp() -{ - int val = 0; - if(mode == EMarketMode::CREATURE_EXP) - { - for (int i = 0; i < sacrificedUnits.size(); i++) - { - val += expPerUnit[i] * sacrificedUnits[i]; - } - } - else - { - for(const CArtifactInstance *art : arts->artifactsOnAltar) - { - int dmp, valOfArt; - market->getOffer(art->artType->id, 0, dmp, valOfArt, mode); - val += valOfArt; //WAS val += valOfArt * arts->artifactsOnAltar.count(*i); - } - } - val = hero->calculateXp(val); - expOnAltar->setText(boost::lexical_cast(val)); -} - -void CAltarWindow::setExpToLevel() -{ - expToLevel->setText(boost::lexical_cast(CGI->heroh->reqExp(CGI->heroh->level(hero->exp)+1) - hero->exp)); -} - -void CAltarWindow::blockTrade() -{ - hLeft = hRight = nullptr; - readyToTrade = false; - if(slider) - { - slider->block(true); - max->block(true); - } - deal->block(true); -} - -void CAltarWindow::updateRight(CTradeableItem *toUpdate) -{ - int val = sacrificedUnits[toUpdate->serial]; - toUpdate->setType(val ? CREATURE : CREATURE_PLACEHOLDER); - toUpdate->subtitle = val ? boost::str(boost::format(CGI->generaltexth->allTexts[122]) % boost::lexical_cast(val * expPerUnit[toUpdate->serial])) : ""; //%s exp -} - -int CAltarWindow::firstFreeSlot() -{ - int ret = -1; - while(items[0][++ret]->id >= 0 && ret + 1 < items[0].size()); - return ret < items[0].size() ? ret : -1; -} - -void CAltarWindow::SacrificeBackpack() -{ - std::multiset toOmmit = arts->artifactsOnAltar; - - for (auto & elem : hero->artifactsInBackpack) - { - - if(vstd::contains(toOmmit, elem.artifact)) - { - toOmmit -= elem.artifact; - continue; - } - - putOnAltar(nullptr, elem.artifact); - } - - arts->scrollBackpack(0); - calcTotalExp(); -} - -void CAltarWindow::artifactPicked() -{ - redraw(); -} - -void CAltarWindow::showAll(SDL_Surface * to) -{ - CTradeWindow::showAll(to); - if(mode == EMarketMode::ARTIFACT_EXP && arts && arts->commonInfo->src.art) - { - artIcon->setFrame(arts->commonInfo->src.art->artType->iconIndex); - artIcon->showAll(to); - - int dmp, val; - market->getOffer(arts->commonInfo->src.art->artType->id, 0, dmp, val, EMarketMode::ARTIFACT_EXP); - printAtMiddleLoc(boost::lexical_cast(val), 304, 498, FONT_SMALL, Colors::WHITE, to); - } -} - -bool CAltarWindow::putOnAltar(CTradeableItem* altarSlot, const CArtifactInstance *art) -{ - int artID = art->artType->id; - if(artID != 1 && artID < 7) //special art - { - logGlobal->warnStream() << "Cannot put special artifact on altar!"; - return false; - } - - if(!altarSlot) - { - int slotIndex = firstFreeSlot(); - if(slotIndex < 0) - { - logGlobal->warnStream() << "No free slots on altar!"; - return false; - } - altarSlot = items[0][slotIndex]; - } - - int dmp, val; - market->getOffer(artID, 0, dmp, val, EMarketMode::ARTIFACT_EXP); - - arts->artifactsOnAltar.insert(art); - altarSlot->setArtInstance(art); - altarSlot->subtitle = boost::lexical_cast(val); - - deal->block(false); - return true; -} - -void CAltarWindow::moveFromSlotToAltar(ArtifactPosition slotID, CTradeableItem* altarSlot, const CArtifactInstance *art) -{ - auto freeBackpackSlot = ArtifactPosition(hero->artifactsInBackpack.size() + GameConstants::BACKPACK_START); - if(arts->commonInfo->src.art) - { - arts->commonInfo->dst.slotID = freeBackpackSlot; - arts->commonInfo->dst.AOH = arts; - } - - if(putOnAltar(altarSlot, art)) - { - if(slotID < GameConstants::BACKPACK_START) - LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero, slotID), ArtifactLocation(hero, freeBackpackSlot)); - else - { - arts->commonInfo->src.clear(); - arts->commonInfo->dst.clear(); - CCS->curh->dragAndDropCursor(nullptr); - arts->unmarkSlots(false); - } - } -} - void CSystemOptionsWindow::setMusicVolume( int newVolume ) { Settings volume = settings.write["general"]["music"]; diff --git a/client/GUIClasses.h b/client/GUIClasses.h index 2589dd36c..2cb007e69 100644 --- a/client/GUIClasses.h +++ b/client/GUIClasses.h @@ -387,156 +387,6 @@ public: void keyPressed (const SDL_KeyboardEvent & key); }; -class CTradeWindow : public CWindowObject, public CWindowWithArtifacts //base for markets and altar of sacrifice -{ -public: - enum EType - { - RESOURCE, PLAYER, ARTIFACT_TYPE, CREATURE, CREATURE_PLACEHOLDER, ARTIFACT_PLACEHOLDER, ARTIFACT_INSTANCE - }; - class CTradeableItem : public CIntObject - { - CAnimImage * image; - - std::string getFilename(); - int getIndex(); - public: - const CArtifactInstance *hlp; //holds ptr to artifact instance id type artifact - EType type; - int id; - const int serial; - const bool left; - std::string subtitle; //empty if default - - void setType(EType newType); - void setID(int newID); - - const CArtifactInstance *getArtInstance() const; - void setArtInstance(const CArtifactInstance *art); - - CFunctionList callback; - bool downSelection; - - void showAllAt(const Point &dstPos, const std::string &customSub, SDL_Surface * to); - - void clickRight(tribool down, bool previousState); - void hover (bool on); - void showAll(SDL_Surface * to); - void clickLeft(tribool down, bool previousState); - std::string getName(int number = -1) const; - CTradeableItem(Point pos, EType Type, int ID, bool Left, int Serial); - }; - - const IMarket *market; - const CGHeroInstance *hero; - - CArtifactsOfHero *arts; - //all indexes: 1 = left, 0 = right - std::vector items[2]; - CTradeableItem *hLeft, *hRight; //highlighted items (nullptr if no highlight) - EType itemsType[2]; - - EMarketMode::EMarketMode mode;//0 - res<->res; 1 - res<->plauer; 2 - buy artifact; 3 - sell artifact - CAdventureMapButton *ok, *max, *deal; - CSlider *slider; //for choosing amount to be exchanged - bool readyToTrade; - - CTradeWindow(std::string bgName, const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode); //c - - void showAll(SDL_Surface * to); - - void initSubs(bool Left); - void initTypes(); - void initItems(bool Left); - std::vector *getItemsIds(bool Left); //nullptr if default - void getPositionsFor(std::vector &poss, bool Left, EType type) const; - void removeItems(const std::set &toRemove); - void removeItem(CTradeableItem * t); - void getEmptySlots(std::set &toRemove); - void setMode(EMarketMode::EMarketMode Mode); //mode setter - - void artifactSelected(CArtPlace *slot); //used when selling artifacts -> called when user clicked on artifact slot - - virtual void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const = 0; - virtual void selectionChanged(bool side) = 0; //true == left - virtual Point selectionOffset(bool Left) const = 0; - virtual std::string selectionSubtitle(bool Left) const = 0; - virtual void garrisonChanged() = 0; - virtual void artifactsChanged(bool left) = 0; -}; - -class CMarketplaceWindow : public CTradeWindow -{ - bool printButtonFor(EMarketMode::EMarketMode M) const; - - std::string getBackgroundForMode(EMarketMode::EMarketMode mode); -public: - int r1, r2; //suggested amounts of traded resources - bool madeTransaction; //if player made at least one transaction - CTextBox *traderText; - - void setMax(); - void sliderMoved(int to); - void makeDeal(); - void selectionChanged(bool side); //true == left - CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero = nullptr, EMarketMode::EMarketMode Mode = EMarketMode::RESOURCE_RESOURCE); //c-tor - ~CMarketplaceWindow(); //d-tor - - Point selectionOffset(bool Left) const; - std::string selectionSubtitle(bool Left) const; - - - void garrisonChanged(); //removes creatures with count 0 from the list (apparently whole stack has been sold) - void artifactsChanged(bool left); - void resourceChanged(int type, int val); - - void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const; - void updateTraderText(); -}; - -class CAltarWindow : public CTradeWindow -{ - CAnimImage * artIcon; -public: - CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode); //c-tor - - void getExpValues(); - ~CAltarWindow(); //d-tor - - std::vector sacrificedUnits, //[slot_nr] -> how many creatures from that slot will be sacrificed - expPerUnit; - - CAdventureMapButton *sacrificeAll, *sacrificeBackpack; - CLabel *expToLevel, *expOnAltar; - - - void selectionChanged(bool side); //true == left - void SacrificeAll(); - void SacrificeBackpack(); - - void putOnAltar(int backpackIndex); - bool putOnAltar(CTradeableItem* altarSlot, const CArtifactInstance *art); - void makeDeal(); - void showAll(SDL_Surface * to); - - void blockTrade(); - void sliderMoved(int to); - void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const; - void mimicCres(); - - Point selectionOffset(bool Left) const; - std::string selectionSubtitle(bool Left) const; - void garrisonChanged(); - void artifactsChanged(bool left); - void calcTotalExp(); - void setExpToLevel(); - void updateRight(CTradeableItem *toUpdate); - - void artifactPicked(); - int firstFreeSlot(); - void moveFromSlotToAltar(ArtifactPosition slotID, CTradeableItem* altarSlot, const CArtifactInstance *art); -}; - class CSystemOptionsWindow : public CWindowObject { private: diff --git a/client/gui/CTradeWindow.cpp b/client/gui/CTradeWindow.cpp new file mode 100644 index 000000000..46d3fb09f --- /dev/null +++ b/client/gui/CTradeWindow.cpp @@ -0,0 +1,1507 @@ +#include "StdInc.h" +#include "CTradeWindow.h" + +#include "CGuiHandler.h" +#include "CAnimation.h" +#include "CCursorHandler.h" + +#include "../CAdvmapInterface.h" +#include "../CGameInfo.h" +#include "../CPlayerInterface.h" + +#include "../../CCallback.h" + +#include "../../lib/VCMI_Lib.h" +#include "../../lib/CArtHandler.h" +#include "../../lib/CCreatureHandler.h" +#include "../../lib/CGeneralTextHandler.h" +#include "../../lib/CHeroHandler.h" +#include "../../lib/mapObjects/CGHeroInstance.h" + + +/* + * CTradeWindow.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 + * + */ + +CTradeWindow::CTradeableItem::CTradeableItem(Point pos, EType Type, int ID, bool Left, int Serial): + CIntObject(LCLICK | HOVER | RCLICK, pos), + type(EType(-1)),// set to invalid, will be corrected in setType + id(ID), + serial(Serial), + left(Left) +{ + downSelection = false; + hlp = nullptr; + image = nullptr; + setType(Type); +} + +void CTradeWindow::CTradeableItem::setType(EType newType) +{ + if (type != newType) + { + OBJ_CONSTRUCTION_CAPTURING_ALL; + type = newType; + delete image; + + if (getIndex() < 0) + { + image = new CAnimImage(getFilename(), 0); + image->disable(); + } + else + image = new CAnimImage(getFilename(), getIndex()); + } +} + +void CTradeWindow::CTradeableItem::setID(int newID) +{ + if (id != newID) + { + id = newID; + if (image) + { + int index = getIndex(); + if (index < 0) + image->disable(); + else + { + image->enable(); + image->setFrame(index); + } + } + } +} + +std::string CTradeWindow::CTradeableItem::getFilename() +{ + switch(type) + { + case RESOURCE: + return "RESOURCE"; + case PLAYER: + return "CREST58"; + case ARTIFACT_TYPE: + case ARTIFACT_PLACEHOLDER: + case ARTIFACT_INSTANCE: + return "artifact"; + case CREATURE: + return "TWCRPORT"; + default: + return ""; + } +} + +int CTradeWindow::CTradeableItem::getIndex() +{ + if (id < 0) + return -1; + + switch(type) + { + case RESOURCE: + case PLAYER: + return id; + case ARTIFACT_TYPE: + case ARTIFACT_INSTANCE: + case ARTIFACT_PLACEHOLDER: + return VLC->arth->artifacts[id]->iconIndex; + case CREATURE: + return VLC->creh->creatures[id]->iconIndex; + default: + return -1; + } +} + +void CTradeWindow::CTradeableItem::showAll(SDL_Surface * to) +{ + Point posToBitmap; + Point posToSubCenter; + + switch(type) + { + case RESOURCE: + posToBitmap = Point(19,9); + posToSubCenter = Point(36, 59); + break; + case CREATURE_PLACEHOLDER: + case CREATURE: + posToSubCenter = Point(29, 76); + if(downSelection) + posToSubCenter.y += 5; + break; + case PLAYER: + posToSubCenter = Point(31, 76); + break; + case ARTIFACT_PLACEHOLDER: + case ARTIFACT_INSTANCE: + posToSubCenter = Point(19, 55); + if(downSelection) + posToSubCenter.y += 8; + break; + case ARTIFACT_TYPE: + posToSubCenter = Point(19, 58); + break; + } + + if (image) + { + image->moveTo(pos.topLeft() + posToBitmap); + CIntObject::showAll(to); + } + + printAtMiddleLoc(subtitle, posToSubCenter, FONT_SMALL, Colors::WHITE, to); +} + +void CTradeWindow::CTradeableItem::clickLeft(tribool down, bool previousState) +{ + CTradeWindow *mw = dynamic_cast(parent); + assert(mw); + if(down) + { + + if(type == ARTIFACT_PLACEHOLDER) + { + CAltarWindow *aw = static_cast(mw); + if(const CArtifactInstance *movedArt = aw->arts->commonInfo->src.art) + { + aw->moveFromSlotToAltar(aw->arts->commonInfo->src.slotID, this, movedArt); + } + else if(const CArtifactInstance *art = getArtInstance()) + { + aw->arts->commonInfo->src.AOH = aw->arts; + aw->arts->commonInfo->src.art = art; + aw->arts->commonInfo->src.slotID = aw->hero->getArtPos(art); + aw->arts->markPossibleSlots(art); + + //aw->arts->commonInfo->dst.AOH = aw->arts; + CCS->curh->dragAndDropCursor(new CAnimImage("artifact", art->artType->iconIndex)); + + aw->arts->artifactsOnAltar.erase(art); + setID(-1); + subtitle = ""; + aw->deal->block(!aw->arts->artifactsOnAltar.size()); + } + + aw->calcTotalExp(); + return; + } + if(left) + { + if(mw->hLeft != this) + mw->hLeft = this; + else + return; + } + else + { + if(mw->hRight != this) + mw->hRight = this; + else + return; + } + mw->selectionChanged(left); + } +} + +void CTradeWindow::CTradeableItem::showAllAt(const Point &dstPos, const std::string &customSub, SDL_Surface * to) +{ + Rect oldPos = pos; + std::string oldSub = subtitle; + downSelection = true; + + moveTo(dstPos); + subtitle = customSub; + showAll(to); + + downSelection = false; + moveTo(oldPos.topLeft()); + subtitle = oldSub; +} + +void CTradeWindow::CTradeableItem::hover(bool on) +{ + if(!on) + { + GH.statusbar->clear(); + return; + } + + switch(type) + { + case CREATURE: + case CREATURE_PLACEHOLDER: + GH.statusbar->setText(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->creatures[id]->namePl)); + break; + case ARTIFACT_PLACEHOLDER: + if(id < 0) + GH.statusbar->setText(CGI->generaltexth->zelp[582].first); + else + GH.statusbar->setText(CGI->arth->artifacts[id]->Name()); + break; + } +} + +void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState) +{ + if(down) + { + switch(type) + { + case CREATURE: + case CREATURE_PLACEHOLDER: + //GH.statusbar->print(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->creatures[id]->namePl)); + break; + case ARTIFACT_TYPE: + case ARTIFACT_PLACEHOLDER: + if(id >= 0) + adventureInt->handleRightClick(CGI->arth->artifacts[id]->Description(), down); + break; + } + } +} + +std::string CTradeWindow::CTradeableItem::getName(int number /*= -1*/) const +{ + switch(type) + { + case PLAYER: + return CGI->generaltexth->capColors[id]; + case RESOURCE: + return CGI->generaltexth->restypes[id]; + case CREATURE: + if(number == 1) + return CGI->creh->creatures[id]->nameSing; + else + return CGI->creh->creatures[id]->namePl; + case ARTIFACT_TYPE: + case ARTIFACT_INSTANCE: + return CGI->arth->artifacts[id]->Name(); + } + assert(0); + return ""; +} + +const CArtifactInstance * CTradeWindow::CTradeableItem::getArtInstance() const +{ + switch(type) + { + case ARTIFACT_PLACEHOLDER: + case ARTIFACT_INSTANCE: + return (const CArtifactInstance *)hlp; + default: + return nullptr; + } +} + +void CTradeWindow::CTradeableItem::setArtInstance(const CArtifactInstance *art) +{ + assert(type == ARTIFACT_PLACEHOLDER || type == ARTIFACT_INSTANCE); + hlp = art; + if(art) + setID(art->artType->id); + else + setID(-1); +} + +CTradeWindow::CTradeWindow(std::string bgName, const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode): + CWindowObject(PLAYER_COLORED, bgName), + market(Market), + hero(Hero), + arts(nullptr), + hLeft(nullptr), + hRight(nullptr), + readyToTrade(false) +{ + type |= BLOCK_ADV_HOTKEYS; + mode = Mode; + initTypes(); +} + +void CTradeWindow::initTypes() +{ + switch(mode) + { + case EMarketMode::RESOURCE_RESOURCE: + itemsType[1] = RESOURCE; + itemsType[0] = RESOURCE; + break; + case EMarketMode::RESOURCE_PLAYER: + itemsType[1] = RESOURCE; + itemsType[0] = PLAYER; + break; + case EMarketMode::CREATURE_RESOURCE: + itemsType[1] = CREATURE; + itemsType[0] = RESOURCE; + break; + case EMarketMode::RESOURCE_ARTIFACT: + itemsType[1] = RESOURCE; + itemsType[0] = ARTIFACT_TYPE; + break; + case EMarketMode::ARTIFACT_RESOURCE: + itemsType[1] = ARTIFACT_INSTANCE; + itemsType[0] = RESOURCE; + break; + case EMarketMode::CREATURE_EXP: + itemsType[1] = CREATURE; + itemsType[0] = CREATURE_PLACEHOLDER; + break; + case EMarketMode::ARTIFACT_EXP: + itemsType[1] = ARTIFACT_TYPE; + itemsType[0] = ARTIFACT_PLACEHOLDER; + break; + } +} + +void CTradeWindow::initItems(bool Left) +{ + if(Left && (itemsType[1] == ARTIFACT_TYPE || itemsType[1] == ARTIFACT_INSTANCE)) + { + int xOffset = 0, yOffset = 0; + if(mode == EMarketMode::ARTIFACT_RESOURCE) + { + xOffset = -361; + yOffset = +46; + + auto hlp = new CTradeableItem(Point(137, 469), itemsType[Left], -1, 1, 0); + hlp->recActions &= ~(UPDATE | SHOWALL); + items[Left].push_back(hlp); + } + else //ARTIFACT_EXP + { + xOffset = -363; + yOffset = -12; + } + + BLOCK_CAPTURING; + arts = new CArtifactsOfHero(Point(pos.x+xOffset, pos.y+yOffset)); + arts->commonInfo = new CArtifactsOfHero::SCommonPart; + arts->commonInfo->participants.insert(arts); + arts->recActions = 255; + arts->setHero(hero); + arts->allowedAssembling = false; + addChild(arts); + artSets.push_back(arts); + + if(mode == EMarketMode::ARTIFACT_RESOURCE) + arts->highlightModeCallback = boost::bind(&CTradeWindow::artifactSelected, this, _1); + return; + } + + std::vector *ids = getItemsIds(Left); + std::vector pos; + int amount = -1; + + getPositionsFor(pos, Left, itemsType[Left]); + + if(Left || !ids) + amount = 7; + else + amount = ids->size(); + + if(ids) + vstd::amin(amount, ids->size()); + + for(int j=0; jsize()>j) ? (*ids)[j] : j; + if(id < 0 && mode != EMarketMode::ARTIFACT_EXP) //when sacrificing artifacts we need to prepare empty slots + continue; + + auto hlp = new CTradeableItem(pos[j].topLeft(), itemsType[Left], id, Left, j); + hlp->pos = pos[j] + this->pos.topLeft(); + items[Left].push_back(hlp); + } + + initSubs(Left); +} + +std::vector *CTradeWindow::getItemsIds(bool Left) +{ + std::vector *ids = nullptr; + + if(mode == EMarketMode::ARTIFACT_EXP) + return new std::vector(22, -1); + + if(Left) + { + switch(itemsType[1]) + { + case CREATURE: + ids = new std::vector; + for(int i = 0; i < 7; i++) + { + if(const CCreature *c = hero->getCreature(SlotID(i))) + ids->push_back(c->idNumber); + else + ids->push_back(-1); + } + break; + } + } + else + { + switch(itemsType[0]) + { + case PLAYER: + ids = new std::vector; + for(int i = 0; i < PlayerColor::PLAYER_LIMIT_I; i++) + if(PlayerColor(i) != LOCPLINT->playerID && LOCPLINT->cb->getPlayerStatus(PlayerColor(i)) == EPlayerStatus::INGAME) + ids->push_back(i); + break; + + case ARTIFACT_TYPE: + ids = new std::vector(market->availableItemsIds(mode)); + break; + } + } + + return ids; +} + +void CTradeWindow::getPositionsFor(std::vector &poss, bool Left, EType type) const +{ + using namespace boost::assign; + + if(mode == EMarketMode::ARTIFACT_EXP && !Left) + { + //22 boxes, 5 in row, last row: two boxes centered + int h, w, x, y, dx, dy; + h = w = 44; + x = 317; + y = 53; + dx = 54; + dy = 70; + for (int i = 0; i < 4 ; i++) + for (int j = 0; j < 5 ; j++) + poss += Rect(x + dx*j, y + dy*i, w, h); + + poss += Rect(x + dx*1.5, y + dy*4, w, h); + poss += Rect(x + dx*2.5, y + dy*4, w, h); + } + else + { + //seven boxes: + // X X X + // X X X + // X + int h, w, x, y, dx, dy; + int leftToRightOffset; + getBaseForPositions(type, dx, dy, x, y, h, w, !Left, leftToRightOffset); + + poss += genRect(h, w, x, y), genRect(h, w, x + dx, y), genRect(h, w, x + 2*dx, y), + genRect(h, w, x, y + dy), genRect(h, w, x + dx, y + dy), genRect(h, w, x + 2*dx, y + dy), + genRect(h, w, x + dx, y + 2*dy); + + if(!Left) + { + for(Rect &r : poss) + r.x += leftToRightOffset; + } + } +} + +void CTradeWindow::initSubs(bool Left) +{ + for(CTradeableItem *t : items[Left]) + { + if(Left) + { + switch(itemsType[1]) + { + case CREATURE: + t->subtitle = boost::lexical_cast(hero->getStackCount(SlotID(t->serial))); + break; + case RESOURCE: + t->subtitle = boost::lexical_cast(LOCPLINT->cb->getResourceAmount(static_cast(t->serial))); + break; + } + } + else //right side + { + if(itemsType[0] == PLAYER) + { + t->subtitle = CGI->generaltexth->capColors[t->id]; + } + else if(hLeft)//artifact, creature + { + int h1, h2; //hlp variables for getting offer + market->getOffer(hLeft->id, t->id, h1, h2, mode); + if(t->id != hLeft->id || mode != EMarketMode::RESOURCE_RESOURCE) //don't allow exchanging same resources + { + std::ostringstream oss; + oss << h2; + if(h1!=1) + oss << "/" << h1; + t->subtitle = oss.str(); + } + else + t->subtitle = CGI->generaltexth->allTexts[164]; // n/a + } + else + t->subtitle = ""; + } + } +} + +void CTradeWindow::showAll(SDL_Surface * to) +{ + CWindowObject::showAll(to); + + if(hRight) + CSDL_Ext::drawBorder(to,hRight->pos.x-1,hRight->pos.y-1,hRight->pos.w+2,hRight->pos.h+2,int3(255,231,148)); + if(hLeft && hLeft->type != ARTIFACT_INSTANCE) + CSDL_Ext::drawBorder(to,hLeft->pos.x-1,hLeft->pos.y-1,hLeft->pos.w+2,hLeft->pos.h+2,int3(255,231,148)); + + if(readyToTrade) + { + hLeft->showAllAt(pos.topLeft() + selectionOffset(true), selectionSubtitle(true), to); + hRight->showAllAt(pos.topLeft() + selectionOffset(false), selectionSubtitle(false), to); + } +} + +void CTradeWindow::removeItems(const std::set &toRemove) +{ + for(CTradeableItem *t : toRemove) + removeItem(t); +} + +void CTradeWindow::removeItem(CTradeableItem * t) +{ + items[t->left] -= t; + delete t; + + if(hRight == t) + { + hRight = nullptr; + selectionChanged(false); + } +} + +void CTradeWindow::getEmptySlots(std::set &toRemove) +{ + for(CTradeableItem *t : items[1]) + if(!hero->getStackCount(SlotID(t->serial))) + toRemove.insert(t); +} + +void CTradeWindow::setMode(EMarketMode::EMarketMode Mode) +{ + const IMarket *m = market; + const CGHeroInstance *h = hero; + CTradeWindow *nwindow = nullptr; + + GH.popIntTotally(this); + + switch(Mode) + { + case EMarketMode::CREATURE_EXP: + case EMarketMode::ARTIFACT_EXP: + nwindow = new CAltarWindow(m, h, Mode); + break; + default: + nwindow = new CMarketplaceWindow(m, h, Mode); + break; + } + + GH.pushInt(nwindow); +} + +void CTradeWindow::artifactSelected(CArtPlace *slot) +{ + assert(mode == EMarketMode::ARTIFACT_RESOURCE); + items[1][0]->setArtInstance(slot->ourArt); + if(slot->ourArt) + hLeft = items[1][0]; + else + hLeft = nullptr; + + selectionChanged(true); +} + +std::string CMarketplaceWindow::getBackgroundForMode(EMarketMode::EMarketMode mode) +{ + switch(mode) + { + case EMarketMode::RESOURCE_RESOURCE: + return "TPMRKRES.bmp"; + case EMarketMode::RESOURCE_PLAYER: + return "TPMRKPTS.bmp"; + case EMarketMode::CREATURE_RESOURCE: + return "TPMRKCRS.bmp"; + case EMarketMode::RESOURCE_ARTIFACT: + return "TPMRKABS.bmp"; + case EMarketMode::ARTIFACT_RESOURCE: + return "TPMRKASS.bmp"; + } + assert(0); + return ""; +} + +CMarketplaceWindow::CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode) + : CTradeWindow(getBackgroundForMode(Mode), Market, Hero, Mode) +{ + OBJ_CONSTRUCTION_CAPTURING_ALL; + + madeTransaction = false; + bool sliderNeeded = true; + + new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); + + std::string title; + + if (market->o->ID == Obj::TOWN) + { + switch (mode) + { + break; case EMarketMode::CREATURE_RESOURCE: + title = CGI->townh->factions[ETownType::STRONGHOLD]->town->buildings[BuildingID::FREELANCERS_GUILD]->Name(); + + break; case EMarketMode::RESOURCE_ARTIFACT: + title = CGI->townh->factions[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name(); + sliderNeeded = false; + + break; case EMarketMode::ARTIFACT_RESOURCE: + title = CGI->townh->factions[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name(); + sliderNeeded = false; + + break; default: + title = CGI->generaltexth->allTexts[158]; + } + } + else + { + switch (market->o->ID) + { + break; case Obj::BLACK_MARKET: title = CGI->generaltexth->allTexts[349]; + break; case Obj::TRADING_POST: title = CGI->generaltexth->allTexts[159]; + break; case Obj::TRADING_POST_SNOW: title = CGI->generaltexth->allTexts[159]; + break; default: title = market->o->getObjectName(); + } + } + + new CLabel(300, 27, FONT_BIG, CENTER, Colors::YELLOW, title); + + initItems(false); + initItems(true); + + ok = new CAdventureMapButton(CGI->generaltexth->zelp[600],boost::bind(&CGuiHandler::popIntTotally,&GH,this),516,520,"IOK6432.DEF",SDLK_RETURN); + ok->assignedKeys.insert(SDLK_ESCAPE); + deal = new CAdventureMapButton(CGI->generaltexth->zelp[595],boost::bind(&CMarketplaceWindow::makeDeal,this),307,520,"TPMRKB.DEF"); + deal->block(true); + + if(sliderNeeded) + { + slider = new CSlider(231,490,137,nullptr,0,0); + slider->moved = boost::bind(&CMarketplaceWindow::sliderMoved,this,_1); + max = new CAdventureMapButton(CGI->generaltexth->zelp[596],boost::bind(&CMarketplaceWindow::setMax,this),229,520,"IRCBTNS.DEF"); + max->block(true); + } + else + { + slider = nullptr; + max = nullptr; + deal->moveBy(Point(-30, 0)); + } + + Rect traderTextRect; + + //left side + switch(Mode) + { + case EMarketMode::RESOURCE_RESOURCE: + case EMarketMode::RESOURCE_PLAYER: + case EMarketMode::RESOURCE_ARTIFACT: + new CLabel(154, 148, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[270]); + break; + + case EMarketMode::CREATURE_RESOURCE: + //%s's Creatures + new CLabel(152, 102, FONT_SMALL, CENTER, Colors::WHITE, + boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name)); + break; + case EMarketMode::ARTIFACT_RESOURCE: + //%s's Artifacts + new CLabel(152, 102, FONT_SMALL, CENTER, Colors::WHITE, + boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name)); + break; + } + + //right side + switch(Mode) + { + case EMarketMode::RESOURCE_RESOURCE: + case EMarketMode::CREATURE_RESOURCE: + case EMarketMode::RESOURCE_ARTIFACT: + case EMarketMode::ARTIFACT_RESOURCE: + new CLabel(445, 148, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[168]); + traderTextRect = Rect(316, 48, 260, 75); + break; + case EMarketMode::RESOURCE_PLAYER: + new CLabel(445, 55, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[169]); + traderTextRect = Rect(28, 48, 260, 75); + break; + } + + traderText = new CTextBox("", traderTextRect, 0, FONT_SMALL, CENTER); + int specialOffset = mode == EMarketMode::ARTIFACT_RESOURCE ? 35 : 0; //in selling artifacts mode we need to move res-res and art-res buttons down + + if(printButtonFor(EMarketMode::RESOURCE_PLAYER)) + new CAdventureMapButton(CGI->generaltexth->zelp[612],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_PLAYER), 18, 520,"TPMRKBU1.DEF"); + if(printButtonFor(EMarketMode::RESOURCE_RESOURCE)) + new CAdventureMapButton(CGI->generaltexth->zelp[605],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_RESOURCE), 516, 450 + specialOffset,"TPMRKBU5.DEF"); + if(printButtonFor(EMarketMode::CREATURE_RESOURCE)) + new CAdventureMapButton(CGI->generaltexth->zelp[599],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::CREATURE_RESOURCE), 516, 485,"TPMRKBU4.DEF"); //was y=450, changed to not overlap res-res in some conditions + if(printButtonFor(EMarketMode::RESOURCE_ARTIFACT)) + new CAdventureMapButton(CGI->generaltexth->zelp[598],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::RESOURCE_ARTIFACT), 18, 450 + specialOffset,"TPMRKBU2.DEF"); + if(printButtonFor(EMarketMode::ARTIFACT_RESOURCE)) + new CAdventureMapButton(CGI->generaltexth->zelp[613],boost::bind(&CMarketplaceWindow::setMode,this, EMarketMode::ARTIFACT_RESOURCE), 18, 485,"TPMRKBU3.DEF"); //was y=450, changed to not overlap res-art in some conditions + + updateTraderText(); +} + +CMarketplaceWindow::~CMarketplaceWindow() +{ + hLeft = hRight = nullptr; + for(auto & elem : items[1]) + delete elem; + for(auto & elem : items[0]) + delete elem; + + items[1].clear(); + items[0].clear(); +} + + + +void CMarketplaceWindow::setMax() +{ + slider->moveToMax(); +} + +void CMarketplaceWindow::makeDeal() +{ + int sliderValue = 0; + if(slider) + sliderValue = slider->value; + else + sliderValue = !deal->isBlocked(); //should always be 1 + + if(!sliderValue) + return; + + int leftIdToSend = -1; + switch (mode) + { + case EMarketMode::CREATURE_RESOURCE: + leftIdToSend = hLeft->serial; + break; + case EMarketMode::ARTIFACT_RESOURCE: + leftIdToSend = hLeft->getArtInstance()->id.getNum(); + break; + default: + leftIdToSend = hLeft->id; + break; + } + + if(slider) + { + LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, slider->value*r1, hero); + slider->moveTo(0); + } + else + { + LOCPLINT->cb->trade(market->o, mode, leftIdToSend, hRight->id, r2, hero); + } + madeTransaction = true; + + hLeft = nullptr; + hRight = nullptr; + selectionChanged(true); +} + +void CMarketplaceWindow::sliderMoved( int to ) +{ + redraw(); +} + +void CMarketplaceWindow::selectionChanged(bool side) +{ + readyToTrade = hLeft && hRight; + if(mode == EMarketMode::RESOURCE_RESOURCE) + readyToTrade = readyToTrade && (hLeft->id != hRight->id); //for resource trade, two DIFFERENT resources must be selected + + if(mode == EMarketMode::ARTIFACT_RESOURCE && !hLeft) + arts->unmarkSlots(false); + + if(readyToTrade) + { + int soldItemId = hLeft->id; + market->getOffer(soldItemId, hRight->id, r1, r2, mode); + + if(slider) + { + int newAmount = -1; + if(itemsType[1] == RESOURCE) + newAmount = LOCPLINT->cb->getResourceAmount(static_cast(soldItemId)); + else if(itemsType[1] == CREATURE) + newAmount = hero->getStackCount(SlotID(hLeft->serial)) - (hero->Slots().size() == 1 && hero->needsLastStack()); + else + assert(0); + + slider->setAmount(newAmount / r1); + slider->moveTo(0); + max->block(false); + deal->block(false); + } + else if(itemsType[1] == RESOURCE) //buying -> check if we can afford transaction + { + deal->block(LOCPLINT->cb->getResourceAmount(static_cast(soldItemId)) < r1); + } + else + deal->block(false); + } + else + { + if(slider) + { + max->block(true); + slider->setAmount(0); + slider->moveTo(0); + } + deal->block(true); + } + + if(side && itemsType[0] != PLAYER) //items[1] selection changed, recalculate offers + initSubs(false); + + updateTraderText(); + redraw(); +} + +bool CMarketplaceWindow::printButtonFor(EMarketMode::EMarketMode M) const +{ + return market->allowsTrade(M) && M != mode && (hero || ( M != EMarketMode::CREATURE_RESOURCE && M != EMarketMode::RESOURCE_ARTIFACT && M != EMarketMode::ARTIFACT_RESOURCE )); +} + +void CMarketplaceWindow::garrisonChanged() +{ + if(mode != EMarketMode::CREATURE_RESOURCE) + return; + + std::set toRemove; + getEmptySlots(toRemove); + + + removeItems(toRemove); + initSubs(true); +} + +void CMarketplaceWindow::artifactsChanged(bool Left) +{ + assert(!Left); + if(mode != EMarketMode::RESOURCE_ARTIFACT) + return; + + std::vector available = market->availableItemsIds(mode); + std::set toRemove; + for(CTradeableItem *t : items[0]) + if(!vstd::contains(available, t->id)) + toRemove.insert(t); + + removeItems(toRemove); + redraw(); +} + +std::string CMarketplaceWindow::selectionSubtitle(bool Left) const +{ + if(Left) + { + switch(itemsType[1]) + { + case RESOURCE: + case CREATURE: + { + int val = slider + ? slider->value * r1 + : (((deal->isBlocked())) ? 0 : r1); + + return boost::lexical_cast(val); + } + case ARTIFACT_INSTANCE: + return ((deal->isBlocked()) ? "0" : "1"); + } + } + else + { + switch(itemsType[0]) + { + case RESOURCE: + if(slider) + return boost::lexical_cast( slider->value * r2 ); + else + return boost::lexical_cast(r2); + case ARTIFACT_TYPE: + return ((deal->isBlocked()) ? "0" : "1"); + case PLAYER: + return (hRight ? CGI->generaltexth->capColors[hRight->id] : ""); + } + } + + return "???"; +} + +Point CMarketplaceWindow::selectionOffset(bool Left) const +{ + if(Left) + { + switch(itemsType[1]) + { + case RESOURCE: + return Point(122, 446); + case CREATURE: + return Point(128, 450); + case ARTIFACT_INSTANCE: + return Point(134, 466); + } + } + else + { + switch(itemsType[0]) + { + case RESOURCE: + if(mode == EMarketMode::ARTIFACT_RESOURCE) + return Point(410, 469); + else + return Point(410, 446); + case ARTIFACT_TYPE: + return Point(425, 447); + case PLAYER: + return Point(417, 451); + } + } + + assert(0); + return Point(0,0); +} + +void CMarketplaceWindow::resourceChanged(int type, int val) +{ + initSubs(true); +} + +void CMarketplaceWindow::getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const +{ + switch(type) + { + case RESOURCE: + dx = 82; + dy = 79; + x = 39; + y = 180; + h = 66; + w = 74; + break; + case PLAYER: + dx = 83; + dy = 118; + h = 64; + w = 58; + x = 44; + y = 83; + assert(Right); + break; + case CREATURE://45,123 + x = 45; + y = 123; + w = 58; + h = 64; + dx = 83; + dy = 98; + assert(!Right); + break; + case ARTIFACT_TYPE://45,123 + x = 340-289; + y = 180; + w = 44; + h = 44; + dx = 83; + dy = 79; + break; + } + + leftToRightOffset = 289; +} + +void CMarketplaceWindow::updateTraderText() +{ + if(readyToTrade) + { + if(mode == EMarketMode::RESOURCE_PLAYER) + { + //I can give %s to the %s player. + traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[165]) % hLeft->getName() % hRight->getName())); + } + else if(mode == EMarketMode::RESOURCE_ARTIFACT) + { + //I can offer you the %s for %d %s of %s. + traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[267]) % hRight->getName() % r1 % CGI->generaltexth->allTexts[160 + (r1==1)] % hLeft->getName())); + } + else if(mode == EMarketMode::RESOURCE_RESOURCE) + { + //I can offer you %d %s of %s for %d %s of %s. + traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[157]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % r1 % CGI->generaltexth->allTexts[160 + (r1==1)] % hLeft->getName())); + } + else if(mode == EMarketMode::CREATURE_RESOURCE) + { + //I can offer you %d %s of %s for %d %s. + traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[269]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % r1 % hLeft->getName(r1))); + } + else if(mode == EMarketMode::ARTIFACT_RESOURCE) + { + //I can offer you %d %s of %s for your %s. + traderText->setText(boost::str(boost::format(CGI->generaltexth->allTexts[268]) % r2 % CGI->generaltexth->allTexts[160 + (r2==1)] % hRight->getName() % hLeft->getName(r1))); + } + return; + } + + int gnrtxtnr = -1; + if(madeTransaction) + { + if(mode == EMarketMode::RESOURCE_PLAYER) + gnrtxtnr = 166; //Are there any other resources you'd like to give away? + else + gnrtxtnr = 162; //You have received quite a bargain. I expect to make no profit on the deal. Can I interest you in any of my other wares? + } + else + { + if(mode == EMarketMode::RESOURCE_PLAYER) + gnrtxtnr = 167; //If you'd like to give any of your resources to another player, click on the item you wish to give and to whom. + else + gnrtxtnr = 163; //Please inspect our fine wares. If you feel like offering a trade, click on the items you wish to trade with and for. + } + traderText->setText(CGI->generaltexth->allTexts[gnrtxtnr]); +} + +CAltarWindow::CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero /*= nullptr*/, EMarketMode::EMarketMode Mode) + :CTradeWindow((Mode == EMarketMode::CREATURE_EXP ? "ALTARMON.bmp" : "ALTRART2.bmp"), Market, Hero, Mode) +{ + OBJ_CONSTRUCTION_CAPTURING_ALL; + if(Mode == EMarketMode::CREATURE_EXP) + { + //%s's Creatures + new CLabel(155, 30, FONT_SMALL, CENTER, Colors::YELLOW, + boost::str(boost::format(CGI->generaltexth->allTexts[272]) % hero->name)); + + //Altar of Sacrifice + new CLabel(450, 30, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[479]); + + //To sacrifice creatures, move them from your army on to the Altar and click Sacrifice + new CTextBox(CGI->generaltexth->allTexts[480], Rect(320, 56, 256, 40), 0, FONT_SMALL, CENTER, Colors::YELLOW); + + slider = new CSlider(231,481,137,nullptr,0,0); + slider->moved = boost::bind(&CAltarWindow::sliderMoved,this,_1); + max = new CAdventureMapButton(CGI->generaltexth->zelp[578],boost::bind(&CSlider::moveToMax, slider),147,520,"IRCBTNS.DEF"); + + sacrificedUnits.resize(GameConstants::ARMY_SIZE, 0); + sacrificeAll = new CAdventureMapButton(CGI->generaltexth->zelp[579],boost::bind(&CAltarWindow::SacrificeAll,this),393,520,"ALTARMY.DEF"); + sacrificeBackpack = nullptr; + + initItems(true); + mimicCres(); + artIcon = nullptr; + } + else + { + //Sacrifice artifacts for experience + new CLabel(450, 34, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[477]); + //%s's Creatures + new CLabel(302, 423, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[478]); + + sacrificeAll = new CAdventureMapButton(CGI->generaltexth->zelp[571], boost::bind(&CAltarWindow::SacrificeAll,this),393,520,"ALTFILL.DEF"); + sacrificeAll->block(hero->artifactsInBackpack.empty() && hero->artifactsWorn.empty()); + sacrificeBackpack = new CAdventureMapButton(CGI->generaltexth->zelp[570],boost::bind(&CAltarWindow::SacrificeBackpack,this),147,520,"ALTEMBK.DEF"); + sacrificeBackpack->block(hero->artifactsInBackpack.empty()); + + slider = nullptr; + max = nullptr; + + initItems(true); + initItems(false); + artIcon = new CAnimImage("ARTIFACT", 0, 0, 281, 442); + artIcon->disable(); + } + + //Experience needed to reach next level + new CTextBox(CGI->generaltexth->allTexts[475], Rect(15, 415, 125, 50), 0, FONT_SMALL, CENTER, Colors::YELLOW); + //Total experience on the Altar + new CTextBox(CGI->generaltexth->allTexts[476], Rect(15, 495, 125, 40), 0, FONT_SMALL, CENTER, Colors::YELLOW); + + new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26)); + + ok = new CAdventureMapButton(CGI->generaltexth->zelp[568],boost::bind(&CGuiHandler::popIntTotally,&GH,this),516,520,"IOK6432.DEF",SDLK_RETURN); + ok->assignedKeys.insert(SDLK_ESCAPE); + + deal = new CAdventureMapButton(CGI->generaltexth->zelp[585],boost::bind(&CAltarWindow::makeDeal,this),269,520,"ALTSACR.DEF"); + + if(Hero->getAlignment() != ::EAlignment::EVIL && Mode == EMarketMode::CREATURE_EXP) + new CAdventureMapButton(CGI->generaltexth->zelp[580], boost::bind(&CTradeWindow::setMode,this, EMarketMode::ARTIFACT_EXP), 516, 421, "ALTART.DEF"); + if(Hero->getAlignment() != ::EAlignment::GOOD && Mode == EMarketMode::ARTIFACT_EXP) + new CAdventureMapButton(CGI->generaltexth->zelp[572], boost::bind(&CTradeWindow::setMode,this, EMarketMode::CREATURE_EXP), 516, 421, "ALTSACC.DEF"); + + expPerUnit.resize(GameConstants::ARMY_SIZE, 0); + getExpValues(); + + expToLevel = new CLabel(73, 475, FONT_SMALL, CENTER); + expOnAltar = new CLabel(73, 543, FONT_SMALL, CENTER); + + setExpToLevel(); + calcTotalExp(); + blockTrade(); +} + +CAltarWindow::~CAltarWindow() +{ + +} + +void CAltarWindow::getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const +{ + leftToRightOffset = 289; + x = 45; + y = 110; + w = 58; + h = 64; + dx = 83; + dy = 98; +} + +void CAltarWindow::sliderMoved(int to) +{ + sacrificedUnits[hLeft->serial] = to; + updateRight(hRight); + deal->block(!to); + calcTotalExp(); + redraw(); +} + +void CAltarWindow::makeDeal() +{ + if(mode == EMarketMode::CREATURE_EXP) + { + blockTrade(); + slider->value = 0; + + std::vector toSacrifice = sacrificedUnits; + for (int i = 0; i < toSacrifice.size(); i++) + { + if(toSacrifice[i]) + LOCPLINT->cb->trade(market->o, mode, i, 0, toSacrifice[i], hero); + } + + for(int& val : sacrificedUnits) + val = 0; + + for(CTradeableItem *t : items[0]) + { + t->setType(CREATURE_PLACEHOLDER); + t->subtitle = ""; + } + } + else + { + for(const CArtifactInstance *art : arts->artifactsOnAltar) //sacrifice each artifact on the list + { + LOCPLINT->cb->trade(market->o, mode, hero->getArtPos(art), -1, 1, hero); + } + arts->artifactsOnAltar.clear(); + + for(CTradeableItem *t : items[0]) + { + t->setID(-1); + t->subtitle = ""; + } + + arts->commonInfo->reset(); + //arts->scrollBackpack(0); + deal->block(true); + } + + calcTotalExp(); +} + +void CAltarWindow::SacrificeAll() +{ + if(mode == EMarketMode::CREATURE_EXP) + { + bool movedAnything = false; + for(CTradeableItem *t : items[1]) + sacrificedUnits[t->serial] = hero->getStackCount(SlotID(t->serial)); + + sacrificedUnits[items[1].front()->serial]--; + + for(CTradeableItem *t : items[0]) + { + updateRight(t); + if(t->type == CREATURE) + movedAnything = true; + } + + deal->block(!movedAnything); + calcTotalExp(); + } + else + { + for(auto i = hero->artifactsWorn.cbegin(); i != hero->artifactsWorn.cend(); i++) + { + if(i->second.artifact->artType->id != ArtifactID::ART_LOCK) //ignore locks from assembled artifacts + moveFromSlotToAltar(i->first, nullptr, i->second.artifact); + } + + SacrificeBackpack(); + } + redraw(); +} + +void CAltarWindow::selectionChanged(bool side) +{ + if(mode != EMarketMode::CREATURE_EXP) + return; + + CTradeableItem *&selected = side ? hLeft : hRight; + CTradeableItem *&theOther = side ? hRight : hLeft; + + theOther = *std::find_if(items[!side].begin(), items[!side].end(), [&](const CTradeableItem * item) + { + return item->serial == selected->serial; + }); + + int stackCount = 0; + for (int i = 0; i < GameConstants::ARMY_SIZE; i++) + if(hero->getStackCount(SlotID(i)) > sacrificedUnits[i]) + stackCount++; + + slider->setAmount(hero->getStackCount(SlotID(hLeft->serial)) - (stackCount == 1)); + slider->block(!slider->amount); + slider->value = sacrificedUnits[hLeft->serial]; + max->block(!slider->amount); + readyToTrade = true; + redraw(); +} + +void CAltarWindow::mimicCres() +{ + std::vector positions; + getPositionsFor(positions, false, CREATURE); + + for(CTradeableItem *t : items[1]) + { + auto hlp = new CTradeableItem(positions[t->serial].topLeft(), CREATURE_PLACEHOLDER, t->id, false, t->serial); + hlp->pos = positions[t->serial] + this->pos.topLeft(); + items[0].push_back(hlp); + } +} + +Point CAltarWindow::selectionOffset(bool Left) const +{ + if(Left) + return Point(150, 421); + else + return Point(396, 421); +} + +std::string CAltarWindow::selectionSubtitle(bool Left) const +{ + if(Left && slider && hLeft) + return boost::lexical_cast(slider->value); + else if(!Left && hRight) + return hRight->subtitle; + else + return ""; +} + +void CAltarWindow::artifactsChanged(bool left) +{ + +} + +void CAltarWindow::garrisonChanged() +{ + if(mode != EMarketMode::CREATURE_EXP) + return; + + std::set empty; + getEmptySlots(empty); + + for(CTradeableItem *t : empty) + { + removeItem(*std::find_if(items[0].begin(), items[0].end(), [&](const CTradeableItem * item) + { + return item->serial == t->serial; + })); + } + + initSubs(true); + getExpValues(); +} + +void CAltarWindow::getExpValues() +{ + int dump; + for(CTradeableItem *t : items[1]) + if(t->id >= 0) + market->getOffer(t->id, 0, dump, expPerUnit[t->serial], EMarketMode::CREATURE_EXP); +} + +void CAltarWindow::calcTotalExp() +{ + int val = 0; + if(mode == EMarketMode::CREATURE_EXP) + { + for (int i = 0; i < sacrificedUnits.size(); i++) + { + val += expPerUnit[i] * sacrificedUnits[i]; + } + } + else + { + for(const CArtifactInstance *art : arts->artifactsOnAltar) + { + int dmp, valOfArt; + market->getOffer(art->artType->id, 0, dmp, valOfArt, mode); + val += valOfArt; //WAS val += valOfArt * arts->artifactsOnAltar.count(*i); + } + } + val = hero->calculateXp(val); + expOnAltar->setText(boost::lexical_cast(val)); +} + +void CAltarWindow::setExpToLevel() +{ + expToLevel->setText(boost::lexical_cast(CGI->heroh->reqExp(CGI->heroh->level(hero->exp)+1) - hero->exp)); +} + +void CAltarWindow::blockTrade() +{ + hLeft = hRight = nullptr; + readyToTrade = false; + if(slider) + { + slider->block(true); + max->block(true); + } + deal->block(true); +} + +void CAltarWindow::updateRight(CTradeableItem *toUpdate) +{ + int val = sacrificedUnits[toUpdate->serial]; + toUpdate->setType(val ? CREATURE : CREATURE_PLACEHOLDER); + toUpdate->subtitle = val ? boost::str(boost::format(CGI->generaltexth->allTexts[122]) % boost::lexical_cast(val * expPerUnit[toUpdate->serial])) : ""; //%s exp +} + +int CAltarWindow::firstFreeSlot() +{ + int ret = -1; + while(items[0][++ret]->id >= 0 && ret + 1 < items[0].size()); + return ret < items[0].size() ? ret : -1; +} + +void CAltarWindow::SacrificeBackpack() +{ + std::multiset toOmmit = arts->artifactsOnAltar; + + for (auto & elem : hero->artifactsInBackpack) + { + + if(vstd::contains(toOmmit, elem.artifact)) + { + toOmmit -= elem.artifact; + continue; + } + + putOnAltar(nullptr, elem.artifact); + } + + arts->scrollBackpack(0); + calcTotalExp(); +} + +void CAltarWindow::artifactPicked() +{ + redraw(); +} + +void CAltarWindow::showAll(SDL_Surface * to) +{ + CTradeWindow::showAll(to); + if(mode == EMarketMode::ARTIFACT_EXP && arts && arts->commonInfo->src.art) + { + artIcon->setFrame(arts->commonInfo->src.art->artType->iconIndex); + artIcon->showAll(to); + + int dmp, val; + market->getOffer(arts->commonInfo->src.art->artType->id, 0, dmp, val, EMarketMode::ARTIFACT_EXP); + printAtMiddleLoc(boost::lexical_cast(val), 304, 498, FONT_SMALL, Colors::WHITE, to); + } +} + +bool CAltarWindow::putOnAltar(CTradeableItem* altarSlot, const CArtifactInstance *art) +{ + int artID = art->artType->id; + if(artID != 1 && artID < 7) //special art + { + logGlobal->warnStream() << "Cannot put special artifact on altar!"; + return false; + } + + if(!altarSlot) + { + int slotIndex = firstFreeSlot(); + if(slotIndex < 0) + { + logGlobal->warnStream() << "No free slots on altar!"; + return false; + } + altarSlot = items[0][slotIndex]; + } + + int dmp, val; + market->getOffer(artID, 0, dmp, val, EMarketMode::ARTIFACT_EXP); + + arts->artifactsOnAltar.insert(art); + altarSlot->setArtInstance(art); + altarSlot->subtitle = boost::lexical_cast(val); + + deal->block(false); + return true; +} + +void CAltarWindow::moveFromSlotToAltar(ArtifactPosition slotID, CTradeableItem* altarSlot, const CArtifactInstance *art) +{ + auto freeBackpackSlot = ArtifactPosition(hero->artifactsInBackpack.size() + GameConstants::BACKPACK_START); + if(arts->commonInfo->src.art) + { + arts->commonInfo->dst.slotID = freeBackpackSlot; + arts->commonInfo->dst.AOH = arts; + } + + if(putOnAltar(altarSlot, art)) + { + if(slotID < GameConstants::BACKPACK_START) + LOCPLINT->cb->swapArtifacts(ArtifactLocation(hero, slotID), ArtifactLocation(hero, freeBackpackSlot)); + else + { + arts->commonInfo->src.clear(); + arts->commonInfo->dst.clear(); + CCS->curh->dragAndDropCursor(nullptr); + arts->unmarkSlots(false); + } + } +} diff --git a/client/gui/CTradeWindow.h b/client/gui/CTradeWindow.h new file mode 100644 index 000000000..82ffff9ab --- /dev/null +++ b/client/gui/CTradeWindow.h @@ -0,0 +1,166 @@ +#pragma once + +#include "CIntObjectClasses.h" +#include "CArtifactHolder.h" + +/* + * CTradeWindow.h, 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 + * + */ + +class IMarket; + +class CTradeWindow : public CWindowObject, public CWindowWithArtifacts //base for markets and altar of sacrifice +{ +public: + enum EType + { + RESOURCE, PLAYER, ARTIFACT_TYPE, CREATURE, CREATURE_PLACEHOLDER, ARTIFACT_PLACEHOLDER, ARTIFACT_INSTANCE + }; + class CTradeableItem : public CIntObject + { + CAnimImage * image; + + std::string getFilename(); + int getIndex(); + public: + const CArtifactInstance *hlp; //holds ptr to artifact instance id type artifact + EType type; + int id; + const int serial; + const bool left; + std::string subtitle; //empty if default + + void setType(EType newType); + void setID(int newID); + + const CArtifactInstance *getArtInstance() const; + void setArtInstance(const CArtifactInstance *art); + + CFunctionList callback; + bool downSelection; + + void showAllAt(const Point &dstPos, const std::string &customSub, SDL_Surface * to); + + void clickRight(tribool down, bool previousState); + void hover (bool on); + void showAll(SDL_Surface * to); + void clickLeft(tribool down, bool previousState); + std::string getName(int number = -1) const; + CTradeableItem(Point pos, EType Type, int ID, bool Left, int Serial); + }; + + const IMarket *market; + const CGHeroInstance *hero; + + CArtifactsOfHero *arts; + //all indexes: 1 = left, 0 = right + std::vector items[2]; + CTradeableItem *hLeft, *hRight; //highlighted items (nullptr if no highlight) + EType itemsType[2]; + + EMarketMode::EMarketMode mode;//0 - res<->res; 1 - res<->plauer; 2 - buy artifact; 3 - sell artifact + CAdventureMapButton *ok, *max, *deal; + CSlider *slider; //for choosing amount to be exchanged + bool readyToTrade; + + CTradeWindow(std::string bgName, const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode); //c + + void showAll(SDL_Surface * to); + + void initSubs(bool Left); + void initTypes(); + void initItems(bool Left); + std::vector *getItemsIds(bool Left); //nullptr if default + void getPositionsFor(std::vector &poss, bool Left, EType type) const; + void removeItems(const std::set &toRemove); + void removeItem(CTradeableItem * t); + void getEmptySlots(std::set &toRemove); + void setMode(EMarketMode::EMarketMode Mode); //mode setter + + void artifactSelected(CArtPlace *slot); //used when selling artifacts -> called when user clicked on artifact slot + + virtual void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const = 0; + virtual void selectionChanged(bool side) = 0; //true == left + virtual Point selectionOffset(bool Left) const = 0; + virtual std::string selectionSubtitle(bool Left) const = 0; + virtual void garrisonChanged() = 0; + virtual void artifactsChanged(bool left) = 0; +}; + +class CMarketplaceWindow : public CTradeWindow +{ + bool printButtonFor(EMarketMode::EMarketMode M) const; + + std::string getBackgroundForMode(EMarketMode::EMarketMode mode); +public: + int r1, r2; //suggested amounts of traded resources + bool madeTransaction; //if player made at least one transaction + CTextBox *traderText; + + void setMax(); + void sliderMoved(int to); + void makeDeal(); + void selectionChanged(bool side); //true == left + CMarketplaceWindow(const IMarket *Market, const CGHeroInstance *Hero = nullptr, EMarketMode::EMarketMode Mode = EMarketMode::RESOURCE_RESOURCE); //c-tor + ~CMarketplaceWindow(); //d-tor + + Point selectionOffset(bool Left) const; + std::string selectionSubtitle(bool Left) const; + + + void garrisonChanged(); //removes creatures with count 0 from the list (apparently whole stack has been sold) + void artifactsChanged(bool left); + void resourceChanged(int type, int val); + + void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const; + void updateTraderText(); +}; + +class CAltarWindow : public CTradeWindow +{ + CAnimImage * artIcon; +public: + CAltarWindow(const IMarket *Market, const CGHeroInstance *Hero, EMarketMode::EMarketMode Mode); //c-tor + + void getExpValues(); + ~CAltarWindow(); //d-tor + + std::vector sacrificedUnits, //[slot_nr] -> how many creatures from that slot will be sacrificed + expPerUnit; + + CAdventureMapButton *sacrificeAll, *sacrificeBackpack; + CLabel *expToLevel, *expOnAltar; + + + void selectionChanged(bool side); //true == left + void SacrificeAll(); + void SacrificeBackpack(); + + void putOnAltar(int backpackIndex); + bool putOnAltar(CTradeableItem* altarSlot, const CArtifactInstance *art); + void makeDeal(); + void showAll(SDL_Surface * to); + + void blockTrade(); + void sliderMoved(int to); + void getBaseForPositions(EType type, int &dx, int &dy, int &x, int &y, int &h, int &w, bool Right, int &leftToRightOffset) const; + void mimicCres(); + + Point selectionOffset(bool Left) const; + std::string selectionSubtitle(bool Left) const; + void garrisonChanged(); + void artifactsChanged(bool left); + void calcTotalExp(); + void setExpToLevel(); + void updateRight(CTradeableItem *toUpdate); + + void artifactPicked(); + int firstFreeSlot(); + void moveFromSlotToAltar(ArtifactPosition slotID, CTradeableItem* altarSlot, const CArtifactInstance *art); +};