1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-31 22:05:10 +02:00

Merge branch 'vcmi/beta' into 'vcmi/master'

This commit is contained in:
Ivan Savenko 2023-09-14 15:30:14 +03:00
commit 09d90993fd
233 changed files with 10428 additions and 1206 deletions

3
.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
*.json linguist-language=JSON-with-Comments
*.h linguist-language=C++
*.cpp linguist-language=C++

View File

@ -93,6 +93,12 @@ void CBattleAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::share
movesSkippedByDefense = 0;
}
void CBattleAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences)
{
initBattleInterface(ENV, CB);
autobattlePreferences = autocombatPreferences;
}
BattleAction CBattleAI::useHealingTent(const CStack *stack)
{
auto healingTargets = cb->battleGetStacks(CBattleInfoEssentials::ONLY_MINE);
@ -283,7 +289,8 @@ void CBattleAI::activeStack( const CStack * stack )
return;
}
attemptCastingSpell();
if(autobattlePreferences.enableSpellsUsage)
attemptCastingSpell();
logAi->trace("Spellcast attempt completed in %lld", timeElapsed(start));

View File

@ -68,6 +68,7 @@ public:
~CBattleAI();
void initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB) override;
void initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences) override;
void attemptCastingSpell();
void evaluateCreatureSpellcast(const CStack * stack, PossibleSpellcast & ps); //for offensive damaging spells only
@ -102,4 +103,5 @@ public:
private:
BattleAction goTowardsNearest(const CStack * stack, std::vector<BattleHex> hexes) const;
std::vector<BattleHex> getBrokenWallMoatHexes() const;
AutocombatPreferences autobattlePreferences = AutocombatPreferences();
};

View File

