diff --git a/client/widgets/CArtifactHolder.cpp b/client/widgets/CArtifactHolder.cpp index f885ad961..0af6f901a 100644 --- a/client/widgets/CArtifactHolder.cpp +++ b/client/widgets/CArtifactHolder.cpp @@ -32,7 +32,7 @@ */ CArtPlace::CArtPlace(Point position, const CArtifactInstance * Art): - locked(false), picked(false), marked(false), ourArt(Art) + locked(false), picked(false), marked(false), ourArt(Art) { pos += position; pos.w = pos.h = 44; @@ -180,7 +180,7 @@ void CArtPlace::clickLeft(tribool down, bool previousState) if(srcInBackpack && srcInSameHero) { if(!ourArt //cannot move from backpack to AFTER backpack -> combined with vstd::amin above it will guarantee that dest is at most the last artifact - || ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted + || ourOwner->commonInfo->src.slotID < ourOwner->commonInfo->dst.slotID) //rearranging arts in backpack after taking src artifact, the dest id will be shifted vstd::advance(ourOwner->commonInfo->dst.slotID, -1); } if(srcInSameHero && ourOwner->commonInfo->dst.slotID == ourOwner->commonInfo->src.slotID) //we came to src == dst @@ -386,70 +386,36 @@ void CArtPlace::setArtifact(const CArtifactInstance *art) image->disable(); text = std::string(); hoverText = CGI->generaltexth->allTexts[507]; + return; + } + + image->enable(); + image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex); + + text = art->getEffectiveDescription(ourOwner->curHero); + + if(art->artType->id == ArtifactID::SPELL_SCROLL) + { + int spellID = art->getGivenSpellID(); + if(spellID >= 0) + { + //add spell component info (used to provide a pic in r-click popup) + baseType = CComponent::spell; + type = spellID; + bonusValue = 0; + } } else { - image->enable(); - image->setFrame(locked ? ArtifactID::ART_LOCK : art->artType->iconIndex); - - std::string artDesc = ourArt->artType->Description(); - if (vstd::contains (artDesc, '{')) - text = artDesc; - else - text = '{' + ourArt->artType->Name() + "}\n\n" + artDesc; //workaround for new artifacts with single name, turns it to H3-style - - if(art->artType->id == ArtifactID::SPELL_SCROLL) - { - // we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll. - // so we want to replace text in [...] with a spell name - // however other language versions don't have name placeholder at all, so we have to be careful - int spellID = art->getGivenSpellID(); - size_t nameStart = text.find_first_of('['); - size_t nameEnd = text.find_first_of(']', nameStart); - if(spellID >= 0) - { - if(nameStart != std::string::npos && nameEnd != std::string::npos) - text = text.replace(nameStart, nameEnd - nameStart + 1, CGI->spellh->objects[spellID]->name); - - //add spell component info (used to provide a pic in r-click popup) - baseType = CComponent::spell; - type = spellID; - bonusValue = 0; - } - } - else - { - baseType = CComponent::artifact; - type = art->artType->id; - bonusValue = 0; - } - if (art->artType->constituents) //display info about components of combined artifact - { - //TODO - } - else if (art->artType->constituentOf.size()) //display info about set - { - std::string artList; - auto combinedArt = art->artType->constituentOf[0]; - text += "\n\n"; - text += "{" + combinedArt->Name() + "}"; - int wornArtifacts = 0; - for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set? - { - artList += "\n" + a->Name(); - if (ourOwner->curHero->hasArt(a->id, true)) - wornArtifacts++; - } - text += " (" + boost::str(boost::format("%d") % wornArtifacts) + " / " + - boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList; - //TODO: fancy colors and fonts for this text - } - - if (locked) // Locks should appear as empty. - hoverText = CGI->generaltexth->allTexts[507]; - else - hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name()); + baseType = CComponent::artifact; + type = art->artType->id; + bonusValue = 0; } + + if (locked) // Locks should appear as empty. + hoverText = CGI->generaltexth->allTexts[507]; + else + hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->Name()); } void CArtifactsOfHero::SCommonPart::reset() @@ -811,7 +777,7 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact } else if(src.slot >= GameConstants::BACKPACK_START && src.slot < commonInfo->src.slotID && - src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one + src.isHolder(commonInfo->src.AOH->curHero)) //artifact taken from before currently picked one { //int fixedSlot = src.hero->getArtPos(commonInfo->src.art); vstd::advance(commonInfo->src.slotID, -1); @@ -825,14 +791,14 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact } updateParentWindow(); - int shift = 0; + int shift = 0; // if(dst.slot >= Arts::BACKPACK_START && dst.slot - Arts::BACKPACK_START < backpackPos) // shift++; // - if(src.slot < GameConstants::BACKPACK_START && dst.slot - GameConstants::BACKPACK_START < backpackPos) + if(src.slot < GameConstants::BACKPACK_START && dst.slot - GameConstants::BACKPACK_START < backpackPos) shift++; if(dst.slot < GameConstants::BACKPACK_START && src.slot - GameConstants::BACKPACK_START < backpackPos) - shift--; + shift--; if( (isCurHeroSrc && src.slot >= GameConstants::BACKPACK_START) || (isCurHeroDst && dst.slot >= GameConstants::BACKPACK_START) ) diff --git a/client/widgets/CComponent.cpp b/client/widgets/CComponent.cpp index 41f331539..e841f830c 100644 --- a/client/widgets/CComponent.cpp +++ b/client/widgets/CComponent.cpp @@ -7,6 +7,7 @@ #include "../CMessage.h" #include "../CGameInfo.h" #include "../widgets/Images.h" +#include "../widgets/CArtifactHolder.h" #include "../windows/CAdvmapInterface.h" #include "../../lib/CArtHandler.h" @@ -144,14 +145,26 @@ size_t CComponent::getIndex() std::string CComponent::getDescription() { - switch (compType) + switch(compType) { case primskill: return (subtype < 4)? CGI->generaltexth->arraytxt[2+subtype] //Primary skill : CGI->generaltexth->allTexts[149]; //mana case secskill: return CGI->generaltexth->skillInfoTexts[subtype][val-1]; case resource: return CGI->generaltexth->allTexts[242]; case creature: return ""; - case artifact: return CGI->arth->artifacts[subtype]->Description(); + case artifact: + { + std::unique_ptr art; + if (subtype != ArtifactID::SPELL_SCROLL) + { + art.reset(CArtifactInstance::createNewArtifactInstance(subtype)); + } + else + { + art.reset(CArtifactInstance::createScroll(static_cast(val))); + } + return art->getEffectiveDescription(); + } case experience: return CGI->generaltexth->allTexts[241]; case spell: return CGI->spellh->objects[subtype]->getLevelInfo(val).description; case morale: return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)]; @@ -166,7 +179,7 @@ std::string CComponent::getDescription() std::string CComponent::getSubtitle() { - if (!perDay) + if(!perDay) return getSubtitleInternal(); std::string ret = CGI->generaltexth->allTexts[3]; diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index a601d7e33..dda828c3a 100644 --- a/client/windows/CTradeWindow.cpp +++ b/client/windows/CTradeWindow.cpp @@ -269,7 +269,7 @@ void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState) case ARTIFACT_TYPE: case ARTIFACT_PLACEHOLDER: if(id >= 0) - adventureInt->handleRightClick(CGI->arth->artifacts[id]->Description(), down); + adventureInt->handleRightClick(hlp->getEffectiveDescription(), down); break; } } @@ -500,14 +500,14 @@ void CTradeWindow::getPositionsFor(std::vector &poss, bool Left, EType typ int h, w, x, y, dx, dy; int leftToRightOffset; getBaseForPositions(type, dx, dy, x, y, h, w, !Left, leftToRightOffset); - - const std::vector tmp = + + const std::vector tmp = { 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) + genRect(h, w, x + dx, y + 2*dy) }; - + vstd::concatenate(poss, tmp); if(!Left) diff --git a/lib/CArtHandler.cpp b/lib/CArtHandler.cpp index 4e48b5ff3..80957e0eb 100644 --- a/lib/CArtHandler.cpp +++ b/lib/CArtHandler.cpp @@ -738,9 +738,14 @@ std::string CArtifactInstance::nodeName() const } CArtifactInstance * CArtifactInstance::createScroll( const CSpell *s) +{ + return createScroll(s->id); +} + +CArtifactInstance *CArtifactInstance::createScroll(SpellID sid) { auto ret = new CArtifactInstance(VLC->arth->artifacts[ArtifactID::SPELL_SCROLL]); - auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, s->id); + auto b = new Bonus(Bonus::PERMANENT, Bonus::SPELL, Bonus::ARTIFACT_INSTANCE, -1, ArtifactID::SPELL_SCROLL, sid); ret->addNewBonus(b); return ret; } @@ -752,6 +757,48 @@ void CArtifactInstance::init() setNodeType(ARTIFACT_INSTANCE); } +std::string CArtifactInstance::getEffectiveDescription( + const CGHeroInstance *hero) const +{ + std::string text = this->artType->Description(); + if (!vstd::contains(text, '{')) + text = '{' + this->artType->Name() + "}\n\n" + text; //workaround for new artifacts with single name, turns it to H3-style + + if(this->artType->id == ArtifactID::SPELL_SCROLL) + { + // we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll. + // so we want to replace text in [...] with a spell name + // however other language versions don't have name placeholder at all, so we have to be careful + int spellID = this->getGivenSpellID(); + size_t nameStart = text.find_first_of('['); + size_t nameEnd = text.find_first_of(']', nameStart); + if(spellID >= 0) + { + if(nameStart != std::string::npos && nameEnd != std::string::npos) + text = text.replace(nameStart, nameEnd - nameStart + 1, VLC->spellh->objects[spellID]->name); + } + } + else if (hero && this->artType->constituentOf.size()) //display info about set + { + std::string artList; + auto combinedArt = this->artType->constituentOf[0]; + text += "\n\n"; + text += "{" + combinedArt->Name() + "}"; + int wornArtifacts = 0; + for (auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set? + { + artList += "\n" + a->Name(); + if (hero->hasArt(a->id, true)) + wornArtifacts++; + } + text += " (" + boost::str(boost::format("%d") % wornArtifacts) + " / " + + boost::str(boost::format("%d") % combinedArt->constituents->size()) + ")" + artList; + //TODO: fancy colors and fonts for this text + } + + return text; +} + ArtifactPosition CArtifactInstance::firstAvailableSlot(const CArtifactSet *h) const { for(auto slot : artType->possibleSlots.at(h->bearerType())) @@ -900,7 +947,7 @@ SpellID CArtifactInstance::getGivenSpellID() const const Bonus * b = getBonusLocalFirst(Selector::type(Bonus::SPELL)); if(!b) { - logGlobal->warnStream() << "Warning: " << nodeName() << " doesn't bear any spell!"; + logGlobal->warnStream() << "Warning: " << nodeName() << " doesn't bear any spell!"; return SpellID::NONE; } return SpellID(b->subtype); diff --git a/lib/CArtHandler.h b/lib/CArtHandler.h index d9ffdc46e..a19539c12 100644 --- a/lib/CArtHandler.h +++ b/lib/CArtHandler.h @@ -119,6 +119,7 @@ public: void deserializationFix(); void setType(CArtifact *Art); + std::string getEffectiveDescription(const CGHeroInstance *hero = nullptr) const; ArtifactPosition firstAvailableSlot(const CArtifactSet *h) const; ArtifactPosition firstBackpackSlot(const CArtifactSet *h) const; SpellID getGivenSpellID() const; //to be used with scrolls (and similar arts), -1 if none @@ -143,6 +144,7 @@ public: } static CArtifactInstance *createScroll(const CSpell *s); + static CArtifactInstance *createScroll(SpellID sid); static CArtifactInstance *createNewArtifactInstance(CArtifact *Art); static CArtifactInstance *createNewArtifactInstance(int aid); }; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 968c4b571..df0fe9c86 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -471,7 +471,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer const CArmedInstance *bEndArmy2 = gs->curB->sides.at(1).armyObject; const BattleResult::EResult result = battleResult.get()->result; - auto findBattleQuery = [this] () -> std::shared_ptr + auto findBattleQuery = [this]() -> std::shared_ptr { for(auto &q : queries.allQueries()) { @@ -526,66 +526,70 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer } - std::vector arts; //display them in window + std::vector arts; //display them in window - if (result == BattleResult::NORMAL && finishingBattle->winnerHero) + if(result == BattleResult::NORMAL && finishingBattle->winnerHero) { - if (finishingBattle->loserHero) + auto sendMoveArtifact = [&](const CArtifactInstance *art, MoveArtifact *ma) { - auto artifactsWorn = finishingBattle->loserHero->artifactsWorn; //TODO: wrap it into a function, somehow (boost::variant -_-) + arts.push_back(art); + ma->dst = ArtifactLocation(finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); + sendAndApply(ma); + }; + if(finishingBattle->loserHero) + { + //TODO: wrap it into a function, somehow (boost::variant -_-) + auto artifactsWorn = finishingBattle->loserHero->artifactsWorn; for (auto artSlot : artifactsWorn) { MoveArtifact ma; - ma.src = ArtifactLocation (finishingBattle->loserHero, artSlot.first); + ma.src = ArtifactLocation(finishingBattle->loserHero, artSlot.first); const CArtifactInstance * art = ma.src.getArt(); - if (art && !art->artType->isBig() && art->artType->id != ArtifactID::SPELLBOOK) // don't move war machines or locked arts (spellbook) + if(art && !art->artType->isBig() && + art->artType->id != ArtifactID::SPELLBOOK) + // don't move war machines or locked arts (spellbook) { - arts.push_back (art->artType->id); - ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); - sendAndApply(&ma); + sendMoveArtifact(art, &ma); } } - while (!finishingBattle->loserHero->artifactsInBackpack.empty()) + while(!finishingBattle->loserHero->artifactsInBackpack.empty()) { //we assume that no big artifacts can be found MoveArtifact ma; - ma.src = ArtifactLocation (finishingBattle->loserHero, + ma.src = ArtifactLocation(finishingBattle->loserHero, ArtifactPosition(GameConstants::BACKPACK_START)); //backpack automatically shifts arts to beginning const CArtifactInstance * art = ma.src.getArt(); - arts.push_back (art->artType->id); - ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); - sendAndApply(&ma); + if(art->artType->id != ArtifactID::GRAIL) //grail may not be won + { + sendMoveArtifact(art, &ma); + } } - if (finishingBattle->loserHero->commander) //TODO: what if commanders belong to no hero? + if(finishingBattle->loserHero->commander) //TODO: what if commanders belong to no hero? { artifactsWorn = finishingBattle->loserHero->commander->artifactsWorn; - for (auto artSlot : artifactsWorn) + for(auto artSlot : artifactsWorn) { MoveArtifact ma; - ma.src = ArtifactLocation (finishingBattle->loserHero->commander.get(), artSlot.first); + ma.src = ArtifactLocation(finishingBattle->loserHero->commander.get(), artSlot.first); const CArtifactInstance * art = ma.src.getArt(); if (art && !art->artType->isBig()) { - arts.push_back (art->artType->id); - ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); - sendAndApply(&ma); + sendMoveArtifact(art, &ma); } } } } - for (auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks) + for(auto armySlot : gs->curB->battleGetArmyObject(!battleResult.data->winner)->stacks) { auto artifactsWorn = armySlot.second->artifactsWorn; for (auto artSlot : artifactsWorn) { MoveArtifact ma; - ma.src = ArtifactLocation (armySlot.second, artSlot.first); + ma.src = ArtifactLocation(armySlot.second, artSlot.first); const CArtifactInstance * art = ma.src.getArt(); if (art && !art->artType->isBig()) { - arts.push_back (art->artType->id); - ma.dst = ArtifactLocation (finishingBattle->winnerHero, art->firstAvailableSlot(finishingBattle->winnerHero)); - sendAndApply(&ma); + sendMoveArtifact(art, &ma); } } } @@ -593,23 +597,25 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer sendAndApply(battleResult.data); //after this point casualties objects are destroyed - if (arts.size()) //display loot + if(arts.size()) //display loot { InfoWindow iw; iw.player = finishingBattle->winnerHero->tempOwner; iw.text.addTxt (MetaString::GENERAL_TXT, 30); //You have captured enemy artifact - for (auto id : arts) //TODO; separate function to display loot for various ojects? + for(auto art : arts) //TODO; separate function to display loot for various ojects? { - iw.components.push_back (Component (Component::ARTIFACT, id, 0, 0)); + iw.components.push_back(Component( + Component::ARTIFACT, art->artType->id, + art->artType->id == ArtifactID::SPELL_SCROLL? art->getGivenSpellID() : 0, 0)); if(iw.components.size() >= 14) { sendAndApply(&iw); iw.components.clear(); } } - if (iw.components.size()) + if(iw.components.size()) { sendAndApply(&iw); }