From 5cbd0f8fc8e702737efe22035242d9880056b3d9 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 17 Apr 2023 15:17:15 +0300 Subject: [PATCH] API for wandering heroes access --- client/CPlayerInterface.cpp | 23 ++-- client/PlayerLocalState.cpp | 27 ++++ client/PlayerLocalState.h | 7 +- .../adventureMap/CAdventureMapInterface.cpp | 124 ++++++++++-------- client/adventureMap/CAdventureMapInterface.h | 6 +- client/adventureMap/CList.cpp | 12 +- 6 files changed, 119 insertions(+), 80 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index e0be8e0d8..959c699b4 100644 --- a/client/CPlayerInterface.cpp +++ b/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 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& objectP void CPlayerInterface::setSelection(const CArmedInstance *sel, bool centerView) { + if (sel == localState->getCurrentArmy()) + return; + localState->setSelection(sel); adventureInt->onSelectionChanged(sel, centerView); } diff --git a/client/PlayerLocalState.cpp b/client/PlayerLocalState.cpp index 043656d44..87d92eb23 100644 --- a/client/PlayerLocalState.cpp +++ b/client/PlayerLocalState.cpp @@ -150,3 +150,30 @@ void PlayerLocalState::setHeroAwaken(const CGHeroInstance * hero) vstd::erase(sleepingHeroes, hero); } + +const std::vector & 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); +} diff --git a/client/PlayerLocalState.h b/client/PlayerLocalState.h index 567caf82d..c178b5b8d 100644 --- a/client/PlayerLocalState.h +++ b/client/PlayerLocalState.h @@ -31,6 +31,7 @@ class PlayerLocalState std::map paths; //maps hero => selected path in adventure map std::vector sleepingHeroes; //if hero is in here, he's sleeping + std::vector wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones) void saveHeroPaths(std::map & paths); void loadHeroPaths(std::map & paths); @@ -52,7 +53,6 @@ public: } } spellbookSettings; - std::vector wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones) std::vector 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 & 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); diff --git a/client/adventureMap/CAdventureMapInterface.cpp b/client/adventureMap/CAdventureMapInterface.cpp index bd462519e..2e6e11a08 100644 --- a/client/adventureMap/CAdventureMapInterface.cpp +++ b/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(LOCPLINT->localState->getCurrentHero(), LOCPLINT, false); } @@ -338,11 +341,10 @@ void CAdventureMapInterface::fsystemOptions() void CAdventureMapInterface::fnextHero() { - auto hero = dynamic_cast(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 - { - i++; - if (i >= LOCPLINT->localState->wanderingHeroes.size()) - i = 0; - } - while (((LOCPLINT->localState->wanderingHeroes[i]->movement == 0) || LOCPLINT->localState->isHeroSleeping(LOCPLINT->localState->wanderingHeroes[i])) && (i != startIndex)); + bool currentHeroFound = false; + const CGHeroInstance * firstSuitable = nullptr; + const CGHeroInstance * nextSuitable = nullptr; - if ((LOCPLINT->localState->wanderingHeroes[i]->movement != 0) && !LOCPLINT->localState->isHeroSleeping(LOCPLINT->localState->wanderingHeroes[i])) - return i; - else - return -1; + for (auto const * hero : LOCPLINT->localState->getWanderingHeroes()) + { + 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; + } + + // 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 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(topBlocking)); else if(canSelect) LOCPLINT->setSelection(static_cast(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]); diff --git a/client/adventureMap/CAdventureMapInterface.h b/client/adventureMap/CAdventureMapInterface.h index 67007194a..21fdb6e32 100644 --- a/client/adventureMap/CAdventureMapInterface.h +++ b/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 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 diff --git a/client/adventureMap/CList.cpp b/client/adventureMap/CList.cpp index f9957df8b..3a28420ea 100644 --- a/client/adventureMap/CList.cpp +++ b/client/adventureMap/CList.cpp @@ -225,19 +225,19 @@ std::string CHeroList::CHeroItem::getHoverText() std::shared_ptr CHeroList::createHeroItem(size_t index) { - if (LOCPLINT->localState->wanderingHeroes.size() > index) - return std::make_shared(this, LOCPLINT->localState->wanderingHeroes[index]); + if (LOCPLINT->localState->getWanderingHeroes().size() > index) + return std::make_shared(this, LOCPLINT->localState->getWanderingHero(index)); return std::make_shared(); } 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(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());