diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 95e644e92..dbbfb09b6 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -4,6 +4,7 @@ set(client_SRCS adventureMap/CAdventureMapInterface.cpp adventureMap/CAdventureMapWidget.cpp + adventureMap/AdventureMapShortcuts.cpp adventureMap/CAdventureOptions.cpp adventureMap/CInGameConsole.cpp adventureMap/CInfoBar.cpp @@ -132,6 +133,7 @@ set(client_HEADERS adventureMap/CAdventureMapInterface.h adventureMap/CAdventureMapWidget.h + adventureMap/AdventureMapShortcuts.h adventureMap/CAdventureOptions.h adventureMap/CInGameConsole.h adventureMap/CInfoBar.h diff --git a/client/adventureMap/AdventureMapShortcuts.cpp b/client/adventureMap/AdventureMapShortcuts.cpp new file mode 100644 index 000000000..ca89415db --- /dev/null +++ b/client/adventureMap/AdventureMapShortcuts.cpp @@ -0,0 +1,351 @@ +/* + * AdventureMapShortcuts.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#include "StdInc.h" +#include "AdventureMapShortcuts.h" + +#include "../windows/CKingdomInterface.h" +#include "../windows/CSpellWindow.h" +#include "../windows/CTradeWindow.h" +#include "../lobby/CSavingScreen.h" + +#include "../gui/CGuiHandler.h" +#include "../gui/Shortcut.h" +#include "../CPlayerInterface.h" +#include "../PlayerLocalState.h" +#include "../CGameInfo.h" +#include "CAdventureMapInterface.h" +#include "CAdventureOptions.h" +#include "../windows/settings/SettingsMainWindow.h" + +#include "../../CCallback.h" +#include "../../lib/CConfigHandler.h" +#include "../../lib/CGeneralTextHandler.h" +#include "../../lib/mapObjects/CGHeroInstance.h" +#include "../../lib/mapObjects/CGTownInstance.h" +#include "../../lib/CPathfinder.h" + +AdventureMapShortcuts::AdventureMapShortcuts(CAdventureMapInterface & owner) + :owner(owner) +{} + +std::map> AdventureMapShortcuts::getFunctors() +{ + std::map> result = { + { EShortcut::ADVENTURE_KINGDOM_OVERVIEW, [this]() { this->showOverview(); } }, + { EShortcut::NONE, [this]() { this->worldViewBack(); } }, + { EShortcut::NONE, [this]() { this->worldViewScale1x(); } }, + { EShortcut::NONE, [this]() { this->worldViewScale2x(); } }, + { EShortcut::NONE, [this]() { this->worldViewScale4x(); } }, + { EShortcut::ADVENTURE_TOGGLE_MAP_LEVEL, [this]() { this->switchMapLevel(); } }, + { EShortcut::ADVENTURE_QUEST_LOG, [this]() { this->showQuestlog(); } }, + { EShortcut::ADVENTURE_TOGGLE_SLEEP, [this]() { this->toggleSleepWake(); } }, + { EShortcut::ADVENTURE_SET_HERO_ASLEEP, [this]() { this->setHeroSleeping(); } }, + { EShortcut::ADVENTURE_SET_HERO_AWAKE, [this]() { this->setHeroAwake(); } }, + { EShortcut::ADVENTURE_MOVE_HERO, [this]() { this->moveHeroAlongPath(); } }, + { EShortcut::ADVENTURE_CAST_SPELL, [this]() { this->showSpellbook(); } }, + { EShortcut::ADVENTURE_GAME_OPTIONS, [this]() { this->adventureOptions(); } }, + { EShortcut::GLOBAL_OPTIONS, [this]() { this->systemOptions(); } }, + { EShortcut::ADVENTURE_NEXT_HERO, [this]() { this->nextHero(); } }, + { EShortcut::GAME_END_TURN, [this]() { this->endTurn(); } }, + { EShortcut::ADVENTURE_THIEVES_GUILD, [this]() { this->showThievesGuild(); } }, + { EShortcut::ADVENTURE_VIEW_SCENARIO, [this]() { this->showScenarioInfo(); } }, + { EShortcut::GAME_SAVE_GAME, [this]() { this->saveGame(); } }, + { EShortcut::GAME_LOAD_GAME, [this]() { this->loadGame(); } }, + { EShortcut::ADVENTURE_DIG_GRAIL, [this]() { this->digGrail(); } }, + { EShortcut::ADVENTURE_VIEW_PUZZLE, [this]() { this->viewPuzzleMap(); } }, + { EShortcut::ADVENTURE_VIEW_WORLD, [this]() { this->viewWorldMap(); } }, + { EShortcut::GAME_RESTART_GAME, [this]() { this->restartGame(); } }, + { EShortcut::ADVENTURE_VISIT_OBJECT, [this]() { this->visitObject(); } }, + { EShortcut::ADVENTURE_VIEW_SELECTED, [this]() { this->openObject(); } }, + { EShortcut::GLOBAL_CANCEL, [this]() { this->abortSpellcasting(); } }, + { EShortcut::GAME_OPEN_MARKETPLACE, [this]() { this->showMarketplace(); } }, + { EShortcut::ADVENTURE_NEXT_TOWN, [this]() { this->nextTown(); } }, +// { EShortcut::ADVENTURE_NEXT_OBJECT, [this]() { this->nextObject(); } }, + { EShortcut::ADVENTURE_MOVE_HERO_SW, [this]() { this->moveHeroDirectional({-1, +1}); } }, + { EShortcut::ADVENTURE_MOVE_HERO_SS, [this]() { this->moveHeroDirectional({ 0, +1}); } }, + { EShortcut::ADVENTURE_MOVE_HERO_SE, [this]() { this->moveHeroDirectional({+1, +1}); } }, + { EShortcut::ADVENTURE_MOVE_HERO_WW, [this]() { this->moveHeroDirectional({-1, 0}); } }, + { EShortcut::ADVENTURE_MOVE_HERO_EE, [this]() { this->moveHeroDirectional({+1, 0}); } }, + { EShortcut::ADVENTURE_MOVE_HERO_NW, [this]() { this->moveHeroDirectional({-1, -1}); } }, + { EShortcut::ADVENTURE_MOVE_HERO_NN, [this]() { this->moveHeroDirectional({ 0, -1}); } }, + { EShortcut::ADVENTURE_MOVE_HERO_NE, [this]() { this->moveHeroDirectional({+1, -1}); } }, + }; + + return result; +} + +void AdventureMapShortcuts::showOverview() +{ + GH.pushIntT(); +} + +void AdventureMapShortcuts::worldViewBack() +{ + owner.hotkeyExitWorldView(); + + auto hero = LOCPLINT->localState->getCurrentHero(); + if (hero) + owner.centerOnObject(hero); +} + +void AdventureMapShortcuts::worldViewScale1x() +{ + // TODO set corresponding scale button to "selected" mode + owner.openWorldView(7); +} + +void AdventureMapShortcuts::worldViewScale2x() +{ + owner.openWorldView(11); +} + +void AdventureMapShortcuts::worldViewScale4x() +{ + owner.openWorldView(16); +} + +void AdventureMapShortcuts::switchMapLevel() +{ + // with support for future multi-level maps :) + int maxLevels = LOCPLINT->cb->getMapSize().z; + if (maxLevels < 2) + return; + + owner.hotkeySwitchMapLevel(); +} + +void AdventureMapShortcuts::showQuestlog() +{ + LOCPLINT->showQuestLog(); +} + +void AdventureMapShortcuts::toggleSleepWake() +{ + const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); + if (!h) + return; + bool newSleep = !LOCPLINT->localState->isHeroSleeping(h); + + if (newSleep) + LOCPLINT->localState->setHeroAsleep(h); + else + LOCPLINT->localState->setHeroAwaken(h); + + owner.onHeroChanged(h); + + if (newSleep) + nextHero(); +} + +void AdventureMapShortcuts::setHeroSleeping() +{ + const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); + if (h) + { + LOCPLINT->localState->setHeroAsleep(h); + owner.onHeroChanged(h); + nextHero(); + } +} + +void AdventureMapShortcuts::setHeroAwake() +{ + const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); + if (h) + { + LOCPLINT->localState->setHeroAsleep(h); + owner.onHeroChanged(h); + } +} + +void AdventureMapShortcuts::moveHeroAlongPath() +{ + const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); + if (!h || !LOCPLINT->localState->hasPath(h)) + return; + + LOCPLINT->moveHero(h, LOCPLINT->localState->getPath(h)); +} + +void AdventureMapShortcuts::showSpellbook() +{ + if (!LOCPLINT->localState->getCurrentHero()) //checking necessary values + return; + + owner.centerOnObject(LOCPLINT->localState->getCurrentHero()); + + GH.pushIntT(LOCPLINT->localState->getCurrentHero(), LOCPLINT, false); +} + +void AdventureMapShortcuts::adventureOptions() +{ + GH.pushIntT(); +} + +void AdventureMapShortcuts::systemOptions() +{ + GH.pushIntT(); +} + +void AdventureMapShortcuts::nextHero() +{ + const auto * currHero = LOCPLINT->localState->getCurrentHero(); + const auto * nextHero = LOCPLINT->localState->getNextWanderingHero(currHero); + + if (nextHero) + { + LOCPLINT->localState->setSelection(nextHero); + owner.centerOnObject(nextHero); + } +} + +void AdventureMapShortcuts::endTurn() +{ + if(!LOCPLINT->makingTurn) + return; + + if(settings["adventure"]["heroReminder"].Bool()) + { + for(auto hero : LOCPLINT->localState->getWanderingHeroes()) + { + if(!LOCPLINT->localState->isHeroSleeping(hero) && hero->movement > 0) + { + // Only show hero reminder if conditions met: + // - There still movement points + // - Hero don't have a path or there not points for first step on path + LOCPLINT->localState->verifyPath(hero); + + if(!LOCPLINT->localState->hasPath(hero)) + { + LOCPLINT->showYesNoDialog( CGI->generaltexth->allTexts[55], [this](){ owner.hotkeyEndingTurn(); }, nullptr); + return; + } + + auto path = LOCPLINT->localState->getPath(hero); + if (path.nodes.size() < 2 || path.nodes[path.nodes.size() - 2].turns) + { + LOCPLINT->showYesNoDialog( CGI->generaltexth->allTexts[55], [this](){ owner.hotkeyEndingTurn(); }, nullptr); + return; + } + } + } + } + owner.hotkeyEndingTurn(); +} + +void AdventureMapShortcuts::showThievesGuild() +{ + //find first town with tavern + auto itr = range::find_if(LOCPLINT->localState->getOwnedTowns(), [](const CGTownInstance * town) + { + return town->hasBuilt(BuildingID::TAVERN); + }); + + if(itr != LOCPLINT->localState->getOwnedTowns().end()) + LOCPLINT->showThievesGuildWindow(*itr); + else + LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithTavern")); +} + +void AdventureMapShortcuts::showScenarioInfo() +{ + CAdventureOptions::showScenarioInfo(); +} + +void AdventureMapShortcuts::saveGame() +{ + GH.pushIntT(); +} + +void AdventureMapShortcuts::loadGame() +{ + LOCPLINT->proposeLoadingGame(); +} + +void AdventureMapShortcuts::digGrail() +{ + const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); + + if(h && LOCPLINT->makingTurn) + LOCPLINT->tryDiggging(h); + return; +} + +void AdventureMapShortcuts::viewPuzzleMap() +{ + LOCPLINT->showPuzzleMap(); +} + +void AdventureMapShortcuts::viewWorldMap() +{ + LOCPLINT->viewWorldMap(); +} + +void AdventureMapShortcuts::restartGame() +{ + LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.adventureMap.confirmRestartGame"), + [](){ GH.pushUserEvent(EUserEvent::RESTART_GAME); }, nullptr); +} + +void AdventureMapShortcuts::visitObject() +{ + const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); + + if(h) + LOCPLINT->cb->moveHero(h,h->pos); +} + +void AdventureMapShortcuts::openObject() +{ + const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); + const CGTownInstance *t = LOCPLINT->localState->getCurrentTown(); + if(h) + LOCPLINT->openHeroWindow(h); + + if(t) + LOCPLINT->openTownWindow(t); + + return; +} + +void AdventureMapShortcuts::abortSpellcasting() +{ + owner.hotkeyAbortCastingMode(); +} + +void AdventureMapShortcuts::showMarketplace() +{ + //check if we have any marketplace + const CGTownInstance *townWithMarket = nullptr; + for(const CGTownInstance *t : LOCPLINT->cb->getTownsInfo()) + { + if(t->hasBuilt(BuildingID::MARKETPLACE)) + { + townWithMarket = t; + break; + } + } + + if(townWithMarket) //if any town has marketplace, open window + GH.pushIntT(townWithMarket); + else //if not - complain + LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket")); +} + +void AdventureMapShortcuts::nextTown() +{ + owner.hotkeyEndingTurn(); +} + +void AdventureMapShortcuts::moveHeroDirectional(const Point & direction) +{ + owner.hotkeyMoveHeroDirectional(direction); +} diff --git a/client/adventureMap/AdventureMapShortcuts.h b/client/adventureMap/AdventureMapShortcuts.h new file mode 100644 index 000000000..d2f2761ea --- /dev/null +++ b/client/adventureMap/AdventureMapShortcuts.h @@ -0,0 +1,61 @@ +/* + * AdventureMapShortcuts.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ + +#pragma once + +VCMI_LIB_NAMESPACE_BEGIN +class Point; +VCMI_LIB_NAMESPACE_END + +enum class EShortcut; +class CAdventureMapInterface; + +/// Class that contains list of functions for shortcuts available from adventure map +class AdventureMapShortcuts +{ + CAdventureMapInterface & owner; + + void showOverview(); + void worldViewBack(); + void worldViewScale1x(); + void worldViewScale2x(); + void worldViewScale4x(); + void switchMapLevel(); + void showQuestlog(); + void toggleSleepWake(); + void setHeroSleeping(); + void setHeroAwake(); + void moveHeroAlongPath(); + void showSpellbook(); + void adventureOptions(); + void systemOptions(); + void nextHero(); + void endTurn(); + void showThievesGuild(); + void showScenarioInfo(); + void saveGame(); + void loadGame(); + void digGrail(); + void viewPuzzleMap(); + void viewWorldMap(); + void restartGame(); + void visitObject(); + void openObject(); + void abortSpellcasting(); + void showMarketplace(); + void nextTown(); + void nextObject(); + void moveHeroDirectional(const Point & direction); + +public: + explicit AdventureMapShortcuts(CAdventureMapInterface & owner); + + std::map> getFunctors(); +}; diff --git a/client/adventureMap/CAdventureMapInterface.cpp b/client/adventureMap/CAdventureMapInterface.cpp index 519f9d25b..4d3b728e5 100644 --- a/client/adventureMap/CAdventureMapInterface.cpp +++ b/client/adventureMap/CAdventureMapInterface.cpp @@ -17,35 +17,23 @@ #include "CInfoBar.h" #include "MapAudioPlayer.h" #include "CAdventureMapWidget.h" +#include "AdventureMapShortcuts.h" #include "../mapView/mapHandler.h" #include "../mapView/MapView.h" -#include "../windows/CKingdomInterface.h" -#include "../windows/CSpellWindow.h" -#include "../windows/CTradeWindow.h" -#include "../windows/GUIClasses.h" #include "../windows/InfoWindows.h" #include "../CGameInfo.h" -#include "../CPlayerInterface.h" -#include "../lobby/CSavingScreen.h" -#include "../render/CAnimation.h" #include "../gui/CursorHandler.h" -#include "../render/IImage.h" -#include "../renderSDL/SDL_Extensions.h" #include "../gui/CGuiHandler.h" -#include "../gui/Shortcut.h" -#include "../widgets/TextControls.h" -#include "../widgets/Buttons.h" -#include "../windows/settings/SettingsMainWindow.h" #include "../CMT.h" #include "../PlayerLocalState.h" +#include "../CPlayerInterface.h" #include "../../CCallback.h" #include "../../lib/CConfigHandler.h" #include "../../lib/CGeneralTextHandler.h" #include "../../lib/spells/CSpellHandler.h" #include "../../lib/mapObjects/CGHeroInstance.h" -#include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/CPathfinder.h" #include "../../lib/mapping/CMap.h" @@ -62,53 +50,16 @@ CAdventureMapInterface::CAdventureMapInterface(): pos.h = GH.screenDimensions().y; strongInterest = true; // handle all mouse move events to prevent dead mouse move space in fullscreen mode - widget = std::make_shared(); - exitWorldView(); + shortcuts = std::make_shared(*this); + + widget = std::make_shared(shortcuts); + widget->setState(EGameState::MAKING_TURN); + widget->getMapView()->onViewMapActivated(); widget->setOptionHasQuests(!CGI->mh->getMap()->quests.empty()); widget->setOptionHasUnderground(CGI->mh->getMap()->twoLevel); } -void CAdventureMapInterface::fshowOverview() -{ - GH.pushIntT(); -} - -void CAdventureMapInterface::fworldViewBack() -{ - exitWorldView(); - - auto hero = LOCPLINT->localState->getCurrentHero(); - if (hero) - centerOnObject(hero); -} - -void CAdventureMapInterface::fworldViewScale1x() -{ - // TODO set corresponding scale button to "selected" mode - openWorldView(7); -} - -void CAdventureMapInterface::fworldViewScale2x() -{ - openWorldView(11); -} - -void CAdventureMapInterface::fworldViewScale4x() -{ - openWorldView(16); -} - -void CAdventureMapInterface::fswitchLevel() -{ - // with support for future multi-level maps :) - int maxLevels = CGI->mh->getMap()->levels(); - if (maxLevels < 2) - return; - - widget->getMapView()->onMapLevelSwitched(); -} - void CAdventureMapInterface::onMapViewMoved(const Rect & visibleArea, int mapLevel) { widget->setOptionUndergroundLevel(mapLevel > 0); @@ -125,104 +76,6 @@ void CAdventureMapInterface::onAudioPaused() mapAudio->onAudioPaused(); } -void CAdventureMapInterface::fshowQuestlog() -{ - LOCPLINT->showQuestLog(); -} - -void CAdventureMapInterface::fsleepWake() -{ - const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); - if (!h) - return; - bool newSleep = !LOCPLINT->localState->isHeroSleeping(h); - - if (newSleep) - LOCPLINT->localState->setHeroAsleep(h); - else - LOCPLINT->localState->setHeroAwaken(h); - - onHeroChanged(h); - - if (newSleep) - fnextHero(); -} - -void CAdventureMapInterface::fmoveHero() -{ - const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); - if (!h || !LOCPLINT->localState->hasPath(h) || CGI->mh->hasOngoingAnimations()) - return; - - LOCPLINT->moveHero(h, LOCPLINT->localState->getPath(h)); -} - -void CAdventureMapInterface::fshowSpellbok() -{ - if (!LOCPLINT->localState->getCurrentHero()) //checking necessary values - return; - - centerOnObject(LOCPLINT->localState->getCurrentHero()); - - GH.pushIntT(LOCPLINT->localState->getCurrentHero(), LOCPLINT, false); -} - -void CAdventureMapInterface::fadventureOPtions() -{ - GH.pushIntT(); -} - -void CAdventureMapInterface::fsystemOptions() -{ - GH.pushIntT(); -} - -void CAdventureMapInterface::fnextHero() -{ - const auto * currHero = LOCPLINT->localState->getCurrentHero(); - const auto * nextHero = LOCPLINT->localState->getNextWanderingHero(currHero); - - if (nextHero) - { - LOCPLINT->localState->setSelection(nextHero); - centerOnObject(nextHero); - } -} - -void CAdventureMapInterface::fendTurn() -{ - if(!LOCPLINT->makingTurn) - return; - - if(settings["adventure"]["heroReminder"].Bool()) - { - for(auto hero : LOCPLINT->localState->getWanderingHeroes()) - { - if(!LOCPLINT->localState->isHeroSleeping(hero) && hero->movement > 0) - { - // Only show hero reminder if conditions met: - // - There still movement points - // - Hero don't have a path or there not points for first step on path - LOCPLINT->localState->verifyPath(hero); - - if(!LOCPLINT->localState->hasPath(hero)) - { - LOCPLINT->showYesNoDialog( CGI->generaltexth->allTexts[55], std::bind(&CAdventureMapInterface::endingTurn, this), nullptr ); - return; - } - - auto path = LOCPLINT->localState->getPath(hero); - if (path.nodes.size() < 2 || path.nodes[path.nodes.size() - 2].turns) - { - LOCPLINT->showYesNoDialog( CGI->generaltexth->allTexts[55], std::bind(&CAdventureMapInterface::endingTurn, this), nullptr ); - return; - } - } - } - } - endingTurn(); -} - void CAdventureMapInterface::updateButtons() { const auto * hero = LOCPLINT->localState->getCurrentHero(); @@ -376,136 +229,8 @@ void CAdventureMapInterface::centerOnObject(const CGObjectInstance * obj) void CAdventureMapInterface::keyPressed(EShortcut key) { - if (widget->getState() != EGameState::MAKING_TURN) - return; - //fake mouse use to trigger onTileHovered() GH.fakeMouseMove(); - - const CGHeroInstance *h = LOCPLINT->localState->getCurrentHero(); //selected hero - const CGTownInstance *t = LOCPLINT->localState->getCurrentTown(); //selected town - - switch(key) - { - case EShortcut::ADVENTURE_THIEVES_GUILD: - if(GH.topInt()->type & BLOCK_ADV_HOTKEYS) - return; - - { - //find first town with tavern - auto itr = range::find_if(LOCPLINT->localState->getOwnedTowns(), [](const CGTownInstance * town) - { - return town->hasBuilt(BuildingID::TAVERN); - }); - - if(itr != LOCPLINT->localState->getOwnedTowns().end()) - LOCPLINT->showThievesGuildWindow(*itr); - else - LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithTavern")); - } - return; - case EShortcut::ADVENTURE_VIEW_SCENARIO: - if(isActive()) - CAdventureOptions::showScenarioInfo(); - return; - case EShortcut::GAME_SAVE_GAME: - if(isActive()) - GH.pushIntT(); - return; - case EShortcut::GAME_LOAD_GAME: - if(isActive()) - LOCPLINT->proposeLoadingGame(); - return; - case EShortcut::ADVENTURE_DIG_GRAIL: - { - if(h && isActive() && LOCPLINT->makingTurn) - LOCPLINT->tryDiggging(h); - return; - } - case EShortcut::ADVENTURE_VIEW_PUZZLE: - if(isActive()) - LOCPLINT->showPuzzleMap(); - return; - case EShortcut::ADVENTURE_VIEW_WORLD: - if(isActive()) - LOCPLINT->viewWorldMap(); - return; - case EShortcut::GAME_RESTART_GAME: - if(isActive() && GH.isKeyboardCtrlDown()) - { - LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.adventureMap.confirmRestartGame"), - [](){ GH.pushUserEvent(EUserEvent::RESTART_GAME); }, nullptr); - } - return; - case EShortcut::ADVENTURE_VISIT_OBJECT: //space - try to revisit current object with selected hero - { - if(!isActive()) - return; - if(h) - { - LOCPLINT->cb->moveHero(h,h->pos); - } - } - return; - case EShortcut::ADVENTURE_VIEW_SELECTED: - { - if(!isActive() || !LOCPLINT->localState->getCurrentArmy()) - return; - if(h) - LOCPLINT->openHeroWindow(h); - else if(t) - LOCPLINT->openTownWindow(t); - return; - } - case EShortcut::GLOBAL_CANCEL: - { - //FIXME: this case is never executed since AdvMapInt is disabled while in spellcasting mode - if(!isActive() || GH.topInt().get() != this || !spellBeingCasted) - return; - - abortCastingMode(); - return; - } - case EShortcut::GAME_OPEN_MARKETPLACE: - { - //act on key down if marketplace windows is not already opened - if(GH.topInt()->type & BLOCK_ADV_HOTKEYS) - return; - - if(GH.isKeyboardCtrlDown()) //CTRL + T => open marketplace - { - //check if we have any marketplace - const CGTownInstance *townWithMarket = nullptr; - for(const CGTownInstance *t : LOCPLINT->cb->getTownsInfo()) - { - if(t->hasBuilt(BuildingID::MARKETPLACE)) - { - townWithMarket = t; - break; - } - } - - if(townWithMarket) //if any town has marketplace, open window - GH.pushIntT(townWithMarket); - else //if not - complain - LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket")); - } - case EShortcut::ADVENTURE_NEXT_TOWN: - if(isActive() && !GH.isKeyboardCtrlDown()) //no ctrl, advmapint is on the top => switch to town - { - widget->getTownList()->selectNext(); - } - return; - } - case EShortcut::ADVENTURE_MOVE_HERO_SW: return hotkeyMoveHeroDirectional({-1, +1}); - case EShortcut::ADVENTURE_MOVE_HERO_SS: return hotkeyMoveHeroDirectional({ 0, +1}); - case EShortcut::ADVENTURE_MOVE_HERO_SE: return hotkeyMoveHeroDirectional({+1, +1}); - case EShortcut::ADVENTURE_MOVE_HERO_WW: return hotkeyMoveHeroDirectional({-1, 0}); - case EShortcut::ADVENTURE_MOVE_HERO_EE: return hotkeyMoveHeroDirectional({+1, 0}); - case EShortcut::ADVENTURE_MOVE_HERO_NW: return hotkeyMoveHeroDirectional({-1, -1}); - case EShortcut::ADVENTURE_MOVE_HERO_NN: return hotkeyMoveHeroDirectional({ 0, -1}); - case EShortcut::ADVENTURE_MOVE_HERO_NE: return hotkeyMoveHeroDirectional({+1, -1}); - } } void CAdventureMapInterface::hotkeyMoveHeroDirectional(Point direction) @@ -690,11 +415,11 @@ void CAdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID) if(CInfoWindow *iw = dynamic_cast(GH.topInt().get())) iw->close(); - endingTurn(); + hotkeyEndingTurn(); } } -void CAdventureMapInterface::endingTurn() +void CAdventureMapInterface::hotkeyEndingTurn() { if(settings["session"]["spectate"].Bool()) return; @@ -712,11 +437,6 @@ const CGObjectInstance* CAdventureMapInterface::getActiveObject(const int3 &mapP return nullptr; return *boost::range::max_element(bobjs, &CMapHandler::compareObjectBlitOrder); -/* - if (bobjs.back()->ID == Obj::HERO) - return bobjs.back(); - else - return bobjs.front();*/ } void CAdventureMapInterface::onTileLeftClicked(const int3 &mapPos) @@ -984,7 +704,7 @@ void CAdventureMapInterface::onTileRightClicked(const int3 &mapPos) if(spellBeingCasted) { - abortCastingMode(); + hotkeyAbortCastingMode(); return; } @@ -1030,7 +750,7 @@ void CAdventureMapInterface::exitCastingMode() config->Bool() = false; } -void CAdventureMapInterface::abortCastingMode() +void CAdventureMapInterface::hotkeyAbortCastingMode() { exitCastingMode(); LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[731]); //Spell cancelled @@ -1060,7 +780,7 @@ const IShipyard * CAdventureMapInterface::ourInaccessibleShipyard(const CGObject return ret; } -void CAdventureMapInterface::exitWorldView() +void CAdventureMapInterface::hotkeyExitWorldView() { widget->setState(EGameState::MAKING_TURN); widget->getMapView()->onViewMapActivated(); @@ -1082,3 +802,13 @@ void CAdventureMapInterface::openWorldView(const std::vector& obj openWorldView(11); widget->getMapView()->onViewSpellActivated(11, objectPositions, showTerrain); } + +void CAdventureMapInterface::hotkeyNextTown() +{ + widget->getTownList()->selectNext(); +} + +void CAdventureMapInterface::hotkeySwitchMapLevel() +{ + widget->getMapView()->onMapLevelSwitched(); +} diff --git a/client/adventureMap/CAdventureMapInterface.h b/client/adventureMap/CAdventureMapInterface.h index 4d9615b90..6e92fbc6e 100644 --- a/client/adventureMap/CAdventureMapInterface.h +++ b/client/adventureMap/CAdventureMapInterface.h @@ -30,8 +30,7 @@ class IImage; class CAnimImage; class CGStatusBar; class CAdventureMapWidget; -class CAdvMapPanel; -class CAdvMapWorldViewPanel; +class AdventureMapShortcuts; class CAnimation; class MapView; class CResDataBar; @@ -59,26 +58,9 @@ private: std::shared_ptr mapAudio; std::shared_ptr widget; + std::shared_ptr shortcuts; private: - //functions bound to buttons - void fshowOverview(); - void fworldViewBack(); - void fworldViewScale1x(); - void fworldViewScale2x(); - void fworldViewScale4x(); - void fswitchLevel(); - void fshowQuestlog(); - void fsleepWake(); - void fmoveHero(); - void fshowSpellbok(); - void fadventureOPtions(); - void fsystemOptions(); - void fnextHero(); - void fendTurn(); - - void hotkeyMoveHeroDirectional(Point direction); - bool isActive(); void adjustActiveness(bool aiTurnStart); //should be called every time at AI/human turn transition; blocks GUI during AI turn @@ -93,15 +75,9 @@ private: const CGObjectInstance *getActiveObject(const int3 &tile); - std::optional keyToMoveDirection(EShortcut key); - - void endingTurn(); - /// exits currently opened world view mode and returns to normal map - void exitWorldView(); void exitCastingMode(); void performSpellcasting(const int3 & castTarget); - void abortCastingMode(); protected: // CIntObject interface implementation @@ -117,6 +93,13 @@ protected: public: CAdventureMapInterface(); + void hotkeyMoveHeroDirectional(Point direction); + void hotkeyAbortCastingMode(); + void hotkeyExitWorldView(); + void hotkeyEndingTurn(); + void hotkeyNextTown(); + void hotkeySwitchMapLevel(); + /// Called by PlayerInterface when specified player is ready to start his turn void onHotseatWaitStarted(PlayerColor playerID); diff --git a/client/adventureMap/CAdventureMapWidget.cpp b/client/adventureMap/CAdventureMapWidget.cpp index 6d12b48ec..9894e1a3c 100644 --- a/client/adventureMap/CAdventureMapWidget.cpp +++ b/client/adventureMap/CAdventureMapWidget.cpp @@ -10,6 +10,7 @@ #include "StdInc.h" #include "CAdventureMapWidget.h" +#include "AdventureMapShortcuts.h" #include "CInfoBar.h" #include "CList.h" #include "CMinimap.h" @@ -29,8 +30,9 @@ #include "../../lib/StringConstants.h" #include "../../lib/filesystem/ResourceID.h" -CAdventureMapWidget::CAdventureMapWidget() +CAdventureMapWidget::CAdventureMapWidget( std::shared_ptr shortcuts ) : state(EGameState::NOT_INITIALIZED) + , shortcuts(shortcuts) { pos.x = pos.y = 0; pos.w = GH.screenDimensions().x; @@ -48,6 +50,9 @@ CAdventureMapWidget::CAdventureMapWidget() REGISTER_BUILDER("adventureResourceDateBar", &CAdventureMapWidget::buildResourceDateBar ); REGISTER_BUILDER("adventureStatusBar", &CAdventureMapWidget::buildStatusBar ); + for (const auto & entry : shortcuts->getFunctors()) + addShortcut(entry.first, entry.second); + const JsonNode config(ResourceID("config/widgets/adventureMap.json")); for(const auto & entry : config["options"]["imagesPlayerColored"].Vector()) @@ -57,6 +62,8 @@ CAdventureMapWidget::CAdventureMapWidget() } build(config); + + addUsedEvents(KEYBOARD); } Rect CAdventureMapWidget::readSourceArea(const JsonNode & source, const JsonNode & sourceCommon) @@ -151,7 +158,12 @@ std::shared_ptr CAdventureMapWidget::buildMapButton(const JsonNode & auto position = readTargetArea(input["area"]); auto image = input["image"].String(); auto help = readHintText(input["help"]); - return std::make_shared(position.topLeft(), image, help); + + auto button = std::make_shared(position.topLeft(), image, help); + + loadButtonHotkey(button, input["hotkey"]); + + return button; } std::shared_ptr CAdventureMapWidget::buildMapContainer(const JsonNode & input) diff --git a/client/adventureMap/CAdventureMapWidget.h b/client/adventureMap/CAdventureMapWidget.h index c13bd9d8b..2ed667dba 100644 --- a/client/adventureMap/CAdventureMapWidget.h +++ b/client/adventureMap/CAdventureMapWidget.h @@ -17,6 +17,7 @@ class CMinimap; class MapView; class CInfoBar; class IImage; +class AdventureMapShortcuts; enum class EGameState { @@ -49,6 +50,8 @@ class CAdventureMapWidget : public InterfaceObjectConfigurable std::shared_ptr mapView; std::shared_ptr infoBar; + std::shared_ptr shortcuts; + Rect readTargetArea(const JsonNode & source); Rect readSourceArea(const JsonNode & source, const JsonNode & sourceCommon); Rect readArea(const JsonNode & source, const Rect & boundingBox); @@ -70,7 +73,7 @@ class CAdventureMapWidget : public InterfaceObjectConfigurable void setPlayerChildren(CIntObject * widget, const PlayerColor & player); public: - CAdventureMapWidget(); + explicit CAdventureMapWidget( std::shared_ptr shortcuts ); std::shared_ptr getHeroList(); std::shared_ptr getTownList(); diff --git a/client/gui/InterfaceObjectConfigurable.cpp b/client/gui/InterfaceObjectConfigurable.cpp index b012ed0fb..32c523182 100644 --- a/client/gui/InterfaceObjectConfigurable.cpp +++ b/client/gui/InterfaceObjectConfigurable.cpp @@ -289,15 +289,7 @@ std::shared_ptr InterfaceObjectConfigurable::buildToggleButton(co assert(imgOrder.size() >= 4); button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer()); } - if(!config["callback"].isNull()) - { - std::string callbackName = config["callback"].String(); - - if (callbacks.count(callbackName)) - button->addCallback(callbacks.at(callbackName)); - else - logGlobal->error("Invalid callback '%s' in widget", callbackName ); - } + loadButtonCallback(button, config["callback"]); return button; } @@ -321,32 +313,46 @@ std::shared_ptr InterfaceObjectConfigurable::buildButton(const JsonNode assert(imgOrder.size() >= 4); button->setImageOrder(imgOrder[0].Integer(), imgOrder[1].Integer(), imgOrder[2].Integer(), imgOrder[3].Integer()); } - if(!config["callback"].isNull()) - { - std::string callbackName = config["callback"].String(); - if (callbacks.count(callbackName) > 0) - button->addCallback(std::bind(callbacks.at(callbackName), 0)); - else - logGlobal->error("Invalid callback '%s' in widget", callbackName ); - } - if(!config["hotkey"].isNull()) - { - if(config["hotkey"].getType() == JsonNode::JsonType::DATA_STRING) - { - button->assignedKey = readHotkey(config["hotkey"]); - - auto target = shortcuts.find(button->assignedKey); - if (target != shortcuts.end()) - { - button->addCallback(target->second.callback); - target->second.assignedToButton = true; - } - } - } + loadButtonCallback(button, config["callback"]); + loadButtonHotkey(button, config["hotkey"]); return button; } +void InterfaceObjectConfigurable::loadButtonCallback(std::shared_ptr button, const JsonNode & config) const +{ + if(config.isNull()) + return; + + std::string callbackName = config.String(); + + if (callbacks.count(callbackName) > 0) + button->addCallback(std::bind(callbacks.at(callbackName), 0)); + else + logGlobal->error("Invalid callback '%s' in widget", callbackName ); +} + +void InterfaceObjectConfigurable::loadButtonHotkey(std::shared_ptr button, const JsonNode & config) const +{ + if(config.isNull()) + return; + + if(config.getType() != JsonNode::JsonType::DATA_STRING) + { + logGlobal->error("Invalid shortcut format - string expected!"); + return; + } + + button->assignedKey = readHotkey(config); + + auto target = shortcuts.find(button->assignedKey); + if (target == shortcuts.end()) + return; + + button->addCallback(target->second.callback); + target->second.assignedToButton = true; +} + std::shared_ptr InterfaceObjectConfigurable::buildLabelGroup(const JsonNode & config) const { logGlobal->debug("Building widget CLabelGroup"); diff --git a/client/gui/InterfaceObjectConfigurable.h b/client/gui/InterfaceObjectConfigurable.h index 56ddcd19b..7ba3d1100 100644 --- a/client/gui/InterfaceObjectConfigurable.h +++ b/client/gui/InterfaceObjectConfigurable.h @@ -73,6 +73,9 @@ protected: std::pair readHintText(const JsonNode &) const; EShortcut readHotkey(const JsonNode &) const; + void loadButtonCallback(std::shared_ptr button, const JsonNode & config) const; + void loadButtonHotkey(std::shared_ptr button, const JsonNode & config) const; + //basic widgets std::shared_ptr buildPicture(const JsonNode &) const; std::shared_ptr buildLabel(const JsonNode &) const; diff --git a/client/gui/Shortcut.h b/client/gui/Shortcut.h index 956c6ce88..927906111 100644 --- a/client/gui/Shortcut.h +++ b/client/gui/Shortcut.h @@ -81,7 +81,9 @@ enum class EShortcut // Adventure map screen ADVENTURE_GAME_OPTIONS, // 'o', Open CAdventureOptions window ADVENTURE_TOGGLE_GRID, // F6, Toggles map grid - ADVENTURE_TOGGLE_SLEEP, // z,w, Toggles hero sleep status + ADVENTURE_TOGGLE_SLEEP, // Toggles hero sleep status + ADVENTURE_SET_HERO_ASLEEP, // Moves hero to sleep state + ADVENTURE_SET_HERO_AWAKE, // Move hero to awake state ADVENTURE_MOVE_HERO, // Moves hero alongside set path ADVENTURE_VISIT_OBJECT, // Revisits object hero is standing on ADVENTURE_VIEW_SELECTED,// Open window with currently selected hero/town @@ -98,7 +100,6 @@ enum class EShortcut ADVENTURE_KINGDOM_OVERVIEW, ADVENTURE_QUEST_LOG, ADVENTURE_CAST_SPELL, - ADVENTURE_END_TURN, ADVENTURE_THIEVES_GUILD, // Move hero one tile in specified direction. Bound to cursors & numpad buttons @@ -153,4 +154,3 @@ enum class EShortcut AFTER_LAST }; - diff --git a/client/gui/ShortcutHandler.cpp b/client/gui/ShortcutHandler.cpp index dfd30cb84..80a5b2d72 100644 --- a/client/gui/ShortcutHandler.cpp +++ b/client/gui/ShortcutHandler.cpp @@ -79,8 +79,8 @@ std::vector ShortcutHandler::translateKeycode(SDL_Keycode key) const {SDLK_TAB, EShortcut::GAME_ACTIVATE_CONSOLE }, {SDLK_o, EShortcut::ADVENTURE_GAME_OPTIONS }, {SDLK_F6, EShortcut::ADVENTURE_TOGGLE_GRID }, - {SDLK_z, EShortcut::ADVENTURE_TOGGLE_SLEEP }, - {SDLK_w, EShortcut::ADVENTURE_TOGGLE_SLEEP }, + {SDLK_z, EShortcut::ADVENTURE_SET_HERO_ASLEEP }, + {SDLK_w, EShortcut::ADVENTURE_SET_HERO_AWAKE }, {SDLK_m, EShortcut::ADVENTURE_MOVE_HERO }, {SDLK_SPACE, EShortcut::ADVENTURE_VISIT_OBJECT }, {SDLK_KP_1, EShortcut::ADVENTURE_MOVE_HERO_SW }, @@ -110,7 +110,6 @@ std::vector ShortcutHandler::translateKeycode(SDL_Keycode key) const {SDLK_k, EShortcut::ADVENTURE_KINGDOM_OVERVIEW}, {SDLK_q, EShortcut::ADVENTURE_QUEST_LOG }, {SDLK_c, EShortcut::ADVENTURE_CAST_SPELL }, - {SDLK_e, EShortcut::ADVENTURE_END_TURN }, {SDLK_g, EShortcut::ADVENTURE_THIEVES_GUILD }, {SDLK_q, EShortcut::BATTLE_TOGGLE_QUEUE }, {SDLK_c, EShortcut::BATTLE_USE_CREATURE_SPELL }, @@ -218,6 +217,8 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const {"adventureGameOptions", EShortcut::ADVENTURE_GAME_OPTIONS }, {"adventureToggleGrid", EShortcut::ADVENTURE_TOGGLE_GRID }, {"adventureToggleSleep", EShortcut::ADVENTURE_TOGGLE_SLEEP }, + {"adventureSetHeroAsleep", EShortcut::ADVENTURE_SET_HERO_ASLEEP }, + {"adventureSetHeroAwake", EShortcut::ADVENTURE_SET_HERO_AWAKE }, {"adventureMoveHero", EShortcut::ADVENTURE_MOVE_HERO }, {"adventureVisitObject", EShortcut::ADVENTURE_VISIT_OBJECT }, {"adventureMoveHeroSW", EShortcut::ADVENTURE_MOVE_HERO_SW }, @@ -242,7 +243,6 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const {"adventureKingdomOverview", EShortcut::ADVENTURE_KINGDOM_OVERVIEW}, {"adventureQuestLog", EShortcut::ADVENTURE_QUEST_LOG }, {"adventureCastSpell", EShortcut::ADVENTURE_CAST_SPELL }, - {"adventureEndTurn", EShortcut::ADVENTURE_END_TURN }, {"adventureThievesGuild", EShortcut::ADVENTURE_THIEVES_GUILD }, {"battleToggleQueue", EShortcut::BATTLE_TOGGLE_QUEUE }, {"battleUseCreatureSpell", EShortcut::BATTLE_USE_CREATURE_SPELL }, @@ -278,85 +278,3 @@ EShortcut ShortcutHandler::findShortcut(const std::string & identifier ) const return shortcutNames.at(identifier); return EShortcut::NONE; } - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/config/widgets/adventureMap.json b/config/widgets/adventureMap.json index 72d061ab7..c6869ba36 100644 --- a/config/widgets/adventureMap.json +++ b/config/widgets/adventureMap.json @@ -120,6 +120,7 @@ "name" : "buttonKingdomOverview", "image" : "IAM002.DEF", "help" : "core.help.293", + "hotkey": "adventureKingdomOverview", "area": { "top" : 0, "left": 0, "width" : 32, "height" : 32 } }, { @@ -127,6 +128,7 @@ "name": "buttonUnderground", "image" : "IAM010.DEF", "help" : "core.help.294", + "hotkey": "adventureToggleMapLevel", "area": { "top" : 0, "left": 32, "width" : 32, "height" : 32 } }, { @@ -134,6 +136,7 @@ "name": "buttonSurface", "image" : "IAM003.DEF", "help" : "core.help.294", + "hotkey": "adventureToggleMapLevel", "area": { "top" : 0, "left": 32, "width" : 32, "height" : 32 } }, { @@ -141,6 +144,7 @@ "name": "buttonQuestLog", "image" : "IAM004.DEF", "help" : "core.help.295", + "hotkey": "adventureQuestLog", "area": { "top" : 32, "left": 0, "width" : 32, "height" : 32 } }, { @@ -148,6 +152,7 @@ "name": "buttonSleep", "image" : "IAM005.DEF", "help" : "core.help.296", + "hotkey": "adventureSetHeroAsleep", "area": { "top" : 32, "left": 32, "width" : 32, "height" : 32 } }, { @@ -155,6 +160,7 @@ "name": "buttonWake", "image" : "IAM011.DEF", "help" : "core.help.296", + "hotkey": "adventureSetHeroAwake", "area": { "top" : 32, "left": 32, "width" : 32, "height" : 32 } }, { @@ -162,6 +168,7 @@ "name": "buttonMove", "image" : "IAM006.DEF", "help" : "core.help.297", + "hotkey": "adventureMoveHero", "area": { "top" : 64, "left": 0, "width" : 32, "height" : 32 } }, { @@ -169,6 +176,7 @@ "name": "buttonCast", "image" : "IAM007.DEF", "help" : "core.help.298", + "hotkey": "adventureCastSpell", "area": { "top" : 64, "left": 32, "width" : 32, "height" : 32 } }, { @@ -176,6 +184,7 @@ "name": "buttonAdventureOptions", "image" : "IAM008.DEF", "help" : "core.help.299", + "hotkey": "adventureGameOptions", "area": { "top" : 96, "left": 0, "width" : 32, "height" : 32 } }, { @@ -183,6 +192,7 @@ "name": "buttonSystemOptions", "image" : "IAM009.DEF", "help" : "core.help.300", + "hotkey": "globalOptions", "area": { "top" : 96, "left": 32, "width" : 32, "height" : 32 } }, { @@ -190,12 +200,14 @@ "name": "buttonNextHero", "image" : "IAM000.DEF", "help" : "core.help.301", + "hotkey": "adventureNextHero", "area": { "top" : 128, "left": 0, "width" : 64, "height" : 32 } }, { "type": "adventureMapButton", "name": "buttonEndTurn", "image" : "IAM001.DEF", + "hotkey": "adventureEndTurn", "help" : "core.help.302", "area": { "top" : 160, "left": 0, "width" : 64, "height" : 32 } } @@ -287,7 +299,7 @@ "type": "adventureMapImage", "name": "emptyAreaFillSmallImage", "image" : "DiBoxBck.pcx", - "area": { "top": 0, "bottom" : 0, "left" : 0, "right" : 0 } + "area": { "top": 0, "bottom" : 0, "left" : 0, "right" : 0 }, "sourceArea": { "left" : 0, "top" : 0, "width" : 256, "height" : 256 } }, ] @@ -303,21 +315,21 @@ "type": "adventureMapImage", "name": "backgroundHeroListBorderRight", "image" : "AdvMap.pcx", - "area": { "top": 0, "bottom" : 0, "right" : 121, "width" : 6 } + "area": { "top": 0, "bottom" : 0, "right" : 121, "width" : 6 }, "sourceArea": { "top": 196, "bottom" : 211, "right" : 121, "width" : 6 } }, { "type": "adventureMapImage", "name": "backgroundTownListBorderLeft", "image" : "AdvMap.pcx", - "area": { "top": 0, "bottom" : 0, "right" : 53, "width" : 4 } + "area": { "top": 0, "bottom" : 0, "right" : 53, "width" : 4 }, "sourceArea": { "top": 196, "bottom" : 211, "right" : 53, "width" : 4 } }, { "type": "adventureMapImage", "name" : "backgroundBelowHeroTownList", "image" : "AdvMap.pcx", - "area" : { "right": 0, "left" : 0, "bottom" : 0, "height" : 3 } + "area" : { "right": 0, "left" : 0, "bottom" : 0, "height" : 3 }, "sourceArea": { "bottom" : 208, "height" : 3, "right" : 0, "width" : 193 } }, // Hero List @@ -351,13 +363,13 @@ "item" : { "top" : 16, "left": 0, "width" : 48, "height" : 32 }, "itemsOffset" : { "x" : 0, "y" : 32 }, "itemsCount" : 7 - } + }, // Fill empty area below buttons { "type": "adventureMapImage", "name" : "backgroundBelowButtons", "image" : "DiBoxBck.pcx", - "area": { "top": 192, "bottom" : 3, "right" : 57, "width" : 64 } + "area": { "top": 192, "bottom" : 3, "right" : 57, "width" : 64 }, "sourceArea": { "left" : 0, "top" : 0, "width" : 256, "height" : 256 } }, ] @@ -372,7 +384,7 @@ "type": "adventureMapImage", "name": "emptyAreaFillLargeImage", "image" : "DiBoxBck.pcx", - "area": { "top": 0, "bottom" : 0, "left" : 0, "right" : 0 } + "area": { "top": 0, "bottom" : 0, "left" : 0, "right" : 0 }, "sourceArea": { "left" : 0, "top" : 0, "width" : 256, "height" : 256 } }, ]