diff --git a/AI/Nullkiller/Engine/PriorityEvaluator.cpp b/AI/Nullkiller/Engine/PriorityEvaluator.cpp index 43f695ffd..4997fde20 100644 --- a/AI/Nullkiller/Engine/PriorityEvaluator.cpp +++ b/AI/Nullkiller/Engine/PriorityEvaluator.cpp @@ -92,6 +92,8 @@ int32_t estimateTownIncome(CCallback * cb, const CGObjectInstance * target, cons TResources getCreatureBankResources(const CGObjectInstance * target, const CGHeroInstance * hero) { + //Fixme: unused variable hero + auto objectInfo = VLC->objtypeh->getHandlerFor(target->ID, target->subID)->getObjectInfo(target->appearance); CBankInfo * bankInfo = dynamic_cast(objectInfo.get()); auto resources = bankInfo->getPossibleResourcesReward(); @@ -114,11 +116,33 @@ uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHero auto creatures = bankInfo->getPossibleCreaturesReward(); uint64_t result = 0; - for(auto c : creatures) + const auto& slots = hero->Slots(); + ui64 weakestStackPower = 0; + if (slots.size() >= GameConstants::ARMY_SIZE) { - result += c.data.type->AIValue * c.data.count * c.chance / 100; + //No free slot, we might discard our weakest stack + weakestStackPower = std::numeric_limits().max(); + for (const auto stack : slots) + { + vstd::amin(weakestStackPower, stack.second->getPower()); + } } + for (auto c : creatures) + { + //Only if hero has slot for this creature in the army + if (hero->getSlotFor(c.data.type).validSlot()) + { + result += (c.data.type->AIValue * c.data.count) * c.chance; + } + else + { + //we will need to discard the weakest stack + result += (c.data.type->AIValue * c.data.count - weakestStackPower) * c.chance; + } + } + result /= 100; //divide by total chance + return result; } @@ -168,13 +192,13 @@ uint64_t evaluateArtifactArmyValue(CArtifactInstance * art) return 1500; auto statsValue = - 4 * art->valOfBonuses(Bonus::LAND_MOVEMENT) + 10 * art->valOfBonuses(Bonus::LAND_MOVEMENT) + + 1200 * art->valOfBonuses(Bonus::STACKS_SPEED) + 700 * art->valOfBonuses(Bonus::MORALE) + 700 * art->getAttack(false) + 700 * art->getDefense(false) + 700 * art->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::KNOWLEDGE) + 700 * art->valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::SPELL_POWER) - + 700 * art->getDefense(false) + 500 * art->valOfBonuses(Bonus::LUCK); auto classValue = 0; @@ -234,6 +258,8 @@ uint64_t RewardEvaluator::getArmyReward( return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES ? enemyArmyEliminationRewardRatio * dynamic_cast(target)->getArmyStrength() : 0; + case Obj::PANDORAS_BOX: + return 5000; default: return 0; } @@ -273,7 +299,14 @@ float RewardEvaluator::getEnemyHeroStrategicalValue(const CGHeroInstance * enemy vstd::amax(objectValue, getStrategicalValue(obj)); } - return objectValue / 2.0f + enemy->level / 15.0f; + /* + 1. If an enemy hero can attack nearby object, it's not useful to capture the object on our own. + Killing the hero is almost as important (0.9) as capturing the object itself. + + 2. The formula quickly approaches 1.0 as hero level increases, + but higher level always means higher value and the minimal value for level 1 hero is 0.5 + */ + return std::min(1.0f, objectValue * 0.9f + (1.0f - (1.0f / (1 + enemy->level)))); } float RewardEvaluator::getResourceRequirementStrength(int resType) const @@ -322,13 +355,28 @@ float RewardEvaluator::getStrategicalValue(const CGObjectInstance * target) cons case Obj::RESOURCE: return target->subID == Res::GOLD ? 0 : 0.1f * getResourceRequirementStrength(target->subID); + case Obj::CREATURE_BANK: + { + auto resourceReward = getCreatureBankResources(target, nullptr); + float sum = 0.0f; + for (TResources::nziterator it (resourceReward); it.valid(); it++) + { + //Evaluate resources used for construction. Gold is evaluated separately. + if (it->resType != Res::GOLD) + { + sum += 0.1f * getResourceRequirementStrength(it->resType); + } + } + return sum; + } + case Obj::TOWN: if(ai->buildAnalyzer->getDevelopmentInfo().empty()) return 1; return dynamic_cast(target)->hasFort() ? (target->tempOwner == PlayerColor::NEUTRAL ? 0.8f : 1.0f) - : 0.5f; + : 0.7f; case Obj::HERO: return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES @@ -385,6 +433,9 @@ float RewardEvaluator::getSkillReward(const CGObjectInstance * target, const CGH return 8; case Obj::WITCH_HUT: return evaluateWitchHutSkillScore(dynamic_cast(target), hero, role); + case Obj::PANDORAS_BOX: + //Can contains experience, spells, or skills (only on custom maps) + return 2.5f; case Obj::HERO: return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES ? enemyHeroEliminationSkillRewardRatio * dynamic_cast(target)->level @@ -464,6 +515,11 @@ int32_t RewardEvaluator::getGoldReward(const CGObjectInstance * target, const CG return 10000; case Obj::SEA_CHEST: return 1500; + case Obj::PANDORAS_BOX: + return 5000; + case Obj::PRISON: + //Objectively saves us 2500 to hire hero + return GameConstants::HERO_GOLD_COST; case Obj::HERO: return ai->cb->getPlayerRelations(target->tempOwner, ai->playerID) == PlayerRelations::ENEMIES ? heroEliminationBonus + enemyArmyEliminationGoldRewardRatio * getArmyCost(dynamic_cast(target)) diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index d9ce48f46..e0193efdc 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -47,6 +47,10 @@ #include +#ifdef VCMI_WINDOWS +#include +#endif + template class CApplyOnLobby; #ifdef VCMI_ANDROID @@ -694,7 +698,23 @@ void CServerHandler::threadRunServer() } comm += " > \"" + logName + '\"'; +#ifdef VCMI_WINDOWS + int result = -1; + const auto bufSize = ::MultiByteToWideChar(CP_UTF8, 0, comm.c_str(), comm.size(), nullptr, 0); + if(bufSize > 0) + { + std::wstring wComm(bufSize, {}); + const auto convertResult = ::MultiByteToWideChar(CP_UTF8, 0, comm.c_str(), comm.size(), &wComm[0], bufSize); + if(convertResult > 0) + result = ::_wsystem(wComm.c_str()); + else + logNetwork->error("Error " + std::to_string(GetLastError()) + ": failed to convert server launch command to wide string: " + comm); + } + else + logNetwork->error("Error " + std::to_string(GetLastError()) + ": failed to obtain buffer length to convert server launch command to wide string : " + comm); +#else int result = std::system(comm.c_str()); +#endif if (result == 0) { logNetwork->info("Server closed correctly"); diff --git a/client/Graphics.cpp b/client/Graphics.cpp index 41b4a4507..e9d3ac2e5 100644 --- a/client/Graphics.cpp +++ b/client/Graphics.cpp @@ -173,8 +173,8 @@ void Graphics::loadHeroAnimations() { for (auto & templ : VLC->objtypeh->getHandlerFor(Obj::HERO, elem->getIndex())->getTemplates()) { - if (!heroAnimations.count(templ.animationFile)) - heroAnimations[templ.animationFile] = loadHeroAnimation(templ.animationFile); + if (!heroAnimations.count(templ->animationFile)) + heroAnimations[templ->animationFile] = loadHeroAnimation(templ->animationFile); } } @@ -381,21 +381,21 @@ std::shared_ptr Graphics::getAnimation(const CGObjectInstance* obj) return getAnimation(obj->appearance); } -std::shared_ptr Graphics::getAnimation(const ObjectTemplate & info) +std::shared_ptr Graphics::getAnimation(std::shared_ptr info) { //the only(?) invisible object - if(info.id == Obj::EVENT) + if(info->id == Obj::EVENT) { return std::shared_ptr(); } - if(info.animationFile.empty()) + if(info->animationFile.empty()) { - logGlobal->warn("Def name for obj (%d,%d) is empty!", info.id, info.subid); + logGlobal->warn("Def name for obj (%d,%d) is empty!", info->id, info->subid); return std::shared_ptr(); } - std::shared_ptr ret = mapObjectAnimations[info.animationFile]; + std::shared_ptr ret = mapObjectAnimations[info->animationFile]; //already loaded if(ret) @@ -404,8 +404,8 @@ std::shared_ptr Graphics::getAnimation(const ObjectTemplate & info) return ret; } - ret = std::make_shared(info.animationFile); - mapObjectAnimations[info.animationFile] = ret; + ret = std::make_shared(info->animationFile); + mapObjectAnimations[info->animationFile] = ret; ret->preload(); return ret; diff --git a/client/Graphics.h b/client/Graphics.h index fbf41063a..c1461b064 100644 --- a/client/Graphics.h +++ b/client/Graphics.h @@ -98,7 +98,7 @@ public: void blueToPlayersAdv(SDL_Surface * sur, PlayerColor player); //replaces blue interface colour with a color of player std::shared_ptr getAnimation(const CGObjectInstance * obj); - std::shared_ptr getAnimation(const ObjectTemplate & info); + std::shared_ptr getAnimation(std::shared_ptr info); }; extern Graphics * graphics; diff --git a/client/battle/CBattleInterface.cpp b/client/battle/CBattleInterface.cpp index 5cb46ff95..b416578ba 100644 --- a/client/battle/CBattleInterface.cpp +++ b/client/battle/CBattleInterface.cpp @@ -502,13 +502,14 @@ void CBattleInterface::activate() bWait->activate(); bDefence->activate(); - for (auto hex : bfield) - hex->activate(); - if (attackingHero) attackingHero->activate(); if (defendingHero) defendingHero->activate(); + + for (auto hex : bfield) + hex->activate(); + if (settings["battle"]["showQueue"].Bool()) queue->activate(); diff --git a/client/mapHandler.cpp b/client/mapHandler.cpp index f33a8dad5..c9da73647 100644 --- a/client/mapHandler.cpp +++ b/client/mapHandler.cpp @@ -979,7 +979,7 @@ CMapHandler::AnimBitmapHolder CMapHandler::CMapBlitter::findHeroBitmap(const CGH if (hero->boat) animation = graphics->boatAnimations[hero->boat->subID]; else - animation = graphics->heroAnimations[hero->appearance.animationFile]; + animation = graphics->heroAnimations[hero->appearance->animationFile]; bool moving = !hero->isStanding; int group = getHeroFrameGroup(hero->moveDir, moving); @@ -1486,8 +1486,8 @@ bool CMapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObj return true; if (!b) return false; - if (a->appearance.printPriority != b->appearance.printPriority) - return a->appearance.printPriority > b->appearance.printPriority; + if (a->appearance->printPriority != b->appearance->printPriority) + return a->appearance->printPriority > b->appearance->printPriority; if(a->pos.y != b->pos.y) return a->pos.y < b->pos.y; diff --git a/client/windows/QuickRecruitmentWindow.cpp b/client/windows/QuickRecruitmentWindow.cpp index 7553aa587..a93e63a6d 100644 --- a/client/windows/QuickRecruitmentWindow.cpp +++ b/client/windows/QuickRecruitmentWindow.cpp @@ -29,20 +29,20 @@ void QuickRecruitmentWindow::setButtons() void QuickRecruitmentWindow::setCancelButton() { - cancelButton = std::make_shared(Point((pos.w / 2) + 48, 418), "ICN6432.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_RETURN); + cancelButton = std::make_shared(Point((pos.w / 2) + 48, 418), "ICN6432.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_ESCAPE); cancelButton->setImageOrder(0, 1, 2, 3); } void QuickRecruitmentWindow::setBuyButton() { - buyButton = std::make_shared(Point((pos.w/2)-32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purhaseUnits(); }); + buyButton = std::make_shared(Point((pos.w/2)-32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purhaseUnits(); }, SDLK_RETURN); cancelButton->assignedKeys.insert(SDLK_ESCAPE); buyButton->setImageOrder(0, 1, 2, 3); } void QuickRecruitmentWindow::setMaxButton() { - maxButton = std::make_shared(Point((pos.w/2)-112, 418), "IRCBTNS.DEF", CButton::tooltip(), [&](){ maxAllCards(cards); }); + maxButton = std::make_shared(Point((pos.w/2)-112, 418), "IRCBTNS.DEF", CButton::tooltip(), [&](){ maxAllCards(cards); }, SDLK_m); maxButton->setImageOrder(0, 1, 2, 3); } diff --git a/launcher/modManager/cmodlist.cpp b/launcher/modManager/cmodlist.cpp index eb42c10aa..c0d617442 100644 --- a/launcher/modManager/cmodlist.cpp +++ b/launcher/modManager/cmodlist.cpp @@ -92,6 +92,11 @@ bool CModEntry::isUpdateable() const return false; } +bool CModEntry::isEssential() const +{ + return getValue("storedLocaly").toBool(); +} + bool CModEntry::isInstalled() const { return !localData.isEmpty(); @@ -152,6 +157,10 @@ QVariantMap CModList::copyField(QVariantMap data, QString from, QString to) return renamed; } +void CModList::reloadRepositories() +{ +} + void CModList::resetRepositories() { repositories.clear(); @@ -176,7 +185,7 @@ void CModList::modChanged(QString modID) { } -static QVariant getValue(QVariantMap input, QString path) +static QVariant getValue(QVariant input, QString path) { if(path.size() > 1) { @@ -184,7 +193,7 @@ static QVariant getValue(QVariantMap input, QString path) QString remainder = "/" + path.section('/', 2, -1); entryName.remove(0, 1); - return getValue(input.value(entryName).toMap(), remainder); + return getValue(input.toMap().value(entryName), remainder); } else { @@ -208,11 +217,29 @@ CModEntry CModList::getMod(QString modname) const } else { - if(conf.canConvert()) + if(!conf.toMap().isEmpty()) + { settings = conf.toMap(); + if(settings.value("active").isNull()) + settings["active"] = true; // default + } else settings.insert("active", conf); } + + if(settings["active"].toBool()) + { + QString rootPath = path.section('/', 0, 1); + if(path != rootPath) + { + conf = getValue(modSettings, rootPath); + const auto confMap = conf.toMap(); + if(!conf.isNull() && !confMap["active"].isNull() && !confMap["active"].toBool()) + { + settings = confMap; + } + } + } for(auto entry : repositories) { diff --git a/launcher/modManager/cmodlist.h b/launcher/modManager/cmodlist.h index e3fb9bf04..a5de09622 100644 --- a/launcher/modManager/cmodlist.h +++ b/launcher/modManager/cmodlist.h @@ -49,6 +49,8 @@ public: bool isUpdateable() const; // installed bool isInstalled() const; + // vcmi essential files + bool isEssential() const; // see ModStatus enum int getModStatus() const; @@ -74,6 +76,7 @@ class CModList public: virtual void resetRepositories(); + virtual void reloadRepositories(); virtual void addRepository(QVariantMap data); virtual void setLocalModList(QVariantMap data); virtual void setModSettings(QVariant data); diff --git a/launcher/modManager/cmodlistmodel_moc.cpp b/launcher/modManager/cmodlistmodel_moc.cpp index 780f4f855..2e94b8f81 100644 --- a/launcher/modManager/cmodlistmodel_moc.cpp +++ b/launcher/modManager/cmodlistmodel_moc.cpp @@ -160,6 +160,12 @@ QVariant CModListModel::headerData(int section, Qt::Orientation orientation, int return QVariant(); } +void CModListModel::reloadRepositories() +{ + beginResetModel(); + endResetModel(); +} + void CModListModel::resetRepositories() { beginResetModel(); diff --git a/launcher/modManager/cmodlistmodel_moc.h b/launcher/modManager/cmodlistmodel_moc.h index fea0f951b..208efc625 100644 --- a/launcher/modManager/cmodlistmodel_moc.h +++ b/launcher/modManager/cmodlistmodel_moc.h @@ -61,6 +61,7 @@ public: /// CModListContainer overrides void resetRepositories() override; + void reloadRepositories() override; void addRepository(QVariantMap data) override; void modChanged(QString modID) override; diff --git a/launcher/modManager/cmodlistview_moc.cpp b/launcher/modManager/cmodlistview_moc.cpp index 70419b9cb..63d0d36e1 100644 --- a/launcher/modManager/cmodlistview_moc.cpp +++ b/launcher/modManager/cmodlistview_moc.cpp @@ -310,10 +310,10 @@ void CModListView::selectMod(const QModelIndex & index) // Block buttons if action is not allowed at this time // TODO: automate handling of some of these cases instead of forcing player // to resolve all conflicts manually. - ui->disableButton->setEnabled(!hasDependentMods); + ui->disableButton->setEnabled(!hasDependentMods && !mod.isEssential()); ui->enableButton->setEnabled(!hasBlockingMods && !hasInvalidDeps); ui->installButton->setEnabled(!hasInvalidDeps); - ui->uninstallButton->setEnabled(!hasDependentMods); + ui->uninstallButton->setEnabled(!hasDependentMods && !mod.isEssential()); ui->updateButton->setEnabled(!hasInvalidDeps && !hasDependentMods); loadScreenshots(); @@ -522,6 +522,9 @@ void CModListView::downloadFile(QString file, QString url, QString description) connect(dlManager, SIGNAL(finished(QStringList,QStringList,QStringList)), this, SLOT(downloadFinished(QStringList,QStringList,QStringList))); + + + connect(modModel, &CModListModel::dataChanged, filterModel, &QAbstractItemModel::dataChanged); QString progressBarFormat = "Downloading %s%. %p% (%v KB out of %m KB) finished"; @@ -658,7 +661,7 @@ void CModListView::installMods(QStringList archives) auto mod = modModel->getMod(modName); if(mod.isInstalled() && !mod.getValue("keepDisabled").toBool()) { - if(manager->enableMod(modName)) + if(mod.isDisabled() && manager->enableMod(modName)) { for(QString child : modModel->getChildren(modName)) enableMod(child); diff --git a/launcher/modManager/cmodmanager.cpp b/launcher/modManager/cmodmanager.cpp index 0d830899f..3bc854bb3 100644 --- a/launcher/modManager/cmodmanager.cpp +++ b/launcher/modManager/cmodmanager.cpp @@ -74,6 +74,7 @@ void CModManager::loadMods() CModHandler handler; handler.loadMods(); auto installedMods = handler.getAllMods(); + localMods.clear(); for(auto modname : installedMods) { @@ -82,6 +83,13 @@ void CModManager::loadMods() { boost::filesystem::path name = *CResourceHandler::get()->getResourceName(resID); auto mod = JsonUtils::JsonFromFile(pathToQString(name)); + if(!name.is_absolute()) + { + auto json = JsonUtils::toJson(mod); + json["storedLocaly"].Bool() = true; + mod = JsonUtils::toVariant(json); + } + localMods.insert(QString::fromUtf8(modname.c_str()).toLower(), mod); } } @@ -269,12 +277,10 @@ bool CModManager::doInstallMod(QString modname, QString archivePath) QString upperLevel = modDirName.section('/', 0, 0); if(upperLevel != modDirName) removeModDir(destDir + upperLevel); - - QVariantMap json = JsonUtils::JsonFromFile(destDir + modname + "/mod.json").toMap(); - - localMods.insert(modname, json); - modList->setLocalModList(localMods); - modList->modChanged(modname); + + CResourceHandler::get("initial")->updateFilteredFiles([](const std::string &) { return true; }); + loadMods(); + modList->reloadRepositories(); return true; } @@ -288,16 +294,13 @@ bool CModManager::doUninstallMod(QString modname) if(!QDir(modDir).exists()) return addError(modname, "Data with this mod was not found"); - if(!localMods.contains(modname)) - return addError(modname, "Data with this mod was not found"); - QDir modFullDir(modDir); if(!removeModDir(modDir)) return addError(modname, "Mod is located in protected directory, plase remove it manually:\n" + modFullDir.absolutePath()); - localMods.remove(modname); - modList->setLocalModList(localMods); - modList->modChanged(modname); + CResourceHandler::get("initial")->updateFilteredFiles([](const std::string &){ return true; }); + loadMods(); + modList->reloadRepositories(); return true; } diff --git a/lib/CHeroHandler.cpp b/lib/CHeroHandler.cpp index bfb00fed3..8f27b596a 100644 --- a/lib/CHeroHandler.cpp +++ b/lib/CHeroHandler.cpp @@ -842,10 +842,22 @@ void CHeroHandler::loadObstacles() allConfigs.insert(allConfigs.begin(), "core"); for(auto & mod : allConfigs) { - if(!CResourceHandler::get(mod)->existsResource(ResourceID("config/obstacles.json"))) + ISimpleResourceLoader * modResourceLoader; + try + { + modResourceLoader = CResourceHandler::get(mod); + } + catch(const std::out_of_range &) + { + logMod->warn("Mod '%1%' doesn't exist! Its obstacles won't be loaded!", mod); + continue; + } + + const ResourceID obstaclesResource{"config/obstacles.json"}; + if(!modResourceLoader->existsResource(obstaclesResource)) continue; - const JsonNode config(mod, ResourceID("config/obstacles.json")); + const JsonNode config(mod, obstaclesResource); loadObstacles(config["obstacles"], false, obstacles); loadObstacles(config["absoluteObstacles"], true, absoluteObstacles); } diff --git a/lib/filesystem/CFilesystemLoader.cpp b/lib/filesystem/CFilesystemLoader.cpp index 0585c761b..d4c8b7e51 100644 --- a/lib/filesystem/CFilesystemLoader.cpp +++ b/lib/filesystem/CFilesystemLoader.cpp @@ -16,9 +16,10 @@ namespace bfs = boost::filesystem; CFilesystemLoader::CFilesystemLoader(std::string _mountPoint, bfs::path baseDirectory, size_t depth, bool initial): - baseDirectory(std::move(baseDirectory)), - mountPoint(std::move(_mountPoint)), - fileList(listFiles(mountPoint, depth, initial)) + baseDirectory(std::move(baseDirectory)), + mountPoint(std::move(_mountPoint)), + fileList(listFiles(mountPoint, depth, initial)), + recursiveDepth(depth) { logGlobal->trace("File system loaded, %d files found", fileList.size()); } @@ -52,7 +53,7 @@ void CFilesystemLoader::updateFilteredFiles(std::functiongetHeroCount(h->tempOwner, false) < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)//GameConstants::MAX_HEROES_PER_PLAYER) //free hero slot + if (cb->getHeroCount(h->tempOwner, false) < VLC->modh->settings.MAX_HEROES_ON_MAP_PER_PLAYER)//free hero slot { - cb->changeObjPos(id,pos+int3(1,0,0),0); //update hero parameters SetMovePoints smp; smp.hid = id; @@ -534,7 +533,7 @@ void CGHeroInstance::initObj(CRandomGenerator & rand) { auto customApp = VLC->objtypeh->getHandlerFor(ID, type->heroClass->getIndex())->getOverride(cb->gameState()->getTile(visitablePos())->terType, this); if (customApp) - appearance = customApp.get(); + appearance = customApp; } //copy active (probably growing) bonuses from hero prototype to hero object @@ -1418,7 +1417,9 @@ void CGHeroInstance::setHeroTypeName(const std::string & identifier) if(rawId) subID = rawId.get(); else - subID = 0; //fallback to Orrin, throw error instead? + { + throw std::runtime_error("Couldn't resolve hero identifier " + identifier); + } } } diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index bb320caee..19179159f 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -1133,7 +1133,7 @@ void CGTownInstance::updateAppearance() //FIXME: not the best way to do this auto app = VLC->objtypeh->getHandlerFor(ID, subID)->getOverride(cb->gameState()->getTile(visitablePos())->terType, this); if (app) - appearance = app.get(); + appearance = app; } std::string CGTownInstance::nodeName() const diff --git a/lib/mapObjects/CObjectClassesHandler.cpp b/lib/mapObjects/CObjectClassesHandler.cpp index b9525cb0a..4a3e8ad12 100644 --- a/lib/mapObjects/CObjectClassesHandler.cpp +++ b/lib/mapObjects/CObjectClassesHandler.cpp @@ -117,11 +117,11 @@ std::vector CObjectClassesHandler::loadLegacyData(size_t dataSize) for (size_t i=0; ireadTxt(parser); parser.endLine(); - std::pair key(templ.id.num, templ.subid); - legacyTemplates.insert(std::make_pair(key, templ)); + std::pair key(templ->id.num, templ->subid); + legacyTemplates.insert(std::make_pair(key, std::shared_ptr(templ))); } std::vector ret(dataSize);// create storage for 256 objects @@ -448,7 +448,6 @@ AObjectTypeHandler::AObjectTypeHandler(): AObjectTypeHandler::~AObjectTypeHandler() { - } void AObjectTypeHandler::setType(si32 type, si32 subtype) @@ -488,12 +487,12 @@ void AObjectTypeHandler::init(const JsonNode & input, boost::optionalid = Obj(type); + tmpl->subid = subtype; + tmpl->stringID = entry.first; // FIXME: create "fullID" - type.object.template? + tmpl->readJson(entry.second); + templates.push_back(std::shared_ptr(tmpl)); } if (input["name"].isNull()) @@ -523,7 +522,7 @@ void AObjectTypeHandler::init(const JsonNode & input, boost::optional) const { return false; // by default there are no overrides } @@ -551,26 +550,28 @@ SObjectSounds AObjectTypeHandler::getSounds() const return sounds; } -void AObjectTypeHandler::addTemplate(const ObjectTemplate & templ) +void AObjectTypeHandler::addTemplate(std::shared_ptr templ) { + //Otherwise the template remains constant + auto ptr = const_cast(templ.get()); + ptr->id = Obj(type); + ptr->subid = subtype; templates.push_back(templ); - templates.back().id = Obj(type); - templates.back().subid = subtype; } void AObjectTypeHandler::addTemplate(JsonNode config) { config.setType(JsonNode::JsonType::DATA_STRUCT); // ensure that input is not null JsonUtils::inherit(config, base); - ObjectTemplate tmpl; - tmpl.id = Obj(type); - tmpl.subid = subtype; - tmpl.stringID = ""; // TODO? - tmpl.readJson(config); - templates.push_back(tmpl); + auto tmpl = new ObjectTemplate; + tmpl->id = Obj(type); + tmpl->subid = subtype; + tmpl->stringID = ""; // TODO? + tmpl->readJson(config); + templates.emplace_back(tmpl); } -std::vector AObjectTypeHandler::getTemplates() const +std::vector> AObjectTypeHandler::getTemplates() const { return templates; } @@ -580,14 +581,14 @@ BattleField AObjectTypeHandler::getBattlefield() const return battlefield ? BattleField::fromString(battlefield.get()) : BattleField::NONE; } -std::vector AObjectTypeHandler::getTemplates(const Terrain & terrainType) const +std::vector>AObjectTypeHandler::getTemplates(const Terrain & terrainType) const { - std::vector templates = getTemplates(); - std::vector filtered; + std::vector> templates = getTemplates(); + std::vector> filtered; - std::copy_if(templates.begin(), templates.end(), std::back_inserter(filtered), [&](const ObjectTemplate & obj) + std::copy_if(templates.begin(), templates.end(), std::back_inserter(filtered), [&](std::shared_ptr obj) { - return obj.canBePlacedAt(terrainType); + return obj->canBePlacedAt(terrainType); }); // H3 defines allowed terrains in a weird way - artifacts, monsters and resources have faulty masks here // Perhaps we should re-define faulty templates and remove this workaround (already done for resources) @@ -597,15 +598,15 @@ std::vector AObjectTypeHandler::getTemplates(const Terrain & ter return filtered; } -boost::optional AObjectTypeHandler::getOverride(const Terrain & terrainType, const CGObjectInstance * object) const +std::shared_ptr AObjectTypeHandler::getOverride(const Terrain & terrainType, const CGObjectInstance * object) const { - std::vector ret = getTemplates(terrainType); - for (auto & tmpl : ret) + std::vector> ret = getTemplates(terrainType); + for (const auto & tmpl: ret) { if (objectFilter(object, tmpl)) return tmpl; } - return boost::optional(); + return std::shared_ptr(); //empty } const RandomMapInfo & AObjectTypeHandler::getRMGInfo() diff --git a/lib/mapObjects/CObjectClassesHandler.h b/lib/mapObjects/CObjectClassesHandler.h index e578f585c..cff16a715 100644 --- a/lib/mapObjects/CObjectClassesHandler.h +++ b/lib/mapObjects/CObjectClassesHandler.h @@ -144,7 +144,7 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable JsonNode base; /// describes base template - std::vector templates; + std::vector> templates; SObjectSounds sounds; @@ -154,7 +154,7 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable protected: void preInitObject(CGObjectInstance * obj) const; - virtual bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const; + virtual bool objectFilter(const CGObjectInstance *, std::shared_ptr) const; /// initialization for classes that inherit this one virtual void initTypeData(const JsonNode & input); @@ -177,17 +177,21 @@ public: boost::optional getCustomName() const; SObjectSounds getSounds() const; - void addTemplate(const ObjectTemplate & templ); + void addTemplate(std::shared_ptr templ); void addTemplate(JsonNode config); /// returns all templates matching parameters - std::vector getTemplates() const; - std::vector getTemplates(const Terrain & terrainType) const; + std::vector> getTemplates() const; + std::vector> getTemplates(const Terrain & terrainType) const; + + /// returns preferred template for this object, if present (e.g. one of 3 possible templates for town - village, fort and castle) + /// note that appearance will not be changed - this must be done separately (either by assignment or via pack from server) + std::shared_ptr getOverride(const Terrain & terrainType, const CGObjectInstance * object) const; + BattleField getBattlefield() const; /// returns preferred template for this object, if present (e.g. one of 3 possible templates for town - village, fort and castle) /// note that appearance will not be changed - this must be done separately (either by assignment or via pack from server) - boost::optional getOverride(const Terrain & terrainType, const CGObjectInstance * object) const; const RandomMapInfo & getRMGInfo(); @@ -199,14 +203,14 @@ public: /// Creates object and set up core properties (like ID/subID). Object is NOT initialized /// to allow creating objects before game start (e.g. map loading) - virtual CGObjectInstance * create(const ObjectTemplate & tmpl) const = 0; + virtual CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const = 0; /// Configures object properties. Should be re-entrable, resetting state of the object if necessarily /// This should set remaining properties, including randomized or depending on map virtual void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const = 0; /// Returns object configuration, if available. Otherwise returns NULL - virtual std::unique_ptr getObjectInfo(const ObjectTemplate & tmpl) const = 0; + virtual std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const = 0; template void serialize(Handler &h, const int version) { @@ -264,7 +268,7 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase std::map > handlerConstructors; /// container with H3 templates, used only during loading, no need to serialize it - typedef std::multimap, ObjectTemplate> TTemplatesContainer; + typedef std::multimap, std::shared_ptr> TTemplatesContainer; TTemplatesContainer legacyTemplates; /// contains list of custom names for H3 objects (e.g. Dwellings), used to load H3 data diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index 26597a351..e600b32e9 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -152,24 +152,24 @@ void CGObjectInstance::setOwner(PlayerColor ow) } int CGObjectInstance::getWidth() const//returns width of object graphic in tiles { - return appearance.getWidth(); + return appearance->getWidth(); } int CGObjectInstance::getHeight() const //returns height of object graphic in tiles { - return appearance.getHeight(); + return appearance->getHeight(); } bool CGObjectInstance::visitableAt(int x, int y) const //returns true if object is visitable at location (x, y) form left top tile of image (x, y in tiles) { - return appearance.isVisitableAt(pos.x - x, pos.y - y); + return appearance->isVisitableAt(pos.x - x, pos.y - y); } bool CGObjectInstance::blockingAt(int x, int y) const { - return appearance.isBlockedAt(pos.x - x, pos.y - y); + return appearance->isBlockedAt(pos.x - x, pos.y - y); } bool CGObjectInstance::coveringAt(int x, int y) const { - return appearance.isVisibleAt(pos.x - x, pos.y - y); + return appearance->isVisibleAt(pos.x - x, pos.y - y); } std::set CGObjectInstance::getBlockedPos() const @@ -179,7 +179,7 @@ std::set CGObjectInstance::getBlockedPos() const { for(int h=0; hisBlockedAt(w, h)) ret.insert(int3(pos.x - w, pos.y - h, pos.z)); } } @@ -188,7 +188,7 @@ std::set CGObjectInstance::getBlockedPos() const std::set CGObjectInstance::getBlockedOffsets() const { - return appearance.getBlockedOffsets(); + return appearance->getBlockedOffsets(); } void CGObjectInstance::setType(si32 ID, si32 subID) @@ -210,6 +210,11 @@ void CGObjectInstance::setType(si32 ID, si32 subID) appearance = handler->getTemplates(tile.terType)[0]; else appearance = handler->getTemplates()[0]; // get at least some appearance since alternative is crash + if (ID == Obj::HERO) + { + //adjust for the prison offset + pos = visitablePos(); + } cb->gameState()->map->addBlockVisTiles(this); } @@ -259,7 +264,7 @@ int CGObjectInstance::getSightRadius() const int3 CGObjectInstance::getVisitableOffset() const { - return appearance.getVisitableOffset(); + return appearance->getVisitableOffset(); } void CGObjectInstance::giveDummyBonus(ObjectInstanceID heroID, ui8 duration) const @@ -345,7 +350,7 @@ int3 CGObjectInstance::visitablePos() const bool CGObjectInstance::isVisitable() const { - return appearance.isVisitable(); + return appearance->isVisitable(); } bool CGObjectInstance::passableFor(PlayerColor color) const @@ -370,7 +375,7 @@ void CGObjectInstance::serializeJson(JsonSerializeFormat & handler) handler.serializeInt("y", pos.y); handler.serializeInt("l", pos.z); JsonNode app; - appearance.writeJson(app, false); + appearance->writeJson(app, false); handler.serializeRaw("template",app, boost::none); } diff --git a/lib/mapObjects/CObjectHandler.h b/lib/mapObjects/CObjectHandler.h index be8890b9b..10f0761a0 100644 --- a/lib/mapObjects/CObjectHandler.h +++ b/lib/mapObjects/CObjectHandler.h @@ -123,7 +123,7 @@ public: /// Index of object in map's list of objects ObjectInstanceID id; /// Defines appearance of object on map (animation, blocked tiles, blit order, etc) - ObjectTemplate appearance; + std::shared_ptr appearance; /// If true hero can visit this object only from neighbouring tiles and can't stand on this object bool blockVisit; diff --git a/lib/mapObjects/CRewardableConstructor.cpp b/lib/mapObjects/CRewardableConstructor.cpp index 971913571..21219f3f0 100644 --- a/lib/mapObjects/CRewardableConstructor.cpp +++ b/lib/mapObjects/CRewardableConstructor.cpp @@ -183,7 +183,7 @@ void CRewardableConstructor::initTypeData(const JsonNode & config) objectInfo.init(config); } -CGObjectInstance * CRewardableConstructor::create(const ObjectTemplate & tmpl) const +CGObjectInstance * CRewardableConstructor::create(std::shared_ptr tmpl) const { auto ret = new CRewardableObject(); preInitObject(ret); @@ -196,7 +196,7 @@ void CRewardableConstructor::configureObject(CGObjectInstance * object, CRandomG objectInfo.configureObject(dynamic_cast(object), rng); } -std::unique_ptr CRewardableConstructor::getObjectInfo(const ObjectTemplate & tmpl) const +std::unique_ptr CRewardableConstructor::getObjectInfo(std::shared_ptr tmpl) const { return std::unique_ptr(new CRandomRewardObjectInfo(objectInfo)); } diff --git a/lib/mapObjects/CRewardableConstructor.h b/lib/mapObjects/CRewardableConstructor.h index 94c047bb6..4157ae7cb 100644 --- a/lib/mapObjects/CRewardableConstructor.h +++ b/lib/mapObjects/CRewardableConstructor.h @@ -49,9 +49,9 @@ class DLL_LINKAGE CRewardableConstructor : public AObjectTypeHandler public: CRewardableConstructor(); - CGObjectInstance * create(const ObjectTemplate & tmpl) const override; + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; - std::unique_ptr getObjectInfo(const ObjectTemplate & tmpl) const override; + std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; }; diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 638d61bc7..4caa94198 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -1122,7 +1122,7 @@ std::vector CGMagicSpring::getVisitableOffsets() const for(int y = 0; y < 6; y++) for (int x = 0; x < 8; x++) //starting from left - if (appearance.isVisitableAt(x, y)) + if (appearance->isVisitableAt(x, y)) visitableTiles.push_back (int3(x, y , 0)); return visitableTiles; diff --git a/lib/mapObjects/CommonConstructors.cpp b/lib/mapObjects/CommonConstructors.cpp index 6809175e5..3b870847a 100644 --- a/lib/mapObjects/CommonConstructors.cpp +++ b/lib/mapObjects/CommonConstructors.cpp @@ -56,7 +56,7 @@ void CTownInstanceConstructor::afterLoadFinalization() } } -bool CTownInstanceConstructor::objectFilter(const CGObjectInstance * object, const ObjectTemplate & templ) const +bool CTownInstanceConstructor::objectFilter(const CGObjectInstance * object, std::shared_ptr templ) const { auto town = dynamic_cast(object); @@ -65,10 +65,10 @@ bool CTownInstanceConstructor::objectFilter(const CGObjectInstance * object, con return town->hasBuilt(id); }; - return filters.count(templ.stringID) != 0 && filters.at(templ.stringID).test(buildTest); + return filters.count(templ->stringID) != 0 && filters.at(templ->stringID).test(buildTest); } -CGObjectInstance * CTownInstanceConstructor::create(const ObjectTemplate & tmpl) const +CGObjectInstance * CTownInstanceConstructor::create(std::shared_ptr tmpl) const { CGTownInstance * obj = createTyped(tmpl); obj->town = faction->town; @@ -80,7 +80,7 @@ void CTownInstanceConstructor::configureObject(CGObjectInstance * object, CRando { auto templ = getOverride(object->cb->getTile(object->pos)->terType, object); if(templ) - object->appearance = templ.get(); + object->appearance = templ; } CHeroInstanceConstructor::CHeroInstanceConstructor() @@ -110,7 +110,7 @@ void CHeroInstanceConstructor::afterLoadFinalization() } } -bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, const ObjectTemplate & templ) const +bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, std::shared_ptr templ) const { auto hero = dynamic_cast(object); @@ -119,14 +119,14 @@ bool CHeroInstanceConstructor::objectFilter(const CGObjectInstance * object, con return hero->type->ID == id; }; - if(filters.count(templ.stringID)) + if(filters.count(templ->stringID)) { - return filters.at(templ.stringID).test(heroTest); + return filters.at(templ->stringID).test(heroTest); } return false; } -CGObjectInstance * CHeroInstanceConstructor::create(const ObjectTemplate & tmpl) const +CGObjectInstance * CHeroInstanceConstructor::create(std::shared_ptr tmpl) const { CGHeroInstance * obj = createTyped(tmpl); obj->type = nullptr; //FIXME: set to valid value. somehow. @@ -167,12 +167,12 @@ void CDwellingInstanceConstructor::initTypeData(const JsonNode & input) guards = input["guards"]; } -bool CDwellingInstanceConstructor::objectFilter(const CGObjectInstance *, const ObjectTemplate &) const +bool CDwellingInstanceConstructor::objectFilter(const CGObjectInstance *, std::shared_ptr) const { return false; } -CGObjectInstance * CDwellingInstanceConstructor::create(const ObjectTemplate & tmpl) const +CGObjectInstance * CDwellingInstanceConstructor::create(std::shared_ptr tmpl) const { CGDwelling * obj = createTyped(tmpl); @@ -272,7 +272,7 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input) bankResetDuration = static_cast(input["resetDuration"].Float()); } -CGObjectInstance *CBankInstanceConstructor::create(const ObjectTemplate & tmpl) const +CGObjectInstance *CBankInstanceConstructor::create(std::shared_ptr tmpl) const { return createTyped(tmpl); } @@ -494,7 +494,7 @@ bool CBankInfo::givesSpells() const } -std::unique_ptr CBankInstanceConstructor::getObjectInfo(const ObjectTemplate & tmpl) const +std::unique_ptr CBankInstanceConstructor::getObjectInfo(std::shared_ptr tmpl) const { return std::unique_ptr(new CBankInfo(levels)); } diff --git a/lib/mapObjects/CommonConstructors.h b/lib/mapObjects/CommonConstructors.h index 1dc6ec5d1..605ad429c 100644 --- a/lib/mapObjects/CommonConstructors.h +++ b/lib/mapObjects/CommonConstructors.h @@ -26,17 +26,31 @@ template class CDefaultObjectTypeHandler : public AObjectTypeHandler { protected: - ObjectType * createTyped(const ObjectTemplate & tmpl) const + ObjectType * createTyped(std::shared_ptr tmpl /* = nullptr */) const { auto obj = new ObjectType(); preInitObject(obj); - obj->appearance = tmpl; + + if (tmpl) + { + obj->appearance = tmpl; + } + else + { + auto templates = getTemplates(); + if (templates.empty()) + { + throw std::runtime_error("No handler for created object"); + } + obj->appearance = templates.front(); //just any template for now, will be initialized later + } + return obj; } public: CDefaultObjectTypeHandler() {} - CGObjectInstance * create(const ObjectTemplate & tmpl) const override + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override { return createTyped(tmpl); } @@ -45,7 +59,7 @@ public: { } - virtual std::unique_ptr getObjectInfo(const ObjectTemplate & tmpl) const override + virtual std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override { return nullptr; } @@ -62,7 +76,7 @@ class CTownInstanceConstructor : public CDefaultObjectTypeHandler) const override; void initTypeData(const JsonNode & input) override; public: @@ -70,7 +84,7 @@ public: std::map> filters; CTownInstanceConstructor(); - CGObjectInstance * create(const ObjectTemplate & tmpl) const override; + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; void afterLoadFinalization() override; @@ -87,7 +101,7 @@ class CHeroInstanceConstructor : public CDefaultObjectTypeHandler) const override; void initTypeData(const JsonNode & input) override; public: @@ -95,7 +109,7 @@ public: std::map> filters; CHeroInstanceConstructor(); - CGObjectInstance * create(const ObjectTemplate & tmpl) const override; + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; void afterLoadFinalization() override; @@ -115,13 +129,13 @@ class CDwellingInstanceConstructor : public CDefaultObjectTypeHandler tmpl) const override; void initTypeData(const JsonNode & input) override; public: CDwellingInstanceConstructor(); - CGObjectInstance * create(const ObjectTemplate & tmpl) const override; + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; bool producesCreature(const CCreature * crea) const; @@ -207,10 +221,10 @@ public: CBankInstanceConstructor(); - CGObjectInstance * create(const ObjectTemplate & tmpl) const override; + CGObjectInstance * create(std::shared_ptr tmpl = nullptr) const override; void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override; - std::unique_ptr getObjectInfo(const ObjectTemplate & tmpl) const override; + std::unique_ptr getObjectInfo(std::shared_ptr tmpl) const override; template void serialize(Handler &h, const int version) { diff --git a/lib/mapObjects/ObjectTemplate.cpp b/lib/mapObjects/ObjectTemplate.cpp index deaa1f63a..53b410de4 100644 --- a/lib/mapObjects/ObjectTemplate.cpp +++ b/lib/mapObjects/ObjectTemplate.cpp @@ -53,7 +53,9 @@ ObjectTemplate::ObjectTemplate(): id(Obj::NO_OBJ), subid(0), printPriority(0), - stringID("") + width(0), + height(0), + visitable(false) { } @@ -65,7 +67,13 @@ ObjectTemplate::ObjectTemplate(const ObjectTemplate& other): printPriority(other.printPriority), animationFile(other.animationFile), editorAnimationFile(other.editorAnimationFile), - stringID(other.stringID) + stringID(other.stringID), + width(other.width), + height(other.height), + visitable(other.visitable), + blockedOffsets(other.blockedOffsets), + blockMapOffset(other.blockMapOffset), + visitableOffset(other.visitableOffset) { //default copy constructor is failing with usedTiles this for unknown reason @@ -84,11 +92,18 @@ ObjectTemplate & ObjectTemplate::operator=(const ObjectTemplate & rhs) animationFile = rhs.animationFile; editorAnimationFile = rhs.editorAnimationFile; stringID = rhs.stringID; + width = rhs.width; + height = rhs.height; + visitable = rhs.visitable; + blockedOffsets = rhs.blockedOffsets; + blockMapOffset = rhs.blockMapOffset; + visitableOffset = rhs.visitableOffset; usedTiles.clear(); usedTiles.resize(rhs.usedTiles.size()); for(size_t i = 0; i < usedTiles.size(); i++) std::copy(rhs.usedTiles[i].begin(), rhs.usedTiles[i].end(), std::back_inserter(usedTiles[i])); + return *this; } @@ -121,9 +136,9 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser) assert(visitStr.size() == 6*8); setSize(8, 6); - for (size_t i=0; i<6; i++) // 6 rows + for(size_t i = 0; i < 6; i++) // 6 rows { - for (size_t j=0; j<8; j++) // 8 columns + for(size_t j = 0; j < 8; j++) // 8 columns { auto & tile = usedTiles[i][j]; tile |= VISIBLE; // assume that all tiles are visible @@ -141,7 +156,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser) std::string & terrStr = strings[4]; // allowed terrains, 1 = object can be placed on this terrain assert(terrStr.size() == 9); // all terrains but rock - for (size_t i=0; i<9; i++) + for(size_t i = 0; i < 9; i++) { if (terrStr[8-i] == '1') allowedTerrains.insert(Terrain::createTerrainTypeH3M(i)); @@ -168,6 +183,7 @@ void ObjectTemplate::readTxt(CLegacyConfigParser & parser) visitDir = (8|16|32|64|128); readMsk(); + recalculate(); } void ObjectTemplate::readMsk() @@ -197,9 +213,9 @@ void ObjectTemplate::readMap(CBinaryReader & reader) for(auto & byte : visitMask) byte = reader.readUInt8(); - for (size_t i=0; i<6; i++) // 6 rows + for(size_t i = 0; i < 6; i++) // 6 rows { - for (size_t j=0; j<8; j++) // 8 columns + for(size_t j = 0; j < 8; j++) // 8 columns { auto & tile = usedTiles[5 - i][7 - j]; tile |= VISIBLE; // assume that all tiles are visible @@ -213,7 +229,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader) reader.readUInt16(); ui16 terrMask = reader.readUInt16(); - for (size_t i=0; i<9; i++) + for(size_t i = 0; i < 9; i++) { if (((terrMask >> i) & 1 ) != 0) allowedTerrains.insert(Terrain::createTerrainTypeH3M(i)); @@ -243,6 +259,7 @@ void ObjectTemplate::readMap(CBinaryReader & reader) readMsk(); afterLoadFixup(); + recalculate(); } void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain) @@ -288,16 +305,16 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain) { switch (ch) { - case ' ' : return 0; - case '0' : return 0; - case 'V' : return VISIBLE; - case 'B' : return VISIBLE | BLOCKED; - case 'H' : return BLOCKED; - case 'A' : return VISIBLE | BLOCKED | VISITABLE; - case 'T' : return BLOCKED | VISITABLE; - default: - logGlobal->error("Unrecognized char %s in template mask", ch); - return 0; + case ' ' : return 0; + case '0' : return 0; + case 'V' : return VISIBLE; + case 'B' : return VISIBLE | BLOCKED; + case 'H' : return BLOCKED; + case 'A' : return VISIBLE | BLOCKED | VISITABLE; + case 'T' : return BLOCKED | VISITABLE; + default: + logGlobal->error("Unrecognized char %s in template mask", ch); + return 0; } }; @@ -320,6 +337,7 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain) printPriority = static_cast(node["zIndex"].Float()); afterLoadFixup(); + recalculate(); } void ObjectTemplate::writeJson(JsonNode & node, const bool withTerrain) const @@ -413,38 +431,42 @@ void ObjectTemplate::writeJson(JsonNode & node, const bool withTerrain) const node["zIndex"].Float() = printPriority; } -ui32 ObjectTemplate::getWidth() const +void ObjectTemplate::calculateWidth() { //TODO: Use 2D array - //TODO: better precalculate and store constant value - ui32 ret = 0; - for (const auto &row : usedTiles) //copy is expensive + for(const auto& row : usedTiles) //copy is expensive { - ret = std::max(ret, (ui32)row.size()); + width = std::max(width, (ui32)row.size()); } - return ret; } -ui32 ObjectTemplate::getHeight() const +void ObjectTemplate::calculateHeight() { //TODO: Use 2D array - return static_cast(usedTiles.size()); + height = static_cast(usedTiles.size()); } void ObjectTemplate::setSize(ui32 width, ui32 height) { usedTiles.resize(height); - for (auto & line : usedTiles) + for(auto & line : usedTiles) line.resize(width, 0); } -bool ObjectTemplate::isVisitable() const +void ObjectTemplate::calculateVsitable() { - for (auto & line : usedTiles) - for (auto & tile : line) + for(auto& line : usedTiles) + { + for(auto& tile : line) + { if (tile & VISITABLE) - return true; - return false; + { + visitable = true; + return; + } + } + } + visitable = false; } bool ObjectTemplate::isWithin(si32 X, si32 Y) const @@ -469,31 +491,33 @@ bool ObjectTemplate::isBlockedAt(si32 X, si32 Y) const return isWithin(X, Y) && usedTiles[Y][X] & BLOCKED; } -std::set ObjectTemplate::getBlockedOffsets() const +void ObjectTemplate::calculateBlockedOffsets() { - std::set ret; + blockedOffsets.clear(); for(int w = 0; w < (int)getWidth(); ++w) { for(int h = 0; h < (int)getHeight(); ++h) { if (isBlockedAt(w, h)) - ret.insert(int3(-w, -h, 0)); + blockedOffsets.insert(int3(-w, -h, 0)); } } - return ret; } -int3 ObjectTemplate::getBlockMapOffset() const +void ObjectTemplate::calculateBlockMapOffset() { for(int w = 0; w < (int)getWidth(); ++w) { for(int h = 0; h < (int)getHeight(); ++h) { if (isBlockedAt(w, h)) - return int3(w, h, 0); + { + blockMapOffset = int3(w, h, 0); + return; + } } } - return int3(0,0,0); + blockMapOffset = int3(0, 0, 0); } bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const @@ -502,6 +526,7 @@ bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const // 1 2 3 // 8 4 // 7 6 5 + //TODO: static? cached? int dirMap[3][3] = { { visitDir & 1, visitDir & 2, visitDir & 4 }, @@ -515,22 +540,20 @@ bool ObjectTemplate::isVisitableFrom(si8 X, si8 Y) const return dirMap[dy][dx] != 0; } -int3 ObjectTemplate::getVisitableOffset() const +void ObjectTemplate::calculateVisitableOffset() { for(int y = 0; y < (int)getHeight(); y++) - for (int x = 0; x < (int)getWidth(); x++) + { + for(int x = 0; x < (int)getWidth(); x++) + { if (isVisitableAt(x, y)) - return int3(x,y,0); - - //logGlobal->warn("Warning: getVisitableOffset called on non-visitable obj!"); - return int3(0,0,0); -} - -bool ObjectTemplate::isVisitableFromTop() const -{ - return visitDir & 2; - //for some reason the line below is never called :? - //return isVisitableFrom (0, 1); + { + visitableOffset = int3(x, y, 0); + return; + } + } + } + visitableOffset = int3(0, 0, 0); } bool ObjectTemplate::canBePlacedAt(Terrain terrain) const @@ -538,3 +561,14 @@ bool ObjectTemplate::canBePlacedAt(Terrain terrain) const return allowedTerrains.count(terrain) != 0; } +void ObjectTemplate::recalculate() +{ + calculateWidth(); + calculateHeight(); + calculateVsitable(); + //The lines below use width and height + calculateBlockedOffsets(); + calculateBlockMapOffset(); + calculateVisitableOffset(); +} + diff --git a/lib/mapObjects/ObjectTemplate.h b/lib/mapObjects/ObjectTemplate.h index 080561f2d..e10f1efad 100644 --- a/lib/mapObjects/ObjectTemplate.h +++ b/lib/mapObjects/ObjectTemplate.h @@ -10,6 +10,7 @@ #pragma once #include "../GameConstants.h" +#include "../int3.h" class CBinaryReader; class CLegacyConfigParser; @@ -50,11 +51,22 @@ public: /// string ID, equals to def base name for h3m files (lower case, no extension) or specified in mod data std::string stringID; - ui32 getWidth() const; - ui32 getHeight() const; + inline ui32 getWidth() const + { + return width; + }; + + inline ui32 getHeight() const + { + return height; + }; + void setSize(ui32 width, ui32 height); - bool isVisitable() const; + inline bool isVisitable() const + { + return visitable; + }; // Checks object used tiles // Position is relative to bottom-right corner of the object, can not be negative @@ -62,13 +74,29 @@ public: bool isVisitableAt(si32 X, si32 Y) const; bool isVisibleAt(si32 X, si32 Y) const; bool isBlockedAt(si32 X, si32 Y) const; - std::set getBlockedOffsets() const; - int3 getBlockMapOffset() const; //bottom-right corner when firts blocked tile is + + inline std::set getBlockedOffsets() const + { + return blockedOffsets; + }; + + inline int3 getBlockMapOffset() const + { + return blockMapOffset; + }; // Checks if object is visitable from certain direction. X and Y must be between -1..+1 bool isVisitableFrom(si8 X, si8 Y) const; - int3 getVisitableOffset() const; - bool isVisitableFromTop() const; + inline int3 getVisitableOffset() const + { + //logGlobal->warn("Warning: getVisitableOffset called on non-visitable obj!"); + return visitableOffset; + }; + + inline bool isVisitableFromTop() const + { + return visitDir & 2; + }; // Checks if object can be placed on specific terrain bool canBePlacedAt(Terrain terrain) const; @@ -87,6 +115,25 @@ public: bool operator==(const ObjectTemplate& ot) const { return (id == ot.id && subid == ot.subid); } +private: + ui32 width; + ui32 height; + bool visitable; + + std::set blockedOffsets; + int3 blockMapOffset; + int3 visitableOffset; + + void recalculate(); + + void calculateWidth(); + void calculateHeight(); + void calculateVsitable(); + void calculateBlockedOffsets(); + void calculateBlockMapOffset(); + void calculateVisitableOffset(); + +public: template void serialize(Handler &h, const int version) { h & usedTiles; @@ -98,6 +145,11 @@ public: h & printPriority; h & visitDir; h & editorAnimationFile; + + if (!h.saving) + { + recalculate(); + } } }; diff --git a/lib/mapping/CCampaignHandler.h b/lib/mapping/CCampaignHandler.h index ea1dd6f7f..1e3e923c1 100644 --- a/lib/mapping/CCampaignHandler.h +++ b/lib/mapping/CCampaignHandler.h @@ -146,7 +146,7 @@ public: // FIXME: due to usage of JsonNode I can't make these methods const const CGHeroInstance * strongestHero(PlayerColor owner); std::vector getLostCrossoverHeroes(); /// returns a list of crossover heroes which started the scenario, but didn't complete it - + CCampaignScenario(); template void serialize(Handler &h, const int formatVersion) diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index 01c851a20..647e97708 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -418,7 +418,7 @@ bool CMap::checkForVisitableDir(const int3 & src, const TerrainTile *pom, const if(!vstd::contains(pom->blockingObjects, obj)) //this visitable object is not blocking, ignore continue; - if (!obj->appearance.isVisitableFrom(src.x - dst.x, src.y - dst.y)) + if (!obj->appearance->isVisitableFrom(src.x - dst.x, src.y - dst.y)) return false; } return true; diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 2b93ab8a7..099c999c9 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -491,14 +491,5 @@ public: h & CGTownInstance::universitySkills; h & instanceNames; - - if (!h.saving && formatVersion < 804) - { - uidCounter = objects.size(); - } - else - { - h & uidCounter; - } } }; diff --git a/lib/mapping/CMapEditManager.cpp b/lib/mapping/CMapEditManager.cpp index faa2b9dc1..76315ea1a 100644 --- a/lib/mapping/CMapEditManager.cpp +++ b/lib/mapping/CMapEditManager.cpp @@ -198,4 +198,3 @@ CObjectSelection & CMapEditManager::getObjectSelection() CMapUndoManager & CMapEditManager::getUndoManager() { return undoManager; -} \ No newline at end of file diff --git a/lib/mapping/CMapEditManager.h b/lib/mapping/CMapEditManager.h index 93dcaf705..6d5bc2e98 100644 --- a/lib/mapping/CMapEditManager.h +++ b/lib/mapping/CMapEditManager.h @@ -12,6 +12,7 @@ #include "../GameConstants.h" #include "CMapOperation.h" +#include "Terrain.h" class CGObjectInstance; class CTerrainViewPatternConfig; diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 9030e9737..d960975fc 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -958,9 +958,9 @@ void CMapLoaderH3M::readDefInfo() // Read custom defs for(int idd = 0; idd < defAmount; ++idd) { - ObjectTemplate tmpl; - tmpl.readMap(reader); - templates.push_back(tmpl); + auto tmpl = new ObjectTemplate; + tmpl->readMap(reader); + templates.push_back(std::shared_ptr(tmpl)); } } @@ -977,10 +977,10 @@ void CMapLoaderH3M::readObjects() int defnum = reader.readUInt32(); ObjectInstanceID idToBeGiven = ObjectInstanceID((si32)map->objects.size()); - ObjectTemplate & objTempl = templates.at(defnum); + std::shared_ptr objTempl = templates.at(defnum); reader.skip(5); - switch(objTempl.id) + switch(objTempl->id) { case Obj::EVENT: { @@ -1212,15 +1212,15 @@ void CMapLoaderH3M::readObjects() readMessageAndGuards(art->message, art); - if(objTempl.id == Obj::SPELL_SCROLL) + if(objTempl->id == Obj::SPELL_SCROLL) { spellID = reader.readUInt32(); artID = ArtifactID::SPELL_SCROLL; } - else if(objTempl.id == Obj::ARTIFACT) + else if(objTempl->id == Obj::ARTIFACT) { //specific artifact - artID = objTempl.subid; + artID = objTempl->subid; } art->storedArtifact = CArtifactInstance::createArtifact(map, artID, spellID); @@ -1235,7 +1235,7 @@ void CMapLoaderH3M::readObjects() readMessageAndGuards(res->message, res); res->amount = reader.readUInt32(); - if(objTempl.subid == Res::GOLD) + if(objTempl->subid == Res::GOLD) { // Gold is multiplied by 100. res->amount *= 100; @@ -1246,7 +1246,7 @@ void CMapLoaderH3M::readObjects() case Obj::RANDOM_TOWN: case Obj::TOWN: { - nobj = readTown(objTempl.subid); + nobj = readTown(objTempl->subid); break; } case Obj::MINE: @@ -1347,7 +1347,7 @@ void CMapLoaderH3M::readObjects() auto dwelling = new CGDwelling(); nobj = dwelling; CSpecObjInfo * spec = nullptr; - switch(objTempl.id) + switch(objTempl->id) { case Obj::RANDOM_DWELLING: spec = new CCreGenLeveledCastleInfo(); @@ -1450,7 +1450,7 @@ void CMapLoaderH3M::readObjects() } case Obj::PYRAMID: //Pyramid of WoG object { - if(objTempl.subid == 0) + if(objTempl->subid == 0) { nobj = new CBank(); } @@ -1470,13 +1470,13 @@ void CMapLoaderH3M::readObjects() } default: //any other object { - if (VLC->objtypeh->knownSubObjects(objTempl.id).count(objTempl.subid)) + if (VLC->objtypeh->knownSubObjects(objTempl->id).count(objTempl->subid)) { - nobj = VLC->objtypeh->getHandlerFor(objTempl.id, objTempl.subid)->create(objTempl); + nobj = VLC->objtypeh->getHandlerFor(objTempl->id, objTempl->subid)->create(objTempl); } else { - logGlobal->warn("Unrecognized object: %d:%d at %s on map %s", objTempl.id.toEnum(), objTempl.subid, objPos.toString(), map->name); + logGlobal->warn("Unrecognized object: %d:%d at %s on map %s", objTempl->id.toEnum(), objTempl->subid, objPos.toString(), map->name); nobj = new CGObjectInstance(); } break; @@ -1484,11 +1484,11 @@ void CMapLoaderH3M::readObjects() } nobj->pos = objPos; - nobj->ID = objTempl.id; + nobj->ID = objTempl->id; nobj->id = idToBeGiven; if(nobj->ID != Obj::HERO && nobj->ID != Obj::HERO_PLACEHOLDER && nobj->ID != Obj::PRISON) { - nobj->subID = objTempl.subid; + nobj->subID = objTempl->subid; } nobj->appearance = objTempl; assert(idToBeGiven == ObjectInstanceID((si32)map->objects.size())); diff --git a/lib/mapping/MapFormatH3M.h b/lib/mapping/MapFormatH3M.h index 563932bd4..1a71f2d1d 100644 --- a/lib/mapping/MapFormatH3M.h +++ b/lib/mapping/MapFormatH3M.h @@ -245,7 +245,7 @@ private: /** List of templates loaded from the map, used on later stage to create * objects but not needed for fully functional CMap */ - std::vector templates; + std::vector> templates; /** ptr to the map object which gets filled by data from the buffer */ CMap * map; diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index ff08ba2c5..e14de101b 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -1110,13 +1110,14 @@ void CMapLoaderJson::MapObjectLoader::construct() auto handler = VLC->objtypeh->getHandlerFor(typeName, subtypeName); - ObjectTemplate appearance; + auto appearance = new ObjectTemplate; - appearance.id = Obj(handler->type); - appearance.subid = handler->subtype; - appearance.readJson(configuration["template"], false); + appearance->id = Obj(handler->type); + appearance->subid = handler->subtype; + appearance->readJson(configuration["template"], false); - instance = handler->create(appearance); + // Will be destroyed soon and replaced with shared template + instance = handler->create(std::shared_ptr(appearance)); instance->id = ObjectInstanceID((si32)owner->map->objects.size()); instance->instanceName = jsonKey; diff --git a/lib/rmg/ConnectionsPlacer.cpp b/lib/rmg/ConnectionsPlacer.cpp index 78aa17984..bd4633398 100644 --- a/lib/rmg/ConnectionsPlacer.cpp +++ b/lib/rmg/ConnectionsPlacer.cpp @@ -204,8 +204,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c auto & managerOther = *otherZone->getModificator(); auto factory = VLC->objtypeh->getHandlerFor(Obj::SUBTERRANEAN_GATE, 0); - auto gate1 = factory->create(ObjectTemplate()); - auto gate2 = factory->create(ObjectTemplate()); + auto gate1 = factory->create(); + auto gate2 = factory->create(); rmg::Object rmgGate1(*gate1), rmgGate2(*gate2); rmgGate1.setTemplate(zone.getTerrainType()); rmgGate2.setTemplate(otherZone->getTerrainType()); @@ -249,8 +249,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c if(!success) { auto factory = VLC->objtypeh->getHandlerFor(Obj::MONOLITH_TWO_WAY, generator.getNextMonlithIndex()); - auto teleport1 = factory->create(ObjectTemplate()); - auto teleport2 = factory->create(ObjectTemplate()); + auto teleport1 = factory->create(); + auto teleport2 = factory->create(); zone.getModificator()->addRequiredObject(teleport1, connection.getGuardStrength()); otherZone->getModificator()->addRequiredObject(teleport2, connection.getGuardStrength()); diff --git a/lib/rmg/ObjectManager.cpp b/lib/rmg/ObjectManager.cpp index b995941d9..875b9974a 100644 --- a/lib/rmg/ObjectManager.cpp +++ b/lib/rmg/ObjectManager.cpp @@ -367,7 +367,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD objects.push_back(&instance->object()); if(auto * m = zone.getModificator()) { - if(instance->object().appearance.isVisitableFromTop()) + if(instance->object().appearance->isVisitableFromTop()) m->areaForRoads().add(instance->getVisitablePosition()); else { @@ -449,7 +449,7 @@ CGCreature * ObjectManager::chooseGuard(si32 strength, bool zoneGuard) auto guardFactory = VLC->objtypeh->getHandlerFor(Obj::MONSTER, creId); - auto guard = (CGCreature *) guardFactory->create(ObjectTemplate()); + auto guard = (CGCreature *) guardFactory->create(); guard->character = CGCreature::HOSTILE; auto hlp = new CStackInstance(creId, amount); //will be set during initialization diff --git a/lib/rmg/ObstaclePlacer.cpp b/lib/rmg/ObstaclePlacer.cpp index fe9ba8536..24d259511 100644 --- a/lib/rmg/ObstaclePlacer.cpp +++ b/lib/rmg/ObstaclePlacer.cpp @@ -216,7 +216,7 @@ void ObstaclePlacer::process() auto * riverManager = zone.getModificator(); - typedef std::vector ObstacleVector; + typedef std::vector> ObstacleVector; //obstacleVector possibleObstacles; std::map obstaclesBySize; @@ -233,8 +233,8 @@ void ObstaclePlacer::process() { for(auto temp : handler->getTemplates()) { - if(temp.canBePlacedAt(zone.getTerrainType()) && temp.getBlockMapOffset().valid()) - obstaclesBySize[temp.getBlockedOffsets().size()].push_back(temp); + if(temp->canBePlacedAt(zone.getTerrainType()) && temp->getBlockMapOffset().valid()) + obstaclesBySize[temp->getBlockedOffsets().size()].push_back(temp); } } } @@ -278,7 +278,7 @@ void ObstaclePlacer::process() for(auto & temp : shuffledObstacles) { - auto handler = VLC->objtypeh->getHandlerFor(temp.id, temp.subid); + auto handler = VLC->objtypeh->getHandlerFor(temp->id, temp->subid); auto obj = handler->create(temp); allObjects.emplace_back(*obj); rmg::Object * rmgObject = &allObjects.back(); diff --git a/lib/rmg/RiverPlacer.cpp b/lib/rmg/RiverPlacer.cpp index a9086f056..599c1cded 100644 --- a/lib/rmg/RiverPlacer.cpp +++ b/lib/rmg/RiverPlacer.cpp @@ -374,10 +374,10 @@ void RiverPlacer::connectRiver(const int3 & tile) auto handler = VLC->objtypeh->getHandlerFor(RIVER_DELTA_ID, RIVER_DELTA_SUBTYPE); assert(handler->isStaticObject()); - std::vector tmplates; + std::vector> tmplates; for(auto & temp : handler->getTemplates()) { - if(temp.canBePlacedAt(zone.getTerrainType())) + if(temp->canBePlacedAt(zone.getTerrainType())) tmplates.push_back(temp); } @@ -389,7 +389,7 @@ void RiverPlacer::connectRiver(const int3 & tile) std::string targetTemplateName = RIVER_DELTA_TEMPLATE_NAME.at(river) + std::to_string(deltaOrientations[pos]) + ".def"; for(auto & templ : tmplates) { - if(templ.animationFile == targetTemplateName) + if(templ->animationFile == targetTemplateName) { auto obj = handler->create(templ); rmg::Object deltaObj(*obj, deltaPositions[pos]); diff --git a/lib/rmg/RmgObject.cpp b/lib/rmg/RmgObject.cpp index 08d3bf7ac..9635daf53 100644 --- a/lib/rmg/RmgObject.cpp +++ b/lib/rmg/RmgObject.cpp @@ -105,7 +105,7 @@ void Object::Instance::setPositionRaw(const int3 & position) void Object::Instance::setTemplate(const Terrain & terrain) { - if(dObject.appearance.id == Obj::NO_OBJ) + if(dObject.appearance->id == Obj::NO_OBJ) { auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrain); if(templates.empty()) @@ -130,7 +130,7 @@ void Object::Instance::clear() bool Object::Instance::isVisitableFrom(const int3 & position) const { auto relPosition = position - getPosition(true); - return dObject.appearance.isVisitableFrom(relPosition.x, relPosition.y); + return dObject.appearance->isVisitableFrom(relPosition.x, relPosition.y); } CGObjectInstance & Object::Instance::object() @@ -286,7 +286,7 @@ void Object::Instance::finalize(RmgMap & map) throw rmgException(boost::to_string(boost::format("Tile %s of object %d at %s is outside the map") % tile.toString() % dObject.id % dObject.pos.toString())); } - if (dObject.appearance.id == Obj::NO_OBJ) + if (dObject.appearance->id == Obj::NO_OBJ) { auto terrainType = map.map().getTile(getPosition(true)).terType; auto templates = VLC->objtypeh->getHandlerFor(dObject.ID, dObject.subID)->getTemplates(terrainType); diff --git a/lib/rmg/TownPlacer.cpp b/lib/rmg/TownPlacer.cpp index 2e10423b0..d33ae032b 100644 --- a/lib/rmg/TownPlacer.cpp +++ b/lib/rmg/TownPlacer.cpp @@ -69,7 +69,7 @@ void TownPlacer::placeTowns(ObjectManager & manager) auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, zone.getTownType()); - CGTownInstance * town = (CGTownInstance *) townFactory->create(ObjectTemplate()); + CGTownInstance * town = (CGTownInstance *) townFactory->create(); town->tempOwner = player; town->builtBuildings.insert(BuildingID::FORT); town->builtBuildings.insert(BuildingID::DEFAULT); @@ -163,7 +163,7 @@ bool TownPlacer::placeMines(ObjectManager & manager) { auto mineHandler = VLC->objtypeh->getHandlerFor(Obj::MINE, res); auto & rmginfo = mineHandler->getRMGInfo(); - auto mine = (CGMine*)mineHandler->create(ObjectTemplate()); + auto mine = (CGMine*)mineHandler->create(); mine->producedResource = res; mine->tempOwner = PlayerColor::NEUTRAL; mine->producedQuantity = mine->defaultResProduction(); @@ -184,7 +184,7 @@ bool TownPlacer::placeMines(ObjectManager & manager) { for(int rc = generator.rand.nextInt(1, extraRes); rc > 0; --rc) { - auto resourse = (CGResource*) VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create(ObjectTemplate()); + auto resourse = (CGResource*) VLC->objtypeh->getHandlerFor(Obj::RESOURCE, mine->producedResource)->create(); resourse->amount = CGResource::RANDOM_AMOUNT; manager.addNearbyObject(resourse, mine); } @@ -225,7 +225,7 @@ void TownPlacer::addNewTowns(int count, bool hasFort, PlayerColor player, Object } auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, subType); - auto town = (CGTownInstance *) townFactory->create(ObjectTemplate()); + auto town = (CGTownInstance *) townFactory->create(); town->ID = Obj::TOWN; town->tempOwner = player; diff --git a/lib/rmg/TreasurePlacer.cpp b/lib/rmg/TreasurePlacer.cpp index 7e24f2d28..cf1d58abb 100644 --- a/lib/rmg/TreasurePlacer.cpp +++ b/lib/rmg/TreasurePlacer.cpp @@ -59,11 +59,11 @@ void TreasurePlacer::addAllPossibleObjects() { for(auto temp : handler->getTemplates()) { - if(temp.canBePlacedAt(zone.getTerrainType())) + if(temp->canBePlacedAt(zone.getTerrainType())) { oi.generateObject = [temp]() -> CGObjectInstance * { - return VLC->objtypeh->getHandlerFor(temp.id, temp.subid)->create(temp); + return VLC->objtypeh->getHandlerFor(temp->id, temp->subid)->create(temp); }; auto rmgInfo = handler->getRMGInfo(); oi.value = rmgInfo.value; @@ -97,7 +97,7 @@ void TreasurePlacer::addAllPossibleObjects() auto hid = *RandomGeneratorUtil::nextItem(possibleHeroes, generator.rand); auto factory = VLC->objtypeh->getHandlerFor(Obj::PRISON, 0); - auto obj = (CGHeroInstance *) factory->create(ObjectTemplate()); + auto obj = (CGHeroInstance *) factory->create(); obj->subID = hid; //will be initialized later @@ -159,7 +159,7 @@ void TreasurePlacer::addAllPossibleObjects() for(auto tmplate : dwellingHandler->getTemplates()) { - if(tmplate.canBePlacedAt(zone.getTerrainType())) + if(tmplate->canBePlacedAt(zone.getTerrainType())) { oi.generateObject = [tmplate, secondaryID, dwellingType]() -> CGObjectInstance * { @@ -181,7 +181,7 @@ void TreasurePlacer::addAllPossibleObjects() oi.generateObject = [i, this]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::SPELL_SCROLL, 0); - auto obj = (CGArtifact *) factory->create(ObjectTemplate()); + auto obj = (CGArtifact *) factory->create(); std::vector out; for(auto spell : VLC->spellh->objects) //spellh size appears to be greater (?) @@ -207,7 +207,7 @@ void TreasurePlacer::addAllPossibleObjects() oi.generateObject = [i]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); - auto obj = (CGPandoraBox *) factory->create(ObjectTemplate()); + auto obj = (CGPandoraBox *) factory->create(); obj->resources[Res::GOLD] = i * 5000; return obj; }; @@ -223,7 +223,7 @@ void TreasurePlacer::addAllPossibleObjects() oi.generateObject = [i]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); - auto obj = (CGPandoraBox *) factory->create(ObjectTemplate()); + auto obj = (CGPandoraBox *) factory->create(); obj->gainedExp = i * 5000; return obj; }; @@ -275,7 +275,7 @@ void TreasurePlacer::addAllPossibleObjects() oi.generateObject = [creature, creaturesAmount]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); - auto obj = (CGPandoraBox *) factory->create(ObjectTemplate()); + auto obj = (CGPandoraBox *) factory->create(); auto stack = new CStackInstance(creature, creaturesAmount); obj->creatures.putStack(SlotID(0), stack); return obj; @@ -292,7 +292,7 @@ void TreasurePlacer::addAllPossibleObjects() oi.generateObject = [i, this]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); - auto obj = (CGPandoraBox *) factory->create(ObjectTemplate()); + auto obj = (CGPandoraBox *) factory->create(); std::vector spells; for(auto spell : VLC->spellh->objects) @@ -321,7 +321,7 @@ void TreasurePlacer::addAllPossibleObjects() oi.generateObject = [i, this]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); - auto obj = (CGPandoraBox *) factory->create(ObjectTemplate()); + auto obj = (CGPandoraBox *) factory->create(); std::vector spells; for(auto spell : VLC->spellh->objects) @@ -349,7 +349,7 @@ void TreasurePlacer::addAllPossibleObjects() oi.generateObject = [this]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); - auto obj = (CGPandoraBox *) factory->create(ObjectTemplate()); + auto obj = (CGPandoraBox *) factory->create(); std::vector spells; for(auto spell : VLC->spellh->objects) @@ -421,7 +421,7 @@ void TreasurePlacer::addAllPossibleObjects() oi.generateObject = [creature, creaturesAmount, randomAppearance, this, generateArtInfo]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); - auto obj = (CGSeerHut *) factory->create(ObjectTemplate()); + auto obj = (CGSeerHut *) factory->create(); obj->rewardType = CGSeerHut::CREATURE; obj->rID = creature->idNumber; obj->rVal = creaturesAmount; @@ -457,7 +457,7 @@ void TreasurePlacer::addAllPossibleObjects() oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); - auto obj = (CGSeerHut *) factory->create(ObjectTemplate()); + auto obj = (CGSeerHut *) factory->create(); obj->rewardType = CGSeerHut::EXPERIENCE; obj->rID = 0; //unitialized? @@ -481,7 +481,7 @@ void TreasurePlacer::addAllPossibleObjects() oi.generateObject = [i, randomAppearance, this, generateArtInfo]() -> CGObjectInstance * { auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); - auto obj = (CGSeerHut *) factory->create(ObjectTemplate()); + auto obj = (CGSeerHut *) factory->create(); obj->rewardType = CGSeerHut::RESOURCES; obj->rID = Res::GOLD; obj->rVal = generator.getConfig().questRewardValues[i]; @@ -525,7 +525,7 @@ std::vector TreasurePlacer::prepareTreasurePile(const CTreasureInfo if(!oi) //fail break; - if(oi->templ.isVisitableFromTop()) + if(oi->templ->isVisitableFromTop()) { objectInfos.push_back(oi); } @@ -599,7 +599,7 @@ rmg::Object TreasurePlacer::constructTreasurePile(const std::vector auto instanceAccessibleArea = instance.getAccessibleArea(); if(instance.getBlockedArea().getTilesVector().size() == 1) { - if(instance.object().appearance.isVisitableFromTop() && instance.object().ID != Obj::CORPSE) + if(instance.object().appearance->isVisitableFromTop() && instance.object().ID != Obj::CORPSE) instanceAccessibleArea.add(instance.getVisitablePosition()); } @@ -632,7 +632,7 @@ ObjectInfo * TreasurePlacer::getRandomObject(ui32 desiredValue, ui32 currentValu if(oi.value > maxVal) break; //this assumes values are sorted in ascending order - if(!oi.templ.isVisitableFromTop() && !allowLargeObjects) + if(!oi.templ->isVisitableFromTop() && !allowLargeObjects) continue; if(oi.value >= minValue && oi.maxPerZone > 0) diff --git a/lib/rmg/TreasurePlacer.h b/lib/rmg/TreasurePlacer.h index b3ecf39f7..457ef35a8 100644 --- a/lib/rmg/TreasurePlacer.h +++ b/lib/rmg/TreasurePlacer.h @@ -19,7 +19,7 @@ class CMapGenerator; struct ObjectInfo { - ObjectTemplate templ; + std::shared_ptr templ; ui32 value = 0; ui16 probability = 0; ui32 maxPerZone = -1; diff --git a/lib/rmg/WaterProxy.cpp b/lib/rmg/WaterProxy.cpp index 5b5354c45..777cf8c11 100644 --- a/lib/rmg/WaterProxy.cpp +++ b/lib/rmg/WaterProxy.cpp @@ -200,7 +200,7 @@ bool WaterProxy::placeBoat(Zone & land, const Lake & lake, RouteInfo & info) return false; auto subObjects = VLC->objtypeh->knownSubObjects(Obj::BOAT); - auto* boat = (CGBoat*)VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(subObjects, generator.rand))->create(ObjectTemplate()); + auto* boat = (CGBoat*)VLC->objtypeh->getHandlerFor(Obj::BOAT, *RandomGeneratorUtil::nextItem(subObjects, generator.rand))->create(); rmg::Object rmgObject(*boat); rmgObject.setTemplate(zone.getTerrainType()); @@ -259,7 +259,7 @@ bool WaterProxy::placeShipyard(Zone & land, const Lake & lake, si32 guard, Route return false; int subtype = chooseRandomAppearance(generator.rand, Obj::SHIPYARD, land.getTerrainType()); - auto shipyard = (CGShipyard*) VLC->objtypeh->getHandlerFor(Obj::SHIPYARD, subtype)->create(ObjectTemplate()); + auto shipyard = (CGShipyard*) VLC->objtypeh->getHandlerFor(Obj::SHIPYARD, subtype)->create(); shipyard->tempOwner = PlayerColor::NEUTRAL; rmg::Object rmgObject(*shipyard); diff --git a/lib/serializer/BinaryDeserializer.h b/lib/serializer/BinaryDeserializer.h index b98025d05..f8065732a 100644 --- a/lib/serializer/BinaryDeserializer.h +++ b/lib/serializer/BinaryDeserializer.h @@ -382,6 +382,56 @@ public: else { auto hlp = std::shared_ptr(internalPtr); + data = hlp; + loadedSharedPointers[internalPtrDerived] = typeList.castSharedToMostDerived(hlp); + } + } + else + data.reset(); + } + template + void load(std::shared_ptr &data) //version of the above for const ptr + { + typedef typename std::remove_const::type NonConstT; + NonConstT *internalPtr; + load(internalPtr); + + void *internalPtrDerived = typeList.castToMostDerived(internalPtr); + + if(internalPtr) + { + auto itr = loadedSharedPointers.find(internalPtrDerived); + if(itr != loadedSharedPointers.end()) + { + // This pointer is already loaded. The "data" needs to be pointed to it, + // so their shared state is actually shared. + try + { + auto actualType = typeList.getTypeInfo(internalPtr); + auto typeWeNeedToReturn = typeList.getTypeInfo(); + if(*actualType == *typeWeNeedToReturn) + { + // No casting needed, just unpack already stored shared_ptr and return it + data = boost::any_cast>(itr->second); + } + else + { + // We need to perform series of casts + auto ret = typeList.castShared(itr->second, actualType, typeWeNeedToReturn); + data = boost::any_cast>(ret); + } + } + catch(std::exception &e) + { + logGlobal->error(e.what()); + logGlobal->error("Failed to cast stored shared ptr. Real type: %s. Needed type %s. FIXME FIXME FIXME", itr->second.type().name(), typeid(std::shared_ptr).name()); + //TODO scenario with inheritance -> we can have stored ptr to base and load ptr to derived (or vice versa) + throw; + } + } + else + { + auto hlp = std::shared_ptr(internalPtr); data = hlp; //possibly adds const loadedSharedPointers[internalPtrDerived] = typeList.castSharedToMostDerived(hlp); } diff --git a/lib/serializer/BinarySerializer.h b/lib/serializer/BinarySerializer.h index f8272855a..7d3914205 100644 --- a/lib/serializer/BinarySerializer.h +++ b/lib/serializer/BinarySerializer.h @@ -245,6 +245,12 @@ public: save(internalPtr); } template + void save(const std::shared_ptr &data) + { + const T *internalPtr = data.get(); + save(internalPtr); + } + template void save(const std::unique_ptr &data) { T *internalPtr = data.get(); diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index cb1f6af17..00891b786 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -12,8 +12,8 @@ #include "../ConstTransitivePtr.h" #include "../GameConstants.h" -const ui32 SERIALIZATION_VERSION = 804; -const ui32 MINIMAL_SERIALIZATION_VERSION = 803; +const ui32 SERIALIZATION_VERSION = 805; +const ui32 MINIMAL_SERIALIZATION_VERSION = 805; const std::string SAVEGAME_MAGIC = "VCMISVG"; class CHero; diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 383e7b205..bab26bbc9 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -5572,13 +5572,18 @@ bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2) return true; } - //Ongoing garrison exchange - if (auto dialog = std::dynamic_pointer_cast(queries.topQuery(o1->tempOwner))) + //Ongoing garrison exchange - usually picking from top garison (from o1 to o2), but who knows + auto dialog = std::dynamic_pointer_cast(queries.topQuery(o1->tempOwner)); + if (!dialog) { - if (dialog->exchangingArmies.at(0) == o1 && dialog->exchangingArmies.at(1) == o2) - return true; + dialog = std::dynamic_pointer_cast(queries.topQuery(o2->tempOwner)); + } + if (dialog) + { + auto topArmy = dialog->exchangingArmies.at(0); + auto bottomArmy = dialog->exchangingArmies.at(1); - if (dialog->exchangingArmies.at(1) == o1 && dialog->exchangingArmies.at(0) == o2) + if (topArmy == o1 && bottomArmy == o2 || bottomArmy == o1 && topArmy == o2) return true; } }