diff --git a/Mods/vcmi/config/vcmi/english.json b/Mods/vcmi/config/vcmi/english.json index d2d264574..8be4b1b3f 100644 --- a/Mods/vcmi/config/vcmi/english.json +++ b/Mods/vcmi/config/vcmi/english.json @@ -87,6 +87,10 @@ "vcmi.adventureOptions.showGrid.help" : "{Show Grid}\n\nShow the grid overlay, highlighting the borders between adventure map tiles.", "vcmi.adventureOptions.borderScroll.hover" : "Border Scrolling", "vcmi.adventureOptions.borderScroll.help" : "{Border Scrolling}\n\nScroll adventure map when cursor is adjacent to window edge. Can be disabled by holding down CTRL key.", + "vcmi.adventureOptions.infoBarCreatureManagement.hover" : "Info Panel Creature Management", + "vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Info Panel Creature Management}\n\nAllows rearranging creatures in info panel instead of cycling between default components", + "vcmi.adventureOptions.leftButtonDrag.hover" : "Left Click Drag Map", + "vcmi.adventureOptions.leftButtonDrag.help" : "{Left Click Drag Map}\n\nWhen enabled, moving mouse with left button pressed will drag adventure map view", "vcmi.adventureOptions.mapScrollSpeed1.hover": "", "vcmi.adventureOptions.mapScrollSpeed5.hover": "", "vcmi.adventureOptions.mapScrollSpeed6.hover": "", diff --git a/Mods/vcmi/config/vcmi/ukrainian.json b/Mods/vcmi/config/vcmi/ukrainian.json index 01a0bb956..31915f101 100644 --- a/Mods/vcmi/config/vcmi/ukrainian.json +++ b/Mods/vcmi/config/vcmi/ukrainian.json @@ -68,6 +68,8 @@ "vcmi.adventureOptions.showGrid.help" : "{Показувати сітку}\n\n Відображає сітку, що показує межі між клітинками на мапі пригод.", "vcmi.adventureOptions.borderScroll.hover" : "Прокрутка по краю", "vcmi.adventureOptions.borderScroll.help" : "{{Прокрутка по краю}\n\nПрокручувати мапу пригод, коли курсор знаходиться біля краю вікна. Цю функцію можна вимкнути, утримуючи клавішу CTRL.", + "vcmi.adventureOptions.leftButtonDrag.hover" : "Переміщення мапи лівою кнопкою", + "vcmi.adventureOptions.leftButtonDrag.help" : "{Переміщення мапи лівою кнопкою}\n\nЯкщо увімкнено, переміщення миші з натиснутою лівою кнопкою буде перетягувати мапу пригод", "vcmi.adventureOptions.mapScrollSpeed1.hover": "", "vcmi.adventureOptions.mapScrollSpeed5.hover": "", "vcmi.adventureOptions.mapScrollSpeed6.hover": "", diff --git a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingDialog.java b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingDialog.java index 1fb5e8894..98bbcd9b2 100644 --- a/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingDialog.java +++ b/android/vcmi-app/src/main/java/eu/vcmi/vcmi/settings/LanguageSettingDialog.java @@ -15,13 +15,20 @@ public class LanguageSettingDialog extends LauncherSettingDialog static { AVAILABLE_LANGUAGES.add("english"); + AVAILABLE_LANGUAGES.add("czech"); AVAILABLE_LANGUAGES.add("chinese"); + AVAILABLE_LANGUAGES.add("finnish"); AVAILABLE_LANGUAGES.add("french"); AVAILABLE_LANGUAGES.add("german"); + AVAILABLE_LANGUAGES.add("hungarian"); + AVAILABLE_LANGUAGES.add("italian"); AVAILABLE_LANGUAGES.add("korean"); AVAILABLE_LANGUAGES.add("polish"); + AVAILABLE_LANGUAGES.add("portuguese"); AVAILABLE_LANGUAGES.add("russian"); AVAILABLE_LANGUAGES.add("spanish"); + AVAILABLE_LANGUAGES.add("swedish"); + AVAILABLE_LANGUAGES.add("turkish"); AVAILABLE_LANGUAGES.add("ukrainian"); AVAILABLE_LANGUAGES.add("other_cp1250"); AVAILABLE_LANGUAGES.add("other_cp1251"); diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index 6d62e66d3..7d6be81d4 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -572,7 +572,14 @@ void CPlayerInterface::garrisonsChanged(std::vector ob auto * town = dynamic_cast(object); if (hero) + { adventureInt->onHeroChanged(hero); + + if(hero->inTownGarrison) + { + adventureInt->onTownChanged(hero->visitedTown); + } + } if (town) adventureInt->onTownChanged(town); } diff --git a/client/adventureMap/AdventureMapInterface.cpp b/client/adventureMap/AdventureMapInterface.cpp index 7ee136edd..740b84a48 100644 --- a/client/adventureMap/AdventureMapInterface.cpp +++ b/client/adventureMap/AdventureMapInterface.cpp @@ -394,8 +394,9 @@ void AdventureMapInterface::onPlayerTurnStarted(PlayerColor playerID) LOCPLINT->localState->setSelection(LOCPLINT->localState->getWanderingHero(0)); } - //show new day animation and sound on infobar - widget->getInfoBar()->showDate(); + //show new day animation and sound on infobar, except for 1st day of the game + if (LOCPLINT->cb->getDate(Date::DAY) != 1) + widget->getInfoBar()->showDate(); onHeroChanged(nullptr); Canvas canvas = Canvas::createFromSurface(screen); diff --git a/client/adventureMap/CInfoBar.cpp b/client/adventureMap/CInfoBar.cpp index dc34df6bb..7f5226d46 100644 --- a/client/adventureMap/CInfoBar.cpp +++ b/client/adventureMap/CInfoBar.cpp @@ -27,6 +27,7 @@ #include "../gui/WindowHandler.h" #include "../../CCallback.h" +#include "../../lib/CConfigHandler.h" #include "../../lib/CGeneralTextHandler.h" #include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGTownInstance.h" @@ -51,14 +52,22 @@ CInfoBar::VisibleHeroInfo::VisibleHeroInfo(const CGHeroInstance * hero) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared("ADSTATHR"); - heroTooltip = std::make_shared(Point(0,0), hero); + + if(settings["gameTweaks"]["infoBarCreatureManagement"].Bool()) + heroTooltip = std::make_shared(Point(0,0), hero); + else + heroTooltip = std::make_shared(Point(0,0), hero); } CInfoBar::VisibleTownInfo::VisibleTownInfo(const CGTownInstance * town) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); background = std::make_shared("ADSTATCS"); - townTooltip = std::make_shared(Point(0,0), town); + + if(settings["gameTweaks"]["infoBarCreatureManagement"].Bool()) + townTooltip = std::make_shared(Point(0,0), town); + else + townTooltip = std::make_shared(Point(0,0), town); } CInfoBar::VisibleDateInfo::VisibleDateInfo() @@ -273,8 +282,16 @@ void CInfoBar::tick(uint32_t msPassed) void CInfoBar::clickReleased(const Point & cursorPosition) { + timerCounter = 0; + removeUsedEvents(TIME); //expiration trigger from just clicked element is not valid anymore + if(state == HERO || state == TOWN) + { + if(settings["gameTweaks"]["infoBarCreatureManagement"].Bool()) + return; + showGameStatus(); + } else if(state == GAME) showDate(); else @@ -297,11 +314,13 @@ void CInfoBar::hover(bool on) CInfoBar::CInfoBar(const Rect & position) : CIntObject(LCLICK | SHOW_POPUP | HOVER, position.topLeft()), timerCounter(0), - state(EMPTY) + state(EMPTY), + listener(settings.listen["gameTweaks"]["infoBarCreatureManagement"]) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); pos.w = position.w; pos.h = position.h; + listener(std::bind(&CInfoBar::OnInfoBarCreatureManagementChanged, this)); reset(); } @@ -309,6 +328,10 @@ CInfoBar::CInfoBar(const Point & position): CInfoBar(Rect(position.x, position.y { } +void CInfoBar::OnInfoBarCreatureManagementChanged() +{ + showSelection(); +} void CInfoBar::setTimer(uint32_t msToTrigger) { diff --git a/client/adventureMap/CInfoBar.h b/client/adventureMap/CInfoBar.h index 07261e1d5..c69fed1f0 100644 --- a/client/adventureMap/CInfoBar.h +++ b/client/adventureMap/CInfoBar.h @@ -10,6 +10,7 @@ #pragma once #include "../gui/CIntObject.h" +#include "CConfigHandler.h" VCMI_LIB_NAMESPACE_BEGIN @@ -25,7 +26,9 @@ class CShowableAnim; class CComponent; class CComponentBox; class CHeroTooltip; +class CInteractableHeroTooltip; class CTownTooltip; +class CInteractableTownTooltip; class CLabel; class CMultiLineLabel; @@ -66,14 +69,14 @@ private: class VisibleHeroInfo : public CVisibleInfo { - std::shared_ptr heroTooltip; + std::shared_ptr heroTooltip; //should have CHeroTooltip or CInteractableHeroTooltip; public: VisibleHeroInfo(const CGHeroInstance * hero); }; class VisibleTownInfo : public CVisibleInfo { - std::shared_ptr townTooltip; + std::shared_ptr townTooltip; //should have CTownTooltip or CInteractableTownTooltip; public: VisibleTownInfo(const CGTownInstance * town); }; @@ -140,6 +143,7 @@ private: EState state; uint32_t timerCounter; bool shouldPopAll = false; + SettingsListener listener; std::queue> componentsQueue; @@ -191,5 +195,8 @@ public: /// check if infobar is showed something about pickups bool showingComponents(); + + /// event handler for custom listening on game setting change + void OnInfoBarCreatureManagementChanged(); }; diff --git a/client/adventureMap/CList.cpp b/client/adventureMap/CList.cpp index 23c325ea9..e53968d9f 100644 --- a/client/adventureMap/CList.cpp +++ b/client/adventureMap/CList.cpp @@ -287,8 +287,28 @@ void CHeroList::updateElement(const CGHeroInstance * hero) void CHeroList::updateWidget() { - listBox->resize(LOCPLINT->localState->getWanderingHeroes().size()); - listBox->reset(); + auto & heroes = LOCPLINT->localState->getWanderingHeroes(); + + listBox->resize(heroes.size()); + + for (size_t i = 0; i < heroes.size(); ++i) + { + auto item = std::dynamic_pointer_cast(listBox->getItem(i)); + + if (!item) + continue; + + if (item->hero == heroes[i]) + { + item->update(); + } + else + { + listBox->reset(); + break; + } + } + if (LOCPLINT->localState->getCurrentHero()) select(LOCPLINT->localState->getCurrentHero()); @@ -364,8 +384,28 @@ void CTownList::updateElement(const CGTownInstance * town) void CTownList::updateWidget() { - listBox->resize(LOCPLINT->localState->getOwnedTowns().size()); - listBox->reset(); + auto & towns = LOCPLINT->localState->getOwnedTowns(); + + listBox->resize(towns.size()); + + for (size_t i = 0; i < towns.size(); ++i) + { + auto item = std::dynamic_pointer_cast(listBox->getItem(i)); + + if (!item) + continue; + + if (item->town == towns[i]) + { + item->update(); + } + else + { + listBox->reset(); + break; + } + } + if (LOCPLINT->localState->getCurrentTown()) select(LOCPLINT->localState->getCurrentTown()); diff --git a/client/battle/BattleWindow.cpp b/client/battle/BattleWindow.cpp index 8ff4486c9..26fc2c572 100644 --- a/client/battle/BattleWindow.cpp +++ b/client/battle/BattleWindow.cpp @@ -86,7 +86,7 @@ BattleWindow::BattleWindow(BattleInterface & owner): else tacticPhaseEnded(); - addUsedEvents(KEYBOARD); + addUsedEvents(LCLICK | KEYBOARD); } void BattleWindow::createQueue() @@ -204,6 +204,16 @@ void BattleWindow::keyPressed(EShortcut key) InterfaceObjectConfigurable::keyPressed(key); } +void BattleWindow::clickPressed(const Point & cursorPosition) +{ + if (owner.openingPlaying()) + { + owner.openingEnd(); + return; + } + InterfaceObjectConfigurable::clickPressed(cursorPosition); +} + void BattleWindow::tacticPhaseStarted() { auto menuBattle = widget("menuBattle"); diff --git a/client/battle/BattleWindow.h b/client/battle/BattleWindow.h index 3bd0f4314..379b85e52 100644 --- a/client/battle/BattleWindow.h +++ b/client/battle/BattleWindow.h @@ -86,6 +86,7 @@ public: void deactivate() override; void keyPressed(EShortcut key) override; bool captureThisKey(EShortcut key) override; + void clickPressed(const Point & cursorPosition) override; void show(Canvas & to) override; void showAll(Canvas & to) override; diff --git a/client/gui/EventDispatcher.cpp b/client/gui/EventDispatcher.cpp index 290cf1ee6..c09d95826 100644 --- a/client/gui/EventDispatcher.cpp +++ b/client/gui/EventDispatcher.cpp @@ -172,6 +172,15 @@ void EventDispatcher::dispatchClosePopup(const Point & position) void EventDispatcher::handleLeftButtonClick(const Point & position, bool isPressed) { + // WARNING: this approach is NOT SAFE + // 1) We allow (un)registering elements when list itself is being processed/iterated + // 2) To avoid iterator invalidation we create a copy of this list for processing + // HOWEVER it is completely possible (as in, actually happen, no just theory) to: + // 1) element gets unregistered and deleted from lclickable + // 2) element is completely deleted, as in - destructor called, memory freed + // 3) new element is created *with exactly same address(!) + // 4) new element is registered and code will incorrectly assume that this element is still registered + // POSSIBLE SOLUTION: make EventReceivers inherit from create_shared_from this and store weak_ptr's in lists auto hlp = lclickable; for(auto & i : hlp) { diff --git a/client/mapView/MapViewActions.cpp b/client/mapView/MapViewActions.cpp index bb35f0fc9..b1cd6c240 100644 --- a/client/mapView/MapViewActions.cpp +++ b/client/mapView/MapViewActions.cpp @@ -29,11 +29,12 @@ MapViewActions::MapViewActions(MapView & owner, const std::shared_ptrgetPixelsVisibleDimensions().x; pos.h = model->getPixelsVisibleDimensions().y; - addUsedEvents(LCLICK | SHOW_POPUP | GESTURE | HOVER | MOVE | WHEEL); + addUsedEvents(LCLICK | SHOW_POPUP | DRAG | GESTURE | HOVER | MOVE | WHEEL); } void MapViewActions::setContext(const std::shared_ptr & context) @@ -43,10 +44,32 @@ void MapViewActions::setContext(const std::shared_ptr & con void MapViewActions::clickPressed(const Point & cursorPosition) { - int3 tile = model->getTileAtPoint(cursorPosition - pos.topLeft()); + if (!settings["adventure"]["leftButtonDrag"].Bool()) + { + int3 tile = model->getTileAtPoint(cursorPosition - pos.topLeft()); - if(context->isInMap(tile)) - adventureInt->onTileLeftClicked(tile); + if(context->isInMap(tile)) + adventureInt->onTileLeftClicked(tile); + } +} + +void MapViewActions::clickReleased(const Point & cursorPosition) +{ + if (!dragActive && settings["adventure"]["leftButtonDrag"].Bool()) + { + int3 tile = model->getTileAtPoint(cursorPosition - pos.topLeft()); + + if(context->isInMap(tile)) + adventureInt->onTileLeftClicked(tile); + } + dragActive = false; + dragDistance = Point(0,0); +} + +void MapViewActions::clickCancel(const Point & cursorPosition) +{ + dragActive = false; + dragDistance = Point(0,0); } void MapViewActions::showPopupWindow(const Point & cursorPosition) @@ -67,6 +90,17 @@ void MapViewActions::wheelScrolled(int distance) adventureInt->hotkeyZoom(distance * 4); } +void MapViewActions::mouseDragged(const Point & cursorPosition, const Point & lastUpdateDistance) +{ + dragDistance += lastUpdateDistance; + + if (dragDistance.length() > 16) + dragActive = true; + + if (dragActive && settings["adventure"]["leftButtonDrag"].Bool()) + owner.onMapSwiped(lastUpdateDistance); +} + void MapViewActions::gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) { owner.onMapSwiped(lastUpdateDistance); diff --git a/client/mapView/MapViewActions.h b/client/mapView/MapViewActions.h index 9f6ca11d2..de6158f43 100644 --- a/client/mapView/MapViewActions.h +++ b/client/mapView/MapViewActions.h @@ -22,7 +22,9 @@ class MapViewActions : public CIntObject std::shared_ptr model; std::shared_ptr context; + Point dragDistance; double pinchZoomFactor; + bool dragActive; void handleHover(const Point & cursorPosition); @@ -32,11 +34,14 @@ public: void setContext(const std::shared_ptr & context); void clickPressed(const Point & cursorPosition) override; + void clickReleased(const Point & cursorPosition) override; + void clickCancel(const Point & cursorPosition) override; void showPopupWindow(const Point & cursorPosition) override; void gesturePanning(const Point & initialPosition, const Point & currentPosition, const Point & lastUpdateDistance) override; void gesturePinch(const Point & centerPosition, double lastUpdateFactor) override; void hover(bool on) override; void gesture(bool on, const Point & initialPosition, const Point & finalPosition) override; void mouseMoved(const Point & cursorPosition, const Point & lastUpdateDistance) override; + void mouseDragged(const Point & cursorPosition, const Point & lastUpdateDistance) override; void wheelScrolled(int distance) override; }; diff --git a/client/widgets/CGarrisonInt.cpp b/client/widgets/CGarrisonInt.cpp index 9725017e5..95c068a54 100644 --- a/client/widgets/CGarrisonInt.cpp +++ b/client/widgets/CGarrisonInt.cpp @@ -410,7 +410,19 @@ CGarrisonSlot::CGarrisonSlot(CGarrisonInt * Owner, int x, int y, SlotID IID, CGa pos.h = 64; } - stackCount = std::make_shared(pos.w, pos.h, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, ETextAlignment::BOTTOMRIGHT, Colors::WHITE); + int labelPosW = pos.w; + int labelPosH = pos.h; + + if(Owner->layout == CGarrisonInt::ESlotsLayout::REVERSED_TWO_ROWS) //labels under icon + { + labelPosW = pos.w / 2 + 1; + labelPosH += 7; + } + ETextAlignment labelAlignment = Owner->layout == CGarrisonInt::ESlotsLayout::REVERSED_TWO_ROWS + ? ETextAlignment::CENTER + : ETextAlignment::BOTTOMRIGHT; + + stackCount = std::make_shared(labelPosW, labelPosH, owner->smallIcons ? FONT_TINY : FONT_MEDIUM, labelAlignment, Colors::WHITE); update(); } @@ -488,7 +500,7 @@ void CGarrisonInt::addSplitBtn(std::shared_ptr button) void CGarrisonInt::createSlots() { int distance = interx + (smallIcons ? 32 : 58); - for(int i=0; i<2; i++) + for(int i = 0; i < 2; i++) { std::vector> garrisonSlots; garrisonSlots.resize(7); @@ -499,14 +511,26 @@ void CGarrisonInt::createSlots() garrisonSlots[elem.first.getNum()] = std::make_shared(this, i*garOffset.x + (elem.first.getNum()*distance), i*garOffset.y, elem.first, static_cast(i), elem.second); } } - for(int j=0; j<7; j++) + for(int j = 0; j < 7; j++) { if(!garrisonSlots[j]) garrisonSlots[j] = std::make_shared(this, i*garOffset.x + (j*distance), i*garOffset.y, SlotID(j), static_cast(i), nullptr); - if(twoRows && j>=4) + + if(layout == ESlotsLayout::TWO_ROWS && j >= 4) { garrisonSlots[j]->moveBy(Point(-126, 37)); } + else if(layout == ESlotsLayout::REVERSED_TWO_ROWS) + { + if(j >= 3) + { + garrisonSlots[j]->moveBy(Point(-90, 49)); + } + else + { + garrisonSlots[j]->moveBy(Point(36, 0)); + } + } } vstd::concatenate(availableSlots, garrisonSlots); } @@ -646,16 +670,16 @@ void CGarrisonInt::bulkSmartSplitStack(const CGarrisonSlot * selected) } CGarrisonInt::CGarrisonInt(int x, int y, int inx, const Point & garsOffset, - const CArmedInstance * s1, const CArmedInstance * s2, - bool _removableUnits, bool smallImgs, bool _twoRows) - : highlighted(nullptr), - inSplittingMode(false), - interx(inx), - garOffset(garsOffset), - pb(false), - smallIcons(smallImgs), - removableUnits(_removableUnits), - twoRows(_twoRows) + const CArmedInstance * s1, const CArmedInstance * s2, + bool _removableUnits, bool smallImgs, ESlotsLayout _layout) + : highlighted(nullptr), + inSplittingMode(false), + interx(inx), + garOffset(garsOffset), + pb(false), + smallIcons(smallImgs), + removableUnits(_removableUnits), + layout(_layout) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); diff --git a/client/widgets/CGarrisonInt.h b/client/widgets/CGarrisonInt.h index 975fa5139..baa1360ca 100644 --- a/client/widgets/CGarrisonInt.h +++ b/client/widgets/CGarrisonInt.h @@ -81,6 +81,13 @@ class CGarrisonInt :public CIntObject bool checkSelected(const CGarrisonSlot * selected, TQuantity min = 0) const; public: + enum class ESlotsLayout + { + ONE_ROW, + TWO_ROWS, + REVERSED_TWO_ROWS + }; + int interx; ///< Space between slots Point garOffset; ///< Offset between garrisons (not used if only one hero) std::vector> splitButtons; ///< May be empty if no buttons @@ -89,9 +96,10 @@ public: bool pb, smallIcons, ///< true - 32x32 imgs, false - 58x64 removableUnits, ///< player Can remove units from up - twoRows, ///< slots Will be placed in 2 rows owned[2]; ///< player Owns up or down army ([0] upper, [1] lower) + ESlotsLayout layout; + void selectSlot(CGarrisonSlot * slot); ///< @param slot null = deselect const CGarrisonSlot * getSelection() const; @@ -123,13 +131,13 @@ public: /// @param s1, s2 Top and bottom armies /// @param _removableUnits You can take units from top /// @param smallImgs Units images size 64x58 or 32x32 - /// @param _twoRows Display slots in 2 row (1st row = 4 slots, 2nd = 3 slots) + /// @param _layout - when TWO_ROWS - Display slots in 2 rows (1st row = 4 slots, 2nd = 3 slots), REVERSED_TWO_ROWS = 3 slots in 1st row CGarrisonInt(int x, int y, int inx, - const Point & garsOffset, - const CArmedInstance * s1, const CArmedInstance * s2 = nullptr, - bool _removableUnits = true, - bool smallImgs = false, - bool _twoRows = false); + const Point & garsOffset, + const CArmedInstance * s1, const CArmedInstance * s2 = nullptr, + bool _removableUnits = true, + bool smallImgs = false, + ESlotsLayout _layout = ESlotsLayout::ONE_ROW); }; class CGarrisonHolder diff --git a/client/widgets/MiscWidgets.cpp b/client/widgets/MiscWidgets.cpp index 7e97edbbf..9ef040193 100644 --- a/client/widgets/MiscWidgets.cpp +++ b/client/widgets/MiscWidgets.cpp @@ -303,6 +303,31 @@ CHeroTooltip::CHeroTooltip(Point pos, const CGHeroInstance * hero): init(InfoAboutHero(hero, InfoAboutHero::EInfoLevel::DETAILED)); } +CInteractableHeroTooltip::CInteractableHeroTooltip(Point pos, const CGHeroInstance * hero): + CGarrisonInt(pos.x, pos.y+73, 4, Point(0, 0), hero, nullptr, true, true, CGarrisonInt::ESlotsLayout::REVERSED_TWO_ROWS) +{ + init(InfoAboutHero(hero, InfoAboutHero::EInfoLevel::DETAILED)); +} + +void CInteractableHeroTooltip::init(const InfoAboutHero & hero) +{ + OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + portrait = std::make_shared("PortraitsLarge", hero.portrait, 0, 3, 2-73); + title = std::make_shared(66, 2-73, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, hero.name); + + if(hero.details) + { + for(size_t i = 0; i < hero.details->primskills.size(); i++) + labels.push_back(std::make_shared(75 + 28 * (int)i, 58-73, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, + std::to_string(hero.details->primskills[i]))); + + labels.push_back(std::make_shared(158, 98-73, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, std::to_string(hero.details->mana))); + + morale = std::make_shared("IMRL22", hero.details->morale + 3, 0, 5, 74-73); + luck = std::make_shared("ILCK22", hero.details->luck + 3, 0, 5, 91-73); + } +} + void CTownTooltip::init(const InfoAboutTown & town) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); @@ -357,6 +382,55 @@ CTownTooltip::CTownTooltip(Point pos, const CGTownInstance * town) init(InfoAboutTown(town, true)); } +CInteractableTownTooltip::CInteractableTownTooltip(Point pos, const CGTownInstance * town) + : CGarrisonInt(pos.x, pos.y+73, 4, Point(0, 0), town->getUpperArmy(), nullptr, true, true, CGarrisonInt::ESlotsLayout::REVERSED_TWO_ROWS) +{ + init(InfoAboutTown(town, true)); +} + +void CInteractableTownTooltip::init(const InfoAboutTown & town) +{ + OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); + + //order of icons in def: fort, citadel, castle, no fort + size_t fortIndex = town.fortLevel ? town.fortLevel - 1 : 3; + + fort = std::make_shared("ITMCLS", fortIndex, 0, 105, 31-73); + + assert(town.tType); + + size_t iconIndex = town.tType->clientInfo.icons[town.fortLevel > 0][town.built >= CGI->settings()->getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)]; + + build = std::make_shared("itpt", iconIndex, 0, 3, 2-73); + title = std::make_shared(66, 2-73, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, town.name); + + if(town.details) + { + hall = std::make_shared("ITMTLS", town.details->hallLevel, 0, 67, 31-73); + + if(town.details->goldIncome) + { + income = std::make_shared(157, 58-73, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE, + std::to_string(town.details->goldIncome)); + } + if(town.details->garrisonedHero) //garrisoned hero icon + garrisonedHero = std::make_shared("TOWNQKGH", 149, 76-73); + + if(town.details->customRes)//silo is built + { + if(town.tType->primaryRes == EGameResID::WOOD_AND_ORE )// wood & ore + { + res1 = std::make_shared("SMALRES", GameResID(EGameResID::WOOD), 0, 7, 75-73); + res2 = std::make_shared("SMALRES", GameResID(EGameResID::ORE), 0, 7, 88-73); + } + else + { + res1 = std::make_shared("SMALRES", town.tType->primaryRes, 0, 7, 81-73); + } + } + } +} + void MoraleLuckBox::set(const AFactionMember * node) { OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE); diff --git a/client/widgets/MiscWidgets.h b/client/widgets/MiscWidgets.h index d19f191e5..56d31b643 100644 --- a/client/widgets/MiscWidgets.h +++ b/client/widgets/MiscWidgets.h @@ -10,6 +10,7 @@ #pragma once #include "../gui/CIntObject.h" +#include "CGarrisonInt.h" VCMI_LIB_NAMESPACE_BEGIN @@ -80,6 +81,20 @@ public: CHeroTooltip(Point pos, const CGHeroInstance * hero); }; +/// Class for HD mod-like interactable infobox tooltip. Does not have any background! +class CInteractableHeroTooltip : public CGarrisonInt +{ + std::shared_ptr title; + std::shared_ptr portrait; + std::vector> labels; + std::shared_ptr morale; + std::shared_ptr luck; + + void init(const InfoAboutHero & hero); +public: + CInteractableHeroTooltip(Point pos, const CGHeroInstance * hero); +}; + /// Class for town tooltip. Does not have any background! /// background for infoBox: ADSTATCS /// background for tooltip: TOWNQVBK @@ -99,6 +114,23 @@ public: CTownTooltip(Point pos, const CGTownInstance * town); }; +/// Class for HD mod-like interactable infobox tooltip. Does not have any background! +class CInteractableTownTooltip : public CGarrisonInt +{ + std::shared_ptr title; + std::shared_ptr fort; + std::shared_ptr hall; + std::shared_ptr build; + std::shared_ptr income; + std::shared_ptr garrisonedHero; + std::shared_ptr res1; + std::shared_ptr res2; + + void init(const InfoAboutTown & town); +public: + CInteractableTownTooltip(Point pos, const CGTownInstance * town); +}; + /// draws picture with creature on background, use Animated=true to get animation class CCreaturePic : public CIntObject { diff --git a/client/windows/CKingdomInterface.cpp b/client/windows/CKingdomInterface.cpp index 1369ad42c..9e1a07dde 100644 --- a/client/windows/CKingdomInterface.cpp +++ b/client/windows/CKingdomInterface.cpp @@ -772,7 +772,7 @@ CTownItem::CTownItem(const CGTownInstance * Town) hall = std::make_shared( 69, 31, town, true); fort = std::make_shared(111, 31, town, false); - garr = std::make_shared(313, 3, 4, Point(232,0), town->getUpperArmy(), town->visitingHero, true, true, true); + garr = std::make_shared(313, 3, 4, Point(232,0), town->getUpperArmy(), town->visitingHero, true, true, CGarrisonInt::ESlotsLayout::TWO_ROWS); heroes = std::make_shared(town, Point(244,6), Point(475,6), garr, false); size_t iconIndex = town->town->clientInfo.icons[town->hasFort()][town->builded >= CGI->settings()->getInteger(EGameSettings::TOWNS_BUILDINGS_PER_TURN_CAP)]; diff --git a/client/windows/settings/AdventureOptionsTab.cpp b/client/windows/settings/AdventureOptionsTab.cpp index c22d16e7a..499858037 100644 --- a/client/windows/settings/AdventureOptionsTab.cpp +++ b/client/windows/settings/AdventureOptionsTab.cpp @@ -36,6 +36,14 @@ AdventureOptionsTab::AdventureOptionsTab() OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; setRedrawParent(true); +#ifdef VCMI_MOBILE + addConditional("mobile", true); + addConditional("desktop", false); +#else + addConditional("mobile", false); + addConditional("desktop", true); +#endif + const JsonNode config(ResourceID("config/widgets/settings/adventureOptionsTab.json")); addCallback("playerHeroSpeedChanged", [this](int value) { @@ -110,6 +118,14 @@ AdventureOptionsTab::AdventureOptionsTab() { return setBoolSetting("adventure", "borderScroll", value); }); + addCallback("infoBarCreatureManagementChanged", [](bool value) + { + return setBoolSetting("gameTweaks", "infoBarCreatureManagement", value); + }); + addCallback("leftButtonDragChanged", [](bool value) + { + return setBoolSetting("adventure", "leftButtonDrag", value); + }); build(config); std::shared_ptr playerHeroSpeedToggle = widget("heroMovementSpeedPicker"); @@ -141,4 +157,11 @@ AdventureOptionsTab::AdventureOptionsTab() std::shared_ptr borderScrollCheckbox = widget("borderScrollCheckbox"); borderScrollCheckbox->setSelected(settings["adventure"]["borderScroll"].Bool()); + + std::shared_ptr infoBarCreatureManagementCheckbox = widget("infoBarCreatureManagementCheckbox"); + infoBarCreatureManagementCheckbox->setSelected(settings["gameTweaks"]["infoBarCreatureManagement"].Bool()); + + std::shared_ptr leftButtonDragCheckbox = widget("leftButtonDragCheckbox"); + if (leftButtonDragCheckbox) + leftButtonDragCheckbox->setSelected(settings["adventure"]["leftButtonDrag"].Bool()); } diff --git a/config/schemas/mod.json b/config/schemas/mod.json index 10621db8f..9c0d5b4e3 100644 --- a/config/schemas/mod.json +++ b/config/schemas/mod.json @@ -153,7 +153,7 @@ "language" : { "type" : "string", "description" : "Base language of the mod, before applying localizations. By default vcmi assumes English", - "enum" : [ "czech", "chinese", "english", "french", "german", "hungarian", "italian", "korean", "polish", "russian", "spanish", "ukrainian" ] + "enum" : [ "czech", "chinese", "english", "finnish", "french", "german", "hungarian", "italian", "korean", "polish", "portuguese", "russian", "spanish", "swedish", "turkish", "ukrainian" ] }, "czech" : { "$ref" : "#/definitions/localizable" @@ -164,6 +164,9 @@ "english" : { "$ref" : "#/definitions/localizable" }, + "finnish" : { + "$ref" : "#/definitions/localizable" + }, "french" : { "$ref" : "#/definitions/localizable" }, @@ -182,12 +185,21 @@ "polish" : { "$ref" : "#/definitions/localizable" }, + "portuguese" : { + "$ref" : "#/definitions/localizable" + }, "russian" : { "$ref" : "#/definitions/localizable" }, "spanish" : { "$ref" : "#/definitions/localizable" }, + "swedish" : { + "$ref" : "#/definitions/localizable" + }, + "turkish" : { + "$ref" : "#/definitions/localizable" + }, "ukrainian" : { "$ref" : "#/definitions/localizable" }, diff --git a/config/schemas/settings.json b/config/schemas/settings.json index b3dbd1651..34a2d1631 100644 --- a/config/schemas/settings.json +++ b/config/schemas/settings.json @@ -59,12 +59,12 @@ }, "language" : { "type" : "string", - "enum" : [ "english", "czech", "chinese", "french", "german", "hungarian", "italian", "korean", "polish", "russian", "spanish", "ukrainian" ], + "enum" : [ "english", "czech", "chinese", "finnish", "french", "german", "hungarian", "italian", "korean", "polish", "portuguese", "russian", "spanish", "swedish", "turkish", "ukrainian" ], "default" : "english" }, "gameDataLanguage" : { "type" : "string", - "enum" : [ "auto", "english", "czech", "chinese", "french", "german", "hungarian", "italian", "korean", "polish", "russian", "spanish", "ukrainian", "other_cp1250", "other_cp1251", "other_cp1252" ], + "enum" : [ "auto", "english", "czech", "chinese", "finnish", "french", "german", "hungarian", "italian", "korean", "polish", "portuguese", "russian", "spanish", "swedish", "turkish", "ukrainian", "other_cp1250", "other_cp1251", "other_cp1252" ], "default" : "auto" }, "lastSave" : { @@ -191,7 +191,7 @@ "type" : "object", "additionalProperties" : false, "default" : {}, - "required" : [ "heroMoveTime", "enemyMoveTime", "scrollSpeedPixels", "heroReminder", "quickCombat", "objectAnimation", "terrainAnimation", "alwaysSkipCombat", "borderScroll" ], + "required" : [ "heroMoveTime", "enemyMoveTime", "scrollSpeedPixels", "heroReminder", "quickCombat", "objectAnimation", "terrainAnimation", "alwaysSkipCombat", "borderScroll", "leftButtonDrag" ], "properties" : { "heroMoveTime" : { "type" : "number", @@ -231,6 +231,10 @@ "defaultIOS" : false, "defaultAndroid" : false, "default" : true + }, + "leftButtonDrag" : { + "type" : "boolean", + "default" : false } } }, @@ -499,7 +503,8 @@ "availableCreaturesAsDwellingLabel", "compactTownCreatureInfo", "infoBarPick", - "skipBattleIntroMusic" + "skipBattleIntroMusic", + "infoBarCreatureManagement" ], "properties" : { "showGrid" : { @@ -529,6 +534,10 @@ "skipBattleIntroMusic" : { "type" : "boolean", "default" : false + }, + "infoBarCreatureManagement": { + "type" : "boolean", + "default" : false } } } diff --git a/config/widgets/settings/adventureOptionsTab.json b/config/widgets/settings/adventureOptionsTab.json index fdce6d5bc..ecd3e753f 100644 --- a/config/widgets/settings/adventureOptionsTab.json +++ b/config/widgets/settings/adventureOptionsTab.json @@ -340,6 +340,10 @@ }, { "text": "vcmi.adventureOptions.borderScroll.hover" + }, + { + "text": "vcmi.adventureOptions.leftButtonDrag.hover", + "created" : "desktop" } ] }, @@ -373,6 +377,17 @@ "name": "borderScrollCheckbox", "help": "vcmi.adventureOptions.borderScroll", "callback": "borderScrollChanged" + }, + { + "name": "infoBarCreatureManagementCheckbox", + "help": "vcmi.adventureOptions.infoBarCreatureManagement", + "callback": "infoBarCreatureManagementChanged" + }, + { + "name": "leftButtonDragCheckbox", + "help": "vcmi.adventureOptions.leftButtonDrag", + "callback": "leftButtonDragChanged", + "created" : "desktop" } ] } diff --git a/launcher/aboutProject/aboutproject_moc.cpp b/launcher/aboutProject/aboutproject_moc.cpp index 769e95bac..b24f9e85d 100644 --- a/launcher/aboutProject/aboutproject_moc.cpp +++ b/launcher/aboutProject/aboutproject_moc.cpp @@ -29,6 +29,14 @@ AboutProjectView::AboutProjectView(QWidget * parent) ui->lineEditOperatingSystem->setText(QSysInfo::prettyProductName()); } +void AboutProjectView::changeEvent(QEvent *event) +{ + if(event->type() == QEvent::LanguageChange) + ui->retranslateUi(this); + + QWidget::changeEvent(event); +} + void AboutProjectView::on_updatesButton_clicked() { UpdateDialog::showUpdateDialog(true); diff --git a/launcher/aboutProject/aboutproject_moc.h b/launcher/aboutProject/aboutproject_moc.h index 915f32fac..386565a60 100644 --- a/launcher/aboutProject/aboutproject_moc.h +++ b/launcher/aboutProject/aboutproject_moc.h @@ -21,6 +21,7 @@ class AboutProjectView : public QWidget { Q_OBJECT + void changeEvent(QEvent *event) override; public: explicit AboutProjectView(QWidget * parent = 0); diff --git a/launcher/settingsView/csettingsview_moc.cpp b/launcher/settingsView/csettingsview_moc.cpp index bc1a4ce4e..3690bf68f 100644 --- a/launcher/settingsView/csettingsview_moc.cpp +++ b/launcher/settingsView/csettingsview_moc.cpp @@ -127,7 +127,7 @@ QSize CSettingsView::getPreferredRenderingResolution() return QSize(resX, resY); } #endif - return QApplication::primaryScreen()->geometry().size(); + return QApplication::primaryScreen()->geometry().size() * QApplication::primaryScreen()->devicePixelRatio(); } void CSettingsView::fillValidScalingRange() diff --git a/launcher/translation/french.ts b/launcher/translation/french.ts index c00ec4ba2..c38c637ad 100644 --- a/launcher/translation/french.ts +++ b/launcher/translation/french.ts @@ -198,6 +198,118 @@ Version + + CModListModel + + + Translation + Traduction + + + + Town + Ville + + + + Test + Test + + + + Templates + Modèles + + + + Spells + Sorts + + + + Music + Musique + + + + Sounds + Sons + + + + Skills + Compétences + + + + + Other + Autre + + + + Objects + Objets + + + + + Mechanics + Mécanique + + + + + Interface + Interface + + + + Heroes + Héros + + + + + Graphical + Graphique + + + + Expansion + Extension + + + + Creatures + Créatures + + + + Artifacts + Artefacts + + + + AI + IA + + + + Name + Nom + + + + Type + Type + + + + Version + Version + + CModListView @@ -920,7 +1032,7 @@ Heroes® of Might and Magic® III HD n"est actuellement pas pris en charge Interface Improvements - + Améliorations de l'interface High Definition Support @@ -939,7 +1051,7 @@ Heroes® of Might and Magic® III HD n"est actuellement pas pris en charge Install mod that provides various interface improvements, such as better interface for random maps and selectable actions in battles - + Installer le mod qui fournit diverses améliorations d'interface, telles qu'une meilleure interface pour les cartes aléatoires et des actions sélectionnables dans les batailles diff --git a/lib/mapping/MapFeaturesH3M.cpp b/lib/mapping/MapFeaturesH3M.cpp index a5b240576..b5d3a4b7b 100644 --- a/lib/mapping/MapFeaturesH3M.cpp +++ b/lib/mapping/MapFeaturesH3M.cpp @@ -136,7 +136,7 @@ MapFormatFeaturesH3M MapFormatFeaturesH3M::getFeaturesHOTA(uint32_t hotaVersion) { result.artifactsCount = 163; // + HotA artifacts result.heroesCount = 178; // + Cove - result.heroesPortraitsCount = 185; // + Cove + result.heroesPortraitsCount = 186; // + Cove } if(hotaVersion == 3) { diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index 5e452d845..1df7ff008 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -1737,7 +1737,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec if(!object->spells.empty()) { object->clear(); - logGlobal->warn("Hero %s subID=%d has spells set twice (in map properties and on adventure map instance). Using the latter set...", object->getNameTextID(), object->subID); + logGlobal->debug("Hero %s subID=%d has spells set twice (in map properties and on adventure map instance). Using the latter set...", object->getNameTextID(), object->subID); } object->spells.insert(SpellID::PRESET); //placeholder "preset spells" diff --git a/lib/mapping/MapIdentifiersH3M.cpp b/lib/mapping/MapIdentifiersH3M.cpp index e2eeefb0c..2168848fa 100644 --- a/lib/mapping/MapIdentifiersH3M.cpp +++ b/lib/mapping/MapIdentifiersH3M.cpp @@ -29,7 +29,7 @@ void MapIdentifiersH3M::loadMapping(std::map & resul for (auto entry : mapping.Struct()) { IdentifierID sourceID (entry.second.Integer()); - IdentifierID targetID (*VLC->modh->identifiers.getIdentifier(VLC->modh->scopeGame(), identifierName, entry.first)); + IdentifierID targetID (*VLC->modh->identifiers.getIdentifier(entry.second.meta, identifierName, entry.first)); result[sourceID] = targetID; } @@ -39,13 +39,13 @@ void MapIdentifiersH3M::loadMapping(const JsonNode & mapping) { for (auto entryFaction : mapping["buildings"].Struct()) { - FactionID factionID (*VLC->modh->identifiers.getIdentifier(VLC->modh->scopeGame(), "faction", entryFaction.first)); + FactionID factionID (*VLC->modh->identifiers.getIdentifier(entryFaction.second.meta, "faction", entryFaction.first)); auto buildingMap = entryFaction.second; for (auto entryBuilding : buildingMap.Struct()) { BuildingID sourceID (entryBuilding.second.Integer()); - BuildingID targetID (*VLC->modh->identifiers.getIdentifier(VLC->modh->scopeGame(), "building." + VLC->factions()->getById(factionID)->getJsonKey(), entryBuilding.first)); + BuildingID targetID (*VLC->modh->identifiers.getIdentifier(entryBuilding.second.meta, "building." + VLC->factions()->getById(factionID)->getJsonKey(), entryBuilding.first)); mappingFactionBuilding[factionID][sourceID] = targetID; } @@ -68,7 +68,7 @@ void MapIdentifiersH3M::loadMapping(const JsonNode & mapping) { for (auto entryInner : entryOuter.second.Struct()) { - auto handler = VLC->objtypeh->getHandlerFor( VLC->modh->scopeGame(), entryOuter.first, entryInner.first); + auto handler = VLC->objtypeh->getHandlerFor( entryInner.second.meta, entryOuter.first, entryInner.first); auto entryValues = entryInner.second.Vector(); ObjectTypeIdentifier h3mID{Obj(entryValues[0].Integer()), int32_t(entryValues[1].Integer())}; @@ -78,7 +78,7 @@ void MapIdentifiersH3M::loadMapping(const JsonNode & mapping) } else { - auto handler = VLC->objtypeh->getHandlerFor( VLC->modh->scopeGame(), entryOuter.first, entryOuter.first); + auto handler = VLC->objtypeh->getHandlerFor( entryOuter.second.meta, entryOuter.first, entryOuter.first); auto entryValues = entryOuter.second.Vector(); ObjectTypeIdentifier h3mID{Obj(entryValues[0].Integer()), int32_t(entryValues[1].Integer())}; @@ -90,7 +90,7 @@ void MapIdentifiersH3M::loadMapping(const JsonNode & mapping) for (auto entry : mapping["portraits"].Struct()) { int32_t sourceID = entry.second.Integer(); - int32_t targetID = *VLC->modh->identifiers.getIdentifier(VLC->modh->scopeGame(), "hero", entry.first); + int32_t targetID = *VLC->modh->identifiers.getIdentifier(entry.second.meta, "hero", entry.first); int32_t iconID = VLC->heroTypes()->getByIndex(targetID)->getIconIndex(); mappingHeroPortrait[sourceID] = iconID; diff --git a/lib/rmg/RmgObject.cpp b/lib/rmg/RmgObject.cpp index 540463948..0106bee4f 100644 --- a/lib/rmg/RmgObject.cpp +++ b/lib/rmg/RmgObject.cpp @@ -344,6 +344,16 @@ void Object::Instance::finalize(RmgMap & map) setTemplate(terrainType->getId()); } } + if (dObject.ID == Obj::MONSTER) + { + //Make up for extra offset in HotA creature templates + auto visitableOffset = dObject.getVisitableOffset(); + auto fixedPos = getPosition(true) + visitableOffset; + vstd::abetween(fixedPos.x, visitableOffset.x, map.width() - 1); + vstd::abetween(fixedPos.y, visitableOffset.y, map.height() - 1); + int3 parentPos = getPosition(true) - getPosition(false); + setPosition(fixedPos - parentPos); + } if (dObject.isVisitable() && !map.isOnMap(dObject.visitablePos())) throw rmgException(boost::to_string(boost::format("Visitable tile %s of object %d at %s is outside the map") % dObject.visitablePos().toString() % dObject.id % dObject.pos.toString())); diff --git a/lib/rmg/modificators/ConnectionsPlacer.cpp b/lib/rmg/modificators/ConnectionsPlacer.cpp index 9b4178b4c..5b923acb7 100644 --- a/lib/rmg/modificators/ConnectionsPlacer.cpp +++ b/lib/rmg/modificators/ConnectionsPlacer.cpp @@ -334,7 +334,17 @@ void ConnectionsPlacer::selfSideIndirectConnection(const rmg::ZoneConnection & c if(dist < minDist || otherDist < minDist) return -1.f; + //This could fail is accessibleArea is below the map rmg::Area toPlace(rmgGate1.getArea() + rmgGate1.getAccessibleArea()); + auto inTheMap = toPlace.getTilesVector(); + toPlace.clear(); + for (const int3& tile : inTheMap) + { + if (map.isOnMap(tile)) + { + toPlace.add(tile); + } + } toPlace.translate(-zShift); path2 = managerOther.placeAndConnectObject(toPlace, rmgGate2, minDist, guarded2, true, ObjectManager::OptimizeType::NONE); diff --git a/lib/rmg/modificators/ObjectManager.cpp b/lib/rmg/modificators/ObjectManager.cpp index 72315f76e..2e75c38a1 100644 --- a/lib/rmg/modificators/ObjectManager.cpp +++ b/lib/rmg/modificators/ObjectManager.cpp @@ -606,8 +606,6 @@ bool ObjectManager::addGuard(rmg::Object & object, si32 strength, bool zoneGuard auto & instance = object.addInstance(*guard); instance.setPosition(guardPos - object.getPosition()); instance.setAnyTemplate(); //terrain is irrelevant for monsters, but monsters need some template now - //Make up for extra offset in HotA creature templates - instance.setPosition(instance.getPosition() + instance.object().getVisitableOffset()); return true; } diff --git a/mapeditor/mainwindow.ui b/mapeditor/mainwindow.ui index b24467265..7247dcf7c 100644 --- a/mapeditor/mainwindow.ui +++ b/mapeditor/mainwindow.ui @@ -919,7 +919,7 @@ - Save as + Save as... Ctrl+Shift+S @@ -1231,7 +1231,7 @@ - Export as + Export as... diff --git a/mapeditor/translation/english.ts b/mapeditor/translation/english.ts index 59c1f831d..0fe684d69 100644 --- a/mapeditor/translation/english.ts +++ b/mapeditor/translation/english.ts @@ -136,7 +136,7 @@ - Save as + Save as... @@ -281,6 +281,11 @@ Player 8 + + + Export as... + + Open map diff --git a/mapeditor/translation/french.ts b/mapeditor/translation/french.ts index ec6a69c65..4f67ba0b7 100644 --- a/mapeditor/translation/french.ts +++ b/mapeditor/translation/french.ts @@ -138,7 +138,7 @@ - Save as + Save as... Enregistrer sous... @@ -278,16 +278,16 @@ Player 7 Joueur 7 - - - Export as - Exporter sous... - Player 8 Joueur 8 + + + Export as... + Exporter sous... + Open map diff --git a/mapeditor/translation/german.ts b/mapeditor/translation/german.ts index 87879a780..db9d54f7b 100644 --- a/mapeditor/translation/german.ts +++ b/mapeditor/translation/german.ts @@ -136,7 +136,7 @@ - Save as + Save as... Speichern unter @@ -281,6 +281,11 @@ Player 8 Spieler 8 + + + Export as... + + Open map diff --git a/mapeditor/translation/polish.ts b/mapeditor/translation/polish.ts index 4d90fb892..dcf038c89 100644 --- a/mapeditor/translation/polish.ts +++ b/mapeditor/translation/polish.ts @@ -136,7 +136,7 @@ - Save as + Save as... Zapisz jako @@ -281,6 +281,11 @@ Player 8 Gracz 8 + + + Export as... + + Open map diff --git a/mapeditor/translation/russian.ts b/mapeditor/translation/russian.ts index e691f2e52..718573e2e 100644 --- a/mapeditor/translation/russian.ts +++ b/mapeditor/translation/russian.ts @@ -136,7 +136,7 @@ - Save as + Save as... Сохранить как @@ -281,6 +281,11 @@ Player 8 Игрок 8 + + + Export as... + + Open map diff --git a/mapeditor/translation/spanish.ts b/mapeditor/translation/spanish.ts index 21fce643a..4d0ea87fc 100644 --- a/mapeditor/translation/spanish.ts +++ b/mapeditor/translation/spanish.ts @@ -138,8 +138,8 @@ - Save as - Guardar como + Save as... + Guardar como... @@ -283,6 +283,11 @@ Player 8 Jugador 8 + + + Export as... + Exportar como... + Open map diff --git a/mapeditor/translation/ukrainian.ts b/mapeditor/translation/ukrainian.ts index 7ec45c5b7..d823173da 100644 --- a/mapeditor/translation/ukrainian.ts +++ b/mapeditor/translation/ukrainian.ts @@ -136,7 +136,7 @@ - Save as + Save as... Зберегти як @@ -281,6 +281,11 @@ Player 8 Гравець 8 + + + Export as... + + Open map diff --git a/server/PlayerMessageProcessor.cpp b/server/PlayerMessageProcessor.cpp index 7ff537d88..28713c413 100644 --- a/server/PlayerMessageProcessor.cpp +++ b/server/PlayerMessageProcessor.cpp @@ -299,7 +299,7 @@ void PlayerMessageProcessor::cheatResources(PlayerColor player, std::vector