1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-02 00:10:22 +02:00

Merge remote-tracking branch 'upstream/develop' into cpp-map-editor

# Conflicts:
#	lib/CHeroHandler.cpp
#	lib/mapObjects/CObjectClassesHandler.cpp
#	lib/mapObjects/CObjectClassesHandler.h
#	lib/mapObjects/ObjectTemplate.cpp
#	lib/mapObjects/ObjectTemplate.h
#	lib/mapping/CCampaignHandler.h
#	lib/mapping/CMap.h
#	lib/mapping/CMapEditManager.cpp
#	lib/mapping/CMapEditManager.h
#	lib/rmg/CMapGenerator.cpp
#	lib/rmg/CMapGenerator.h
#	lib/rmg/ConnectionsPlacer.cpp
#	lib/rmg/ObjectManager.cpp
#	lib/rmg/ObstaclePlacer.cpp
#	lib/rmg/ObstaclePlacer.h
#	lib/rmg/RiverPlacer.cpp
#	lib/rmg/RmgArea.h
#	lib/rmg/RmgMap.cpp
#	lib/rmg/RmgMap.h
#	lib/rmg/RmgObject.cpp
#	lib/rmg/TownPlacer.cpp
#	lib/rmg/TreasurePlacer.cpp
#	lib/rmg/TreasurePlacer.h
#	lib/rmg/WaterProxy.cpp
#	lib/rmg/Zone.cpp
#	lib/rmg/Zone.h
#	lib/serializer/CSerializer.h
#	lib/spells/CSpellHandler.cpp
This commit is contained in:
nordsoft 2022-09-11 18:26:11 +04:00
commit 5ce0f7c4e0
50 changed files with 574 additions and 275 deletions

View File

