1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-15 00:05:02 +02:00

Remove pointer to objects from TerrainTile

This commit is contained in:
Ivan Savenko
2025-03-19 14:40:45 +00:00
parent 63d00b080e
commit cd7732456a
26 changed files with 164 additions and 113 deletions

View File

@ -1332,8 +1332,7 @@ bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
{ {
auto tile = cb->getTile(coord, false); auto tile = cb->getTile(coord, false);
assert(tile); assert(tile);
return tile->topVisitableObj(ignoreHero); return cb->getObj(tile->topVisitableObj(ignoreHero));
//return cb->getTile(coord,false)->topVisitableObj(ignoreHero);
}; };
auto isTeleportAction = [&](EPathNodeAction action) -> bool auto isTeleportAction = [&](EPathNodeAction action) -> bool

View File

@ -202,7 +202,7 @@ bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
} }
else if(!fromWater) // do not try to board when in water sector else if(!fromWater) // do not try to board when in water sector
{ {
if(t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT) if(t->visitableObjects.size() == 1 && cb->getObjInstance(t->topVisitableObj())->ID == Obj::BOAT)
return true; return true;
} }
return false; return false;

View File

@ -192,7 +192,7 @@ bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
} }
else if(!fromWater) // do not try to board when in water sector else if(!fromWater) // do not try to board when in water sector
{ {
if(t->visitableObjects.size() == 1 && t->topVisitableId() == Obj::BOAT) if(t->visitableObjects.size() == 1 && cb->getObjInstance(t->topVisitableObj())->ID == Obj::BOAT)
return true; return true;
} }
return false; return false;
@ -200,9 +200,11 @@ bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater)
bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should be handled by pathfinder bool isBlockedBorderGate(int3 tileToHit) //TODO: is that function needed? should be handled by pathfinder
{ {
if(cb->getTile(tileToHit)->topVisitableId() != Obj::BORDER_GATE) const auto * object = cb->getTopObj(tileToHit);
if( object && object->id != Obj::BORDER_GATE)
return false; return false;
auto gate = dynamic_cast<const CGKeys *>(cb->getTile(tileToHit)->topVisitableObj());
auto gate = dynamic_cast<const CGKeys *>(object);
return !gate->passableFor(ai->playerID); return !gate->passableFor(ai->playerID);
} }

View File

@ -192,7 +192,7 @@ Goals::TSubgoal PathfindingManager::clearWayTo(HeroPtr hero, int3 firstTileToGet
if(isBlockedBorderGate(firstTileToGet)) if(isBlockedBorderGate(firstTileToGet))
{ {
//FIXME: this way we'll not visit gate and activate quest :? //FIXME: this way we'll not visit gate and activate quest :?
return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTile(firstTileToGet)->visitableObjects.back()->getObjTypeIndex())); return sptr(Goals::FindObj(Obj::KEYMASTER, cb->getTopObj(firstTileToGet)->getObjTypeIndex()));
} }
auto topObj = cb->getTopObj(firstTileToGet); auto topObj = cb->getTopObj(firstTileToGet);

View File

@ -1868,10 +1868,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
auto getObj = [&](int3 coord, bool ignoreHero) auto getObj = [&](int3 coord, bool ignoreHero)
{ {
auto tile = cb->getTile(coord, false); return cb->getObj(cb->getTile(coord)->topVisitableObj(ignoreHero));
assert(tile);
return tile->topVisitableObj(ignoreHero);
//return cb->getTile(coord,false)->topVisitableObj(ignoreHero);
}; };
auto isTeleportAction = [&](EPathNodeAction action) -> bool auto isTeleportAction = [&](EPathNodeAction action) -> bool

View File

