1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-22 22:13:35 +02:00
vcmi/lib/rmg/modificators/TownPlacer.cpp
Ivan Savenko 3dd4fa2528 Reduce usage of pointers to VLC entities
Final goal (of multiple PR's) is to remove all remaining pointers from
serializeable game state, and replace them with either identifiers or
with shared/unique pointers.

CGTownInstance::town and CGHeroInstance::type members have been removed.
Now this data is computed dynamically using subID member.

VLC entity of a town can now be accessed via following methods:
- getFactionID() returns ID of a faction
- getFaction() returns pointer to a faction
- getTown() returns pointer to a town

VLC entity of a hero can now be accessed via following methods:
- getHeroTypeID() returns ID of a hero
- getHeroClassID() returns ID of a hero class
- getHeroType() returns pointer to a hero
- getHeroClass() returns pointer to a hero class
2024-10-10 12:28:08 +00:00

255 lines
7.7 KiB
C++

/*
* TownPlacer.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "TownPlacer.h"
#include "../CMapGenerator.h"
#include "../RmgMap.h"
#include "../../entities/faction/CTownHandler.h"
#include "../../mapObjectConstructors/AObjectTypeHandler.h"
#include "../../mapObjectConstructors/CObjectClassesHandler.h"
#include "../../mapObjects/CGTownInstance.h"
#include "../../mapping/CMap.h"
#include "../../mapping/CMapEditManager.h"
#include "../../spells/CSpellHandler.h" //for choosing random spells
#include "../RmgPath.h"
#include "../RmgObject.h"
#include "ObjectManager.h"
#include "../Functions.h"
#include "RoadPlacer.h"
#include "MinePlacer.h"
#include "WaterAdopter.h"
#include "../TileInfo.h"
#include <vstd/RNG.h>
VCMI_LIB_NAMESPACE_BEGIN
void TownPlacer::process()
{
auto * manager = zone.getModificator<ObjectManager>();
if(!manager)
{
logGlobal->error("ObjectManager doesn't exist for zone %d, skip modificator %s", zone.getId(), getName());
return;
}
placeTowns(*manager);
}
void TownPlacer::init()
{
POSTFUNCTION(MinePlacer);
POSTFUNCTION(RoadPlacer);
}
void TownPlacer::placeTowns(ObjectManager & manager)
{
if(zone.getOwner() && ((zone.getType() == ETemplateZoneType::CPU_START) || (zone.getType() == ETemplateZoneType::PLAYER_START)))
{
//set zone types to player faction, generate main town
logGlobal->info("Preparing playing zone");
int player_id = *zone.getOwner() - 1;
const auto & playerSettings = map.getMapGenOptions().getPlayersSettings();
PlayerColor player;
if (playerSettings.size() > player_id)
{
const auto & currentPlayerSettings = std::next(playerSettings.begin(), player_id);
player = currentPlayerSettings->first;
zone.setTownType(currentPlayerSettings->second.getStartingTown());
if(zone.getTownType() == FactionID::RANDOM)
zone.setTownType(getRandomTownType(true));
}
else //no player - randomize town
{
player = PlayerColor::NEUTRAL;
zone.setTownType(getRandomTownType());
}
auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, zone.getTownType());
CGTownInstance * town = dynamic_cast<CGTownInstance *>(townFactory->create(map.mapInstance->cb, nullptr));
town->tempOwner = player;
town->addBuilding(BuildingID::FORT);
town->addBuilding(BuildingID::DEFAULT);
for(auto spellID : VLC->spellh->getDefaultAllowed()) //add all regular spells to town
town->possibleSpells.push_back(spellID);
auto position = placeMainTown(manager, *town);
totalTowns++;
//register MAIN town of zone only
map.registerZone(town->getFactionID());
if(player.isValidPlayer()) //configure info for owning player
{
logGlobal->trace("Fill player info %d", player_id);
// Update player info
auto & playerInfo = map.getPlayer(player.getNum());
playerInfo.allowedFactions.clear();
playerInfo.allowedFactions.insert(zone.getTownType());
playerInfo.hasMainTown = true;
playerInfo.posOfMainTown = position;
playerInfo.generateHeroAtMainTown = true;
//now create actual towns
addNewTowns(zone.getPlayerTowns().getCastleCount() - 1, true, player, manager);
addNewTowns(zone.getPlayerTowns().getTownCount(), false, player, manager);
}
else
{
addNewTowns(zone.getPlayerTowns().getCastleCount() - 1, true, PlayerColor::NEUTRAL, manager);
addNewTowns(zone.getPlayerTowns().getTownCount(), false, PlayerColor::NEUTRAL, manager);
}
}
else //randomize town types for any other zones as well
{
zone.setTownType(getRandomTownType());
}
addNewTowns(zone.getNeutralTowns().getCastleCount(), true, PlayerColor::NEUTRAL, manager);
addNewTowns(zone.getNeutralTowns().getTownCount(), false, PlayerColor::NEUTRAL, manager);
if(!totalTowns) //if there's no town present, get random faction for dwellings and pandoras
{
//25% chance for neutral
if (zone.getRand().nextInt(1, 100) <= 25)
{
zone.setTownType(ETownType::NEUTRAL);
}
else
{
if(!zone.getTownTypes().empty())
zone.setTownType(*RandomGeneratorUtil::nextItem(zone.getTownTypes(), zone.getRand()));
else if(!zone.getMonsterTypes().empty())
zone.setTownType(*RandomGeneratorUtil::nextItem(zone.getMonsterTypes(), zone.getRand())); //this happens in Clash of Dragons in treasure zones, where all towns are banned
else //just in any case
zone.setTownType(getRandomTownType());
}
}
}
int3 TownPlacer::placeMainTown(ObjectManager & manager, CGTownInstance & town)
{
//towns are big objects and should be centered around visitable position
rmg::Object rmgObject(town);
rmgObject.setTemplate(zone.getTerrainType(), zone.getRand());
int3 position(-1, -1, -1);
{
Zone::Lock lock(zone.areaMutex);
position = manager.findPlaceForObject(zone.areaPossible().get(), rmgObject, [this](const int3& t)
{
float distance = zone.getPos().dist2dSQ(t);
return 100000.f - distance; //some big number
}, ObjectManager::OptimizeType::WEIGHT);
}
rmgObject.setPosition(position + int3(2, 2, 0)); //place visitable tile in the exact center of a zone
manager.placeObject(rmgObject, false, true, true);
cleanupBoundaries(rmgObject);
zone.setPos(rmgObject.getVisitablePosition()); //roads lead to main town
return position;
}
void TownPlacer::cleanupBoundaries(const rmg::Object & rmgObject)
{
Zone::Lock lock(zone.areaMutex);
for(const auto & t : rmgObject.getArea().getBorderOutside())
{
if (t.y > rmgObject.getVisitablePosition().y) //Line below the town
{
if (map.isOnMap(t))
{
map.setOccupied(t, ETileType::FREE);
zone.areaPossible()->erase(t);
zone.freePaths()->add(t);
}
}
}
}
void TownPlacer::addNewTowns(int count, bool hasFort, const PlayerColor & player, ObjectManager & manager)
{
for(int i = 0; i < count; i++)
{
FactionID subType = zone.getTownType();
if(totalTowns>0)
{
if(!zone.areTownsSameType())
{
if(!zone.getTownTypes().empty())
subType = *RandomGeneratorUtil::nextItem(zone.getTownTypes(), zone.getRand());
else
subType = *RandomGeneratorUtil::nextItem(zone.getDefaultTownTypes(), zone.getRand()); //it is possible to have zone with no towns allowed
}
}
auto townFactory = VLC->objtypeh->getHandlerFor(Obj::TOWN, subType);
auto * town = dynamic_cast<CGTownInstance *>(townFactory->create(map.mapInstance->cb, nullptr));
town->ID = Obj::TOWN;
town->tempOwner = player;
if (hasFort)
town->addBuilding(BuildingID::FORT);
town->addBuilding(BuildingID::DEFAULT);
for(auto spellID : VLC->spellh->getDefaultAllowed()) //add all regular spells to town
town->possibleSpells.push_back(spellID);
if(totalTowns <= 0)
{
//FIXME: discovered bug with small zones - getPos is close to map boarder and we have outOfMap exception
//register MAIN town of zone
map.registerZone(town->getFactionID());
//first town in zone goes in the middle
placeMainTown(manager, *town);
}
else
{
manager.addRequiredObject(RequiredObjectInfo(town, 0, true));
}
totalTowns++;
}
}
FactionID TownPlacer::getRandomTownType(bool matchUndergroundType)
{
auto townTypesAllowed = (!zone.getTownTypes().empty() ? zone.getTownTypes() : zone.getDefaultTownTypes());
if(matchUndergroundType)
{
std::set<FactionID> townTypesVerify;
for(auto factionIdx : townTypesAllowed)
{
bool preferUnderground = (*VLC->townh)[factionIdx]->preferUndergroundPlacement;
if(zone.isUnderground() ? preferUnderground : !preferUnderground)
{
townTypesVerify.insert(factionIdx);
}
}
if(!townTypesVerify.empty())
townTypesAllowed = townTypesVerify;
}
return *RandomGeneratorUtil::nextItem(townTypesAllowed, zone.getRand());
}
int TownPlacer::getTotalTowns() const
{
return totalTowns;
}
VCMI_LIB_NAMESPACE_END