Pārlūkot izejas kodu

API for wandering heroes access

Ivan Savenko 2 gadi atpakaļ
vecāks
revīzija
5cbd0f8fc8

+ 13 - 10
client/CPlayerInterface.cpp

@@ -405,7 +405,7 @@ void CPlayerInterface::heroKilled(const CGHeroInstance* hero)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 	LOG_TRACE_PARAMS(logGlobal, "Hero %s killed handler for player %s", hero->getNameTranslated() % playerID);
-	localState->wanderingHeroes -= hero;
+	localState->removeWanderingHero(hero);
 	adventureInt->onHeroChanged(hero);
 	localState->erasePath(hero);
 }
@@ -423,7 +423,7 @@ void CPlayerInterface::heroVisit(const CGHeroInstance * visitor, const CGObjectI
 void CPlayerInterface::heroCreated(const CGHeroInstance * hero)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	localState->wanderingHeroes.push_back(hero);
+	localState->addWanderingHero(hero);
 	adventureInt->onHeroChanged(hero);
 }
 void CPlayerInterface::openTownWindow(const CGTownInstance * town)
@@ -507,16 +507,16 @@ void CPlayerInterface::heroInGarrisonChange(const CGTownInstance *town)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
 
-	if (town->garrisonHero) //wandering hero moved to the garrison
+	if(town->garrisonHero) //wandering hero moved to the garrison
 	{
-		if (town->garrisonHero->tempOwner == playerID && vstd::contains(localState->wanderingHeroes,town->garrisonHero)) // our hero
-			localState->wanderingHeroes -= town->garrisonHero;
+		if(town->garrisonHero->tempOwner == playerID)
+			localState->removeWanderingHero(town->garrisonHero);
 	}
 
-	if (town->visitingHero) //hero leaves garrison
+	if(town->visitingHero) //hero leaves garrison
 	{
-		if (town->visitingHero->tempOwner == playerID && !vstd::contains(localState->wanderingHeroes,town->visitingHero)) // our hero
-			localState->wanderingHeroes.push_back(town->visitingHero);
+		if(town->visitingHero->tempOwner == playerID)
+			localState->addWanderingHero(town->visitingHero);
 	}
 	adventureInt->onHeroChanged(nullptr);
 	adventureInt->onTownChanged(town);
@@ -1368,13 +1368,13 @@ void CPlayerInterface::objectPropertyChanged(const SetObjectProperty * sop)
 
 void CPlayerInterface::initializeHeroTownList()
 {
-	if(!localState->wanderingHeroes.size())
+	if(localState->getWanderingHeroes().empty())
 	{
 		std::vector<const CGHeroInstance*> heroes = cb->getHeroesInfo();
 		for(auto & hero : heroes)
 		{
 			if(!hero->inTownGarrison)
-				localState->wanderingHeroes.push_back(hero);
+				localState->addWanderingHero(hero);
 		}
 	}
 
@@ -2084,6 +2084,9 @@ void CPlayerInterface::showWorldViewEx(const std::vector<ObjectPosInfo>& objectP
 
 void CPlayerInterface::setSelection(const CArmedInstance *sel, bool centerView)
 {
+	if (sel == localState->getCurrentArmy())
+		return;
+
 	localState->setSelection(sel);
 	adventureInt->onSelectionChanged(sel, centerView);
 }

+ 27 - 0
client/PlayerLocalState.cpp

@@ -150,3 +150,30 @@ void PlayerLocalState::setHeroAwaken(const CGHeroInstance * hero)
 
 	vstd::erase(sleepingHeroes, hero);
 }
+
+const std::vector<const CGHeroInstance *> & PlayerLocalState::getWanderingHeroes()
+{
+	return wanderingHeroes;
+}
+
+const CGHeroInstance * PlayerLocalState::getWanderingHero(size_t index)
+{
+	if (index < wanderingHeroes.size())
+		return wanderingHeroes[index];
+	return nullptr;
+}
+
+void PlayerLocalState::addWanderingHero(const CGHeroInstance * hero)
+{
+	assert(hero);
+	assert(!vstd::contains(wanderingHeroes, hero));
+	wanderingHeroes.push_back(hero);
+}
+
+void PlayerLocalState::removeWanderingHero(const CGHeroInstance * hero)
+{
+	assert(hero);
+	assert(vstd::contains(wanderingHeroes, hero));
+	vstd::erase(wanderingHeroes, hero);
+	vstd::erase(sleepingHeroes, hero);
+}

+ 6 - 1
client/PlayerLocalState.h

@@ -31,6 +31,7 @@ class PlayerLocalState
 
 	std::map<const CGHeroInstance *, CGPath> paths; //maps hero => selected path in adventure map
 	std::vector<const CGHeroInstance *> sleepingHeroes; //if hero is in here, he's sleeping
+	std::vector<const CGHeroInstance *> wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones)
 
 	void saveHeroPaths(std::map<const CGHeroInstance *, int3> & paths);
 	void loadHeroPaths(std::map<const CGHeroInstance *, int3> & paths);