@ -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<CBankInfo *>(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<ui64>().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<const CGHeroInstance *>(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<const CGTownInstance *>(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<const CGWitchHut *>(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<const CGHeroInstance *>(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<const CGHeroInstance *>(target))

View File

@ -47,6 +47,10 @@
#include <vcmi/events/EventBus.h>
#ifdef VCMI_WINDOWS
#include <windows.h>
#endif
template<typename T> 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");

View File

@ -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<CAnimation> Graphics::getAnimation(const CGObjectInstance* obj)
return getAnimation(obj->appearance);
}
std::shared_ptr<CAnimation> Graphics::getAnimation(const ObjectTemplate & info)
std::shared_ptr<CAnimation> Graphics::getAnimation(std::shared_ptr<const ObjectTemplate> info)
{
//the only(?) invisible object
if(info.id == Obj::EVENT)
if(info->id == Obj::EVENT)
{
return std::shared_ptr<CAnimation>();
}
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<CAnimation>();
}
std::shared_ptr<CAnimation> ret = mapObjectAnimations[info.animationFile];
std::shared_ptr<CAnimation> ret = mapObjectAnimations[info->animationFile];
//already loaded
if(ret)
@ -404,8 +404,8 @@ std::shared_ptr<CAnimation> Graphics::getAnimation(const ObjectTemplate & info)
return ret;
}
ret = std::make_shared<CAnimation>(info.animationFile);
mapObjectAnimations[info.animationFile] = ret;
ret = std::make_shared<CAnimation>(info->animationFile);
mapObjectAnimations[info->animationFile] = ret;
ret->preload();
return ret;

View File

@ -98,7 +98,7 @@ public:
void blueToPlayersAdv(SDL_Surface * sur, PlayerColor player); //replaces blue interface colour with a color of player
std::shared_ptr<CAnimation> getAnimation(const CGObjectInstance * obj);
std::shared_ptr<CAnimation> getAnimation(const ObjectTemplate & info);
std::shared_ptr<CAnimation> getAnimation(std::shared_ptr<const ObjectTemplate> info);
};
extern Graphics * graphics;

View File

@ -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();

View File

@ -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;

View File

@ -29,20 +29,20 @@ void QuickRecruitmentWindow::setButtons()
void QuickRecruitmentWindow::setCancelButton()
{
cancelButton = std::make_shared<CButton>(Point((pos.w / 2) + 48, 418), "ICN6432.DEF", CButton::tooltip(), [&](){ close(); }, SDLK_RETURN);
cancelButton = std::make_shared<CButton>(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<CButton>(Point((pos.w/2)-32, 418), "IBY6432.DEF", CButton::tooltip(), [&](){ purhaseUnits(); });
buyButton = std::make_shared<CButton>(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<CButton>(Point((pos.w/2)-112, 418), "IRCBTNS.DEF", CButton::tooltip(), [&](){ maxAllCards(cards); });
maxButton = std::make_shared<CButton>(Point((pos.w/2)-112, 418), "IRCBTNS.DEF", CButton::tooltip(), [&](){ maxAllCards(cards); }, SDLK_m);
maxButton->setImageOrder(0, 1, 2, 3);
}

View File

@ -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<QVariantMap>())
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)
{

View File

@ -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);

View File

@ -160,6 +160,12 @@ QVariant CModListModel::headerData(int section, Qt::Orientation orientation, int
return QVariant();
}
void CModListModel::reloadRepositories()
{
beginResetModel();
endResetModel();
}
void CModListModel::resetRepositories()
{
beginResetModel();

View File

@ -61,6 +61,7 @@ public:
/// CModListContainer overrides
void resetRepositories() override;
void reloadRepositories() override;
void addRepository(QVariantMap data) override;
void modChanged(QString modID) override;

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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::function<bool(const std::string
{
if (filter(mountPoint))
{
fileList = listFiles(mountPoint, 1, false);
fileList = listFiles(mountPoint, recursiveDepth, false);
}
}

View File

@ -45,6 +45,8 @@ private:
boost::filesystem::path baseDirectory;
std::string mountPoint;
size_t recursiveDepth;
/** A list of files in the directory
* key = ResourceID for resource loader

View File

@ -450,9 +450,8 @@ void CGHeroInstance::onHeroVisit(const CGHeroInstance * h) const
{
int txt_id;
if (cb->getHeroCount(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);
}
}
}

View File

@ -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

View File

@ -117,11 +117,11 @@ std::vector<JsonNode> CObjectClassesHandler::loadLegacyData(size_t dataSize)
for (size_t i=0; i<totalNumber; i++)
{
ObjectTemplate templ;
templ.readTxt(parser);
auto templ = new ObjectTemplate;
templ->readTxt(parser);
parser.endLine();
std::pair<si32, si32> key(templ.id.num, templ.subid);
legacyTemplates.insert(std::make_pair(key, templ));
std::pair<si32, si32> key(templ->id.num, templ->subid);
legacyTemplates.insert(std::make_pair(key, std::shared_ptr<const ObjectTemplate>(templ)));
}
std::vector<JsonNode> 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::optional<std::strin
entry.second.setType(JsonNode::JsonType::DATA_STRUCT);
JsonUtils::inherit(entry.second, base);
ObjectTemplate tmpl;
tmpl.id = Obj(type);
tmpl.subid = subtype;
tmpl.stringID = entry.first; // FIXME: create "fullID" - type.object.template?
tmpl.readJson(entry.second);
templates.push_back(tmpl);
auto tmpl = new ObjectTemplate;
tmpl->id = 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<const ObjectTemplate>(tmpl));
}
if (input["name"].isNull())
@ -523,7 +522,7 @@ void AObjectTypeHandler::init(const JsonNode & input, boost::optional<std::strin
initTypeData(input);
}
bool AObjectTypeHandler::objectFilter(const CGObjectInstance *, const ObjectTemplate &) const
bool AObjectTypeHandler::objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) 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<const ObjectTemplate> templ)
{
//Otherwise the template remains constant
auto ptr = const_cast<ObjectTemplate*>(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<ObjectTemplate> AObjectTypeHandler::getTemplates() const
std::vector<std::shared_ptr<const ObjectTemplate>> AObjectTypeHandler::getTemplates() const
{
return templates;
}
@ -580,14 +581,14 @@ BattleField AObjectTypeHandler::getBattlefield() const
return battlefield ? BattleField::fromString(battlefield.get()) : BattleField::NONE;
}
std::vector<ObjectTemplate> AObjectTypeHandler::getTemplates(const Terrain & terrainType) const
std::vector<std::shared_ptr<const ObjectTemplate>>AObjectTypeHandler::getTemplates(const Terrain & terrainType) const
{
std::vector<ObjectTemplate> templates = getTemplates();
std::vector<ObjectTemplate> filtered;
std::vector<std::shared_ptr<const ObjectTemplate>> templates = getTemplates();
std::vector<std::shared_ptr<const ObjectTemplate>> 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<const ObjectTemplate> 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<ObjectTemplate> AObjectTypeHandler::getTemplates(const Terrain & ter
return filtered;
}
boost::optional<ObjectTemplate> AObjectTypeHandler::getOverride(const Terrain & terrainType, const CGObjectInstance * object) const
std::shared_ptr<const ObjectTemplate> AObjectTypeHandler::getOverride(const Terrain & terrainType, const CGObjectInstance * object) const
{
std::vector<ObjectTemplate> ret = getTemplates(terrainType);
for (auto & tmpl : ret)
std::vector<std::shared_ptr<const ObjectTemplate>> ret = getTemplates(terrainType);
for (const auto & tmpl: ret)
{
if (objectFilter(object, tmpl))
return tmpl;
}
return boost::optional<ObjectTemplate>();
return std::shared_ptr<const ObjectTemplate>(); //empty
}
const RandomMapInfo & AObjectTypeHandler::getRMGInfo()

View File

@ -144,7 +144,7 @@ class DLL_LINKAGE AObjectTypeHandler : public boost::noncopyable
JsonNode base; /// describes base template
std::vector<ObjectTemplate> templates;
std::vector<std::shared_ptr<const ObjectTemplate>> 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 ObjectTemplate>) const;
/// initialization for classes that inherit this one
virtual void initTypeData(const JsonNode & input);
@ -177,17 +177,21 @@ public:
boost::optional<std::string> getCustomName() const;
SObjectSounds getSounds() const;
void addTemplate(const ObjectTemplate & templ);
void addTemplate(std::shared_ptr<const ObjectTemplate> templ);
void addTemplate(JsonNode config);
/// returns all templates matching parameters
std::vector<ObjectTemplate> getTemplates() const;
std::vector<ObjectTemplate> getTemplates(const Terrain & terrainType) const;
std::vector<std::shared_ptr<const ObjectTemplate>> getTemplates() const;
std::vector<std::shared_ptr<const ObjectTemplate>> 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<const ObjectTemplate> 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<ObjectTemplate> 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<const ObjectTemplate> 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<IObjectInfo> getObjectInfo(const ObjectTemplate & tmpl) const = 0;
virtual std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const = 0;
template <typename Handler> void serialize(Handler &h, const int version)
{
@ -264,7 +268,7 @@ class DLL_LINKAGE CObjectClassesHandler : public IHandlerBase
std::map<std::string, std::function<TObjectTypeHandler()> > handlerConstructors;
/// container with H3 templates, used only during loading, no need to serialize it
typedef std::multimap<std::pair<si32, si32>, ObjectTemplate> TTemplatesContainer;
typedef std::multimap<std::pair<si32, si32>, std::shared_ptr<const ObjectTemplate>> TTemplatesContainer;
TTemplatesContainer legacyTemplates;
/// contains list of custom names for H3 objects (e.g. Dwellings), used to load H3 data

View File

@ -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<int3> CGObjectInstance::getBlockedPos() const
@ -179,7 +179,7 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
{
for(int h=0; h<getHeight(); ++h)
{
if(appearance.isBlockedAt(w, h))
if(appearance->isBlockedAt(w, h))
ret.insert(int3(pos.x - w, pos.y - h, pos.z));
}
}
@ -188,7 +188,7 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
std::set<int3> 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);
}

View File

@ -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<const ObjectTemplate> appearance;
/// If true hero can visit this object only from neighbouring tiles and can't stand on this object
bool blockVisit;

View File

@ -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<const ObjectTemplate> tmpl) const
{
auto ret = new CRewardableObject();
preInitObject(ret);
@ -196,7 +196,7 @@ void CRewardableConstructor::configureObject(CGObjectInstance * object, CRandomG
objectInfo.configureObject(dynamic_cast<CRewardableObject*>(object), rng);
}
std::unique_ptr<IObjectInfo> CRewardableConstructor::getObjectInfo(const ObjectTemplate & tmpl) const
std::unique_ptr<IObjectInfo> CRewardableConstructor::getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const
{
return std::unique_ptr<IObjectInfo>(new CRandomRewardObjectInfo(objectInfo));
}

View File

@ -49,9 +49,9 @@ class DLL_LINKAGE CRewardableConstructor : public AObjectTypeHandler
public:
CRewardableConstructor();
CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
std::unique_ptr<IObjectInfo> getObjectInfo(const ObjectTemplate & tmpl) const override;
std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
};

View File

@ -1122,7 +1122,7 @@ std::vector<int3> 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;

View File

@ -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<const ObjectTemplate> templ) const
{
auto town = dynamic_cast<const CGTownInstance *>(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<const ObjectTemplate> 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<const ObjectTemplate> templ) const
{
auto hero = dynamic_cast<const CGHeroInstance *>(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<const ObjectTemplate> 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 ObjectTemplate>) const
{
return false;
}
CGObjectInstance * CDwellingInstanceConstructor::create(const ObjectTemplate & tmpl) const
CGObjectInstance * CDwellingInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
{
CGDwelling * obj = createTyped(tmpl);
@ -272,7 +272,7 @@ void CBankInstanceConstructor::initTypeData(const JsonNode & input)
bankResetDuration = static_cast<si32>(input["resetDuration"].Float());
}
CGObjectInstance *CBankInstanceConstructor::create(const ObjectTemplate & tmpl) const
CGObjectInstance *CBankInstanceConstructor::create(std::shared_ptr<const ObjectTemplate> tmpl) const
{
return createTyped(tmpl);
}
@ -494,7 +494,7 @@ bool CBankInfo::givesSpells() const
}
std::unique_ptr<IObjectInfo> CBankInstanceConstructor::getObjectInfo(const ObjectTemplate & tmpl) const
std::unique_ptr<IObjectInfo> CBankInstanceConstructor::getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const
{
return std::unique_ptr<IObjectInfo>(new CBankInfo(levels));
}

View File

@ -26,17 +26,31 @@ template<class ObjectType>
class CDefaultObjectTypeHandler : public AObjectTypeHandler
{
protected:
ObjectType * createTyped(const ObjectTemplate & tmpl) const
ObjectType * createTyped(std::shared_ptr<const ObjectTemplate> 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<const ObjectTemplate> tmpl = nullptr) const override
{
return createTyped(tmpl);
}
@ -45,7 +59,7 @@ public:
{
}
virtual std::unique_ptr<IObjectInfo> getObjectInfo(const ObjectTemplate & tmpl) const override
virtual std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override
{
return nullptr;
}
@ -62,7 +76,7 @@ class CTownInstanceConstructor : public CDefaultObjectTypeHandler<CGTownInstance
{
JsonNode filtersJson;
protected:
bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const override;
bool objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) const override;
void initTypeData(const JsonNode & input) override;
public:
@ -70,7 +84,7 @@ public:
std::map<std::string, LogicalExpression<BuildingID>> filters;
CTownInstanceConstructor();
CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
void afterLoadFinalization() override;
@ -87,7 +101,7 @@ class CHeroInstanceConstructor : public CDefaultObjectTypeHandler<CGHeroInstance
{
JsonNode filtersJson;
protected:
bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const override;
bool objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate>) const override;
void initTypeData(const JsonNode & input) override;
public:
@ -95,7 +109,7 @@ public:
std::map<std::string, LogicalExpression<HeroTypeID>> filters;
CHeroInstanceConstructor();
CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> tmpl = nullptr) const override;
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
void afterLoadFinalization() override;
@ -115,13 +129,13 @@ class CDwellingInstanceConstructor : public CDefaultObjectTypeHandler<CGDwelling
JsonNode guards;
protected:
bool objectFilter(const CGObjectInstance *, const ObjectTemplate &) const override;
bool objectFilter(const CGObjectInstance *, std::shared_ptr<const ObjectTemplate> tmpl) const override;
void initTypeData(const JsonNode & input) override;
public:
CDwellingInstanceConstructor();
CGObjectInstance * create(const ObjectTemplate & tmpl) const override;
CGObjectInstance * create(std::shared_ptr<const ObjectTemplate> 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<const ObjectTemplate> tmpl = nullptr) const override;
void configureObject(CGObjectInstance * object, CRandomGenerator & rng) const override;
std::unique_ptr<IObjectInfo> getObjectInfo(const ObjectTemplate & tmpl) const override;
std::unique_ptr<IObjectInfo> getObjectInfo(std::shared_ptr<const ObjectTemplate> tmpl) const override;
template <typename Handler> void serialize(Handler &h, const int version)
{

View File

@ -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<si32>(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<ui32>(ret, (ui32)row.size());
width = std::max<ui32>(width, (ui32)row.size());
}
return ret;
}
ui32 ObjectTemplate::getHeight() const
void ObjectTemplate::calculateHeight()
{
//TODO: Use 2D array
return static_cast<ui32>(usedTiles.size());
height = static_cast<ui32>(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<int3> ObjectTemplate::getBlockedOffsets() const
void ObjectTemplate::calculateBlockedOffsets()
{
std::set<int3> 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();
}

View File

@ -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<int3> getBlockedOffsets() const;
int3 getBlockMapOffset() const; //bottom-right corner when firts blocked tile is
inline std::set<int3> 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<int3> blockedOffsets;
int3 blockMapOffset;
int3 visitableOffset;
void recalculate();
void calculateWidth();
void calculateHeight();
void calculateVsitable();
void calculateBlockedOffsets();
void calculateBlockMapOffset();
void calculateVisitableOffset();
public:
template <typename Handler> void serialize(Handler &h, const int version)
{
h & usedTiles;
@ -98,6 +145,11 @@ public:
h & printPriority;
h & visitDir;
h & editorAnimationFile;
if (!h.saving)
{
recalculate();
}
}
};

View File

@ -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<CGHeroInstance *> getLostCrossoverHeroes(); /// returns a list of crossover heroes which started the scenario, but didn't complete it
CCampaignScenario();
template <typename Handler> void serialize(Handler &h, const int formatVersion)

View File

@ -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;

View File

@ -491,14 +491,5 @@ public:
h & CGTownInstance::universitySkills;
h & instanceNames;
if (!h.saving && formatVersion < 804)
{
uidCounter = objects.size();
}
else
{
h & uidCounter;
}
}
};

View File

@ -198,4 +198,3 @@ CObjectSelection & CMapEditManager::getObjectSelection()
CMapUndoManager & CMapEditManager::getUndoManager()
{
return undoManager;
}

View File

@ -12,6 +12,7 @@
#include "../GameConstants.h"
#include "CMapOperation.h"
#include "Terrain.h"
class CGObjectInstance;
class CTerrainViewPatternConfig;

View File

@ -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<const ObjectTemplate>(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<const ObjectTemplate> 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()));

View File

@ -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<ObjectTemplate> templates;
std::vector<std::shared_ptr<const ObjectTemplate>> templates;
/** ptr to the map object which gets filled by data from the buffer */
CMap * map;

View File

@ -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<const ObjectTemplate>(appearance));
instance->id = ObjectInstanceID((si32)owner->map->objects.size());
instance->instanceName = jsonKey;

View File

@ -204,8 +204,8 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c
auto & managerOther = *otherZone->getModificator<ObjectManager>();
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<ObjectManager>()->addRequiredObject(teleport1, connection.getGuardStrength());
otherZone->getModificator<ObjectManager>()->addRequiredObject(teleport2, connection.getGuardStrength());

View File

@ -367,7 +367,7 @@ void ObjectManager::placeObject(rmg::Object & object, bool guarded, bool updateD
objects.push_back(&instance->object());
if(auto * m = zone.getModificator<RoadPlacer>())
{
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

View File

@ -216,7 +216,7 @@ void ObstaclePlacer::process()
auto * riverManager = zone.getModificator<RiverPlacer>();
typedef std::vector<ObjectTemplate> ObstacleVector;
typedef std::vector<std::shared_ptr<const ObjectTemplate>> ObstacleVector;
//obstacleVector possibleObstacles;
std::map<int, ObstacleVector> 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();

View File

@ -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<ObjectTemplate> tmplates;
std::vector<std::shared_ptr<const ObjectTemplate>> 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]);

View File

@ -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);

View File

@ -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;

View File

@ -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<SpellID> 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 <CSpell *> 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 <CSpell *> 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 <CSpell *> 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<ObjectInfo*> 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<ObjectInfo*>
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)

