Răsfoiți Sursa

Merge pull request #1196 from IvanSavenko/hero_positioning_fix

Fixes incorrect positioning of heroes on spawn/release from prison
Andrii Danylchenko 2 ani în urmă
părinte
comite
40e55e2788
3 a modificat fișierele cu 39 adăugiri și 13 ștergeri
  1. 19 9
      lib/CGameState.cpp
  2. 6 1
      lib/NetPacksLib.cpp
  3. 14 3
      lib/mapObjects/CObjectHandler.cpp

+ 19 - 9
lib/CGameState.cpp

@@ -1873,17 +1873,27 @@ void CGameState::initVisitingAndGarrisonedHeroes()
 		{
 			for(CGTownInstance *t : k->second.towns)
 			{
-				int3 vistile = t->visitablePos(); vistile.x++; //tile next to the entrance
-				if(vistile == h->pos || h->pos==t->visitablePos())
+				bool heroOnTownBlockableTile = t->blockingAt(h->visitablePos().x, h->visitablePos().y);
+				bool heroOnTownVisitableTile = t->visitableAt(h->visitablePos().x, h->visitablePos().y);
+
+				// 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 townVisitablePos = t->visitablePos();
+					int3 correctedPos = townVisitablePos + h->getVisitableOffset();
+
+					map->removeBlockVisTiles(h);
+					h->pos = correctedPos;
+					map->addBlockVisTiles(h);
+
+					assert(t->visitableAt(h->visitablePos().x, h->visitablePos().y));
+				}
+
+				if (heroOnTownBlockableTile || heroOnTownVisitableTile)
+				{
+					assert(t->visitingHero == nullptr);
 					t->setVisitingHero(h);
-					if(h->pos == t->pos) //visiting hero placed in the editor has same pos as the town - we need to correct it
-					{
-						map->removeBlockVisTiles(h);
-						h->pos.x -= 1;
-						map->addBlockVisTiles(h);
-					}
-					break;
 				}
 			}
 		}

+ 6 - 1
lib/NetPacksLib.cpp

@@ -703,13 +703,18 @@ DLL_LINKAGE void GiveHero::applyGs(CGameState *gs)
 	//bonus system
 	h->detachFrom(gs->globalEffects);
 	h->attachTo(*gs->getPlayerState(player));
-	h->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, h->type->heroClass->getIndex())->getTemplates().front();
 
+	auto oldOffset = h->getVisitableOffset();
 	gs->map->removeBlockVisTiles(h,true);
+	h->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, h->type->heroClass->getIndex())->getTemplates().front();
+	auto newOffset = h->getVisitableOffset();
+
 	h->setOwner(player);
 	h->movement =  h->maxMovePoints(true);
+	h->pos = h->pos - oldOffset + newOffset;
 	gs->map->heroesOnMap.push_back(h);
 	gs->getPlayerState(h->getOwner())->heroes.push_back(h);
+
 	gs->map->addBlockVisTiles(h);
 	h->inTownGarrison = false;
 }

+ 14 - 3
lib/mapObjects/CObjectHandler.cpp

@@ -195,7 +195,9 @@ std::set<int3> CGObjectInstance::getBlockedOffsets() const
 
 void CGObjectInstance::setType(si32 ID, si32 subID)
 {
-	const TerrainTile &tile = cb->gameState()->map->getTile(visitablePos());
+	auto position = visitablePos();
+	auto oldOffset = getVisitableOffset();
+	auto &tile = cb->gameState()->map->getTile(position);
 
 	//recalculate blockvis tiles - new appearance might have different blockmap than before
 	cb->gameState()->map->removeBlockVisTiles(this, true);
@@ -206,14 +208,23 @@ void CGObjectInstance::setType(si32 ID, si32 subID)
 		return;
 	}
 	if(!handler->getTemplates(tile.terType->id).empty())
+	{
 		appearance = handler->getTemplates(tile.terType->id)[0];
+	}
 	else
+	{
+		logGlobal->warn("Object %d:%d at %s has no templates suitable for terrain %s", ID, subID, visitablePos().toString(), tile.terType->name);
 		appearance = handler->getTemplates()[0]; // get at least some appearance since alternative is crash
+	}
 
 	if(this->ID == Obj::PRISON && ID == Obj::HERO)
 	{
-		//adjust for the prison offset
-		pos = visitablePos();
+		auto newOffset = getVisitableOffset();
+		// FIXME: potentially unused code - setType is NOT called when releasing hero from prison
+		// instead, appearance update & pos adjustment occurs in GiveHero::applyGs
+
+		// adjust position since hero and prison may have different visitable offset
+		pos = pos - oldOffset + newOffset;
 	}
 
 	this->ID = Obj(ID);