@@ -52,7 +53,6 @@ public:
 		}
 	} spellbookSettings;
 
-	std::vector<const CGHeroInstance *> wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones)
 	std::vector<const CGTownInstance *> ownedTowns; //our towns on the adventure map
 
 	explicit PlayerLocalState(CPlayerInterface & owner);
@@ -61,6 +61,11 @@ public:
 	void setHeroAsleep(const CGHeroInstance * hero);
 	void setHeroAwaken(const CGHeroInstance * hero);
 
+	const std::vector<const CGHeroInstance *> & getWanderingHeroes();
+	const CGHeroInstance * getWanderingHero(size_t index);
+	void addWanderingHero(const CGHeroInstance * hero);
+	void removeWanderingHero(const CGHeroInstance * hero);
+
 	void setPath(const CGHeroInstance *h, const CGPath & path);
 	bool setPath(const CGHeroInstance *h, const int3 & destination);
 

+ 64 - 56
client/adventureMap/CAdventureMapInterface.cpp

@@ -67,7 +67,6 @@ CAdventureMapInterface::CAdventureMapInterface():
 	terrain(new MapView(Point(ADVOPT.advmapX, ADVOPT.advmapY), Point(ADVOPT.advmapW, ADVOPT.advmapH))),
 	state(EGameState::NOT_INITIALIZED),
 	spellBeingCasted(nullptr),