View File

@ -19,7 +19,7 @@ class CMapGenerator;
struct ObjectInfo
{
ObjectTemplate templ;
std::shared_ptr<const ObjectTemplate> templ;
ui32 value = 0;
ui16 probability = 0;
ui32 maxPerZone = -1;

View File

@ -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);

View File

@ -382,6 +382,56 @@ public:
else
{
auto hlp = std::shared_ptr<NonConstT>(internalPtr);
data = hlp;
loadedSharedPointers[internalPtrDerived] = typeList.castSharedToMostDerived(hlp);
}
}
else
data.reset();
}
template <typename T>
void load(std::shared_ptr<const T> &data) //version of the above for const ptr
{
typedef typename std::remove_const<T>::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<T>();
if(*actualType == *typeWeNeedToReturn)
{
// No casting needed, just unpack already stored shared_ptr and return it
data = boost::any_cast<std::shared_ptr<const T>>(itr->second);
}
else
{
// We need to perform series of casts
auto ret = typeList.castShared(itr->second, actualType, typeWeNeedToReturn);
data = boost::any_cast<std::shared_ptr<const T>>(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<T>).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<const T>(internalPtr);
data = hlp; //possibly adds const
loadedSharedPointers[internalPtrDerived] = typeList.castSharedToMostDerived(hlp);
}

View File

@ -245,6 +245,12 @@ public:
save(internalPtr);
}
template <typename T>
void save(const std::shared_ptr<const T> &data)
{
const T *internalPtr = data.get();
save(internalPtr);
}
template <typename T>
void save(const std::unique_ptr<T> &data)
{
T *internalPtr = data.get();

View File

@ -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;

View File

@ -5572,13 +5572,18 @@ bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2)
return true;
}
//Ongoing garrison exchange
if (auto dialog = std::dynamic_pointer_cast<CGarrisonDialogQuery>(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<CGarrisonDialogQuery>(queries.topQuery(o1->tempOwner));
if (!dialog)
{
if (dialog->exchangingArmies.at(0) == o1 && dialog->exchangingArmies.at(1) == o2)
return true;
dialog = std::dynamic_pointer_cast<CGarrisonDialogQuery>(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;
}
}