@ -595,7 +595,7 @@ void ApplyClientNetPackVisitor::visitSetAvailableCreatures(SetAvailableCreatures
PlayerColor p; PlayerColor p;
if(dw->ID == Obj::WAR_MACHINE_FACTORY) //War Machines Factory is not flaggable, it's "owned" by visitor if(dw->ID == Obj::WAR_MACHINE_FACTORY) //War Machines Factory is not flaggable, it's "owned" by visitor
p = cl.getTile(dw->visitablePos())->visitableObjects.back()->tempOwner; p = cl.getObjInstance(cl.getTile(dw->visitablePos())->visitableObjects.back())->getOwner();
else else
p = dw->tempOwner; p = dw->tempOwner;
@ -1016,7 +1016,9 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object)); const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor)); const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
const auto market = cl.getMarket(pack.object); const auto market = cl.getMarket(pack.object);
callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showMarketWindow, market, hero, pack.queryID); const auto * tile = cl.getTile(obj->visitablePos());
const auto * topObject = cl.getObjInstance(tile->visitableObjects.back());
callInterfaceIfPresent(cl, topObject->getOwner(), &IGameEventsReceiver::showMarketWindow, market, hero, pack.queryID);
} }
break; break;
case EOpenWindowMode::HILL_FORT_WINDOW: case EOpenWindowMode::HILL_FORT_WINDOW:
@ -1025,7 +1027,9 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
//displays Hill fort window //displays Hill fort window
const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object)); const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor)); const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showHillFortWindow, obj, hero); const auto * tile = cl.getTile(obj->visitablePos());
const auto * topObject = cl.getObjInstance(tile->visitableObjects.back());
callInterfaceIfPresent(cl, topObject->getOwner(), &IGameEventsReceiver::showHillFortWindow, obj, hero);
} }
break; break;
case EOpenWindowMode::PUZZLE_MAP: case EOpenWindowMode::PUZZLE_MAP:
@ -1076,7 +1080,10 @@ void ApplyClientNetPackVisitor::visitSetAvailableArtifacts(SetAvailableArtifacts
{ {
const CGBlackMarket *bm = dynamic_cast<const CGBlackMarket *>(cl.getObj(ObjectInstanceID(pack.id))); const CGBlackMarket *bm = dynamic_cast<const CGBlackMarket *>(cl.getObj(ObjectInstanceID(pack.id)));
assert(bm); assert(bm);
callInterfaceIfPresent(cl, cl.getTile(bm->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::availableArtifactsChanged, bm); const auto * tile = cl.getTile(bm->visitablePos());
const auto * topObject = cl.getObjInstance(tile->visitableObjects.back());
callInterfaceIfPresent(cl, topObject->getOwner(), &IGameEventsReceiver::availableArtifactsChanged, bm);
} }
} }

View File

