From 4a169972f029da50e90c3c7366b996606e1e49c9 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 17 Apr 2023 01:09:25 +0300 Subject: [PATCH] Further refactoring to cleanup code of advmap and locplint --- client/CPlayerInterface.cpp | 99 ++--- client/CPlayerInterface.h | 36 +- .../adventureMap/CAdventureMapInterface.cpp | 359 +++++++----------- client/adventureMap/CAdventureMapInterface.h | 68 ++-- client/adventureMap/CMinimap.cpp | 1 + client/adventureMap/CMinimap.h | 3 +- client/battle/BattleStacksController.cpp | 8 - 7 files changed, 237 insertions(+), 337 deletions(-) diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index ec4e89c34..d4f6b7e3b 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -254,24 +254,42 @@ void CPlayerInterface::playerStartsTurn(PlayerColor player) EVENT_HANDLER_CALLED_BY_CLIENT; if (!vstd::contains (GH.listInt, adventureInt)) { - GH.popInts ((int)GH.listInt.size()); //after map load - remove everything else + // after map load - remove all active windows and replace them with adventure map + GH.popInts ((int)GH.listInt.size()); GH.pushInt (adventureInt); } - else - { - while (GH.listInt.front() != adventureInt && !dynamic_cast(GH.listInt.front().get())) //don't remove dialogs that expect query answer - GH.popInts(1); - } - if(CSH->howManyPlayerInterfaces() == 1) - { - GH.curInt = this; - adventureInt->startTurn(); - } - if (player != playerID && this == LOCPLINT) + // remove all dialogs that do not expect query answer + while (GH.listInt.front() != adventureInt && !dynamic_cast(GH.listInt.front().get())) + GH.popInts(1); + + if (player != playerID && LOCPLINT == this) { waitWhileDialog(); - adventureInt->aiTurnStarted(); + adventureInt->onEnemyTurnStarted(player); + } +} + +void CPlayerInterface::performAutosave() +{ + std::string prefix = settings["session"]["saveprefix"].String(); + int frequency = static_cast(settings["general"]["saveFrequency"].Integer()); + if(firstCall) + { + autosaveCount = getLastIndex(prefix + "Autosave_"); + + if(firstCall > 0) //new game, not loaded + { + int index = getLastIndex(prefix + "Newgame_"); + index %= SAVES_COUNT; + cb->save("Saves/" + prefix + "Newgame_Autosave_" + std::to_string(index + 1)); + } + firstCall = 0; + } + else if(frequency > 0 && cb->getDate() % frequency == 0) + { + LOCPLINT->cb->save("Saves/" + prefix + "Autosave_" + std::to_string(autosaveCount++ + 1)); + autosaveCount %= 5; } } @@ -285,35 +303,11 @@ void CPlayerInterface::yourTurn() GH.curInt = this; NotificationHandler::notify("Your turn"); - - std::string prefix = settings["session"]["saveprefix"].String(); - int frequency = static_cast(settings["general"]["saveFrequency"].Integer()); - if (firstCall) - { - if(CSH->howManyPlayerInterfaces() == 1) - adventureInt->onCurrentPlayerChanged(playerID); - - autosaveCount = getLastIndex(prefix + "Autosave_"); - - if (firstCall > 0) //new game, not loaded - { - int index = getLastIndex(prefix + "Newgame_"); - index %= SAVES_COUNT; - cb->save("Saves/" + prefix + "Newgame_Autosave_" + std::to_string(index + 1)); - } - firstCall = 0; - } - else if(frequency > 0 && cb->getDate() % frequency == 0) - { - LOCPLINT->cb->save("Saves/" + prefix + "Autosave_" + std::to_string(autosaveCount++ + 1)); - autosaveCount %= 5; - } - - adventureInt->onCurrentPlayerChanged(playerID); + performAutosave(); if (CSH->howManyPlayerInterfaces() > 1) //hot seat message { - adventureInt->startHotSeatWait(playerID); + adventureInt->onHotseatWaitStarted(playerID); makingTurn = true; std::string msg = CGI->generaltexth->allTexts[13]; @@ -325,7 +319,7 @@ void CPlayerInterface::yourTurn() else { makingTurn = true; - adventureInt->startTurn(); + adventureInt->onPlayerTurnStarted(playerID); } } acceptTurn(); @@ -343,11 +337,9 @@ void CPlayerInterface::acceptTurn() { waitWhileDialog(); // wait for player to accept turn in hot-seat mode - adventureInt->startTurn(); + adventureInt->onPlayerTurnStarted(playerID); } - adventureInt->initializeNewTurn(); - // warn player if he has no town if (cb->howManyTowns() == 0) { @@ -532,11 +524,6 @@ void CPlayerInterface::openTownWindow(const CGTownInstance * town) GH.pushInt(newCastleInt); } -void CPlayerInterface::activateForSpectator() -{ - adventureInt->activate(); -} - void CPlayerInterface::heroPrimarySkillChanged(const CGHeroInstance * hero, int which, si64 val) { EVENT_HANDLER_CALLED_BY_CLIENT; @@ -698,11 +685,6 @@ void CPlayerInterface::garrisonsChanged(std::vector ob GH.totalRedraw(); } -void CPlayerInterface::garrisonChanged( const CGObjectInstance * obj) -{ - garrisonsChanged(std::vector(1, obj)); -} - void CPlayerInterface::buildChanged(const CGTownInstance *town, BuildingID buildingID, int what) //what: 1 - built, 2 - demolished { EVENT_HANDLER_CALLED_BY_CLIENT; @@ -1669,10 +1651,12 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul if (victoryLossCheckResult.loss()) showInfoDialog(CGI->generaltexth->allTexts[95]); - //we assume GH.curInt == LOCPLINT + assert(GH.curInt == LOCPLINT); auto previousInterface = LOCPLINT; //without multiple player interfaces some of lines below are useless, but for hotseat we wanna swap player interface temporarily + LOCPLINT = this; //this is needed for dialog to show and avoid freeze, dialog showing logic should be reworked someday GH.curInt = this; //waiting for dialogs requires this to get events + if(!makingTurn) { makingTurn = true; //also needed for dialog to show with current implementation @@ -1690,9 +1674,7 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul if(adventureInt) { GH.terminate_cond->setn(true); - adventureInt->deactivate(); - if (GH.topInt() == adventureInt) - GH.popInt(adventureInt); + GH.popInts(GH.listInt.size()); adventureInt.reset(); } } @@ -1710,7 +1692,8 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul requestReturningToMainMenu(false); } - if (GH.curInt == this) GH.curInt = nullptr; + if (GH.curInt == this) + GH.curInt = nullptr; } else { diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index c023d676f..edaa8e470 100644 --- a/client/CPlayerInterface.h +++ b/client/CPlayerInterface.h @@ -91,30 +91,35 @@ public: /// Central class for managing user interface logic class CPlayerInterface : public CGameInterface, public IUpdateable { -public: - HeroPathStorage paths; + // -1 - just loaded game; 1 - just started game; 0 otherwise + int firstCall; + int autosaveCount; + static const int SAVES_COUNT = 5; + + std::pair lastBattleArmies; + bool allowBattleReplay = false; + std::list> dialogs; //queue of dialogs awaiting to be shown (not currently shown!) + const BattleAction *curAction; //during the battle - action currently performed by active stack (or nullptr) - std::shared_ptr env; ObjectInstanceID destinationTeleport; //contain -1 or object id if teleportation int3 destinationTeleportPos; +public: // TODO: make private + std::shared_ptr env; + + HeroPathStorage paths; + //minor interfaces CondSh *showingDialog; //indicates if dialog box is displayed static boost::recursive_mutex *pim; bool makingTurn; //if player is already making his turn - int firstCall; // -1 - just loaded game; 1 - just started game; 0 otherwise - int autosaveCount; - static const int SAVES_COUNT = 5; CCastleInterface * castleInt; //nullptr if castle window isn't opened static std::shared_ptr battleInt; //nullptr if no battle CInGameConsole * cingconsole; std::shared_ptr cb; //to communicate with engine - const BattleAction *curAction; //during the battle - action currently performed by active stack (or nullptr) - - std::list> dialogs; //queue of dialogs awaiting to be shown (not currently shown!) std::vector wanderingHeroes; //our heroes on the adventure map (not the garrisoned ones) std::vector towns; //our towns on the adventure map @@ -123,8 +128,6 @@ public: //During battle is quick combat mode is used std::shared_ptr autofightingAI; //AI that makes decisions bool isAutoFightOn; //Flag, switch it to stop quick combat. Don't touch if there is no battle interface. - bool allowBattleReplay = false; - std::pair lastBattleArmies; struct SpellbookLastSetting { @@ -141,6 +144,7 @@ public: } } spellbookSettings; +public: void update() override; void initializeHeroTownList(); int getLastIndex(std::string namePrefix); @@ -233,7 +237,6 @@ public: //-------------// void showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList onYes); void garrisonsChanged(std::vector objs); - void garrisonChanged(const CGObjectInstance * obj); void heroKilled(const CGHeroInstance* hero); void waitWhileDialog(bool unlockPim = true); void waitForAllDialogs(bool unlockPim = true); @@ -241,7 +244,6 @@ public: void openTownWindow(const CGTownInstance * town); //shows townscreen void openHeroWindow(const CGHeroInstance * hero); //shows hero window with given hero void initGameInterface(std::shared_ptr ENV, std::shared_ptr CB) override; - void activateForSpectator(); // TODO: spectator probably need own player interface class // show dialogs void showInfoDialog(const std::string &text, std::shared_ptr component); @@ -265,11 +267,8 @@ public: ~CPlayerInterface(); private: - template void serializeTempl(Handler &h, const int version); -private: - struct IgnoreEvents { CPlayerInterface & owner; @@ -284,14 +283,15 @@ private: }; - - bool duringMovement; bool ignoreEvents; size_t numOfMovedArts; void doMoveHero(const CGHeroInstance *h, CGPath path); void setMovementStatus(bool value); + + /// Performs autosave, if needed according to settings + void performAutosave(); }; extern CPlayerInterface * LOCPLINT; diff --git a/client/adventureMap/CAdventureMapInterface.cpp b/client/adventureMap/CAdventureMapInterface.cpp index a938555ee..cdf2ffd2e 100644 --- a/client/adventureMap/CAdventureMapInterface.cpp +++ b/client/adventureMap/CAdventureMapInterface.cpp @@ -55,34 +55,7 @@ std::shared_ptr adventureInt; -void CAdventureMapInterface::setScrollingCursor(ui8 direction) const -{ - if(direction & CAdventureMapInterface::RIGHT) - { - if(direction & CAdventureMapInterface::UP) - CCS->curh->set(Cursor::Map::SCROLL_NORTHEAST); - else if(direction & CAdventureMapInterface::DOWN) - CCS->curh->set(Cursor::Map::SCROLL_SOUTHEAST); - else - CCS->curh->set(Cursor::Map::SCROLL_EAST); - } - else if(direction & CAdventureMapInterface::LEFT) - { - if(direction & CAdventureMapInterface::UP) - CCS->curh->set(Cursor::Map::SCROLL_NORTHWEST); - else if(direction & CAdventureMapInterface::DOWN) - CCS->curh->set(Cursor::Map::SCROLL_SOUTHWEST); - else - CCS->curh->set(Cursor::Map::SCROLL_WEST); - } - else if(direction & CAdventureMapInterface::UP) - CCS->curh->set(Cursor::Map::SCROLL_NORTH); - else if(direction & CAdventureMapInterface::DOWN) - CCS->curh->set(Cursor::Map::SCROLL_SOUTH); -} - CAdventureMapInterface::CAdventureMapInterface(): - mode(EAdvMapMode::NORMAL), minimap(new CMinimap(Rect(ADVOPT.minimapX, ADVOPT.minimapY, ADVOPT.minimapW, ADVOPT.minimapH))), statusbar(CGStatusBar::create(ADVOPT.statusbarX,ADVOPT.statusbarY,ADVOPT.statusbarG)), heroList(new CHeroList(ADVOPT.hlistSize, Point(ADVOPT.hlistX, ADVOPT.hlistY), ADVOPT.hlistAU, ADVOPT.hlistAD)), @@ -91,13 +64,11 @@ CAdventureMapInterface::CAdventureMapInterface(): resdatabar(new CResDataBar), mapAudio(new MapAudioPlayer()), terrain(new MapView(Point(ADVOPT.advmapX, ADVOPT.advmapY), Point(ADVOPT.advmapW, ADVOPT.advmapH))), - state(EGameStates::NA), + state(EGameState::NOT_INITIALIZED), spellBeingCasted(nullptr), - selection(nullptr), + currentSelection(nullptr), activeMapPanel(nullptr), - duringAITurn(false), - scrollingDir(0), - scrollingState(false) + scrollingCursorSet(false) { pos.x = pos.y = 0; pos.w = GH.screenDimensions().x; @@ -218,7 +189,7 @@ CAdventureMapInterface::CAdventureMapInterface(): onCurrentPlayerChanged(LOCPLINT->playerID); - int iconColorMultiplier = player.getNum() * 19; + int iconColorMultiplier = currentPlayerID.getNum() * 19; int wvLeft = heroList->pos.x - 2; // TODO correct drawing position //int wvTop = 195; for (int i = 0; i < 5; ++i) @@ -246,8 +217,6 @@ CAdventureMapInterface::CAdventureMapInterface(): underground->block(!CGI->mh->getMap()->twoLevel); questlog->block(!CGI->mh->getMap()->quests.size()); worldViewUnderground->block(!CGI->mh->getMap()->twoLevel); - - addUsedEvents(MOVE); } void CAdventureMapInterface::fshowOverview() @@ -351,7 +320,7 @@ void CAdventureMapInterface::fshowSpellbok() if (!getCurrentHero()) //checking necessary values return; - centerOnObject(selection); + centerOnObject(currentSelection); GH.pushIntT(getCurrentHero(), LOCPLINT, false); } @@ -368,7 +337,7 @@ void CAdventureMapInterface::fsystemOptions() void CAdventureMapInterface::fnextHero() { - auto hero = dynamic_cast(selection); + auto hero = dynamic_cast(currentSelection); int next = getNextHeroIndex(vstd::find_pos(LOCPLINT->wanderingHeroes, hero)); if (next < 0) return; @@ -451,7 +420,7 @@ void CAdventureMapInterface::onHeroChanged(const CGHeroInstance *h) heroList->update(h); if (h == getCurrentHero()) - adventureInt->infoBar->showSelection(); + infoBar->showSelection(); int start = vstd::find_pos(LOCPLINT->wanderingHeroes, h); int next = getNextHeroIndex(start); @@ -478,7 +447,7 @@ void CAdventureMapInterface::onHeroChanged(const CGHeroInstance *h) void CAdventureMapInterface::onTownChanged(const CGTownInstance * town) { townList->update(town); - adventureInt->infoBar->showSelection(); + infoBar->showSelection(); } void CAdventureMapInterface::showInfoBoxMessage(const std::vector & components, std::string message, int timer) @@ -501,10 +470,12 @@ void CAdventureMapInterface::activate() LOCPLINT->cingconsole->pos = this->pos; } - if(!duringAITurn) + if(state != EGameState::ENEMY_TURN) { + assert(state == EGameState::MAKING_TURN); + activeMapPanel->activate(); - if (mode == EAdvMapMode::NORMAL) + if (state == EGameState::MAKING_TURN) { heroList->activate(); townList->activate(); @@ -522,13 +493,13 @@ void CAdventureMapInterface::deactivate() { CIntObject::deactivate(); - if(!duringAITurn) + if(state != EGameState::ENEMY_TURN) { - scrollingDir = 0; + assert(state == EGameState::MAKING_TURN); CCS->curh->set(Cursor::Map::POINTER); activeMapPanel->deactivate(); - if (mode == EAdvMapMode::NORMAL) + if (state == EGameState::MAKING_TURN) { heroList->deactivate(); townList->deactivate(); @@ -544,20 +515,13 @@ void CAdventureMapInterface::showAll(SDL_Surface * to) { bg->draw(to, 0, 0); - if(state != EGameStates::INGAME) + if(state != EGameState::MAKING_TURN) return; - switch (mode) - { - case EAdvMapMode::NORMAL: + heroList->showAll(to); + townList->showAll(to); + infoBar->showAll(to); - heroList->showAll(to); - townList->showAll(to); - infoBar->showAll(to); - break; - case EAdvMapMode::WORLD_VIEW: - break; - } activeMapPanel->showAll(to); minimap->showAll(to); @@ -585,7 +549,7 @@ void CAdventureMapInterface::onHeroWokeUp(const CGHeroInstance * hero) sleepWake->clickLeft(true, false); sleepWake->clickLeft(false, true); //could've just called - //adventureInt->fsleepWake(); + //fsleepWake(); //but no authentic button click/sound ;-) } @@ -595,12 +559,13 @@ void CAdventureMapInterface::setHeroSleeping(const CGHeroInstance *hero, bool sl LOCPLINT->sleepingHeroes.push_back(hero); //FIXME: should we check for existence? else LOCPLINT->sleepingHeroes -= hero; - onHeroChanged(nullptr); + + onHeroChanged(hero); } void CAdventureMapInterface::show(SDL_Surface * to) { - if(state != EGameStates::INGAME) + if(state != EGameState::MAKING_TURN) return; handleMapScrollingUpdate(); @@ -628,38 +593,69 @@ void CAdventureMapInterface::show(SDL_Surface * to) void CAdventureMapInterface::handleMapScrollingUpdate() { uint32_t timePassed = GH.mainFPSmng->getElapsedMilliseconds(); - double scrollSpeedPixels = settings["adventure"]["scrollSpeedPixels"].Float(); - int32_t scrollDistance = static_cast(scrollSpeedPixels * timePassed / 1000); - //if advmap needs updating AND (no dialog is shown OR ctrl is pressed) + uint32_t scrollSpeedPixels = settings["adventure"]["scrollSpeedPixels"].Float(); + uint32_t scrollDistance = scrollSpeedPixels * timePassed / 1000; - if(scrollingDir & LEFT) - terrain->onMapScrolled(Point(-scrollDistance, 0)); + bool scrollingActive = !GH.isKeyboardCtrlDown() && isActive() && state == EGameState::MAKING_TURN; - if(scrollingDir & RIGHT) - terrain->onMapScrolled(Point(+scrollDistance, 0)); + Point cursorPosition = GH.getCursorPosition(); + Point scrollDirection; - if(scrollingDir & UP) - terrain->onMapScrolled(Point(0, -scrollDistance)); + if (cursorPosition.x < 15) + scrollDirection.x = -1; - if(scrollingDir & DOWN) - terrain->onMapScrolled(Point(0, +scrollDistance)); + if (cursorPosition.x > GH.screenDimensions().x - 15) + scrollDirection.x = +1; - if(scrollingDir) + if (cursorPosition.y < 15) + scrollDirection.y = -1; + + if (cursorPosition.y > GH.screenDimensions().y - 15) + scrollDirection.y = +1; + + Point scrollDelta = scrollDirection * scrollDistance; + + if (scrollingActive && scrollDelta != Point(0,0)) + terrain->onMapScrolled(scrollDelta); + + if (scrollDelta == Point(0,0) && !scrollingCursorSet) + return; + + if(scrollDelta.x > 0) { - setScrollingCursor(scrollingDir); - scrollingState = true; + if(scrollDelta.y < 0) + CCS->curh->set(Cursor::Map::SCROLL_NORTHEAST); + if(scrollDelta.y > 0) + CCS->curh->set(Cursor::Map::SCROLL_SOUTHEAST); + if(scrollDelta.y == 0) + CCS->curh->set(Cursor::Map::SCROLL_EAST); } - else if(scrollingState) + if(scrollDelta.x < 0) { - CCS->curh->set(Cursor::Map::POINTER); - scrollingState = false; + if(scrollDelta.y < 0) + CCS->curh->set(Cursor::Map::SCROLL_NORTHWEST); + if(scrollDelta.y > 0) + CCS->curh->set(Cursor::Map::SCROLL_SOUTHWEST); + if(scrollDelta.y == 0) + CCS->curh->set(Cursor::Map::SCROLL_WEST); + } + + if (scrollDelta.x == 0) + { + if(scrollDelta.y < 0) + CCS->curh->set(Cursor::Map::SCROLL_NORTH); + if(scrollDelta.y > 0) + CCS->curh->set(Cursor::Map::SCROLL_SOUTH); + if(scrollDelta.y == 0) + CCS->curh->set(Cursor::Map::POINTER); } } + void CAdventureMapInterface::selectionChanged() { const CGTownInstance *to = LOCPLINT->towns[townList->getSelectedIndex()]; - if (selection != to) + if (currentSelection != to) setSelection(to); } @@ -673,37 +669,9 @@ void CAdventureMapInterface::centerOnObject(const CGObjectInstance * obj) terrain->onCenteredObject(obj); } -void CAdventureMapInterface::keyReleased(const SDL_Keycode &key) -{ - if (mode != EAdvMapMode::NORMAL) - return; - - switch (key) - { - case SDLK_s: - if(isActive()) - GH.pushIntT(); - return; - default: - { - auto direction = keyToMoveDirection(key); - - if (!direction) - return; - - ui8 Dir = (direction->x<0 ? LEFT : 0) | - (direction->x>0 ? RIGHT : 0) | - (direction->y<0 ? UP : 0) | - (direction->y>0 ? DOWN : 0) ; - - scrollingDir &= ~Dir; - } - } -} - void CAdventureMapInterface::keyPressed(const SDL_Keycode & key) { - if (mode != EAdvMapMode::NORMAL) + if (state != EGameState::MAKING_TURN) return; const CGHeroInstance *h = getCurrentHero(); //selected hero @@ -732,6 +700,10 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key) if(isActive()) CAdventureOptions::showScenarioInfo(); return; + case SDLK_s: + if(isActive()) + GH.pushIntT(); + return; case SDLK_l: if(isActive()) LOCPLINT->proposeLoadingGame(); @@ -769,7 +741,7 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key) return; case SDLK_RETURN: { - if(!isActive() || !selection) + if(!isActive() || !currentSelection) return; if(h) LOCPLINT->openHeroWindow(h); @@ -783,7 +755,7 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key) if(!isActive() || GH.topInt().get() != this || !spellBeingCasted) return; - leaveCastingMode(); + abortCastingMode(); return; } case SDLK_t: @@ -830,20 +802,6 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key) if (!direction) return; - ui8 Dir = (direction->x<0 ? LEFT : 0) | - (direction->x>0 ? RIGHT : 0) | - (direction->y<0 ? UP : 0) | - (direction->y>0 ? DOWN : 0) ; - - - - //ctrl makes arrow move screen, not hero - if(GH.isKeyboardCtrlDown()) - { - scrollingDir |= Dir; - return; - } - if(!h || !isActive()) return; @@ -872,7 +830,6 @@ void CAdventureMapInterface::keyPressed(const SDL_Keycode & key) if(!path.nodes[0].turns) LOCPLINT->moveHero(h, path); } - return; } } @@ -901,9 +858,9 @@ std::optional CAdventureMapInterface::keyToMoveDirection(const SDL_Keycod void CAdventureMapInterface::setSelection(const CArmedInstance *sel, bool centerView) { assert(sel); - if(selection != sel) + if(currentSelection != sel) infoBar->popAll(); - selection = sel; + currentSelection = sel; mapAudio->onSelectionChanged(sel); if(centerView) centerOnObject(sel); @@ -938,47 +895,6 @@ void CAdventureMapInterface::setSelection(const CArmedInstance *sel, bool center heroList->redraw(); } -void CAdventureMapInterface::mouseMoved( const Point & cursorPosition ) -{ - // adventure map scrolling with mouse - // currently disabled in world view mode (as it is in OH3), but should work correctly if mode check is removed - if(!GH.isKeyboardCtrlDown() && isActive() && mode == EAdvMapMode::NORMAL) - { - if(cursorPosition.x<15) - { - scrollingDir |= LEFT; - } - else - { - scrollingDir &= ~LEFT; - } - if(cursorPosition.x > GH.screenDimensions().x - 15) - { - scrollingDir |= RIGHT; - } - else - { - scrollingDir &= ~RIGHT; - } - if(cursorPosition.y<15) - { - scrollingDir |= UP; - } - else - { - scrollingDir &= ~UP; - } - if(cursorPosition.y > GH.screenDimensions().y - 15) - { - scrollingDir |= DOWN; - } - else - { - scrollingDir &= ~DOWN; - } - } -} - bool CAdventureMapInterface::isActive() { return active & ~CIntObject::KEYBOARD; @@ -992,22 +908,23 @@ void CAdventureMapInterface::onMapTilesChanged(boost::optionalupdate(); } -void CAdventureMapInterface::startHotSeatWait(PlayerColor Player) +void CAdventureMapInterface::onHotseatWaitStarted(PlayerColor playerID) { - state = EGameStates::WAITING; + onCurrentPlayerChanged(playerID); + state = EGameState::HOTSEAT_WAIT; } -void CAdventureMapInterface::aiTurnStarted() +void CAdventureMapInterface::onEnemyTurnStarted(PlayerColor playerID) { if(settings["session"]["spectate"].Bool()) return; adjustActiveness(true); mapAudio->onEnemyTurnStarted(); - adventureInt->minimap->setAIRadar(true); - adventureInt->infoBar->startEnemyTurn(LOCPLINT->cb->getCurrentPlayer()); - adventureInt->minimap->showAll(screen);//force refresh on inactive object - adventureInt->infoBar->showAll(screen);//force refresh on inactive object + minimap->setAIRadar(true); + infoBar->startEnemyTurn(LOCPLINT->cb->getCurrentPlayer()); + minimap->showAll(screen);//force refresh on inactive object + infoBar->showAll(screen);//force refresh on inactive object } void CAdventureMapInterface::adjustActiveness(bool aiTurnStart) @@ -1016,30 +933,37 @@ void CAdventureMapInterface::adjustActiveness(bool aiTurnStart) if(wasActive) deactivate(); - adventureInt->duringAITurn = aiTurnStart; + + if (aiTurnStart) + state = EGameState::ENEMY_TURN; + else + state = EGameState::MAKING_TURN; + if(wasActive) activate(); } -void CAdventureMapInterface::onCurrentPlayerChanged(PlayerColor Player) +void CAdventureMapInterface::onCurrentPlayerChanged(PlayerColor playerID) { - selection = nullptr; + currentSelection = nullptr; - if (Player == player) + if (playerID == currentPlayerID) return; - player = Player; - bg->playerColored(player); + currentPlayerID = playerID; + bg->playerColored(currentPlayerID); - panelMain->setPlayerColor(player); - panelWorldView->setPlayerColor(player); - panelWorldView->recolorIcons(player, player.getNum() * 19); - resdatabar->colorize(player); + panelMain->setPlayerColor(currentPlayerID); + panelWorldView->setPlayerColor(currentPlayerID); + panelWorldView->recolorIcons(currentPlayerID, currentPlayerID.getNum() * 19); + resdatabar->colorize(currentPlayerID); } -void CAdventureMapInterface::startTurn() +void CAdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID) { - state = EGameStates::INGAME; + onCurrentPlayerChanged(playerID); + + state = EGameState::MAKING_TURN; if(LOCPLINT->cb->getCurrentPlayer() == LOCPLINT->playerID || settings["session"]["spectate"].Bool()) { @@ -1047,10 +971,7 @@ void CAdventureMapInterface::startTurn() minimap->setAIRadar(false); infoBar->showSelection(); } -} -void CAdventureMapInterface::initializeNewTurn() -{ heroList->update(); townList->update(); @@ -1121,7 +1042,7 @@ const CGObjectInstance* CAdventureMapInterface::getActiveObject(const int3 &mapP void CAdventureMapInterface::onTileLeftClicked(const int3 &mapPos) { - if(mode != EAdvMapMode::NORMAL) + if(state != EGameState::MAKING_TURN) return; //FIXME: this line breaks H3 behavior for Dimension Door @@ -1134,7 +1055,7 @@ void CAdventureMapInterface::onTileLeftClicked(const int3 &mapPos) const CGObjectInstance *topBlocking = getActiveObject(mapPos); - int3 selPos = selection->getSightCenter(); + int3 selPos = currentSelection->getSightCenter(); if(spellBeingCasted && isInScreenRange(selPos, mapPos)) { const TerrainTile *heroTile = LOCPLINT->cb->getTile(selPos); @@ -1143,11 +1064,11 @@ void CAdventureMapInterface::onTileLeftClicked(const int3 &mapPos) { case SpellID::SCUTTLE_BOAT: //Scuttle Boat if(topBlocking && topBlocking->ID == Obj::BOAT) - leaveCastingMode(true, mapPos); + leaveCastingMode(mapPos); break; case SpellID::DIMENSION_DOOR: if(!tile || tile->isClear(heroTile)) - leaveCastingMode(true, mapPos); + leaveCastingMode(mapPos); break; } return; @@ -1157,9 +1078,9 @@ void CAdventureMapInterface::onTileLeftClicked(const int3 &mapPos) canSelect |= topBlocking && topBlocking->ID == Obj::TOWN && LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, topBlocking->tempOwner); bool isHero = false; - if(selection->ID != Obj::HERO) //hero is not selected (presumably town) + if(currentSelection->ID != Obj::HERO) //hero is not selected (presumably town) { - if(selection == topBlocking) //selected town clicked + if(currentSelection == topBlocking) //selected town clicked LOCPLINT->openTownWindow(static_cast(topBlocking)); else if(canSelect) setSelection(static_cast(topBlocking), false); @@ -1209,9 +1130,12 @@ void CAdventureMapInterface::onTileLeftClicked(const int3 &mapPos) void CAdventureMapInterface::onTileHovered(const int3 &mapPos) { - if(mode != EAdvMapMode::NORMAL //disable in world view - || !selection) //may occur just at the start of game (fake move before full intiialization) + if(state == EGameState::MAKING_TURN) return; + + if(!currentSelection) //may occur just at the start of game (fake move before full intiialization) + return; + if(!LOCPLINT->cb->isVisible(mapPos)) { CCS->curh->set(Cursor::Map::POINTER); @@ -1246,7 +1170,7 @@ void CAdventureMapInterface::onTileHovered(const int3 &mapPos) case SpellID::DIMENSION_DOOR: { const TerrainTile * t = LOCPLINT->cb->getTile(mapPos, false); - int3 hpos = selection->getSightCenter(); + int3 hpos = currentSelection->getSightCenter(); if((!t || t->isClear(LOCPLINT->cb->getTile(hpos))) && isInScreenRange(hpos, mapPos)) CCS->curh->set(Cursor::Map::TELEPORT); else @@ -1256,7 +1180,7 @@ void CAdventureMapInterface::onTileHovered(const int3 &mapPos) } } - if(selection->ID == Obj::TOWN) + if(currentSelection->ID == Obj::TOWN) { if(objAtTile) { @@ -1305,7 +1229,7 @@ void CAdventureMapInterface::onTileHovered(const int3 &mapPos) case CGPathNode::TELEPORT_BLOCKING_VISIT: if(objAtTile && objAtTile->ID == Obj::HERO) { - if(selection == objAtTile) + if(currentSelection == objAtTile) CCS->curh->set(Cursor::Map::HERO); else CCS->curh->set(cursorExchange[turns]); @@ -1368,13 +1292,15 @@ void CAdventureMapInterface::showMoveDetailsInStatusbar(const CGHeroInstance & h void CAdventureMapInterface::onTileRightClicked(const int3 &mapPos) { - if(mode != EAdvMapMode::NORMAL) + if(state != EGameState::MAKING_TURN) return; + if(spellBeingCasted) { - leaveCastingMode(); + abortCastingMode(); return; } + if(!LOCPLINT->cb->isVisible(mapPos)) { CRClickPopup::createAndPush(VLC->generaltexth->allTexts[61]); //Uncharted Territory @@ -1407,40 +1333,45 @@ void CAdventureMapInterface::enterCastingMode(const CSpell * sp) GH.fakeMouseMove(); } -void CAdventureMapInterface::leaveCastingMode(bool cast, int3 dest) +void CAdventureMapInterface::abortCastingMode() { assert(spellBeingCasted); - SpellID id = spellBeingCasted->id; spellBeingCasted = nullptr; terrain->deactivate(); activate(); +} - if(cast) +void CAdventureMapInterface::leaveCastingMode(const int3 & dest) +{ + SpellID id = spellBeingCasted->id; + + abortCastingMode(); +// if(cast) LOCPLINT->cb->castSpell(getCurrentHero(), id, dest); - else - LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[731]); //Spell cancelled +// else +// LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[731]); //Spell cancelled } const CGHeroInstance * CAdventureMapInterface::getCurrentHero() const { - if(selection && selection->ID == Obj::HERO) - return dynamic_cast(selection); + if(currentSelection && currentSelection->ID == Obj::HERO) + return dynamic_cast(currentSelection); else return nullptr; } const CGTownInstance * CAdventureMapInterface::getCurrentTown() const { - if(selection && selection->ID == Obj::TOWN) - return dynamic_cast(selection); + if(currentSelection && currentSelection->ID == Obj::TOWN) + return dynamic_cast(currentSelection); else return nullptr; } const CArmedInstance * CAdventureMapInterface::getCurrentArmy() const { - if (selection) - return dynamic_cast(selection); + if (currentSelection) + return dynamic_cast(currentSelection); else return nullptr; } @@ -1455,7 +1386,7 @@ const IShipyard * CAdventureMapInterface::ourInaccessibleShipyard(const CGObject const IShipyard *ret = IShipyard::castFrom(obj); if(!ret || - obj->tempOwner != player || + obj->tempOwner != currentPlayerID || (CCS->curh->get() != Cursor::Map::T1_SAIL && CCS->curh->get() != Cursor::Map::POINTER)) return nullptr; @@ -1464,7 +1395,7 @@ const IShipyard * CAdventureMapInterface::ourInaccessibleShipyard(const CGObject void CAdventureMapInterface::exitWorldView() { - mode = EAdvMapMode::NORMAL; + state = EGameState::MAKING_TURN; panelMain->activate(); panelWorldView->deactivate(); @@ -1480,7 +1411,7 @@ void CAdventureMapInterface::exitWorldView() void CAdventureMapInterface::openWorldView(int tileSize) { - mode = EAdvMapMode::WORLD_VIEW; + state = EGameState::WORLD_VIEW; panelMain->deactivate(); panelWorldView->activate(); diff --git a/client/adventureMap/CAdventureMapInterface.h b/client/adventureMap/CAdventureMapInterface.h index 2f2d86c75..e3c3ad396 100644 --- a/client/adventureMap/CAdventureMapInterface.h +++ b/client/adventureMap/CAdventureMapInterface.h @@ -11,9 +11,6 @@ #include "../gui/CIntObject.h" -#include "../../lib/int3.h" -#include "../../lib/GameConstants.h" - VCMI_LIB_NAMESPACE_BEGIN class CGObjectInstance; @@ -24,6 +21,7 @@ class IShipyard; struct CGPathNode; struct ObjectPosInfo; struct Component; +class int3; VCMI_LIB_NAMESPACE_END @@ -44,35 +42,31 @@ class MapAudioPlayer; struct MapDrawingInfo; -enum class EAdvMapMode -{ - NORMAL, - WORLD_VIEW -}; - /// That's a huge class which handles general adventure map actions and /// shows the right menu(questlog, spellbook, end turn,..) from where you /// can get to the towns and heroes. class CAdventureMapInterface : public CIntObject { private: - enum EDirections {LEFT=1, RIGHT=2, UP=4, DOWN=8}; - enum class EGameStates {NA, INGAME, WAITING}; + enum class EGameState + { + NOT_INITIALIZED, + HOTSEAT_WAIT, + MAKING_TURN, + ENEMY_TURN, + WORLD_VIEW + }; - EGameStates state; - EAdvMapMode mode; + EGameState state; /// Currently selected object, can be town, hero or null - const CArmedInstance *selection; + const CArmedInstance *currentSelection; /// currently acting player - PlayerColor player; - - bool duringAITurn; + PlayerColor currentPlayerID; /// uses EDirections enum - ui8 scrollingDir; - bool scrollingState; + bool scrollingCursorSet; const CSpell *spellBeingCasted; //nullptr if none @@ -125,7 +119,6 @@ private: void fnextHero(); void fendTurn(); - void setScrollingCursor(ui8 direction) const; void selectionChanged(); bool isActive(); void adjustActiveness(bool aiTurnStart); //should be called every time at AI/human turn transition; blocks GUI during AI turn @@ -148,9 +141,10 @@ private: int getNextHeroIndex(int startIndex); //for Next Hero button - cycles awake heroes with movement only void endingTurn(); -public: - CAdventureMapInterface(); + /// exits currently opened world view mode and returns to normal map + void exitWorldView(); +protected: // CIntObject interface implementation void activate() override; @@ -160,22 +154,25 @@ public: void showAll(SDL_Surface * to) override; void keyPressed(const SDL_Keycode & key) override; - void keyReleased(const SDL_Keycode & key) override; - void mouseMoved (const Point & cursorPosition) override; - // public interface +public: + CAdventureMapInterface(); - void startHotSeatWait(PlayerColor Player); - void startTurn(); - void initializeNewTurn(); - void aiTurnStarted(); + /// Called by PlayerInterface when specified player is ready to start his turn + void onHotseatWaitStarted(PlayerColor playerID); + + /// Called by PlayerInterface when AI or remote human player starts his turn + void onEnemyTurnStarted(PlayerColor playerID); + + /// Called by PlayerInterface when local human player starts his turn + void onPlayerTurnStarted(PlayerColor playerID); + + /// Called by PlayerInterface when interface should be switched to specified player without starting turn + void onCurrentPlayerChanged(PlayerColor playerID); /// Called by PlayerInterface when hero is forced to wake up, e.g. on moving sleeping hero void onHeroWokeUp(const CGHeroInstance * hero); - /// Called by PlayerInterface when current player changes in hotseat - void onCurrentPlayerChanged(PlayerColor Player); - /// Called by PlayerInterface when specific map tile changed and must be updated on minimap void onMapTilesChanged( boost::optional > positions); @@ -215,7 +212,8 @@ public: void onTileRightClicked(const int3 & mapPos); void enterCastingMode(const CSpell * sp); - void leaveCastingMode(bool cast = false, int3 dest = int3(-1, -1, -1)); + void leaveCastingMode(const int3 & castTarget); + void abortCastingMode(); const CGHeroInstance * getCurrentHero() const; const CGTownInstance * getCurrentTown() const; @@ -224,9 +222,6 @@ public: /// returns area of screen covered by terrain (main game area) Rect terrainAreaPixels() const; - /// exits currently opened world view mode and returns to normal map - void exitWorldView(); - /// opens world view at default scale void openWorldView(); @@ -235,7 +230,6 @@ public: /// opens world view with specific info, e.g. after View Earth/Air is shown void openWorldView(const std::vector& objectPositions, bool showTerrain); - }; extern std::shared_ptr adventureInt; diff --git a/client/adventureMap/CMinimap.cpp b/client/adventureMap/CMinimap.cpp index ce9e0e5bc..35c182e00 100644 --- a/client/adventureMap/CMinimap.cpp +++ b/client/adventureMap/CMinimap.cpp @@ -19,6 +19,7 @@ #include "../gui/CGuiHandler.h" #include "../render/Colors.h" #include "../renderSDL/SDL_PixelAccess.h" +#include "../render/Canvas.h" #include "../windows/InfoWindows.h" #include "../../CCallback.h" diff --git a/client/adventureMap/CMinimap.h b/client/adventureMap/CMinimap.h index fa596ed18..78d6d4bfa 100644 --- a/client/adventureMap/CMinimap.h +++ b/client/adventureMap/CMinimap.h @@ -10,13 +10,12 @@ #pragma once #include "../gui/CIntObject.h" -#include "../../lib/GameConstants.h" -#include "../render/Canvas.h" VCMI_LIB_NAMESPACE_BEGIN class ColorRGBA; VCMI_LIB_NAMESPACE_END +class Canvas; class CMinimap; class CMinimapInstance : public CIntObject diff --git a/client/battle/BattleStacksController.cpp b/client/battle/BattleStacksController.cpp index 8f6b2a29a..9a224e965 100644 --- a/client/battle/BattleStacksController.cpp +++ b/client/battle/BattleStacksController.cpp @@ -246,14 +246,6 @@ void BattleStacksController::setActiveStack(const CStack *stack) bool BattleStacksController::stackNeedsAmountBox(const CStack * stack) const { - BattleHex currentActionTarget; - if(owner.curInt->curAction) - { - auto target = owner.curInt->curAction->getTarget(owner.curInt->cb.get()); - if(!target.empty()) - currentActionTarget = target.at(0).hexValue; - } - //do not show box for singular war machines, stacked war machines with box shown are supported as extension feature if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON) && stack->getCount() == 1) return false;