Răsfoiți Sursa

Replaced most of accesses to CGObjectInstance::pos with anchorPoint()

Ivan Savenko 1 an în urmă
părinte
comite
a8f8c3f4b1
40 a modificat fișierele cu 142 adăugiri și 150 ștergeri
  1. 2 2
      AI/Nullkiller/AIGateway.cpp
  2. 1 1
      AI/Nullkiller/Goals/BuildThis.cpp
  3. 1 1
      AI/VCAI/Goals/FindObj.cpp
  4. 6 6
      AI/VCAI/VCAI.cpp
  5. 1 1
      client/HeroMovementController.cpp
  6. 3 3
      client/adventureMap/MapAudioPlayer.cpp
  7. 2 2
      client/mapView/MapRenderer.cpp
  8. 2 2
      client/mapView/MapRendererContext.cpp
  9. 3 3
      client/mapView/MapRendererContextState.cpp
  10. 1 1
      client/mapView/MapViewController.cpp
  11. 7 7
      client/mapView/mapHandler.cpp
  12. 2 2
      client/windows/CQuestLog.cpp
  13. 1 1
      lib/CGameInfoCallback.cpp
  14. 13 18
      lib/gameState/CGameState.cpp
  15. 2 2
      lib/gameState/CGameStateCampaign.cpp
  16. 3 3
      lib/mapObjects/CGCreature.cpp
  17. 2 2
      lib/mapObjects/CGHeroInstance.cpp
  18. 17 20
      lib/mapObjects/CGObjectInstance.cpp
  19. 8 9
      lib/mapObjects/CGObjectInstance.h
  20. 1 1
      lib/mapObjects/CGTownInstance.cpp
  21. 0 2
      lib/mapObjects/CGTownInstance.h
  22. 1 1
      lib/mapObjects/CQuest.cpp
  23. 1 1
      lib/mapObjects/IObjectInterface.cpp
  24. 1 1
      lib/mapObjects/IObjectInterface.h
  25. 11 11
      lib/mapObjects/MiscObjects.cpp
  26. 2 2
      lib/mapObjects/TownBuildingInstance.cpp
  27. 1 1
      lib/mapObjects/TownBuildingInstance.h
  28. 14 14
      lib/mapping/CMap.cpp
  29. 1 1
      lib/mapping/CMapOperation.cpp
  30. 2 2
      lib/mapping/MapFormatH3M.cpp
  31. 1 1
      lib/mapping/MapFormatJson.cpp
  32. 2 2
      lib/networkPacks/NetPacksLib.cpp
  33. 7 7
      lib/rmg/RmgObject.cpp
  34. 2 2
      lib/rmg/modificators/QuestArtifactPlacer.cpp
  35. 3 3
      lib/spells/AdventureSpellMechanics.cpp
  36. 1 1
      mapeditor/maphandler.cpp
  37. 1 1
      mapeditor/mapsettings/abstractsettings.cpp
  38. 6 6
      mapeditor/scenelayer.cpp
  39. 6 3
      server/CGameHandler.cpp
  40. 1 1
      server/NetPacksServer.cpp

+ 2 - 2
AI/Nullkiller/AIGateway.cpp

@@ -864,7 +864,7 @@ void AIGateway::makeTurn()
 
 void AIGateway::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
 {
-	LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->getNameTranslated() % obj->getObjectName() % obj->pos.toString());
+	LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->getNameTranslated() % obj->getObjectName() % obj->anchorPos().toString());
 	switch(obj->ID)
 	{
 	case Obj::TOWN:
@@ -1455,7 +1455,7 @@ bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
 void AIGateway::buildStructure(const CGTownInstance * t, BuildingID building)
 {
 	auto name = t->town->buildings.at(building)->getNameTranslated();
-	logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->getNameTranslated(), t->pos.toString());
+	logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->getNameTranslated(), t->anchorPos().toString());
 	cb->buildBuilding(t, building); //just do this;
 }
 

+ 1 - 1
AI/Nullkiller/Goals/BuildThis.cpp

@@ -52,7 +52,7 @@ void BuildThis::accept(AIGateway * ai)
 		if(cb->canBuildStructure(town, b) == EBuildingState::ALLOWED)
 		{
 			logAi->debug("Player %d will build %s in town of %s at %s",
-				ai->playerID, town->town->buildings.at(b)->getNameTranslated(), town->getNameTranslated(), town->pos.toString());
+				ai->playerID, town->town->buildings.at(b)->getNameTranslated(), town->getNameTranslated(), town->anchorPos().toString());
 			cb->buildBuilding(town, b);
 
 			return;

+ 1 - 1
AI/VCAI/Goals/FindObj.cpp

@@ -46,7 +46,7 @@ TSubgoal FindObj::whatToDoToAchieve()
 			}
 		}
 	}
-	if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is
+	if(o && ai->isAccessible(o->visitablePos())) //we don't use isAccessibleForHero as we don't know which hero it is
 		return sptr(VisitObj(o->id.getNum()));
 	else
 		return sptr(Explore());

+ 6 - 6
AI/VCAI/VCAI.cpp

@@ -1032,7 +1032,7 @@ void VCAI::mainLoop()
 
 void VCAI::performObjectInteraction(const CGObjectInstance * obj, HeroPtr h)
 {
-	LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->getNameTranslated() % obj->getObjectName() % obj->pos.toString());
+	LOG_TRACE_PARAMS(logAi, "Hero %s and object %s at %s", h->getNameTranslated() % obj->getObjectName() % obj->anchorPos().toString());
 	switch(obj->ID)
 	{
 	case Obj::TOWN:
@@ -1417,11 +1417,11 @@ void VCAI::wander(HeroPtr h)
 				//TODO pick the truly best
 				const CGTownInstance * t = *boost::max_element(townsNotReachable, compareReinforcements);
 				logAi->debug("%s can't reach any town, we'll try to make our way to %s at %s", h->getNameTranslated(), t->getNameTranslated(), t->visitablePos().toString());
-				int3 pos1 = h->pos;
+				int3 posBefore = h->visitablePos();
 				striveToGoal(sptr(Goals::ClearWayTo(t->visitablePos()).sethero(h))); //TODO: drop "strive", add to mainLoop
 				//if out hero is stuck, we may need to request another hero to clear the way we see
 
-				if(pos1 == h->pos && h == primaryHero()) //hero can't move
+				if(posBefore == h->visitablePos() && h == primaryHero()) //hero can't move
 				{
 					if(canRecruitAnyHero(t))
 						recruitHero(t);
@@ -1471,7 +1471,7 @@ void VCAI::wander(HeroPtr h)
 				{
 					auto chosenObject = cb->getObjInstance(ObjectInstanceID(bestObjectGoal->objid));
 					if(chosenObject != nullptr)
-						logAi->debug("Of all %d destinations, object %s at pos=%s seems nice", dests.size(), chosenObject->getObjectName(), chosenObject->pos.toString());
+						logAi->debug("Of all %d destinations, object %s at pos=%s seems nice", dests.size(), chosenObject->getObjectName(), chosenObject->anchorPos().toString());
 				}
 				else
 					logAi->debug("Trying to realize goal of type %s as part of wandering.", bestObjectGoal->name());
@@ -1995,7 +1995,7 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 void VCAI::buildStructure(const CGTownInstance * t, BuildingID building)
 {
 	auto name = t->town->buildings.at(building)->getNameTranslated();
-	logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->getNameTranslated(), t->pos.toString());
+	logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->getNameTranslated(), t->anchorPos().toString());
 	cb->buildBuilding(t, building); //just do this;
 }
 