-	currentSelection(nullptr),
 	activeMapPanel(nullptr),
 	scrollingCursorSet(false)
 {
@@ -75,7 +74,11 @@ CAdventureMapInterface::CAdventureMapInterface():
 	pos.w = GH.screenDimensions().x;
 	pos.h = GH.screenDimensions().y;
 	strongInterest = true; // handle all mouse move events to prevent dead mouse move space in fullscreen mode
-	townList->onSelect = std::bind(&CAdventureMapInterface::selectionChanged,this);
+	townList->onSelect = [this](){
+		const CGTownInstance * selectedTown = LOCPLINT->localState->ownedTowns[townList->getSelectedIndex()];
+		LOCPLINT->setSelection(selectedTown);
+	};
+
 	bg = IImage::createFromFile(ADVOPT.mainGraphic);
 	if(!ADVOPT.worldViewGraphic.empty())
 	{
@@ -321,7 +324,7 @@ void CAdventureMapInterface::fshowSpellbok()
 	if (!LOCPLINT->localState->getCurrentHero()) //checking necessary values
 		return;
 
-	centerOnObject(currentSelection);
+	centerOnObject(LOCPLINT->localState->getCurrentHero());
 
 	GH.pushIntT<CSpellWindow>(LOCPLINT->localState->getCurrentHero(), LOCPLINT, false);
 }
@@ -338,11 +341,10 @@ void CAdventureMapInterface::fsystemOptions()
 
 void CAdventureMapInterface::fnextHero()
 {
-	auto hero = dynamic_cast<const CGHeroInstance*>(currentSelection);
-	int next = getNextHeroIndex(vstd::find_pos(LOCPLINT->localState->wanderingHeroes, hero));
-	if (next < 0)
-		return;
-	LOCPLINT->setSelection(LOCPLINT->localState->wanderingHeroes[next], true);
+	const auto * nextHero = getNextHero(LOCPLINT->localState->getCurrentHero());
+
+	if (nextHero)
+		LOCPLINT->setSelection(nextHero, true);
 }
 
 void CAdventureMapInterface::fendTurn()
@@ -352,7 +354,7 @@ void CAdventureMapInterface::fendTurn()
 
 	if(settings["adventure"]["heroReminder"].Bool())
 	{
-		for(auto hero : LOCPLINT->localState->wanderingHeroes)
+		for(auto hero : LOCPLINT->localState->getWanderingHeroes())
 		{
 			if(!LOCPLINT->localState->isHeroSleeping(hero) && hero->movement > 0)
 			{
@@ -395,25 +397,39 @@ void CAdventureMapInterface::updateSpellbook(const CGHeroInstance *h)
 	spellbook->block(!h);
 }
 
-int CAdventureMapInterface::getNextHeroIndex(int startIndex)
+const CGHeroInstance * CAdventureMapInterface::getNextHero(const CGHeroInstance * currentHero)
 {
-	if (LOCPLINT->localState->wanderingHeroes.size() == 0)
-		return -1;
-	if (startIndex < 0)
-		startIndex = 0;
-	int i = startIndex;
-	do
+	bool currentHeroFound = false;
+	const CGHeroInstance * firstSuitable = nullptr;
+	const CGHeroInstance * nextSuitable = nullptr;
+
+	for (auto const * hero : LOCPLINT->localState->getWanderingHeroes())
 	{
-		i++;
-		if (i >= LOCPLINT->localState->wanderingHeroes.size())
-			i = 0;
+		if (hero == currentHero)
+		{
+			currentHeroFound = true;
+			continue;
+		}
+
+		if (LOCPLINT->localState->isHeroSleeping(hero))
+			continue;
+
+		if (hero->movement == 0)
+			continue;
+
+		if (!firstSuitable)
+			firstSuitable = hero;
+
+		if (!nextSuitable && currentHeroFound)
+			nextSuitable = hero;
 	}
-	while (((LOCPLINT->localState->wanderingHeroes[i]->movement == 0) || LOCPLINT->localState->isHeroSleeping(LOCPLINT->localState->wanderingHeroes[i])) && (i != startIndex));
 
-	if ((LOCPLINT->localState->wanderingHeroes[i]->movement != 0) && !LOCPLINT->localState->isHeroSleeping(LOCPLINT->localState->wanderingHeroes[i]))
-		return i;
-	else
-		return -1;
+	// if we found suitable hero after currently selected hero -> return this hero
+	if (nextSuitable)
+		return nextSuitable;
+
+	// othervice -> loop over and return first suitable hero in the list (or null if none)
+	return firstSuitable;
 }
 
 void CAdventureMapInterface::onHeroChanged(const CGHeroInstance *h)
@@ -423,26 +439,23 @@ void CAdventureMapInterface::onHeroChanged(const CGHeroInstance *h)
 	if (h == LOCPLINT->localState->getCurrentHero())
 		infoBar->showSelection();
 
-	int start = vstd::find_pos(LOCPLINT->localState->wanderingHeroes, h);
-	int next = getNextHeroIndex(start);
-	if (next < 0)
+	const auto * nextSuitableHero = getNextHero(h);
+	if (nextSuitableHero == nullptr)
 	{
 		nextHero->block(true);
 		return;
 	}
-	const CGHeroInstance *nextH = LOCPLINT->localState->wanderingHeroes[next];
-	bool noActiveHeroes = (next == start) && ((nextH->movement == 0) || LOCPLINT->localState->isHeroSleeping(nextH));
-	nextHero->block(noActiveHeroes);
 
-	if(!h)
+	nextHero->block(false);
+
+	if(!LOCPLINT->localState->getCurrentHero())
 	{
 		moveHero->block(true);
 		return;
 	}
-	//default value is for everywhere but CPlayerInterface::moveHero, because paths are not updated from there immediately
-	bool hasPath = LOCPLINT->localState->hasPath(h);
 
-	moveHero->block(!(bool)hasPath || (h->movement == 0));
+	bool hasPath = LOCPLINT->localState->hasPath(h);
+	moveHero->block(!hasPath || h->movement == 0);
 }
 
 void CAdventureMapInterface::onTownChanged(const CGTownInstance * town)
@@ -644,14 +657,6 @@ void CAdventureMapInterface::handleMapScrollingUpdate()
 	}
 }
 