@ -47,6 +47,11 @@ void CStupidAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::share
CB->unlockGsWhenWaiting = false;
}
void CStupidAI::initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences)
{
initBattleInterface(ENV, CB);
}
void CStupidAI::actionFinished(const BattleAction &action)
{
print("actionFinished called");

View File

@ -29,6 +29,7 @@ public:
~CStupidAI();
void initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB) override;
void initBattleInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CBattleCallback> CB, AutocombatPreferences autocombatPreferences) override;
void actionFinished(const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero
void actionStarted(const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero
void activeStack(const CStack * stack) override; //called when it's turn of that stack

View File

@ -6,10 +6,10 @@ sudo update-alternatives --set x86_64-w64-mingw32-g++ /usr/bin/x86_64-w64-mingw3
# Workaround for getting new MinGW headers on Ubuntu 22.04.
# Remove it once MinGW headers version in repository will be 10.0 at least
curl -O -L http://mirrors.kernel.org/ubuntu/pool/universe/m/mingw-w64/mingw-w64-common_10.0.0-2_all.deb \
&& sudo dpkg -i mingw-w64-common_10.0.0-2_all.deb;
curl -O -L http://mirrors.kernel.org/ubuntu/pool/universe/m/mingw-w64/mingw-w64-x86-64-dev_10.0.0-2_all.deb \
&& sudo dpkg -i mingw-w64-x86-64-dev_10.0.0-2_all.deb;
curl -O -L http://mirrors.kernel.org/ubuntu/pool/universe/m/mingw-w64/mingw-w64-common_10.0.0-3_all.deb \
&& sudo dpkg -i mingw-w64-common_10.0.0-3_all.deb;
curl -O -L http://mirrors.kernel.org/ubuntu/pool/universe/m/mingw-w64/mingw-w64-x86-64-dev_10.0.0-3_all.deb \
&& sudo dpkg -i mingw-w64-x86-64-dev_10.0.0-3_all.deb;
mkdir ~/.conan ; cd ~/.conan
curl -L "https://github.com/vcmi/vcmi-deps-windows-conan/releases/download/1.0/vcmi-deps-windows-conan-w64.tgz" \

View File

@ -1,3 +1,65 @@
# 1.3.1 -> 1.3.2
### GENERAL
* VCMI now uses new application icon
* Added initial version of Czech translation
* Game will now use tile hero is moving from for movement cost calculations, in line with H3
* Added option to open hero backpack window in hero screen
* Added detection of misclicks for touch inputs to make hitting small UI elements easier
* Hero commander will now gain option to learn perks on reaching master level in corresponding abilities
* It is no longer possible to stop movement while moving over water with Water Walk
* Game will now automatically update hero path if it was blocked by another hero
* Added "vcmiartifacts angelWings" form to "give artifacts" cheat
### STABILITY
* Fixed freeze in Launcher on repository checkout and on mod install
* Fixed crash on loading VCMI map with placed Abandoned Mine
* Fixed crash on loading VCMI map with neutral towns
* Fixed crash on attempting to visit unknown object, such as Market of Time
* Fixed crash on attempting to teleport unit that is immune to a spell
* Fixed crash on switching fullscreen mode during AI turn
### CAMPAIGNS
* Fixed reorderging of hero primary skills after moving to next scenario in campaigns
### BATTLES
* Conquering a town will now correctly award additional 500 experience points
* Quick combat is now enabled by default
* Fixed invisible creatures from SUMMON_GUARDIANS and TRANSMUTATION bonuses
* Added option to toggle spell usage by AI in quick combat
* Fixed updating of spell point of enemy hero in game interface after spell cast
* Fixed wrong creature spellcasting shortcut (now set to "F")
* It is now possible to perform melee attack by creatures with spells, especially area spells
* Right-click will now properly end spellcast mode
* Fixed cursor preview when casting spell using touchscreen
* Long tap during spell casting will now properly abort the spell
### INTERFACE
* Added "Fill all empty slots with 1 creature" option to radial wheel in garrison windows
* Context popup for adventure map monsters will now show creature icon
* Game will now show correct victory message for gather troops victory condition
* Fixed incorrect display of number of owned Sawmills in Kingdom Overview window
* Fixed incorrect color of resource bar in hotseat mode
* Fixed broken toggle map level button in world view mode
* Fixed corrupted interface after opening puzzle window from world view mode
* Fixed blocked interface after attempt to start invalid map
* Add yellow border to selected commander grandmaster ability
* Always use bonus description for commander abilities instead of not provided wog-specific translation
* Fix scrolling when commander has large number of grandmaster abilities
* Fixed corrupted message on another player defeat
* Fixed unavailable Quest Log button on maps with quests
* Fixed incorrect values on a difficulty selector in save load screen
* Removed invalid error message on attempting to move non-existing unit in exchange window
### RANDOM MAP GENERATOR
* Fixed bug leading to unreachable resources around mines
### MAP EDITOR
* Fixed crash on maps containing abandoned mines
* Fixed crash on maps containing neutral objects
* Fixed problem with random map initialized in map editor
* Fixed problem with initialization of random dwellings
# 1.3.0 -> 1.3.1
### GENERAL:
@ -430,8 +492,8 @@
### MODDING:
* All configurable objects from H3 now have their configuration in json
* Improvements to functionality of configurable objects
* Replaced `SECONDARY_SKILL_PREMY` bonus with separate bonuses for each skill. See https://wiki.vcmi.eu/List_of_all_bonus_types
* Removed multiple bonuses that can be replaced with another bonus. See https://wiki.vcmi.eu/List_of_all_bonus_types
* Replaced `SECONDARY_SKILL_PREMY` bonus with separate bonuses for each skill.
* Removed multiple bonuses that can be replaced with another bonus.
* It is now possible to define new hero movement sounds in terrains
* Implemented translation support for mods
* Implemented translation support for .h3m maps and .h3c campaigns
@ -756,7 +818,7 @@
* Wall hit/miss sound will be played when using catapult during siege
### SPELLS:
* New configuration format: http://wiki.vcmi.eu/index.php?title=Spell_Format
* New configuration format
### RANDOM MAP GENERATOR:
* Towns from mods can be used
@ -789,7 +851,6 @@
* (linux) Added a SIGSEV violation handler to vcmiserver executable for logging stacktrace (for convenience)
### ADVENTURE AI:
More info at http://wiki.vcmi.eu/index.php?title=Adventure_AI
* AI will use fuzzy logic to compare and choose multiple possible subgoals.
* AI will now use SectorMap to find a way to guarded / covered objects.
* Significantly improved exploration algorithm.
@ -812,7 +873,6 @@ More info at http://wiki.vcmi.eu/index.php?title=Adventure_AI
* Extended building dependencies support
### MODS:
* See http://wiki.vcmi.eu/index.php?title=Modding_changelog#0.94_-.3E_0.95 for format changes
* Custom victory/loss conditions for maps or campaigns
* 7 days without towns loss condition is no longer hardcoded
* Only changed mods will be validated
@ -870,7 +930,6 @@ More info at http://wiki.vcmi.eu/index.php?title=Adventure_AI
* Fixed crash at month of double population.
### MODS:
* See http://wiki.vcmi.eu/index.php?title=Modding_changelog#0.92_-.3E_0.93 for compatibility info.
* Improved json validation. Now it support most of features from latest json schema draft.
* Icons use path to icon instead of image indexes.
* It is possible to edit data of another mod or H3 data via mods.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 934 B

After

Width:  |  Height:  |  Size: 942 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 914 B

After

Width:  |  Height:  |  Size: 927 B

View File

@ -0,0 +1,8 @@
{
"basepath" : "buttons/",
"images" :
[
{ "frame" : 0, "file" : "backpackNormal.png"},
{ "frame" : 1, "file" : "backpackPressed.png"}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,370 @@
{
"vcmi.adventureMap.monsterThreat.title" : "\n\nHrozba: ",
"vcmi.adventureMap.monsterThreat.levels.0" : "Bez námahy",
"vcmi.adventureMap.monsterThreat.levels.1" : "Velmi slabá",
"vcmi.adventureMap.monsterThreat.levels.2" : "Slabá",
"vcmi.adventureMap.monsterThreat.levels.3" : "Trochu slabší",
"vcmi.adventureMap.monsterThreat.levels.4" : "Podobná",
"vcmi.adventureMap.monsterThreat.levels.5" : "Trochu silnější",
"vcmi.adventureMap.monsterThreat.levels.6" : "Silná",
"vcmi.adventureMap.monsterThreat.levels.7" : "Velmi silná",
"vcmi.adventureMap.monsterThreat.levels.8" : "Výzva",
"vcmi.adventureMap.monsterThreat.levels.9" : "Převažující",
"vcmi.adventureMap.monsterThreat.levels.10" : "Smrtelná",
"vcmi.adventureMap.monsterThreat.levels.11" : "Nemožná",
"vcmi.adventureMap.confirmRestartGame" : "Jste si jisti, že chcete restartovat hru?",
"vcmi.adventureMap.noTownWithMarket" : "Nejsou dostupná jakákoliv tržiště!",
"vcmi.adventureMap.noTownWithTavern" : "Nejsou dostupná jakákoliv města s krčmou!",
"vcmi.adventureMap.spellUnknownProblem" : "Neznámý problém s tímto kouzlem! Další informace nejsou k dispozici.",
"vcmi.adventureMap.playerAttacked" : "Hráč byl napaden: %s",
"vcmi.adventureMap.moveCostDetails" : "Body pohybu - Cena: %TURNS tahů + %POINTS bodů, zbylé body: %REMAINING",
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Body pohybu - Cena: %POINTS bodů, zbylé body: %REMAINING",
"vcmi.capitalColors.0" : "Červený",
"vcmi.capitalColors.1" : "Modrý",
"vcmi.capitalColors.2" : "Hnědý",
"vcmi.capitalColors.3" : "Zelený",
"vcmi.capitalColors.4" : "Oranžový",
"vcmi.capitalColors.5" : "Fialový",
"vcmi.capitalColors.6" : "Tyrkysový",
"vcmi.capitalColors.7" : "Růžový",
"vcmi.radialWheel.mergeSameUnit" : "Sloučit stejné jednotky",
"vcmi.radialWheel.fillSingleUnit" : "Vyplnit jednou jednotkou",
"vcmi.radialWheel.splitSingleUnit" : "Rozdělit jedinou jednotku",
"vcmi.radialWheel.splitUnitEqually" : "Rovnoměrně rozdělit jednotky",
"vcmi.radialWheel.moveUnit" : "Přesunout jednotky do jiného oddílu",
"vcmi.radialWheel.splitUnit" : "Rozdělit jednotku do jiné pozice",
"vcmi.mainMenu.highscoresNotImplemented" : "Omlouvám se, menu nejvyšší skóre ještě není implementováno\n",
"vcmi.mainMenu.serverConnecting" : "Připojování...",
"vcmi.mainMenu.serverAddressEnter" : "Zadejte adresu:",
"vcmi.mainMenu.serverClosing" : "Zavírání...",
"vcmi.mainMenu.hostTCP" : "Pořádat hru TCP/IP",
"vcmi.mainMenu.joinTCP" : "Připojit se do hry TCP/IP",
"vcmi.mainMenu.playerName" : "Hráč",
"vcmi.lobby.filename" : "Název souboru",
"vcmi.lobby.creationDate" : "Datum vytvoření",
"vcmi.server.errors.existingProcess" : "Již běží jiný server VCMI. Prosím, ukončete ho před startem nové hry.",
"vcmi.server.errors.modsIncompatibility" : "Následující modifikace jsou nutné pro načtení hry:",
"vcmi.server.confirmReconnect" : "Chcete se připojit k poslední relaci?",
"vcmi.settingsMainWindow.generalTab.hover" : "Obecné",
"vcmi.settingsMainWindow.generalTab.help" : "Přepne na kartu obecných nastavení, která obsahuje nastavení související s obecným chováním klienta hry",
"vcmi.settingsMainWindow.battleTab.hover" : "Bitva",
"vcmi.settingsMainWindow.battleTab.help" : "Přepne na kartu nastavení bitvy, která umožňuje konfiguraci chování hry v bitvách",
"vcmi.settingsMainWindow.adventureTab.hover" : "Mapa světa",
"vcmi.settingsMainWindow.adventureTab.help" : "Přepne na kartu nastavení mapy světa (mapa světa je sekce hry, ve které hráči mohou ovládat pohyb hrdinů)",
"vcmi.systemOptions.videoGroup" : "Nastavení obrazu",
"vcmi.systemOptions.audioGroup" : "Nastavení zvuku",
"vcmi.systemOptions.otherGroup" : "Ostatní nastavení", // unused right now
"vcmi.systemOptions.townsGroup" : "Obrazovka města",
"vcmi.systemOptions.fullscreenBorderless.hover" : "Celá obrazovka (bez okrajů)",
"vcmi.systemOptions.fullscreenBorderless.help" : "{Celá obrazovka bez okrajů}\n\nPokud je vybráno, VCMI poběží v režimu celé obrazovky bez okrajů. V tomto režimu bude hra respektovat systémové rozlišení a ignorovat vybrané rozlišení ve hře.",
"vcmi.systemOptions.fullscreenExclusive.hover" : "Celá obrazovka (exkluzivní)",
"vcmi.systemOptions.fullscreenExclusive.help" : "{Celá obrazovka}\n\nPokud je vybráno, VCMI poběží v režimu exkluzivní celé obrazovky. V tomto režimu hra změní rozlišení na vybrané.",
"vcmi.systemOptions.resolutionButton.hover" : "Rozlišení: %wx%h",
"vcmi.systemOptions.resolutionButton.help" : "{Vybrat rozlišení}\n\nZmění rozlišení herní obrazovky.",
"vcmi.systemOptions.resolutionMenu.hover" : "Vybrat rozlišení",
"vcmi.systemOptions.resolutionMenu.help" : "Změnit rozlišení herní obrazovky.",
"vcmi.systemOptions.scalingButton.hover" : "Škálování rozhraní: %p%",
"vcmi.systemOptions.scalingButton.help" : "{Škálování rozhraní}\n\nZmění škálování herního rozhraní",
"vcmi.systemOptions.scalingMenu.hover" : "Vybrat škálování rozhraní",
"vcmi.systemOptions.scalingMenu.help" : "Změní škálování herního rozhraní.",
"vcmi.systemOptions.longTouchButton.hover" : "Doba dlouhého podržení: %d ms", // Translation note: "ms" = "milliseconds"
"vcmi.systemOptions.longTouchButton.help" : "{Doba dlouhého podržení}\n\nPři používání dotykové obrazovky budou zobrazeno vyskakovací okno při podržení prstu na obrazovce, v milisekundách",
"vcmi.systemOptions.longTouchMenu.hover" : "Vybrat dobu dlouhého podržení",
"vcmi.systemOptions.longTouchMenu.help" : "Změnit dobu dlouhého podržení.",
"vcmi.systemOptions.longTouchMenu.entry" : "%d milisekund",
"vcmi.systemOptions.framerateButton.hover" : "Zobrazit FPS",
"vcmi.systemOptions.framerateButton.help" : "{Zobrazit FPS}\n\nPřepne viditelnost počitadla snímků za sekundu v rohu obrazovky hry",
"vcmi.systemOptions.hapticFeedbackButton.hover" : "Vibrace",
"vcmi.systemOptions.hapticFeedbackButton.help" : "{Vibrace}\n\nPřepnout stav vibrací při dotykovém ovládání",
"vcmi.adventureOptions.infoBarPick.hover" : "Zobrazit zprávy v panelu informací",
"vcmi.adventureOptions.infoBarPick.help" : "{Zobrazit zprávy v panelu informací}\n\nKdyž bude možné, herní zprávy z návštěv míst na mapě budou zobrazeny v panelu informací místo ve zvláštním okně.",
"vcmi.adventureOptions.numericQuantities.hover" : "Číselné množství jednotek",
"vcmi.adventureOptions.numericQuantities.help" : "{Číselné množství jednotek}\n\nZobrazit přibližné množství nepřátelských jednotek ve formátu A-B.",
"vcmi.adventureOptions.forceMovementInfo.hover" : "Vždy zobrazit cenu pohybu",
"vcmi.adventureOptions.forceMovementInfo.help" : "{Vždy zobrazit cenu pohybu}\n\nVždy zobrazit informace o bodech pohybu v panelu informací. (Místo zobrazení pouze při stisknuté klávese ALT)",
"vcmi.adventureOptions.showGrid.hover" : "Zobrazit mřížku",
"vcmi.adventureOptions.showGrid.help" : "{Zobrazit mřížku}\n\nZobrazit překrytí mřížkou, zvýrazňuje hranice mezi dlaždicemi mapy světa.",
"vcmi.adventureOptions.borderScroll.hover" : "Posouvání okraji",
"vcmi.adventureOptions.borderScroll.help" : "{Posouvání okraji}\n\nPosouvat mapu světa, když je kurzor na okraji obrazovky. Může být zakázáno držením klávesy CTRL.",
"vcmi.adventureOptions.infoBarCreatureManagement.hover" : "Info Panel Creature Management", //TODO
"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": "",
"vcmi.adventureOptions.mapScrollSpeed1.help": "Nastavit posouvání mapy na velmi pomalé",
"vcmi.adventureOptions.mapScrollSpeed5.help": "Nastavit posouvání mapy na velmi rychlé",
"vcmi.adventureOptions.mapScrollSpeed6.help": "Nastavit posouvání mapy na okamžité",
"vcmi.battleOptions.queueSizeLabel.hover": "Zobrazit frontu pořadí tahů",
"vcmi.battleOptions.queueSizeNoneButton.hover": "VYPNUTO",
"vcmi.battleOptions.queueSizeAutoButton.hover": "AUTO",
"vcmi.battleOptions.queueSizeSmallButton.hover": "MALÁ",
"vcmi.battleOptions.queueSizeBigButton.hover": "VELKÁ",
"vcmi.battleOptions.queueSizeNoneButton.help": "Nezobrazovat frontu pořadí tahů",
"vcmi.battleOptions.queueSizeAutoButton.help": "Nastavit automaticky velikost fronty pořadí tahů podle rozlišení obrazovky hry (Při výšce herního rozlišení menší než 700 pixelů je použita velikost MALÁ, jinak velikost VELKÁ)",
"vcmi.battleOptions.queueSizeSmallButton.help": "Zobrazit malou frontu pořadí tahů",
"vcmi.battleOptions.queueSizeBigButton.help": "Zobrazit velkou frontu pořadí tahů (není podporováno, pokud výška rozlišení hry není alespoň 700 pixelů)",
"vcmi.battleOptions.animationsSpeed1.hover": "",
"vcmi.battleOptions.animationsSpeed5.hover": "",
"vcmi.battleOptions.animationsSpeed6.hover": "",
"vcmi.battleOptions.animationsSpeed1.help": "Nastavit rychlost animací na velmi pomalé",
"vcmi.battleOptions.animationsSpeed5.help": "Nastavit rychlost animací na velmi rychlé",
"vcmi.battleOptions.animationsSpeed6.help": "Nastavit rychlost animací na okamžité",
"vcmi.battleOptions.movementHighlightOnHover.hover": "Zvýraznění pohybu při najetí",
"vcmi.battleOptions.movementHighlightOnHover.help": "{Zvýraznění pohybu při najetí}\n\nZvýraznit rozsah pohybu jednotky při najetí na něj.",
"vcmi.battleOptions.rangeLimitHighlightOnHover.hover": "Zobrazit omezení dostřelu střelců",
"vcmi.battleOptions.rangeLimitHighlightOnHover.help": "{Zobrazit omezení dostřelu střelců při najetí}\n\nZobrazit dostřel střelce při najetí na něj.",
"vcmi.battleOptions.showStickyHeroInfoWindows.hover": "Zobrazit okno statistik hrdinů",
"vcmi.battleOptions.showStickyHeroInfoWindows.help": "{Zobrazit okno statistik hrdinů}\n\nTrvale zapne okno statistiky hrdinů, které ukazuje hlavní schopnosti a magickou energii.",
"vcmi.battleOptions.skipBattleIntroMusic.hover": "Přeskočit úvodní hudbu",
"vcmi.battleOptions.skipBattleIntroMusic.help": "{Přeskočit úvodní hudbu}\n\nPovolí akce při úvodní hudbě přehrávané při začátku každé bitvy.",
"vcmi.battleWindow.pressKeyToSkipIntro" : "Stiskněte jakoukoliv klávesu pro okamžité zahájení bitvy",
"vcmi.battleWindow.damageEstimation.melee" : "Zaútočit na %CREATURE (%DAMAGE).",
"vcmi.battleWindow.damageEstimation.meleeKills" : "Zaútočit na %CREATURE (%DAMAGE, %KILLS).",
"vcmi.battleWindow.damageEstimation.ranged" : "Vystřelit na %CREATURE (%SHOTS, %DAMAGE).",
"vcmi.battleWindow.damageEstimation.rangedKills" : "Vystřelit na %CREATURE (%SHOTS, %DAMAGE, %KILLS).",
"vcmi.battleWindow.damageEstimation.shots" : "%d střel zbývá",
"vcmi.battleWindow.damageEstimation.shots.1" : "%d střela zbývá",
"vcmi.battleWindow.damageEstimation.damage" : "%d poškození",
"vcmi.battleWindow.damageEstimation.damage.1" : "%d poškození",
"vcmi.battleWindow.damageEstimation.kills" : "%d zahyne",
"vcmi.battleWindow.damageEstimation.kills.1" : "%d zahyne",
"vcmi.battleResultsWindow.applyResultsLabel" : "Použít výsledek bitvy",
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "Zobrazit dostupné jednotky",
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{Zobrazit dostupné jednotky}\n\nZobrazit počet jednotek dostupných ke koupení místo jejich týdenního přírůstku v přehledu města. (levý spodní okraj obrazovky města).",
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.hover" : "Zobrazit týdenní přírůstek jednotek",
"vcmi.otherOptions.creatureGrowthAsDwellingLabel.help" : "{Zobrazit týdenní přírůstek jednotek}\n\nZobrazit týdenní přírůstek jednotek místo dostupného počtu ke koupení v přehledu města (levý spodní okraj obrazovky města).",
"vcmi.otherOptions.compactTownCreatureInfo.hover": "Kompaktní informace o jednotkách",
"vcmi.otherOptions.compactTownCreatureInfo.help": "{Kompaktní informace o jednotkách}\n\nZobrazit menší informace o jednotkách města v jeho přehledu (levý spodní okraj obrazovky města).",
"vcmi.townHall.missingBase" : "Základní budova %s musí být postavena jako první",
"vcmi.townHall.noCreaturesToRecruit" : "Žádné jednotky k vycvičení!", //TODO
"vcmi.townHall.greetingManaVortex" : "As you near the %s your body is filled with new energy. You have doubled your normal spell points.",
"vcmi.townHall.greetingKnowledge" : "You study the glyphs on the %s and gain insight into the workings of various magics (+1 Knowledge).",
"vcmi.townHall.greetingSpellPower" : "%s vás učí nové cesty zaměření vaší magické síly (+1 Síla kouzel).",
"vcmi.townHall.greetingExperience" : "Návštěva %s vás naučila spoustu nových dovedností (+1000 zkušeností).",
"vcmi.townHall.greetingAttack" : "Čas strávený poblíž místa zvaného %s allows you to learn more effective combat skills (+1 Attack Skill).",
"vcmi.townHall.greetingDefence" : "Spending time in the %s, the experienced warriors therein teach you additional defensive skills (+1 Defense).",
"vcmi.townHall.hasNotProduced" : "%s zatím nic nevyrobil.",
"vcmi.townHall.hasProduced" : "%s vyrobil %d %s tento týden.",
"vcmi.townHall.greetingCustomBonus" : "%s vám dává +%d %s%s",
"vcmi.townHall.greetingCustomUntil" : " do další bitvy.",
"vcmi.townHall.greetingInTownMagicWell" : "%s obnovil na maximum vaši magickou energii.",
"vcmi.logicalExpressions.anyOf" : "Něco z následujících:",
"vcmi.logicalExpressions.allOf" : "Všechny následující:",
"vcmi.logicalExpressions.noneOf" : "Žádné z následujících:",
"vcmi.heroWindow.openCommander.hover" : "Open commander info window",
"vcmi.heroWindow.openCommander.help" : "Shows details about the commander of this hero",
"vcmi.heroWindow.openBackpack.hover" : "Open artifact backpack window",
"vcmi.heroWindow.openBackpack.help" : "Opens window that allows easier artifact backpack management",
"vcmi.commanderWindow.artifactMessage" : "Do you want to return this artifact to the hero?",
"vcmi.creatureWindow.showBonuses.hover" : "Switch to bonuses view",
"vcmi.creatureWindow.showBonuses.help" : "Display all active bonuses of the commander",
"vcmi.creatureWindow.showSkills.hover" : "Switch to skills view",
"vcmi.creatureWindow.showSkills.help" : "Display all learned skills of the commander",
"vcmi.creatureWindow.returnArtifact.hover" : "Vrátit artefakt",
"vcmi.creatureWindow.returnArtifact.help" : "Klikněte na toto tlačítko pro navrácení artefaktů do hrdinova batohu",
"vcmi.questLog.hideComplete.hover" : "Hide complete quests",
"vcmi.questLog.hideComplete.help" : "Hide all completed quests",
"vcmi.randomMapTab.widgets.randomTemplate" : "(Random)",
"vcmi.randomMapTab.widgets.templateLabel" : "Šablona",
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Setup...",
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Team Alignments",
"vcmi.randomMapTab.widgets.roadTypesLabel" : "Druhy cest",
"vcmi.optionsTab.widgets.chessFieldBase.help" : "{Extra timer}\n\nStarts counting down when the {turn timer} reaches zero. It is set only once at the beginning of the game. When this timer reaches zero, the player's turn ends.",
"vcmi.optionsTab.widgets.chessFieldTurn.help" : "{Turn timer}\n\nStarts counting down when the player starts their turn on the adventure map. It is reset to its initial value at the start of each turn. Any unused turn time will be added to the {Extra timer} if it is in use.",
"vcmi.optionsTab.widgets.chessFieldBattle.help" : "{Battle timer}\n\nCounts down during battles when the {stack timer} reaches zero. It is reset to its initial value at the start of each battle. If the timer reaches zero, the currently active stack will defend.",
"vcmi.optionsTab.widgets.chessFieldCreature.help" : "{Stack timer}\n\nStarts counting down when the player is selecting an action for the current stack during battle. It resets to its initial value after the stack's action is completed.",
"vcmi.optionsTab.widgets.labelTimer" : "Časovač",
"vcmi.optionsTab.widgets.timerModeSwitch.classic" : "Klasický časovač",
"vcmi.optionsTab.widgets.timerModeSwitch.chess" : "Šachový časovač",
// Custom victory conditions for H3 campaigns and HotA maps
"vcmi.map.victoryCondition.daysPassed.toOthers" : "Nepřítel zvládl přežít do této chvíle. Vítězství je jeho!",
"vcmi.map.victoryCondition.daysPassed.toSelf" : "Gratulace! Zvládli jste přežít. Vítězství je vaše!",
"vcmi.map.victoryCondition.eliminateMonsters.toOthers" : "Nepřítel porazil všechny bojovníky zamořující tuto zemi a nárokuje si vítězství!",
"vcmi.map.victoryCondition.eliminateMonsters.toSelf" : "Gratulace! Porazili jste všechny nepřátele zamořující tuto zemi a můžete si nárokovat vítězství!",
"vcmi.map.victoryCondition.collectArtifacts.message" : "Získejte tři artefakty",
"vcmi.map.victoryCondition.angelicAlliance.toSelf" : "Gratulace! Všichni vaši nepřítelé byli poraženi a máte Andělskou alianci! Vítězství je vaše!",
"vcmi.map.victoryCondition.angelicAlliance.message" : "Porazte všechny nepřátele a vyrobte Andělskou alianci",
// few strings from WoG used by vcmi
"vcmi.stackExperience.description" : "» S t a c k E x p e r i e n c e D e t a i l s «\n\nCreature Type ................... : %s\nExperience Rank ................. : %s (%i)\nExperience Points ............... : %i\nExperience Points to Next Rank .. : %i\nMaximum Experience per Battle ... : %i%% (%i)\nNumber of Creatures in stack .... : %i\nMaximum New Recruits\n without losing current Rank .... : %i\nExperience Multiplier ........... : %.2f\nUpgrade Multiplier .............. : %.2f\nExperience after Rank 10 ........ : %i\nMaximum New Recruits to remain at\n Rank 10 if at Maximum Experience : %i",
"vcmi.stackExperience.rank.0" : "Basic",
"vcmi.stackExperience.rank.1" : "Novice",
"vcmi.stackExperience.rank.2" : "Trained",
"vcmi.stackExperience.rank.3" : "Skilled",
"vcmi.stackExperience.rank.4" : "Proven",
"vcmi.stackExperience.rank.5" : "Veteran",
"vcmi.stackExperience.rank.6" : "Adept",
"vcmi.stackExperience.rank.7" : "Expert",
"vcmi.stackExperience.rank.8" : "Elite",
"vcmi.stackExperience.rank.9" : "Master",
"vcmi.stackExperience.rank.10" : "Ace",
"core.bonus.ADDITIONAL_ATTACK.name": "Dvojitý úder",
"core.bonus.ADDITIONAL_ATTACK.description": "Útočí dvakrát",
"core.bonus.ADDITIONAL_RETALIATION.name": "Additional retaliations",
"core.bonus.ADDITIONAL_RETALIATION.description": "May retaliate ${val} extra times",
"core.bonus.AIR_IMMUNITY.name": "Vzdušná odolnost",
"core.bonus.AIR_IMMUNITY.description": "Immune to all spells from the school of Air magic",
"core.bonus.ATTACKS_ALL_ADJACENT.name": "Útok okolo",
"core.bonus.ATTACKS_ALL_ADJACENT.description": "Útočí na všechny sousední jednotky",
"core.bonus.BLOCKS_RETALIATION.name": "No retaliation",
"core.bonus.BLOCKS_RETALIATION.description": "Enemy cannot retaliate",
"core.bonus.BLOCKS_RANGED_RETALIATION.name": "No ranged retaliation",
"core.bonus.BLOCKS_RANGED_RETALIATION.description": "Enemy cannot retaliate by using a ranged attack",
"core.bonus.CATAPULT.name": "Katapult",
"core.bonus.CATAPULT.description": "Attacks siege walls",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reduce Casting Cost (${val})",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduces the spellcasting cost for the hero by ${val}",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Magic Damper (${val})",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Increases spellcasting cost of enemy spells by ${val}",
"core.bonus.CHARGE_IMMUNITY.name": "Immune to Charge",
"core.bonus.CHARGE_IMMUNITY.description": "Immune to Cavalier's and Champion's Charge",
"core.bonus.DARKNESS.name": "Darkness cover",
"core.bonus.DARKNESS.description": "Creates a shroud of darkness with a ${val} radius",
"core.bonus.DEATH_STARE.name": "Death Stare (${val}%)",
"core.bonus.DEATH_STARE.description": "Has a ${val}% chance to kill a single creature",
"core.bonus.DEFENSIVE_STANCE.name": "Defense Bonus",
"core.bonus.DEFENSIVE_STANCE.description": "+${val} Defense when defending",
"core.bonus.DESTRUCTION.name": "Destruction",
"core.bonus.DESTRUCTION.description": "Has ${val}% chance to kill extra units after attack",
"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Death Blow",
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "Has a ${val}% chance of dealing double base damage when attacking",
"core.bonus.DRAGON_NATURE.name": "Drak",
"core.bonus.DRAGON_NATURE.description": "Creature has a Dragon Nature",
"core.bonus.EARTH_IMMUNITY.name": "Zemní odolnost",
"core.bonus.EARTH_IMMUNITY.description": "Immune to all spells from the school of Earth magic",
"core.bonus.ENCHANTER.name": "Enchanter",
"core.bonus.ENCHANTER.description": "Can cast mass ${subtype.spell} every turn",
"core.bonus.ENCHANTED.name": "Enchanted",
"core.bonus.ENCHANTED.description": "Affected by permanent ${subtype.spell}",
"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Ignore Defense (${val}%)",
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "When attacking, ${val}% of the defender's defense is ignored",
"core.bonus.FIRE_IMMUNITY.name": "Ohnivá odolnost",
"core.bonus.FIRE_IMMUNITY.description": "Immune to all spells from the school of Fire magic",
"core.bonus.FIRE_SHIELD.name": "Ohnivý štít (${val}%)",
"core.bonus.FIRE_SHIELD.description": "Reflects part of melee damage",
"core.bonus.FIRST_STRIKE.name": "First Strike",
"core.bonus.FIRST_STRIKE.description": "This creature retaliates before being attacked",
"core.bonus.FEAR.name": "Fear",
"core.bonus.FEAR.description": "Causes Fear on an enemy stack",
"core.bonus.FEARLESS.name": "Nebojácnost",
"core.bonus.FEARLESS.description": "Odolnost proti strachu",
"core.bonus.FLYING.name": "Letec",
"core.bonus.FLYING.description": "Při pohybu létá (přes překážky)",
"core.bonus.FREE_SHOOTING.name": "Blízké výstřely",
"core.bonus.FREE_SHOOTING.description": "Může použít výstřely při útoku zblízka",
"core.bonus.GARGOYLE.name": "Gargoyle",
"core.bonus.GARGOYLE.description": "Cannot be raised or healed",
"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Reduce Damage (${val}%)",
"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Reduces physical damage from ranged or melee attacks",
"core.bonus.HATE.name": "Nesnáší ${subtype.creature}",
"core.bonus.HATE.description": "Does ${val}% more damage to ${subtype.creature}",
"core.bonus.HEALER.name": "Léčitel",
"core.bonus.HEALER.description": "Léčí spojenecké jednotky",
"core.bonus.HP_REGENERATION.name": "Regenerace",
"core.bonus.HP_REGENERATION.description": "Každé kolo léčí ${val} životů",
"core.bonus.JOUSTING.name": "Champion charge",
"core.bonus.JOUSTING.description": "+${val}% damage for each hex travelled",
"core.bonus.KING.name": "Král",
"core.bonus.KING.description": "Vulnerable to SLAYER level ${val} or higher",
"core.bonus.LEVEL_SPELL_IMMUNITY.name": "Spell immunity 1-${val}",
"core.bonus.LEVEL_SPELL_IMMUNITY.description": "Immune to spells of levels 1-${val}",
"core.bonus.LIMITED_SHOOTING_RANGE.name" : "Limited shooting range",
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "Unable to target units farther than ${val} hexes",
"core.bonus.LIFE_DRAIN.name": "Vysátí životů (${val}%)",
"core.bonus.LIFE_DRAIN.description": "Vysaje ${val}% uděleného poškození",
"core.bonus.MANA_CHANNELING.name": "Magic Channel ${val}%",
"core.bonus.MANA_CHANNELING.description": "Gives your hero ${val}% of the mana spent by the enemy",
"core.bonus.MANA_DRAIN.name": "Vysátí many",
"core.bonus.MANA_DRAIN.description": "Každé kolo vysaje ${val} many",
"core.bonus.MAGIC_MIRROR.name": "Kouzelné zrcadlo (${val}%)",
"core.bonus.MAGIC_MIRROR.description": "Has a ${val}% chance to redirect an offensive spell to an enemy unit",
"core.bonus.MAGIC_RESISTANCE.name": "Magic Resistance (${val}%)",
"core.bonus.MAGIC_RESISTANCE.description": "Has a ${val}% chance to resist an enemy spell",
"core.bonus.MIND_IMMUNITY.name": "Mind Spell Immunity",
"core.bonus.MIND_IMMUNITY.description": "Immune to Mind-type spells",
"core.bonus.NO_DISTANCE_PENALTY.name": "No distance penalty",
"core.bonus.NO_DISTANCE_PENALTY.description": "Does full damage at any distance",
"core.bonus.NO_MELEE_PENALTY.name": "No melee penalty",
"core.bonus.NO_MELEE_PENALTY.description": "Creature has no Melee Penalty",
"core.bonus.NO_MORALE.name": "Neutral Morale",
"core.bonus.NO_MORALE.description": "Creature is immune to morale effects",
"core.bonus.NO_WALL_PENALTY.name": "No wall penalty",
"core.bonus.NO_WALL_PENALTY.description": "Full damage during siege",
"core.bonus.NON_LIVING.name": "Non living",
"core.bonus.NON_LIVING.description": "Immunity to many effects",
"core.bonus.RANDOM_SPELLCASTER.name": "Random spellcaster",
"core.bonus.RANDOM_SPELLCASTER.description": "Can cast random spell",
"core.bonus.RANGED_RETALIATION.name": "Ranged retaliation",
"core.bonus.RANGED_RETALIATION.description": "Can perform ranged counterattack",
"core.bonus.RECEPTIVE.name": "Receptive",
"core.bonus.RECEPTIVE.description": "No Immunity to Friendly Spells",
"core.bonus.REBIRTH.name": "Rebirth (${val}%)",
"core.bonus.REBIRTH.description": "${val}% of stack will rise after death",
"core.bonus.RETURN_AFTER_STRIKE.name": "Attack and Return",
"core.bonus.RETURN_AFTER_STRIKE.description": "Returns after melee attack",
"core.bonus.SHOOTER.name": "Ranged",
"core.bonus.SHOOTER.description": "Creature can shoot",
"core.bonus.SHOOTS_ALL_ADJACENT.name": "Shoot all around",
"core.bonus.SHOOTS_ALL_ADJACENT.description": "This creature's ranged attacks strike all targets in a small area",
"core.bonus.SOUL_STEAL.name": "Soul Steal",
"core.bonus.SOUL_STEAL.description": "Gains ${val} new creatures for each enemy killed",
"core.bonus.SPELLCASTER.name": "Spellcaster",
"core.bonus.SPELLCASTER.description": "Can cast ${subtype.spell}",
"core.bonus.SPELL_AFTER_ATTACK.name": "Cast After Attack",
"core.bonus.SPELL_AFTER_ATTACK.description": "Has a ${val}% chance to cast ${subtype.spell} after it attacks",
"core.bonus.SPELL_BEFORE_ATTACK.name": "Cast Before Attack",
"core.bonus.SPELL_BEFORE_ATTACK.description": "Has a ${val}% chance to cast ${subtype.spell} before it attacks",
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "Spell Resistance",
"core.bonus.SPELL_DAMAGE_REDUCTION.description": "Damage from spells reduced by ${val}%.",
"core.bonus.SPELL_IMMUNITY.name": "Odolnost vůči kouzlům",
"core.bonus.SPELL_IMMUNITY.description": "Odolnost proti ${subtype.spell}",
"core.bonus.SPELL_LIKE_ATTACK.name": "Spell-like attack",
"core.bonus.SPELL_LIKE_ATTACK.description": "Attacks with ${subtype.spell}",
"core.bonus.SPELL_RESISTANCE_AURA.name": "Aura of Resistance",
"core.bonus.SPELL_RESISTANCE_AURA.description": "Nearby stacks get ${val}% magic resistance",
"core.bonus.SUMMON_GUARDIANS.name": "Summon guardians",
"core.bonus.SUMMON_GUARDIANS.description": "At the start of battle summons ${subtype.creature} (${val}%)",
"core.bonus.SYNERGY_TARGET.name": "Synergizable",
"core.bonus.SYNERGY_TARGET.description": "This creature is vulnerable to synergy effect",
"core.bonus.TWO_HEX_ATTACK_BREATH.name": "Breath",
"core.bonus.TWO_HEX_ATTACK_BREATH.description": "Breath Attack (2-hex range)",
"core.bonus.THREE_HEADED_ATTACK.name": "Three-headed attack",
"core.bonus.THREE_HEADED_ATTACK.description": "Attacks three adjacent units",
"core.bonus.TRANSMUTATION.name": "Transmutation",
"core.bonus.TRANSMUTATION.description": "${val}% chance to transform attacked unit to a different type",
"core.bonus.UNDEAD.name": "Nemrtvý",
"core.bonus.UNDEAD.description": "Jednotka je nemrtvá",
"core.bonus.UNLIMITED_RETALIATIONS.name": "Unlimited retaliations",
"core.bonus.UNLIMITED_RETALIATIONS.description": "Can retaliate against an unlimited number of attacks",
"core.bonus.WATER_IMMUNITY.name": "Water immunity",
"core.bonus.WATER_IMMUNITY.description": "Immune to all spells from the school of Water magic",
"core.bonus.WIDE_BREATH.name": "Wide breath",
"core.bonus.WIDE_BREATH.description": "Wide breath attack (multiple hexes)"
}

View File

@ -31,7 +31,7 @@
"vcmi.capitalColors.7" : "Pink",
"vcmi.radialWheel.mergeSameUnit" : "Merge same creatures",
"vcmi.radialWheel.showUnitInformation" : "Show creature information",
"vcmi.radialWheel.fillSingleUnit" : "Fill with single creatures",
"vcmi.radialWheel.splitSingleUnit" : "Split off single creature",
"vcmi.radialWheel.splitUnitEqually" : "Split creatures equally",
"vcmi.radialWheel.moveUnit" : "Move creatures to another army",
@ -173,6 +173,8 @@
"vcmi.heroWindow.openCommander.hover" : "Open commander info window",
"vcmi.heroWindow.openCommander.help" : "Shows details about the commander of this hero",
"vcmi.heroWindow.openBackpack.hover" : "Open artifact backpack window",
"vcmi.heroWindow.openBackpack.help" : "Opens window that allows easier artifact backpack management",
"vcmi.commanderWindow.artifactMessage" : "Do you want to return this artifact to the hero?",

View File

@ -31,7 +31,6 @@
"vcmi.capitalColors.7" : "Rosa",
"vcmi.radialWheel.mergeSameUnit" : "Gleiche Kreaturen zusammenführen",
"vcmi.radialWheel.showUnitInformation" : "Informationen zur Kreatur anzeigen",
"vcmi.radialWheel.splitSingleUnit" : "Wegtrennen einzelner Kreaturen",
"vcmi.radialWheel.splitUnitEqually" : "Gleichmäßiges trennen der Kreaturen",
"vcmi.radialWheel.moveUnit" : "Verschieben der Kreatur in andere Armee",

View File

@ -31,7 +31,6 @@
"vcmi.capitalColors.7" : "Różowy",
"vcmi.radialWheel.mergeSameUnit" : "Złącz takie same stworzenia",
"vcmi.radialWheel.showUnitInformation" : "Pokaż informacje o stworzeniu",
"vcmi.radialWheel.splitSingleUnit" : "Wydziel pojedyncze stworzenie",
"vcmi.radialWheel.splitUnitEqually" : "Podziel stworzenia równo",
"vcmi.radialWheel.moveUnit" : "Przenieś stworzenia do innej armii",
@ -173,6 +172,8 @@
"vcmi.heroWindow.openCommander.hover" : "Otwórz okno dowódcy",
"vcmi.heroWindow.openCommander.help" : "Wyświetla informacje o dowódcy przynależącym do tego bohatera",
"vcmi.heroWindow.openBackpack.hover" : "Otwórz okno sakwy",
"vcmi.heroWindow.openBackpack.help" : "Otwiera okno pozwalające łatwiej zarządzać artefaktami w sakwie",
"vcmi.commanderWindow.artifactMessage" : "Czy chcesz zwrócić ten artefakt bohaterowi?",

View File

@ -31,7 +31,7 @@
"vcmi.capitalColors.7" : "Рожевий",
"vcmi.radialWheel.mergeSameUnit" : "Об'єднати однакових істот",
"vcmi.radialWheel.showUnitInformation" : "Показати відомості про істоту",
"vcmi.radialWheel.fillSingleUnit" : "Заповнити одиничними істотами",
"vcmi.radialWheel.splitSingleUnit" : "Відділити одну істоту",
"vcmi.radialWheel.splitUnitEqually" : "Розділити істот порівну",
"vcmi.radialWheel.moveUnit" : "Перемістити істоту до іншої армії",
@ -173,6 +173,8 @@
"vcmi.heroWindow.openCommander.hover" : "Відкрити вікно командира",
"vcmi.heroWindow.openCommander.help" : "Показує інформацію про командира героя",
"vcmi.heroWindow.openBackpack.hover" : "Відкрити вікно рюкзака з артефактами",
"vcmi.heroWindow.openBackpack.help" : "Відкриває вікно, що дозволяє легше керувати рюкзаком артефактів",
"vcmi.commanderWindow.artifactMessage" : "Бажаєте передати цей артефакт герою?",

View File

@ -12,6 +12,16 @@
]
},
"czech" : {
"name" : "Nezbytné soubory VCMI",
"description" : "Nezbytné soubory pro správný běh VCMI",
"skipValidation" : true,
"translations" : [
"config/vcmi/czech.json"
]
},
"french" : {
"name" : "VCMI - fichiers nécessaires",
"description" : "Fichiers nécessaires pour exécuter correctement VCMI",

View File

@ -1,45 +0,0 @@
[![GitHub](https://github.com/vcmi/vcmi/actions/workflows/github.yml/badge.svg)](https://github.com/vcmi/vcmi/actions/workflows/github.yml)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/vcmi/badge.svg)](https://scan.coverity.com/projects/vcmi)
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.3.0/total)](https://github.com/vcmi/vcmi/releases/tag/1.3.0)
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.3.1/total)](https://github.com/vcmi/vcmi/releases/tag/1.3.1)
[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/total)](https://github.com/vcmi/vcmi/releases)
# VCMI Project
VCMI is work-in-progress attempt to recreate engine for Heroes III, giving it new and extended possibilities.
## Links
* Homepage: https://vcmi.eu/
* Wiki: https://wiki.vcmi.eu/
* Forums: https://forum.vcmi.eu/
* Bugtracker: https://github.com/vcmi/vcmi/issues
* Slack: https://slack.vcmi.eu/
* Discord: https://discord.gg/chBT42V
## Installation guides
To use VCMI you need to own original data files.
* [Android](https://wiki.vcmi.eu/Installation_on_Android)
* [Linux](https://wiki.vcmi.eu/Installation_on_Linux)
* [macOS](https://wiki.vcmi.eu/Installation_on_macOS)
* [Windows](https://wiki.vcmi.eu/Installation_on_Windows)
* [iOS](https://wiki.vcmi.eu/Installation_on_iOS)
## Building from source
Platform support is constantly tested by continuous integration and CMake configuration adjusted to generate nice looking projects for all major IDE. Following guides will help you to setup build environment with no effort:
* (optional) All platforms: [using Conan package manager to obtain prebuilt dependencies](docs/conan.md)
* [On Linux](https://wiki.vcmi.eu/How_to_build_VCMI_(Linux))
* [On Linux for Windows with Conan and mingw](https://wiki.vcmi.eu/How_to_build_VCMI_(Linux/Cmake/Conan))
* [On macOS](https://wiki.vcmi.eu/How_to_build_VCMI_(macOS))
* [On Windows using MSVC and Vcpkg](https://wiki.vcmi.eu/How_to_build_VCMI_(Windows/Vcpkg))
* [iOS on macOS](https://wiki.vcmi.eu/How_to_build_VCMI_(iOS))
* [Android on any OS](https://wiki.vcmi.eu/How_to_build_VCMI_(Android))
## Copyright and license
VCMI Project source code is licensed under GPL version 2 or later.
VCMI Project assets are licensed under CC-BY-SA 4.0. Assets sources and information about contributors are available under following link: [https://github.com/vcmi/vcmi-assets]
Copyright (C) 2007-2023 VCMI Team (check AUTHORS file for the contributors list)

View File

@ -10,7 +10,7 @@ android {
applicationId "is.xyz.vcmi"
minSdk 19
targetSdk 33
versionCode 1311
versionCode 1320
versionName "1.3.1"
setProperty("archivesBaseName", "vcmi")
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.2 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -3,7 +3,7 @@
<string name="url_project_page" translatable="false">https://vcmi.eu</string>
<string name="url_project_repo" translatable="false">https://github.com/vcmi/vcmi</string>
<string name="url_launcher_repo" translatable="false">https://github.com/vcmi/vcmi-android</string>
<string name="url_launcher_privacy" translatable="false">https://github.com/vcmi/vcmi/wiki/VCMI-App-Privacy-Policy</string>
<string name="url_launcher_privacy" translatable="false">https://github.com/vcmi/vcmi/blob/master/docs/players/Privacy_Policy.md</string>
<string name="app_name">VCMI</string>
<string name="server_name">VCMI Server</string>

View File

@ -457,6 +457,7 @@ endif()
if(NOT WIN32 AND NOT APPLE AND NOT ANDROID)
#FIXME: move to client makefile?
install(FILES "${CMAKE_SOURCE_DIR}/client/icons/vcmiclient.16x16.png" DESTINATION share/icons/hicolor/16x16/apps RENAME vcmiclient.png)
install(FILES "${CMAKE_SOURCE_DIR}/client/icons/vcmiclient.22x22.png" DESTINATION share/icons/hicolor/22x22/apps RENAME vcmiclient.png)
install(FILES "${CMAKE_SOURCE_DIR}/client/icons/vcmiclient.32x32.png" DESTINATION share/icons/hicolor/32x32/apps RENAME vcmiclient.png)
install(FILES "${CMAKE_SOURCE_DIR}/client/icons/vcmiclient.48x48.png" DESTINATION share/icons/hicolor/48x48/apps RENAME vcmiclient.png)
install(FILES "${CMAKE_SOURCE_DIR}/client/icons/vcmiclient.64x64.png" DESTINATION share/icons/hicolor/64x64/apps RENAME vcmiclient.png)

View File

@ -476,9 +476,6 @@ void CPlayerInterface::heroManaPointsChanged(const CGHeroInstance * hero)
adventureInt->onHeroChanged(hero);
if (makingTurn && hero->tempOwner == playerID)
adventureInt->onHeroChanged(hero);
for (auto window : GH.windows().findWindows<BattleWindow>())
window->heroManaPointsChanged(hero);
}
void CPlayerInterface::heroMovePointsChanged(const CGHeroInstance * hero)
{
@ -662,7 +659,11 @@ void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet
if ((replayAllowed && useQuickCombat) || forceQuickCombat)
{
autofightingAI = CDynLibHandler::getNewBattleAI(settings["server"]["friendlyAI"].String());
autofightingAI->initBattleInterface(env, cb);
AutocombatPreferences autocombatPreferences = AutocombatPreferences();
autocombatPreferences.enableSpellsUsage = settings["battle"]["enableAutocombatSpells"].Bool();
autofightingAI->initBattleInterface(env, cb, autocombatPreferences);
autofightingAI->battleStart(army1, army2, tile, hero1, hero2, side, false);
isAutoFightOn = true;
cb->registerBattleInterface(autofightingAI);
@ -897,6 +898,12 @@ void CPlayerInterface::battleTriggerEffect (const BattleTriggerEffect & bte)
RETURN_IF_QUICK_COMBAT;
battleInt->effectsController->battleTriggerEffect(bte);
if(bte.effect == vstd::to_underlying(BonusType::MANA_DRAIN))
{
const CGHeroInstance * manaDrainedHero = LOCPLINT->cb->getHero(ObjectInstanceID(bte.additionalInfo));
battleInt->windowObject->heroManaPointsChanged(manaDrainedHero);
}
}
void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, bool ranged)
{
@ -1611,15 +1618,6 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
if (GH.curInt == this)
GH.curInt = nullptr;
}
else
{
if (victoryLossCheckResult.loss() && cb->getPlayerStatus(playerID) == EPlayerStatus::INGAME) //enemy has lost
{
MetaString message = victoryLossCheckResult.messageToSelf;
message.appendLocalString(EMetaText::COLOR, player.getNum());
showInfoDialog(message.toString(), std::vector<std::shared_ptr<CComponent>>(1, std::make_shared<CComponent>(CComponent::flag, player.getNum(), 0)));
}
}
}
void CPlayerInterface::playerBonusChanged( const Bonus &bonus, bool gain )
@ -2009,13 +2007,13 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance * h, CGPath path)
auto canStop = [&](CGPathNode * node) -> bool
{
if (node->layer == EPathfindingLayer::LAND || node->layer == EPathfindingLayer::SAIL)
return true;
if (node->layer != EPathfindingLayer::LAND && node->layer != EPathfindingLayer::SAIL)
return false;
if (node->accessible == EPathAccessibility::ACCESSIBLE)
return true;
if (node->accessible != EPathAccessibility::ACCESSIBLE)
return false;
return false;
return true;
};
for (i=(int)path.nodes.size()-1; i>0 && (stillMoveHero.data == CONTINUE_MOVE || !canStop(&path.nodes[i])); i--)

View File

@ -573,17 +573,35 @@ void CServerHandler::sendRestartGame() const
sendLobbyPack(endGame);
}
void CServerHandler::sendStartGame(bool allowOnlyAI) const
bool CServerHandler::validateGameStart(bool allowOnlyAI) const
{
try
{
verifyStateBeforeStart(allowOnlyAI ? true : settings["session"]["onlyai"].Bool());
}
catch (const std::exception & e)
catch(CModHandler::Incompatibility & e)
{
showServerError( std::string("Unable to start map! Reason: ") + e.what());
return;
logGlobal->warn("Incompatibility exception during start scenario: %s", e.what());
auto errorMsg = CGI->generaltexth->translate("vcmi.server.errors.modsIncompatibility") + '\n';
errorMsg += e.what();
showServerError(errorMsg);
return false;
}
catch(std::exception & e)
{
logGlobal->error("Exception during startScenario: %s", e.what());
showServerError( std::string("Unable to start map! Reason: ") + e.what());
return false;
}
return true;
}
void CServerHandler::sendStartGame(bool allowOnlyAI) const
{
verifyStateBeforeStart(allowOnlyAI ? true : settings["session"]["onlyai"].Bool());
LobbyStartGame lsg;
if(client)
@ -708,7 +726,7 @@ void CServerHandler::startCampaignScenario(std::shared_ptr<CampaignState> cs)
});
}
void CServerHandler::showServerError(std::string txt) const
void CServerHandler::showServerError(const std::string & txt) const
{
CInfoWindow::showInfoDialog(txt, {});
}

View File

@ -148,16 +148,18 @@ public:
void sendRestartGame() const override;
void sendStartGame(bool allowOnlyAI = false) const override;
bool validateGameStart(bool allowOnlyAI = false) const;
void debugStartTest(std::string filename, bool save = false);
void startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameState = nullptr);
void endGameplay(bool closeConnection = true, bool restart = false);
void startCampaignScenario(std::shared_ptr<CampaignState> cs = {});
void showServerError(std::string txt) const;
void showServerError(const std::string & txt) const;
// TODO: LobbyState must be updated within game so we should always know how many player interfaces our client handle
int howManyPlayerInterfaces();
ui8 getLoadMode();
void debugStartTest(std::string filename, bool save = false);
void restoreLastSession();
void visitForLobby(CPackForLobby & lobbyPack);

View File

@ -17,7 +17,9 @@
#include "mapView/mapHandler.h"
#include "adventureMap/CInGameConsole.h"
#include "battle/BattleInterface.h"
#include "battle/BattleWindow.h"
#include "gui/CGuiHandler.h"
#include "gui/WindowHandler.h"
#include "widgets/MiscWidgets.h"
#include "CMT.h"
#include "CServerHandler.h"
@ -153,6 +155,9 @@ void ApplyClientNetPackVisitor::visitSetMana(SetMana & pack)
{
const CGHeroInstance *h = cl.getHero(pack.hid);
callInterfaceIfPresent(cl, h->tempOwner, &IGameEventsReceiver::heroManaPointsChanged, h);
for (auto window : GH.windows().findWindows<BattleWindow>())
window->heroManaPointsChanged(h);
}
void ApplyClientNetPackVisitor::visitSetMovePoints(SetMovePoints & pack)

View File

@ -102,7 +102,10 @@ void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyEndGame(LobbyEndGame & pack)
}
if(pack.restart)
handler.sendStartGame();
{
if (handler.validateGameStart())
handler.sendStartGame();
}
}
void ApplyOnLobbyHandlerNetPackVisitor::visitLobbyStartGame(LobbyStartGame & pack)

View File

@ -68,7 +68,10 @@ bool PlayerLocalState::setPath(const CGHeroInstance * h, const int3 & destinatio
{
CGPath path;
if(!owner.cb->getPathsInfo(h)->getPath(path, destination))
{
paths.erase(h); //invalidate previously possible path if selected (before other hero blocked only path / fly spell expired)
return false;
}
setPath(h, path);
return true;

View File

@ -397,12 +397,12 @@ void AdventureMapShortcuts::moveHeroDirectional(const Point & direction)
bool AdventureMapShortcuts::optionCanViewQuests()
{
return optionInMapView() && CGI->mh->getMap()->quests.empty();
return optionInMapView() && !CGI->mh->getMap()->quests.empty();
}
bool AdventureMapShortcuts::optionCanToggleLevel()
{
return optionInMapView() && LOCPLINT->cb->getMapSize().z > 1;
return optionSidePanelActive() && LOCPLINT->cb->getMapSize().z > 1;
}
bool AdventureMapShortcuts::optionMapLevelSurface()

View File

@ -372,11 +372,15 @@ void AdventureMapWidget::setPlayerChildren(CIntObject * widget, const PlayerColo
auto container = dynamic_cast<CAdventureMapContainerWidget *>(entry);
auto icon = dynamic_cast<CAdventureMapIcon *>(entry);
auto button = dynamic_cast<CButton *>(entry);
auto resDataBar = dynamic_cast<CResDataBar *>(entry);
auto texture = dynamic_cast<FilledTexturePlayerColored *>(entry);
if(button)
button->setPlayerColor(player);
if(resDataBar)
resDataBar->colorize(player);
if(icon)
icon->setPlayer(player);

View File

@ -24,6 +24,7 @@
#include "../gui/CIntObject.h"
#include "../gui/WindowHandler.h"
#include "../windows/CCreatureWindow.h"
#include "../windows/InfoWindows.h"
#include "../../CCallback.h"
#include "../../lib/CConfigHandler.h"
@ -215,7 +216,7 @@ std::vector<PossiblePlayerBattleAction> BattleActionsController::getPossibleActi
return std::vector<PossiblePlayerBattleAction>(allActions);
}
void BattleActionsController::reorderPossibleActionsPriority(const CStack * stack, MouseHoveredHexContext context)
void BattleActionsController::reorderPossibleActionsPriority(const CStack * stack, const CStack * targetStack)
{
if(owner.tacticsMode || possibleActions.empty()) return; //this function is not supposed to be called in tactics mode or before getPossibleActionsForStack
@ -229,10 +230,17 @@ void BattleActionsController::reorderPossibleActionsPriority(const CStack * stac
case PossiblePlayerBattleAction::NO_LOCATION:
case PossiblePlayerBattleAction::FREE_LOCATION:
case PossiblePlayerBattleAction::OBSTACLE:
if(!stack->hasBonusOfType(BonusType::NO_SPELLCAST_BY_DEFAULT) && context == MouseHoveredHexContext::OCCUPIED_HEX)
return 1;
else
return 100; //bottom priority
if(!stack->hasBonusOfType(BonusType::NO_SPELLCAST_BY_DEFAULT) && targetStack != nullptr)
{
PlayerColor stackOwner = owner.curInt->cb->battleGetOwner(targetStack);
bool enemyTargetingPositiveSpellcast = item.spell().toSpell()->isPositive() && stackOwner != LOCPLINT->playerID;
bool friendTargetingNegativeSpellcast = item.spell().toSpell()->isNegative() && stackOwner == LOCPLINT->playerID;
if(!enemyTargetingPositiveSpellcast && !friendTargetingNegativeSpellcast)
return 1;
}
return 100; //bottom priority
break;
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
return 2;
@ -600,10 +608,10 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
return false;
case PossiblePlayerBattleAction::ANY_LOCATION:
return isCastingPossibleHere(action.spell().toSpell(), targetStack, targetHex);
return isCastingPossibleHere(action.spell().toSpell(), nullptr, targetHex);
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
return !selectedStack && targetStack && isCastingPossibleHere(action.spell().toSpell(), targetStack, targetHex);
return !selectedStack && targetStack && isCastingPossibleHere(action.spell().toSpell(), nullptr, targetHex);
case PossiblePlayerBattleAction::RANDOM_GENIE_SPELL:
if(targetStack && targetStackOwned && targetStack != owner.stacksController->getActiveStack() && targetStack->alive()) //only positive spells for other allied creatures
@ -621,7 +629,7 @@ bool BattleActionsController::actionIsLegal(PossiblePlayerBattleAction action, B
case PossiblePlayerBattleAction::OBSTACLE:
case PossiblePlayerBattleAction::FREE_LOCATION:
return isCastingPossibleHere(action.spell().toSpell(), targetStack, targetHex);
return isCastingPossibleHere(action.spell().toSpell(), nullptr, targetHex);
case PossiblePlayerBattleAction::CATAPULT:
return owner.siegeController && owner.siegeController->isAttackableByCatapult(targetHex);
@ -788,7 +796,7 @@ PossiblePlayerBattleAction BattleActionsController::selectAction(BattleHex targe
const CStack * targetStack = getStackForHex(targetHex);
reorderPossibleActionsPriority(owner.stacksController->getActiveStack(), targetStack ? MouseHoveredHexContext::OCCUPIED_HEX : MouseHoveredHexContext::UNOCCUPIED_HEX);
reorderPossibleActionsPriority(owner.stacksController->getActiveStack(), targetStack);
for (PossiblePlayerBattleAction action : possibleActions)
{
@ -972,6 +980,11 @@ void BattleActionsController::activateStack()
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
actionsToSelect.push_back(possibleActions.front());
actionsToSelect.push_back(PossiblePlayerBattleAction::ATTACK);
break;
case PossiblePlayerBattleAction::ANY_LOCATION:
actionsToSelect.push_back(possibleActions.front());
actionsToSelect.push_back(PossiblePlayerBattleAction::ATTACK);
break;
}
}
@ -981,8 +994,19 @@ void BattleActionsController::activateStack()
void BattleActionsController::onHexRightClicked(BattleHex clickedHex)
{
if (spellcastingModeActive())
auto spellcastActionPredicate = [](PossiblePlayerBattleAction & action)
{
return action.spellcast();
};
bool isCurrentStackInSpellcastMode = std::all_of(possibleActions.begin(), possibleActions.end(), spellcastActionPredicate);
if (spellcastingModeActive() || isCurrentStackInSpellcastMode)
{
endCastingSpell();
CRClickPopup::createAndPush(CGI->generaltexth->translate("core.genrltxt.731")); // spell cancelled
return;
}
auto selectedStack = owner.curInt->cb->battleGetStackByPos(clickedHex, true);
@ -998,7 +1022,7 @@ void BattleActionsController::onHexRightClicked(BattleHex clickedHex)
bool BattleActionsController::spellcastingModeActive() const
{
return heroSpellToCast != nullptr;;
return heroSpellToCast != nullptr;
}
bool BattleActionsController::currentActionSpellcasting(BattleHex hoveredHex)

View File

@ -23,12 +23,6 @@ VCMI_LIB_NAMESPACE_END
class BattleInterface;
enum class MouseHoveredHexContext
{
UNOCCUPIED_HEX,
OCCUPIED_HEX
};
/// Class that controls actions that can be performed by player, e.g. moving stacks, attacking, etc
/// As well as all relevant feedback for these actions in user interface
class BattleActionsController
@ -53,7 +47,7 @@ class BattleActionsController
bool isCastingPossibleHere (const CSpell * spell, const CStack *shere, BattleHex myNumber);
bool canStackMoveHere (const CStack *sactive, BattleHex MyNumber) const; //TODO: move to BattleState / callback
std::vector<PossiblePlayerBattleAction> getPossibleActionsForStack (const CStack *stack) const; //called when stack gets its turn
void reorderPossibleActionsPriority(const CStack * stack, MouseHoveredHexContext context);
void reorderPossibleActionsPriority(const CStack * stack, const CStack * targetStack);
bool actionIsLegal(PossiblePlayerBattleAction action, BattleHex hoveredHex);

View File

@ -128,6 +128,9 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
attackCursors = std::make_shared<CAnimation>("CRCOMBAT");
attackCursors->preload();
spellCursors = std::make_shared<CAnimation>("CRSPELL");
spellCursors->preload();
initializeHexEdgeMaskToFrameIndex();
rangedFullDamageLimitImages = std::make_shared<CAnimation>("battle/rangeHighlights/rangeHighlightsGreen.json");
@ -888,10 +891,22 @@ void BattleFieldController::show(Canvas & to)
if (isActive() && isGesturing() && getHoveredHex() != BattleHex::INVALID)
{
auto cursorIndex = CCS->curh->get<Cursor::Combat>();
auto imageIndex = static_cast<size_t>(cursorIndex);
auto combatCursorIndex = CCS->curh->get<Cursor::Combat>();
if (combatCursorIndex)
{
auto combatImageIndex = static_cast<size_t>(*combatCursorIndex);
to.draw(attackCursors->getImage(combatImageIndex), hexPositionAbsolute(getHoveredHex()).center() - CCS->curh->getPivotOffsetCombat(combatImageIndex));
return;
}
auto spellCursorIndex = CCS->curh->get<Cursor::Spellcast>();
if (spellCursorIndex)
{
auto spellImageIndex = static_cast<size_t>(*spellCursorIndex);
to.draw(spellCursors->getImage(spellImageIndex), hexPositionAbsolute(getHoveredHex()).center() - CCS->curh->getPivotOffsetSpellcast());
return;
}
to.draw(attackCursors->getImage(imageIndex), hexPositionAbsolute(getHoveredHex()).center() - CCS->curh->getPivotOffsetCombat(imageIndex));
}
}

View File

@ -37,6 +37,7 @@ class BattleFieldController : public CIntObject
std::shared_ptr<CAnimation> shootingRangeLimitImages;
std::shared_ptr<CAnimation> attackCursors;
std::shared_ptr<CAnimation> spellCursors;
/// Canvas that contains background, hex grid (if enabled), absolute obstacles and movement range of active stack
std::unique_ptr<Canvas> backgroundWithHexes;

View File

@ -104,11 +104,9 @@ void BattleInterface::playIntroSoundAndUnlockInterface()
{
auto onIntroPlayed = [this]()
{
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
if(LOCPLINT->battleInt)
{
boost::unique_lock<boost::recursive_mutex> un(*CPlayerInterface::pim);
onIntroSoundPlayed();
}
};
int battleIntroSoundChannel = CCS->soundh->playSoundFromSet(CCS->soundh->battleIntroSounds);

View File

@ -450,6 +450,10 @@ void BattleWindow::showAlternativeActionIcon(PossiblePlayerBattleAction action)
case PossiblePlayerBattleAction::AIMED_SPELL_CREATURE:
iconName = variables["actionIconSpell"].String();
break;
case PossiblePlayerBattleAction::ANY_LOCATION:
iconName = variables["actionIconSpell"].String();
break;
//TODO: figure out purpose of this icon
//case PossiblePlayerBattleAction::???:
@ -500,7 +504,11 @@ void BattleWindow::bAutofightf()
blockUI(true);
auto ai = CDynLibHandler::getNewBattleAI(settings["server"]["friendlyAI"].String());
ai->initBattleInterface(owner.curInt->env, owner.curInt->cb);
AutocombatPreferences autocombatPreferences = AutocombatPreferences();
autocombatPreferences.enableSpellsUsage = settings["battle"]["enableAutocombatSpells"].Bool();
ai->initBattleInterface(owner.curInt->env, owner.curInt->cb, autocombatPreferences);
ai->battleStart(owner.army1, owner.army2, int3(0,0,0), owner.attackingHeroInstance, owner.defendingHeroInstance, owner.curInt->cb->battleGetMySide(), false);
owner.curInt->autofightingAI = ai;
owner.curInt->cb->registerBattleInterface(ai);

View File

@ -128,6 +128,7 @@ void InputHandler::preprocessEvent(const SDL_Event & ev)
#endif
else if(ev.type == SDL_KEYDOWN && ev.key.keysym.sym==SDLK_F4)
{
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
Settings full = settings.write["video"]["fullscreen"];
full->Bool() = !full->Bool();

View File

@ -50,10 +50,10 @@ void InputSourceMouse::handleEventMouseButtonDown(const SDL_MouseButtonEvent & b
if(button.clicks > 1)
GH.events().dispatchMouseDoubleClick(position);
else
GH.events().dispatchMouseLeftButtonPressed(position);
GH.events().dispatchMouseLeftButtonPressed(position, 0);
break;
case SDL_BUTTON_RIGHT:
GH.events().dispatchShowPopup(position);
GH.events().dispatchShowPopup(position, 0);
break;
case SDL_BUTTON_MIDDLE:
middleClickPosition = position;
@ -74,7 +74,7 @@ void InputSourceMouse::handleEventMouseButtonUp(const SDL_MouseButtonEvent & but
switch(button.button)
{
case SDL_BUTTON_LEFT:
GH.events().dispatchMouseLeftButtonReleased(position);
GH.events().dispatchMouseLeftButtonReleased(position, 0);
break;
case SDL_BUTTON_RIGHT:
GH.events().dispatchClosePopup(position);

View File

@ -39,6 +39,7 @@ InputSourceTouch::InputSourceTouch()
params.relativeModeSpeedFactor = settings["general"]["relativePointerSpeedMultiplier"].Float();
params.longTouchTimeMilliseconds = settings["general"]["longTouchTimeMilliseconds"].Float();
params.hapticFeedbackEnabled = settings["general"]["hapticFeedback"].Bool();
params.touchToleranceDistance = settings["input"]["touchToleranceDistance"].Float();
if (params.useRelativeMode)
state = TouchState::RELATIVE_MODE;
@ -121,9 +122,9 @@ void InputSourceTouch::handleEventFingerDown(const SDL_TouchFingerEvent & tfinge
if(tfinger.x > 0.5)
{
if (tfinger.y < 0.5)
GH.events().dispatchShowPopup(GH.getCursorPosition());
GH.events().dispatchShowPopup(GH.getCursorPosition(), params.touchToleranceDistance);
else
GH.events().dispatchMouseLeftButtonPressed(GH.getCursorPosition());
GH.events().dispatchMouseLeftButtonPressed(GH.getCursorPosition(), params.touchToleranceDistance);
}
break;
}
@ -168,7 +169,7 @@ void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
if (tfinger.y < 0.5)
GH.events().dispatchClosePopup(GH.getCursorPosition());
else
GH.events().dispatchMouseLeftButtonReleased(GH.getCursorPosition());
GH.events().dispatchMouseLeftButtonReleased(GH.getCursorPosition(), params.touchToleranceDistance);
}
break;
}
@ -180,8 +181,8 @@ void InputSourceTouch::handleEventFingerUp(const SDL_TouchFingerEvent & tfinger)
case TouchState::TAP_DOWN_SHORT:
{
GH.input().setCursorPosition(convertTouchToMouse(tfinger));
GH.events().dispatchMouseLeftButtonPressed(convertTouchToMouse(tfinger));
GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger));
GH.events().dispatchMouseLeftButtonPressed(convertTouchToMouse(tfinger), params.touchToleranceDistance);
GH.events().dispatchMouseLeftButtonReleased(convertTouchToMouse(tfinger), params.touchToleranceDistance);
state = TouchState::IDLE;
break;
}
@ -230,7 +231,7 @@ void InputSourceTouch::handleUpdate()
uint32_t currentTime = SDL_GetTicks();
if (currentTime > lastTapTimeTicks + params.longTouchTimeMilliseconds)
{
GH.events().dispatchShowPopup(GH.getCursorPosition());
GH.events().dispatchShowPopup(GH.getCursorPosition(), params.touchToleranceDistance);
if (GH.windows().isTopWindowPopup())
{

View File

@ -78,6 +78,9 @@ struct TouchInputParameters
/// gesture will be qualified as pinch if distance between fingers is at least specified here
uint32_t pinchSensitivityThreshold = 10;
/// touch event will trigger clicking of elements up to X pixels away from actual touch position
uint32_t touchToleranceDistance = 20;
bool useRelativeMode = false;
bool hapticFeedbackEnabled = false;

View File

@ -263,6 +263,11 @@ bool CIntObject::receiveEvent(const Point & position, int eventType) const
return pos.isInside(position);
}
const Rect & CIntObject::getPosition() const
{
return pos;
}
void CIntObject::onScreenResize()
{
center(pos, true);

View File

@ -109,6 +109,8 @@ public:
/// by default, usedEvents inside UI elements are always handled
bool receiveEvent(const Point & position, int eventType) const override;
const Rect & getPosition() const override;
const Rect & center(const Rect &r, bool propagate = true); //sets pos so that r will be in the center of screen, assigns sizes of r to pos, returns new position
const Rect & center(const Point &p, bool propagate = true); //moves object so that point p will be in its center
const Rect & center(bool propagate = true); //centers when pos.w and pos.h are set, returns new position

View File

@ -125,7 +125,6 @@ class CursorHandler final
void changeGraphic(Cursor::Type type, size_t index);
Point getPivotOffsetSpellcast();
Point getPivotOffset();
void updateSpellcastCursor();
@ -153,7 +152,7 @@ public:
/// Returns current index of cursor
template<typename Index>
Index get()
std::optional<Index> get()
{
bool typeValid = true;
@ -164,9 +163,10 @@ public:
if (typeValid)
return static_cast<Index>(frame);
return Index::POINTER;
return std::nullopt;
}
Point getPivotOffsetSpellcast();
Point getPivotOffsetDefault(size_t index);
Point getPivotOffsetMap(size_t index);
Point getPivotOffsetCombat(size_t index);

View File

@ -16,7 +16,7 @@
#include "MouseButton.h"
#include "WindowHandler.h"
#include "../../lib/Point.h"
#include "../../lib/Rect.h"
template<typename Functor>
void EventDispatcher::processLists(ui16 activityFlag, const Functor & cb)
@ -134,28 +134,64 @@ void EventDispatcher::dispatchMouseDoubleClick(const Point & position)
}
if(!doubleClicked)
handleLeftButtonClick(position, true);
handleLeftButtonClick(position, 0, true);
}
void EventDispatcher::dispatchMouseLeftButtonPressed(const Point & position)
void EventDispatcher::dispatchMouseLeftButtonPressed(const Point & position, int tolerance)
{
handleLeftButtonClick(position, true);
handleLeftButtonClick(position, tolerance, true);
}
void EventDispatcher::dispatchMouseLeftButtonReleased(const Point & position)
void EventDispatcher::dispatchMouseLeftButtonReleased(const Point & position, int tolerance)
{
handleLeftButtonClick(position, false);
handleLeftButtonClick(position, tolerance, false);
}
void EventDispatcher::dispatchShowPopup(const Point & position)
AEventsReceiver * EventDispatcher::findElementInToleranceRange(const EventReceiversList & list, const Point & position, int eventToTest, int tolerance)
{
AEventsReceiver * bestElement = nullptr;
int bestDistance = std::numeric_limits<int>::max();
for(auto & i : list)
{
// if there is element that can actually receive event then tolerance clicking is disabled
if( i->receiveEvent(position, eventToTest))
return nullptr;
if (i->getPosition().distanceTo(position) > bestDistance)
continue;
Point center = i->getPosition().center();
Point distance = center - position;
if (distance.lengthSquared() == 0)
continue;
Point moveDelta = distance * tolerance / distance.length();
Point testPosition = position + moveDelta;
if( !i->receiveEvent(testPosition, eventToTest))
continue;
bestElement = i;
bestDistance = i->getPosition().distanceTo(position);
}
return bestElement;
}
void EventDispatcher::dispatchShowPopup(const Point & position, int tolerance)
{
AEventsReceiver * nearestElement = findElementInToleranceRange(rclickable, position, AEventsReceiver::LCLICK, tolerance);
auto hlp = rclickable;
for(auto & i : hlp)
{
if(!vstd::contains(rclickable, i))
continue;
if( !i->receiveEvent(position, AEventsReceiver::LCLICK))
if( !i->receiveEvent(position, AEventsReceiver::SHOW_POPUP) && i != nearestElement)
continue;
i->showPopupWindow(position);
@ -170,7 +206,7 @@ void EventDispatcher::dispatchClosePopup(const Point & position)
assert(!GH.windows().isTopWindowPopup());
}
void EventDispatcher::handleLeftButtonClick(const Point & position, bool isPressed)
void EventDispatcher::handleLeftButtonClick(const Point & position, int tolerance, bool isPressed)
{
// WARNING: this approach is NOT SAFE
// 1) We allow (un)registering elements when list itself is being processed/iterated
@ -181,13 +217,15 @@ void EventDispatcher::handleLeftButtonClick(const Point & position, bool isPress
// 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
AEventsReceiver * nearestElement = findElementInToleranceRange(lclickable, position, AEventsReceiver::LCLICK, tolerance);
auto hlp = lclickable;
for(auto & i : hlp)
{
if(!vstd::contains(lclickable, i))
continue;
if( i->receiveEvent(position, AEventsReceiver::LCLICK))
if( i->receiveEvent(position, AEventsReceiver::LCLICK) || i == nearestElement)
{
if(isPressed)
i->clickPressed(position);

View File

@ -35,8 +35,8 @@ class EventDispatcher
EventReceiversList textInterested;
EventReceiversList panningInterested;
void handleLeftButtonClick(const Point & position, bool isPressed);
void handleLeftButtonClick(const Point & position, int tolerance, bool isPressed);
AEventsReceiver * findElementInToleranceRange(const EventReceiversList & list, const Point & position, int eventToTest, int tolerance);
template<typename Functor>
void processLists(ui16 activityFlag, const Functor & cb);
@ -56,15 +56,15 @@ public:
void dispatchShortcutReleased(const std::vector<EShortcut> & shortcuts);
/// Mouse events
void dispatchMouseLeftButtonPressed(const Point & position);
void dispatchMouseLeftButtonReleased(const Point & position);
void dispatchMouseLeftButtonPressed(const Point & position, int tolerance);
void dispatchMouseLeftButtonReleased(const Point & position, int tolerance);
void dispatchMouseScrolled(const Point & distance, const Point & position);
void dispatchMouseDoubleClick(const Point & position);
void dispatchMouseMoved(const Point & distance, const Point & position);
void dispatchMouseDragged(const Point & currentPosition, const Point & lastUpdateDistance);
void dispatchShowPopup(const Point & position);
void dispatchShowPopup(const Point & position, int tolerance);
void dispatchClosePopup(const Point & position);
void dispatchGesturePanningStarted(const Point & initialPosition);

View File

@ -11,6 +11,7 @@
VCMI_LIB_NAMESPACE_BEGIN
class Point;
class Rect;
VCMI_LIB_NAMESPACE_END
class EventDispatcher;
@ -39,6 +40,8 @@ protected:
/// If true, event of selected type in selected position will be processed by this element
virtual bool receiveEvent(const Point & position, int eventType) const= 0;
virtual const Rect & getPosition() const= 0;
public:
virtual void clickPressed(const Point & cursorPosition) {}
virtual void clickReleased(const Point & cursorPosition) {}

View File

@ -120,7 +120,7 @@ std::vector<EShortcut> ShortcutHandler::translateKeycode(SDL_Keycode key) const
{SDLK_KP_MINUS, EShortcut::ADVENTURE_ZOOM_OUT },
{SDLK_BACKSPACE, EShortcut::ADVENTURE_ZOOM_RESET },
{SDLK_q, EShortcut::BATTLE_TOGGLE_QUEUE },
{SDLK_c, EShortcut::BATTLE_USE_CREATURE_SPELL },
{SDLK_f, EShortcut::BATTLE_USE_CREATURE_SPELL },
{SDLK_s, EShortcut::BATTLE_SURRENDER },
{SDLK_r, EShortcut::BATTLE_RETREAT },
{SDLK_a, EShortcut::BATTLE_AUTOCOMBAT },

View File

@ -9,4 +9,4 @@
*/
#pragma once
enum class ETextAlignment {TOPLEFT, CENTER, BOTTOMRIGHT};
enum class ETextAlignment {TOPLEFT, TOPCENTER, CENTER, BOTTOMRIGHT};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 KiB

After

Width:  |  Height:  |  Size: 396 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 907 B

After

Width:  |  Height:  |  Size: 703 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 661 KiB

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 444 KiB

View File

@ -1,238 +0,0 @@
/* XPM */
static char *x[] = {
/* columns rows colors chars-per-pixel */
"32 32 200 2 ",
" c gray11",
". c #161616",
"X c #2C0E00",
"o c #2D1003",
"O c #25140C",
"+ c #28130A",
"@ c #311305",
"# c #331609",
"$ c #34190C",
"% c #391C0D",
"& c #241610",
"* c #241A16",
"= c #251E1A",
"- c #2C1C14",
"; c #391E11",
": c #2A241D",
"> c #20201F",
", c #3D2112",
"< c #38231B",
"1 c #3C351E",
"2 c #242323",
"3 c #2C2B2B",
"4 c #282625",
"5 c #332E20",
"6 c #362D29",
"7 c #302620",
"8 c #363122",
"9 c #3C3521",
"0 c #3E352C",
"q c #36302D",
"w c #353433",
"e c #3D3B3B",
"r c #3B3431",
"t c #412516",
"y c #462A1B",
"u c #492E1E",
"i c #45271B",
"p c #443A1B",
"a c #4A3F1C",
"s c #4A2F20",
"d c #4D3323",
"f c #423B24",
"g c #48372E",
"h c #483F25",
"j c #503627",
"k c #513728",
"l c #543B2C",
"z c #593D2F",
"x c #483D37",
"c c #473C37",
"v c #573E30",
"b c #583F31",
"n c #5A4C1D",
"m c #63531C",
"M c #6F5C18",
"N c #725D13",
"B c #7D6515",
"V c #4E4426",
"C c #514623",
"Z c #44423F",
"A c #5C4234",
"S c #5A4438",
"D c #625220",
"F c #6C5A24",
"G c #6B5A29",
"H c #715E26",
"J c #674D3F",
"K c #604537",
"L c #7A6525",
"P c #746129",
"I c #444242",
"U c #494645",
"Y c #4C4B4A",
"T c #4A4845",
"R c #554A46",
"E c #5B4D45",
"W c #514742",
"Q c #555352",
"! c #595757",
"~ c #5D5B5A",
"^ c #694E40",
"/ c #6B5549",
"( c #775D4F",
") c #705648",
"_ c #6B5F58",
"` c #755E52",
"' c #62605E",
"] c #7E6557",
"[ c #7A665A",
"{ c #666564",
"} c #696766",
"| c #6D6B6A",
" . c #6A6865",
".. c #7B6B62",
"X. c #726D69",
"o. c #7A7674",
"O. c #7D7B7B",
"+. c #767471",
"@. c #95750A",
"#. c #98770A",
"$. c #846B1B",
"%. c #876D17",
"&. c #8E731E",
"*. c #93761D",
"=. c #96781C",
"-. c #9B7B10",
";. c #A07F0F",
":. c #A07D10",
">. c #836C22",
",. c #866F29",
"<. c #8C7324",
"1. c #997D22",
"2. c #967A28",
"3. c #826A5D",
"4. c #80675A",
"5. c #856D60",
"6. c #897164",
"7. c #8C7569",
"8. c #8F786C",
"9. c #927C6F",
"0. c #827D79",
"q. c #937E72",
"w. c #A7840F",
"e. c #AE8A16",
"r. c #A5831A",
"t. c #B08C1C",
"y. c #B79015",
"u. c #BD961A",
"i. c #A88825",
"p. c #B08E23",
"a. c #BB9624",
"s. c #BB9626",
"d. c #C69D1B",
"f. c #C29B22",
"g. c #C29C2A",
"h. c #CCA220",
"j. c #CDA42A",
"k. c #D9AD26",
"l. c #D2A726",
"z. c #E6B726",
"x. c #86827D",
"c. c #8A847F",
"v. c #978175",
"b. c #94867D",
"n. c #9B867A",
"m. c #9E897D",
"M. c #998377",
"N. c #848383",
"B. c #8A8680",
"V. c #8F8B86",
"C. c #9B8D85",
"Z. c #938E89",
"A. c #918984",
"S. c #96928E",
"D. c #9A9693",
"F. c #9C9996",
"G. c #9D9B99",
"H. c #939292",
"J. c #A28E82",
"K. c #A69287",
"L. c #A5948A",
"P. c #AC988D",
"I. c #A9958A",
"U. c #A69C96",
"Y. c #AD9C93",
"T. c #A29D99",
"R. c #A89F99",
"E. c #B19E93",
"W. c #A5A29F",
"Q. c #B3A196",
"!. c #B8A69C",
"~. c #B9A69C",
"^. c #A4A3A2",
"/. c #A9A6A3",
"(. c #ADABA9",
"). c #ABA8A5",
"_. c #BCABA1",
"`. c #B2AEAC",
"'. c #B5A9A3",
"]. c #B3B2AF",
"[. c #B3B2B2",
"{. c #B9B5B3",
"}. c #BAB9B9",
"|. c #BBB9B6",
" X c #C0AFA5",
".X c #C4B3AA",
"XX c #C8B8AE",
"oX c #C1BFBE",
"OX c #CDBDB4",
"+X c #CCC9C7",
"@X c #CFCCCB",
"#X c #C5C3C1",
"$X c #D3D1CF",
"%X c #D6D3D2",
"&X c #DDDBDA",
"*X c #EDECEB",
"=X c #E4E3E3",
"-X c #F3F3F3",
";X c #F9F9F9",
":X c None",
/* pixels */
":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X",
":X:X:XO.V.| Q :X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:XZ Q ' T :X:X",
":X:XI G.(.}.}.}.(.^.H.N.O.| } ~ ! ' { { } X.o.0.B.x.x.x.x.x.:X:X",
":X:X3 N.H.^.[.}.}.}.}.}.}.}.}.[.`.(./.^.T.T.D.S.Z.V.B.x.x.o.:X:X",
":X:X:X| O. .^ ..b.D./.[.{.}.}.}.{.[.`.(.^.F.S.D.W.'.'.c.c.' :X:X",
":X:X:X! { c % s b ^ ( 5.7.q.b.C.C.L.L.I.Y.E.~. X XXXOXZ.V.~ :X:X",
":X:X:XU ~ c X @ t j K ) [ 3.6.7.q.b.n.J.L.P.E.E.~. XXXZ.D.Y :X:X",
":X:X:Xw Q Z X X X % y v ^ ( ] 5.6.8.q.M.C.J.I.E.Q.~.~.F.F.:X:X:X",
":X:X:X4 Y U X X X X @ t d A ) ] 3.6.7.9.q.n.J.K.P.E.W.W.D.:X:X:X",
":X:X:X:XI U @ X X X X X % i z ^ ( 4.3.6.8.q.M.n.m.I.U.(.x.:X:X:X",
":X . w 3 * + X + O & & & - d K g x Z ^ 7...U E R N.N.e :X:X",
":XG j.9 2 ,.s.* + = s.j.j.j.P * i F j.j.0 [ R i.l.i.' Y g.2.:X:X",
":XH z.f 2 2.k.* * p.f.,.,.2.l.G X L z.z.8 x q p.z.s.{ Y k.i.:X:X",
":Xh i.f.>.d.L : = h.1.O X : L V X H k.p.a.4 <.f.a.p.{ e h.1.:X:X",
":X:XF d.<.d.w k = d.&.+ X X X X o F d.B d.2 <.t.<.r.{ w u.*.:X:X",
":X:XF y.$.y.I ' : e.$.- # & = * X D y.V =.e.e.D %.=.{ 2 e.$.:X:X",
":X:X9 %.w.n Q G.: -.B : : 1 w.n X n w.: M w.-.2 B %.Y > -.B :X:X",
":X:X:Xm #. -X-X6 a @.@.@.#.M 5 o n @.: 1 a a = N B w @.N :X:X",
":X:X:X8 a #X;XC.7 p p p p 8 , # 8 p * X X X + 1 p p 9 :X:X",
":X:X:X:X:X:XO.*X@Xk d h u u u y t % # o X X X < Q Y :X:X:X:X:X:X",
":X:X:X:X:X:X:X&X*X[ d j j d s u u t , % @ o X r Y 3 :X:X:X:X:X:X",
":X:X:X:X:X:X:XF.&XW.z k k j j s d u y , , # - c I :X:X:X:X:X:X:X",
":X:X:X:X:X:X:X:X$X%X/ z l k k d d d u u t , 6 w 3 :X:X:X:X:X:X:X",
":X:X:X:X:X:X:X:XF.@XT.v l l l k d d d d u S .Y :X:X:X:X:X:X:X:X",
":X:X:X:X:X:X:X:XI |.#X..A z l l k j j d v F.F.! :X:X:X:X:X:X:X:X",
":X:X:X:X:X:X:X:X:XO.{.]./ A z l z l j z {.=X}.:X:X:X:X:X:X:X:X:X",
":X:X:X:X:X:X:X:X:X:XF.].).` A A z l S `.-X;XO.:X:X:X:X:X:X:X:X:X",
":X:X:X:X:X:X:X:X:X:Xe G.).].V.....b.$X=X*X^.:X:X:X:X:X:X:X:X:X:X",
":X:X:X:X:X:X:X:X:X:X:XT D.W.(.{.}.+X%X&X/.:X:X:X:X:X:X:X:X:X:X:X",
":X:X:X:X:X:X:X:X:X:X:X:Xe x.T./.].|.}.O.:X:X:X:X:X:X:X:X:X:X:X:X",
":X:X:X:X:X:X:X:X:X:X:X:X:X:XY | o. .:X:X:X:X:X:X:X:X:X:X:X:X:X:X",
":X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X:X"
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 893 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

After

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.5 KiB

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 32 KiB

View File

@ -343,7 +343,7 @@ void CBonusSelection::updateAfterStateChange()
{
buttonStart->block(getCampaign()->scenario(CSH->campaignMap).travelOptions.bonusesToChoose.size());
}
else if(buttonStart->isBlocked())
else
{
buttonStart->block(false);
}
@ -390,6 +390,9 @@ void CBonusSelection::goBack()
void CBonusSelection::startMap()
{
if (!CSH->validateGameStart())
return;
auto showPrologVideo = [=]()
{
auto exitCb = [=]()

View File

@ -88,6 +88,8 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)
break;
}
buttonStart->block(true); // to be unblocked after map list is ready
buttonBack = std::make_shared<CButton>(Point(581, 535), "SCNRBACK.DEF", CGI->generaltexth->zelp[105], [&]()
{
CSH->sendClientDisconnecting();
@ -126,34 +128,11 @@ void CLobbyScreen::startCampaign()
void CLobbyScreen::startScenario(bool allowOnlyAI)
{
try
if (CSH->validateGameStart(allowOnlyAI))
{
CSH->sendStartGame(allowOnlyAI);
buttonStart->block(true);
}
catch(CModHandler::Incompatibility & e)
{
logGlobal->warn("Incompatibility exception during start scenario: %s", e.what());
auto errorMsg = CGI->generaltexth->translate("vcmi.server.errors.modsIncompatibility") + '\n';
errorMsg += e.what();
CInfoWindow::showInfoDialog(errorMsg, CInfoWindow::TCompsInfo(), PlayerColor(1));
}
catch(std::exception & e)
{
logGlobal->error("Exception during startScenario: %s", e.what());
if(std::string(e.what()) == "ExceptionNoHuman")
CInfoWindow::showInfoDialog(CGI->generaltexth->allTexts[530], CInfoWindow::TCompsInfo(), PlayerColor(1));
if(std::string(e.what()) == "ExceptionNoTemplate")
CInfoWindow::showInfoDialog(CGI->generaltexth->allTexts[751], CInfoWindow::TCompsInfo(), PlayerColor(1));
}
catch(...)
{
logGlobal->error("Unknown exception");
}
}
void CLobbyScreen::toggleMode(bool host)
@ -192,6 +171,8 @@ void CLobbyScreen::updateAfterStateChange()
if(CSH->mi && tabOpt)
tabOpt->recreate();
buttonStart->block(CSH->mi == nullptr || CSH->isGuest());
card->changeSelection();
if (card->iconDifficulty)
{

View File

@ -216,6 +216,10 @@ void InfoCard::changeSelection()
flagbox->recreate();
labelDifficulty->setText(CGI->generaltexth->arraytxt[142 + mapInfo->mapHeader->difficulty]);
iconDifficulty->setSelected(SEL->getCurrentDifficulty());
if(SEL->screenType == ESelectionScreen::loadGame || SEL->screenType == ESelectionScreen::saveGame)
for(auto & button : iconDifficulty->buttons)
button.second->setEnabled(button.first == SEL->getCurrentDifficulty());
const std::array<std::string, 5> difficultyPercent = {"80%", "100%", "130%", "160%", "200%"};
labelDifficultyPercent->setText(difficultyPercent[SEL->getCurrentDifficulty()]);

View File

@ -18,6 +18,7 @@
#include "../render/CAnimation.h"
#include "../render/Canvas.h"
#include "../render/IImage.h"
#include "../render/Colors.h"
#include "../../CCallback.h"
@ -246,7 +247,6 @@ uint8_t MapRendererRoad::checksum(IMapRendererContext & context, const int3 & co
MapRendererBorder::MapRendererBorder()
{
emptyFill = std::make_unique<Canvas>(Point(32,32));
animation = std::make_unique<CAnimation>("EDG");
animation->preload();
}
@ -298,7 +298,7 @@ void MapRendererBorder::renderTile(IMapRendererContext & context, Canvas & targe
}
else
{
target.draw(*emptyFill, Point(0,0));
target.drawColor(Rect(0,0,32,32), Colors::BLACK);
}
}

View File

@ -90,7 +90,6 @@ public:
class MapRendererBorder
{
std::unique_ptr<CAnimation> animation;
std::unique_ptr<Canvas> emptyFill;
size_t getIndexForTile(IMapRendererContext & context, const int3 & coordinates);

View File

@ -218,8 +218,9 @@ void MapViewController::afterRender()
if(!hero)
hero = boat->hero;
if(movementContext->progress >= 1.0)
if(movementContext->progress >= 0.999)
{
logGlobal->debug("Ending movement animation");
setViewCenter(hero->getSightCenter());
removeObject(context->getObject(movementContext->target));
@ -229,20 +230,23 @@ void MapViewController::afterRender()
}
}
if(teleportContext && teleportContext->progress >= 1.0)
if(teleportContext && teleportContext->progress >= 0.999)
{
logGlobal->debug("Ending teleport animation");
activateAdventureContext(teleportContext->animationTime);
}
if(fadingOutContext && fadingOutContext->progress <= 0.0)
if(fadingOutContext && fadingOutContext->progress <= 0.001)
{
logGlobal->debug("Ending fade out animation");
removeObject(context->getObject(fadingOutContext->target));
activateAdventureContext(fadingOutContext->animationTime);
}
if(fadingInContext && fadingInContext->progress >= 1.0)
if(fadingInContext && fadingInContext->progress >= 0.999)
{
logGlobal->debug("Ending fade in animation");
activateAdventureContext(fadingInContext->animationTime);
}
}
@ -300,6 +304,7 @@ bool MapViewController::isEventVisible(const CGHeroInstance * obj, const int3 &
void MapViewController::fadeOutObject(const CGObjectInstance * obj)
{
logGlobal->debug("Starting fade out animation");
fadingOutContext = std::make_shared<MapRendererAdventureFadingContext>(*state);
fadingOutContext->animationTime = adventureContext->animationTime;
adventureContext = fadingOutContext;
@ -319,6 +324,7 @@ void MapViewController::fadeOutObject(const CGObjectInstance * obj)
void MapViewController::fadeInObject(const CGObjectInstance * obj)
{
logGlobal->debug("Starting fade in animation");
fadingInContext = std::make_shared<MapRendererAdventureFadingContext>(*state);
fadingInContext->animationTime = adventureContext->animationTime;
adventureContext = fadingInContext;
@ -457,6 +463,7 @@ void MapViewController::onAfterHeroTeleported(const CGHeroInstance * obj, const
if(isEventVisible(obj, from, dest))
{
logGlobal->debug("Starting teleport animation");
teleportContext = std::make_shared<MapRendererAdventureTransitionContext>(*state);
teleportContext->animationTime = adventureContext->animationTime;
adventureContext = teleportContext;
@ -491,6 +498,7 @@ void MapViewController::onHeroMoved(const CGHeroInstance * obj, const int3 & fro
if(movementTime > 1)
{
logGlobal->debug("Starting movement animation");
movementContext = std::make_shared<MapRendererAdventureMovingContext>(*state);
movementContext->animationTime = adventureContext->animationTime;
adventureContext = movementContext;

View File

@ -149,6 +149,7 @@ void Canvas::drawText(const Point & position, const EFonts & font, const SDL_Col
switch (alignment)
{
case ETextAlignment::TOPLEFT: return graphics->fonts[font]->renderTextLeft (surface, text, colorDest, renderArea.topLeft() + position);
case ETextAlignment::TOPCENTER: return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, renderArea.topLeft() + position);
case ETextAlignment::CENTER: return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, renderArea.topLeft() + position);
case ETextAlignment::BOTTOMRIGHT: return graphics->fonts[font]->renderTextRight (surface, text, colorDest, renderArea.topLeft() + position);
}
@ -159,6 +160,7 @@ void Canvas::drawText(const Point & position, const EFonts & font, const SDL_Col
switch (alignment)
{
case ETextAlignment::TOPLEFT: return graphics->fonts[font]->renderTextLinesLeft (surface, text, colorDest, renderArea.topLeft() + position);
case ETextAlignment::TOPCENTER: return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, renderArea.topLeft() + position);
case ETextAlignment::CENTER: return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, renderArea.topLeft() + position);
case ETextAlignment::BOTTOMRIGHT: return graphics->fonts[font]->renderTextLinesRight (surface, text, colorDest, renderArea.topLeft() + position);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 MiB

After

Width:  |  Height:  |  Size: 401 KiB

View File

@ -128,6 +128,13 @@ void CButton::setState(ButtonState newState)
{
if (state == newState)
return;
if (newState == BLOCKED)
removeUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
else
addUsedEvents(LCLICK | SHOW_POPUP | HOVER | KEYBOARD);
state = newState;
update();
}

View File

@ -11,11 +11,9 @@
#include "CArtifactsOfHeroBackpack.h"
#include "../gui/CGuiHandler.h"
#include "../gui/Shortcut.h"
#include "Buttons.h"
#include "Images.h"
#include "GameSettings.h"
#include "IHandlerBase.h"
#include "ObjectLists.h"
#include "../CPlayerInterface.h"
@ -27,25 +25,28 @@ CArtifactsOfHeroBackpack::CArtifactsOfHeroBackpack(const Point & position)
{
OBJECT_CONSTRUCTION_CAPTURING(255 - DISPOSE);
pos += position;
setRedrawParent(true);
const auto backpackCap = VLC->settings()->getInteger(EGameSettings::HEROES_BACKPACK_CAP);
auto visibleCapasityMax = HERO_BACKPACK_WINDOW_SLOT_LINES * HERO_BACKPACK_WINDOW_SLOT_COLUMNS;
auto visibleCapacityMax = HERO_BACKPACK_WINDOW_SLOT_ROWS * HERO_BACKPACK_WINDOW_SLOT_COLUMNS;
if(backpackCap >= 0)
visibleCapasityMax = visibleCapasityMax > backpackCap ? backpackCap : visibleCapasityMax;
visibleCapacityMax = visibleCapacityMax > backpackCap ? backpackCap : visibleCapacityMax;
backpack.resize(visibleCapasityMax);
backpack.resize(visibleCapacityMax);
size_t artPlaceIdx = 0;
for(auto & artPlace : backpack)
{
artPlace = std::make_shared<CHeroArtPlace>(
Point(46 * (artPlaceIdx % HERO_BACKPACK_WINDOW_SLOT_COLUMNS), 46 * (artPlaceIdx / HERO_BACKPACK_WINDOW_SLOT_COLUMNS)));
const auto pos = Point(slotSizeWithMargin * (artPlaceIdx % HERO_BACKPACK_WINDOW_SLOT_COLUMNS),
slotSizeWithMargin * (artPlaceIdx / HERO_BACKPACK_WINDOW_SLOT_COLUMNS));
backpackSlotsBackgrounds.emplace_back(std::make_shared<CPicture>("heroWindow/artifactSlotEmpty", pos));
artPlace = std::make_shared<CHeroArtPlace>(pos);
artPlace->setArtifact(nullptr);
artPlace->leftClickCallback = std::bind(&CArtifactsOfHeroBase::leftClickArtPlace, this, _1);
artPlace->rightClickCallback = std::bind(&CArtifactsOfHeroBase::rightClickArtPlace, this, _1);
artPlaceIdx++;
}
if(backpackCap < 0 || visibleCapasityMax < backpackCap)
if(backpackCap < 0 || visibleCapacityMax < backpackCap)
{
auto onCreate = [](size_t index) -> std::shared_ptr<CIntObject>
{
@ -56,9 +57,19 @@ CArtifactsOfHeroBackpack::CArtifactsOfHeroBackpack(const Point & position)
scrollBackpack(static_cast<int>(pos) * HERO_BACKPACK_WINDOW_SLOT_COLUMNS - backpackPos);
};
backpackListBox = std::make_shared<CListBoxWithCallback>(
posMoved, onCreate, Point(0, 0), Point(0, 0), HERO_BACKPACK_WINDOW_SLOT_LINES, 0, 0, 1,
Rect(HERO_BACKPACK_WINDOW_SLOT_COLUMNS * 46 + 10, 0, HERO_BACKPACK_WINDOW_SLOT_LINES * 46 - 5, 0));
posMoved, onCreate, Point(0, 0), Point(0, 0), HERO_BACKPACK_WINDOW_SLOT_ROWS, 0, 0, 1,
Rect(HERO_BACKPACK_WINDOW_SLOT_COLUMNS * slotSizeWithMargin + sliderPosOffsetX, 0, HERO_BACKPACK_WINDOW_SLOT_ROWS * slotSizeWithMargin - 2, 0));
}
pos.w = visibleCapacityMax > HERO_BACKPACK_WINDOW_SLOT_COLUMNS ? HERO_BACKPACK_WINDOW_SLOT_COLUMNS : visibleCapacityMax;
pos.w *= slotSizeWithMargin;
if(backpackListBox)
pos.w += sliderPosOffsetX + 16; // 16 is slider width. TODO: get it from CListBox directly;
pos.h = (visibleCapacityMax / HERO_BACKPACK_WINDOW_SLOT_COLUMNS);
if(visibleCapacityMax % HERO_BACKPACK_WINDOW_SLOT_COLUMNS != 0)
pos.h += 1;
pos.h *= slotSizeWithMargin;
}
void CArtifactsOfHeroBackpack::swapArtifacts(const ArtifactLocation & srcLoc, const ArtifactLocation & dstLoc)
@ -75,7 +86,7 @@ void CArtifactsOfHeroBackpack::pickUpArtifact(CHeroArtPlace & artPlace)
void CArtifactsOfHeroBackpack::scrollBackpack(int offset)
{
if(backpackListBox)
backpackListBox->resize(getActiveSlotLinesNum());
backpackListBox->resize(getActiveSlotRowsNum());
backpackPos += offset;
auto slot = ArtifactPosition(GameConstants::BACKPACK_START + backpackPos);
for(auto artPlace : backpack)
@ -89,11 +100,11 @@ void CArtifactsOfHeroBackpack::scrollBackpack(int offset)
void CArtifactsOfHeroBackpack::updateBackpackSlots()
{
if(backpackListBox)
backpackListBox->resize(getActiveSlotLinesNum());
backpackListBox->resize(getActiveSlotRowsNum());
CArtifactsOfHeroBase::updateBackpackSlots();
}
size_t CArtifactsOfHeroBackpack::getActiveSlotLinesNum()
size_t CArtifactsOfHeroBackpack::getActiveSlotRowsNum()
{
return (curHero->artifactsInBackpack.size() + HERO_BACKPACK_WINDOW_SLOT_COLUMNS - 1) / HERO_BACKPACK_WINDOW_SLOT_COLUMNS;
}

View File

@ -27,10 +27,13 @@ public:
void pickUpArtifact(CHeroArtPlace & artPlace);
void scrollBackpack(int offset) override;
void updateBackpackSlots() override;
size_t getActiveSlotLinesNum();
size_t getActiveSlotRowsNum();
private:
std::shared_ptr<CListBoxWithCallback> backpackListBox;
std::vector<std::shared_ptr<CPicture>> backpackSlotsBackgrounds;
const size_t HERO_BACKPACK_WINDOW_SLOT_COLUMNS = 8;
const size_t HERO_BACKPACK_WINDOW_SLOT_LINES = 8;
const size_t HERO_BACKPACK_WINDOW_SLOT_ROWS = 8;
const int slotSizeWithMargin = 46;
const int sliderPosOffsetX = 10;
};

View File

@ -368,14 +368,15 @@ void CGarrisonSlot::gesture(bool on, const Point & initialPosition, const Point
std::vector<RadialMenuConfig> menuElements = {
{ RadialMenuConfig::ITEM_NW, hasSameUnit, "stackMerge", "vcmi.radialWheel.mergeSameUnit", [this](){owner->bulkMergeStacks(this);} },
{ RadialMenuConfig::ITEM_NE, stackExists, "stackInfo", "vcmi.radialWheel.showUnitInformation", [this](){viewInfo();} },
{ RadialMenuConfig::ITEM_NE, hasOwnEmptySlots, "stackFillOne", "vcmi.radialWheel.fillSingleUnit", [this](){owner->bulkSplitStack(this);} },
{ RadialMenuConfig::ITEM_WW, hasOwnEmptySlots, "stackSplitOne", "vcmi.radialWheel.splitSingleUnit", [this](){splitIntoParts(this->getGarrison(), 1); } },
{ RadialMenuConfig::ITEM_EE, hasOwnEmptySlots, "stackSplitEqual", "vcmi.radialWheel.splitUnitEqually", [this](){owner->bulkSmartSplitStack(this);} },
{ RadialMenuConfig::ITEM_SW, hasOtherEmptySlots, "heroMove", "vcmi.radialWheel.moveUnit", [this](){owner->moveStackToAnotherArmy(this);} },
{ RadialMenuConfig::ITEM_SE, hasAnyEmptySlots, "heroSwap", "vcmi.radialWheel.splitUnit", [this](){ owner->selectSlot(this); owner->splitClick();} },
};
GH.windows().createAndPushWindow<RadialMenu>(pos.center(), menuElements);
if (hasAnyEmptySlots || hasSameUnit)
GH.windows().createAndPushWindow<RadialMenu>(pos.center(), menuElements);
}
void CGarrisonSlot::update()

View File

@ -165,7 +165,7 @@ void CWindowWithArtifacts::leftClickArtPlaceHero(CArtifactsOfHeroBase & artsInst
if constexpr(std::is_same_v<decltype(artSetWeak), std::weak_ptr<CArtifactsOfHeroBackpack>>)
{
if(!isTransferAllowed)
if(!isTransferAllowed && artPlace.getArt())
{
if(closeCallback)
closeCallback();

View File

@ -17,6 +17,7 @@
#include "../CPlayerInterface.h"
#include "../CGameInfo.h"
#include "../PlayerLocalState.h"
#include "../widgets/TextControls.h"
#include "../widgets/CGarrisonInt.h"
#include "../windows/CCastleInterface.h"
@ -31,6 +32,7 @@
#include "../../lib/CModHandler.h"
#include "../../lib/GameSettings.h"
#include "../../lib/TextOperations.h"
#include "../../lib/mapObjects/CGCreature.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CGTownInstance.h"
@ -436,6 +438,27 @@ void CInteractableTownTooltip::init(const InfoAboutTown & town)
}
}
CreatureTooltip::CreatureTooltip(Point pos, const CGCreature * creature)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
auto creatureData = (*CGI->creh)[creature->stacks.begin()->second->getCreatureID()].get();
creatureImage = std::make_shared<CAnimImage>(graphics->getAnimation("TWCRPORT"), creatureData->getIconIndex());
creatureImage->center(Point(parent->pos.x + parent->pos.w / 2, parent->pos.y + creatureImage->pos.h / 2 + 11));
bool isHeroSelected = LOCPLINT->localState->getCurrentHero() != nullptr;
std::string textContent = isHeroSelected
? creature->getHoverText(LOCPLINT->localState->getCurrentHero())
: creature->getHoverText(LOCPLINT->playerID);
//TODO: window is bigger than OH3
//TODO: vertical alignment does not match H3. Commented below example that matches H3 for creatures count but supports only 1 line:
/*std::shared_ptr<CLabel> = std::make_shared<CLabel>(parent->pos.w / 2, 103,
FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, creature->getHoverText(LOCPLINT->playerID));*/
tooltipTextbox = std::make_shared<CTextBox>(textContent, Rect(15, 95, 230, 150), 0, FONT_SMALL, ETextAlignment::TOPCENTER, Colors::WHITE);
}
void MoraleLuckBox::set(const AFactionMember * node)
{
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);

Some files were not shown because too many files have changed in this diff Show More