@ -39,8 +39,9 @@ ColorRGBA CMinimapInstance::getTileColor(const int3 & pos) const
return Colors::BLACK; return Colors::BLACK;
// if object at tile is owned - it will be colored as its owner // if object at tile is owned - it will be colored as its owner
for (const CGObjectInstance *obj : tile->blockingObjects) for (const ObjectInstanceID objectID : tile->blockingObjects)
{ {
const auto * obj = GAME->interface()->cb->getObj(objectID);
PlayerColor player = obj->getOwner(); PlayerColor player = obj->getOwner();
if(player == PlayerColor::NEUTRAL) if(player == PlayerColor::NEUTRAL)
return graphics->neutralColor; return graphics->neutralColor;

View File

@ -278,10 +278,12 @@ std::string MapRendererAdventureContext::overlayText(const int3 & coordinates) c
if (!tile.visitable()) if (!tile.visitable())
return {}; return {};
if ( tile.visitableObjects.back()->ID == Obj::EVENT) const auto * object = getObject(tile.visitableObjects.back());
if ( object->ID == Obj::EVENT)
return {}; return {};
return tile.visitableObjects.back()->getObjectName(); return object->getObjectName();
} }
ColorRGBA MapRendererAdventureContext::overlayTextColor(const int3 & coordinates) const ColorRGBA MapRendererAdventureContext::overlayTextColor(const int3 & coordinates) const
@ -294,7 +296,7 @@ ColorRGBA MapRendererAdventureContext::overlayTextColor(const int3 & coordinates
if (!tile.visitable()) if (!tile.visitable())
return {}; return {};
const auto * object = tile.visitableObjects.back(); const auto * object = getObject(tile.visitableObjects.back());
if (object->getOwner() == GAME->interface()->playerID) if (object->getOwner() == GAME->interface()->playerID)
return { 0, 192, 0}; return { 0, 192, 0};

View File

@ -74,9 +74,10 @@ std::shared_ptr<CanvasImage> CMapOverviewWidget::createMinimapForLayer(std::uniq
if(drawPlayerElements) if(drawPlayerElements)
// if object at tile is owned - it will be colored as its owner // if object at tile is owned - it will be colored as its owner
for (const CGObjectInstance *obj : tile.blockingObjects) for (ObjectInstanceID objectID : tile.blockingObjects)
{ {
PlayerColor player = obj->getOwner(); const auto * object = map->getObject(objectID);
PlayerColor player = object->getOwner();
if(player == PlayerColor::NEUTRAL) if(player == PlayerColor::NEUTRAL)
{ {
color = graphics->neutralColor; color = graphics->neutralColor;

View File

@ -866,7 +866,7 @@ TerrainId CStackInstance::getNativeTerrain() const
TerrainId CStackInstance::getCurrentTerrain() const TerrainId CStackInstance::getCurrentTerrain() const
{ {
return armyObj->getCurrentTerrain(); return getArmy()->getCurrentTerrain();
} }
void CStackInstance::deserializationFix() void CStackInstance::deserializationFix()

View File

@ -453,8 +453,8 @@ std::vector <const CGObjectInstance *> CGameInfoCallback::getBlockingObjs( int3
const TerrainTile *t = getTile(pos); const TerrainTile *t = getTile(pos);
ERROR_RET_VAL_IF(!t, "Not a valid tile requested!", ret); ERROR_RET_VAL_IF(!t, "Not a valid tile requested!", ret);
for(const CGObjectInstance * obj : t->blockingObjects) for(const auto & objID : t->blockingObjects)
ret.push_back(obj); ret.push_back(getObj(objID));
return ret; return ret;
} }
@ -464,10 +464,12 @@ std::vector <const CGObjectInstance *> CGameInfoCallback::getVisitableObjs(int3
const TerrainTile *t = getTile(pos, verbose); const TerrainTile *t = getTile(pos, verbose);
ERROR_VERBOSE_OR_NOT_RET_VAL_IF(!t, verbose, pos.toString() + " is not visible!", ret); ERROR_VERBOSE_OR_NOT_RET_VAL_IF(!t, verbose, pos.toString() + " is not visible!", ret);
for(const CGObjectInstance * obj : t->visitableObjects) for(const auto & objID : t->visitableObjects)
{ {
if(!getPlayerID().has_value() || obj->ID != Obj::EVENT) //hide events from players const auto & object = getObj(objID);
ret.push_back(obj);
if(!getPlayerID().has_value() || object->ID != Obj::EVENT) //hide events from players
ret.push_back(object);
} }
return ret; return ret;
} }
@ -492,9 +494,12 @@ std::vector <const CGObjectInstance *> CGameInfoCallback::getFlaggableObjects(in
std::vector<const CGObjectInstance *> ret; std::vector<const CGObjectInstance *> ret;
const TerrainTile *t = getTile(pos); const TerrainTile *t = getTile(pos);
ERROR_RET_VAL_IF(!t, "Not a valid tile requested!", ret); ERROR_RET_VAL_IF(!t, "Not a valid tile requested!", ret);
for(const CGObjectInstance *obj : t->blockingObjects) for(const auto & objectID : t->blockingObjects)
{
const auto * obj = getObj(objectID);
if(obj->tempOwner != PlayerColor::UNFLAGGABLE) if(obj->tempOwner != PlayerColor::UNFLAGGABLE)
ret.push_back(obj); ret.push_back(obj);
}
return ret; return ret;
} }
@ -724,7 +729,8 @@ bool CGameInfoCallback::isOwnedOrVisited(const CGObjectInstance *obj) const
return true; return true;
const TerrainTile *t = getTile(obj->visitablePos()); //get entrance tile const TerrainTile *t = getTile(obj->visitablePos()); //get entrance tile
const CGObjectInstance *visitor = t->visitableObjects.back(); //visitong hero if present or the object itself at last const ObjectInstanceID visitorID = t->visitableObjects.back(); //visitong hero if present or the object itself at last
const CGObjectInstance * visitor = getObj(visitorID);
return visitor->ID == Obj::HERO && canGetFullInfo(visitor); //owned or allied hero is a visitor return visitor->ID == Obj::HERO && canGetFullInfo(visitor); //owned or allied hero is a visitor
} }

View File

@ -1030,7 +1030,8 @@ BattleField CGameState::battleGetBattlefieldType(int3 tile, vstd::RNG & rand)
const TerrainTile &t = map->getTile(tile); const TerrainTile &t = map->getTile(tile);
auto * topObject = t.visitableObjects.front(); ObjectInstanceID topObjectID = t.visitableObjects.front();
const CGObjectInstance * topObject = gs->getObjInstance(topObjectID);
if(topObject && topObject->getBattlefield() != BattleField::NONE) if(topObject && topObject->getBattlefield() != BattleField::NONE)
{ {
return topObject->getBattlefield(); return topObject->getBattlefield();
@ -1129,9 +1130,9 @@ void CGameState::calculatePaths(const std::shared_ptr<PathfinderConfig> & config
* @return int3(-1, -1, -1) if the tile is unguarded, or the position of * @return int3(-1, -1, -1) if the tile is unguarded, or the position of
* the monster guarding the tile. * the monster guarding the tile.
*/ */
std::vector<CGObjectInstance*> CGameState::guardingCreatures (int3 pos) const std::vector<const CGObjectInstance*> CGameState::guardingCreatures (int3 pos) const
{ {
std::vector<CGObjectInstance*> guards; std::vector<const CGObjectInstance*> guards;
const int3 originalPos = pos; const int3 originalPos = pos;
if (!map->isInTheMap(pos)) if (!map->isInTheMap(pos))
return guards; return guards;
@ -1139,12 +1140,13 @@ std::vector<CGObjectInstance*> CGameState::guardingCreatures (int3 pos) const
const TerrainTile &posTile = map->getTile(pos); const TerrainTile &posTile = map->getTile(pos);
if (posTile.visitable()) if (posTile.visitable())
{ {
for (CGObjectInstance* obj : posTile.visitableObjects) for (ObjectInstanceID objectID : posTile.visitableObjects)
{ {
if(obj->isBlockedVisitable()) const CGObjectInstance * object = getObjInstance(objectID);
if(object->isBlockedVisitable())
{ {
if (obj->ID == Obj::MONSTER) // Monster if (object->ID == Obj::MONSTER) // Monster
guards.push_back(obj); guards.push_back(object);
} }
} }
} }
@ -1158,11 +1160,13 @@ std::vector<CGObjectInstance*> CGameState::guardingCreatures (int3 pos) const
const auto & tile = map->getTile(pos); const auto & tile = map->getTile(pos);
if (tile.visitable() && (tile.isWater() == posTile.isWater())) if (tile.visitable() && (tile.isWater() == posTile.isWater()))
{ {
for (CGObjectInstance* obj : tile.visitableObjects) for (ObjectInstanceID objectID : tile.visitableObjects)
{ {
if (obj->ID == Obj::MONSTER && map->checkForVisitableDir(pos, &map->getTile(originalPos), originalPos)) // Monster being able to attack investigated tile const CGObjectInstance * object = getObjInstance(objectID);
if (object->ID == Obj::MONSTER && map->checkForVisitableDir(pos, &map->getTile(originalPos), originalPos)) // Monster being able to attack investigated tile
{ {
guards.push_back(obj); guards.push_back(object);
} }
} }
} }

View File

@ -99,7 +99,7 @@ public:
bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile bool checkForVisitableDir(const int3 & src, const int3 & dst) const; //check if src tile is visitable from dst tile
void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const override; void calculatePaths(const std::shared_ptr<PathfinderConfig> & config) const override;
int3 guardingCreaturePosition (int3 pos) const override; int3 guardingCreaturePosition (int3 pos) const override;
std::vector<CGObjectInstance*> guardingCreatures (int3 pos) const; std::vector<const CGObjectInstance*> guardingCreatures (int3 pos) const;
/// Gets a artifact ID randomly and removes the selected artifact from this handler. /// Gets a artifact ID randomly and removes the selected artifact from this handler.
ArtifactID pickRandomArtifact(vstd::RNG & rand, int flags); ArtifactID pickRandomArtifact(vstd::RNG & rand, int flags);

View File

@ -96,9 +96,12 @@ int3 IBoatGenerator::bestLocation() const
if (tile->blocked()) if (tile->blocked())
{ {
bool hasBoat = false; bool hasBoat = false;
for (auto const * object : tile->blockingObjects) for (auto const & objectID : tile->blockingObjects)
{
const auto * object = getObject()->cb->getObj(objectID);
if (object->ID == Obj::BOAT || object->ID == Obj::HERO) if (object->ID == Obj::BOAT || object->ID == Obj::HERO)
hasBoat = true; hasBoat = true;
}
if (!hasBoat) if (!hasBoat)
continue; // tile is blocked, but not by boat -> check next potential position continue; // tile is blocked, but not by boat -> check next potential position
@ -122,7 +125,9 @@ IBoatGenerator::EGeneratorState IBoatGenerator::shipyardStatus() const
if(t->blockingObjects.empty()) if(t->blockingObjects.empty())
return GOOD; //OK return GOOD; //OK
if(t->blockingObjects.front()->ID == Obj::BOAT || t->blockingObjects.front()->ID == Obj::HERO) auto blockerObject = getObject()->cb->getObjInstance(t->blockingObjects.front());
if(blockerObject->ID == Obj::BOAT || blockerObject->ID == Obj::HERO)
return BOAT_ALREADY_BUILT; //blocked with boat return BOAT_ALREADY_BUILT; //blocked with boat
return TILE_BLOCKED; //blocked return TILE_BLOCKED; //blocked

View File

@ -322,14 +322,16 @@ bool CGTeleport::isConnected(const CGObjectInstance * src, const CGObjectInstanc
bool CGTeleport::isExitPassable(CGameState * gs, const CGHeroInstance * h, const CGObjectInstance * obj) bool CGTeleport::isExitPassable(CGameState * gs, const CGHeroInstance * h, const CGObjectInstance * obj)
{ {
auto * objTopVisObj = gs->getMap().getTile(obj->visitablePos()).topVisitableObj(); ObjectInstanceID topObjectID = gs->getMap().getTile(obj->visitablePos()).topVisitableObj();
if(objTopVisObj->ID == Obj::HERO) const CGObjectInstance * topObject = gs->getObjInstance(topObjectID);
if(topObject->ID == Obj::HERO)
{ {
if(h->id == objTopVisObj->id) // Just to be sure it's won't happen. if(h->id == topObject->id) // Just to be sure it's won't happen.
return false; return false;
// Check if it's friendly hero or not // Check if it's friendly hero or not
if(gs->getPlayerRelations(h->tempOwner, objTopVisObj->tempOwner) != PlayerRelations::ENEMIES) if(gs->getPlayerRelations(h->tempOwner, topObject->tempOwner) != PlayerRelations::ENEMIES)
{ {
// Exchange between heroes only possible via subterranean gates // Exchange between heroes only possible via subterranean gates
if(!dynamic_cast<const CGSubterraneanGate *>(obj)) if(!dynamic_cast<const CGSubterraneanGate *>(obj))

View File

@ -143,15 +143,10 @@ bool TerrainTile::isClear(const TerrainTile * from) const
return entrableTerrain(from) && !blocked(); return entrableTerrain(from) && !blocked();
} }
Obj TerrainTile::topVisitableId(bool excludeTop) const ObjectInstanceID TerrainTile::topVisitableObj(bool excludeTop) const
{
return topVisitableObj(excludeTop) ? topVisitableObj(excludeTop)->ID : Obj(Obj::NO_OBJ);
}
CGObjectInstance * TerrainTile::topVisitableObj(bool excludeTop) const
{ {
if(visitableObjects.empty() || (excludeTop && visitableObjects.size() == 1)) if(visitableObjects.empty() || (excludeTop && visitableObjects.size() == 1))
return nullptr; return {};
if(excludeTop) if(excludeTop)
return visitableObjects[visitableObjects.size()-2]; return visitableObjects[visitableObjects.size()-2];
@ -202,10 +197,10 @@ void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
{ {
TerrainTile & curt = terrain[zVal][xVal][yVal]; TerrainTile & curt = terrain[zVal][xVal][yVal];
if(total || obj->visitableAt(int3(xVal, yVal, zVal))) if(total || obj->visitableAt(int3(xVal, yVal, zVal)))
curt.visitableObjects -= obj; curt.visitableObjects -= obj->id;
if(total || obj->blockingAt(int3(xVal, yVal, zVal))) if(total || obj->blockingAt(int3(xVal, yVal, zVal)))
curt.blockingObjects -= obj; curt.blockingObjects -= obj->id;
} }
} }
} }
@ -224,10 +219,10 @@ void CMap::addBlockVisTiles(CGObjectInstance * obj)
{ {
TerrainTile & curt = terrain[zVal][xVal][yVal]; TerrainTile & curt = terrain[zVal][xVal][yVal];
if(obj->visitableAt(int3(xVal, yVal, zVal))) if(obj->visitableAt(int3(xVal, yVal, zVal)))
curt.visitableObjects.push_back(obj); curt.visitableObjects.push_back(obj->id);
if(obj->blockingAt(int3(xVal, yVal, zVal))) if(obj->blockingAt(int3(xVal, yVal, zVal)))
curt.blockingObjects.push_back(obj); curt.blockingObjects.push_back(obj->id);
} }
} }
} }
@ -300,12 +295,12 @@ bool CMap::checkForVisitableDir(const int3 & src, const TerrainTile * pom, const
{ {
if (!pom->entrableTerrain()) //rock is never accessible if (!pom->entrableTerrain()) //rock is never accessible
return false; return false;
for(auto * obj : pom->visitableObjects) //checking destination tile for(const auto & objID : pom->visitableObjects) //checking destination tile
{ {
if(!vstd::contains(pom->blockingObjects, obj)) //this visitable object is not blocking, ignore if(!vstd::contains(pom->blockingObjects, objID)) //this visitable object is not blocking, ignore
continue; continue;
if (!obj->appearance->isVisitableFrom(src.x - dst.x, src.y - dst.y)) if (!getObject(objID)->appearance->isVisitableFrom(src.x - dst.x, src.y - dst.y))
return false; return false;
} }
return true; return true;
@ -320,9 +315,10 @@ int3 CMap::guardingCreaturePosition (int3 pos) const
const TerrainTile &posTile = getTile(pos); const TerrainTile &posTile = getTile(pos);
if (posTile.visitable()) if (posTile.visitable())
{ {
for (CGObjectInstance* obj : posTile.visitableObjects) for (const auto & objID : posTile.visitableObjects)
{ {
if (obj->ID == Obj::MONSTER) const auto * object = getObject(objID);
if (object->ID == Obj::MONSTER)
return pos; return pos;
} }
} }
@ -340,12 +336,11 @@ int3 CMap::guardingCreaturePosition (int3 pos) const
const auto & tile = getTile(pos); const auto & tile = getTile(pos);
if (tile.visitable() && (tile.isWater() == water)) if (tile.visitable() && (tile.isWater() == water))
{ {
for (CGObjectInstance* obj : tile.visitableObjects) for (const auto & objID : tile.visitableObjects)
{ {
if (obj->ID == Obj::MONSTER && checkForVisitableDir(pos, &posTile, originalPos)) // Monster being able to attack investigated tile const auto * object = getObject(objID);
{ if (object->ID == Obj::MONSTER && checkForVisitableDir(pos, &posTile, originalPos)) // Monster being able to attack investigated tile
return pos; return pos;
}
} }
} }
} }
@ -361,8 +356,9 @@ int3 CMap::guardingCreaturePosition (int3 pos) const
const CGObjectInstance * CMap::getObjectiveObjectFrom(const int3 & pos, Obj type) const CGObjectInstance * CMap::getObjectiveObjectFrom(const int3 & pos, Obj type)
{ {
for (CGObjectInstance * object : getTile(pos).visitableObjects) for(const auto & objID : getTile(pos).visitableObjects)
{ {
const auto * object = getObject(objID);
if (object->ID == type) if (object->ID == type)
return object; return object;
} }

View File

@ -96,8 +96,7 @@ struct DLL_LINKAGE TerrainTile
/// Checks for blocking objects and terraint type (water / land). /// Checks for blocking objects and terraint type (water / land).
bool isClear(const TerrainTile * from = nullptr) const; bool isClear(const TerrainTile * from = nullptr) const;
/// Gets the ID of the top visitable object or -1 if there is none. /// Gets the ID of the top visitable object or -1 if there is none.
Obj topVisitableId(bool excludeTop = false) const; ObjectInstanceID topVisitableObj(bool excludeTop = false) const;
CGObjectInstance * topVisitableObj(bool excludeTop = false) const;
inline bool isWater() const; inline bool isWater() const;
inline bool isLand() const; inline bool isLand() const;
EDiggingStatus getDiggingStatus(bool excludeTop = true) const; EDiggingStatus getDiggingStatus(bool excludeTop = true) const;
@ -127,8 +126,8 @@ struct DLL_LINKAGE TerrainTile
/// 7th bit - whether tile is coastal (allows disembarking if land or block movement if water); 8th bit - Favorable Winds effect /// 7th bit - whether tile is coastal (allows disembarking if land or block movement if water); 8th bit - Favorable Winds effect
ui8 extTileFlags; ui8 extTileFlags;
std::vector<CGObjectInstance *> visitableObjects; std::vector<ObjectInstanceID> visitableObjects;
std::vector<CGObjectInstance *> blockingObjects; std::vector<ObjectInstanceID> blockingObjects;
template <typename Handler> template <typename Handler>
void serialize(Handler & h) void serialize(Handler & h)

View File

@ -2741,11 +2741,13 @@ void CMapLoaderH3M::afterRead()
const CGObjectInstance * mainTown = nullptr; const CGObjectInstance * mainTown = nullptr;
for(auto * obj : t.visitableObjects) for(ObjectInstanceID objID : t.visitableObjects)
{ {
if(obj->ID == Obj::TOWN || obj->ID == Obj::RANDOM_TOWN) const CGObjectInstance * object = map->getObject(objID);
if(object->ID == Obj::TOWN || object->ID == Obj::RANDOM_TOWN)
{ {
mainTown = obj; mainTown = object;
break; break;
} }
} }

View File

@ -1313,9 +1313,11 @@ void TryMoveHero::applyGs(CGameState *gs)
if(result == EMBARK) //hero enters boat at destination tile if(result == EMBARK) //hero enters boat at destination tile
{ {
const TerrainTile &tt = gs->getMap().getTile(h->convertToVisitablePos(end));
assert(destTile.visitableObjects.size() >= 1 && destTile.visitableObjects.back()->ID == Obj::BOAT); //the only visitable object at destination is Boat ObjectInstanceID topObjectID = tt.visitableObjects.back();
auto * boat = dynamic_cast<CGBoat *>(destTile.visitableObjects.back()); CGObjectInstance * topObject = gs->getObjInstance(topObjectID);
assert(tt.visitableObjects.size() >= 1 && topObject->ID == Obj::BOAT); //the only visitable object at destination is Boat
auto * boat = dynamic_cast<CGBoat *>(topObject);
assert(boat); assert(boat);
gs->getMap().removeBlockVisTiles(boat); //hero blockvis mask will be used, we don't need to duplicate it with boat gs->getMap().removeBlockVisTiles(boat); //hero blockvis mask will be used, we don't need to duplicate it with boat

View File

@ -103,6 +103,7 @@ PathNodeInfo::PathNodeInfo()
void PathNodeInfo::setNode(CGameState * gs, CGPathNode * n) void PathNodeInfo::setNode(CGameState * gs, CGPathNode * n)
{ {
node = n; node = n;
guarded = false;
if(coord != node->coord) if(coord != node->coord)
{ {
@ -110,23 +111,25 @@ void PathNodeInfo::setNode(CGameState * gs, CGPathNode * n)
coord = node->coord; coord = node->coord;
tile = gs->getTile(coord); tile = gs->getTile(coord);
nodeObject = tile->topVisitableObj(); nodeObject = nullptr;
nodeHero = nullptr;
if(nodeObject && nodeObject->ID == Obj::HERO) ObjectInstanceID topObjectID = tile->topVisitableObj();
if (topObjectID.hasValue())
{ {
nodeHero = dynamic_cast<const CGHeroInstance *>(nodeObject); nodeObject = gs->getObjInstance(topObjectID);
nodeObject = tile->topVisitableObj(true);
if(!nodeObject) if (nodeObject->ID == Obj::HERO)
nodeObject = nodeHero; {
} nodeHero = dynamic_cast<const CGHeroInstance *>(nodeObject);
else ObjectInstanceID bottomObjectID = tile->topVisitableObj(true);
{
nodeHero = nullptr; if (bottomObjectID.hasValue())
nodeObject = gs->getObjInstance(bottomObjectID);
}
} }
} }
guarded = false;
} }
void PathNodeInfo::updateInfo(CPathfinderHelper * hlp, CGameState * gs) void PathNodeInfo::updateInfo(CPathfinderHelper * hlp, CGameState * gs)

View File

@ -251,7 +251,8 @@ TeleporterTilesVector CPathfinderHelper::getAllowedTeleportChannelExits(const Te
auto pos = obj->getBlockedPos(); auto pos = obj->getBlockedPos();
for(const auto & p : pos) for(const auto & p : pos)
{ {
if(gs->getMap().getTile(p).topVisitableId() == obj->ID) ObjectInstanceID topObject = gs->getMap().getTile(p).topVisitableObj();
if(topObject.hasValue() && getObj(topObject)->ID == obj->ID)
allowedExits.push_back(p); allowedExits.push_back(p);
} }
} }

View File

@ -34,7 +34,10 @@ namespace PathfinderUtil
case ELayer::SAIL: case ELayer::SAIL:
if(tinfo.visitable()) if(tinfo.visitable())
{ {
if(tinfo.visitableObjects.front()->ID == Obj::SANCTUARY && tinfo.visitableObjects.back()->ID == Obj::HERO && tinfo.visitableObjects.back()->tempOwner != player) //non-owned hero stands on Sanctuary auto frontVisitable = gs->getObjInstance(tinfo.visitableObjects.front());
auto backVisitable = gs->getObjInstance(tinfo.visitableObjects.front());
if(frontVisitable->ID == Obj::SANCTUARY && backVisitable->ID == Obj::HERO && backVisitable->getOwner() != player) //non-owned hero stands on Sanctuary
{ {
return EPathAccessibility::BLOCKED; return EPathAccessibility::BLOCKED;
} }
@ -43,8 +46,10 @@ namespace PathfinderUtil
bool hasBlockedVisitable = false; bool hasBlockedVisitable = false;
bool hasVisitable = false; bool hasVisitable = false;
for(const CGObjectInstance * obj : tinfo.visitableObjects) for(const auto objID : tinfo.visitableObjects)
{ {
auto obj = gs->getObjInstance(objID);
if(obj->isBlockedVisitable()) if(obj->isBlockedVisitable())
hasBlockedVisitable = true; hasBlockedVisitable = true;
else if(!obj->passableFor(player) && obj->ID != Obj::EVENT) else if(!obj->passableFor(player) && obj->ID != Obj::EVENT)

View File

@ -262,7 +262,11 @@ bool ScuttleBoatMechanics::canBeCastAtImpl(spells::Problem & problem, const CGam
return false; return false;
const TerrainTile * t = cb->getTile(pos); const TerrainTile * t = cb->getTile(pos);
if(!t || t->visitableObjects.empty() || t->visitableObjects.back()->ID != Obj::BOAT) if(!t || t->visitableObjects.empty())
return false;
const CGObjectInstance * topObject = cb->getObj(t->visitableObjects.back());
if (topObject->ID != Obj::BOAT)
return false; return false;
return true; return true;
@ -286,7 +290,7 @@ ESpellCastResult ScuttleBoatMechanics::applyAdventureEffects(SpellCastEnvironmen
RemoveObject ro; RemoveObject ro;
ro.initiator = parameters.caster->getCasterOwner(); ro.initiator = parameters.caster->getCasterOwner();
ro.objectID = t.visitableObjects.back()->id; ro.objectID = t.visitableObjects.back();
env->apply(ro); env->apply(ro);
return ESpellCastResult::OK; return ESpellCastResult::OK;
} }
@ -476,7 +480,8 @@ ESpellCastResult TownPortalMechanics::applyAdventureEffects(SpellCastEnvironment
{ {
const TerrainTile & tile = env->getMap()->getTile(parameters.pos); const TerrainTile & tile = env->getMap()->getTile(parameters.pos);
auto * const topObj = tile.topVisitableObj(false); ObjectInstanceID topObjID = tile.topVisitableObj(false);
const CGObjectInstance * topObj = env->getMap()->getObject(topObjID);
if(!topObj) if(!topObj)
{ {
@ -556,7 +561,9 @@ void TownPortalMechanics::endCast(SpellCastEnvironment * env, const AdventureSpe
else else
{ {
const TerrainTile & tile = env->getMap()->getTile(parameters.pos); const TerrainTile & tile = env->getMap()->getTile(parameters.pos);
auto * const topObj = tile.topVisitableObj(false); ObjectInstanceID topObjID = tile.topVisitableObj(false);
const CGObjectInstance * topObj = env->getMap()->getObject(topObjID);
destination = dynamic_cast<const CGTownInstance*>(topObj); destination = dynamic_cast<const CGTownInstance*>(topObj);
} }

View File

@ -128,12 +128,17 @@ void ObjectPickerLayer::highlight(std::function<bool(const CGObjectInstance *)>
for(int i = 0; i < map->width; ++i) for(int i = 0; i < map->width; ++i)
{ {
auto tl = map->getTile(int3(i, j, scene->level)); auto tl = map->getTile(int3(i, j, scene->level));
auto * obj = tl.topVisitableObj(); ObjectInstanceID objID = tl.topVisitableObj();
if(!obj && !tl.blockingObjects.empty()) if(!objID.hasValue() && !tl.blockingObjects.empty())
obj = tl.blockingObjects.front(); objID = tl.blockingObjects.front();
if (objID.hasValue())
{
const CGObjectInstance * obj = map->getObject(objID);
if(obj && predicate(obj)) if(obj && predicate(obj))
possibleObjects.insert(obj); possibleObjects.insert(obj);
}
} }
} }
} }

View File

@ -826,13 +826,17 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
CGObjectInstance * guardian = nullptr; CGObjectInstance * guardian = nullptr;
if (!t.visitableObjects.empty()) if (!t.visitableObjects.empty())
objectToVisit = t.visitableObjects.back(); objectToVisit = gameState()->getObjInstance(t.visitableObjects.back());
if (isInTheMap(guardPos)) if (isInTheMap(guardPos))
{ {
for (auto const & object : getTile(guardPos)->visitableObjects) for (auto const & objectID : getTile(guardPos)->visitableObjects)
{
auto object = gameState()->getObjInstance(objectID);
if (object->ID == MapObjectID::MONSTER) // exclude other objects, such as hero flying above monster if (object->ID == MapObjectID::MONSTER) // exclude other objects, such as hero flying above monster
guardian = object; guardian = object;
}
} }
const bool embarking = !h->boat && objectToVisit && objectToVisit->ID == Obj::BOAT; const bool embarking = !h->boat && objectToVisit && objectToVisit->ID == Obj::BOAT;
@ -909,10 +913,9 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
// should be called if hero changes tile but before applying TryMoveHero package // should be called if hero changes tile but before applying TryMoveHero package
auto leaveTile = [&]() auto leaveTile = [&]()
{ {
for (CGObjectInstance *obj : gs->getMap().getTile(h->visitablePos()).visitableObjects) for(const auto & objID : gs->getMap().getTile(h->visitablePos()).visitableObjects)
{ gameState()->getObjInstance(objID)->onHeroLeave(h);
obj->onHeroLeave(h);
}
this->getTilesInRange(tmh.fowRevealed, h->getSightCenter()+(tmh.end-tmh.start), h->getSightRadius(), ETileVisibility::HIDDEN, h->tempOwner); this->getTilesInRange(tmh.fowRevealed, h->getSightCenter()+(tmh.end-tmh.start), h->getSightRadius(), ETileVisibility::HIDDEN, h->tempOwner);
}; };
@ -956,12 +959,14 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
//interaction with blocking object (like resources) //interaction with blocking object (like resources)
auto blockingVisit = [&]() -> bool auto blockingVisit = [&]() -> bool
{ {
for (CGObjectInstance *obj : t.visitableObjects) for (ObjectInstanceID objectID : t.visitableObjects)
{ {
if(h->boat && !obj->isBlockedVisitable() && !h->boat->onboardVisitAllowed) const CGObjectInstance * object = getObj(objectID);
if(h->boat && !object->isBlockedVisitable() && !h->boat->onboardVisitAllowed)
return doMove(TryMoveHero::SUCCESS, this->IGNORE_GUARDS, DONT_VISIT_DEST, REMAINING_ON_TILE); return doMove(TryMoveHero::SUCCESS, this->IGNORE_GUARDS, DONT_VISIT_DEST, REMAINING_ON_TILE);
if (obj != h && obj->isBlockedVisitable() && !obj->passableFor(h->tempOwner)) if (object != h && object->isBlockedVisitable() && !object->passableFor(h->tempOwner))
{ {
EVisitDest visitDest = VISIT_DEST; EVisitDest visitDest = VISIT_DEST;
if(h->boat && !h->boat->onboardVisitAllowed) if(h->boat && !h->boat->onboardVisitAllowed)
@ -3657,10 +3662,10 @@ void CGameHandler::visitObjectOnTile(const TerrainTile &t, const CGHeroInstance
if (!t.visitableObjects.empty()) if (!t.visitableObjects.empty())
{ {
//to prevent self-visiting heroes on space press //to prevent self-visiting heroes on space press
if (t.visitableObjects.back() != h) if (t.visitableObjects.back() != h->id)
objectVisited(t.visitableObjects.back(), h); objectVisited(gameState()->getObjInstance(t.visitableObjects.back()), h);
else if (t.visitableObjects.size() > 1) else if (t.visitableObjects.size() > 1)
objectVisited(*(t.visitableObjects.end()-2),h); objectVisited(gameState()->getObjInstance(*(t.visitableObjects.end()-2)),h);
} }
} }

View File

@ -199,7 +199,7 @@ bool HeroPoolProcessor::hireHero(const ObjectInstanceID & objectID, const HeroTy
return false; return false;
} }
if(gameHandler->getTile(mapObject->visitablePos())->visitableObjects.back() != mapObject && gameHandler->complain("Tavern entry must be unoccupied!")) if(gameHandler->getTile(mapObject->visitablePos())->visitableObjects.back() != mapObject->id && gameHandler->complain("Tavern entry must be unoccupied!"))
return false; return false;
} }