-
-void CAdventureMapInterface::selectionChanged()
-{
-	const CGTownInstance *to = LOCPLINT->localState->ownedTowns[townList->getSelectedIndex()];
-	if (currentSelection != to)
-		LOCPLINT->setSelection(to);
-}
-
 void CAdventureMapInterface::centerOnTile(int3 on)
 {
 	terrain->onCenteredTile(on);
@@ -734,7 +739,7 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key)
 		return;
 	case SDLK_RETURN:
 		{
-			if(!isActive() || !currentSelection)
+			if(!isActive() || !LOCPLINT->localState->getCurrentArmy())
 				return;
 			if(h)
 				LOCPLINT->openHeroWindow(h);
@@ -851,9 +856,8 @@ std::optional<Point> CAdventureMapInterface::keyToMoveDirection(const SDL_Keycod
 void CAdventureMapInterface::onSelectionChanged(const CArmedInstance *sel, bool centerView)
 {
 	assert(sel);
-	if(currentSelection != sel)
-		infoBar->popAll();
-	currentSelection = sel;
+
+	infoBar->popAll();
 	mapAudio->onSelectionChanged(sel);
 	if(centerView)
 		centerOnObject(sel);
@@ -938,7 +942,7 @@ void CAdventureMapInterface::adjustActiveness(bool aiTurnStart)
 
 void CAdventureMapInterface::onCurrentPlayerChanged(PlayerColor playerID)
 {
-	currentSelection = nullptr;
+	LOCPLINT->localState->setSelection(nullptr);
 
 	if (playerID == currentPlayerID)
 		return;
@@ -971,7 +975,7 @@ void CAdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID)
 	const CGHeroInstance * heroToSelect = nullptr;
 
 	// find first non-sleeping hero
-	for (auto hero : LOCPLINT->localState->wanderingHeroes)
+	for (auto hero : LOCPLINT->localState->getWanderingHeroes())
 	{
 		if (!LOCPLINT->localState->isHeroSleeping(hero))
 		{
@@ -988,9 +992,13 @@ void CAdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID)
 		LOCPLINT->setSelection(heroToSelect, centerView);
 	}
 	else if (LOCPLINT->localState->ownedTowns.size())
+	{
 		LOCPLINT->setSelection(LOCPLINT->localState->ownedTowns.front(), centerView);
+	}
 	else
-		LOCPLINT->setSelection(LOCPLINT->localState->wanderingHeroes.front());
+	{
+		LOCPLINT->setSelection(LOCPLINT->localState->getWanderingHero(0), centerView);
+	}
 
 	//show new day animation and sound on infobar
 	infoBar->showDate();
@@ -1048,7 +1056,7 @@ void CAdventureMapInterface::onTileLeftClicked(const int3 &mapPos)
 
 	const CGObjectInstance *topBlocking = getActiveObject(mapPos);
 
-	int3 selPos = currentSelection->getSightCenter();
+	int3 selPos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
 	if(spellBeingCasted && isInScreenRange(selPos, mapPos))
 	{
 		const TerrainTile *heroTile = LOCPLINT->cb->getTile(selPos);
@@ -1071,9 +1079,9 @@ void CAdventureMapInterface::onTileLeftClicked(const int3 &mapPos)
 	canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner);
 
 	bool isHero = false;
-	if(currentSelection->ID != Obj::HERO) //hero is not selected (presumably town)
+	if(LOCPLINT->localState->getCurrentArmy()->ID != Obj::HERO) //hero is not selected (presumably town)
 	{
-		if(currentSelection == topBlocking) //selected town clicked
+		if(LOCPLINT->localState->getCurrentArmy() == topBlocking) //selected town clicked
 			LOCPLINT->openTownWindow(static_cast<const CGTownInstance*>(topBlocking));
 		else if(canSelect)
 			LOCPLINT->setSelection(static_cast<const CArmedInstance*>(topBlocking), false);
@@ -1126,7 +1134,7 @@ void CAdventureMapInterface::onTileHovered(const int3 &mapPos)
 	if(state == EGameState::MAKING_TURN)
 		return;
 
-	if(!currentSelection) //may occur just at the start of game (fake move before full intiialization)
+	if(!LOCPLINT->localState->getCurrentArmy()) //may occur just at the start of game (fake move before full intiialization)
 		return;
 
 	if(!LOCPLINT->cb->isVisible(mapPos))
@@ -1163,7 +1171,7 @@ void CAdventureMapInterface::onTileHovered(const int3 &mapPos)
 		case SpellID::DIMENSION_DOOR:
 			{
 				const TerrainTile * t = LOCPLINT->cb->getTile(mapPos, false);
-				int3 hpos = currentSelection->getSightCenter();
+				int3 hpos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
 				if((!t || t->isClear(LOCPLINT->cb->getTile(hpos))) && isInScreenRange(hpos, mapPos))
 					CCS->curh->set(Cursor::Map::TELEPORT);
 				else
@@ -1173,7 +1181,7 @@ void CAdventureMapInterface::onTileHovered(const int3 &mapPos)
 		}
 	}
 
-	if(currentSelection->ID == Obj::TOWN)
+	if(LOCPLINT->localState->getCurrentArmy()->ID == Obj::TOWN)
 	{
 		if(objAtTile)
 		{
@@ -1222,7 +1230,7 @@ void CAdventureMapInterface::onTileHovered(const int3 &mapPos)
 		case CGPathNode::TELEPORT_BLOCKING_VISIT:
 			if(objAtTile && objAtTile->ID == Obj::HERO)
 			{
-				if(currentSelection == objAtTile)
+				if(LOCPLINT->localState->getCurrentArmy()  == objAtTile)
 					CCS->curh->set(Cursor::Map::HERO);
 				else
 					CCS->curh->set(cursorExchange[turns]);

+ 1 - 5
client/adventureMap/CAdventureMapInterface.h

@@ -59,9 +59,6 @@ private:
 
 	EGameState state;
 
-	/// Currently selected object, can be town, hero or null
-	const CArmedInstance *currentSelection;
-
 	/// currently acting player
 	PlayerColor currentPlayerID;
 
@@ -119,7 +116,6 @@ private:
 	void fnextHero();
 	void fendTurn();
 
-	void selectionChanged();
 	bool isActive();
 	void adjustActiveness(bool aiTurnStart); //should be called every time at AI/human turn transition; blocks GUI during AI turn
 
@@ -137,7 +133,7 @@ private:
 	std::optional<Point> keyToMoveDirection(const SDL_Keycode & key);
 
 	void setHeroSleeping(const CGHeroInstance *hero, bool sleep);
-	int getNextHeroIndex(int startIndex); //for Next Hero button - cycles awake heroes with movement only
+	const CGHeroInstance * getNextHero(const CGHeroInstance * currentHero); //for Next Hero button - cycles awake heroes with movement only
 	void endingTurn();
 
 	/// exits currently opened world view mode and returns to normal map

+ 6 - 6
client/adventureMap/CList.cpp

@@ -225,19 +225,19 @@ std::string CHeroList::CHeroItem::getHoverText()
 
 std::shared_ptr<CIntObject> CHeroList::createHeroItem(size_t index)
 {
-	if (LOCPLINT->localState->wanderingHeroes.size() > index)
-		return std::make_shared<CHeroItem>(this, LOCPLINT->localState->wanderingHeroes[index]);
+	if (LOCPLINT->localState->getWanderingHeroes().size() > index)
+		return std::make_shared<CHeroItem>(this, LOCPLINT->localState->getWanderingHero(index));
 	return std::make_shared<CEmptyHeroItem>();
 }
 
 CHeroList::CHeroList(int size, Point position, std::string btnUp, std::string btnDown):
-	CList(size, position, btnUp, btnDown, LOCPLINT->localState->wanderingHeroes.size(), 303, 304, std::bind(&CHeroList::createHeroItem, this, _1))
+	CList(size, position, btnUp, btnDown, LOCPLINT->localState->getWanderingHeroes().size(), 303, 304, std::bind(&CHeroList::createHeroItem, this, _1))
 {
 }
 
 void CHeroList::select(const CGHeroInstance * hero)
 {
-	selectIndex(vstd::find_pos(LOCPLINT->localState->wanderingHeroes, hero));
+	selectIndex(vstd::find_pos(LOCPLINT->localState->getWanderingHeroes(), hero));
 }
 
 void CHeroList::update(const CGHeroInstance * hero)
@@ -246,7 +246,7 @@ void CHeroList::update(const CGHeroInstance * hero)
 	for(auto & elem : listBox->getItems())
 	{
 		auto item = std::dynamic_pointer_cast<CHeroItem>(elem);
-		if(item && item->hero == hero && vstd::contains(LOCPLINT->localState->wanderingHeroes, hero))
+		if(item && item->hero == hero && vstd::contains(LOCPLINT->localState->getWanderingHeroes(), hero))
 		{
 			item->update();
 			return;
@@ -254,7 +254,7 @@ void CHeroList::update(const CGHeroInstance * hero)
 	}
 	//simplest solution for now: reset list and restore selection
 
-	listBox->resize(LOCPLINT->localState->wanderingHeroes.size());
+	listBox->resize(LOCPLINT->localState->getWanderingHeroes().size());
 	if (LOCPLINT->localState->getCurrentHero())
 		select(LOCPLINT->localState->getCurrentHero());