@@ -2081,7 +2081,7 @@ void VCAI::tryRealize(Goals::BuildThis & g)
 		if (cb->canBuildStructure(t, b) == EBuildingState::ALLOWED)
 		{
 			logAi->debug("Player %d will build %s in town of %s at %s",
-				playerID, t->town->buildings.at(b)->getNameTranslated(), t->getNameTranslated(), t->pos.toString());
+				playerID, t->town->buildings.at(b)->getNameTranslated(), t->getNameTranslated(), t->anchorPos().toString());
 			cb->buildBuilding(t, b);
 			throw goalFulfilledException(sptr(g));
 		}

+ 1 - 1
client/HeroMovementController.cpp

@@ -375,7 +375,7 @@ void HeroMovementController::sendMovementRequest(const CGHeroInstance * h, const
 	{
 		updateMovementSound(h, currNode.coord, nextNode.coord, nextNode.action);
 
-		assert(h->pos.z == nextNode.coord.z); // Z should change only if it's movement via teleporter and in this case this code shouldn't be executed at all
+		assert(h->anchorPos().z == nextNode.coord.z); // Z should change only if it's movement via teleporter and in this case this code shouldn't be executed at all
 
 		logGlobal->trace("Requesting hero movement to %s", nextNode.coord.toString());
 

+ 3 - 3
client/adventureMap/MapAudioPlayer.cpp

@@ -81,9 +81,9 @@ void MapAudioPlayer::addObject(const CGObjectInstance * obj)
 		{
 			for(int fy = 0; fy < obj->getHeight(); ++fy)
 			{
-				int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);
+				int3 currTile(obj->anchorPos().x - fx, obj->anchorPos().y - fy, obj->anchorPos().z);
 
-				if(LOCPLINT->cb->isInTheMap(currTile) && obj->coveringAt(currTile.x, currTile.y))
+				if(LOCPLINT->cb->isInTheMap(currTile) && obj->coveringAt(currTile))
 					objects[currTile.z][currTile.x][currTile.y].push_back(obj->id);
 			}
 		}
@@ -108,7 +108,7 @@ void MapAudioPlayer::addObject(const CGObjectInstance * obj)
 
 		for(const auto & tile : tiles)
 		{
-			int3 currTile = obj->pos + tile;
+			int3 currTile = obj->anchorPos() + tile;
 
 			if(LOCPLINT->cb->isInTheMap(currTile))
 				objects[currTile.z][currTile.x][currTile.y].push_back(obj->id);

+ 2 - 2
client/mapView/MapRenderer.cpp

@@ -591,8 +591,8 @@ void MapRendererOverlay::renderTile(IMapRendererContext & context, Canvas & targ
 
 			if(context.objectTransparency(objectID, coordinates) > 0 && !context.isActiveHero(object))
 			{
-				visitable |= object->visitableAt(coordinates.x, coordinates.y);
-				blocking |= object->blockingAt(coordinates.x, coordinates.y);
+				visitable |= object->visitableAt(coordinates);
+				blocking |= object->blockingAt(coordinates);
 			}
 		}
 

+ 2 - 2
client/mapView/MapRendererContext.cpp

@@ -120,7 +120,7 @@ size_t MapRendererBaseContext::objectGroupIndex(ObjectInstanceID objectID) const
 Point MapRendererBaseContext::objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const
 {
 	const CGObjectInstance * object = getObject(objectID);
-	int3 offsetTiles(object->getPosition() - coordinates);
+	int3 offsetTiles(object->anchorPos() - coordinates);
 	return Point(offsetTiles) * Point(32, 32);
 }
 
@@ -498,7 +498,7 @@ size_t MapRendererWorldViewContext::overlayImageIndex(const int3 & coordinates)
 	{
 		const auto * object = getObject(objectID);
 
-		if(!object->visitableAt(coordinates.x, coordinates.y))
+		if(!object->visitableAt(coordinates))
 			continue;
 
 		ObjectPosInfo info(object);

+ 3 - 3
client/mapView/MapRendererContextState.cpp

@@ -49,9 +49,9 @@ void MapRendererContextState::addObject(const CGObjectInstance * obj)
 	{
 		for(int fy = 0; fy < obj->getHeight(); ++fy)
 		{
-			int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);
+			int3 currTile(obj->anchorPos().x - fx, obj->anchorPos().y - fy, obj->anchorPos().z);
 
-			if(LOCPLINT->cb->isInTheMap(currTile) && obj->coveringAt(currTile.x, currTile.y))
+			if(LOCPLINT->cb->isInTheMap(currTile) && obj->coveringAt(currTile))
 			{
 				auto & container = objects[currTile.z][currTile.x][currTile.y];
 
@@ -73,7 +73,7 @@ void MapRendererContextState::addMovingObject(const CGObjectInstance * object, c
 	{
 		for(int y = yFrom; y <= yDest; ++y)
 		{
-			int3 currTile(x, y, object->pos.z);
+			int3 currTile(x, y, object->anchorPos().z);
 
 			if(LOCPLINT->cb->isInTheMap(currTile))
 			{

+ 1 - 1
client/mapView/MapViewController.cpp

@@ -317,7 +317,7 @@ bool MapViewController::isEventVisible(const CGObjectInstance * obj, const Playe
 	if(obj->isVisitable())
 		return context->isVisible(obj->visitablePos());
 	else
-		return context->isVisible(obj->pos);
+		return context->isVisible(obj->anchorPos());
 }
 
 bool MapViewController::isEventVisible(const CGHeroInstance * obj, const int3 & from, const int3 & dest)

+ 7 - 7
client/mapView/mapHandler.cpp

@@ -59,7 +59,7 @@ std::string CMapHandler::getTerrainDescr(const int3 & pos, bool rightClick) cons
 
 	for(const auto & object : map->objects)
 	{
-		if(object && object->coveringAt(pos.x, pos.y) && object->pos.z == pos.z && object->isTile2Terrain())
+		if(object && object->coveringAt(pos) && object->isTile2Terrain())
 		{
 			result = object->getObjectName();
 			break;
@@ -103,15 +103,15 @@ bool CMapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObj
 
 	for(const auto & aOffset : a->getBlockedOffsets())
 	{
-		int3 testTarget = a->pos + aOffset + int3(0, 1, 0);
-		if(b->blockingAt(testTarget.x, testTarget.y))
+		int3 testTarget = a->anchorPos() + aOffset + int3(0, 1, 0);
+		if(b->blockingAt(testTarget))
 			bBlocksA += 1;
 	}
 
 	for(const auto & bOffset : b->getBlockedOffsets())
 	{
-		int3 testTarget = b->pos + bOffset + int3(0, 1, 0);
-		if(a->blockingAt(testTarget.x, testTarget.y))
+		int3 testTarget = b->anchorPos() + bOffset + int3(0, 1, 0);
+		if(a->blockingAt(testTarget))
 			aBlocksB += 1;
 	}
 
@@ -126,8 +126,8 @@ bool CMapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObj
 		return aBlocksB < bBlocksA;
 
 	// object that don't have clear priority via tile blocking will appear based on their row
-	if(a->pos.y != b->pos.y)
-		return a->pos.y < b->pos.y;
+	if(a->anchorPos().y != b->anchorPos().y)
+		return a->anchorPos().y < b->anchorPos().y;
 
 	// heroes should appear on top of objects on the same tile
 	if(b->ID==Obj::HERO && a->ID!=Obj::HERO)

+ 2 - 2
client/windows/CQuestLog.cpp

@@ -78,7 +78,7 @@ void CQuestMinimap::addQuestMarks (const QuestInfo * q)
 
 	int3 tile;
 	if (q->obj)
-		tile = q->obj->pos;
+		tile = q->obj->visitablePos();
 	else
 		tile = q->tile;
 
@@ -104,7 +104,7 @@ void CQuestMinimap::update()
 void CQuestMinimap::iconClicked()
 {
 	if(currentQuest->obj)
-		adventureInt->centerOnTile(currentQuest->obj->pos);
+		adventureInt->centerOnTile(currentQuest->obj->visitablePos());
 	//moveAdvMapSelection();
 }
 

+ 1 - 1
lib/CGameInfoCallback.cpp

@@ -539,7 +539,7 @@ EDiggingStatus CGameInfoCallback::getTileDigStatus(int3 tile, bool verbose) cons
 
 	for(const auto & object : gs->map->objects)
 	{
-		if(object && object->ID == Obj::HOLE && object->pos == tile)
+		if(object && object->ID == Obj::HOLE && object->anchorPos() == tile)
 			return EDiggingStatus::TILE_OCCUPIED;
 	}
 	return getTile(tile)->getDiggingStatus();

+ 13 - 18
lib/gameState/CGameState.cpp

@@ -449,7 +449,7 @@ void CGameState::initGrailPosition()
 		//remove tiles with holes
 		for(auto & elem : map->objects)
 			if(elem && elem->ID == Obj::HOLE)
-				allowedPos -= elem->pos;
+				allowedPos -= elem->anchorPos();
 
 		if(!allowedPos.empty())
 		{
@@ -495,7 +495,7 @@ void CGameState::randomizeMapObjects()
 			{
 				for (int j = 0; j < object->getHeight() ; j++)
 				{
-					int3 pos = object->pos - int3(i,j,0);
+					int3 pos = object->anchorPos() - int3(i,j,0);
 					if(map->isInTheMap(pos)) map->getTile(pos).extTileFlags |= 128;
 				}
 			}
@@ -530,7 +530,7 @@ void CGameState::placeStartingHero(const PlayerColor & playerColor, const HeroTy
 {
 	for(auto town : map->towns)
 	{
-		if(town->getPosition() == townPos)
+		if(town->anchorPos() == townPos)
 		{
 			townPos = town->visitablePos();
 			break;
@@ -545,8 +545,7 @@ void CGameState::placeStartingHero(const PlayerColor & playerColor, const HeroTy
 	hero->setHeroType(heroTypeId);
 	hero->tempOwner = playerColor;
 
-	hero->pos = townPos;
-	hero->pos += hero->getVisitableOffset();
+	hero->setAnchorPos(townPos + hero->getVisitableOffset());
 	map->getEditManager()->insertObject(hero);
 }
 
@@ -614,7 +613,7 @@ void CGameState::initHeroes()
 			auto boat = dynamic_cast<CGBoat*>(handler->create(callback, nullptr));
 			handler->configureObject(boat, gs->getRandomGenerator());
 
-			boat->pos = hero->pos;
+			boat->setAnchorPos(hero->anchorPos());
 			boat->appearance = handler->getTemplates().front();
 			boat->id = ObjectInstanceID(static_cast<si32>(gs->map->objects.size()));
 
@@ -964,22 +963,18 @@ void CGameState::placeHeroesInTowns()
 		{
 			for(CGTownInstance * t : player.second.getTowns())
 			{
-				if(h->visitablePos().z != t->visitablePos().z)
-					continue;
-
-				bool heroOnTownBlockableTile = t->blockingAt(h->visitablePos().x, h->visitablePos().y);
+				bool heroOnTownBlockableTile = t->blockingAt(h->visitablePos());
 
 				// current hero position is at one of blocking tiles of current town
 				// assume that this hero should be visiting the town (H3M format quirk) and move hero to correct position
 				if (heroOnTownBlockableTile)
 				{
-					int3 correctedPos = h->convertFromVisitablePos(t->visitablePos());
-
 					map->removeBlockVisTiles(h);
-					h->pos = correctedPos;
+					int3 correctedPos = h->convertFromVisitablePos(t->visitablePos());
+					h->setAnchorPos(correctedPos);
 					map->addBlockVisTiles(h);
 
-					assert(t->visitableAt(h->visitablePos().x, h->visitablePos().y));
+					assert(t->visitableAt(h->visitablePos()));
 				}
 			}
 		}
@@ -1001,7 +996,7 @@ void CGameState::initVisitingAndGarrisonedHeroes()
 				if(h->visitablePos().z != t->visitablePos().z)
 					continue;
 
-				if (t->visitableAt(h->visitablePos().x, h->visitablePos().y))
+				if (t->visitableAt(h->visitablePos()))
 				{
 					assert(t->visitingHero == nullptr);
 					t->setVisitingHero(h);
@@ -1066,7 +1061,7 @@ BattleField CGameState::battleGetBattlefieldType(int3 tile, vstd::RNG & rand)
 	for(auto &obj : map->objects)
 	{
 		//look only for objects covering given tile
-		if( !obj || obj->pos.z != tile.z || !obj->coveringAt(tile.x, tile.y))
+		if( !obj || !obj->coveringAt(tile))
 			continue;
 
 		auto customBattlefield = obj->getBattlefield();
@@ -1250,10 +1245,10 @@ bool CGameState::isVisible(const CGObjectInstance * obj, const std::optional<Pla
 	{
 		for(int fx=0; fx < obj->getWidth(); ++fx)
 		{
-			int3 pos = obj->pos + int3(-fx, -fy, 0);
+			int3 pos = obj->anchorPos() + int3(-fx, -fy, 0);
 
 			if ( map->isInTheMap(pos) &&
-				 obj->coveringAt(pos.x, pos.y) &&
+				 obj->coveringAt(pos) &&
 				 isVisible(pos, *player))
 				return true;
 		}

+ 2 - 2
lib/gameState/CGameStateCampaign.cpp

@@ -368,7 +368,7 @@ void CGameStateCampaign::replaceHeroesPlaceholders()
 		heroToPlace->id = campaignHeroReplacement.heroPlaceholderId;
 		if(heroPlaceholder->tempOwner.isValidPlayer())
 			heroToPlace->tempOwner = heroPlaceholder->tempOwner;
-		heroToPlace->pos = heroPlaceholder->pos;
+		heroToPlace->setAnchorPos(heroPlaceholder->anchorPos());
 		heroToPlace->type = heroToPlace->getHeroType().toHeroType();
 		heroToPlace->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, heroToPlace->type->heroClass->getIndex())->getTemplates().front();
 
@@ -655,7 +655,7 @@ void CGameStateCampaign::initTowns()
 		if (!owner->human)
 			continue;
 
-		if (town->pos != pi.posOfMainTown)
+		if (town->anchorPos() != pi.posOfMainTown)
 			continue;
 
 		BuildingID newBuilding;

+ 3 - 3
lib/mapObjects/CGCreature.cpp

@@ -33,7 +33,7 @@ std::string CGCreature::getHoverText(PlayerColor player) const
 	if(stacks.empty())
 	{
 		//should not happen...
-		logGlobal->error("Invalid stack at tile %s: subID=%d; id=%d", pos.toString(), getCreature(), id.getNum());
+		logGlobal->error("Invalid stack at tile %s: subID=%d; id=%d", anchorPos().toString(), getCreature(), id.getNum());
 		return "INVALID_STACK";
 	}
 
@@ -562,7 +562,7 @@ bool CGCreature::containsUpgradedStack() const
 	float c = 5325.181015f;
 	float d = 32788.727920f;
 
-	int val = static_cast<int>(std::floor(a * pos.x + b * pos.y + c * pos.z + d));
+	int val = static_cast<int>(std::floor(a * visitablePos().x + b * visitablePos().y + c * visitablePos().z + d));
 	return ((val % 32768) % 100) < 50;
 }
 
@@ -591,7 +591,7 @@ int CGCreature::getNumberOfStacks(const CGHeroInstance *hero) const
 	ui32 c = 1943276003u;
 	ui32 d = 3174620878u;
 
-	ui32 R1 = a * static_cast<ui32>(pos.x) + b * static_cast<ui32>(pos.y) + c * static_cast<ui32>(pos.z) + d;
+	ui32 R1 = a * static_cast<ui32>(visitablePos().x) + b * static_cast<ui32>(visitablePos().y) + c * static_cast<ui32>(visitablePos().z) + d;
 	ui32 R2 = (R1 >> 16) & 0x7fff;
 
 	int R4 = R2 % 100 + 1;

+ 2 - 2
lib/mapObjects/CGHeroInstance.cpp

@@ -1514,11 +1514,11 @@ bool CGHeroInstance::hasVisions(const CGObjectInstance * target, BonusSubtypeID
 	if (visionsMultiplier > 0)
 		vstd::amax(visionsRange, 3); //minimum range is 3 tiles, but only if VISIONS bonus present
 
-	const int distance = static_cast<int>(target->pos.dist2d(visitablePos()));
+	const int distance = static_cast<int>(target->anchorPos().dist2d(visitablePos()));
 
 	//logGlobal->debug(boost::str(boost::format("Visions: dist %d, mult %d, range %d") % distance % visionsMultiplier % visionsRange));
 
-	return (distance < visionsRange) && (target->pos.z == pos.z);
+	return (distance < visionsRange) && (target->anchorPos().z == anchorPos().z);
 }
 
 std::string CGHeroInstance::getHeroTypeName() const

+ 17 - 20
lib/mapObjects/CGObjectInstance.cpp

@@ -54,14 +54,14 @@ MapObjectSubID CGObjectInstance::getObjTypeIndex() const
 	return subID;
 }
 
-int3 CGObjectInstance::getPosition() const
+int3 CGObjectInstance::anchorPos() const
 {
 	return pos;
 }
 
 int3 CGObjectInstance::getTopVisiblePos() const
 {
-	return pos - appearance->getTopVisibleOffset();
+	return anchorPos() - appearance->getTopVisibleOffset();
 }
 
 void CGObjectInstance::setOwner(const PlayerColor & ow)
@@ -69,6 +69,11 @@ void CGObjectInstance::setOwner(const PlayerColor & ow)
 	tempOwner = ow;
 }
 
+void CGObjectInstance::setAnchorPos(int3 newPos)
+{
+	pos = newPos;
+}
+
 int CGObjectInstance::getWidth() const
 {
 	return appearance->getWidth();
@@ -79,32 +84,19 @@ int CGObjectInstance::getHeight() const
 	return appearance->getHeight();
 }
 
-bool CGObjectInstance::visitableAt(int x, int y) const
-{
-	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);
-}
-
-bool CGObjectInstance::coveringAt(int x, int y) const
-{
-	return appearance->isVisibleAt(pos.x - x, pos.y - y);
-}
-
 bool CGObjectInstance::visitableAt(const int3 & testPos) const
 {
-	return pos.z == testPos.z && appearance->isVisitableAt(pos.x - testPos.x, pos.y - testPos.y);
+	return anchorPos().z == testPos.z && appearance->isVisitableAt(anchorPos().x - testPos.x, anchorPos().y - testPos.y);
 }
+
 bool CGObjectInstance::blockingAt(const int3 & testPos) const
 {
-	return pos.z == testPos.z && appearance->isBlockedAt(pos.x - testPos.x, pos.y - testPos.y);
+	return anchorPos().z == testPos.z && appearance->isBlockedAt(anchorPos().x - testPos.x, anchorPos().y - testPos.y);
 }
 
 bool CGObjectInstance::coveringAt(const int3 & testPos) const
 {
-	return pos.z == testPos.z && appearance->isVisibleAt(pos.x - testPos.x, pos.y - testPos.y);
+	return anchorPos().z == testPos.z && appearance->isVisibleAt(anchorPos().x - testPos.x, anchorPos().y - testPos.y);
 }
 
 std::set<int3> CGObjectInstance::getBlockedPos() const
@@ -115,7 +107,7 @@ std::set<int3> CGObjectInstance::getBlockedPos() const
 		for(int h=0; h<getHeight(); ++h)
 		{
 			if(appearance->isBlockedAt(w, h))
-				ret.insert(int3(pos.x - w, pos.y - h, pos.z));
+				ret.insert(int3(anchorPos().x - w, anchorPos().y - h, anchorPos().z));
 		}
 	}
 	return ret;
@@ -215,6 +207,8 @@ int CGObjectInstance::getSightRadius() const
 
 int3 CGObjectInstance::getVisitableOffset() const
 {
+	if (!isVisitable())
+		throw std::runtime_error("Attempt to access visitable offset of a non-visitable object!");
 	return appearance->getVisitableOffset();
 }
 
@@ -313,6 +307,9 @@ void CGObjectInstance::onHeroVisit( const CGHeroInstance * h ) const
 
 int3 CGObjectInstance::visitablePos() const
 {
+	if (!isVisitable())
+		throw std::runtime_error("Attempt to access visitable position on a non-visitable object!");
+
 	return pos - getVisitableOffset();
 }
 

+ 8 - 9
lib/mapObjects/CGObjectInstance.h

@@ -28,8 +28,6 @@ using TObjectTypeHandler = std::shared_ptr<AObjectTypeHandler>;
 class DLL_LINKAGE CGObjectInstance : public IObjectInterface
 {
 public:
-	/// Position of bottom-right corner of object on map
-	int3 pos;
 	/// Type of object, e.g. town, hero, creature.
 	MapObjectID ID;
 	/// Subtype of object, depends on type
@@ -41,6 +39,9 @@ public:
 	/// Defines appearance of object on map (animation, blocked tiles, blit order, etc)
 	std::shared_ptr<const ObjectTemplate> appearance;
 
+	/// Position of bottom-right corner of object on map
+	int3 pos;
+
 	std::string instanceName;
 	std::string typeName;
 	std::string subTypeName;
@@ -62,21 +63,19 @@ public:
 		return this->tempOwner;
 	}
 	void setOwner(const PlayerColor & ow);
+	void setAnchorPos(int3 pos);
 
 	/** APPEARANCE ACCESSORS **/
 
 	int getWidth() const; //returns width of object graphic in tiles
 	int getHeight() const; //returns height of object graphic in tiles
 	int3 visitablePos() const override;
-	int3 getPosition() const override;
+	int3 anchorPos() const override;
 	int3 getTopVisiblePos() const;
-	bool visitableAt(int x, int y) const; //returns true if object is visitable at location (x, y) (h3m pos)
-	bool blockingAt(int x, int y) const; //returns true if object is blocking location (x, y) (h3m pos)
-	bool coveringAt(int x, int y) const; //returns true if object covers with picture location (x, y) (h3m pos)
 
-	bool visitableAt(const int3 & pos) const; //returns true if object is visitable at location (x, y) (h3m pos)
-	bool blockingAt (const int3 & pos) const; //returns true if object is blocking location (x, y) (h3m pos)
-	bool coveringAt (const int3 & pos) const; //returns true if object covers with picture location (x, y) (h3m pos)
+	bool visitableAt(const int3 & pos) const; //returns true if object is visitable at location
+	bool blockingAt (const int3 & pos) const; //returns true if object is blocking location
+	bool coveringAt (const int3 & pos) const; //returns true if object covers with picture location
 
 	std::set<int3> getBlockedPos() const; //returns set of positions blocked by this object
 	const std::set<int3> & getBlockedOffsets() const; //returns set of relative positions blocked by this object

+ 1 - 1
lib/mapObjects/CGTownInstance.cpp

@@ -954,7 +954,7 @@ TResources CGTownInstance::getBuildingCost(const BuildingID & buildingID) const
 		return town->buildings.at(buildingID)->resources;
 	else
 	{
-		logGlobal->error("Town %s at %s has no possible building %d!", getNameTranslated(), pos.toString(), buildingID.toEnum());
+		logGlobal->error("Town %s at %s has no possible building %d!", getNameTranslated(), anchorPos().toString(), buildingID.toEnum());
 		return TResources();
 	}
 

+ 0 - 2
lib/mapObjects/CGTownInstance.h

@@ -56,8 +56,6 @@ class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public I
 	std::set<BuildingID> builtBuildings;
 
 public:
-	using CGDwelling::getPosition;
-
 	enum EFortLevel {NONE = 0, FORT = 1, CITADEL = 2, CASTLE = 3};
 
 	CTownAndVisitingHero townAndVis;

+ 1 - 1
lib/mapObjects/CQuest.cpp

@@ -614,7 +614,7 @@ void CGSeerHut::onHeroVisit(const CGHeroInstance * h) const
 
 int CGSeerHut::checkDirection() const
 {
-	int3 cord = getCreatureToKill(false)->pos;
+	int3 cord = getCreatureToKill(false)->visitablePos();
 	if(static_cast<double>(cord.x) / static_cast<double>(cb->getMapSize().x) < 0.34) //north
 	{
 		if(static_cast<double>(cord.y) / static_cast<double>(cb->getMapSize().y) < 0.34) //northwest

+ 1 - 1
lib/mapObjects/IObjectInterface.cpp

@@ -145,7 +145,7 @@ void IBoatGenerator::getProblemText(MetaString &out, const CGHeroInstance *visit
 			out.appendLocalString(EMetaText::ADVOB_TXT, 189);
 		break;
 	case NO_WATER:
-		logGlobal->error("Shipyard without water at tile %s! ", getObject()->getPosition().toString());
+		logGlobal->error("Shipyard without water at tile %s! ", getObject()->anchorPos().toString());
 		return;
 	}
 }

+ 1 - 1
lib/mapObjects/IObjectInterface.h

@@ -47,7 +47,7 @@ public:
 
 	virtual PlayerColor getOwner() const = 0;
 	virtual int3 visitablePos() const = 0;
-	virtual int3 getPosition() const = 0;
+	virtual int3 anchorPos() const = 0;
 
 	virtual void onHeroVisit(const CGHeroInstance * h) const;
 	virtual void onHeroLeave(const CGHeroInstance * h) const;

+ 11 - 11
lib/mapObjects/MiscObjects.cpp

@@ -111,7 +111,7 @@ void CGMine::initObj(vstd::RNG & rand)
 		}
 		else
 		{
-			logGlobal->error("Abandoned mine at (%s) has no valid resource candidates!", pos.toString());
+			logGlobal->error("Abandoned mine at (%s) has no valid resource candidates!", anchorPos().toString());
 			producedResource = GameResID::GOLD;
 		}
 	}
@@ -510,11 +510,11 @@ void CGMonolith::onHeroVisit( const CGHeroInstance * h ) const
 
 		if(cb->isTeleportChannelImpassable(channel))
 		{
-			logGlobal->debug("Cannot find corresponding exit monolith for %d at %s", id.getNum(), pos.toString());
+			logGlobal->debug("Cannot find corresponding exit monolith for %d at %s", id.getNum(), anchorPos().toString());
 			td.impassable = true;
 		}
 		else if(getRandomExit(h) == ObjectInstanceID())
-			logGlobal->debug("All exits blocked for monolith %d at %s", id.getNum(), pos.toString());
+			logGlobal->debug("All exits blocked for monolith %d at %s", id.getNum(), anchorPos().toString());
 	}
 	else
 		h->showInfoDialog(70);
@@ -574,7 +574,7 @@ void CGSubterraneanGate::onHeroVisit( const CGHeroInstance * h ) const
 	if(cb->isTeleportChannelImpassable(channel))
 	{
 		h->showInfoDialog(153);//Just inside the entrance you find a large pile of rubble blocking the tunnel. You leave discouraged.
-		logGlobal->debug("Cannot find exit subterranean gate for  %d at %s", id.getNum(), pos.toString());
+		logGlobal->debug("Cannot find exit subterranean gate for  %d at %s", id.getNum(), anchorPos().toString());
 		td.impassable = true;
 	}
 	else
@@ -602,13 +602,13 @@ void CGSubterraneanGate::postInit(IGameCallback * cb) //matches subterranean gat
 
 		auto * hlp = dynamic_cast<CGSubterraneanGate *>(cb->gameState()->getObjInstance(obj->id));
 		if(hlp)
-			gatesSplit[hlp->pos.z].push_back(hlp);
+			gatesSplit[hlp->visitablePos().z].push_back(hlp);
 	}
 
 	//sort by position
 	std::sort(gatesSplit[0].begin(), gatesSplit[0].end(), [](const CGObjectInstance * a, const CGObjectInstance * b)
 	{
-		return a->pos < b->pos;
+		return a->visitablePos() < b->visitablePos();
 	});
 
 	auto assignToChannel = [&](CGSubterraneanGate * obj)
@@ -631,7 +631,7 @@ void CGSubterraneanGate::postInit(IGameCallback * cb) //matches subterranean gat
 			CGSubterraneanGate *checked = gatesSplit[1][j];
 			if(checked->channel != TeleportChannelID())
 				continue;
-			si32 hlp = checked->pos.dist2dSQ(objCurrent->pos);
+			si32 hlp = checked->visitablePos().dist2dSQ(objCurrent->visitablePos());
 			if(hlp < best.second)
 			{
 				best.first = j;
@@ -657,11 +657,11 @@ void CGWhirlpool::onHeroVisit( const CGHeroInstance * h ) const
 	TeleportDialog td(h->id, channel);
 	if(cb->isTeleportChannelImpassable(channel))
 	{
-		logGlobal->debug("Cannot find exit whirlpool for %d at %s", id.getNum(), pos.toString());
+		logGlobal->debug("Cannot find exit whirlpool for %d at %s", id.getNum(), anchorPos().toString());
 		td.impassable = true;
 	}
 	else if(getRandomExit(h) == ObjectInstanceID())
-		logGlobal->debug("All exits are blocked for whirlpool  %d at %s", id.getNum(), pos.toString());
+		logGlobal->debug("All exits are blocked for whirlpool  %d at %s", id.getNum(), anchorPos().toString());
 
 	if(!isProtected(h))
 	{
@@ -1086,9 +1086,9 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const
 
 			for(const auto & eye : eyes)
 			{
-				cb->getTilesInRange (fw.tiles, eye->pos, 10, ETileVisibility::HIDDEN, h->tempOwner);
+				cb->getTilesInRange (fw.tiles, eye->visitablePos(), 10, ETileVisibility::HIDDEN, h->tempOwner);
 				cb->sendAndApply(fw);
-				cv.pos = eye->pos;
+				cv.pos = eye->visitablePos();
 
 				cb->sendAndApply(cv);
 			}

+ 2 - 2
lib/mapObjects/TownBuildingInstance.cpp

@@ -56,9 +56,9 @@ int3 TownBuildingInstance::visitablePos() const
 	return town->visitablePos();
 }
 
-int3 TownBuildingInstance::getPosition() const
+int3 TownBuildingInstance::anchorPos() const
 {
-	return town->getPosition();
+	return town->anchorPos();
 }
 
 TownRewardableBuildingInstance::TownRewardableBuildingInstance(IGameCallback *cb)

+ 1 - 1
lib/mapObjects/TownBuildingInstance.h

@@ -38,7 +38,7 @@ public:
 	const IOwnableObject * asOwnable() const override;
 
 	int3 visitablePos() const override;
-	int3 getPosition() const override;
+	int3 anchorPos() const override;
 
 	template <typename Handler> void serialize(Handler &h)
 	{

+ 14 - 14
lib/mapping/CMap.cpp

@@ -232,22 +232,22 @@ CMap::~CMap()
 
 void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
 {
-	const int zVal = obj->pos.z;
+	const int zVal = obj->anchorPos().z;
 	for(int fx = 0; fx < obj->getWidth(); ++fx)
 	{
-		int xVal = obj->pos.x - fx;
+		int xVal = obj->anchorPos().x - fx;
 		for(int fy = 0; fy < obj->getHeight(); ++fy)
 		{
-			int yVal = obj->pos.y - fy;
+			int yVal = obj->anchorPos().y - fy;
 			if(xVal>=0 && xVal < width && yVal>=0 && yVal < height)
 			{
 				TerrainTile & curt = terrain[zVal][xVal][yVal];
-				if(total || obj->visitableAt(xVal, yVal))
+				if(total || obj->visitableAt(int3(xVal, yVal, zVal)))
 				{
 					curt.visitableObjects -= obj;
 					curt.visitable = curt.visitableObjects.size();
 				}
-				if(total || obj->blockingAt(xVal, yVal))
+				if(total || obj->blockingAt(int3(xVal, yVal, zVal)))
 				{
 					curt.blockingObjects -= obj;
 					curt.blocked = curt.blockingObjects.size();
@@ -259,22 +259,22 @@ void CMap::removeBlockVisTiles(CGObjectInstance * obj, bool total)
 
 void CMap::addBlockVisTiles(CGObjectInstance * obj)
 {
-	const int zVal = obj->pos.z;
+	const int zVal = obj->anchorPos().z;
 	for(int fx = 0; fx < obj->getWidth(); ++fx)
 	{
-		int xVal = obj->pos.x - fx;
+		int xVal = obj->anchorPos().x - fx;
 		for(int fy = 0; fy < obj->getHeight(); ++fy)
 		{
-			int yVal = obj->pos.y - fy;
+			int yVal = obj->anchorPos().y - fy;
 			if(xVal>=0 && xVal < width && yVal >= 0 && yVal < height)
 			{
 				TerrainTile & curt = terrain[zVal][xVal][yVal];
-				if(obj->visitableAt(xVal, yVal))
+				if(obj->visitableAt(int3(xVal, yVal, zVal)))
 				{
 					curt.visitableObjects.push_back(obj);
 					curt.visitable = true;
 				}
-				if(obj->blockingAt(xVal, yVal))
+				if(obj->blockingAt(int3(xVal, yVal, zVal)))
 				{
 					curt.blockingObjects.push_back(obj);
 					curt.blocked = true;
@@ -444,14 +444,14 @@ const CGObjectInstance * CMap::getObjectiveObjectFrom(const int3 & pos, Obj type
 				bestMatch = object;
 			else
 			{
-				if (object->pos.dist2dSQ(pos) < bestMatch->pos.dist2dSQ(pos))
+				if (object->anchorPos().dist2dSQ(pos) < bestMatch->anchorPos().dist2dSQ(pos))
 					bestMatch = object;// closer than one we already found
 			}
 		}
 	}
 	assert(bestMatch != nullptr); // if this happens - victory conditions or map itself is very, very broken
 
-	logGlobal->error("Will use %s from %s", bestMatch->getObjectName(), bestMatch->pos.toString());
+	logGlobal->error("Will use %s from %s", bestMatch->getObjectName(), bestMatch->anchorPos().toString());
 	return bestMatch;
 }
 
@@ -635,7 +635,7 @@ void CMap::addNewObject(CGObjectInstance * obj)
 void CMap::moveObject(CGObjectInstance * obj, const int3 & pos)
 {
 	removeBlockVisTiles(obj);
-	obj->pos = pos;
+	obj->setAnchorPos(pos);
 	addBlockVisTiles(obj);
 }
 
@@ -803,7 +803,7 @@ void CMap::reindexObjects()
 		if (lhs->isRemovable() && !rhs->isRemovable())
 			return false;
 
-		return lhs->pos.y < rhs->pos.y;
+		return lhs->anchorPos().y < rhs->anchorPos().y;
 	});
 
 	// instanceNames don't change

+ 1 - 1
lib/mapping/CMapOperation.cpp

@@ -615,7 +615,7 @@ std::string CInsertObjectOperation::getLabel() const
 CMoveObjectOperation::CMoveObjectOperation(CMap* map, CGObjectInstance* obj, const int3& targetPosition)
 	: CMapOperation(map),
 	obj(obj),
-	initialPos(obj->pos),
+	initialPos(obj->anchorPos()),
 	targetPos(targetPosition)
 {
 }

+ 2 - 2
lib/mapping/MapFormatH3M.cpp

@@ -913,7 +913,7 @@ void CMapLoaderH3M::loadArtifactsOfHero(CGHeroInstance * hero)
 
 	if(!hero->artifactsWorn.empty() || !hero->artifactsInBackpack.empty())
 	{
-		logGlobal->debug("Hero %d at %s has set artifacts twice (in map properties and on adventure map instance). Using the latter set...", hero->getHeroType().getNum(), hero->pos.toString());
+		logGlobal->debug("Hero %d at %s has set artifacts twice (in map properties and on adventure map instance). Using the latter set...", hero->getHeroType().getNum(), hero->anchorPos().toString());
 
 		hero->artifactsInBackpack.clear();
 		while(!hero->artifactsWorn.empty())
@@ -1651,7 +1651,7 @@ void CMapLoaderH3M::readObjects()
 		if(!newObject)
 			continue;
 
-		newObject->pos = mapPosition;
+		newObject->setAnchorPos(mapPosition);
 		newObject->ID = objectTemplate->id;
 		newObject->id = objectInstanceID;
 		if(newObject->ID != Obj::HERO && newObject->ID != Obj::HERO_PLACEHOLDER && newObject->ID != Obj::PRISON)

+ 1 - 1
lib/mapping/MapFormatJson.cpp

@@ -1072,7 +1072,7 @@ void CMapLoaderJson::MapObjectLoader::construct()
 
 	instance->id = ObjectInstanceID(static_cast<si32>(owner->map->objects.size()));
 	instance->instanceName = jsonKey;
-	instance->pos = pos;
+	instance->setAnchorPos(pos);
 	owner->map->addNewObject(instance);
 }
 

+ 2 - 2
lib/networkPacks/NetPacksLib.cpp

@@ -1047,7 +1047,7 @@ void ChangeObjPos::applyGs(CGameState *gs)
 		return;
 	}
 	gs->map->removeBlockVisTiles(obj);
-	obj->pos = nPos + obj->getVisitableOffset();
+	obj->setAnchorPos(nPos + obj->getVisitableOffset());
 	gs->map->addBlockVisTiles(obj);
 }
 
@@ -1467,7 +1467,7 @@ void GiveHero::applyGs(CGameState *gs)
 
 	h->setOwner(player);
 	h->setMovementPoints(h->movementPointsLimit(true));
-	h->pos = h->convertFromVisitablePos(oldVisitablePos);
+	h->setAnchorPos(h->convertFromVisitablePos(oldVisitablePos));
 	gs->map->heroesOnMap.emplace_back(h);
 	gs->getPlayerState(h->getOwner())->addOwnedObject(h);
 

+ 7 - 7
lib/rmg/RmgObject.cpp

@@ -87,7 +87,7 @@ const rmg::Area & Object::Instance::getAccessibleArea() const
 void Object::Instance::setPosition(const int3 & position)
 {
 	dPosition = position;
-	dObject.pos = dPosition + dParent.getPosition();
+	dObject.setAnchorPos(dPosition + dParent.getPosition());
 	
 	dBlockedAreaCache.clear();
 	dAccessibleAreaCache.clear();
@@ -96,21 +96,21 @@ void Object::Instance::setPosition(const int3 & position)
 
 void Object::Instance::setPositionRaw(const int3 & position)
 {
-	if(!dObject.pos.valid())
+	if(!dObject.anchorPos().valid())
 	{
-		dObject.pos = dPosition + dParent.getPosition();
+		dObject.setAnchorPos(dPosition + dParent.getPosition());
 		dBlockedAreaCache.clear();
 		dAccessibleAreaCache.clear();
 		dParent.clearCachedArea();
 	}
 		
-	auto shift = position + dParent.getPosition() - dObject.pos;
+	auto shift = position + dParent.getPosition() - dObject.anchorPos();
 	
 	dAccessibleAreaCache.translate(shift);
 	dBlockedAreaCache.translate(shift);
 	
 	dPosition = position;
-	dObject.pos = dPosition + dParent.getPosition();
+	dObject.setAnchorPos(dPosition + dParent.getPosition());
 }
 
 void Object::Instance::setAnyTemplate(vstd::RNG & rng)
@@ -497,12 +497,12 @@ void Object::Instance::finalize(RmgMap & map, vstd::RNG & rng)
 	}
 
 	if (dObject.isVisitable() && !map.isOnMap(dObject.visitablePos()))
-		throw rmgException(boost::str(boost::format("Visitable tile %s of object %d at %s is outside the map") % dObject.visitablePos().toString() % dObject.id % dObject.pos.toString()));
+		throw rmgException(boost::str(boost::format("Visitable tile %s of object %d at %s is outside the map") % dObject.visitablePos().toString() % dObject.id % dObject.anchorPos().toString()));
 
 	for(const auto & tile : dObject.getBlockedPos())
 	{
 		if(!map.isOnMap(tile))
-			throw rmgException(boost::str(boost::format("Tile %s of object %d at %s is outside the map") % tile.toString() % dObject.id % dObject.pos.toString()));
+			throw rmgException(boost::str(boost::format("Tile %s of object %d at %s is outside the map") % tile.toString() % dObject.id % dObject.anchorPos().toString()));
 	}
 
 	for(const auto & tile : getBlockedArea().getTilesVector())

+ 2 - 2
lib/rmg/modificators/QuestArtifactPlacer.cpp

@@ -112,7 +112,7 @@ void QuestArtifactPlacer::placeQuestArtifacts(vstd::RNG & rand)
 
 			logGlobal->trace("Replacing %s at %s with the quest artifact %s",
 				objectToReplace->getObjectName(),
-				objectToReplace->getPosition().toString(),
+				objectToReplace->anchorPos().toString(),
 				VLC->artifacts()->getById(artifactToPlace)->getNameTranslated());
 
 			//Update appearance. Terrain is irrelevant.
@@ -121,7 +121,7 @@ void QuestArtifactPlacer::placeQuestArtifacts(vstd::RNG & rand)
 			auto templates = handler->getTemplates();
 			//artifactToReplace->appearance = templates.front();
 			newObj->appearance  = templates.front();
-			newObj->pos = objectToReplace->pos;
+			newObj->setAnchorPos(objectToReplace->anchorPos());
 			mapProxy->insertObject(newObj);
 			mapProxy->removeObject(objectToReplace);
 			break;

+ 3 - 3
lib/spells/AdventureSpellMechanics.cpp

@@ -209,7 +209,7 @@ ESpellCastResult SummonBoatMechanics::applyAdventureEffects(SpellCastEnvironment
 			if(b->hero || b->layer != EPathfindingLayer::SAIL)
 				continue; //we're looking for unoccupied boat
 
-			double nDist = b->pos.dist2d(parameters.caster->getHeroCaster()->visitablePos());
+			double nDist = b->visitablePos().dist2d(parameters.caster->getHeroCaster()->visitablePos());
 			if(!nearest || nDist < dist) //it's first boat or closer than previous
 			{
 				nearest = b;
@@ -669,11 +669,11 @@ const CGTownInstance * TownPortalMechanics::findNearestTown(SpellCastEnvironment
 		return nullptr;
 
 	auto nearest = pool.cbegin(); //nearest town's iterator
-	si32 dist = (*nearest)->pos.dist2dSQ(parameters.caster->getHeroCaster()->pos);
+	si32 dist = (*nearest)->visitablePos().dist2dSQ(parameters.caster->getHeroCaster()->visitablePos());
 
 	for(auto i = nearest + 1; i != pool.cend(); ++i)
 	{
-		si32 curDist = (*i)->pos.dist2dSQ(parameters.caster->getHeroCaster()->pos);
+		si32 curDist = (*i)->visitablePos().dist2dSQ(parameters.caster->getHeroCaster()->visitablePos());
 
 		if(curDist < dist)
 		{

+ 1 - 1
mapeditor/maphandler.cpp

@@ -379,7 +379,7 @@ void MapHandler::drawObjects(QPainter & painter, int x, int y, int z, const std:
 		
 		if(objData.objBitmap)
 		{
-			auto pos = obj->getPosition();
+			auto pos = obj->anchorPos();
 
 			painter.drawImage(QPoint(x * tileSize, y * tileSize), *objData.objBitmap, object.rect, Qt::AutoColor | Qt::NoOpaqueDetection);
 

+ 1 - 1
mapeditor/mapsettings/abstractsettings.cpp

@@ -115,7 +115,7 @@ std::string AbstractSettings::getMonsterName(const CMap & map, int objectIdx)
 	std::string name;
 	if(auto monster = dynamic_cast<const CGCreature*>(map.objects[objectIdx].get()))
 	{
-		name = boost::str(boost::format("%1% at %2%") % monster->getObjectName() % monster->getPosition().toString());
+		name = boost::str(boost::format("%1% at %2%") % monster->getObjectName() % monster->anchorPos().toString());
 	}
 	return name;
 }

+ 6 - 6
mapeditor/scenelayer.cpp

@@ -425,7 +425,7 @@ void ObjectsLayer::setDirty(const CGObjectInstance * object)
 	{
 		for(int i = 0; i < object->getWidth(); ++i)
 		{
-			setDirty(object->getPosition().x - i, object->getPosition().y - j);
+			setDirty(object->anchorPos().x - i, object->anchorPos().y - j);
 		}
 	}
 }
@@ -479,7 +479,7 @@ void SelectionObjectsLayer::draw()
 	{
 		if(obj != newObject)
 		{
-			QRect bbox(obj->getPosition().x, obj->getPosition().y, 1, 1);
+			QRect bbox(obj->anchorPos().x, obj->anchorPos().y, 1, 1);
 			for(auto & t : obj->getBlockedPos())
 			{
 				QPoint topLeft(std::min(t.x, bbox.topLeft().x()), std::min(t.y, bbox.topLeft().y()));
@@ -496,7 +496,7 @@ void SelectionObjectsLayer::draw()
 		if(selectionMode == SelectionMode::MOVEMENT && (shift.x() || shift.y()))
 		{
 			painter.setOpacity(0.7);
-			auto newPos = QPoint(obj->getPosition().x, obj->getPosition().y) + shift;
+			auto newPos = QPoint(obj->anchorPos().x, obj->anchorPos().y) + shift;
 			handler->drawObjectAt(painter, obj, newPos.x(), newPos.y());
 		}
 	}
@@ -517,7 +517,7 @@ CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y, const CGO
 		if(!object.obj || object.obj == ignore || lockedObjects.count(object.obj))
 			continue;
 		
-		if(object.obj->visitableAt(x, y))
+		if(object.obj->visitableAt(int3(x, y, scene->level)))
 		{
 			return const_cast<CGObjectInstance*>(object.obj);
 		}
@@ -529,7 +529,7 @@ CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y, const CGO
 		if(!object.obj || object.obj == ignore || lockedObjects.count(object.obj))
 			continue;
 		
-		if(object.obj->blockingAt(x, y))
+		if(object.obj->blockingAt(int3(x, y, scene->level)))
 		{
 			return const_cast<CGObjectInstance*>(object.obj);
 		}
@@ -541,7 +541,7 @@ CGObjectInstance * SelectionObjectsLayer::selectObjectAt(int x, int y, const CGO
 		if(!object.obj || object.obj == ignore || lockedObjects.count(object.obj))
 			continue;
 		
-		if(object.obj->coveringAt(x, y))
+		if(object.obj->coveringAt(int3(x, y, scene->level)))
 		{
 			return const_cast<CGObjectInstance*>(object.obj);
 		}

+ 6 - 3
server/CGameHandler.cpp

@@ -803,7 +803,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 		return false;
 	}
 
-	logGlobal->trace("Player %d (%s) wants to move hero %d from %s to %s", asker, asker.toString(), hid.getNum(), h->pos.toString(), dst.toString());
+	logGlobal->trace("Player %d (%s) wants to move hero %d from %s to %s", asker, asker.toString(), hid.getNum(), h->anchorPos().toString(), dst.toString());
 	const int3 hmpos = h->convertToVisitablePos(dst);
 
 	if (!gs->map->isInTheMap(hmpos))
@@ -902,7 +902,7 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, EMovementMode moveme
 	// should be called if hero changes tile but before applying TryMoveHero package
 	auto leaveTile = [&]()
 	{
-		for (CGObjectInstance *obj : gs->map->getTile(int3(h->pos.x-1, h->pos.y, h->pos.z)).visitableObjects)
+		for (CGObjectInstance *obj : gs->map->getTile(h->visitablePos()).visitableObjects)
 		{
 			obj->onHeroLeave(h);
 		}
@@ -4222,8 +4222,11 @@ CGObjectInstance * CGameHandler::createNewObject(const int3 & visitablePosition,
 	else
 		o->appearance = handler->getTemplates().front();
 
+	if (o->isVisitable())
+		o->setAnchorPos(visitablePosition + o->getVisitableOffset());
+	else
+		o->setAnchorPos(visitablePosition);
 
-	o->pos = visitablePosition + o->getVisitableOffset();
 	return o;
 }
 

+ 1 - 1
server/NetPacksServer.cpp

@@ -283,7 +283,7 @@ void ApplyGhNetPackVisitor::visitTradeOnMarketplace(TradeOnMarketplace & pack)
 			gh.throwAndComplain(&pack, "Can not trade - no hero!");
 
 		// TODO: check that object is actually being visited (e.g. Query exists)
-		if (!object->visitableAt(hero->visitablePos().x, hero->visitablePos().y))
+		if (!object->visitableAt(hero->visitablePos()))
 			gh.throwAndComplain(&pack, "Can not trade - object not visited!");
 
 		if (object->getOwner().isValidPlayer() && gh.getPlayerRelations(object->getOwner(), hero->getOwner()) == PlayerRelations::ENEMIES)