1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-26 03:52:01 +02:00

Merge pull request #2268 from SoundSSGood/CArtifactInstance-rework

CArtifact CArtifactInstance refactoring
This commit is contained in:
Ivan Savenko 2023-07-11 14:36:08 +03:00 committed by GitHub
commit 9acab48bc3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 610 additions and 507 deletions

View File

@ -1006,7 +1006,7 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
//FIXME: why are the above possible to be null? //FIXME: why are the above possible to be null?
bool emptySlotFound = false; bool emptySlotFound = false;
for(auto slot : artifact->artType->possibleSlots.at(target->bearerType())) for(auto slot : artifact->artType->getPossibleSlots().at(target->bearerType()))
{ {
ArtifactLocation destLocation(target, slot); ArtifactLocation destLocation(target, slot);
if(target->isPositionFree(slot) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move if(target->isPositionFree(slot) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move
@ -1019,7 +1019,7 @@ void AIGateway::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance
} }
if(!emptySlotFound) //try to put that atifact in already occupied slot if(!emptySlotFound) //try to put that atifact in already occupied slot
{ {
for(auto slot : artifact->artType->possibleSlots.at(target->bearerType())) for(auto slot : artifact->artType->getPossibleSlots().at(target->bearerType()))
{ {
auto otherSlot = target->getSlot(slot); auto otherSlot = target->getSlot(slot);
if(otherSlot && otherSlot->artifact) //we need to exchange artifact for better one if(otherSlot && otherSlot->artifact) //we need to exchange artifact for better one

View File

@ -306,10 +306,10 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
auto art1 = a1->artType; auto art1 = a1->artType;
auto art2 = a2->artType; auto art2 = a2->artType;
if(art1->price == art2->price) if(art1->getPrice() == art2->getPrice())
return art1->valOfBonuses(BonusType::PRIMARY_SKILL) > art2->valOfBonuses(BonusType::PRIMARY_SKILL); return art1->valOfBonuses(BonusType::PRIMARY_SKILL) > art2->valOfBonuses(BonusType::PRIMARY_SKILL);
else else
return art1->price > art2->price; return art1->getPrice() > art2->getPrice();
} }
bool isWeeklyRevisitable(const CGObjectInstance * obj) bool isWeeklyRevisitable(const CGObjectInstance * obj)

View File

@ -256,8 +256,8 @@ bool compareArtifacts(const CArtifactInstance * a1, const CArtifactInstance * a2
auto art1 = a1->artType; auto art1 = a1->artType;
auto art2 = a2->artType; auto art2 = a2->artType;
if(art1->price == art2->price) if(art1->getPrice() == art2->getPrice())
return art1->valOfBonuses(BonusType::PRIMARY_SKILL) > art2->valOfBonuses(BonusType::PRIMARY_SKILL); return art1->valOfBonuses(BonusType::PRIMARY_SKILL) > art2->valOfBonuses(BonusType::PRIMARY_SKILL);
else else
return art1->price > art2->price; return art1->getPrice() > art2->getPrice();
} }

View File

@ -1192,7 +1192,7 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
//FIXME: why are the above possible to be null? //FIXME: why are the above possible to be null?
bool emptySlotFound = false; bool emptySlotFound = false;
for(auto slot : artifact->artType->possibleSlots.at(target->bearerType())) for(auto slot : artifact->artType->getPossibleSlots().at(target->bearerType()))
{ {
ArtifactLocation destLocation(target, slot); ArtifactLocation destLocation(target, slot);
if(target->isPositionFree(slot) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move if(target->isPositionFree(slot) && artifact->canBePutAt(destLocation, true)) //combined artifacts are not always allowed to move
@ -1205,7 +1205,7 @@ void VCAI::pickBestArtifacts(const CGHeroInstance * h, const CGHeroInstance * ot
} }
if(!emptySlotFound) //try to put that atifact in already occupied slot if(!emptySlotFound) //try to put that atifact in already occupied slot
{ {
for(auto slot : artifact->artType->possibleSlots.at(target->bearerType())) for(auto slot : artifact->artType->getPossibleSlots().at(target->bearerType()))
{ {
auto otherSlot = target->getSlot(slot); auto otherSlot = target->getSlot(slot);
if(otherSlot && otherSlot->artifact) //we need to exchange artifact for better one if(otherSlot && otherSlot->artifact) //we need to exchange artifact for better one

View File

@ -236,11 +236,11 @@ void CHeroArtPlace::addCombinedArtInfo(std::map<const CArtifact*, int> & arts)
text += "{" + combinedArt.first->getNameTranslated() + "}"; text += "{" + combinedArt.first->getNameTranslated() + "}";
if(arts.size() == 1) if(arts.size() == 1)
{ {
for(const auto part : *combinedArt.first->constituents) for(const auto part : combinedArt.first->getConstituents())
artList += "\n" + part->getNameTranslated(); artList += "\n" + part->getNameTranslated();
} }
text += " (" + boost::str(boost::format("%d") % combinedArt.second) + " / " + text += " (" + boost::str(boost::format("%d") % combinedArt.second) + " / " +
boost::str(boost::format("%d") % combinedArt.first->constituents->size()) + ")" + artList; boost::str(boost::format("%d") % combinedArt.first->getConstituents().size()) + ")" + artList;
} }
} }
@ -290,9 +290,9 @@ bool ArtifactUtilsClient::askToDisassemble(const CGHeroInstance * hero, const Ar
const auto art = hero->getArt(slot); const auto art = hero->getArt(slot);
assert(art); assert(art);
if(art->canBeDisassembled()) if(art->isCombined())
{ {
if(ArtifactUtils::isSlotBackpack(slot) && !ArtifactUtils::isBackpackFreeSlots(hero, art->artType->constituents->size() - 1)) if(ArtifactUtils::isSlotBackpack(slot) && !ArtifactUtils::isBackpackFreeSlots(hero, art->artType->getConstituents().size() - 1))
return false; return false;
LOCPLINT->showArtifactAssemblyDialog( LOCPLINT->showArtifactAssemblyDialog(

View File

@ -98,13 +98,10 @@ void CArtifactsOfHeroAltar::deleteFromVisible(const CArtifactInstance * artInst)
} }
else else
{ {
if(artInst->canBeDisassembled()) for(const auto & part : artInst->getPartsInfo())
{
for(const auto & part : dynamic_cast<const CCombinedArtifactInstance*>(artInst)->constituentsInfo)
{ {
if(part.slot != ArtifactPosition::PRE_FIRST) if(part.slot != ArtifactPosition::PRE_FIRST)
getArtPlace(part.slot)->setArtifact(nullptr); getArtPlace(part.slot)->setArtifact(nullptr);
} }
} }
} }
}

View File

@ -257,14 +257,14 @@ void CArtifactsOfHeroBase::setSlotData(ArtPlacePtr artPlace, const ArtifactPosit
{ {
artPlace->lockSlot(slotInfo->locked); artPlace->lockSlot(slotInfo->locked);
artPlace->setArtifact(slotInfo->artifact); artPlace->setArtifact(slotInfo->artifact);
if(!slotInfo->artifact->canBeDisassembled()) if(!slotInfo->artifact->isCombined())
{ {
// If the artifact is part of at least one combined artifact, add additional information // If the artifact is part of at least one combined artifact, add additional information
std::map<const CArtifact*, int> arts; std::map<const CArtifact*, int> arts;
for(const auto combinedArt : slotInfo->artifact->artType->constituentOf) for(const auto combinedArt : slotInfo->artifact->artType->getPartOf())
{ {
arts.insert(std::pair(combinedArt, 0)); arts.insert(std::pair(combinedArt, 0));
for(const auto part : *combinedArt->constituents) for(const auto part : combinedArt->getConstituents())
if(artSet.hasArt(part->getId(), true)) if(artSet.hasArt(part->getId(), true))
arts.at(combinedArt)++; arts.at(combinedArt)++;
} }

View File

@ -34,6 +34,7 @@
#include "../../lib/CGeneralTextHandler.h" #include "../../lib/CGeneralTextHandler.h"
#include "../../lib/NetPacksBase.h" #include "../../lib/NetPacksBase.h"
#include "../../lib/CArtHandler.h" #include "../../lib/CArtHandler.h"
#include "../../lib/CArtifactInstance.h"
CComponent::CComponent(Etype Type, int Subtype, int Val, ESize imageSize, EFonts font): CComponent::CComponent(Etype Type, int Subtype, int Val, ESize imageSize, EFonts font):
perDay(false) perDay(false)
@ -169,16 +170,12 @@ std::string CComponent::getDescription()
case artifact: case artifact:
{ {
auto artID = ArtifactID(subtype); auto artID = ArtifactID(subtype);
std::unique_ptr<CArtifactInstance> art; auto description = VLC->arth->objects[artID]->getDescriptionTranslated();
if (artID != ArtifactID::SPELL_SCROLL) if(artID == ArtifactID::SPELL_SCROLL)
{ {
art.reset(ArtifactUtils::createNewArtifactInstance(artID)); ArtifactUtils::insertScrrollSpellName(description, SpellID(val));
} }
else return description;
{
art.reset(ArtifactUtils::createScroll(SpellID(val)));
}
return art->getDescription();
} }
case experience: return CGI->generaltexth->allTexts[241]; case experience: return CGI->generaltexth->allTexts[241];
case spell: return (*CGI->spellh)[subtype]->getDescriptionTranslated(val); case spell: return (*CGI->spellh)[subtype]->getDescriptionTranslated(val);

View File

@ -800,7 +800,7 @@ void CCastleBuildings::enterBlacksmith(ArtifactID artifactID)
bool possible = LOCPLINT->cb->getResourceAmount(EGameResID::GOLD) >= price; bool possible = LOCPLINT->cb->getResourceAmount(EGameResID::GOLD) >= price;
if(possible) if(possible)
{ {
for(auto slot : art->possibleSlots.at(ArtBearer::HERO)) for(auto slot : art->getPossibleSlots().at(ArtBearer::HERO))
{ {
if(hero->getArt(slot) == nullptr) if(hero->getArt(slot) == nullptr)
{ {

View File

@ -588,7 +588,7 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
auto art = parent->info->stackNode->getArt(ArtifactPosition::CREATURE_SLOT); auto art = parent->info->stackNode->getArt(ArtifactPosition::CREATURE_SLOT);
if(art) if(art)
{ {
parent->stackArtifactIcon = std::make_shared<CAnimImage>("ARTIFACT", art->artType->iconIndex, 0, pos.x, pos.y); parent->stackArtifactIcon = std::make_shared<CAnimImage>("ARTIFACT", art->artType->getIconIndex(), 0, pos.x, pos.y);
parent->stackArtifactHelp = std::make_shared<LRClickableAreaWTextComp>(Rect(pos, Point(44, 44)), CComponent::artifact); parent->stackArtifactHelp = std::make_shared<LRClickableAreaWTextComp>(Rect(pos, Point(44, 44)), CComponent::artifact);
parent->stackArtifactHelp->type = art->artType->getId(); parent->stackArtifactHelp->type = art->artType->getId();

View File

@ -803,7 +803,7 @@ void CMarketplaceWindow::makeDeal()
leftIdToSend = hLeft->serial; leftIdToSend = hLeft->serial;
break; break;
case EMarketMode::ARTIFACT_RESOURCE: case EMarketMode::ARTIFACT_RESOURCE:
leftIdToSend = hLeft->getArtInstance()->id.getNum(); leftIdToSend = hLeft->getArtInstance()->getId().getNum();
break; break;
case EMarketMode::RESOURCE_ARTIFACT: case EMarketMode::RESOURCE_ARTIFACT:
if(!ArtifactID(hRight->id).toArtifact()->canBePutAt(hero)) if(!ArtifactID(hRight->id).toArtifact()->canBePutAt(hero))

View File

@ -220,6 +220,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/BattleFieldHandler.cpp ${MAIN_LIB_DIR}/BattleFieldHandler.cpp
${MAIN_LIB_DIR}/CAndroidVMHelper.cpp ${MAIN_LIB_DIR}/CAndroidVMHelper.cpp
${MAIN_LIB_DIR}/CArtHandler.cpp ${MAIN_LIB_DIR}/CArtHandler.cpp
${MAIN_LIB_DIR}/CArtifactInstance.cpp
${MAIN_LIB_DIR}/CBonusTypeHandler.cpp ${MAIN_LIB_DIR}/CBonusTypeHandler.cpp
${MAIN_LIB_DIR}/CBuildingHandler.cpp ${MAIN_LIB_DIR}/CBuildingHandler.cpp
${MAIN_LIB_DIR}/CConfigHandler.cpp ${MAIN_LIB_DIR}/CConfigHandler.cpp
@ -550,6 +551,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
${MAIN_LIB_DIR}/BattleFieldHandler.h ${MAIN_LIB_DIR}/BattleFieldHandler.h
${MAIN_LIB_DIR}/CAndroidVMHelper.h ${MAIN_LIB_DIR}/CAndroidVMHelper.h
${MAIN_LIB_DIR}/CArtHandler.h ${MAIN_LIB_DIR}/CArtHandler.h
${MAIN_LIB_DIR}/CArtifactInstance.h
${MAIN_LIB_DIR}/CBonusTypeHandler.h ${MAIN_LIB_DIR}/CBonusTypeHandler.h
${MAIN_LIB_DIR}/CBuildingHandler.h ${MAIN_LIB_DIR}/CBuildingHandler.h
${MAIN_LIB_DIR}/CConfigHandler.h ${MAIN_LIB_DIR}/CConfigHandler.h

View File

@ -12,6 +12,7 @@
#include "CArtHandler.h" #include "CArtHandler.h"
#include "GameSettings.h" #include "GameSettings.h"
#include "spells/CSpellHandler.h"
#include "mapping/CMap.h" #include "mapping/CMap.h"
#include "mapObjects/CGHeroInstance.h" #include "mapObjects/CGHeroInstance.h"
@ -21,7 +22,7 @@ VCMI_LIB_NAMESPACE_BEGIN
DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtAnyPosition(const CArtifactSet * target, const ArtifactID & aid) DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtAnyPosition(const CArtifactSet * target, const ArtifactID & aid)
{ {
const auto * art = aid.toArtifact(); const auto * art = aid.toArtifact();
for(const auto & slot : art->possibleSlots.at(target->bearerType())) for(const auto & slot : art->getPossibleSlots().at(target->bearerType()))
{ {
if(art->canBePutAt(target, slot)) if(art->canBePutAt(target, slot))
return slot; return slot;
@ -119,15 +120,15 @@ DLL_LINKAGE std::vector<const CArtifact*> ArtifactUtils::assemblyPossibilities(
{ {
std::vector<const CArtifact*> arts; std::vector<const CArtifact*> arts;
const auto * art = aid.toArtifact(); const auto * art = aid.toArtifact();
if(art->canBeDisassembled()) if(art->isCombined())
return arts; return arts;
for(const auto artifact : art->constituentOf) for(const auto artifact : art->getPartOf())
{ {
assert(artifact->constituents); assert(artifact->constituents);
bool possible = true; bool possible = true;
for(const auto constituent : *artifact->constituents) //check if all constituents are available for(const auto constituent : artifact->getConstituents()) //check if all constituents are available
{ {
if(equipped) if(equipped)
{ {
@ -165,24 +166,23 @@ DLL_LINKAGE CArtifactInstance * ArtifactUtils::createScroll(const SpellID & sid)
DLL_LINKAGE CArtifactInstance * ArtifactUtils::createNewArtifactInstance(CArtifact * art) DLL_LINKAGE CArtifactInstance * ArtifactUtils::createNewArtifactInstance(CArtifact * art)
{ {
if(art->canBeDisassembled()) assert(art);
auto * artInst = new CArtifactInstance(art);
if(art->isCombined())
{ {
auto * ret = new CCombinedArtifactInstance(art); assert(art->constituents);
ret->createConstituents(); for(const auto & part : art->getConstituents())
return ret; artInst->addPart(ArtifactUtils::createNewArtifactInstance(part), ArtifactPosition::PRE_FIRST);
} }
else if(art->isGrowing())
{
auto * ret = new CArtifactInstance(art);
if(dynamic_cast<CGrowingArtifact*>(art))
{ {
auto bonus = std::make_shared<Bonus>(); auto bonus = std::make_shared<Bonus>();
bonus->type = BonusType::LEVEL_COUNTER; bonus->type = BonusType::LEVEL_COUNTER;
bonus->val = 0; bonus->val = 0;
ret->addNewBonus(bonus); artInst->addNewBonus(bonus);
}
return ret;
} }
return artInst;
} }
DLL_LINKAGE CArtifactInstance * ArtifactUtils::createNewArtifactInstance(const ArtifactID & aid) DLL_LINKAGE CArtifactInstance * ArtifactUtils::createNewArtifactInstance(const ArtifactID & aid)
@ -209,15 +209,28 @@ DLL_LINKAGE CArtifactInstance * ArtifactUtils::createArtifact(CMap * map, const
art = new CArtifactInstance(); // random, empty art = new CArtifactInstance(); // random, empty
} }
map->addNewArtifactInstance(art); map->addNewArtifactInstance(art);
if(art->artType && art->canBeDisassembled()) if(art->artType && art->isCombined())
{ {
auto * combined = dynamic_cast<CCombinedArtifactInstance*>(art); for(auto & part : art->getPartsInfo())
for(CCombinedArtifactInstance::ConstituentInfo & ci : combined->constituentsInfo)
{ {
map->addNewArtifactInstance(ci.art); map->addNewArtifactInstance(part.art);
} }
} }
return art; return art;
} }
DLL_LINKAGE void ArtifactUtils::insertScrrollSpellName(std::string & description, const SpellID & sid)
{
// We expect scroll description to be like this: This scroll contains the [spell name] spell which is added
// into spell book for as long as hero carries 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
auto nameStart = description.find_first_of('[');
auto nameEnd = description.find_first_of(']', nameStart);
if(sid.getNum() >= 0)
{
if(nameStart != std::string::npos && nameEnd != std::string::npos)
description = description.replace(nameStart, nameEnd - nameStart + 1, sid.toSpell(VLC->spells())->getNameTranslated());
}
}
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -41,6 +41,7 @@ namespace ArtifactUtils
DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(CArtifact * art); DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(CArtifact * art);
DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(const ArtifactID & aid); DLL_LINKAGE CArtifactInstance * createNewArtifactInstance(const ArtifactID & aid);
DLL_LINKAGE CArtifactInstance * createArtifact(CMap * map, const ArtifactID & aid, int spellID = -1); DLL_LINKAGE CArtifactInstance * createArtifact(CMap * map, const ArtifactID & aid, int spellID = -1);
DLL_LINKAGE void insertScrrollSpellName(std::string & description, const SpellID & sid);
} }
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -15,9 +15,7 @@
#include "CGeneralTextHandler.h" #include "CGeneralTextHandler.h"
#include "CModHandler.h" #include "CModHandler.h"
#include "GameSettings.h" #include "GameSettings.h"
#include "spells/CSpellHandler.h"
#include "mapObjects/MapObjects.h" #include "mapObjects/MapObjects.h"
#include "NetPacksBase.h"
#include "StringConstants.h" #include "StringConstants.h"
#include "mapObjectConstructors/AObjectTypeHandler.h" #include "mapObjectConstructors/AObjectTypeHandler.h"
@ -48,6 +46,51 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
bool CCombinedArtifact::isCombined() const
{
return !(constituents.empty());
}
const std::vector<CArtifact*> & CCombinedArtifact::getConstituents() const
{
return constituents;
}
const std::vector<CArtifact*> & CCombinedArtifact::getPartOf() const
{
return partOf;
}
bool CScrollArtifact::isScroll() const
{
return static_cast<const CArtifact*>(this)->getId() == ArtifactID::SPELL_SCROLL;
}
bool CGrowingArtifact::isGrowing() const
{
return !bonusesPerLevel.empty() || !thresholdBonuses.empty();
}
std::vector <std::pair<ui16, Bonus>> & CGrowingArtifact::getBonusesPerLevel()
{
return bonusesPerLevel;
}
const std::vector <std::pair<ui16, Bonus>> & CGrowingArtifact::getBonusesPerLevel() const
{
return bonusesPerLevel;
}
std::vector <std::pair<ui16, Bonus>> & CGrowingArtifact::getThresholdBonuses()
{
return thresholdBonuses;
}
const std::vector <std::pair<ui16, Bonus>> & CGrowingArtifact::getThresholdBonuses() const
{
return thresholdBonuses;
}
int32_t CArtifact::getIndex() const int32_t CArtifact::getIndex() const
{ {
return id.toEnum(); return id.toEnum();
@ -136,11 +179,6 @@ bool CArtifact::isTradable() const
} }
} }
bool CArtifact::canBeDisassembled() const
{
return !(constituents == nullptr);
}
bool CArtifact::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved) const bool CArtifact::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved) const
{ {
auto simpleArtCanBePutAt = [this](const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved) -> bool auto simpleArtCanBePutAt = [this](const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved) -> bool
@ -160,7 +198,7 @@ bool CArtifact::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, b
auto artCanBePutAt = [this, simpleArtCanBePutAt](const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved) -> bool auto artCanBePutAt = [this, simpleArtCanBePutAt](const CArtifactSet * artSet, ArtifactPosition slot, bool assumeDestRemoved) -> bool
{ {
if(canBeDisassembled()) if(isCombined())
{ {
if(!simpleArtCanBePutAt(artSet, slot, assumeDestRemoved)) if(!simpleArtCanBePutAt(artSet, slot, assumeDestRemoved))
return false; return false;
@ -172,7 +210,7 @@ bool CArtifact::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, b
if(assumeDestRemoved) if(assumeDestRemoved)
fittingSet.removeArtifact(slot); fittingSet.removeArtifact(slot);
assert(constituents); assert(constituents);
for(const auto art : *constituents) for(const auto art : constituents)
{ {
auto possibleSlot = ArtifactUtils::getArtAnyPosition(&fittingSet, art->getId()); auto possibleSlot = ArtifactUtils::getArtAnyPosition(&fittingSet, art->getId());
if(ArtifactUtils::isSlotEquipment(possibleSlot)) if(ArtifactUtils::isSlotEquipment(possibleSlot))
@ -215,6 +253,8 @@ bool CArtifact::canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot, b
} }
CArtifact::CArtifact() CArtifact::CArtifact()
: iconIndex(ArtifactID::NONE),
price(0)
{ {
setNodeType(ARTIFACT); setNodeType(ARTIFACT);
possibleSlots[ArtBearer::HERO]; //we want to generate map entry even if it will be empty possibleSlots[ArtBearer::HERO]; //we want to generate map entry even if it will be empty
@ -259,38 +299,21 @@ void CArtifact::addNewBonus(const std::shared_ptr<Bonus>& b)
CBonusSystemNode::addNewBonus(b); CBonusSystemNode::addNewBonus(b);
} }
const std::map<ArtBearer::ArtBearer, std::vector<ArtifactPosition>> & CArtifact::getPossibleSlots() const
{
return possibleSlots;
}
void CArtifact::updateFrom(const JsonNode& data) void CArtifact::updateFrom(const JsonNode& data)
{ {
//TODO:CArtifact::updateFrom //TODO:CArtifact::updateFrom
} }
void CArtifact::serializeJson(JsonSerializeFormat & handler) void CArtifact::setImage(int32_t iconIndex, std::string image, std::string large)
{ {
this->iconIndex = iconIndex;
} this->image = image;
this->large = large;
void CGrowingArtifact::levelUpArtifact (CArtifactInstance * art)
{
auto b = std::make_shared<Bonus>();
b->type = BonusType::LEVEL_COUNTER;
b->val = 1;
b->duration = BonusDuration::COMMANDER_KILLED;
art->accumulateBonus(b);
for(const auto & bonus : bonusesPerLevel)
{
if (art->valOfBonuses(BonusType::LEVEL_COUNTER) % bonus.first == 0) //every n levels
{
art->accumulateBonus(std::make_shared<Bonus>(bonus.second));
}
}
for(const auto & bonus : thresholdBonuses)
{
if (art->valOfBonuses(BonusType::LEVEL_COUNTER) == bonus.first) //every n levels
{
art->addNewBonus(std::make_shared<Bonus>(bonus.second));
}
}
} }
CArtHandler::~CArtHandler() = default; CArtHandler::~CArtHandler() = default;
@ -376,17 +399,19 @@ CArtifact * CArtHandler::loadFromJson(const std::string & scope, const JsonNode
assert(identifier.find(':') == std::string::npos); assert(identifier.find(':') == std::string::npos);
assert(!scope.empty()); assert(!scope.empty());
CArtifact * art = nullptr; CArtifact * art = new CArtifact();
if(!node["growing"].isNull())
if(!VLC->settings()->getBoolean(EGameSettings::MODULE_COMMANDERS) || node["growing"].isNull())
{ {
art = new CArtifact(); for(auto bonus : node["growing"]["bonusesPerLevel"].Vector())
{
art->bonusesPerLevel.emplace_back(static_cast<ui16>(bonus["level"].Float()), Bonus());
JsonUtils::parseBonus(bonus["bonus"], &art->bonusesPerLevel.back().second);
} }
else for(auto bonus : node["growing"]["thresholdBonuses"].Vector())
{ {
auto * growing = new CGrowingArtifact(); art->thresholdBonuses.emplace_back(static_cast<ui16>(bonus["level"].Float()), Bonus());
loadGrowingArt(growing, node); JsonUtils::parseBonus(bonus["bonus"], &art->thresholdBonuses.back().second);
art = growing; }
} }
art->id = ArtifactID(index); art->id = ArtifactID(index);
art->identifier = identifier; art->identifier = identifier;
@ -571,34 +596,19 @@ void CArtHandler::loadComponents(CArtifact * art, const JsonNode & node)
{ {
if (!node["components"].isNull()) if (!node["components"].isNull())
{ {
art->constituents = std::make_unique<std::vector<CArtifact *>>();
for(const auto & component : node["components"].Vector()) for(const auto & component : node["components"].Vector())
{ {
VLC->modh->identifiers.requestIdentifier("artifact", component, [=](si32 id) VLC->modh->identifiers.requestIdentifier("artifact", component, [=](si32 id)
{ {
// when this code is called both combinational art as well as component are loaded // when this code is called both combinational art as well as component are loaded
// so it is safe to access any of them // so it is safe to access any of them
art->constituents->push_back(objects[id]); art->constituents.push_back(objects[id]);
objects[id]->constituentOf.push_back(art); objects[id]->partOf.push_back(art);
}); });
} }
} }
} }
void CArtHandler::loadGrowingArt(CGrowingArtifact * art, const JsonNode & node) const
{
for (auto b : node["growing"]["bonusesPerLevel"].Vector())
{
art->bonusesPerLevel.emplace_back(static_cast<ui16>(b["level"].Float()), Bonus());
JsonUtils::parseBonus(b["bonus"], &art->bonusesPerLevel.back().second);
}
for (auto b : node["growing"]["thresholdBonuses"].Vector())
{
art->thresholdBonuses.emplace_back(static_cast<ui16>(b["level"].Float()), Bonus());
JsonUtils::parseBonus(b["bonus"], &art->thresholdBonuses.back().second);
}
}
ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags, std::function<bool(ArtifactID)> accepts) ArtifactID CArtHandler::pickRandomArtifact(CRandomGenerator & rand, int flags, std::function<bool(ArtifactID)> accepts)
{ {
auto getAllowedArts = [&](std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, CArtifact::EartClass flag) auto getAllowedArts = [&](std::vector<ConstTransitivePtr<CArtifact> > &out, std::vector<CArtifact*> *arts, CArtifact::EartClass flag)
@ -683,7 +693,7 @@ bool CArtHandler::legalArtifact(const ArtifactID & id)
auto art = objects[id]; auto art = objects[id];
//assert ( (!art->constituents) || art->constituents->size() ); //artifacts is not combined or has some components //assert ( (!art->constituents) || art->constituents->size() ); //artifacts is not combined or has some components
if(art->constituents) if(art->isCombined())
return false; //no combo artifacts spawning return false; //no combo artifacts spawning
if(art->aClass < CArtifact::ART_TREASURE || art->aClass > CArtifact::ART_RELIC) if(art->aClass < CArtifact::ART_TREASURE || art->aClass > CArtifact::ART_RELIC)
@ -787,174 +797,6 @@ void CArtHandler::afterLoadFinalization()
CBonusSystemNode::treeHasChanged(); CBonusSystemNode::treeHasChanged();
} }
CArtifactInstance::CArtifactInstance()
{
init();
}
CArtifactInstance::CArtifactInstance( CArtifact *Art)
{
init();
setType(Art);
}
void CArtifactInstance::setType( CArtifact *Art )
{
artType = Art;
attachTo(*Art);
}
std::string CArtifactInstance::nodeName() const
{
return "Artifact instance of " + (artType ? artType->getJsonKey() : std::string("uninitialized")) + " type";
}
void CArtifactInstance::init()
{
id = ArtifactInstanceID();
id = static_cast<ArtifactInstanceID>(ArtifactID::NONE); //to be randomized
setNodeType(ARTIFACT_INSTANCE);
}
std::string CArtifactInstance::getDescription() const
{
std::string text = artType->getDescriptionTranslated();
if(artType->getId() == 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
SpellID spellID = getScrollSpellID();
size_t nameStart = text.find_first_of('[');
size_t nameEnd = text.find_first_of(']', nameStart);
if(spellID.getNum() >= 0)
{
if(nameStart != std::string::npos && nameEnd != std::string::npos)
text = text.replace(nameStart, nameEnd - nameStart + 1, spellID.toSpell(VLC->spells())->getNameTranslated());
}
}
return text;
}
ArtifactID CArtifactInstance::getTypeId() const
{
return artType->getId();
}
bool CArtifactInstance::canBePutAt(const ArtifactLocation & al, bool assumeDestRemoved) const
{
return artType->canBePutAt(al.getHolderArtSet(), al.slot, assumeDestRemoved);
}
void CArtifactInstance::putAt(const ArtifactLocation & al)
{
al.getHolderArtSet()->putArtifact(al.slot, this);
}
void CArtifactInstance::removeFrom(const ArtifactLocation & al)
{
al.getHolderArtSet()->removeArtifact(al.slot);
}
bool CArtifactInstance::canBeDisassembled() const
{
return artType->canBeDisassembled();
}
void CArtifactInstance::move(const ArtifactLocation & src, const ArtifactLocation & dst)
{
removeFrom(src);
putAt(dst);
}
void CArtifactInstance::deserializationFix()
{
setType(artType);
}
SpellID CArtifactInstance::getScrollSpellID() const
{
const auto b = getBonusLocalFirst(Selector::type()(BonusType::SPELL));
if(!b)
{
logMod->warn("Warning: %s doesn't bear any spell!", nodeName());
return SpellID::NONE;
}
return SpellID(b->subtype);
}
bool CArtifactInstance::isPart(const CArtifactInstance *supposedPart) const
{
return supposedPart == this;
}
CCombinedArtifactInstance::CCombinedArtifactInstance(CArtifact *Art)
: CArtifactInstance(Art) //TODO: seems unused, but need to be written
{
}
void CCombinedArtifactInstance::createConstituents()
{
assert(artType);
assert(artType->constituents);
for(const CArtifact * art : *artType->constituents)
{
addAsConstituent(ArtifactUtils::createNewArtifactInstance(art->getId()), ArtifactPosition::PRE_FIRST);
}
}
void CCombinedArtifactInstance::addAsConstituent(CArtifactInstance * art, const ArtifactPosition & slot)
{
assert(vstd::contains_if(*artType->constituents, [=](const CArtifact * constituent){
return constituent->getId() == art->artType->getId();
}));
assert(art->getParentNodes().size() == 1 && art->getParentNodes().front() == art->artType);
constituentsInfo.emplace_back(art, slot);
attachTo(*art);
}
void CCombinedArtifactInstance::removeFrom(const ArtifactLocation & al)
{
CArtifactInstance::removeFrom(al);
for(auto & part : constituentsInfo)
{
if(part.slot != ArtifactPosition::PRE_FIRST)
part.slot = ArtifactPosition::PRE_FIRST;
}
}
void CCombinedArtifactInstance::deserializationFix()
{
for(ConstituentInfo &ci : constituentsInfo)
attachTo(*ci.art);
}
bool CCombinedArtifactInstance::isPart(const CArtifactInstance *supposedPart) const
{
bool me = CArtifactInstance::isPart(supposedPart);
if(me)
return true;
//check for constituents
for(const ConstituentInfo &constituent : constituentsInfo)
if(constituent.art == supposedPart)
return true;
return false;
}
CCombinedArtifactInstance::ConstituentInfo::ConstituentInfo(CArtifactInstance * Art, const ArtifactPosition & Slot):
art(Art),
slot(Slot)
{
}
bool CCombinedArtifactInstance::ConstituentInfo::operator==(const ConstituentInfo &rhs) const
{
return art == rhs.art && slot == rhs.slot;
}
CArtifactSet::~CArtifactSet() = default; CArtifactSet::~CArtifactSet() = default;
const CArtifactInstance * CArtifactSet::getArt(const ArtifactPosition & pos, bool excludeLocked) const const CArtifactInstance * CArtifactSet::getArt(const ArtifactPosition & pos, bool excludeLocked) const
@ -1033,11 +875,11 @@ ArtifactPosition CArtifactSet::getArtPos(const CArtifactInstance *art) const
const CArtifactInstance * CArtifactSet::getArtByInstanceId(const ArtifactInstanceID & artInstId) const const CArtifactInstance * CArtifactSet::getArtByInstanceId(const ArtifactInstanceID & artInstId) const
{ {
for(auto i : artifactsWorn) for(auto i : artifactsWorn)
if(i.second.artifact->id == artInstId) if(i.second.artifact->getId() == artInstId)
return i.second.artifact; return i.second.artifact;
for(auto i : artifactsInBackpack) for(auto i : artifactsInBackpack)
if(i.artifact->id == artInstId) if(i.artifact->getId() == artInstId)
return i.artifact; return i.artifact;
return nullptr; return nullptr;
@ -1047,7 +889,7 @@ const ArtifactPosition CArtifactSet::getSlotByInstance(const CArtifactInstance *
{ {
if(artInst) if(artInst)
{ {
for(auto & slot : artInst->artType->possibleSlots.at(bearerType())) for(const auto & slot : artInst->artType->getPossibleSlots().at(bearerType()))
if(getArt(slot) == artInst) if(getArt(slot) == artInst)
return slot; return slot;
@ -1087,19 +929,18 @@ unsigned CArtifactSet::getArtPosCount(const ArtifactID & aid, bool onlyWorn, boo
void CArtifactSet::putArtifact(ArtifactPosition slot, CArtifactInstance * art) void CArtifactSet::putArtifact(ArtifactPosition slot, CArtifactInstance * art)
{ {
setNewArtSlot(slot, art, false); setNewArtSlot(slot, art, false);
if(art->artType->canBeDisassembled() && ArtifactUtils::isSlotEquipment(slot)) if(art->artType->isCombined() && ArtifactUtils::isSlotEquipment(slot))
{ {
const CArtifactInstance * mainPart = nullptr; const CArtifactInstance * mainPart = nullptr;
auto & parts = dynamic_cast<CCombinedArtifactInstance*>(art)->constituentsInfo; for(const auto & part : art->getPartsInfo())
for(const auto & part : parts) if(vstd::contains(part.art->artType->getPossibleSlots().at(bearerType()), slot)
if(vstd::contains(part.art->artType->possibleSlots.at(bearerType()), slot)
&& (part.slot == ArtifactPosition::PRE_FIRST)) && (part.slot == ArtifactPosition::PRE_FIRST))
{ {
mainPart = part.art; mainPart = part.art;
break; break;
} }
for(auto & part : parts) for(auto & part : art->getPartsInfo())
{ {
if(part.art != mainPart) if(part.art != mainPart)
{ {
@ -1118,10 +959,9 @@ void CArtifactSet::removeArtifact(ArtifactPosition slot)
auto art = getArt(slot, false); auto art = getArt(slot, false);
if(art) if(art)
{ {
if(art->canBeDisassembled()) if(art->isCombined())
{ {
auto combinedArt = dynamic_cast<CCombinedArtifactInstance*>(art); for(auto & part : art->getPartsInfo())
for(auto & part : combinedArt->constituentsInfo)
{ {
if(getArt(part.slot, false)) if(getArt(part.slot, false))
eraseArtSlot(part.slot); eraseArtSlot(part.slot);
@ -1131,19 +971,18 @@ void CArtifactSet::removeArtifact(ArtifactPosition slot)
} }
} }
std::pair<const CCombinedArtifactInstance *, const CArtifactInstance *> CArtifactSet::searchForConstituent(const ArtifactID & aid) const std::pair<const CArtifactInstance *, const CArtifactInstance *> CArtifactSet::searchForConstituent(const ArtifactID & aid) const
{ {
for(const auto & slot : artifactsInBackpack) for(const auto & slot : artifactsInBackpack)
{ {
auto art = slot.artifact; auto art = slot.artifact;
if(art->canBeDisassembled()) if(art->isCombined())
{ {
auto * ass = dynamic_cast<CCombinedArtifactInstance *>(art.get()); for(auto & ci : art->getPartsInfo())
for(auto& ci : ass->constituentsInfo)
{ {
if(ci.art->getTypeId() == aid) if(ci.art->getTypeId() == aid)
{ {
return {ass, ci.art}; return {art, ci.art};
} }
} }
} }
@ -1156,7 +995,7 @@ const CArtifactInstance * CArtifactSet::getHiddenArt(const ArtifactID & aid) con
return searchForConstituent(aid).second; return searchForConstituent(aid).second;
} }
const CCombinedArtifactInstance * CArtifactSet::getAssemblyByConstituent(const ArtifactID & aid) const const CArtifactInstance * CArtifactSet::getAssemblyByConstituent(const ArtifactID & aid) const
{ {
return searchForConstituent(aid).first; return searchForConstituent(aid).first;
} }

View File

@ -20,9 +20,7 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class CArtHandler; class CArtHandler;
class CArtifact;
class CGHeroInstance; class CGHeroInstance;
struct ArtifactLocation;
class CArtifactSet; class CArtifactSet;
class CArtifactInstance; class CArtifactInstance;
class CRandomGenerator; class CRandomGenerator;
@ -44,26 +42,75 @@ namespace ArtBearer
}; };
} }
class DLL_LINKAGE CArtifact : public Artifact, public CBonusSystemNode //container for artifacts class DLL_LINKAGE CCombinedArtifact
{
protected:
CCombinedArtifact() = default;
std::vector<CArtifact*> constituents; // Artifacts IDs a combined artifact consists of, or nullptr.
std::vector<CArtifact*> partOf; // Reverse map of constituents - combined arts that include this art
public:
bool isCombined() const;
const std::vector<CArtifact*> & getConstituents() const;
const std::vector<CArtifact*> & getPartOf() const;
template <typename Handler> void serialize(Handler & h, const int version)
{
h & constituents;
h & partOf;
}
};
class DLL_LINKAGE CScrollArtifact
{
protected:
CScrollArtifact() = default;
public:
bool isScroll() const;
};
class DLL_LINKAGE CGrowingArtifact
{
protected:
CGrowingArtifact() = default;
std::vector <std::pair<ui16, Bonus>> bonusesPerLevel; // Bonus given each n levels
std::vector <std::pair<ui16, Bonus>> thresholdBonuses; // After certain level they will be added once
public:
bool isGrowing() const;
std::vector <std::pair<ui16, Bonus>> & getBonusesPerLevel();
const std::vector <std::pair<ui16, Bonus>> & getBonusesPerLevel() const;
std::vector <std::pair<ui16, Bonus>> & getThresholdBonuses();
const std::vector <std::pair<ui16, Bonus>> & getThresholdBonuses() const;
template <typename Handler> void serialize(Handler & h, const int version)
{
h & bonusesPerLevel;
h & thresholdBonuses;
}
};
// Container for artifacts. Not for instances.
class DLL_LINKAGE CArtifact
: public Artifact, public CBonusSystemNode, public CCombinedArtifact, public CScrollArtifact, public CGrowingArtifact
{ {
ArtifactID id; ArtifactID id;
std::string image;
std::string large; // big image for custom artifacts, used in drag & drop
std::string advMapDef; // used for adventure map object
std::string modScope; std::string modScope;
std::string identifier; std::string identifier;
int32_t iconIndex;
uint32_t price;
CreatureID warMachine;
// Bearer Type => ids of slots where artifact can be placed
std::map<ArtBearer::ArtBearer, std::vector<ArtifactPosition>> possibleSlots;
public: public:
enum EartClass {ART_SPECIAL=1, ART_TREASURE=2, ART_MINOR=4, ART_MAJOR=8, ART_RELIC=16}; //artifact classes enum EartClass {ART_SPECIAL=1, ART_TREASURE=2, ART_MINOR=4, ART_MAJOR=8, ART_RELIC=16}; //artifact classes
std::string image;
std::string large; // big image for custom artifacts, used in drag & drop
std::string advMapDef; //used for adventure map object
si32 iconIndex = ArtifactID::NONE;
ui32 price = 0;
std::map<ArtBearer::ArtBearer, std::vector<ArtifactPosition> > possibleSlots; //Bearer Type => ids of slots where artifact can be placed
std::unique_ptr<std::vector<CArtifact *> > constituents; // Artifacts IDs a combined artifact consists of, or nullptr.
std::vector<CArtifact *> constituentOf; // Reverse map of constituents - combined arts that include this art
EartClass aClass = ART_SPECIAL; EartClass aClass = ART_SPECIAL;
CreatureID warMachine;
int32_t getIndex() const override; int32_t getIndex() const override;
int32_t getIconIndex() const override; int32_t getIconIndex() const override;
@ -88,26 +135,25 @@ public:
int getArtClassSerial() const; //0 - treasure, 1 - minor, 2 - major, 3 - relic, 4 - spell scroll, 5 - other int getArtClassSerial() const; //0 - treasure, 1 - minor, 2 - major, 3 - relic, 4 - spell scroll, 5 - other
std::string nodeName() const override; std::string nodeName() const override;
void addNewBonus(const std::shared_ptr<Bonus>& b) override; void addNewBonus(const std::shared_ptr<Bonus>& b) override;
const std::map<ArtBearer::ArtBearer, std::vector<ArtifactPosition>> & getPossibleSlots() const;
virtual void levelUpArtifact (CArtifactInstance * art){};
virtual bool canBeDisassembled() const;
virtual bool canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot = ArtifactPosition::FIRST_AVAILABLE, virtual bool canBePutAt(const CArtifactSet * artSet, ArtifactPosition slot = ArtifactPosition::FIRST_AVAILABLE,
bool assumeDestRemoved = false) const; bool assumeDestRemoved = false) const;
void updateFrom(const JsonNode & data); void updateFrom(const JsonNode & data);
void serializeJson(JsonSerializeFormat & handler); // Is used for testing purposes only
void setImage(int32_t iconIndex, std::string image, std::string large);
template <typename Handler> void serialize(Handler & h, const int version) template <typename Handler> void serialize(Handler & h, const int version)
{ {
h & static_cast<CBonusSystemNode&>(*this); h & static_cast<CBonusSystemNode&>(*this);
h & static_cast<CCombinedArtifact&>(*this);
h & static_cast<CGrowingArtifact&>(*this);
h & image; h & image;
h & large; h & large;
h & advMapDef; h & advMapDef;
h & iconIndex; h & iconIndex;
h & price; h & price;
h & possibleSlots; h & possibleSlots;
h & constituents;
h & constituentOf;
h & aClass; h & aClass;
h & id; h & id;
h & modScope; h & modScope;
@ -121,99 +167,6 @@ public:
friend class CArtHandler; friend class CArtHandler;
}; };
class DLL_LINKAGE CGrowingArtifact : public CArtifact //for example commander artifacts getting bonuses after battle
{
public:
std::vector <std::pair <ui16, Bonus> > bonusesPerLevel; //bonus given each n levels
std::vector <std::pair <ui16, Bonus> > thresholdBonuses; //after certain level they will be added once
void levelUpArtifact(CArtifactInstance * art) override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CArtifact&>(*this);
h & bonusesPerLevel;
h & thresholdBonuses;
}
};
class DLL_LINKAGE CArtifactInstance : public CBonusSystemNode
{
protected:
void init();
public:
CArtifactInstance(CArtifact * Art);
CArtifactInstance();
ConstTransitivePtr<CArtifact> artType;
ArtifactInstanceID id;
std::string nodeName() const override;
void deserializationFix();
void setType(CArtifact *Art);
std::string getDescription() const;
SpellID getScrollSpellID() const; //to be used with scrolls (and similar arts), -1 if none
ArtifactID getTypeId() const;
bool canBePutAt(const ArtifactLocation & al, bool assumeDestRemoved = false) const; //forwards to the above one
virtual bool canBeDisassembled() const;
/// Checks if this a part of this artifact: artifact instance is a part
/// of itself, additionally truth is returned for constituents of combined arts
virtual bool isPart(const CArtifactInstance *supposedPart) const;
virtual void putAt(const ArtifactLocation & al);
virtual void removeFrom(const ArtifactLocation & al);
virtual void move(const ArtifactLocation & src, const ArtifactLocation & dst);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CBonusSystemNode&>(*this);
h & artType;
h & id;
BONUS_TREE_DESERIALIZATION_FIX
}
};
class DLL_LINKAGE CCombinedArtifactInstance : public CArtifactInstance
{
public:
CCombinedArtifactInstance(CArtifact * Art);
struct ConstituentInfo
{
ConstTransitivePtr<CArtifactInstance> art;
ArtifactPosition slot;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & art;
h & slot;
}
bool operator==(const ConstituentInfo &rhs) const;
ConstituentInfo(CArtifactInstance * art = nullptr, const ArtifactPosition & slot = ArtifactPosition::PRE_FIRST);
};
std::vector<ConstituentInfo> constituentsInfo;
bool isPart(const CArtifactInstance *supposedPart) const override;
void createConstituents();
void addAsConstituent(CArtifactInstance * art, const ArtifactPosition & slot);
void removeFrom(const ArtifactLocation & al) override;
CCombinedArtifactInstance() = default;
void deserializationFix();
friend class CArtifactInstance;
friend struct AssembledArtifact;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & static_cast<CArtifactInstance&>(*this);
h & constituentsInfo;
BONUS_TREE_DESERIALIZATION_FIX
}
};
class DLL_LINKAGE CArtHandler : public CHandlerBase<ArtifactID, Artifact, CArtifact, ArtifactService> class DLL_LINKAGE CArtHandler : public CHandlerBase<ArtifactID, Artifact, CArtifact, ArtifactService>
{ {
public: public:
@ -269,7 +222,6 @@ private:
void loadClass(CArtifact * art, const JsonNode & node) const; void loadClass(CArtifact * art, const JsonNode & node) const;
void loadType(CArtifact * art, const JsonNode & node) const; void loadType(CArtifact * art, const JsonNode & node) const;
void loadComponents(CArtifact * art, const JsonNode & node); void loadComponents(CArtifact * art, const JsonNode & node);
void loadGrowingArt(CGrowingArtifact * art, const JsonNode & node) const;
void erasePickedArt(const ArtifactID & id); void erasePickedArt(const ArtifactID & id);
}; };
@ -313,7 +265,7 @@ public:
const ArtifactPosition getSlotByInstance(const CArtifactInstance * artInst) const; const ArtifactPosition getSlotByInstance(const CArtifactInstance * artInst) const;
/// Search for constituents of assemblies in backpack which do not have an ArtifactPosition /// Search for constituents of assemblies in backpack which do not have an ArtifactPosition
const CArtifactInstance * getHiddenArt(const ArtifactID & aid) const; const CArtifactInstance * getHiddenArt(const ArtifactID & aid) const;
const CCombinedArtifactInstance * getAssemblyByConstituent(const ArtifactID & aid) const; const CArtifactInstance * getAssemblyByConstituent(const ArtifactID & aid) const;
/// Checks if hero possess artifact of given id (either in backack or worn) /// Checks if hero possess artifact of given id (either in backack or worn)
bool hasArt(const ArtifactID & aid, bool onlyWorn = false, bool searchBackpackAssemblies = false, bool allowLocked = true) const; bool hasArt(const ArtifactID & aid, bool onlyWorn = false, bool searchBackpackAssemblies = false, bool allowLocked = true) const;
bool hasArtBackpack(const ArtifactID & aid) const; bool hasArtBackpack(const ArtifactID & aid) const;
@ -335,7 +287,7 @@ public:
void serializeJsonArtifacts(JsonSerializeFormat & handler, const std::string & fieldName, CMap * map); void serializeJsonArtifacts(JsonSerializeFormat & handler, const std::string & fieldName, CMap * map);
protected: protected:
std::pair<const CCombinedArtifactInstance *, const CArtifactInstance *> searchForConstituent(const ArtifactID & aid) const; std::pair<const CArtifactInstance *, const CArtifactInstance *> searchForConstituent(const ArtifactID & aid) const;
private: private:
void serializeJsonHero(JsonSerializeFormat & handler, CMap * map); void serializeJsonHero(JsonSerializeFormat & handler, CMap * map);

192
lib/CArtifactInstance.cpp Normal file
View File

@ -0,0 +1,192 @@
/*
* CArtifactInstance.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CArtifactInstance.h"
#include "ArtifactUtils.h"
#include "CArtHandler.h"
#include "NetPacksBase.h"
VCMI_LIB_NAMESPACE_BEGIN
void CCombinedArtifactInstance::addPart(CArtifactInstance * art, const ArtifactPosition & slot)
{
auto artInst = static_cast<CArtifactInstance*>(this);
assert(vstd::contains_if(*artInst->artType->constituents,
[=](const CArtifact * partType)
{
return partType->getId() == art->getTypeId();
}));
assert(art->getParentNodes().size() == 1 && art->getParentNodes().front() == art->artType);
partsInfo.emplace_back(art, slot);
artInst->attachTo(*art);
}
bool CCombinedArtifactInstance::isPart(const CArtifactInstance * supposedPart) const
{
if(supposedPart == this)
return true;
for(const PartInfo & constituent : partsInfo)
{
if(constituent.art == supposedPart)
return true;
}
return false;
}
std::vector<CCombinedArtifactInstance::PartInfo> & CCombinedArtifactInstance::getPartsInfo()
{
// TODO romove this func. encapsulation violation
return partsInfo;
}
const std::vector<CCombinedArtifactInstance::PartInfo> & CCombinedArtifactInstance::getPartsInfo() const
{
return partsInfo;
}
SpellID CScrollArtifactInstance::getScrollSpellID() const
{
auto artInst = static_cast<const CArtifactInstance*>(this);
const auto bonus = artInst->getBonusLocalFirst(Selector::type()(BonusType::SPELL));
if(!bonus)
{
logMod->warn("Warning: %s doesn't bear any spell!", artInst->nodeName());
return SpellID::NONE;
}
return SpellID(bonus->subtype);
}
void CGrowingArtifactInstance::growingUp()
{
auto artInst = static_cast<CArtifactInstance*>(this);
if(artInst->artType->isGrowing())
{
auto bonus = std::make_shared<Bonus>();
bonus->type = BonusType::LEVEL_COUNTER;
bonus->val = 1;
bonus->duration = BonusDuration::COMMANDER_KILLED;
artInst->accumulateBonus(bonus);
for(const auto & bonus : artInst->artType->getBonusesPerLevel())
{
// Every n levels
if(artInst->valOfBonuses(BonusType::LEVEL_COUNTER) % bonus.first == 0)
{
artInst->accumulateBonus(std::make_shared<Bonus>(bonus.second));
}
}
for(const auto & bonus : artInst->artType->getThresholdBonuses())
{
// At n level
if(artInst->valOfBonuses(BonusType::LEVEL_COUNTER) == bonus.first)
{
artInst->addNewBonus(std::make_shared<Bonus>(bonus.second));
}
}
}
}
void CArtifactInstance::init()
{
// Artifact to be randomized
id = static_cast<ArtifactInstanceID>(ArtifactID::NONE);
setNodeType(ARTIFACT_INSTANCE);
}
CArtifactInstance::CArtifactInstance(CArtifact * art)
{
init();
setType(art);
}
CArtifactInstance::CArtifactInstance()
{
init();
}
void CArtifactInstance::setType(CArtifact * art)
{
artType = art;
attachTo(*art);
}
std::string CArtifactInstance::nodeName() const
{
return "Artifact instance of " + (artType ? artType->getJsonKey() : std::string("uninitialized")) + " type";
}
std::string CArtifactInstance::getDescription() const
{
std::string text = artType->getDescriptionTranslated();
if(artType->isScroll())
ArtifactUtils::insertScrrollSpellName(text, getScrollSpellID());
return text;
}
ArtifactID CArtifactInstance::getTypeId() const
{
return artType->getId();
}
ArtifactInstanceID CArtifactInstance::getId() const
{
return id;
}
void CArtifactInstance::setId(ArtifactInstanceID id)
{
this->id = id;
}
bool CArtifactInstance::canBePutAt(const ArtifactLocation & al, bool assumeDestRemoved) const
{
return artType->canBePutAt(al.getHolderArtSet(), al.slot, assumeDestRemoved);
}
bool CArtifactInstance::isCombined() const
{
return artType->isCombined();
}
void CArtifactInstance::putAt(const ArtifactLocation & al)
{
al.getHolderArtSet()->putArtifact(al.slot, this);
}
void CArtifactInstance::removeFrom(const ArtifactLocation & al)
{
al.getHolderArtSet()->removeArtifact(al.slot);
for(auto & part : partsInfo)
{
if(part.slot != ArtifactPosition::PRE_FIRST)
part.slot = ArtifactPosition::PRE_FIRST;
}
}
void CArtifactInstance::move(const ArtifactLocation & src, const ArtifactLocation & dst)
{
removeFrom(src);
putAt(dst);
}
void CArtifactInstance::deserializationFix()
{
setType(artType);
for(PartInfo & part : partsInfo)
attachTo(*part.art);
}
VCMI_LIB_NAMESPACE_END

102
lib/CArtifactInstance.h Normal file
View File

@ -0,0 +1,102 @@
/*
* CArtifactInstance.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
*
*/
#pragma once
#include "bonuses/CBonusSystemNode.h"
#include "GameConstants.h"
VCMI_LIB_NAMESPACE_BEGIN
struct ArtifactLocation;
class DLL_LINKAGE CCombinedArtifactInstance
{
protected:
CCombinedArtifactInstance() = default;
public:
struct PartInfo
{
ConstTransitivePtr<CArtifactInstance> art;
ArtifactPosition slot;
template <typename Handler> void serialize(Handler & h, const int version)
{
h & art;
h & slot;
}
PartInfo(CArtifactInstance * art = nullptr, const ArtifactPosition & slot = ArtifactPosition::PRE_FIRST)
: art(art), slot(slot) {};
};
void addPart(CArtifactInstance * art, const ArtifactPosition & slot);
// Checks if supposed part inst is part of this combined art inst
bool isPart(const CArtifactInstance * supposedPart) const;
std::vector<PartInfo> & getPartsInfo();
const std::vector<PartInfo> & getPartsInfo() const;
template <typename Handler> void serialize(Handler & h, const int version)
{
h & partsInfo;
}
protected:
std::vector<PartInfo> partsInfo;
};
class DLL_LINKAGE CScrollArtifactInstance
{
protected:
CScrollArtifactInstance() = default;
public:
SpellID getScrollSpellID() const;
};
class DLL_LINKAGE CGrowingArtifactInstance
{
protected:
CGrowingArtifactInstance() = default;
public:
void growingUp();
};
class DLL_LINKAGE CArtifactInstance
: public CBonusSystemNode, public CCombinedArtifactInstance, public CScrollArtifactInstance, public CGrowingArtifactInstance
{
protected:
void init();
ArtifactInstanceID id;
public:
ConstTransitivePtr<CArtifact> artType;
CArtifactInstance(CArtifact * art);
CArtifactInstance();
void setType(CArtifact * art);
std::string nodeName() const override;
std::string getDescription() const;
ArtifactID getTypeId() const;
ArtifactInstanceID getId() const;
void setId(ArtifactInstanceID id);
bool canBePutAt(const ArtifactLocation & al, bool assumeDestRemoved = false) const;
bool isCombined() const;
void putAt(const ArtifactLocation & al);
void removeFrom(const ArtifactLocation & al);
void move(const ArtifactLocation & src, const ArtifactLocation & dst);
void deserializationFix();
template <typename Handler> void serialize(Handler & h, const int version)
{
h & static_cast<CBonusSystemNode&>(*this);
h & static_cast<CCombinedArtifactInstance&>(*this);
h & artType;
h & id;
BONUS_TREE_DESERIALIZATION_FIX
}
};
VCMI_LIB_NAMESPACE_END

View File

@ -13,6 +13,7 @@
#include "bonuses/CBonusSystemNode.h" #include "bonuses/CBonusSystemNode.h"
#include "GameConstants.h" #include "GameConstants.h"
#include "CArtHandler.h" #include "CArtHandler.h"
#include "CArtifactInstance.h"
#include "CCreatureHandler.h" #include "CCreatureHandler.h"
#include <vcmi/Entity.h> #include <vcmi/Entity.h>

View File

@ -206,7 +206,7 @@ namespace JsonRandom
{ {
CArtifact * art = VLC->arth->objects[artID]; CArtifact * art = VLC->arth->objects[artID];
if(!vstd::iswithin(art->price, minValue, maxValue)) if(!vstd::iswithin(art->getPrice(), minValue, maxValue))
return false; return false;
if(!allowedClasses.empty() && !allowedClasses.count(art->aClass)) if(!allowedClasses.empty() && !allowedClasses.count(art->aClass))
@ -217,7 +217,7 @@ namespace JsonRandom
if(!allowedPositions.empty()) if(!allowedPositions.empty())
{ {
for(const auto & pos : art->possibleSlots[ArtBearer::HERO]) for(const auto & pos : art->getPossibleSlots().at(ArtBearer::HERO))
{ {
if(allowedPositions.count(pos)) if(allowedPositions.count(pos))
return true; return true;

View File

@ -1492,21 +1492,31 @@ struct DLL_LINKAGE BattleResultAccepted : public CPackForClient
{ {
void applyGs(CGameState * gs) const; void applyGs(CGameState * gs) const;
CGHeroInstance * hero1 = nullptr; struct HeroBattleResults
CGHeroInstance * hero2 = nullptr; {
CArmedInstance * army1 = nullptr; HeroBattleResults()
CArmedInstance * army2 = nullptr; : hero(nullptr), army(nullptr), exp(0) {}
TExpType exp[2];
CGHeroInstance * hero;
CArmedInstance * army;
TExpType exp;
template <typename Handler> void serialize(Handler & h, const int version) template <typename Handler> void serialize(Handler & h, const int version)
{ {
h & hero1; h & hero;
h & hero2; h & army;
h & army1;
h & army2;
h & exp; h & exp;
} }
}; };
std::array<HeroBattleResults, 2> heroResult;
ui8 winnerSide;
template <typename Handler> void serialize(Handler & h, const int version)
{
h & heroResult;
h & winnerSide;
}
};
struct DLL_LINKAGE BattleResult : public Query struct DLL_LINKAGE BattleResult : public Query
{ {

View File

@ -1524,12 +1524,17 @@ void NewObject::applyGs(CGameState *gs)
void NewArtifact::applyGs(CGameState *gs) void NewArtifact::applyGs(CGameState *gs)
{ {
assert(!vstd::contains(gs->map->artInstances, art)); assert(!vstd::contains(gs->map->artInstances, art));
gs->map->addNewArtifactInstance(art);
assert(!art->getParentNodes().size()); assert(!art->getParentNodes().size());
assert(art->artType);
art->setType(art->artType); art->setType(art->artType);
if(auto * cart = dynamic_cast<CCombinedArtifactInstance *>(art.get())) if(art->isCombined())
cart->createConstituents(); {
assert(art->artType->getConstituents());
for(const auto & part : art->artType->getConstituents())
art->addPart(ArtifactUtils::createNewArtifactInstance(part), ArtifactPosition::PRE_FIRST);
}
gs->map->addNewArtifactInstance(art);
} }
const CStackInstance * StackLocation::getStack() const CStackInstance * StackLocation::getStack()
@ -1822,7 +1827,7 @@ void EraseArtifact::applyGs(CGameState *gs)
for(auto& p : aset->artifactsWorn) for(auto& p : aset->artifactsWorn)
{ {
auto art = p.second.artifact; auto art = p.second.artifact;
if(art->canBeDisassembled() && art->isPart(slot->artifact)) if(art->isCombined() && art->isPart(slot->artifact))
{ {
dis.al.slot = aset->getArtPos(art); dis.al.slot = aset->getArtPos(art);
#ifndef NDEBUG #ifndef NDEBUG
@ -1930,10 +1935,10 @@ void AssembledArtifact::applyGs(CGameState *gs)
return art->getId() == builtArt->getId(); return art->getId() == builtArt->getId();
})); }));
auto * combinedArt = new CCombinedArtifactInstance(builtArt); auto * combinedArt = new CArtifactInstance(builtArt);
gs->map->addNewArtifactInstance(combinedArt); gs->map->addNewArtifactInstance(combinedArt);
// Retrieve all constituents // Retrieve all constituents
for(const CArtifact * constituent : *builtArt->constituents) for(const CArtifact * constituent : builtArt->getConstituents())
{ {
ArtifactPosition pos = combineEquipped ? artSet->getArtPos(constituent->getId(), true, false) : ArtifactPosition pos = combineEquipped ? artSet->getArtPos(constituent->getId(), true, false) :
artSet->getArtBackpackPos(constituent->getId()); artSet->getArtBackpackPos(constituent->getId());
@ -1944,8 +1949,8 @@ void AssembledArtifact::applyGs(CGameState *gs)
constituentInstance->removeFrom(ArtifactLocation(al.artHolder, pos)); constituentInstance->removeFrom(ArtifactLocation(al.artHolder, pos));
if(combineEquipped) if(combineEquipped)
{ {
if(!vstd::contains(combinedArt->artType->possibleSlots[artSet->bearerType()], al.slot) if(!vstd::contains(combinedArt->artType->getPossibleSlots().at(artSet->bearerType()), al.slot)
&& vstd::contains(combinedArt->artType->possibleSlots[artSet->bearerType()], pos)) && vstd::contains(combinedArt->artType->getPossibleSlots().at(artSet->bearerType()), pos))
al.slot = pos; al.slot = pos;
if(al.slot == pos) if(al.slot == pos)
pos = ArtifactPosition::PRE_FIRST; pos = ArtifactPosition::PRE_FIRST;
@ -1955,7 +1960,7 @@ void AssembledArtifact::applyGs(CGameState *gs)
al.slot = std::min(al.slot, pos); al.slot = std::min(al.slot, pos);
pos = ArtifactPosition::PRE_FIRST; pos = ArtifactPosition::PRE_FIRST;
} }
combinedArt->addAsConstituent(constituentInstance, pos); combinedArt->addPart(constituentInstance, pos);
} }
//put new combined artifacts //put new combined artifacts
@ -1964,19 +1969,19 @@ void AssembledArtifact::applyGs(CGameState *gs)
void DisassembledArtifact::applyGs(CGameState *gs) void DisassembledArtifact::applyGs(CGameState *gs)
{ {
auto * disassembled = dynamic_cast<CCombinedArtifactInstance *>(al.getArt()); auto * disassembled = al.getArt();
assert(disassembled); assert(disassembled);
std::vector<CCombinedArtifactInstance::ConstituentInfo> constituents = disassembled->constituentsInfo; auto parts = disassembled->getPartsInfo();
disassembled->removeFrom(al); disassembled->removeFrom(al);
for(CCombinedArtifactInstance::ConstituentInfo &ci : constituents) for(auto & part : parts)
{ {
ArtifactLocation constituentLoc = al; ArtifactLocation partLoc = al;
constituentLoc.slot = (ci.slot >= 0 ? ci.slot : al.slot); //-1 is slot of main constituent -> it'll replace combined artifact in its pos // ArtifactPosition::PRE_FIRST is value of main part slot -> it'll replace combined artifact in its pos
disassembled->detachFrom(*ci.art); partLoc.slot = (ArtifactUtils::isSlotEquipment(part.slot) ? part.slot : al.slot);
ci.art->putAt(constituentLoc); disassembled->detachFrom(*part.art);
part.art->putAt(partLoc);
} }
gs->map->eraseArtifactInstance(disassembled); gs->map->eraseArtifactInstance(disassembled);
} }
@ -2209,32 +2214,32 @@ void BattleUpdateGateState::applyGs(CGameState * gs) const
void BattleResultAccepted::applyGs(CGameState * gs) const void BattleResultAccepted::applyGs(CGameState * gs) const
{ {
for(auto * h : {hero1, hero2}) // Remove any "until next battle" bonuses
for(auto & res : heroResult)
{ {
if(h) if(res.hero)
{ res.hero->removeBonusesRecursive(Bonus::OneBattle);
h->removeBonusesRecursive(Bonus::OneBattle); //remove any "until next battle" bonuses
if (h->commander && h->commander->alive)
{
for (auto art : h->commander->artifactsWorn) //increment bonuses for commander artifacts
{
art.second.artifact->artType->levelUpArtifact (art.second.artifact);
}
}
}
} }
// Grow up growing artifacts
if(const auto hero = heroResult[winnerSide].hero)
{
if(hero->commander && hero->commander->alive)
{
for(auto & art : hero->commander->artifactsWorn)
art.second.artifact->growingUp();
}
for(auto & art : hero->artifactsWorn)
{
art.second.artifact->growingUp();
}
}
if(VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE)) if(VLC->settings()->getBoolean(EGameSettings::MODULE_STACK_EXPERIENCE))
{ {
for(int i = 0; i < 2; i++) if(heroResult[0].army)
{ heroResult[0].army->giveStackExp(heroResult[0].exp);
if(exp[i]) if(heroResult[1].army)
{ heroResult[1].army->giveStackExp(heroResult[1].exp);
if(auto * army = (i == 0 ? army1 : army2))
army->giveStackExp(exp[i]);
}
}
CBonusSystemNode::treeHasChanged(); CBonusSystemNode::treeHasChanged();
} }

View File

@ -378,7 +378,7 @@ BattleInfo * BattleInfo::setupBattle(const int3 & tile, TerrainId terrain, const
if(nullptr != warMachineArt) if(nullptr != warMachineArt)
{ {
CreatureID cre = warMachineArt->artType->warMachine; CreatureID cre = warMachineArt->artType->getWarMachine();
if(cre != CreatureID::NONE) if(cre != CreatureID::NONE)
curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(cre, 1), side, SlotID::WAR_MACHINES_SLOT, hex); curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(cre, 1), side, SlotID::WAR_MACHINES_SLOT, hex);

View File

@ -406,10 +406,10 @@ void CGHeroInstance::initArmy(CRandomGenerator & rand, IArmyDescriptor * dst)
ArtifactID aid = creature->warMachine; ArtifactID aid = creature->warMachine;
const CArtifact * art = aid.toArtifact(); const CArtifact * art = aid.toArtifact();
if(art != nullptr && !art->possibleSlots.at(ArtBearer::HERO).empty()) if(art != nullptr && !art->getPossibleSlots().at(ArtBearer::HERO).empty())
{ {
//TODO: should we try another possible slots? //TODO: should we try another possible slots?
ArtifactPosition slot = art->possibleSlots.at(ArtBearer::HERO).front(); ArtifactPosition slot = art->getPossibleSlots().at(ArtBearer::HERO).front();
if(!getArt(slot)) if(!getArt(slot))
putArtifact(slot, ArtifactUtils::createNewArtifactInstance(aid)); putArtifact(slot, ArtifactUtils::createNewArtifactInstance(aid));

View File

@ -153,7 +153,7 @@ bool CQuest::checkQuest(const CGHeroInstance * h) const
if(h->getArtPosCount(elem.first, false, true, true) < elem.second) if(h->getArtPosCount(elem.first, false, true, true) < elem.second)
return false; return false;
if(!h->hasArt(elem.first)) if(!h->hasArt(elem.first))
reqSlots += h->getAssemblyByConstituent(elem.first)->constituentsInfo.size() - 2; reqSlots += h->getAssemblyByConstituent(elem.first)->getPartsInfo().size() - 2;
} }
if(ArtifactUtils::isBackpackFreeSlots(h, reqSlots)) if(ArtifactUtils::isBackpackFreeSlots(h, reqSlots))
return true; return true;
@ -804,7 +804,7 @@ void CGSeerHut::finishQuest(const CGHeroInstance * h, ui32 accept) const
{ {
const auto * assembly = h->getAssemblyByConstituent(elem); const auto * assembly = h->getAssemblyByConstituent(elem);
assert(assembly); assert(assembly);
auto parts = assembly->constituentsInfo; auto parts = assembly->getPartsInfo();
// Remove the assembly // Remove the assembly
cb->removeArtifact(ArtifactLocation(h, h->getArtPos(assembly))); cb->removeArtifact(ArtifactLocation(h, h->getArtPos(assembly)));

View File

@ -820,7 +820,7 @@ void CGArtifact::afterAddToMap(CMap * map)
//Artifacts from map objects are never removed //Artifacts from map objects are never removed
//FIXME: This should be revertible in map editor //FIXME: This should be revertible in map editor
if(ID == Obj::SPELL_SCROLL && storedArtifact && storedArtifact->id.getNum() < 0) if(ID == Obj::SPELL_SCROLL && storedArtifact && storedArtifact->getId().getNum() < 0)
map->addNewArtifactInstance(storedArtifact); map->addNewArtifactInstance(storedArtifact);
} }

View File

@ -466,7 +466,7 @@ void CMap::checkForObjectives()
void CMap::addNewArtifactInstance(CArtifactInstance * art) void CMap::addNewArtifactInstance(CArtifactInstance * art)
{ {
art->id = ArtifactInstanceID(static_cast<si32>(artInstances.size())); art->setId(static_cast<ArtifactInstanceID>(artInstances.size()));
artInstances.emplace_back(art); artInstances.emplace_back(art);
} }
@ -474,7 +474,7 @@ void CMap::eraseArtifactInstance(CArtifactInstance * art)
{ {
//TODO: handle for artifacts removed in map editor //TODO: handle for artifacts removed in map editor
assert(artInstances[art->id.getNum()] == art); assert(artInstances[art->id.getNum()] == art);
artInstances[art->id.getNum()].dellNull(); artInstances[art->getId().getNum()].dellNull();
} }
void CMap::addNewQuestInstance(CQuest* quest) void CMap::addNewQuestInstance(CQuest* quest)

View File

@ -746,7 +746,7 @@ void CMapLoaderH3M::readAllowedArtifacts()
if(!features.levelSOD) if(!features.levelSOD)
{ {
for(CArtifact * artifact : VLC->arth->objects) for(CArtifact * artifact : VLC->arth->objects)
if(artifact->constituents) if(artifact->isCombined())
map->allowedArtifact[artifact->getId()] = false; map->allowedArtifact[artifact->getId()] = false;
} }

View File

@ -206,7 +206,6 @@ void registerTypesMapObjects2(Serializer &s)
// s.template registerType<CBonusSystemNode>(); // s.template registerType<CBonusSystemNode>();
s.template registerType<CBonusSystemNode, CArtifact>(); s.template registerType<CBonusSystemNode, CArtifact>();
s.template registerType<CArtifact, CGrowingArtifact>();
s.template registerType<CBonusSystemNode, CCreature>(); s.template registerType<CBonusSystemNode, CCreature>();
s.template registerType<CBonusSystemNode, CStackInstance>(); s.template registerType<CBonusSystemNode, CStackInstance>();
s.template registerType<CStackInstance, CCommanderInstance>(); s.template registerType<CStackInstance, CCommanderInstance>();
@ -218,7 +217,6 @@ void registerTypesMapObjects2(Serializer &s)
s.template registerType<CBonusSystemNode, BattleInfo>(); s.template registerType<CBonusSystemNode, BattleInfo>();
//s.template registerType<QuestInfo>(); //s.template registerType<QuestInfo>();
s.template registerType<CBonusSystemNode, CArtifactInstance>(); s.template registerType<CBonusSystemNode, CArtifactInstance>();
s.template registerType<CArtifactInstance, CCombinedArtifactInstance>();
//s.template registerType<CObstacleInstance>(); //s.template registerType<CObstacleInstance>();
s.template registerType<CObstacleInstance, SpellCreatedObstacle>(); s.template registerType<CObstacleInstance, SpellCreatedObstacle>();

View File

@ -113,7 +113,7 @@ void CMapGenerator::initQuestArtsRemaining()
for (auto art : VLC->arth->objects) for (auto art : VLC->arth->objects)
{ {
//Don't use parts of combined artifacts //Don't use parts of combined artifacts
if (art->aClass == CArtifact::ART_TREASURE && VLC->arth->legalArtifact(art->getId()) && art->constituentOf.empty()) if (art->aClass == CArtifact::ART_TREASURE && VLC->arth->legalArtifact(art->getId()) && art->getPartOf().empty())
questArtifacts.push_back(art->getId()); questArtifacts.push_back(art->getId());
} }
} }

View File

@ -33,7 +33,7 @@ void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib)
registerVectoredType<CArtifact, ArtifactID>(&lib->arth->objects, registerVectoredType<CArtifact, ArtifactID>(&lib->arth->objects,
[](const CArtifact &art){ return art.getId(); }); [](const CArtifact &art){ return art.getId(); });
registerVectoredType<CArtifactInstance, ArtifactInstanceID>(&gs->map->artInstances, registerVectoredType<CArtifactInstance, ArtifactInstanceID>(&gs->map->artInstances,
[](const CArtifactInstance &artInst){ return artInst.id; }); [](const CArtifactInstance &artInst){ return artInst.getId(); });
registerVectoredType<CQuest, si32>(&gs->map->quests, registerVectoredType<CQuest, si32>(&gs->map->quests,
[](const CQuest &q){ return q.qid; }); [](const CQuest &q){ return q.qid; });

View File

@ -140,7 +140,7 @@ std::list<Validator::Issue> Validator::validate(const CMap * map)
{ {
if(ins->storedArtifact) if(ins->storedArtifact)
{ {
if(!map->allowedSpell[ins->storedArtifact->id.getNum()]) if(!map->allowedSpell[ins->storedArtifact->getId().getNum()])
issues.emplace_back(QString("Spell scroll %1 is prohibited by map settings").arg(ins->getObjectName().c_str()), false); issues.emplace_back(QString("Spell scroll %1 is prohibited by map settings").arg(ins->getObjectName().c_str()), false);
} }
else else

View File

@ -807,12 +807,13 @@ void CGameHandler::endBattleConfirm(const BattleInfo * battleInfo)
changePrimSkill(finishingBattle->winnerHero, PrimarySkill::EXPERIENCE, battleResult.data->exp[finishingBattle->winnerSide]); changePrimSkill(finishingBattle->winnerHero, PrimarySkill::EXPERIENCE, battleResult.data->exp[finishingBattle->winnerSide]);
BattleResultAccepted raccepted; BattleResultAccepted raccepted;
raccepted.army1 = const_cast<CArmedInstance*>(bEndArmy1); raccepted.heroResult[0].army = const_cast<CArmedInstance*>(bEndArmy1);
raccepted.army2 = const_cast<CArmedInstance*>(bEndArmy2); raccepted.heroResult[1].army = const_cast<CArmedInstance*>(bEndArmy2);
raccepted.hero1 = const_cast<CGHeroInstance*>(battleInfo->sides.at(0).hero); raccepted.heroResult[0].hero = const_cast<CGHeroInstance*>(battleInfo->sides.at(0).hero);
raccepted.hero2 = const_cast<CGHeroInstance*>(battleInfo->sides.at(1).hero); raccepted.heroResult[1].hero = const_cast<CGHeroInstance*>(battleInfo->sides.at(1).hero);
raccepted.exp[0] = battleResult.data->exp[0]; raccepted.heroResult[0].exp = battleResult.data->exp[0];
raccepted.exp[1] = battleResult.data->exp[1]; raccepted.heroResult[1].exp = battleResult.data->exp[1];
raccepted.winnerSide = finishingBattle->winnerSide;
sendAndApply(&raccepted); sendAndApply(&raccepted);
queries.popIfTop(battleQuery); queries.popIfTop(battleQuery);
@ -4083,7 +4084,7 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition
if(assemble) if(assemble)
{ {
CArtifact * combinedArt = VLC->arth->objects[assembleTo]; CArtifact * combinedArt = VLC->arth->objects[assembleTo];
if(!combinedArt->constituents) if(!combinedArt->isCombined())
COMPLAIN_RET("assembleArtifacts: Artifact being attempted to assemble is not a combined artifacts!"); COMPLAIN_RET("assembleArtifacts: Artifact being attempted to assemble is not a combined artifacts!");
if (!vstd::contains(ArtifactUtils::assemblyPossibilities(hero, destArtifact->getTypeId(), if (!vstd::contains(ArtifactUtils::assemblyPossibilities(hero, destArtifact->getTypeId(),
ArtifactUtils::isSlotEquipment(artifactSlot)), combinedArt)) ArtifactUtils::isSlotEquipment(artifactSlot)), combinedArt))
@ -4102,11 +4103,11 @@ bool CGameHandler::assembleArtifacts (ObjectInstanceID heroID, ArtifactPosition
} }
else else
{ {
if(!destArtifact->canBeDisassembled()) if(!destArtifact->isCombined())
COMPLAIN_RET("assembleArtifacts: Artifact being attempted to disassemble is not a combined artifact!"); COMPLAIN_RET("assembleArtifacts: Artifact being attempted to disassemble is not a combined artifact!");
if(ArtifactUtils::isSlotBackpack(artifactSlot) if(ArtifactUtils::isSlotBackpack(artifactSlot)
&& !ArtifactUtils::isBackpackFreeSlots(hero, destArtifact->artType->constituents->size() - 1)) && !ArtifactUtils::isBackpackFreeSlots(hero, destArtifact->artType->getConstituents().size() - 1))
COMPLAIN_RET("assembleArtifacts: Artifact being attempted to disassemble but backpack is full!"); COMPLAIN_RET("assembleArtifacts: Artifact being attempted to disassemble but backpack is full!");
DisassembledArtifact da; DisassembledArtifact da;
@ -4159,9 +4160,9 @@ bool CGameHandler::buyArtifact(ObjectInstanceID hid, ArtifactID aid)
{ {
const CArtifact * art = aid.toArtifact(); const CArtifact * art = aid.toArtifact();
COMPLAIN_RET_FALSE_IF(nullptr == art, "Invalid artifact index to buy"); COMPLAIN_RET_FALSE_IF(nullptr == art, "Invalid artifact index to buy");
COMPLAIN_RET_FALSE_IF(art->warMachine == CreatureID::NONE, "War machine artifact required"); COMPLAIN_RET_FALSE_IF(art->getWarMachine() == CreatureID::NONE, "War machine artifact required");
COMPLAIN_RET_FALSE_IF(hero->hasArt(aid),"Hero already has this machine!"); COMPLAIN_RET_FALSE_IF(hero->hasArt(aid),"Hero already has this machine!");
const int price = art->price; const int price = art->getPrice();
COMPLAIN_RET_FALSE_IF(getPlayerState(hero->getOwner())->resources[EGameResID::GOLD] < price, "Not enough gold!"); COMPLAIN_RET_FALSE_IF(getPlayerState(hero->getOwner())->resources[EGameResID::GOLD] < price, "Not enough gold!");
if ((town->hasBuilt(BuildingID::BLACKSMITH) && town->town->warMachine == aid) if ((town->hasBuilt(BuildingID::BLACKSMITH) && town->town->warMachine == aid)
@ -6808,17 +6809,12 @@ bool CGameHandler::giveHeroNewArtifact(const CGHeroInstance * h, const CArtifact
COMPLAIN_RET_FALSE_IF(!artType->canBePutAt(h, pos, false), "Cannot put artifact in that slot!"); COMPLAIN_RET_FALSE_IF(!artType->canBePutAt(h, pos, false), "Cannot put artifact in that slot!");
} }
CArtifactInstance * newArtInst = nullptr; auto * newArtInst = new CArtifactInstance();
if(artType->canBeDisassembled())
newArtInst = new CCombinedArtifactInstance();
else
newArtInst = new CArtifactInstance();
newArtInst->artType = artType; // *NOT* via settype -> all bonus-related stuff must be done by NewArtifact apply newArtInst->artType = artType; // *NOT* via settype -> all bonus-related stuff must be done by NewArtifact apply
NewArtifact na; NewArtifact na;
na.art = newArtInst; na.art = newArtInst;
sendAndApply(&na); // -> updates a!!!, will create a on other machines sendAndApply(&na); // -> updates newArtInst!!!
if(giveHeroArtifact(h, newArtInst, pos)) if(giveHeroArtifact(h, newArtInst, pos))
return true; return true;

View File

@ -32,9 +32,7 @@ protected:
TEST_F(CArtifactTest, RegistersIcons) TEST_F(CArtifactTest, RegistersIcons)
{ {
subject->iconIndex = 4242; subject-> setImage(4242, "Test1", "Test2");
subject->image = "Test1";
subject->large = "Test2";
auto cb = [this](auto && PH1, auto && PH2, auto && PH3, auto && PH4) auto cb = [this](auto && PH1, auto && PH2, auto && PH3, auto && PH4)
{ {