mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-19 21:10:12 +02:00
Merge remote-tracking branch 'upstream/develop' into develop
This commit is contained in:
commit
65f2d0c44e
AI
CMakeLists.txtCMakePresets.jsonMods/vcmi
Data/stackWindow
Sprites/lobby
config
client
ArtifactsUIController.cppClientCommandManager.cppNetPacksLobbyClient.cpp
battle
BattleAnimationClasses.cppBattleAnimationClasses.hBattleEffectsController.cppBattleEffectsController.hBattleFieldController.cppBattleInterface.cppBattleInterfaceClasses.cppBattleObstacleController.cppBattleStacksController.cppCreatureAnimation.cpp
lobby
mainmenu
mapView
render
renderSDL
ImageScaled.cppImageScaled.hRenderHandler.cppRenderHandler.hSDLImage.cppSDLImage.hSDL_Extensions.cpp
widgets
windows
clientapp
config
campaignSets.json
creatures
castle.jsonconflux.jsondungeon.jsonfortress.jsoninferno.jsonnecropolis.jsonneutral.jsonrampart.jsonspecial.jsonstronghold.jsontower.json
objects
schemas
spells
docs
launcher/translation
chinese.tsczech.tsenglish.tsfrench.tsgerman.tspolish.tsportuguese.tsrussian.tsspanish.tsswedish.tsukrainian.tsvietnamese.ts
lib
@ -763,7 +763,7 @@ void AIGateway::showGarrisonDialog(const CArmedInstance * up, const CGHeroInstan
|
||||
//you can't request action from action-response thread
|
||||
requestActionASAP([=]()
|
||||
{
|
||||
if(removableUnits && up->tempOwner == down->tempOwner && nullkiller->settings->isGarrisonTroopsUsageAllowed() && !cb->getStartInfo()->isSteadwickFallCampaignMission())
|
||||
if(removableUnits && up->tempOwner == down->tempOwner && nullkiller->settings->isGarrisonTroopsUsageAllowed() && !cb->getStartInfo()->isRestorationOfErathiaCampaign())
|
||||
{
|
||||
pickBestCreatures(down, up);
|
||||
}
|
||||
|
@ -731,7 +731,7 @@ void VCAI::showGarrisonDialog(const CArmedInstance * up, const CGHeroInstance *
|
||||
//you can't request action from action-response thread
|
||||
requestActionASAP([=]()
|
||||
{
|
||||
if(removableUnits && !cb->getStartInfo()->isSteadwickFallCampaignMission())
|
||||
if(removableUnits && !cb->getStartInfo()->isRestorationOfErathiaCampaign())
|
||||
pickBestCreatures(down, up);
|
||||
|
||||
answerQuery(queryID, 0);
|
||||
|
@ -355,13 +355,6 @@ if(MINGW OR MSVC)
|
||||
if(ICONV_FOUND)
|
||||
set(SYSTEM_LIBS ${SYSTEM_LIBS} iconv)
|
||||
endif()
|
||||
|
||||
# Prevent compiler issues when building Debug
|
||||
# Assembler might fail with "too many sections"
|
||||
# With big-obj or 64-bit build will take hours
|
||||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Og")
|
||||
endif()
|
||||
endif(MINGW)
|
||||
endif(MINGW OR MSVC)
|
||||
|
||||
|
@ -134,7 +134,9 @@
|
||||
"description": "VCMI Windows Ninja using MinGW",
|
||||
"inherits": "default-release",
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Release"
|
||||
"CMAKE_BUILD_TYPE": "Release",
|
||||
"CMAKE_C_COMPILER": "gcc",
|
||||
"CMAKE_CXX_COMPILER": "g++"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
Binary file not shown.
Before ![]() (image error) Size: 29 KiB After ![]() (image error) Size: 35 KiB ![]() ![]() |
BIN
Mods/vcmi/Sprites/lobby/delete-normal.png
Normal file
BIN
Mods/vcmi/Sprites/lobby/delete-normal.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 997 B |
BIN
Mods/vcmi/Sprites/lobby/delete-pressed.png
Normal file
BIN
Mods/vcmi/Sprites/lobby/delete-pressed.png
Normal file
Binary file not shown.
After ![]() (image error) Size: 986 B |
8
Mods/vcmi/Sprites/lobby/deleteButton.json
Normal file
8
Mods/vcmi/Sprites/lobby/deleteButton.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"basepath" : "lobby/",
|
||||
"images" :
|
||||
[
|
||||
{ "frame" : 0, "file" : "delete-normal.png"},
|
||||
{ "frame" : 1, "file" : "delete-pressed.png"}
|
||||
]
|
||||
}
|
@ -85,6 +85,7 @@
|
||||
"vcmi.spellResearch.research" : "研究此法术",
|
||||
"vcmi.spellResearch.skip" : "跳过此法术",
|
||||
"vcmi.spellResearch.abort" : "中止",
|
||||
"vcmi.spellResearch.noMoreSpells" : "没有更多的法术可供研究。",
|
||||
|
||||
"vcmi.mainMenu.serverConnecting" : "连接中...",
|
||||
"vcmi.mainMenu.serverAddressEnter" : "使用地址:",
|
||||
@ -705,6 +706,8 @@
|
||||
"core.bonus.DISINTEGRATE.description": "死亡后不会留下尸体",
|
||||
"core.bonus.INVINCIBLE.name": "无敌",
|
||||
"core.bonus.INVINCIBLE.description": "不受任何效果影响",
|
||||
"core.bonus.MECHANICAL.name": "机械",
|
||||
"core.bonus.MECHANICAL.description": "免疫大多数效果,可修复",
|
||||
"core.bonus.PRISM_HEX_ATTACK_BREATH.name": "棱光吐息",
|
||||
"core.bonus.PRISM_HEX_ATTACK_BREATH.description": "攻击后向三方向扩散攻击"
|
||||
}
|
||||
|
@ -28,6 +28,13 @@
|
||||
"vcmi.adventureMap.movementPointsHeroInfo" : "(Movement points: %REMAINING / %POINTS)",
|
||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Sorry, replay opponent turn is not implemented yet!",
|
||||
|
||||
"vcmi.bonusSource.artifact" : "Artifact",
|
||||
"vcmi.bonusSource.creature" : "Ability",
|
||||
"vcmi.bonusSource.spell" : "Spell",
|
||||
"vcmi.bonusSource.hero" : "Hero",
|
||||
"vcmi.bonusSource.commander" : "Commander",
|
||||
"vcmi.bonusSource.other" : "Other",
|
||||
|
||||
"vcmi.capitalColors.0" : "Red",
|
||||
"vcmi.capitalColors.1" : "Blue",
|
||||
"vcmi.capitalColors.2" : "Tan",
|
||||
@ -85,6 +92,7 @@
|
||||
"vcmi.spellResearch.research" : "Research this Spell",
|
||||
"vcmi.spellResearch.skip" : "Skip this Spell",
|
||||
"vcmi.spellResearch.abort" : "Abort",
|
||||
"vcmi.spellResearch.noMoreSpells" : "There are no more spells available for research.",
|
||||
|
||||
"vcmi.mainMenu.serverConnecting" : "Connecting...",
|
||||
"vcmi.mainMenu.serverAddressEnter" : "Enter address:",
|
||||
@ -106,6 +114,12 @@
|
||||
"vcmi.lobby.handicap.resource" : "Gives players appropriate resources to start with in addition to the normal starting resources. Negative values are allowed, but are limited to 0 in total (the player never starts with negative resources).",
|
||||
"vcmi.lobby.handicap.income" : "Changes the player's various incomes by the percentage. Is rounded up.",
|
||||
"vcmi.lobby.handicap.growth" : "Changes the growth rate of creatures in the towns owned by the player. Is rounded up.",
|
||||
"vcmi.lobby.deleteUnsupportedSave" : "{Unsupported saves found}\n\nVCMI has found %d saved games that are no longer supported, possibly due to differences in VCMI versions.\n\nDo you want to delete them?",
|
||||
"vcmi.lobby.deleteSaveGameTitle" : "Select a Saved Game to delete",
|
||||
"vcmi.lobby.deleteMapTitle" : "Select a Scenario to delete",
|
||||
"vcmi.lobby.deleteFile" : "Do you want to delete following file?",
|
||||
"vcmi.lobby.deleteFolder" : "Do you want to delete following folder?",
|
||||
"vcmi.lobby.deleteMode" : "Switch to delete mode and back",
|
||||
|
||||
"vcmi.lobby.login.title" : "VCMI Online Lobby",
|
||||
"vcmi.lobby.login.username" : "Username:",
|
||||
|
@ -25,8 +25,16 @@
|
||||
"vcmi.adventureMap.playerAttacked" : "Spieler wurde attackiert: %s",
|
||||
"vcmi.adventureMap.moveCostDetails" : "Bewegungspunkte - Kosten: %TURNS Runden + %POINTS Punkte, Verbleibende Punkte: %REMAINING",
|
||||
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Bewegungspunkte - Kosten: %POINTS Punkte, Verbleibende Punkte: %REMAINING",
|
||||
"vcmi.adventureMap.movementPointsHeroInfo" : "(Bewegungspunkte: %REMAINING / %POINTS)",
|
||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Das Wiederholen des gegnerischen Zuges ist aktuell noch nicht implementiert!",
|
||||
|
||||
"vcmi.bonusSource.artifact" : "Artefakt",
|
||||
"vcmi.bonusSource.creature" : "Fähigkeit",
|
||||
"vcmi.bonusSource.spell" : "Zauber",
|
||||
"vcmi.bonusSource.hero" : "Held",
|
||||
"vcmi.bonusSource.commander" : "Commander",
|
||||
"vcmi.bonusSource.other" : "Anderes",
|
||||
|
||||
"vcmi.capitalColors.0" : "Rot",
|
||||
"vcmi.capitalColors.1" : "Blau",
|
||||
"vcmi.capitalColors.2" : "Braun",
|
||||
@ -41,6 +49,12 @@
|
||||
"vcmi.heroOverview.secondarySkills" : "Sekundäre Skills",
|
||||
"vcmi.heroOverview.spells" : "Zaubersprüche",
|
||||
|
||||
"vcmi.quickExchange.moveUnit" : "Einheit bewegen",
|
||||
"vcmi.quickExchange.moveAllUnits" : "Alle Einheiten bewegen",
|
||||
"vcmi.quickExchange.swapAllUnits" : "Einheiten tauschen",
|
||||
"vcmi.quickExchange.moveAllArtifacts" : "Alle Artefakte bewegen",
|
||||
"vcmi.quickExchange.swapAllArtifacts" : "Artefakte tauschen",
|
||||
|
||||
"vcmi.radialWheel.mergeSameUnit" : "Gleiche Kreaturen zusammenführen",
|
||||
"vcmi.radialWheel.fillSingleUnit" : "Füllen mit einzelnen Kreaturen",
|
||||
"vcmi.radialWheel.splitSingleUnit" : "Wegtrennen einzelner Kreaturen",
|
||||
@ -59,6 +73,16 @@
|
||||
"vcmi.radialWheel.moveUp" : "Nach oben bewegen",
|
||||
"vcmi.radialWheel.moveDown" : "Nach unten bewegen",
|
||||
"vcmi.radialWheel.moveBottom" : "Ganz nach unten bewegen",
|
||||
|
||||
"vcmi.randomMap.description" : "Die Karte wurde mit dem Zufallsgenerator erstellt.\nTemplate war %s, Größe %dx%d, Level %d, Spieler %d, Computer %d, Wasser %s, Monster %s, VCMI-Karte",
|
||||
"vcmi.randomMap.description.isHuman" : ", %s ist Mensch",
|
||||
"vcmi.randomMap.description.townChoice" : ", %s Stadt-Wahl ist %s",
|
||||
"vcmi.randomMap.description.water.none" : "Kein",
|
||||
"vcmi.randomMap.description.water.normal" : "Normal",
|
||||
"vcmi.randomMap.description.water.islands" : "Inseln",
|
||||
"vcmi.randomMap.description.monster.weak" : "Schwach",
|
||||
"vcmi.randomMap.description.monster.normal" : "Normal",
|
||||
"vcmi.randomMap.description.monster.strong" : "Stark",
|
||||
|
||||
"vcmi.spellBook.search" : "suchen...",
|
||||
|
||||
@ -68,6 +92,7 @@
|
||||
"vcmi.spellResearch.research" : "Erforsche diesen Zauberspruch",
|
||||
"vcmi.spellResearch.skip" : "Überspringe diesen Zauberspruch",
|
||||
"vcmi.spellResearch.abort" : "Abbruch",
|
||||
"vcmi.spellResearch.noMoreSpells" : "Es sind keine weiteren Zaubersprüche für die Forschung verfügbar.",
|
||||
|
||||
"vcmi.mainMenu.serverConnecting" : "Verbinde...",
|
||||
"vcmi.mainMenu.serverAddressEnter" : "Addresse eingeben:",
|
||||
@ -89,7 +114,13 @@
|
||||
"vcmi.lobby.handicap.resource" : "Gibt den Spielern entsprechende Ressourcen zum Start zusätzlich zu den normalen Startressourcen. Negative Werte sind erlaubt, werden aber insgesamt auf 0 begrenzt (der Spieler beginnt nie mit negativen Ressourcen).",
|
||||
"vcmi.lobby.handicap.income" : "Verändert die verschiedenen Einkommen des Spielers um den Prozentsatz. Wird aufgerundet.",
|
||||
"vcmi.lobby.handicap.growth" : "Verändert die Wachstumsrate der Kreaturen in den Städten, die der Spieler besitzt. Wird aufgerundet.",
|
||||
|
||||
"vcmi.lobby.deleteUnsupportedSave" : "{Nicht unterstützte Spielstände gefunden}\n\nVCMI hat %d gespeicherte Spiele gefunden, die nicht mehr unterstützt werden, möglicherweise aufgrund von Unterschieden in VCMI-Versionen.\n\nMöchtet Ihr sie löschen?",
|
||||
"vcmi.lobby.deleteSaveGameTitle" : "Wählt gespeichertes Spiel zum Löschen aus",
|
||||
"vcmi.lobby.deleteMapTitle" : "Wählt ein zu löschendes Szenario",
|
||||
"vcmi.lobby.deleteFile" : "Möchtet Ihr folgende Datei löschen?",
|
||||
"vcmi.lobby.deleteFolder" : "Möchtet Ihr folgenden Ordner löschen?",
|
||||
"vcmi.lobby.deleteMode" : "In den Löschmodus wechseln und zurück",
|
||||
|
||||
"vcmi.lobby.login.title" : "VCMI Online Lobby",
|
||||
"vcmi.lobby.login.username" : "Benutzername:",
|
||||
"vcmi.lobby.login.connecting" : "Verbinde...",
|
||||
@ -153,10 +184,12 @@
|
||||
"vcmi.client.errors.invalidMap" : "{Ungültige Karte oder Kampagne}\n\nDas Spiel konnte nicht gestartet werden! Die ausgewählte Karte oder Kampagne ist möglicherweise ungültig oder beschädigt. Grund:\n%s",
|
||||
"vcmi.client.errors.missingCampaigns" : "{Fehlende Dateien}\n\nEs wurden keine Kampagnendateien gefunden! Möglicherweise verwendest du unvollständige oder beschädigte Heroes 3 Datendateien. Bitte installiere die Spieldaten neu.",
|
||||
"vcmi.server.errors.disconnected" : "{Netzwerkfehler}\n\nDie Verbindung zum Spielserver wurde unterbrochen!",
|
||||
"vcmi.server.errors.playerLeft" : "{Verlassen eines Spielers}\n\n%s Spieler hat die Verbindung zum Spiel unterbrochen!", //%s -> player color
|
||||
"vcmi.server.errors.existingProcess" : "Es läuft ein weiterer vcmiserver-Prozess, bitte beendet diesen zuerst",
|
||||
"vcmi.server.errors.modsToEnable" : "{Erforderliche Mods um das Spiel zu laden}",
|
||||
"vcmi.server.errors.modsToDisable" : "{Folgende Mods müssen deaktiviert werden}",
|
||||
"vcmi.server.errors.modNoDependency" : "Mod {'%s'} konnte nicht geladen werden!\n Sie hängt von Mod {'%s'} ab, die nicht aktiv ist!\n",
|
||||
"vcmi.server.errors.modDependencyLoop" : "Mod {'%s'} konnte nicht geladen werden.!\n Möglicherweise befindet sie sich in einer (weichen) Abhängigkeitsschleife.",
|
||||
"vcmi.server.errors.modConflict" : "Mod {'%s'} konnte nicht geladen werden!\n Konflikte mit aktiver Mod {'%s'}!\n",
|
||||
"vcmi.server.errors.unknownEntity" : "Spielstand konnte nicht geladen werden! Unbekannte Entität '%s' im gespeicherten Spiel gefunden! Der Spielstand ist möglicherweise nicht mit der aktuell installierten Version der Mods kompatibel!",
|
||||
|
||||
@ -339,6 +372,9 @@
|
||||
"vcmi.townHall.missingBase" : "Basis Gebäude %s muss als erstes gebaut werden",
|
||||
"vcmi.townHall.noCreaturesToRecruit" : "Es gibt keine Kreaturen zu rekrutieren!",
|
||||
|
||||
"vcmi.townStructure.bank.borrow" : "Ihr betretet die Bank. Ein Bankangestellter sieht Euch und sagt: \"Wir haben ein spezielles Angebot für Euch gemacht. Ihr könnt bei uns einen Kredit von 2500 Gold für 5 Tage aufnehmen. Ihr werdet jeden Tag 500 Gold zurückzahlen müssen.\"",
|
||||
"vcmi.townStructure.bank.payBack" : "Ihr betretet die Bank. Ein Bankangestellter sieht Euch und sagt: \"Ihr habt Euren Kredit bereits erhalten. Zahlt Ihn ihn zurück, bevor Ihr einen neuen aufnehmt.\"",
|
||||
|
||||
"vcmi.logicalExpressions.anyOf" : "Eines der folgenden:",
|
||||
"vcmi.logicalExpressions.allOf" : "Alles der folgenden:",
|
||||
"vcmi.logicalExpressions.noneOf" : "Keines der folgenden:",
|
||||
@ -347,6 +383,13 @@
|
||||
"vcmi.heroWindow.openCommander.help" : "Zeige Informationen über Kommandanten dieses Helden",
|
||||
"vcmi.heroWindow.openBackpack.hover" : "Artefakt-Rucksack-Fenster öffnen",
|
||||
"vcmi.heroWindow.openBackpack.help" : "Öffnet ein Fenster, das die Verwaltung des Artefakt-Rucksacks erleichtert",
|
||||
"vcmi.heroWindow.sortBackpackByCost.hover" : "Nach Kosten sortieren",
|
||||
"vcmi.heroWindow.sortBackpackByCost.help" : "Artefakte im Rucksack nach Kosten sortieren.",
|
||||
"vcmi.heroWindow.sortBackpackBySlot.hover" : "Nach Slot sortieren",
|
||||
"vcmi.heroWindow.sortBackpackBySlot.help" : "Artefakte im Rucksack nach Ausrüstungsslot sortieren.",
|
||||
"vcmi.heroWindow.sortBackpackByClass.hover" : "Nach Klasse sortieren",
|
||||
"vcmi.heroWindow.sortBackpackByClass.help" : "Artefakte im Rucksack nach Artefaktklasse sortieren. Schatz, Klein, Groß, Relikt",
|
||||
"vcmi.heroWindow.fusingArtifact.fusing" : "Ihr verfügt über alle Komponenten, die für die Fusion der %s benötigt werden. Möchtet Ihr die Verschmelzung durchführen? {Alle Komponenten werden bei der Fusion verbraucht.}",
|
||||
|
||||
"vcmi.tavernWindow.inviteHero" : "Helden einladen",
|
||||
|
||||
@ -523,7 +566,9 @@
|
||||
"core.seerhut.quest.reachDate.visit.3" : "Geschlossen bis %s.",
|
||||
"core.seerhut.quest.reachDate.visit.4" : "Geschlossen bis %s.",
|
||||
"core.seerhut.quest.reachDate.visit.5" : "Geschlossen bis %s.",
|
||||
|
||||
|
||||
"mapObject.core.hillFort.object.description" : "Aufwertungen von Kreaturen. Die Stufen 1 - 4 sind billiger als in der zugehörigen Stadt.",
|
||||
|
||||
"core.bonus.ADDITIONAL_ATTACK.name": "Doppelschlag",
|
||||
"core.bonus.ADDITIONAL_ATTACK.description": "Greift zweimal an",
|
||||
"core.bonus.ADDITIONAL_RETALIATION.name": "Zusätzliche Vergeltungsmaßnahmen",
|
||||
@ -671,5 +716,13 @@
|
||||
"core.bonus.WATER_IMMUNITY.name": "Wasser-Immunität",
|
||||
"core.bonus.WATER_IMMUNITY.description": "Immun gegen alle Zauber der Wasserschule",
|
||||
"core.bonus.WIDE_BREATH.name": "Breiter Atem",
|
||||
"core.bonus.WIDE_BREATH.description": "Breiter Atem-Angriff (mehrere Felder)"
|
||||
"core.bonus.WIDE_BREATH.description": "Breiter Atem-Angriff (mehrere Felder)",
|
||||
"core.bonus.DISINTEGRATE.name": "Auflösen",
|
||||
"core.bonus.DISINTEGRATE.description": "Kein Leichnam bleibt nach dem Tod übrig",
|
||||
"core.bonus.INVINCIBLE.name": "Unbesiegbar",
|
||||
"core.bonus.INVINCIBLE.description": "Kann durch nichts beeinflusst werden",
|
||||
"core.bonus.MECHANICAL.name": "Mechanisch",
|
||||
"core.bonus.MECHANICAL.description": "Immunität gegen viele Effekte, reparierbar",
|
||||
"core.bonus.PRISM_HEX_ATTACK_BREATH.name": "Prisma-Atem",
|
||||
"core.bonus.PRISM_HEX_ATTACK_BREATH.description": "Prisma-Atem-Angriff (drei Richtungen)"
|
||||
}
|
||||
|
@ -12,9 +12,11 @@
|
||||
"vcmi.adventureMap.monsterThreat.levels.9" : "Przytłaczający",
|
||||
"vcmi.adventureMap.monsterThreat.levels.10" : "Śmiertelny",
|
||||
"vcmi.adventureMap.monsterThreat.levels.11" : "Nie do pokonania",
|
||||
"vcmi.adventureMap.monsterLevel" : "\n\n%Jednostka %ATTACK_TYPE %LEVEL poziomu z miasta %TOWN",
|
||||
"vcmi.adventureMap.monsterLevel" : "\n\nJednostka %ATTACK_TYPE %LEVEL poziomu z miasta %TOWN",
|
||||
"vcmi.adventureMap.monsterMeleeType" : "Walcząca wręcz",
|
||||
"vcmi.adventureMap.monsterRangedType" : "Dystansowa",
|
||||
"vcmi.adventureMap.search.hover" : "Wyszukiwarka obiektów",
|
||||
"vcmi.adventureMap.search.help" : "Wybierz obiekt który chcesz znaleźć na mapie.",
|
||||
|
||||
"vcmi.adventureMap.confirmRestartGame" : "Czy na pewno chcesz zrestartować grę?",
|
||||
"vcmi.adventureMap.noTownWithMarket" : "Brak dostępnego targowiska!",
|
||||
@ -26,6 +28,13 @@
|
||||
"vcmi.adventureMap.movementPointsHeroInfo" : "(Punkty ruchu: %REMAINING / %POINTS)",
|
||||
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Wybacz, powtórka ruchu wroga nie została jeszcze zaimplementowana!",
|
||||
|
||||
"vcmi.bonusSource.artifact" : "Artefakt",
|
||||
"vcmi.bonusSource.creature" : "Umiej.",
|
||||
"vcmi.bonusSource.spell" : "Zaklęcie",
|
||||
"vcmi.bonusSource.hero" : "Bohater",
|
||||
"vcmi.bonusSource.commander" : "Dowódca",
|
||||
"vcmi.bonusSource.other" : "Inne",
|
||||
|
||||
"vcmi.capitalColors.0" : "Czerwony",
|
||||
"vcmi.capitalColors.1" : "Niebieski",
|
||||
"vcmi.capitalColors.2" : "Brązowy",
|
||||
@ -40,6 +49,12 @@
|
||||
"vcmi.heroOverview.secondarySkills" : "Umiejętności drugorzędne",
|
||||
"vcmi.heroOverview.spells" : "Zaklęcia",
|
||||
|
||||
"vcmi.quickExchange.moveUnit" : "Przenieś jednostkę",
|
||||
"vcmi.quickExchange.moveAllUnits" : "Przenieś wszystkie jednostki",
|
||||
"vcmi.quickExchange.swapAllUnits" : "Zamień armię",
|
||||
"vcmi.quickExchange.moveAllArtifacts" : "Przenieś wszystkie artefakty",
|
||||
"vcmi.quickExchange.swapAllArtifacts" : "Zamień artefakty",
|
||||
|
||||
"vcmi.radialWheel.mergeSameUnit" : "Złącz takie same stworzenia",
|
||||
"vcmi.radialWheel.fillSingleUnit" : "Wypełnij pojedynczymi stworzeniami",
|
||||
"vcmi.radialWheel.splitSingleUnit" : "Wydziel pojedyncze stworzenie",
|
||||
@ -59,14 +74,25 @@
|
||||
"vcmi.radialWheel.moveDown" : "Przenieś w dół",
|
||||
"vcmi.radialWheel.moveBottom" : "Przenieś na spód",
|
||||
|
||||
"vcmi.randomMap.description" : "Mapa stworzona przez generator map losowych.\nSzablon: %s, rozmiar %dx%d, poziomów %d, graczy %d, komputerowych %d, woda %s, potwory %s, mapa VCMI",
|
||||
"vcmi.randomMap.description.isHuman" : ", %s jest człowiekiem",
|
||||
"vcmi.randomMap.description.townChoice" : ", %s wybiera %s",
|
||||
"vcmi.randomMap.description.water.none" : "brak",
|
||||
"vcmi.randomMap.description.water.normal" : "norm.",
|
||||
"vcmi.randomMap.description.water.islands" : "wyspy",
|
||||
"vcmi.randomMap.description.monster.weak" : "słabi",
|
||||
"vcmi.randomMap.description.monster.normal" : "norm.",
|
||||
"vcmi.randomMap.description.monster.strong" : "silni",
|
||||
|
||||
"vcmi.spellBook.search" : "szukaj...",
|
||||
|
||||
"vcmi.spellResearch.canNotAfford" : "Nie stać Cię na zastąpienie {%SPELL1} przez {%SPELL2}, ale za to możesz odrzucić ten czar i kontynuować badania.",
|
||||
"vcmi.spellResearch.comeAgain" : "Badania zostały już przeprowadzone dzisiaj. Wróć jutro.",
|
||||
"vcmi.spellResearch.pay" : "Czy chcesz zastąpić {%SPELL1} czarem {%SPELL2}? Czy odrzucić ten czar i kontynuować badania?",
|
||||
"vcmi.spellResearch.pay" : "Czy chcesz zastąpić {%SPELL1} zaklęciem {%SPELL2}? Czy odrzucić ten czar i kontynuować badania?",
|
||||
"vcmi.spellResearch.research" : "Zamień zaklęcia",
|
||||
"vcmi.spellResearch.skip" : "Kontynuuj badania",
|
||||
"vcmi.spellResearch.abort" : "Anuluj",
|
||||
"vcmi.spellResearch.noMoreSpells" : "Nie ma już więcej zaklęć do zbadania.",
|
||||
|
||||
"vcmi.mainMenu.serverConnecting" : "Łączenie...",
|
||||
"vcmi.mainMenu.serverAddressEnter" : "Wprowadź adres:",
|
||||
@ -157,6 +183,7 @@
|
||||
"vcmi.server.errors.modsToEnable" : "{Następujące mody są wymagane do wczytania gry}",
|
||||
"vcmi.server.errors.modsToDisable" : "{Następujące mody muszą zostać wyłączone}",
|
||||
"vcmi.server.errors.modNoDependency" : "Nie udało się wczytać moda {'%s'}!\n Jest on zależny od moda {'%s'} który nie jest aktywny!\n",
|
||||
"vcmi.server.errors.modDependencyLoop" : "Nie udało się wczytać moda {'%s'}!\n Być może znajduje się w pętli zależności",
|
||||
"vcmi.server.errors.modConflict" : "Nie udało się wczytać moda {'%s'}!\n Konflikty z aktywnym modem {'%s'}!\n",
|
||||
"vcmi.server.errors.unknownEntity" : "Nie udało się wczytać zapisu! Nieznany element '%s' znaleziony w pliku zapisu! Zapis może nie być zgodny z aktualnie zainstalowaną wersją modów!",
|
||||
|
||||
@ -356,6 +383,7 @@
|
||||
"vcmi.heroWindow.sortBackpackBySlot.help" : "Sortuj artefakty w sakwie według umiejscowienia na ciele",
|
||||
"vcmi.heroWindow.sortBackpackByClass.hover" : "Sortuj wg. jakości",
|
||||
"vcmi.heroWindow.sortBackpackByClass.help" : "Sortuj artefakty w sakwie według jakości: Skarb, Pomniejszy, Potężny, Relikt",
|
||||
"vcmi.heroWindow.fusingArtifact.fusing" : "Posiadasz wszystkie niezbędne komponenty do stworzenia %s. Czy chcesz wykonać fuzję? {Wszystkie komponenty zostaną użyte}",
|
||||
|
||||
"vcmi.tavernWindow.inviteHero" : "Zaproś bohatera",
|
||||
|
||||
@ -686,5 +714,9 @@
|
||||
"core.bonus.DISINTEGRATE.name": "Rozpadanie",
|
||||
"core.bonus.DISINTEGRATE.description": "Po śmierci nie pozostaje żaden trup",
|
||||
"core.bonus.INVINCIBLE.name": "Niezwyciężony",
|
||||
"core.bonus.INVINCIBLE.description": "Nic nie może mieć na niego wpływu"
|
||||
"core.bonus.INVINCIBLE.description": "Nic nie może mieć na niego wpływu",
|
||||
"core.bonus.MECHANICAL.name": "Mechaniczny",
|
||||
"core.bonus.MECHANICAL.description": "Odporny na wiele efektów, naprawialny",
|
||||
"core.bonus.PRISM_HEX_ATTACK_BREATH.name": "Pryzmatyczny oddech",
|
||||
"core.bonus.PRISM_HEX_ATTACK_BREATH.description": "Atakuje pryzmatycznym zionięciem (trzy kierunki)"
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
ArtifactsUIController::ArtifactsUIController()
|
||||
{
|
||||
numOfMovedArts = 0;
|
||||
numOfArtsAskAssembleSession = 0;
|
||||
}
|
||||
|
||||
bool ArtifactsUIController::askToAssemble(const ArtifactLocation & al, const bool onlyEquipped, const bool checkIgnored)
|
||||
|
@ -394,7 +394,7 @@ void ClientCommandManager::handleDef2bmpCommand(std::istringstream& singleWordBu
|
||||
{
|
||||
std::string URI;
|
||||
singleWordBuffer >> URI;
|
||||
auto anim = GH.renderHandler().loadAnimation(AnimationPath::builtin(URI), EImageBlitMode::ALPHA);
|
||||
auto anim = GH.renderHandler().loadAnimation(AnimationPath::builtin(URI), EImageBlitMode::SIMPLE);
|
||||
anim->exportBitmaps(VCMIDirs::get().userExtractedPath());
|
||||
}
|
||||
|
||||
|
@ -226,7 +226,7 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyUpdateState(LobbyUpdateState &
|
||||
else
|
||||
lobby->updateAfterStateChange();
|
||||
|
||||
if(pack.hostChanged)
|
||||
if(pack.hostChanged || pack.refreshList)
|
||||
lobby->toggleMode(handler.isHost());
|
||||
}
|
||||
|
||||
|
@ -881,9 +881,10 @@ uint32_t CastAnimation::getAttackClimaxFrame() const
|
||||
return maxFrames / 2;
|
||||
}
|
||||
|
||||
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, int effects, bool reversed):
|
||||
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, int effects, float transparencyFactor, bool reversed):
|
||||
BattleAnimation(owner),
|
||||
animation(GH.renderHandler().loadAnimation(animationName, EImageBlitMode::ALPHA)),
|
||||
animation(GH.renderHandler().loadAnimation(animationName, EImageBlitMode::SIMPLE)),
|
||||
transparencyFactor(transparencyFactor),
|
||||
effectFlags(effects),
|
||||
effectFinished(false),
|
||||
reversed(reversed)
|
||||
@ -892,32 +893,32 @@ EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath &
|
||||
}
|
||||
|
||||
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<BattleHex> hex, int effects, bool reversed):
|
||||
EffectAnimation(owner, animationName, effects, reversed)
|
||||
EffectAnimation(owner, animationName, effects, 1.0f, reversed)
|
||||
{
|
||||
battlehexes = hex;
|
||||
}
|
||||
|
||||
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHex hex, int effects, bool reversed):
|
||||
EffectAnimation(owner, animationName, effects, reversed)
|
||||
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHex hex, int effects, float transparencyFactor, bool reversed):
|
||||
EffectAnimation(owner, animationName, effects, transparencyFactor, reversed)
|
||||
{
|
||||
assert(hex.isValid());
|
||||
battlehexes.push_back(hex);
|
||||
}
|
||||
|
||||
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<Point> pos, int effects, bool reversed):
|
||||
EffectAnimation(owner, animationName, effects, reversed)
|
||||
EffectAnimation(owner, animationName, effects, 1.0f, reversed)
|
||||
{
|
||||
positions = pos;
|
||||
}
|
||||
|
||||
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos, int effects, bool reversed):
|
||||
EffectAnimation(owner, animationName, effects, reversed)
|
||||
EffectAnimation(owner, animationName, effects, 1.0f, reversed)
|
||||
{
|
||||
positions.push_back(pos);
|
||||
}
|
||||
|
||||
EffectAnimation::EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos, BattleHex hex, int effects, bool reversed):
|
||||
EffectAnimation(owner, animationName, effects, reversed)
|
||||
EffectAnimation(owner, animationName, effects, 1.0f, reversed)
|
||||
{
|
||||
assert(hex.isValid());
|
||||
battlehexes.push_back(hex);
|
||||
@ -951,6 +952,7 @@ bool EffectAnimation::init()
|
||||
be.effectID = ID;
|
||||
be.animation = animation;
|
||||
be.currentFrame = 0;
|
||||
be.transparencyFactor = transparencyFactor;
|
||||
be.type = reversed ? BattleEffect::AnimType::REVERSE : BattleEffect::AnimType::DEFAULT;
|
||||
|
||||
for (size_t i = 0; i < std::max(battlehexes.size(), positions.size()); ++i)
|
||||
|
@ -309,9 +309,10 @@ public:
|
||||
class EffectAnimation : public BattleAnimation
|
||||
{
|
||||
std::string soundName;
|
||||
int effectFlags;
|
||||
float transparencyFactor;
|
||||
bool effectFinished;
|
||||
bool reversed;
|
||||
int effectFlags;
|
||||
|
||||
std::shared_ptr<CAnimation> animation;
|
||||
std::vector<Point> positions;
|
||||
@ -335,14 +336,14 @@ public:
|
||||
};
|
||||
|
||||
/// Create animation with screen-wide effect
|
||||
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, int effects = 0, bool reversed = false);
|
||||
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, int effects = 0, float transparencyFactor = 1.f, bool reversed = false);
|
||||
|
||||
/// Create animation positioned at point(s). Note that positions must be are absolute, including battleint position offset
|
||||
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos , int effects = 0, bool reversed = false);
|
||||
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<Point> pos , int effects = 0, bool reversed = false);
|
||||
|
||||
/// Create animation positioned at certain hex(es)
|
||||
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHex hex , int effects = 0, bool reversed = false);
|
||||
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, BattleHex hex , int effects = 0, float transparencyFactor = 1.0f, bool reversed = false);
|
||||
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, std::vector<BattleHex> hex, int effects = 0, bool reversed = false);
|
||||
|
||||
EffectAnimation(BattleInterface & owner, const AnimationPath & animationName, Point pos, BattleHex hex, int effects = 0, bool reversed = false);
|
||||
|
@ -44,7 +44,7 @@ void BattleEffectsController::displayEffect(EBattleEffect effect, const BattleHe
|
||||
displayEffect(effect, AudioPath(), destTile);
|
||||
}
|
||||
|
||||
void BattleEffectsController::displayEffect(EBattleEffect effect, const AudioPath & soundFile, const BattleHex & destTile)
|
||||
void BattleEffectsController::displayEffect(EBattleEffect effect, const AudioPath & soundFile, const BattleHex & destTile, float transparencyFactor)
|
||||
{
|
||||
size_t effectID = static_cast<size_t>(effect);
|
||||
|
||||
@ -52,7 +52,7 @@ void BattleEffectsController::displayEffect(EBattleEffect effect, const AudioPat
|
||||
|
||||
CCS->soundh->playSound( soundFile );
|
||||
|
||||
owner.stacksController->addNewAnim(new EffectAnimation(owner, customAnim, destTile));
|
||||
owner.stacksController->addNewAnim(new EffectAnimation(owner, customAnim, destTile, 0, transparencyFactor));
|
||||
}
|
||||
|
||||
void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bte)
|
||||
@ -69,7 +69,7 @@ void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bt
|
||||
switch(static_cast<BonusType>(bte.effect))
|
||||
{
|
||||
case BonusType::HP_REGENERATION:
|
||||
displayEffect(EBattleEffect::REGENERATION, AudioPath::builtin("REGENER"), stack->getPosition());
|
||||
displayEffect(EBattleEffect::REGENERATION, AudioPath::builtin("REGENER"), stack->getPosition(), 0.5);
|
||||
break;
|
||||
case BonusType::MANA_DRAIN:
|
||||
displayEffect(EBattleEffect::MANA_DRAIN, AudioPath::builtin("MANADRAI"), stack->getPosition());
|
||||
@ -78,7 +78,7 @@ void BattleEffectsController::battleTriggerEffect(const BattleTriggerEffect & bt
|
||||
displayEffect(EBattleEffect::POISON, AudioPath::builtin("POISON"), stack->getPosition());
|
||||
break;
|
||||
case BonusType::FEAR:
|
||||
displayEffect(EBattleEffect::FEAR, AudioPath::builtin("FEAR"), stack->getPosition());
|
||||
displayEffect(EBattleEffect::FEAR, AudioPath::builtin("FEAR"), stack->getPosition(), 0.5);
|
||||
break;
|
||||
case BonusType::MORALE:
|
||||
{
|
||||
@ -124,6 +124,7 @@ void BattleEffectsController::collectRenderableObjects(BattleRenderer & renderer
|
||||
currentFrame %= elem.animation->size();
|
||||
|
||||
auto img = elem.animation->getImage(currentFrame, static_cast<size_t>(elem.type));
|
||||
img->setAlpha(255 * elem.transparencyFactor);
|
||||
|
||||
canvas.draw(img, elem.pos);
|
||||
});
|
||||
|
@ -39,7 +39,8 @@ struct BattleEffect
|
||||
|
||||
AnimType type;
|
||||
Point pos; //position on the screen
|
||||
float currentFrame;
|
||||
float currentFrame = 0.0;
|
||||
float transparencyFactor = 1.0;
|
||||
std::shared_ptr<CAnimation> animation;
|
||||
int effectID; //uniqueID equal ot ID of appropriate CSpellEffectAnim
|
||||
BattleHex tile; //Indicates if effect which hex the effect is drawn on
|
||||
@ -65,7 +66,7 @@ public:
|
||||
|
||||
//displays custom effect on the battlefield
|
||||
void displayEffect(EBattleEffect effect, const BattleHex & destTile);
|
||||
void displayEffect(EBattleEffect effect, const AudioPath & soundFile, const BattleHex & destTile);
|
||||
void displayEffect(EBattleEffect effect, const AudioPath & soundFile, const BattleHex & destTile, float transparencyFactor = 1.f);
|
||||
|
||||
void battleTriggerEffect(const BattleTriggerEffect & bte);
|
||||
|
||||
|
@ -114,7 +114,7 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
|
||||
|
||||
//preparing cells and hexes
|
||||
cellBorder = GH.renderHandler().loadImage(ImagePath::builtin("CCELLGRD.BMP"), EImageBlitMode::COLORKEY);
|
||||
cellShade = GH.renderHandler().loadImage(ImagePath::builtin("CCELLSHD.BMP"), EImageBlitMode::ALPHA);
|
||||
cellShade = GH.renderHandler().loadImage(ImagePath::builtin("CCELLSHD.BMP"), EImageBlitMode::SIMPLE);
|
||||
cellUnitMovementHighlight = GH.renderHandler().loadImage(ImagePath::builtin("UnitMovementHighlight.PNG"), EImageBlitMode::COLORKEY);
|
||||
cellUnitMaxMovementHighlight = GH.renderHandler().loadImage(ImagePath::builtin("UnitMaxMovementHighlight.PNG"), EImageBlitMode::COLORKEY);
|
||||
|
||||
@ -124,8 +124,6 @@ BattleFieldController::BattleFieldController(BattleInterface & owner):
|
||||
rangedFullDamageLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsGreen.json"), EImageBlitMode::COLORKEY);
|
||||
shootingRangeLimitImages = GH.renderHandler().loadAnimation(AnimationPath::builtin("battle/rangeHighlights/rangeHighlightsRed.json"), EImageBlitMode::COLORKEY);
|
||||
|
||||
cellShade->setShadowEnabled(true);
|
||||
|
||||
if(!owner.siegeController)
|
||||
{
|
||||
auto bfieldType = owner.getBattle()->battleGetBattlefieldType();
|
||||
|
@ -535,9 +535,9 @@ void BattleInterface::displaySpellAnimationQueue(const CSpell * spell, const CSp
|
||||
flags |= EffectAnimation::SCREEN_FILL;
|
||||
|
||||
if (!destinationTile.isValid())
|
||||
stacksController->addNewAnim(new EffectAnimation(*this, animation.resourceName, flags));
|
||||
stacksController->addNewAnim(new EffectAnimation(*this, animation.resourceName, flags, animation.transparency));
|
||||
else
|
||||
stacksController->addNewAnim(new EffectAnimation(*this, animation.resourceName, destinationTile, flags));
|
||||
stacksController->addNewAnim(new EffectAnimation(*this, animation.resourceName, destinationTile, flags, animation.transparency));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -398,7 +398,7 @@ BattleHero::BattleHero(const BattleInterface & owner, const CGHeroInstance * her
|
||||
else
|
||||
animationPath = hero->getHeroClass()->imageBattleMale;
|
||||
|
||||
animation = GH.renderHandler().loadAnimation(animationPath, EImageBlitMode::ALPHA);
|
||||
animation = GH.renderHandler().loadAnimation(animationPath, EImageBlitMode::WITH_SHADOW);
|
||||
|
||||
pos.w = 64;
|
||||
pos.h = 136;
|
||||
|
@ -50,11 +50,11 @@ void BattleObstacleController::loadObstacleImage(const CObstacleInstance & oi)
|
||||
if (oi.obstacleType == CObstacleInstance::ABSOLUTE_OBSTACLE)
|
||||
{
|
||||
// obstacle uses single bitmap image for animations
|
||||
obstacleImages[oi.uniqueID] = GH.renderHandler().loadImage(animationName.toType<EResType::IMAGE>(), EImageBlitMode::COLORKEY);
|
||||
obstacleImages[oi.uniqueID] = GH.renderHandler().loadImage(animationName.toType<EResType::IMAGE>(), EImageBlitMode::SIMPLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
obstacleAnimations[oi.uniqueID] = GH.renderHandler().loadAnimation(animationName, EImageBlitMode::COLORKEY);
|
||||
obstacleAnimations[oi.uniqueID] = GH.renderHandler().loadAnimation(animationName, EImageBlitMode::SIMPLE);
|
||||
obstacleImages[oi.uniqueID] = obstacleAnimations[oi.uniqueID]->getImage(0);
|
||||
}
|
||||
}
|
||||
@ -78,7 +78,7 @@ void BattleObstacleController::obstacleRemoved(const std::vector<ObstacleChanges
|
||||
if(animationPath.empty())
|
||||
continue;
|
||||
|
||||
auto animation = GH.renderHandler().loadAnimation(animationPath, EImageBlitMode::COLORKEY);
|
||||
auto animation = GH.renderHandler().loadAnimation(animationPath, EImageBlitMode::SIMPLE);
|
||||
auto first = animation->getImage(0, 0);
|
||||
if(!first)
|
||||
continue;
|
||||
@ -105,7 +105,7 @@ void BattleObstacleController::obstaclePlaced(const std::vector<std::shared_ptr<
|
||||
if(!oi->visibleForSide(side, owner.getBattle()->battleHasNativeStack(side)))
|
||||
continue;
|
||||
|
||||
auto animation = GH.renderHandler().loadAnimation(oi->getAppearAnimation(), EImageBlitMode::ALPHA);
|
||||
auto animation = GH.renderHandler().loadAnimation(oi->getAppearAnimation(), EImageBlitMode::SIMPLE);
|
||||
auto first = animation->getImage(0, 0);
|
||||
if(!first)
|
||||
continue;
|
||||
|
@ -636,7 +636,7 @@ void BattleStacksController::stackAttacking( const StackAttackInfo & info )
|
||||
{
|
||||
owner.addToAnimationStage(EAnimationEvents::AFTER_HIT, [=]()
|
||||
{
|
||||
owner.effectsController->displayEffect(EBattleEffect::DRAIN_LIFE, AudioPath::builtin("DRAINLIF"), attacker->getPosition());
|
||||
owner.effectsController->displayEffect(EBattleEffect::DRAIN_LIFE, AudioPath::builtin("DRAINLIF"), attacker->getPosition(), 0.5);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../render/ColorFilter.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../render/IRenderHandler.h"
|
||||
|
||||
static const ColorRGBA creatureBlueBorder = { 0, 255, 255, 255 };
|
||||
@ -199,8 +200,8 @@ CreatureAnimation::CreatureAnimation(const AnimationPath & name_, TSpeedControll
|
||||
speedController(controller),
|
||||
once(false)
|
||||
{
|
||||
forward = GH.renderHandler().loadAnimation(name_, EImageBlitMode::ALPHA);
|
||||
reverse = GH.renderHandler().loadAnimation(name_, EImageBlitMode::ALPHA);
|
||||
forward = GH.renderHandler().loadAnimation(name_, EImageBlitMode::WITH_SHADOW_AND_OVERLAY);
|
||||
reverse = GH.renderHandler().loadAnimation(name_, EImageBlitMode::WITH_SHADOW_AND_OVERLAY);
|
||||
|
||||
// if necessary, add one frame into vcmi-only group DEAD
|
||||
if(forward->size(size_t(ECreatureAnimType::DEAD)) == 0)
|
||||
@ -339,15 +340,14 @@ void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter,
|
||||
|
||||
if(image)
|
||||
{
|
||||
image->setShadowEnabled(true);
|
||||
image->setOverlayEnabled(isIdle());
|
||||
if (isIdle())
|
||||
image->setOverlayColor(genBorderColor(getBorderStrength(elapsedTime), border));
|
||||
else
|
||||
image->setOverlayColor(Colors::TRANSPARENCY);
|
||||
|
||||
image->adjustPalette(shifter, 0);
|
||||
|
||||
canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -765,9 +765,9 @@ void OptionsTab::SelectionWindow::sliderMove(int slidPos)
|
||||
redraw();
|
||||
}
|
||||
|
||||
bool OptionsTab::SelectionWindow::receiveEvent(const Point & position, int eventType) const
|
||||
void OptionsTab::SelectionWindow::notFocusedClick()
|
||||
{
|
||||
return true; // capture click also outside of window
|
||||
close();
|
||||
}
|
||||
|
||||
void OptionsTab::SelectionWindow::clickReleased(const Point & cursorPosition)
|
||||
@ -775,12 +775,6 @@ void OptionsTab::SelectionWindow::clickReleased(const Point & cursorPosition)
|
||||
if(slider && slider->pos.isInside(cursorPosition))
|
||||
return;
|
||||
|
||||
if(!pos.isInside(cursorPosition))
|
||||
{
|
||||
close();
|
||||
return;
|
||||
}
|
||||
|
||||
int elem = getElement(cursorPosition);
|
||||
|
||||
setElement(elem, true);
|
||||
@ -898,15 +892,9 @@ OptionsTab::HandicapWindow::HandicapWindow()
|
||||
center();
|
||||
}
|
||||
|
||||
bool OptionsTab::HandicapWindow::receiveEvent(const Point & position, int eventType) const
|
||||
void OptionsTab::HandicapWindow::notFocusedClick()
|
||||
{
|
||||
return true; // capture click also outside of window
|
||||
}
|
||||
|
||||
void OptionsTab::HandicapWindow::clickReleased(const Point & cursorPosition)
|
||||
{
|
||||
if(!pos.isInside(cursorPosition)) // make it possible to close window by touching/clicking outside of window
|
||||
close();
|
||||
close();
|
||||
}
|
||||
|
||||
OptionsTab::SelectedBox::SelectedBox(Point position, PlayerSettings & playerSettings, SelType type)
|
||||
|
@ -63,8 +63,7 @@ public:
|
||||
std::map<PlayerColor, std::map<EGameResID, std::shared_ptr<CTextInput>>> textinputs;
|
||||
std::vector<std::shared_ptr<CButton>> buttons;
|
||||
|
||||
bool receiveEvent(const Point & position, int eventType) const override;
|
||||
void clickReleased(const Point & cursorPosition) override;
|
||||
void notFocusedClick() override;
|
||||
public:
|
||||
HandicapWindow();
|
||||
};
|
||||
@ -167,7 +166,7 @@ private:
|
||||
|
||||
void sliderMove(int slidPos);
|
||||
|
||||
bool receiveEvent(const Point & position, int eventType) const override;
|
||||
void notFocusedClick() override;
|
||||
void clickReleased(const Point & cursorPosition) override;
|
||||
void showPopupWindow(const Point & cursorPosition) override;
|
||||
|
||||
|
@ -42,9 +42,11 @@
|
||||
#include "../../lib/mapping/CMapInfo.h"
|
||||
#include "../../lib/mapping/CMapHeader.h"
|
||||
#include "../../lib/mapping/MapFormat.h"
|
||||
#include "../../lib/networkPacks/PacksForLobby.h"
|
||||
#include "../../lib/texts/CGeneralTextHandler.h"
|
||||
#include "../../lib/texts/TextOperations.h"
|
||||
#include "../../lib/TerrainHandler.h"
|
||||
#include "../../lib/UnlockGuard.h"
|
||||
|
||||
bool mapSorter::operator()(const std::shared_ptr<ElementInfo> aaa, const std::shared_ptr<ElementInfo> bbb)
|
||||
{
|
||||
@ -152,7 +154,7 @@ static ESortBy getSortBySelectionScreen(ESelectionScreen Type)
|
||||
}
|
||||
|
||||
SelectionTab::SelectionTab(ESelectionScreen Type)
|
||||
: CIntObject(LCLICK | SHOW_POPUP | KEYBOARD | DOUBLECLICK), callOnSelect(nullptr), tabType(Type), selectionPos(0), sortModeAscending(true), inputNameRect{32, 539, 350, 20}, curFolder(""), currentMapSizeFilter(0), showRandom(false)
|
||||
: CIntObject(LCLICK | SHOW_POPUP | KEYBOARD | DOUBLECLICK), callOnSelect(nullptr), tabType(Type), selectionPos(0), sortModeAscending(true), inputNameRect{32, 539, 350, 20}, curFolder(""), currentMapSizeFilter(0), showRandom(false), deleteMode(false)
|
||||
{
|
||||
OBJECT_CONSTRUCTION;
|
||||
|
||||
@ -192,20 +194,23 @@ SelectionTab::SelectionTab(ESelectionScreen Type)
|
||||
|
||||
int positionsToShow = 18;
|
||||
std::string tabTitle;
|
||||
std::string tabTitleDelete;
|
||||
switch(tabType)
|
||||
{
|
||||
case ESelectionScreen::newGame:
|
||||
tabTitle = CGI->generaltexth->arraytxt[229];
|
||||
tabTitle = "{" + CGI->generaltexth->arraytxt[229] + "}";
|
||||
tabTitleDelete = "{red|" + CGI->generaltexth->translate("vcmi.lobby.deleteMapTitle") + "}";
|
||||
break;
|
||||
case ESelectionScreen::loadGame:
|
||||
tabTitle = CGI->generaltexth->arraytxt[230];
|
||||
tabTitle = "{" + CGI->generaltexth->arraytxt[230] + "}";
|
||||
tabTitleDelete = "{red|" + CGI->generaltexth->translate("vcmi.lobby.deleteSaveGameTitle") + "}";
|
||||
break;
|
||||
case ESelectionScreen::saveGame:
|
||||
positionsToShow = 16;
|
||||
tabTitle = CGI->generaltexth->arraytxt[231];
|
||||
tabTitle = "{" + CGI->generaltexth->arraytxt[231] + "}";
|
||||
break;
|
||||
case ESelectionScreen::campaignList:
|
||||
tabTitle = CGI->generaltexth->allTexts[726];
|
||||
tabTitle = "{" + CGI->generaltexth->allTexts[726] + "}";
|
||||
setRedrawParent(true); // we use parent background so we need to make sure it's will be redrawn too
|
||||
pos.w = parent->pos.w;
|
||||
pos.h = parent->pos.h;
|
||||
@ -225,12 +230,26 @@ SelectionTab::SelectionTab(ESelectionScreen Type)
|
||||
auto sortByDate = std::make_shared<CButton>(Point(371, 85), AnimationPath::builtin("selectionTabSortDate"), CButton::tooltip("", CGI->generaltexth->translate("vcmi.lobby.sortDate")), std::bind(&SelectionTab::sortBy, this, ESortBy::_changeDate), EShortcut::MAPS_SORT_CHANGEDATE);
|
||||
sortByDate->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("lobby/selectionTabSortDate")));
|
||||
buttonsSortBy.push_back(sortByDate);
|
||||
|
||||
if(tabType == ESelectionScreen::loadGame || tabType == ESelectionScreen::newGame)
|
||||
{
|
||||
buttonDeleteMode = std::make_shared<CButton>(Point(367, 18), AnimationPath::builtin("lobby/deleteButton"), CButton::tooltip("", CGI->generaltexth->translate("vcmi.lobby.deleteMode")), [this, tabTitle, tabTitleDelete](){
|
||||
deleteMode = !deleteMode;
|
||||
if(deleteMode)
|
||||
labelTabTitle->setText(tabTitleDelete);
|
||||
else
|
||||
labelTabTitle->setText(tabTitle);
|
||||
});
|
||||
|
||||
if(tabType == ESelectionScreen::newGame)
|
||||
buttonDeleteMode->setEnabled(false);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < positionsToShow; i++)
|
||||
listItems.push_back(std::make_shared<ListItem>(Point(30, 129 + i * 25)));
|
||||
|
||||
labelTabTitle = std::make_shared<CLabel>(205, 28, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW, tabTitle);
|
||||
labelTabTitle = std::make_shared<CLabel>(205, 28, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, tabTitle);
|
||||
slider = std::make_shared<CSlider>(Point(372, 86 + (enableUiEnhancements ? 30 : 0)), (tabType != ESelectionScreen::saveGame ? 480 : 430) - (enableUiEnhancements ? 30 : 0), std::bind(&SelectionTab::sliderMove, this, _1), positionsToShow, (int)curItems.size(), 0, Orientation::VERTICAL, CSlider::BLUE);
|
||||
slider->setPanningStep(24);
|
||||
|
||||
@ -242,10 +261,10 @@ SelectionTab::SelectionTab(ESelectionScreen Type)
|
||||
|
||||
void SelectionTab::toggleMode()
|
||||
{
|
||||
allItems.clear();
|
||||
curItems.clear();
|
||||
if(CSH->isGuest())
|
||||
{
|
||||
allItems.clear();
|
||||
curItems.clear();
|
||||
if(slider)
|
||||
slider->block(true);
|
||||
}
|
||||
@ -263,9 +282,12 @@ void SelectionTab::toggleMode()
|
||||
}
|
||||
|
||||
case ESelectionScreen::loadGame:
|
||||
inputName->disable();
|
||||
parseSaves(getFiles("Saves/", EResType::SAVEGAME));
|
||||
break;
|
||||
{
|
||||
inputName->disable();
|
||||
auto unsupported = parseSaves(getFiles("Saves/", EResType::SAVEGAME));
|
||||
handleUnsupportedSavegames(unsupported);
|
||||
break;
|
||||
}
|
||||
|
||||
case ESelectionScreen::saveGame:
|
||||
parseSaves(getFiles("Saves/", EResType::SAVEGAME));
|
||||
@ -309,7 +331,35 @@ void SelectionTab::clickReleased(const Point & cursorPosition)
|
||||
|
||||
if(line != -1 && curItems.size() > line)
|
||||
{
|
||||
select(line);
|
||||
if(!deleteMode)
|
||||
select(line);
|
||||
else
|
||||
{
|
||||
int py = line + slider->getValue();
|
||||
vstd::amax(py, 0);
|
||||
vstd::amin(py, curItems.size() - 1);
|
||||
|
||||
if(curItems[py]->isFolder && boost::algorithm::starts_with(curItems[py]->folderName, ".."))
|
||||
{
|
||||
select(line);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!curItems[py]->isFolder)
|
||||
CInfoWindow::showYesNoDialog(CGI->generaltexth->translate("vcmi.lobby.deleteFile") + "\n\n" + curItems[py]->fullFileURI, std::vector<std::shared_ptr<CComponent>>(), [this, py](){
|
||||
LobbyDelete ld;
|
||||
ld.type = tabType == ESelectionScreen::newGame ? LobbyDelete::EType::RANDOMMAP : LobbyDelete::EType::SAVEGAME;
|
||||
ld.name = curItems[py]->fileURI;
|
||||
CSH->sendLobbyPack(ld);
|
||||
}, nullptr);
|
||||
else
|
||||
CInfoWindow::showYesNoDialog(CGI->generaltexth->translate("vcmi.lobby.deleteFolder") + "\n\n" + curFolder + curItems[py]->folderName, std::vector<std::shared_ptr<CComponent>>(), [this, py](){
|
||||
LobbyDelete ld;
|
||||
ld.type = LobbyDelete::EType::SAVEGAME_FOLDER;
|
||||
ld.name = curFolder + curItems[py]->folderName;
|
||||
CSH->sendLobbyPack(ld);
|
||||
}, nullptr);
|
||||
}
|
||||
}
|
||||
#ifdef VCMI_MOBILE
|
||||
// focus input field if clicked inside it
|
||||
@ -475,6 +525,9 @@ void SelectionTab::filter(int size, bool selectFirst)
|
||||
|
||||
curItems.clear();
|
||||
|
||||
if(buttonDeleteMode)
|
||||
buttonDeleteMode->setEnabled(tabType != ESelectionScreen::newGame || showRandom);
|
||||
|
||||
for(auto elem : allItems)
|
||||
{
|
||||
if((elem->mapHeader && (!size || elem->mapHeader->width == size)) || tabType == ESelectionScreen::campaignList)
|
||||
@ -826,8 +879,10 @@ void SelectionTab::parseMaps(const std::unordered_set<ResourcePath> & files)
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionTab::parseSaves(const std::unordered_set<ResourcePath> & files)
|
||||
std::vector<ResourcePath> SelectionTab::parseSaves(const std::unordered_set<ResourcePath> & files)
|
||||
{
|
||||
std::vector<ResourcePath> unsupported;
|
||||
|
||||
for(auto & file : files)
|
||||
{
|
||||
try
|
||||
@ -866,15 +921,43 @@ void SelectionTab::parseSaves(const std::unordered_set<ResourcePath> & files)
|
||||
|
||||
allItems.push_back(mapInfo);
|
||||
}
|
||||
catch(const std::exception & e)
|
||||
catch(const IdentifierResolutionException & e)
|
||||
{
|
||||
logGlobal->error("Error: Failed to process %s: %s", file.getName(), e.what());
|
||||
}
|
||||
catch(const std::exception & e)
|
||||
{
|
||||
unsupported.push_back(file); // IdentifierResolutionException is not relevant -> not ask to delete, when mods are disabled
|
||||
logGlobal->error("Error: Failed to process %s: %s", file.getName(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
return unsupported;
|
||||
}
|
||||
|
||||
void SelectionTab::handleUnsupportedSavegames(const std::vector<ResourcePath> & files)
|
||||
{
|
||||
if(CSH->isHost() && files.size())
|
||||
{
|
||||
MetaString text = MetaString::createFromTextID("vcmi.lobby.deleteUnsupportedSave");
|
||||
text.replaceNumber(files.size());
|
||||
CInfoWindow::showYesNoDialog(text.toString(), std::vector<std::shared_ptr<CComponent>>(), [files](){
|
||||
for(auto & file : files)
|
||||
{
|
||||
LobbyDelete ld;
|
||||
ld.type = LobbyDelete::EType::SAVEGAME;
|
||||
ld.name = file.getName();
|
||||
CSH->sendLobbyPack(ld);
|
||||
}
|
||||
}, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void SelectionTab::parseCampaigns(const std::unordered_set<ResourcePath> & files)
|
||||
{
|
||||
auto campaignSets = JsonNode(JsonPath::builtin("config/campaignSets.json"));
|
||||
auto mainmenu = JsonNode(JsonPath::builtin("config/mainmenu.json"));
|
||||
|
||||
allItems.reserve(files.size());
|
||||
for(auto & file : files)
|
||||
{
|
||||
@ -882,8 +965,28 @@ void SelectionTab::parseCampaigns(const std::unordered_set<ResourcePath> & files
|
||||
info->fileURI = file.getOriginalName();
|
||||
info->campaignInit();
|
||||
info->name = info->getNameForList();
|
||||
|
||||
if(info->campaign)
|
||||
allItems.push_back(info);
|
||||
{
|
||||
// skip campaigns organized in sets
|
||||
std::string foundInSet = "";
|
||||
for (auto const & set : campaignSets.Struct())
|
||||
for (auto const & item : set.second["items"].Vector())
|
||||
if(file.getName() == ResourcePath(item["file"].String()).getName())
|
||||
foundInSet = set.first;
|
||||
|
||||
// set has to be used in main menu
|
||||
bool setInMainmenu = false;
|
||||
if(!foundInSet.empty())
|
||||
for (auto const & item : mainmenu["window"]["items"].Vector())
|
||||
if(item["name"].String() == "campaign")
|
||||
for (auto const & button : item["buttons"].Vector())
|
||||
if(boost::algorithm::ends_with(boost::algorithm::to_lower_copy(button["command"].String()), boost::algorithm::to_lower_copy(foundInSet)))
|
||||
setInMainmenu = true;
|
||||
|
||||
if(!setInMainmenu)
|
||||
allItems.push_back(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,8 @@ class SelectionTab : public CIntObject
|
||||
// FIXME: CSelectionBase use them too!
|
||||
std::shared_ptr<CAnimation> iconsVictoryCondition;
|
||||
std::shared_ptr<CAnimation> iconsLossCondition;
|
||||
|
||||
std::vector<std::shared_ptr<ListItem>> unSupportedSaves;
|
||||
public:
|
||||
std::vector<std::shared_ptr<ElementInfo>> allItems;
|
||||
std::vector<std::shared_ptr<ElementInfo>> curItems;
|
||||
@ -120,11 +122,16 @@ private:
|
||||
ESelectionScreen tabType;
|
||||
Rect inputNameRect;
|
||||
|
||||
std::shared_ptr<CButton> buttonDeleteMode;
|
||||
bool deleteMode;
|
||||
|
||||
auto checkSubfolder(std::string path);
|
||||
|
||||
bool isMapSupported(const CMapInfo & info);
|
||||
void parseMaps(const std::unordered_set<ResourcePath> & files);
|
||||
void parseSaves(const std::unordered_set<ResourcePath> & files);
|
||||
std::vector<ResourcePath> parseSaves(const std::unordered_set<ResourcePath> & files);
|
||||
void parseCampaigns(const std::unordered_set<ResourcePath> & files);
|
||||
std::unordered_set<ResourcePath> getFiles(std::string dirURI, EResType resType);
|
||||
|
||||
void handleUnsupportedSavegames(const std::vector<ResourcePath> & files);
|
||||
};
|
||||
|
@ -67,7 +67,8 @@ CCampaignScreen::CCampaignScreen(const JsonNode & config, std::string name)
|
||||
}
|
||||
|
||||
for(const JsonNode & node : config[name]["items"].Vector())
|
||||
campButtons.push_back(std::make_shared<CCampaignButton>(node, config, campaignSet));
|
||||
if(CResourceHandler::get()->existsResource(ResourcePath(node["file"].String(), EResType::CAMPAIGN)))
|
||||
campButtons.push_back(std::make_shared<CCampaignButton>(node, config, campaignSet));
|
||||
}
|
||||
|
||||
void CCampaignScreen::activate()
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "../widgets/VideoWidget.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../CServerHandler.h"
|
||||
#include "../render/AssetGenerator.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
@ -403,6 +404,9 @@ void CMainMenu::openCampaignScreen(std::string name)
|
||||
{
|
||||
auto const & config = CMainMenuConfig::get().getCampaigns();
|
||||
|
||||
AssetGenerator::createCampaignBackground();
|
||||
AssetGenerator::createChroniclesCampaignImages();
|
||||
|
||||
if(!vstd::contains(config.Struct(), name))
|
||||
{
|
||||
logGlobal->error("Unknown campaign set: %s", name);
|
||||
@ -413,7 +417,9 @@ void CMainMenu::openCampaignScreen(std::string name)
|
||||
for (auto const & entry : config[name]["items"].Vector())
|
||||
{
|
||||
ResourcePath resourceID(entry["file"].String(), EResType::CAMPAIGN);
|
||||
if (!CResourceHandler::get()->existsResource(resourceID))
|
||||
if(entry["optional"].Bool())
|
||||
continue;
|
||||
if(!CResourceHandler::get()->existsResource(resourceID))
|
||||
campaignsFound = false;
|
||||
}
|
||||
|
||||
|
@ -316,7 +316,7 @@ uint8_t MapRendererBorder::checksum(IMapRendererContext & context, const int3 &
|
||||
MapRendererFow::MapRendererFow()
|
||||
{
|
||||
fogOfWarFullHide = GH.renderHandler().loadAnimation(AnimationPath::builtin("TSHRC"), EImageBlitMode::OPAQUE);
|
||||
fogOfWarPartialHide = GH.renderHandler().loadAnimation(AnimationPath::builtin("TSHRE"), EImageBlitMode::ALPHA);
|
||||
fogOfWarPartialHide = GH.renderHandler().loadAnimation(AnimationPath::builtin("TSHRE"), EImageBlitMode::SIMPLE);
|
||||
|
||||
static const std::vector<int> rotations = {22, 15, 2, 13, 12, 16, 28, 17, 20, 19, 7, 24, 26, 25, 30, 32, 27};
|
||||
|
||||
@ -383,24 +383,25 @@ std::shared_ptr<CAnimation> MapRendererObjects::getBaseAnimation(const CGObjectI
|
||||
}
|
||||
|
||||
bool generateMovementGroups = (info->id == Obj::BOAT) || (info->id == Obj::HERO);
|
||||
bool enableOverlay = obj->ID != Obj::BOAT && obj->ID != Obj::HERO && obj->getOwner() != PlayerColor::UNFLAGGABLE;
|
||||
|
||||
// Boat appearance files only contain single, unanimated image
|
||||
// proper boat animations are actually in different file
|
||||
if (info->id == Obj::BOAT)
|
||||
if(auto boat = dynamic_cast<const CGBoat*>(obj); boat && !boat->actualAnimation.empty())
|
||||
return getAnimation(boat->actualAnimation, generateMovementGroups);
|
||||
return getAnimation(boat->actualAnimation, generateMovementGroups, enableOverlay);
|
||||
|
||||
return getAnimation(info->animationFile, generateMovementGroups);
|
||||
return getAnimation(info->animationFile, generateMovementGroups, enableOverlay);
|
||||
}
|
||||
|
||||
std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const AnimationPath & filename, bool generateMovementGroups)
|
||||
std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const AnimationPath & filename, bool generateMovementGroups, bool enableOverlay)
|
||||
{
|
||||
auto it = animations.find(filename);
|
||||
|
||||
if(it != animations.end())
|
||||
return it->second;
|
||||
|
||||
auto ret = GH.renderHandler().loadAnimation(filename, EImageBlitMode::ALPHA);
|
||||
auto ret = GH.renderHandler().loadAnimation(filename, enableOverlay ? EImageBlitMode::WITH_SHADOW_AND_OVERLAY : EImageBlitMode::WITH_SHADOW);
|
||||
animations[filename] = ret;
|
||||
|
||||
if(generateMovementGroups)
|
||||
@ -427,14 +428,14 @@ std::shared_ptr<CAnimation> MapRendererObjects::getFlagAnimation(const CGObjectI
|
||||
{
|
||||
assert(dynamic_cast<const CGHeroInstance *>(obj) != nullptr);
|
||||
assert(obj->tempOwner.isValidPlayer());
|
||||
return getAnimation(AnimationPath::builtin(heroFlags[obj->tempOwner.getNum()]), true);
|
||||
return getAnimation(AnimationPath::builtin(heroFlags[obj->tempOwner.getNum()]), true, false);
|
||||
}
|
||||
|
||||
if(obj->ID == Obj::BOAT)
|
||||
{
|
||||
const auto * boat = dynamic_cast<const CGBoat *>(obj);
|
||||
if(boat && boat->hero && !boat->flagAnimations[boat->hero->tempOwner.getNum()].empty())
|
||||
return getAnimation(boat->flagAnimations[boat->hero->tempOwner.getNum()], true);
|
||||
return getAnimation(boat->flagAnimations[boat->hero->tempOwner.getNum()], true, false);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
@ -447,7 +448,7 @@ std::shared_ptr<CAnimation> MapRendererObjects::getOverlayAnimation(const CGObje
|
||||
// Boats have additional animation with waves around boat
|
||||
const auto * boat = dynamic_cast<const CGBoat *>(obj);
|
||||
if(boat && boat->hero && !boat->overlayAnimation.empty())
|
||||
return getAnimation(boat->overlayAnimation, true);
|
||||
return getAnimation(boat->overlayAnimation, true, false);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
@ -478,22 +479,14 @@ void MapRendererObjects::renderImage(IMapRendererContext & context, Canvas & tar
|
||||
return;
|
||||
|
||||
image->setAlpha(transparency);
|
||||
image->setShadowEnabled(true);
|
||||
if (object->ID != Obj::HERO)
|
||||
if (object->ID != Obj::HERO) // heroes use separate image with flag instead of player-colored palette
|
||||
{
|
||||
image->setOverlayEnabled(object->getOwner().isValidPlayer() || object->getOwner() == PlayerColor::NEUTRAL);
|
||||
|
||||
if (object->getOwner().isValidPlayer())
|
||||
image->setOverlayColor(graphics->playerColors[object->getOwner().getNum()]);
|
||||
|
||||
if (object->getOwner() == PlayerColor::NEUTRAL)
|
||||
image->setOverlayColor(graphics->neutralColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// heroes use separate image with flag instead of player-colored palette
|
||||
image->setOverlayEnabled(false);
|
||||
}
|
||||
|
||||
Point offsetPixels = context.objectImageOffset(object->id, coordinates);
|
||||
|
||||
@ -567,10 +560,10 @@ uint8_t MapRendererObjects::checksum(IMapRendererContext & context, const int3 &
|
||||
}
|
||||
|
||||
MapRendererOverlay::MapRendererOverlay()
|
||||
: imageGrid(GH.renderHandler().loadImage(ImagePath::builtin("debug/grid"), EImageBlitMode::ALPHA))
|
||||
, imageBlocked(GH.renderHandler().loadImage(ImagePath::builtin("debug/blocked"), EImageBlitMode::ALPHA))
|
||||
, imageVisitable(GH.renderHandler().loadImage(ImagePath::builtin("debug/visitable"), EImageBlitMode::ALPHA))
|
||||
, imageSpellRange(GH.renderHandler().loadImage(ImagePath::builtin("debug/spellRange"), EImageBlitMode::ALPHA))
|
||||
: imageGrid(GH.renderHandler().loadImage(ImagePath::builtin("debug/grid"), EImageBlitMode::COLORKEY))
|
||||
, imageBlocked(GH.renderHandler().loadImage(ImagePath::builtin("debug/blocked"), EImageBlitMode::COLORKEY))
|
||||
, imageVisitable(GH.renderHandler().loadImage(ImagePath::builtin("debug/visitable"), EImageBlitMode::COLORKEY))
|
||||
, imageSpellRange(GH.renderHandler().loadImage(ImagePath::builtin("debug/spellRange"), EImageBlitMode::COLORKEY))
|
||||
{
|
||||
|
||||
}
|
||||
@ -626,7 +619,7 @@ uint8_t MapRendererOverlay::checksum(IMapRendererContext & context, const int3 &
|
||||
}
|
||||
|
||||
MapRendererPath::MapRendererPath()
|
||||
: pathNodes(GH.renderHandler().loadAnimation(AnimationPath::builtin("ADAG"), EImageBlitMode::ALPHA))
|
||||
: pathNodes(GH.renderHandler().loadAnimation(AnimationPath::builtin("ADAG"), EImageBlitMode::SIMPLE))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ class MapRendererObjects
|
||||
std::shared_ptr<CAnimation> getFlagAnimation(const CGObjectInstance * obj);
|
||||
std::shared_ptr<CAnimation> getOverlayAnimation(const CGObjectInstance * obj);
|
||||
|
||||
std::shared_ptr<CAnimation> getAnimation(const AnimationPath & filename, bool generateMovementGroups);
|
||||
std::shared_ptr<CAnimation> getAnimation(const AnimationPath & filename, bool generateMovementGroups, bool enableOverlay);
|
||||
|
||||
std::shared_ptr<IImage> getImage(IMapRendererContext & context, const CGObjectInstance * obj, const std::shared_ptr<CAnimation> & animation) const;
|
||||
|
||||
|
@ -54,9 +54,8 @@ void MapRendererContextState::addObject(const CGObjectInstance * obj)
|
||||
if(LOCPLINT->cb->isInTheMap(currTile) && obj->coveringAt(currTile))
|
||||
{
|
||||
auto & container = objects[currTile.z][currTile.x][currTile.y];
|
||||
|
||||
container.push_back(obj->id);
|
||||
boost::range::sort(container, compareObjectBlitOrder);
|
||||
auto position = std::upper_bound(container.begin(), container.end(), obj->id, compareObjectBlitOrder);
|
||||
container.insert(position, obj->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ void AssetGenerator::generateAll()
|
||||
for (int i = 0; i < PlayerColor::PLAYER_LIMIT_I; ++i)
|
||||
createPlayerColoredBackground(PlayerColor(i));
|
||||
createCombatUnitNumberWindow();
|
||||
createCampaignBackground();
|
||||
createChroniclesCampaignImages();
|
||||
}
|
||||
|
||||
void AssetGenerator::createAdventureOptionsCleanBackground()
|
||||
@ -206,3 +208,121 @@ void AssetGenerator::createCombatUnitNumberWindow()
|
||||
texture->adjustPalette(shifterNeutral, ignoredMask);
|
||||
texture->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePathNeutral));
|
||||
}
|
||||
|
||||
void AssetGenerator::createCampaignBackground()
|
||||
{
|
||||
std::string filename = "data/CampaignBackground8.png";
|
||||
|
||||
if(CResourceHandler::get()->existsResource(ResourcePath(filename))) // overridden by mod, no generation
|
||||
return;
|
||||
|
||||
if(!CResourceHandler::get("local")->createResource(filename))
|
||||
return;
|
||||
ResourcePath savePath(filename, EResType::IMAGE);
|
||||
|
||||
auto locator = ImageLocator(ImagePath::builtin("CAMPBACK"));
|
||||
locator.scalingFactor = 1;
|
||||
|
||||
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE);
|
||||
Canvas canvas = Canvas(Point(800, 600), CanvasScalingPolicy::IGNORE);
|
||||
|
||||
canvas.draw(img, Point(0, 0), Rect(0, 0, 800, 600));
|
||||
|
||||
// left image
|
||||
canvas.draw(img, Point(220, 73), Rect(290, 73, 141, 115));
|
||||
canvas.draw(img, Point(37, 70), Rect(87, 70, 207, 120));
|
||||
|
||||
// right image
|
||||
canvas.draw(img, Point(513, 67), Rect(463, 67, 71, 126));
|
||||
canvas.draw(img, Point(586, 71), Rect(536, 71, 207, 117));
|
||||
|
||||
// middle image
|
||||
canvas.draw(img, Point(306, 68), Rect(86, 68, 209, 122));
|
||||
|
||||
// disabled fields
|
||||
canvas.draw(img, Point(40, 72), Rect(313, 74, 197, 114));
|
||||
canvas.draw(img, Point(310, 72), Rect(313, 74, 197, 114));
|
||||
canvas.draw(img, Point(590, 72), Rect(313, 74, 197, 114));
|
||||
canvas.draw(img, Point(43, 245), Rect(313, 74, 197, 114));
|
||||
canvas.draw(img, Point(313, 244), Rect(313, 74, 197, 114));
|
||||
canvas.draw(img, Point(586, 246), Rect(313, 74, 197, 114));
|
||||
canvas.draw(img, Point(34, 417), Rect(313, 74, 197, 114));
|
||||
canvas.draw(img, Point(404, 414), Rect(313, 74, 197, 114));
|
||||
|
||||
// skull
|
||||
auto locatorSkull = ImageLocator(ImagePath::builtin("CAMPNOSC"));
|
||||
locatorSkull.scalingFactor = 1;
|
||||
std::shared_ptr<IImage> imgSkull = GH.renderHandler().loadImage(locatorSkull, EImageBlitMode::OPAQUE);
|
||||
canvas.draw(imgSkull, Point(562, 509), Rect(178, 108, 43, 19));
|
||||
|
||||
std::shared_ptr<IImage> image = GH.renderHandler().createImage(canvas.getInternalSurface());
|
||||
|
||||
image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
|
||||
}
|
||||
|
||||
void AssetGenerator::createChroniclesCampaignImages()
|
||||
{
|
||||
for(int i = 1; i < 9; i++)
|
||||
{
|
||||
std::string filename = "data/CampaignHc" + std::to_string(i) + "Image.png";
|
||||
|
||||
if(CResourceHandler::get()->existsResource(ResourcePath(filename))) // overridden by mod, no generation
|
||||
continue;
|
||||
|
||||
auto imgPathBg = ImagePath::builtin("data/chronicles_" + std::to_string(i) + "/GamSelBk");
|
||||
if(!CResourceHandler::get()->existsResource(imgPathBg)) // Chronicle episode not installed
|
||||
continue;
|
||||
|
||||
if(!CResourceHandler::get("local")->createResource(filename))
|
||||
continue;
|
||||
ResourcePath savePath(filename, EResType::IMAGE);
|
||||
|
||||
auto locator = ImageLocator(imgPathBg);
|
||||
locator.scalingFactor = 1;
|
||||
|
||||
std::shared_ptr<IImage> img = GH.renderHandler().loadImage(locator, EImageBlitMode::OPAQUE);
|
||||
Canvas canvas = Canvas(Point(200, 116), CanvasScalingPolicy::IGNORE);
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case 1:
|
||||
canvas.draw(img, Point(0, 0), Rect(149, 144, 200, 116));
|
||||
break;
|
||||
case 2:
|
||||
canvas.draw(img, Point(0, 0), Rect(156, 150, 200, 116));
|
||||
break;
|
||||
case 3:
|
||||
canvas.draw(img, Point(0, 0), Rect(171, 153, 200, 116));
|
||||
break;
|
||||
case 4:
|
||||
canvas.draw(img, Point(0, 0), Rect(35, 358, 200, 116));
|
||||
break;
|
||||
case 5:
|
||||
canvas.draw(img, Point(0, 0), Rect(216, 248, 200, 116));
|
||||
break;
|
||||
case 6:
|
||||
canvas.draw(img, Point(0, 0), Rect(58, 234, 200, 116));
|
||||
break;
|
||||
case 7:
|
||||
canvas.draw(img, Point(0, 0), Rect(184, 219, 200, 116));
|
||||
break;
|
||||
case 8:
|
||||
canvas.draw(img, Point(0, 0), Rect(268, 210, 200, 116));
|
||||
|
||||
//skull
|
||||
auto locatorSkull = ImageLocator(ImagePath::builtin("CampSP1"));
|
||||
locatorSkull.scalingFactor = 1;
|
||||
std::shared_ptr<IImage> imgSkull = GH.renderHandler().loadImage(locatorSkull, EImageBlitMode::OPAQUE);
|
||||
canvas.draw(imgSkull, Point(162, 94), Rect(162, 94, 41, 22));
|
||||
canvas.draw(img, Point(162, 94), Rect(424, 304, 14, 4));
|
||||
canvas.draw(img, Point(162, 98), Rect(424, 308, 10, 4));
|
||||
canvas.draw(img, Point(158, 102), Rect(424, 312, 10, 4));
|
||||
canvas.draw(img, Point(154, 106), Rect(424, 316, 10, 4));
|
||||
break;
|
||||
}
|
||||
|
||||
std::shared_ptr<IImage> image = GH.renderHandler().createImage(canvas.getInternalSurface());
|
||||
|
||||
image->exportBitmap(*CResourceHandler::get("local")->getResourceName(savePath));
|
||||
}
|
||||
}
|
||||
|
@ -21,4 +21,6 @@ public:
|
||||
static void createBigSpellBook();
|
||||
static void createPlayerColoredBackground(const PlayerColor & player);
|
||||
static void createCombatUnitNumberWindow();
|
||||
static void createCampaignBackground();
|
||||
static void createChroniclesCampaignImages();
|
||||
};
|
||||
|
@ -37,9 +37,29 @@ enum class EImageBlitMode : uint8_t
|
||||
/// RGBA: full alpha transparency range, e.g. shadows
|
||||
COLORKEY,
|
||||
|
||||
/// Should be avoided if possible, use only for images that use def's with semi-transparency
|
||||
/// Indexed or RGBA: Image might have full alpha transparency range, e.g. shadows
|
||||
ALPHA
|
||||
/// Full transparency including shadow, but treated as a single image
|
||||
/// Indexed: Image can have alpha transparency, e.g. shadow
|
||||
/// RGBA: full alpha transparency range, e.g. shadows
|
||||
/// Upscaled form: single image, no option to display shadow separately
|
||||
SIMPLE,
|
||||
|
||||
/// RGBA, may consist from 2 separate parts: base and shadow, overlay not preset or treated as part of body
|
||||
WITH_SHADOW,
|
||||
|
||||
/// RGBA, may consist from 3 separate parts: base, shadow, and overlay
|
||||
WITH_SHADOW_AND_OVERLAY,
|
||||
|
||||
/// RGBA, contains only body, with shadow and overlay disabled
|
||||
ONLY_BODY,
|
||||
|
||||
/// RGBA, contains only body, with shadow disabled and overlay treated as part of body
|
||||
ONLY_BODY_IGNORE_OVERLAY,
|
||||
|
||||
/// RGBA, contains only shadow
|
||||
ONLY_SHADOW,
|
||||
|
||||
/// RGBA, contains only overlay
|
||||
ONLY_OVERLAY,
|
||||
};
|
||||
|
||||
/// Base class for images for use in client code.
|
||||
@ -75,10 +95,7 @@ public:
|
||||
//only indexed bitmaps with 7 special colors
|
||||
virtual void setOverlayColor(const ColorRGBA & color) = 0;
|
||||
|
||||
virtual void setShadowEnabled(bool on) = 0;
|
||||
virtual void setBodyEnabled(bool on) = 0;
|
||||
virtual void setOverlayEnabled(bool on) = 0;
|
||||
virtual std::shared_ptr<ISharedImage> getSharedImage() const = 0;
|
||||
virtual std::shared_ptr<const ISharedImage> getSharedImage() const = 0;
|
||||
|
||||
virtual ~IImage() = default;
|
||||
};
|
||||
@ -94,12 +111,12 @@ public:
|
||||
virtual bool isTransparent(const Point & coords) const = 0;
|
||||
virtual void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const = 0;
|
||||
|
||||
virtual std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) = 0;
|
||||
virtual std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) const = 0;
|
||||
|
||||
virtual std::shared_ptr<ISharedImage> horizontalFlip() const = 0;
|
||||
virtual std::shared_ptr<ISharedImage> verticalFlip() const = 0;
|
||||
virtual std::shared_ptr<ISharedImage> scaleInteger(int factor, SDL_Palette * palette) const = 0;
|
||||
virtual std::shared_ptr<ISharedImage> scaleTo(const Point & size, SDL_Palette * palette) const = 0;
|
||||
virtual std::shared_ptr<const ISharedImage> horizontalFlip() const = 0;
|
||||
virtual std::shared_ptr<const ISharedImage> verticalFlip() const = 0;
|
||||
virtual std::shared_ptr<const ISharedImage> scaleInteger(int factor, SDL_Palette * palette) const = 0;
|
||||
virtual std::shared_ptr<const ISharedImage> scaleTo(const Point & size, SDL_Palette * palette) const = 0;
|
||||
|
||||
|
||||
virtual ~ISharedImage() = default;
|
||||
|
@ -71,6 +71,7 @@ ImageLocator ImageLocator::copyFile() const
|
||||
{
|
||||
ImageLocator result;
|
||||
result.scalingFactor = 1;
|
||||
result.preScaledFactor = preScaledFactor;
|
||||
result.image = image;
|
||||
result.defFile = defFile;
|
||||
result.defFrame = defFrame;
|
||||
@ -123,8 +124,12 @@ std::string ImageLocator::toString() const
|
||||
if (playerColored.isValidPlayer())
|
||||
result += "-player" + playerColored.toString();
|
||||
|
||||
if (layer != EImageLayer::ALL)
|
||||
result += "-layer" + std::to_string(static_cast<int>(layer));
|
||||
if (layer == EImageBlitMode::ONLY_OVERLAY)
|
||||
result += "-overlay";
|
||||
|
||||
if (layer == EImageBlitMode::ONLY_SHADOW)
|
||||
result += "-shadow";
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -9,18 +9,11 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "IImage.h"
|
||||
|
||||
#include "../../lib/filesystem/ResourcePath.h"
|
||||
#include "../../lib/constants/EntityIdentifiers.h"
|
||||
|
||||
enum class EImageLayer
|
||||
{
|
||||
ALL,
|
||||
|
||||
BODY,
|
||||
SHADOW,
|
||||
OVERLAY,
|
||||
};
|
||||
|
||||
struct ImageLocator
|
||||
{
|
||||
std::optional<ImagePath> image;
|
||||
@ -28,12 +21,13 @@ struct ImageLocator
|
||||
int defFrame = -1;
|
||||
int defGroup = -1;
|
||||
|
||||
PlayerColor playerColored = PlayerColor::CANNOT_DETERMINE;
|
||||
PlayerColor playerColored = PlayerColor::CANNOT_DETERMINE; // FIXME: treat as identical to blue to avoid double-loading?
|
||||
|
||||
bool verticalFlip = false;
|
||||
bool horizontalFlip = false;
|
||||
int8_t scalingFactor = 0; // 0 = auto / use default scaling
|
||||
EImageLayer layer = EImageLayer::ALL;
|
||||
int8_t preScaledFactor = 1;
|
||||
EImageBlitMode layer = EImageBlitMode::OPAQUE;
|
||||
|
||||
ImageLocator() = default;
|
||||
ImageLocator(const AnimationPath & path, int frame, int group);
|
||||
|
@ -21,19 +21,17 @@
|
||||
|
||||
#include <SDL_surface.h>
|
||||
|
||||
ImageScaled::ImageScaled(const ImageLocator & inputLocator, const std::shared_ptr<ISharedImage> & source, EImageBlitMode mode)
|
||||
ImageScaled::ImageScaled(const ImageLocator & inputLocator, const std::shared_ptr<const ISharedImage> & source, EImageBlitMode mode)
|
||||
: source(source)
|
||||
, locator(inputLocator)
|
||||
, colorMultiplier(Colors::WHITE_TRUE)
|
||||
, alphaValue(SDL_ALPHA_OPAQUE)
|
||||
, blitMode(mode)
|
||||
{
|
||||
setBodyEnabled(true);
|
||||
if (mode == EImageBlitMode::ALPHA)
|
||||
setShadowEnabled(true);
|
||||
prepareImages();
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> ImageScaled::getSharedImage() const
|
||||
std::shared_ptr<const ISharedImage> ImageScaled::getSharedImage() const
|
||||
{
|
||||
return body;
|
||||
}
|
||||
@ -92,8 +90,7 @@ void ImageScaled::setOverlayColor(const ColorRGBA & color)
|
||||
void ImageScaled::playerColored(PlayerColor player)
|
||||
{
|
||||
playerColor = player;
|
||||
if (body)
|
||||
setBodyEnabled(true); // regenerate
|
||||
prepareImages();
|
||||
}
|
||||
|
||||
void ImageScaled::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove)
|
||||
@ -106,41 +103,63 @@ void ImageScaled::adjustPalette(const ColorFilter &shifter, uint32_t colorsToSki
|
||||
// TODO: implement
|
||||
}
|
||||
|
||||
void ImageScaled::setShadowEnabled(bool on)
|
||||
void ImageScaled::prepareImages()
|
||||
{
|
||||
assert(blitMode == EImageBlitMode::ALPHA);
|
||||
if (on)
|
||||
switch(blitMode)
|
||||
{
|
||||
locator.layer = EImageLayer::SHADOW;
|
||||
locator.playerColored = PlayerColor::CANNOT_DETERMINE;
|
||||
shadow = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
|
||||
}
|
||||
else
|
||||
shadow = nullptr;
|
||||
}
|
||||
case EImageBlitMode::OPAQUE:
|
||||
case EImageBlitMode::COLORKEY:
|
||||
case EImageBlitMode::SIMPLE:
|
||||
locator.layer = blitMode;
|
||||
locator.playerColored = playerColor;
|
||||
body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
|
||||
break;
|
||||
|
||||
void ImageScaled::setBodyEnabled(bool on)
|
||||
{
|
||||
if (on)
|
||||
case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
|
||||
case EImageBlitMode::ONLY_BODY:
|
||||
locator.layer = EImageBlitMode::ONLY_BODY;
|
||||
locator.playerColored = playerColor;
|
||||
body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
|
||||
break;
|
||||
|
||||
case EImageBlitMode::WITH_SHADOW:
|
||||
case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY:
|
||||
locator.layer = EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY;
|
||||
locator.playerColored = playerColor;
|
||||
body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
|
||||
break;
|
||||
|
||||
case EImageBlitMode::ONLY_SHADOW:
|
||||
case EImageBlitMode::ONLY_OVERLAY:
|
||||
body = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
switch(blitMode)
|
||||
{
|
||||
locator.layer = blitMode == EImageBlitMode::ALPHA ? EImageLayer::BODY : EImageLayer::ALL;
|
||||
locator.playerColored = playerColor;
|
||||
body = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
|
||||
case EImageBlitMode::SIMPLE:
|
||||
case EImageBlitMode::WITH_SHADOW:
|
||||
case EImageBlitMode::ONLY_SHADOW:
|
||||
case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
|
||||
locator.layer = EImageBlitMode::ONLY_SHADOW;
|
||||
locator.playerColored = PlayerColor::CANNOT_DETERMINE;
|
||||
shadow = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
|
||||
break;
|
||||
default:
|
||||
shadow = nullptr;
|
||||
break;
|
||||
}
|
||||
else
|
||||
body = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void ImageScaled::setOverlayEnabled(bool on)
|
||||
{
|
||||
assert(blitMode == EImageBlitMode::ALPHA);
|
||||
if (on)
|
||||
switch(blitMode)
|
||||
{
|
||||
locator.layer = EImageLayer::OVERLAY;
|
||||
locator.playerColored = PlayerColor::CANNOT_DETERMINE;
|
||||
overlay = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
|
||||
case EImageBlitMode::ONLY_OVERLAY:
|
||||
case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
|
||||
locator.layer = EImageBlitMode::ONLY_OVERLAY;
|
||||
locator.playerColored = PlayerColor::CANNOT_DETERMINE;
|
||||
overlay = GH.renderHandler().loadImage(locator, blitMode)->getSharedImage();
|
||||
break;
|
||||
default:
|
||||
overlay = nullptr;
|
||||
break;
|
||||
}
|
||||
else
|
||||
overlay = nullptr;
|
||||
}
|
||||
|
@ -25,16 +25,16 @@ class ImageScaled final : public IImage
|
||||
private:
|
||||
|
||||
/// Original unscaled image
|
||||
std::shared_ptr<ISharedImage> source;
|
||||
std::shared_ptr<const ISharedImage> source;
|
||||
|
||||
/// Upscaled shadow of our image, may be null
|
||||
std::shared_ptr<ISharedImage> shadow;
|
||||
std::shared_ptr<const ISharedImage> shadow;
|
||||
|
||||
/// Upscaled main part of our image, may be null
|
||||
std::shared_ptr<ISharedImage> body;
|
||||
std::shared_ptr<const ISharedImage> body;
|
||||
|
||||
/// Upscaled overlay (player color, selection highlight) of our image, may be null
|
||||
std::shared_ptr<ISharedImage> overlay;
|
||||
std::shared_ptr<const ISharedImage> overlay;
|
||||
|
||||
ImageLocator locator;
|
||||
|
||||
@ -44,8 +44,9 @@ private:
|
||||
uint8_t alphaValue;
|
||||
EImageBlitMode blitMode;
|
||||
|
||||
void prepareImages();
|
||||
public:
|
||||
ImageScaled(const ImageLocator & locator, const std::shared_ptr<ISharedImage> & source, EImageBlitMode mode);
|
||||
ImageScaled(const ImageLocator & locator, const std::shared_ptr<const ISharedImage> & source, EImageBlitMode mode);
|
||||
|
||||
void scaleInteger(int factor) override;
|
||||
void scaleTo(const Point & size) override;
|
||||
@ -60,8 +61,5 @@ public:
|
||||
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
|
||||
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
|
||||
|
||||
void setShadowEnabled(bool on) override;
|
||||
void setBodyEnabled(bool on) override;
|
||||
void setOverlayEnabled(bool on) override;
|
||||
std::shared_ptr<ISharedImage> getSharedImage() const override;
|
||||
std::shared_ptr<const ISharedImage> getSharedImage() const override;
|
||||
};
|
||||
|
@ -55,6 +55,59 @@ std::shared_ptr<CDefFile> RenderHandler::getAnimationFile(const AnimationPath &
|
||||
return result;
|
||||
}
|
||||
|
||||
std::optional<ResourcePath> RenderHandler::getPathForScaleFactor(ResourcePath path, std::string factor)
|
||||
{
|
||||
if(path.getType() == EResType::IMAGE)
|
||||
{
|
||||
auto p = ImagePath::builtin(path.getName());
|
||||
if(CResourceHandler::get()->existsResource(p.addPrefix("SPRITES" + factor + "X/")))
|
||||
return std::optional<ResourcePath>(p.addPrefix("SPRITES" + factor + "X/"));
|
||||
if(CResourceHandler::get()->existsResource(p.addPrefix("DATA" + factor + "X/")))
|
||||
return std::optional<ResourcePath>(p.addPrefix("DATA" + factor + "X/"));
|
||||
}
|
||||
else
|
||||
{
|
||||
auto p = AnimationPath::builtin(path.getName());
|
||||
auto pJson = p.toType<EResType::JSON>();
|
||||
if(CResourceHandler::get()->existsResource(p.addPrefix("SPRITES" + factor + "X/")))
|
||||
return std::optional<ResourcePath>(p.addPrefix("SPRITES" + factor + "X/"));
|
||||
if(CResourceHandler::get()->existsResource(pJson))
|
||||
return std::optional<ResourcePath>(p);
|
||||
if(CResourceHandler::get()->existsResource(pJson.addPrefix("SPRITES" + factor + "X/")))
|
||||
return std::optional<ResourcePath>(p.addPrefix("SPRITES" + factor + "X/"));
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::pair<ResourcePath, int> RenderHandler::getScalePath(ResourcePath p)
|
||||
{
|
||||
auto path = p;
|
||||
int scaleFactor = 1;
|
||||
if(getScalingFactor() > 1)
|
||||
{
|
||||
std::vector<int> factorsToCheck = {getScalingFactor(), 4, 3, 2};
|
||||
for(auto factorToCheck : factorsToCheck)
|
||||
{
|
||||
std::string name = boost::algorithm::to_upper_copy(p.getName());
|
||||
boost::replace_all(name, "SPRITES/", std::string("SPRITES") + std::to_string(factorToCheck) + std::string("X/"));
|
||||
boost::replace_all(name, "DATA/", std::string("DATA") + std::to_string(factorToCheck) + std::string("X/"));
|
||||
ResourcePath scaledPath = ImagePath::builtin(name);
|
||||
if(p.getType() != EResType::IMAGE)
|
||||
scaledPath = AnimationPath::builtin(name);
|
||||
auto tmpPath = getPathForScaleFactor(scaledPath, std::to_string(factorToCheck));
|
||||
if(tmpPath)
|
||||
{
|
||||
path = *tmpPath;
|
||||
scaleFactor = factorToCheck;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::pair<ResourcePath, int>(path, scaleFactor);
|
||||
};
|
||||
|
||||
void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & config)
|
||||
{
|
||||
std::string basepath;
|
||||
@ -96,7 +149,9 @@ void RenderHandler::initFromJson(AnimationLayoutMap & source, const JsonNode & c
|
||||
|
||||
RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const AnimationPath & path)
|
||||
{
|
||||
AnimationPath actualPath = boost::starts_with(path.getName(), "SPRITES") ? path : path.addPrefix("SPRITES/");
|
||||
auto tmp = getScalePath(path);
|
||||
auto animPath = AnimationPath::builtin(tmp.first.getName());
|
||||
AnimationPath actualPath = boost::starts_with(animPath.getName(), "SPRITES") ? animPath : animPath.addPrefix("SPRITES/");
|
||||
|
||||
auto it = animationLayouts.find(actualPath);
|
||||
|
||||
@ -123,11 +178,15 @@ RenderHandler::AnimationLayoutMap & RenderHandler::getAnimationLayout(const Anim
|
||||
std::unique_ptr<ui8[]> textData(new ui8[stream->getSize()]);
|
||||
stream->read(textData.get(), stream->getSize());
|
||||
|
||||
const JsonNode config(reinterpret_cast<const std::byte*>(textData.get()), stream->getSize(), path.getOriginalName());
|
||||
const JsonNode config(reinterpret_cast<const std::byte*>(textData.get()), stream->getSize(), animPath.getOriginalName());
|
||||
|
||||
initFromJson(result, config);
|
||||
}
|
||||
|
||||
for(auto & g : result)
|
||||
for(auto & i : g.second)
|
||||
i.preScaledFactor = tmp.second;
|
||||
|
||||
animationLayouts[actualPath] = result;
|
||||
return animationLayouts[actualPath];
|
||||
}
|
||||
@ -153,7 +212,7 @@ ImageLocator RenderHandler::getLocatorForAnimationFrame(const AnimationPath & pa
|
||||
return ImageLocator(path, frame, group);
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> RenderHandler::loadImageImpl(const ImageLocator & locator)
|
||||
std::shared_ptr<const ISharedImage> RenderHandler::loadImageImpl(const ImageLocator & locator)
|
||||
{
|
||||
auto it = imageFiles.find(locator);
|
||||
if (it != imageFiles.end())
|
||||
@ -172,24 +231,34 @@ std::shared_ptr<ISharedImage> RenderHandler::loadImageImpl(const ImageLocator &
|
||||
return scaledImage;
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> RenderHandler::loadImageFromFileUncached(const ImageLocator & locator)
|
||||
std::shared_ptr<const ISharedImage> RenderHandler::loadImageFromFileUncached(const ImageLocator & locator)
|
||||
{
|
||||
if (locator.image)
|
||||
{
|
||||
// TODO: create EmptySharedImage class that will be instantiated if image does not exists or fails to load
|
||||
return std::make_shared<SDLImageShared>(*locator.image);
|
||||
return std::make_shared<SDLImageShared>(*locator.image, locator.preScaledFactor);
|
||||
}
|
||||
|
||||
if (locator.defFile)
|
||||
{
|
||||
auto defFile = getAnimationFile(*locator.defFile);
|
||||
return std::make_shared<SDLImageShared>(defFile.get(), locator.defFrame, locator.defGroup);
|
||||
int preScaledFactor = locator.preScaledFactor;
|
||||
if(!defFile) // no prescale for this frame
|
||||
{
|
||||
auto tmpPath = (*locator.defFile).getName();
|
||||
boost::algorithm::replace_all(tmpPath, "SPRITES2X/", "SPRITES/");
|
||||
boost::algorithm::replace_all(tmpPath, "SPRITES3X/", "SPRITES/");
|
||||
boost::algorithm::replace_all(tmpPath, "SPRITES4X/", "SPRITES/");
|
||||
preScaledFactor = 1;
|
||||
defFile = getAnimationFile(AnimationPath::builtin(tmpPath));
|
||||
}
|
||||
return std::make_shared<SDLImageShared>(defFile.get(), locator.defFrame, locator.defGroup, preScaledFactor);
|
||||
}
|
||||
|
||||
throw std::runtime_error("Invalid image locator received!");
|
||||
}
|
||||
|
||||
void RenderHandler::storeCachedImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image)
|
||||
void RenderHandler::storeCachedImage(const ImageLocator & locator, std::shared_ptr<const ISharedImage> image)
|
||||
{
|
||||
imageFiles[locator] = image;
|
||||
|
||||
@ -202,7 +271,7 @@ void RenderHandler::storeCachedImage(const ImageLocator & locator, std::shared_p
|
||||
#endif
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> RenderHandler::loadImageFromFile(const ImageLocator & locator)
|
||||
std::shared_ptr<const ISharedImage> RenderHandler::loadImageFromFile(const ImageLocator & locator)
|
||||
{
|
||||
if (imageFiles.count(locator))
|
||||
return imageFiles.at(locator);
|
||||
@ -212,7 +281,7 @@ std::shared_ptr<ISharedImage> RenderHandler::loadImageFromFile(const ImageLocato
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> RenderHandler::transformImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image)
|
||||
std::shared_ptr<const ISharedImage> RenderHandler::transformImage(const ImageLocator & locator, std::shared_ptr<const ISharedImage> image)
|
||||
{
|
||||
if (imageFiles.count(locator))
|
||||
return imageFiles.at(locator);
|
||||
@ -229,27 +298,19 @@ std::shared_ptr<ISharedImage> RenderHandler::transformImage(const ImageLocator &
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> RenderHandler::scaleImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image)
|
||||
std::shared_ptr<const ISharedImage> RenderHandler::scaleImage(const ImageLocator & locator, std::shared_ptr<const ISharedImage> image)
|
||||
{
|
||||
if (imageFiles.count(locator))
|
||||
return imageFiles.at(locator);
|
||||
|
||||
auto handle = image->createImageReference(locator.layer == EImageLayer::ALL ? EImageBlitMode::OPAQUE : EImageBlitMode::ALPHA);
|
||||
auto handle = image->createImageReference(locator.layer);
|
||||
|
||||
assert(locator.scalingFactor != 1); // should be filtered-out before
|
||||
|
||||
handle->setBodyEnabled(locator.layer == EImageLayer::ALL || locator.layer == EImageLayer::BODY);
|
||||
if (locator.layer != EImageLayer::ALL)
|
||||
{
|
||||
handle->setOverlayEnabled(locator.layer == EImageLayer::OVERLAY);
|
||||
handle->setShadowEnabled( locator.layer == EImageLayer::SHADOW);
|
||||
}
|
||||
if (locator.layer == EImageLayer::ALL && locator.playerColored != PlayerColor::CANNOT_DETERMINE)
|
||||
if (locator.playerColored != PlayerColor::CANNOT_DETERMINE)
|
||||
handle->playerColored(locator.playerColored);
|
||||
|
||||
handle->scaleInteger(locator.scalingFactor);
|
||||
|
||||
// TODO: try to optimize image size (possibly even before scaling?) - trim image borders if they are completely transparent
|
||||
auto result = handle->getSharedImage();
|
||||
storeCachedImage(locator, result);
|
||||
return result;
|
||||
@ -257,10 +318,39 @@ std::shared_ptr<ISharedImage> RenderHandler::scaleImage(const ImageLocator & loc
|
||||
|
||||
std::shared_ptr<IImage> RenderHandler::loadImage(const ImageLocator & locator, EImageBlitMode mode)
|
||||
{
|
||||
if (locator.scalingFactor == 0 && getScalingFactor() != 1 )
|
||||
ImageLocator adjustedLocator = locator;
|
||||
|
||||
if(adjustedLocator.image)
|
||||
{
|
||||
auto unscaledLocator = locator;
|
||||
auto scaledLocator = locator;
|
||||
std::string imgPath = (*adjustedLocator.image).getName();
|
||||
if(adjustedLocator.layer == EImageBlitMode::ONLY_OVERLAY)
|
||||
imgPath += "-OVERLAY";
|
||||
if(adjustedLocator.layer == EImageBlitMode::ONLY_SHADOW)
|
||||
imgPath += "-SHADOW";
|
||||
|
||||
if(CResourceHandler::get()->existsResource(ImagePath::builtin(imgPath)) ||
|
||||
CResourceHandler::get()->existsResource(ImagePath::builtin(imgPath).addPrefix("DATA/")) ||
|
||||
CResourceHandler::get()->existsResource(ImagePath::builtin(imgPath).addPrefix("SPRITES/")))
|
||||
adjustedLocator.image = ImagePath::builtin(imgPath);
|
||||
}
|
||||
|
||||
if(adjustedLocator.defFile && adjustedLocator.scalingFactor == 0)
|
||||
{
|
||||
auto tmp = getScalePath(*adjustedLocator.defFile);
|
||||
adjustedLocator.defFile = AnimationPath::builtin(tmp.first.getName());
|
||||
adjustedLocator.preScaledFactor = tmp.second;
|
||||
}
|
||||
if(adjustedLocator.image && adjustedLocator.scalingFactor == 0)
|
||||
{
|
||||
auto tmp = getScalePath(*adjustedLocator.image);
|
||||
adjustedLocator.image = ImagePath::builtin(tmp.first.getName());
|
||||
adjustedLocator.preScaledFactor = tmp.second;
|
||||
}
|
||||
|
||||
if (adjustedLocator.scalingFactor == 0 && getScalingFactor() != 1 )
|
||||
{
|
||||
auto unscaledLocator = adjustedLocator;
|
||||
auto scaledLocator = adjustedLocator;
|
||||
|
||||
unscaledLocator.scalingFactor = 1;
|
||||
scaledLocator.scalingFactor = getScalingFactor();
|
||||
@ -269,22 +359,22 @@ std::shared_ptr<IImage> RenderHandler::loadImage(const ImageLocator & locator, E
|
||||
return std::make_shared<ImageScaled>(scaledLocator, unscaledImage, mode);
|
||||
}
|
||||
|
||||
if (locator.scalingFactor == 0)
|
||||
if (adjustedLocator.scalingFactor == 0)
|
||||
{
|
||||
auto scaledLocator = locator;
|
||||
auto scaledLocator = adjustedLocator;
|
||||
scaledLocator.scalingFactor = getScalingFactor();
|
||||
|
||||
return loadImageImpl(scaledLocator)->createImageReference(mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
return loadImageImpl(locator)->createImageReference(mode);
|
||||
}
|
||||
return loadImageImpl(adjustedLocator)->createImageReference(mode);
|
||||
}
|
||||
|
||||
std::shared_ptr<IImage> RenderHandler::loadImage(const AnimationPath & path, int frame, int group, EImageBlitMode mode)
|
||||
{
|
||||
ImageLocator locator = getLocatorForAnimationFrame(path, frame, group);
|
||||
auto tmp = getScalePath(path);
|
||||
ImageLocator locator = getLocatorForAnimationFrame(AnimationPath::builtin(tmp.first.getName()), frame, group);
|
||||
locator.preScaledFactor = tmp.second;
|
||||
return loadImage(locator, mode);
|
||||
}
|
||||
|
||||
@ -296,7 +386,7 @@ std::shared_ptr<IImage> RenderHandler::loadImage(const ImagePath & path, EImageB
|
||||
|
||||
std::shared_ptr<IImage> RenderHandler::createImage(SDL_Surface * source)
|
||||
{
|
||||
return std::make_shared<SDLImageShared>(source)->createImageReference(EImageBlitMode::ALPHA);
|
||||
return std::make_shared<SDLImageShared>(source)->createImageReference(EImageBlitMode::SIMPLE);
|
||||
}
|
||||
|
||||
std::shared_ptr<CAnimation> RenderHandler::loadAnimation(const AnimationPath & path, EImageBlitMode mode)
|
||||
|
@ -25,24 +25,26 @@ class RenderHandler : public IRenderHandler
|
||||
|
||||
std::map<AnimationPath, std::shared_ptr<CDefFile>> animationFiles;
|
||||
std::map<AnimationPath, AnimationLayoutMap> animationLayouts;
|
||||
std::map<ImageLocator, std::shared_ptr<ISharedImage>> imageFiles;
|
||||
std::map<ImageLocator, std::shared_ptr<const ISharedImage>> imageFiles;
|
||||
std::map<EFonts, std::shared_ptr<const IFont>> fonts;
|
||||
|
||||
std::shared_ptr<CDefFile> getAnimationFile(const AnimationPath & path);
|
||||
std::optional<ResourcePath> getPathForScaleFactor(ResourcePath path, std::string factor);
|
||||
std::pair<ResourcePath, int> getScalePath(ResourcePath p);
|
||||
AnimationLayoutMap & getAnimationLayout(const AnimationPath & path);
|
||||
void initFromJson(AnimationLayoutMap & layout, const JsonNode & config);
|
||||
|
||||
void addImageListEntry(size_t index, size_t group, const std::string & listName, const std::string & imageName);
|
||||
void addImageListEntries(const EntityService * service);
|
||||
void storeCachedImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image);
|
||||
void storeCachedImage(const ImageLocator & locator, std::shared_ptr<const ISharedImage> image);
|
||||
|
||||
std::shared_ptr<ISharedImage> loadImageImpl(const ImageLocator & config);
|
||||
std::shared_ptr<const ISharedImage> loadImageImpl(const ImageLocator & config);
|
||||
|
||||
std::shared_ptr<ISharedImage> loadImageFromFileUncached(const ImageLocator & locator);
|
||||
std::shared_ptr<ISharedImage> loadImageFromFile(const ImageLocator & locator);
|
||||
std::shared_ptr<const ISharedImage> loadImageFromFileUncached(const ImageLocator & locator);
|
||||
std::shared_ptr<const ISharedImage> loadImageFromFile(const ImageLocator & locator);
|
||||
|
||||
std::shared_ptr<ISharedImage> transformImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image);
|
||||
std::shared_ptr<ISharedImage> scaleImage(const ImageLocator & locator, std::shared_ptr<ISharedImage> image);
|
||||
std::shared_ptr<const ISharedImage> transformImage(const ImageLocator & locator, std::shared_ptr<const ISharedImage> image);
|
||||
std::shared_ptr<const ISharedImage> scaleImage(const ImageLocator & locator, std::shared_ptr<const ISharedImage> image);
|
||||
|
||||
ImageLocator getLocatorForAnimationFrame(const AnimationPath & path, int frame, int group);
|
||||
|
||||
|
@ -89,11 +89,12 @@ int IImage::height() const
|
||||
return dimensions().y;
|
||||
}
|
||||
|
||||
SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group)
|
||||
SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group, int preScaleFactor)
|
||||
: surf(nullptr),
|
||||
margins(0, 0),
|
||||
fullSize(0, 0),
|
||||
originalPalette(nullptr)
|
||||
originalPalette(nullptr),
|
||||
preScaleFactor(preScaleFactor)
|
||||
{
|
||||
SDLImageLoader loader(this);
|
||||
data->loadFrame(frame, group, loader);
|
||||
@ -101,11 +102,12 @@ SDLImageShared::SDLImageShared(const CDefFile * data, size_t frame, size_t group
|
||||
savePalette();
|
||||
}
|
||||
|
||||
SDLImageShared::SDLImageShared(SDL_Surface * from)
|
||||
SDLImageShared::SDLImageShared(SDL_Surface * from, int preScaleFactor)
|
||||
: surf(nullptr),
|
||||
margins(0, 0),
|
||||
fullSize(0, 0),
|
||||
originalPalette(nullptr)
|
||||
originalPalette(nullptr),
|
||||
preScaleFactor(preScaleFactor)
|
||||
{
|
||||
surf = from;
|
||||
if (surf == nullptr)
|
||||
@ -118,11 +120,12 @@ SDLImageShared::SDLImageShared(SDL_Surface * from)
|
||||
fullSize.y = surf->h;
|
||||
}
|
||||
|
||||
SDLImageShared::SDLImageShared(const ImagePath & filename)
|
||||
SDLImageShared::SDLImageShared(const ImagePath & filename, int preScaleFactor)
|
||||
: surf(nullptr),
|
||||
margins(0, 0),
|
||||
fullSize(0, 0),
|
||||
originalPalette(nullptr)
|
||||
originalPalette(nullptr),
|
||||
preScaleFactor(preScaleFactor)
|
||||
{
|
||||
surf = BitmapHandler::loadBitmap(filename);
|
||||
|
||||
@ -136,6 +139,8 @@ SDLImageShared::SDLImageShared(const ImagePath & filename)
|
||||
savePalette();
|
||||
fullSize.x = surf->w;
|
||||
fullSize.y = surf->h;
|
||||
|
||||
optimizeSurface();
|
||||
}
|
||||
}
|
||||
|
||||
@ -177,7 +182,7 @@ void SDLImageShared::draw(SDL_Surface * where, SDL_Palette * palette, const Poin
|
||||
if (palette && surf->format->palette)
|
||||
SDL_SetSurfacePalette(surf, palette);
|
||||
|
||||
if(surf->format->palette && mode == EImageBlitMode::ALPHA)
|
||||
if(surf->format->palette && mode != EImageBlitMode::OPAQUE && mode != EImageBlitMode::COLORKEY)
|
||||
{
|
||||
CSDL_Ext::blit8bppAlphaTo24bpp(surf, sourceRect, where, destShift, alpha);
|
||||
}
|
||||
@ -258,6 +263,13 @@ void SDLImageShared::optimizeSurface()
|
||||
SDL_SetSurfaceBlendMode(surf, SDL_BLENDMODE_NONE);
|
||||
SDL_BlitSurface(surf, &rectSDL, newSurface, nullptr);
|
||||
|
||||
if (SDL_HasColorKey(surf))
|
||||
{
|
||||
uint32_t colorKey;
|
||||
SDL_GetColorKey(surf, &colorKey);
|
||||
SDL_SetColorKey(newSurface, SDL_TRUE, colorKey);
|
||||
}
|
||||
|
||||
SDL_FreeSurface(surf);
|
||||
surf = newSurface;
|
||||
|
||||
@ -266,7 +278,7 @@ void SDLImageShared::optimizeSurface()
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> SDLImageShared::scaleInteger(int factor, SDL_Palette * palette) const
|
||||
std::shared_ptr<const ISharedImage> SDLImageShared::scaleInteger(int factor, SDL_Palette * palette) const
|
||||
{
|
||||
if (factor <= 0)
|
||||
throw std::runtime_error("Unable to scale by integer value of " + std::to_string(factor));
|
||||
@ -274,9 +286,15 @@ std::shared_ptr<ISharedImage> SDLImageShared::scaleInteger(int factor, SDL_Palet
|
||||
if (palette && surf && surf->format->palette)
|
||||
SDL_SetSurfacePalette(surf, palette);
|
||||
|
||||
SDL_Surface * scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ);
|
||||
SDL_Surface * scaled = nullptr;
|
||||
if(preScaleFactor == factor)
|
||||
return shared_from_this();
|
||||
else if(preScaleFactor == 1)
|
||||
scaled = CSDL_Ext::scaleSurfaceIntegerFactor(surf, factor, EScalingAlgorithm::XBRZ);
|
||||
else
|
||||
scaled = CSDL_Ext::scaleSurface(surf, (surf->w / preScaleFactor) * factor, (surf->h / preScaleFactor) * factor);
|
||||
|
||||
auto ret = std::make_shared<SDLImageShared>(scaled);
|
||||
auto ret = std::make_shared<SDLImageShared>(scaled, preScaleFactor);
|
||||
|
||||
ret->fullSize.x = fullSize.x * factor;
|
||||
ret->fullSize.y = fullSize.y * factor;
|
||||
@ -294,10 +312,10 @@ std::shared_ptr<ISharedImage> SDLImageShared::scaleInteger(int factor, SDL_Palet
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> SDLImageShared::scaleTo(const Point & size, SDL_Palette * palette) const
|
||||
std::shared_ptr<const ISharedImage> SDLImageShared::scaleTo(const Point & size, SDL_Palette * palette) const
|
||||
{
|
||||
float scaleX = float(size.x) / dimensions().x;
|
||||
float scaleY = float(size.y) / dimensions().y;
|
||||
float scaleX = float(size.x) / fullSize.x;
|
||||
float scaleY = float(size.y) / fullSize.y;
|
||||
|
||||
if (palette && surf->format->palette)
|
||||
SDL_SetSurfacePalette(surf, palette);
|
||||
@ -311,7 +329,7 @@ std::shared_ptr<ISharedImage> SDLImageShared::scaleTo(const Point & size, SDL_Pa
|
||||
else
|
||||
CSDL_Ext::setDefaultColorKey(scaled);//just in case
|
||||
|
||||
auto ret = std::make_shared<SDLImageShared>(scaled);
|
||||
auto ret = std::make_shared<SDLImageShared>(scaled, preScaleFactor);
|
||||
|
||||
ret->fullSize.x = (int) round((float)fullSize.x * scaleX);
|
||||
ret->fullSize.y = (int) round((float)fullSize.y * scaleY);
|
||||
@ -348,17 +366,17 @@ void SDLImageIndexed::playerColored(PlayerColor player)
|
||||
bool SDLImageShared::isTransparent(const Point & coords) const
|
||||
{
|
||||
if (surf)
|
||||
return CSDL_Ext::isTransparent(surf, coords.x, coords.y);
|
||||
return CSDL_Ext::isTransparent(surf, coords.x - margins.x, coords.y - margins.y);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
Point SDLImageShared::dimensions() const
|
||||
{
|
||||
return fullSize;
|
||||
return fullSize / preScaleFactor;
|
||||
}
|
||||
|
||||
std::shared_ptr<IImage> SDLImageShared::createImageReference(EImageBlitMode mode)
|
||||
std::shared_ptr<IImage> SDLImageShared::createImageReference(EImageBlitMode mode) const
|
||||
{
|
||||
if (surf && surf->format->palette)
|
||||
return std::make_shared<SDLImageIndexed>(shared_from_this(), originalPalette, mode);
|
||||
@ -366,10 +384,10 @@ std::shared_ptr<IImage> SDLImageShared::createImageReference(EImageBlitMode mode
|
||||
return std::make_shared<SDLImageRGB>(shared_from_this(), mode);
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> SDLImageShared::horizontalFlip() const
|
||||
std::shared_ptr<const ISharedImage> SDLImageShared::horizontalFlip() const
|
||||
{
|
||||
SDL_Surface * flipped = CSDL_Ext::horizontalFlip(surf);
|
||||
auto ret = std::make_shared<SDLImageShared>(flipped);
|
||||
auto ret = std::make_shared<SDLImageShared>(flipped, preScaleFactor);
|
||||
ret->fullSize = fullSize;
|
||||
ret->margins.x = margins.x;
|
||||
ret->margins.y = fullSize.y - surf->h - margins.y;
|
||||
@ -378,10 +396,10 @@ std::shared_ptr<ISharedImage> SDLImageShared::horizontalFlip() const
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::shared_ptr<ISharedImage> SDLImageShared::verticalFlip() const
|
||||
std::shared_ptr<const ISharedImage> SDLImageShared::verticalFlip() const
|
||||
{
|
||||
SDL_Surface * flipped = CSDL_Ext::verticalFlip(surf);
|
||||
auto ret = std::make_shared<SDLImageShared>(flipped);
|
||||
auto ret = std::make_shared<SDLImageShared>(flipped, preScaleFactor);
|
||||
ret->fullSize = fullSize;
|
||||
ret->margins.x = fullSize.x - surf->w - margins.x;
|
||||
ret->margins.y = margins.y;
|
||||
@ -416,7 +434,7 @@ void SDLImageIndexed::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove,
|
||||
void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask)
|
||||
{
|
||||
// If shadow is enabled, following colors must be skipped unconditionally
|
||||
if (shadowEnabled)
|
||||
if (blitMode == EImageBlitMode::WITH_SHADOW || blitMode == EImageBlitMode::WITH_SHADOW_AND_OVERLAY)
|
||||
colorsToSkipMask |= (1 << 0) + (1 << 1) + (1 << 4);
|
||||
|
||||
// Note: here we skip first colors in the palette that are predefined in H3 images
|
||||
@ -432,19 +450,14 @@ void SDLImageIndexed::adjustPalette(const ColorFilter & shifter, uint32_t colors
|
||||
}
|
||||
}
|
||||
|
||||
SDLImageIndexed::SDLImageIndexed(const std::shared_ptr<ISharedImage> & image, SDL_Palette * originalPalette, EImageBlitMode mode)
|
||||
SDLImageIndexed::SDLImageIndexed(const std::shared_ptr<const ISharedImage> & image, SDL_Palette * originalPalette, EImageBlitMode mode)
|
||||
:SDLImageBase::SDLImageBase(image, mode)
|
||||
,originalPalette(originalPalette)
|
||||
{
|
||||
|
||||
currentPalette = SDL_AllocPalette(originalPalette->ncolors);
|
||||
SDL_SetPaletteColors(currentPalette, originalPalette->colors, 0, originalPalette->ncolors);
|
||||
|
||||
if (mode == EImageBlitMode::ALPHA)
|
||||
{
|
||||
setOverlayColor(Colors::TRANSPARENCY);
|
||||
setShadowTransparency(1.0);
|
||||
}
|
||||
preparePalette();
|
||||
}
|
||||
|
||||
SDLImageIndexed::~SDLImageIndexed()
|
||||
@ -491,36 +504,42 @@ void SDLImageIndexed::setOverlayColor(const ColorRGBA & color)
|
||||
}
|
||||
}
|
||||
|
||||
void SDLImageIndexed::setShadowEnabled(bool on)
|
||||
void SDLImageIndexed::preparePalette()
|
||||
{
|
||||
if (on)
|
||||
setShadowTransparency(1.0);
|
||||
switch(blitMode)
|
||||
{
|
||||
case EImageBlitMode::ONLY_SHADOW:
|
||||
case EImageBlitMode::ONLY_OVERLAY:
|
||||
adjustPalette(ColorFilter::genAlphaShifter(0), 0);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!on && blitMode == EImageBlitMode::ALPHA)
|
||||
setShadowTransparency(0.0);
|
||||
switch(blitMode)
|
||||
{
|
||||
case EImageBlitMode::SIMPLE:
|
||||
case EImageBlitMode::WITH_SHADOW:
|
||||
case EImageBlitMode::ONLY_SHADOW:
|
||||
case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
|
||||
setShadowTransparency(1.0);
|
||||
break;
|
||||
case EImageBlitMode::ONLY_BODY:
|
||||
case EImageBlitMode::ONLY_BODY_IGNORE_OVERLAY:
|
||||
case EImageBlitMode::ONLY_OVERLAY:
|
||||
setShadowTransparency(0.0);
|
||||
break;
|
||||
}
|
||||
|
||||
shadowEnabled = on;
|
||||
}
|
||||
|
||||
void SDLImageIndexed::setBodyEnabled(bool on)
|
||||
{
|
||||
if (on)
|
||||
adjustPalette(ColorFilter::genEmptyShifter(), 0);
|
||||
else
|
||||
adjustPalette(ColorFilter::genAlphaShifter(0), 0);
|
||||
|
||||
bodyEnabled = on;
|
||||
}
|
||||
|
||||
void SDLImageIndexed::setOverlayEnabled(bool on)
|
||||
{
|
||||
if (on)
|
||||
setOverlayColor(Colors::WHITE_TRUE);
|
||||
|
||||
if (!on && blitMode == EImageBlitMode::ALPHA)
|
||||
setOverlayColor(Colors::TRANSPARENCY);
|
||||
|
||||
overlayEnabled = on;
|
||||
switch(blitMode)
|
||||
{
|
||||
case EImageBlitMode::ONLY_OVERLAY:
|
||||
case EImageBlitMode::WITH_SHADOW_AND_OVERLAY:
|
||||
setOverlayColor(Colors::WHITE_TRUE);
|
||||
break;
|
||||
case EImageBlitMode::ONLY_SHADOW:
|
||||
case EImageBlitMode::ONLY_BODY:
|
||||
setOverlayColor(Colors::TRANSPARENCY);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
SDLImageShared::~SDLImageShared()
|
||||
@ -529,13 +548,13 @@ SDLImageShared::~SDLImageShared()
|
||||
SDL_FreePalette(originalPalette);
|
||||
}
|
||||
|
||||
SDLImageBase::SDLImageBase(const std::shared_ptr<ISharedImage> & image, EImageBlitMode mode)
|
||||
SDLImageBase::SDLImageBase(const std::shared_ptr<const ISharedImage> & image, EImageBlitMode mode)
|
||||
:image(image)
|
||||
, alphaValue(SDL_ALPHA_OPAQUE)
|
||||
, blitMode(mode)
|
||||
{}
|
||||
|
||||
std::shared_ptr<ISharedImage> SDLImageBase::getSharedImage() const
|
||||
std::shared_ptr<const ISharedImage> SDLImageBase::getSharedImage() const
|
||||
{
|
||||
return image;
|
||||
}
|
||||
@ -600,21 +619,6 @@ void SDLImageBase::setBlitMode(EImageBlitMode mode)
|
||||
blitMode = mode;
|
||||
}
|
||||
|
||||
void SDLImageRGB::setShadowEnabled(bool on)
|
||||
{
|
||||
// Not supported. Theoretically we can try to extract all pixels of specific colors, but better to use 8-bit images or composite images
|
||||
}
|
||||
|
||||
void SDLImageRGB::setBodyEnabled(bool on)
|
||||
{
|
||||
// Not supported. Theoretically we can try to extract all pixels of specific colors, but better to use 8-bit images or composite images
|
||||
}
|
||||
|
||||
void SDLImageRGB::setOverlayEnabled(bool on)
|
||||
{
|
||||
// Not supported. Theoretically we can try to extract all pixels of specific colors, but better to use 8-bit images or composite images
|
||||
}
|
||||
|
||||
void SDLImageRGB::setOverlayColor(const ColorRGBA & color)
|
||||
{}
|
||||
|
||||
|
@ -35,6 +35,9 @@ class SDLImageShared final : public ISharedImage, public std::enable_shared_from
|
||||
//total size including borders
|
||||
Point fullSize;
|
||||
|
||||
//pre scaled image
|
||||
int preScaleFactor;
|
||||
|
||||
// Keep the original palette, in order to do color switching operation
|
||||
void savePalette();
|
||||
|
||||
@ -42,11 +45,11 @@ class SDLImageShared final : public ISharedImage, public std::enable_shared_from
|
||||
|
||||
public:
|
||||
//Load image from def file
|
||||
SDLImageShared(const CDefFile *data, size_t frame, size_t group=0);
|
||||
SDLImageShared(const CDefFile *data, size_t frame, size_t group=0, int preScaleFactor=1);
|
||||
//Load from bitmap file
|
||||
SDLImageShared(const ImagePath & filename);
|
||||
SDLImageShared(const ImagePath & filename, int preScaleFactor=1);
|
||||
//Create using existing surface, extraRef will increase refcount on SDL_Surface
|
||||
SDLImageShared(SDL_Surface * from);
|
||||
SDLImageShared(SDL_Surface * from, int preScaleFactor=1);
|
||||
~SDLImageShared();
|
||||
|
||||
void draw(SDL_Surface * where, SDL_Palette * palette, const Point & dest, const Rect * src, const ColorRGBA & colorMultiplier, uint8_t alpha, EImageBlitMode mode) const override;
|
||||
@ -54,11 +57,11 @@ public:
|
||||
void exportBitmap(const boost::filesystem::path & path, SDL_Palette * palette) const override;
|
||||
Point dimensions() const override;
|
||||
bool isTransparent(const Point & coords) const override;
|
||||
std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) override;
|
||||
std::shared_ptr<ISharedImage> horizontalFlip() const override;
|
||||
std::shared_ptr<ISharedImage> verticalFlip() const override;
|
||||
std::shared_ptr<ISharedImage> scaleInteger(int factor, SDL_Palette * palette) const override;
|
||||
std::shared_ptr<ISharedImage> scaleTo(const Point & size, SDL_Palette * palette) const override;
|
||||
std::shared_ptr<IImage> createImageReference(EImageBlitMode mode) const override;
|
||||
std::shared_ptr<const ISharedImage> horizontalFlip() const override;
|
||||
std::shared_ptr<const ISharedImage> verticalFlip() const override;
|
||||
std::shared_ptr<const ISharedImage> scaleInteger(int factor, SDL_Palette * palette) const override;
|
||||
std::shared_ptr<const ISharedImage> scaleTo(const Point & size, SDL_Palette * palette) const override;
|
||||
|
||||
friend class SDLImageLoader;
|
||||
};
|
||||
@ -66,19 +69,19 @@ public:
|
||||
class SDLImageBase : public IImage, boost::noncopyable
|
||||
{
|
||||
protected:
|
||||
std::shared_ptr<ISharedImage> image;
|
||||
std::shared_ptr<const ISharedImage> image;
|
||||
|
||||
uint8_t alphaValue;
|
||||
EImageBlitMode blitMode;
|
||||
|
||||
public:
|
||||
SDLImageBase(const std::shared_ptr<ISharedImage> & image, EImageBlitMode mode);
|
||||
SDLImageBase(const std::shared_ptr<const ISharedImage> & image, EImageBlitMode mode);
|
||||
|
||||
bool isTransparent(const Point & coords) const override;
|
||||
Point dimensions() const override;
|
||||
void setAlpha(uint8_t value) override;
|
||||
void setBlitMode(EImageBlitMode mode) override;
|
||||
std::shared_ptr<ISharedImage> getSharedImage() const override;
|
||||
std::shared_ptr<const ISharedImage> getSharedImage() const override;
|
||||
};
|
||||
|
||||
class SDLImageIndexed final : public SDLImageBase
|
||||
@ -86,13 +89,10 @@ class SDLImageIndexed final : public SDLImageBase
|
||||
SDL_Palette * currentPalette = nullptr;
|
||||
SDL_Palette * originalPalette = nullptr;
|
||||
|
||||
bool bodyEnabled = true;
|
||||
bool shadowEnabled = false;
|
||||
bool overlayEnabled = false;
|
||||
|
||||
void setShadowTransparency(float factor);
|
||||
void preparePalette();
|
||||
public:
|
||||
SDLImageIndexed(const std::shared_ptr<ISharedImage> & image, SDL_Palette * palette, EImageBlitMode mode);
|
||||
SDLImageIndexed(const std::shared_ptr<const ISharedImage> & image, SDL_Palette * palette, EImageBlitMode mode);
|
||||
~SDLImageIndexed();
|
||||
|
||||
void draw(SDL_Surface * where, const Point & pos, const Rect * src) const override;
|
||||
@ -103,10 +103,6 @@ public:
|
||||
void scaleInteger(int factor) override;
|
||||
void scaleTo(const Point & size) override;
|
||||
void exportBitmap(const boost::filesystem::path & path) const override;
|
||||
|
||||
void setShadowEnabled(bool on) override;
|
||||
void setBodyEnabled(bool on) override;
|
||||
void setOverlayEnabled(bool on) override;
|
||||
};
|
||||
|
||||
class SDLImageRGB final : public SDLImageBase
|
||||
@ -122,8 +118,4 @@ public:
|
||||
void scaleInteger(int factor) override;
|
||||
void scaleTo(const Point & size) override;
|
||||
void exportBitmap(const boost::filesystem::path & path) const override;
|
||||
|
||||
void setShadowEnabled(bool on) override;
|
||||
void setBodyEnabled(bool on) override;
|
||||
void setOverlayEnabled(bool on) override;
|
||||
};
|
||||
|
@ -90,7 +90,7 @@ SDL_Surface * CSDL_Ext::newSurface(const Point & dimensions, SDL_Surface * mod)
|
||||
if (mod->format->palette)
|
||||
{
|
||||
assert(ret->format->palette);
|
||||
assert(ret->format->palette->ncolors == mod->format->palette->ncolors);
|
||||
assert(ret->format->palette->ncolors >= mod->format->palette->ncolors);
|
||||
memcpy(ret->format->palette->colors, mod->format->palette->colors, mod->format->palette->ncolors * sizeof(SDL_Color));
|
||||
}
|
||||
return ret;
|
||||
|
@ -103,38 +103,38 @@ void CArtifactsOfHeroBase::setShowPopupArtPlacesCallback(const CArtPlace::ClickF
|
||||
|
||||
void CArtifactsOfHeroBase::clickPressedArtPlace(CComponentHolder & artPlace, const Point & cursorPosition)
|
||||
{
|
||||
auto ownedPlace = getArtPlace(cursorPosition);
|
||||
assert(ownedPlace != nullptr);
|
||||
if(auto ownedPlace = getArtPlace(cursorPosition))
|
||||
{
|
||||
if(ownedPlace->isLocked())
|
||||
return;
|
||||
|
||||
if(ownedPlace->isLocked())
|
||||
return;
|
||||
|
||||
if(clickPressedCallback)
|
||||
clickPressedCallback(*ownedPlace, cursorPosition);
|
||||
if(clickPressedCallback)
|
||||
clickPressedCallback(*ownedPlace, cursorPosition);
|
||||
}
|
||||
}
|
||||
|
||||
void CArtifactsOfHeroBase::showPopupArtPlace(CComponentHolder & artPlace, const Point & cursorPosition)
|
||||
{
|
||||
auto ownedPlace = getArtPlace(cursorPosition);
|
||||
assert(ownedPlace != nullptr);
|
||||
if(auto ownedPlace = getArtPlace(cursorPosition))
|
||||
{
|
||||
if(ownedPlace->isLocked())
|
||||
return;
|
||||
|
||||
if(ownedPlace->isLocked())
|
||||
return;
|
||||
|
||||
if(showPopupCallback)
|
||||
showPopupCallback(*ownedPlace, cursorPosition);
|
||||
if(showPopupCallback)
|
||||
showPopupCallback(*ownedPlace, cursorPosition);
|
||||
}
|
||||
}
|
||||
|
||||
void CArtifactsOfHeroBase::gestureArtPlace(CComponentHolder & artPlace, const Point & cursorPosition)
|
||||
{
|
||||
auto ownedPlace = getArtPlace(cursorPosition);
|
||||
assert(ownedPlace != nullptr);
|
||||
if(auto ownedPlace = getArtPlace(cursorPosition))
|
||||
{
|
||||
if(ownedPlace->isLocked())
|
||||
return;
|
||||
|
||||
if(ownedPlace->isLocked())
|
||||
return;
|
||||
|
||||
if(gestureCallback)
|
||||
gestureCallback(*ownedPlace, cursorPosition);
|
||||
if(gestureCallback)
|
||||
gestureCallback(*ownedPlace, cursorPosition);
|
||||
}
|
||||
}
|
||||
|
||||
void CArtifactsOfHeroBase::setHero(const CGHeroInstance * hero)
|
||||
|
@ -24,24 +24,24 @@ CArtifactsOfHeroMarket::CArtifactsOfHeroMarket(const Point & position, const int
|
||||
|
||||
void CArtifactsOfHeroMarket::clickPressedArtPlace(CComponentHolder & artPlace, const Point & cursorPosition)
|
||||
{
|
||||
auto ownedPlace = getArtPlace(cursorPosition);
|
||||
assert(ownedPlace != nullptr);
|
||||
|
||||
if(ownedPlace->isLocked())
|
||||
return;
|
||||
|
||||
if(const auto art = getArt(ownedPlace->slot))
|
||||
if(auto ownedPlace = getArtPlace(cursorPosition))
|
||||
{
|
||||
if(onSelectArtCallback && art->getType()->isTradable())
|
||||
if(ownedPlace->isLocked())
|
||||
return;
|
||||
|
||||
if(const auto art = getArt(ownedPlace->slot))
|
||||
{
|
||||
unmarkSlots();
|
||||
artPlace.selectSlot(true);
|
||||
onSelectArtCallback(ownedPlace.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(onClickNotTradableCallback)
|
||||
onClickNotTradableCallback();
|
||||
if(onSelectArtCallback && art->getType()->isTradable())
|
||||
{
|
||||
unmarkSlots();
|
||||
artPlace.selectSlot(true);
|
||||
onSelectArtCallback(ownedPlace.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
if(onClickNotTradableCallback)
|
||||
onClickNotTradableCallback();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -274,12 +274,12 @@ bool CGarrisonSlot::mustForceReselection() const
|
||||
if (!LOCPLINT->makingTurn)
|
||||
return true;
|
||||
|
||||
if (!creature || !selection->creature)
|
||||
return false;
|
||||
|
||||
// Attempt to take creatures from ally (select theirs first)
|
||||
if (!selection->our())
|
||||
return true;
|
||||
|
||||
if (!creature || !selection->creature)
|
||||
return false;
|
||||
|
||||
// Attempt to swap creatures with ally (select ours first)
|
||||
if (selection->creature != creature && withAlly)
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../render/ColorFilter.h"
|
||||
#include "../render/Colors.h"
|
||||
|
||||
#include "../battle/BattleInterface.h"
|
||||
#include "../battle/BattleInterfaceClasses.h"
|
||||
@ -194,12 +195,12 @@ CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, size_t Group, i
|
||||
{
|
||||
pos.x += x;
|
||||
pos.y += y;
|
||||
anim = GH.renderHandler().loadAnimation(name, EImageBlitMode::COLORKEY);
|
||||
anim = GH.renderHandler().loadAnimation(name, (Flags & CCreatureAnim::CREATURE_MODE) ? EImageBlitMode::WITH_SHADOW_AND_OVERLAY : EImageBlitMode::COLORKEY);
|
||||
init();
|
||||
}
|
||||
|
||||
CAnimImage::CAnimImage(const AnimationPath & name, size_t Frame, Rect targetPos, size_t Group, ui8 Flags):
|
||||
anim(GH.renderHandler().loadAnimation(name, EImageBlitMode::COLORKEY)),
|
||||
anim(GH.renderHandler().loadAnimation(name, (Flags & CCreatureAnim::CREATURE_MODE) ? EImageBlitMode::WITH_SHADOW_AND_OVERLAY : EImageBlitMode::COLORKEY)),
|
||||
frame(Frame),
|
||||
group(Group),
|
||||
flags(Flags),
|
||||
@ -317,7 +318,7 @@ bool CAnimImage::isPlayerColored() const
|
||||
}
|
||||
|
||||
CShowableAnim::CShowableAnim(int x, int y, const AnimationPath & name, ui8 Flags, ui32 frameTime, size_t Group, uint8_t alpha):
|
||||
anim(GH.renderHandler().loadAnimation(name, (Flags & CREATURE_MODE) ? EImageBlitMode::ALPHA : EImageBlitMode::COLORKEY)),
|
||||
anim(GH.renderHandler().loadAnimation(name, (Flags & CREATURE_MODE) ? EImageBlitMode::WITH_SHADOW_AND_OVERLAY : EImageBlitMode::COLORKEY)),
|
||||
group(Group),
|
||||
frame(0),
|
||||
first(0),
|
||||
@ -430,9 +431,8 @@ void CShowableAnim::blitImage(size_t frame, size_t group, Canvas & to)
|
||||
auto img = anim->getImage(frame, group);
|
||||
if(img)
|
||||
{
|
||||
if (flags & CREATURE_MODE)
|
||||
img->setShadowEnabled(true);
|
||||
img->setAlpha(alpha);
|
||||
img->setOverlayColor(Colors::TRANSPARENCY);
|
||||
to.draw(img, pos.topLeft(), src);
|
||||
}
|
||||
}
|
||||
|
@ -116,12 +116,12 @@ void CListBox::updatePositions()
|
||||
(elem)->moveTo(itemPos);
|
||||
itemPos += itemOffset;
|
||||
}
|
||||
if (isActive())
|
||||
if(slider)
|
||||
{
|
||||
redraw();
|
||||
if (slider)
|
||||
slider->scrollTo((int)first);
|
||||
slider->scrollTo((int)first);
|
||||
moveChildForeground(slider.get());
|
||||
}
|
||||
redraw();
|
||||
}
|
||||
|
||||
void CListBox::reset()
|
||||
@ -185,9 +185,6 @@ void CListBox::scrollTo(size_t which)
|
||||
//scroll down
|
||||
else if (first + items.size() <= which && which < totalSize)
|
||||
moveToPos(which - items.size() + 1);
|
||||
|
||||
if(slider)
|
||||
slider->scrollTo(which);
|
||||
}
|
||||
|
||||
void CListBox::moveToPos(size_t which)
|
||||
|
@ -98,7 +98,7 @@ CBuildingRect::CBuildingRect(CCastleBuildings * Par, const CGTownInstance * Town
|
||||
border = GH.renderHandler().loadImage(str->borderName, EImageBlitMode::COLORKEY);
|
||||
|
||||
if(!str->areaName.empty())
|
||||
area = GH.renderHandler().loadImage(str->areaName, EImageBlitMode::ALPHA);
|
||||
area = GH.renderHandler().loadImage(str->areaName, EImageBlitMode::SIMPLE);
|
||||
}
|
||||
|
||||
const CBuilding * CBuildingRect::getBuilding()
|
||||
@ -2055,7 +2055,14 @@ void CMageGuildScreen::Scroll::clickPressed(const Point & cursorPosition)
|
||||
auto cost = costBase * std::pow(town->spellResearchAcceptedCounter + 1, costExponent);
|
||||
|
||||
std::vector<std::shared_ptr<CComponent>> resComps;
|
||||
auto newSpell = town->spells[level].at(town->spellsAtLevel(level, false));
|
||||
|
||||
int index = town->spellsAtLevel(level, false);
|
||||
if (index >= town->spells[level].size())
|
||||
{
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.spellResearch.noMoreSpells"));
|
||||
return;
|
||||
}
|
||||
auto newSpell = town->spells[level].at(index);
|
||||
resComps.push_back(std::make_shared<CComponent>(ComponentType::SPELL, spell->id));
|
||||
resComps.push_back(std::make_shared<CComponent>(ComponentType::SPELL, newSpell));
|
||||
resComps.back()->newLine = true;
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "../widgets/Images.h"
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../widgets/ObjectLists.h"
|
||||
#include "../widgets/GraphicalPrimitiveCanvas.h"
|
||||
#include "../windows/InfoWindows.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/Shortcut.h"
|
||||
@ -250,6 +251,47 @@ CStackWindow::BonusLineSection::BonusLineSection(CStackWindow * owner, size_t li
|
||||
Point(214, 4)
|
||||
};
|
||||
|
||||
auto drawBonusSource = [this](int leftRight, Point p, BonusInfo & bi)
|
||||
{
|
||||
std::map<BonusSource, ColorRGBA> bonusColors = {
|
||||
{BonusSource::ARTIFACT, Colors::GREEN},
|
||||
{BonusSource::ARTIFACT_INSTANCE, Colors::GREEN},
|
||||
{BonusSource::CREATURE_ABILITY, Colors::YELLOW},
|
||||
{BonusSource::SPELL_EFFECT, Colors::ORANGE},
|
||||
{BonusSource::SECONDARY_SKILL, Colors::PURPLE},
|
||||
{BonusSource::HERO_SPECIAL, Colors::PURPLE},
|
||||
{BonusSource::STACK_EXPERIENCE, Colors::CYAN},
|
||||
{BonusSource::COMMANDER, Colors::CYAN},
|
||||
};
|
||||
|
||||
std::map<BonusSource, std::string> bonusNames = {
|
||||
{BonusSource::ARTIFACT, CGI->generaltexth->translate("vcmi.bonusSource.artifact")},
|
||||
{BonusSource::ARTIFACT_INSTANCE, CGI->generaltexth->translate("vcmi.bonusSource.artifact")},
|
||||
{BonusSource::CREATURE_ABILITY, CGI->generaltexth->translate("vcmi.bonusSource.creature")},
|
||||
{BonusSource::SPELL_EFFECT, CGI->generaltexth->translate("vcmi.bonusSource.spell")},
|
||||
{BonusSource::SECONDARY_SKILL, CGI->generaltexth->translate("vcmi.bonusSource.hero")},
|
||||
{BonusSource::HERO_SPECIAL, CGI->generaltexth->translate("vcmi.bonusSource.hero")},
|
||||
{BonusSource::STACK_EXPERIENCE, CGI->generaltexth->translate("vcmi.bonusSource.commander")},
|
||||
{BonusSource::COMMANDER, CGI->generaltexth->translate("vcmi.bonusSource.commander")},
|
||||
};
|
||||
|
||||
auto c = bonusColors.count(bi.bonusSource) ? bonusColors[bi.bonusSource] : ColorRGBA(192, 192, 192);
|
||||
std::string t = bonusNames.count(bi.bonusSource) ? bonusNames[bi.bonusSource] : CGI->generaltexth->translate("vcmi.bonusSource.other");
|
||||
int maxLen = 50;
|
||||
EFonts f = FONT_TINY;
|
||||
Point pText = p + Point(3, 40);
|
||||
|
||||
// 1px Black border
|
||||
bonusSource[leftRight].push_back(std::make_shared<CLabel>(pText.x - 1, pText.y, f, ETextAlignment::TOPLEFT, Colors::BLACK, t, maxLen));
|
||||
bonusSource[leftRight].push_back(std::make_shared<CLabel>(pText.x + 1, pText.y, f, ETextAlignment::TOPLEFT, Colors::BLACK, t, maxLen));
|
||||
bonusSource[leftRight].push_back(std::make_shared<CLabel>(pText.x, pText.y - 1, f, ETextAlignment::TOPLEFT, Colors::BLACK, t, maxLen));
|
||||
bonusSource[leftRight].push_back(std::make_shared<CLabel>(pText.x, pText.y + 1, f, ETextAlignment::TOPLEFT, Colors::BLACK, t, maxLen));
|
||||
bonusSource[leftRight].push_back(std::make_shared<CLabel>(pText.x, pText.y, f, ETextAlignment::TOPLEFT, c, t, maxLen));
|
||||
|
||||
frame[leftRight] = std::make_shared<GraphicalPrimitiveCanvas>(Rect(p.x, p.y, 52, 52));
|
||||
frame[leftRight]->addRectangle(Point(0, 0), Point(52, 52), c);
|
||||
};
|
||||
|
||||
for(size_t leftRight : {0, 1})
|
||||
{
|
||||
auto position = offset[leftRight];
|
||||
@ -259,8 +301,9 @@ CStackWindow::BonusLineSection::BonusLineSection(CStackWindow * owner, size_t li
|
||||
{
|
||||
BonusInfo & bi = parent->activeBonuses[bonusIndex];
|
||||
icon[leftRight] = std::make_shared<CPicture>(bi.imagePath, position.x, position.y);
|
||||
name[leftRight] = std::make_shared<CLabel>(position.x + 60, position.y + 2, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, bi.name);
|
||||
description[leftRight] = std::make_shared<CMultiLineLabel>(Rect(position.x + 60, position.y + 17, 137, 30), FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, bi.description);
|
||||
name[leftRight] = std::make_shared<CLabel>(position.x + 60, position.y + 2, FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, bi.name, 137);
|
||||
description[leftRight] = std::make_shared<CMultiLineLabel>(Rect(position.x + 60, position.y + 20, 137, 26), FONT_TINY, ETextAlignment::TOPLEFT, Colors::WHITE, bi.description);
|
||||
drawBonusSource(leftRight, Point(position.x - 1, position.y - 1), bi);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -284,7 +327,7 @@ CStackWindow::BonusesSection::BonusesSection(CStackWindow * owner, int yOffset,
|
||||
return std::make_shared<BonusLineSection>(owner, index);
|
||||
};
|
||||
|
||||
lines = std::make_shared<CListBox>(onCreate, Point(0, 0), Point(0, itemHeight), visibleSize, totalSize, 0, 1, Rect(pos.w - 15, 0, pos.h, pos.h));
|
||||
lines = std::make_shared<CListBox>(onCreate, Point(0, 0), Point(0, itemHeight), visibleSize, totalSize, 0, totalSize > 3 ? 1 : 0, Rect(pos.w - 15, 0, pos.h, pos.h));
|
||||
}
|
||||
|
||||
CStackWindow::ButtonsSection::ButtonsSection(CStackWindow * owner, int yOffset)
|
||||
@ -533,7 +576,7 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
|
||||
animation->setAmount(parent->info->creatureCount);
|
||||
}
|
||||
|
||||
name = std::make_shared<CLabel>(215, 12, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, parent->info->getName());
|
||||
name = std::make_shared<CLabel>(215, 13, FONT_SMALL, ETextAlignment::CENTER, Colors::YELLOW, parent->info->getName());
|
||||
|
||||
const BattleInterface* battleInterface = LOCPLINT->battleInt.get();
|
||||
const CStack* battleStack = parent->info->stack;
|
||||
@ -786,6 +829,12 @@ void CStackWindow::initBonusesList()
|
||||
BonusList output;
|
||||
BonusList input;
|
||||
input = *(info->stackNode->getBonuses(CSelector(Bonus::Permanent), Selector::all));
|
||||
std::sort(input.begin(), input.end(), [this](std::shared_ptr<Bonus> v1, std::shared_ptr<Bonus> & v2){
|
||||
if (v1->source != v2->source)
|
||||
return v1->source == BonusSource::CREATURE_ABILITY || (v1->source < v2->source);
|
||||
else
|
||||
return info->stackNode->bonusToString(v1, false) < info->stackNode->bonusToString(v2, false);
|
||||
});
|
||||
|
||||
while(!input.empty())
|
||||
{
|
||||
@ -801,6 +850,7 @@ void CStackWindow::initBonusesList()
|
||||
bonusInfo.name = info->stackNode->bonusToString(b, false);
|
||||
bonusInfo.description = info->stackNode->bonusToString(b, true);
|
||||
bonusInfo.imagePath = info->stackNode->bonusToGraphics(b);
|
||||
bonusInfo.bonusSource = b->source;
|
||||
|
||||
//if it's possible to give any description or image for this kind of bonus
|
||||
//TODO: figure out why half of bonuses don't have proper description
|
||||
|
@ -31,6 +31,7 @@ class CListBox;
|
||||
class CArtPlace;
|
||||
class CCommanderArtPlace;
|
||||
class LRClickableArea;
|
||||
class GraphicalPrimitiveCanvas;
|
||||
|
||||
class CCommanderSkillIcon : public LRClickableAreaWText //TODO: maybe bring commander skill button initialization logic inside?
|
||||
{
|
||||
@ -58,6 +59,7 @@ class CStackWindow : public CWindowObject
|
||||
std::string name;
|
||||
std::string description;
|
||||
ImagePath imagePath;
|
||||
BonusSource bonusSource;
|
||||
};
|
||||
|
||||
class CWindowSection : public CIntObject
|
||||
@ -84,6 +86,8 @@ class CStackWindow : public CWindowObject
|
||||
std::array<std::shared_ptr<CPicture>, 2> icon;
|
||||
std::array<std::shared_ptr<CLabel>, 2> name;
|
||||
std::array<std::shared_ptr<CMultiLineLabel>, 2> description;
|
||||
std::array<std::shared_ptr<GraphicalPrimitiveCanvas>, 2> frame;
|
||||
std::array<std::vector<std::shared_ptr<CLabel>>, 2> bonusSource;
|
||||
public:
|
||||
BonusLineSection(CStackWindow * owner, size_t lineIndex);
|
||||
};
|
||||
|
@ -199,10 +199,11 @@ void CHeroWindow::update()
|
||||
OBJECT_CONSTRUCTION;
|
||||
if(!garr)
|
||||
{
|
||||
bool removableTroops = curHero->getOwner() == LOCPLINT->playerID;
|
||||
std::string helpBox = heroscrn[32];
|
||||
boost::algorithm::replace_first(helpBox, "%s", CGI->generaltexth->allTexts[43]);
|
||||
|
||||
garr = std::make_shared<CGarrisonInt>(Point(15, 485), 8, Point(), curHero);
|
||||
garr = std::make_shared<CGarrisonInt>(Point(15, 485), 8, Point(), curHero, nullptr, removableTroops);
|
||||
auto split = std::make_shared<CButton>(Point(539, 519), AnimationPath::builtin("hsbtns9.def"), CButton::tooltip(CGI->generaltexth->allTexts[256], helpBox), [this](){ garr->splitClick(); }, EShortcut::HERO_ARMY_SPLIT);
|
||||
garr->addSplitBtn(split);
|
||||
}
|
||||
|
@ -54,8 +54,8 @@ void CreaturePurchaseCard::switchCreatureLevel()
|
||||
auto index = vstd::find_pos(upgradesID, creatureOnTheCard->getId());
|
||||
auto nextCreatureId = vstd::circularAt(upgradesID, ++index);
|
||||
creatureOnTheCard = nextCreatureId.toCreature();
|
||||
picture = std::make_shared<CCreaturePic>(parent->pos.x, parent->pos.y, creatureOnTheCard);
|
||||
creatureClickArea = std::make_shared<CCreatureClickArea>(Point(parent->pos.x, parent->pos.y), picture, creatureOnTheCard);
|
||||
picture = std::make_shared<CCreaturePic>(picture->pos.x - pos.x, picture->pos.y - pos.y, creatureOnTheCard);
|
||||
creatureClickArea = std::make_shared<CCreatureClickArea>(Point(picture->pos.x - pos.x, picture->pos.y - pos.y), picture, creatureOnTheCard);
|
||||
parent->updateAllSliders();
|
||||
cost->set(creatureOnTheCard->getFullRecruitCost() * slider->getValue());
|
||||
}
|
||||
|
@ -950,7 +950,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, BuildingID bu
|
||||
}
|
||||
else if(auto uni = dynamic_cast<const CGUniversity *>(_market); uni->appearance)
|
||||
{
|
||||
titlePic = std::make_shared<CAnimImage>(uni->appearance->animationFile, 0);
|
||||
titlePic = std::make_shared<CAnimImage>(uni->appearance->animationFile, 0, 0, 0, 0, CShowableAnim::CREATURE_MODE);
|
||||
titleStr = uni->title;
|
||||
speechStr = uni->speech;
|
||||
}
|
||||
@ -1703,7 +1703,7 @@ void VideoWindow::keyPressed(EShortcut key)
|
||||
exit(true);
|
||||
}
|
||||
|
||||
bool VideoWindow::receiveEvent(const Point & position, int eventType) const
|
||||
void VideoWindow::notFocusedClick()
|
||||
{
|
||||
return true; // capture click also outside of window
|
||||
exit(true);
|
||||
}
|
||||
|
@ -523,5 +523,5 @@ public:
|
||||
|
||||
void clickPressed(const Point & cursorPosition) override;
|
||||
void keyPressed(EShortcut key) override;
|
||||
bool receiveEvent(const Point & position, int eventType) const override;
|
||||
void notFocusedClick() override;
|
||||
};
|
||||
|
@ -56,6 +56,11 @@ if(WIN32)
|
||||
endif()
|
||||
target_compile_definitions(vcmiclient PRIVATE WINDOWS_IGNORE_PACKING_MISMATCH)
|
||||
|
||||
if(NOT ffmpeg_LIBRARIES)
|
||||
target_compile_definitions(vcmiclient PRIVATE DISABLE_VIDEO)
|
||||
endif()
|
||||
|
||||
|
||||
# TODO: very hacky, find proper solution to copy AI dlls into bin dir
|
||||
if(MSVC)
|
||||
add_custom_command(TARGET vcmiclient POST_BUILD
|
||||
|
@ -47,5 +47,21 @@
|
||||
{ "id": 6, "x":34, "y":417, "file":"DATA/FINAL", "image":"CAMPUA1", "video":"UNHOLY", "requires": [4] },
|
||||
{ "id": 7, "x":404, "y":414, "file":"DATA/SECRET", "image":"CAMPSP1", "video":"SPECTRE", "requires": [6] }
|
||||
]
|
||||
},
|
||||
"chr":
|
||||
{
|
||||
"images" : [ {"x": 0, "y": 0, "name":"data/CampaignBackground8"} ],
|
||||
"exitbutton" : {"x": 658, "y": 482, "name":"CMPSCAN" },
|
||||
"items":
|
||||
[
|
||||
{ "id": 1, "x":40, "y":72, "file":"Maps/Chronicles/Hc1_Main", "image":"CampaignHc1Image", "video":"", "requires": [], "optional": true },
|
||||
{ "id": 2, "x":310, "y":72, "file":"Maps/Chronicles/Hc2_Main", "image":"CampaignHc2Image", "video":"", "requires": [], "optional": true },
|
||||
{ "id": 3, "x":590, "y":72, "file":"Maps/Chronicles/Hc3_Main", "image":"CampaignHc3Image", "video":"", "requires": [], "optional": true },
|
||||
{ "id": 4, "x":43, "y":245, "file":"Maps/Chronicles/Hc4_Main", "image":"CampaignHc4Image", "video":"", "requires": [], "optional": true },
|
||||
{ "id": 5, "x":313, "y":244, "file":"Maps/Chronicles/Hc5_Main", "image":"CampaignHc5Image", "video":"", "requires": [], "optional": true },
|
||||
{ "id": 6, "x":586, "y":244, "file":"Maps/Chronicles/Hc6_Main", "image":"CampaignHc6Image", "video":"", "requires": [], "optional": true },
|
||||
{ "id": 7, "x":34, "y":413, "file":"Maps/Chronicles/Hc7_Main", "image":"CampaignHc7Image", "video":"", "requires": [], "optional": true },
|
||||
{ "id": 8, "x":404, "y":414, "file":"Maps/Chronicles/Hc8_Main", "image":"CampaignHc8Image", "video":"", "requires": [], "optional": true }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,7 @@
|
||||
"extraNames": [ "lightCrossbowman" ],
|
||||
"faction": "castle",
|
||||
"upgrades": ["marksman"],
|
||||
"shots" : 12,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" : {
|
||||
@ -86,6 +87,7 @@
|
||||
"index": 3,
|
||||
"level": 2,
|
||||
"faction": "castle",
|
||||
"shots" : 24,
|
||||
"abilities":
|
||||
{
|
||||
"shooter" : {
|
||||
@ -228,6 +230,7 @@
|
||||
"level": 5,
|
||||
"faction": "castle",
|
||||
"upgrades": ["zealot"],
|
||||
"shots" : 12,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" : {
|
||||
@ -257,6 +260,7 @@
|
||||
"index": 9,
|
||||
"level": 5,
|
||||
"faction": "castle",
|
||||
"shots" : 24,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" : {
|
||||
@ -344,14 +348,8 @@
|
||||
"index": 12,
|
||||
"level": 7,
|
||||
"faction": "castle",
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_2" : // Will be affected by Advanced Slayer or better
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 2
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
@ -363,6 +361,11 @@
|
||||
"propagator" : "HERO",
|
||||
"stacking" : "Angels"
|
||||
},
|
||||
"KING_2" : // Will be affected by Advanced Slayer or better
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 2
|
||||
},
|
||||
"hateDevils" :
|
||||
{
|
||||
"type" : "HATE",
|
||||
@ -398,11 +401,6 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_2" : // Will be affected by Advanced Slayer or better
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 2
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
@ -430,6 +428,11 @@
|
||||
"propagator" : "HERO",
|
||||
"stacking" : "Angels"
|
||||
},
|
||||
"KING_2" : // Will be affected by Advanced Slayer or better
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 2
|
||||
},
|
||||
"hateDevils" :
|
||||
{
|
||||
"type" : "HATE",
|
||||
|
@ -127,6 +127,7 @@
|
||||
"index": 127,
|
||||
"level": 2,
|
||||
"faction": "conflux",
|
||||
"shots" : 24,
|
||||
"abilities":
|
||||
{
|
||||
"nonLiving" :
|
||||
@ -301,6 +302,7 @@
|
||||
"level": 3,
|
||||
"faction": "conflux",
|
||||
"doubleWide" : true,
|
||||
"shots" : 24,
|
||||
"abilities":
|
||||
{
|
||||
"nonLiving" :
|
||||
@ -472,11 +474,11 @@
|
||||
{
|
||||
"type" : "NON_LIVING"
|
||||
},
|
||||
"canFly" :
|
||||
"energizes" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
},
|
||||
"spellcaster":
|
||||
"spellcaster" :
|
||||
{
|
||||
"type" : "SPELLCASTER",
|
||||
"subtype" : "spell.protectFire",
|
||||
@ -767,11 +769,6 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
@ -784,6 +781,11 @@
|
||||
{
|
||||
"type" : "SPELL_SCHOOL_IMMUNITY",
|
||||
"subtype" : "spellSchool.fire"
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
@ -807,11 +809,6 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
@ -820,11 +817,6 @@
|
||||
{
|
||||
"type" : "TWO_HEX_ATTACK_BREATH"
|
||||
},
|
||||
"immuneToFire" :
|
||||
{
|
||||
"type" : "SPELL_SCHOOL_IMMUNITY",
|
||||
"subtype" : "spellSchool.fire"
|
||||
},
|
||||
"rebirthOnce" :
|
||||
{
|
||||
"type" : "CASTS",
|
||||
@ -834,6 +826,16 @@
|
||||
{
|
||||
"type" : "REBIRTH",
|
||||
"val" : 20
|
||||
},
|
||||
"immuneToFire" :
|
||||
{
|
||||
"type" : "SPELL_SCHOOL_IMMUNITY",
|
||||
"subtype" : "spellSchool.fire"
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
|
@ -138,6 +138,7 @@
|
||||
"level": 3,
|
||||
"faction": "dungeon",
|
||||
"upgrades": ["evilEye"],
|
||||
"shots" : 12,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" :
|
||||
@ -179,6 +180,7 @@
|
||||
"index": 75,
|
||||
"level": 3,
|
||||
"faction": "dungeon",
|
||||
"shots" : 24,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" :
|
||||
@ -221,6 +223,7 @@
|
||||
"level": 4,
|
||||
"faction": "dungeon",
|
||||
"doubleWide" : true,
|
||||
"shots" : 4,
|
||||
"abilities":
|
||||
{
|
||||
"shooter" :
|
||||
@ -264,6 +267,7 @@
|
||||
"level": 4,
|
||||
"faction": "dungeon",
|
||||
"doubleWide" : true,
|
||||
"shots" : 8,
|
||||
"abilities":
|
||||
{
|
||||
"shooter" :
|
||||
@ -425,19 +429,14 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
},
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"twoHexAttackBreath" :
|
||||
{
|
||||
"type" : "TWO_HEX_ATTACK_BREATH"
|
||||
@ -446,6 +445,11 @@
|
||||
{
|
||||
"type" : "LEVEL_SPELL_IMMUNITY",
|
||||
"val" : 3
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"upgrades": ["blackDragon"],
|
||||
@ -470,19 +474,14 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
},
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"twoHexAttackBreath" :
|
||||
{
|
||||
"type" : "TWO_HEX_ATTACK_BREATH"
|
||||
@ -492,6 +491,11 @@
|
||||
"type" : "LEVEL_SPELL_IMMUNITY",
|
||||
"val" : 5
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
},
|
||||
"hateTitans" :
|
||||
{
|
||||
"type" : "HATE",
|
||||
|
@ -44,6 +44,7 @@
|
||||
"faction": "fortress",
|
||||
"upgrades": ["lizardWarrior"],
|
||||
"hasDoubleWeek": true,
|
||||
"shots" : 12,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" :
|
||||
@ -74,6 +75,7 @@
|
||||
"index": 101,
|
||||
"level": 2,
|
||||
"faction": "fortress",
|
||||
"shots" : 24,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" :
|
||||
@ -99,54 +101,6 @@
|
||||
"wince": "ALIZWNCE.wav"
|
||||
}
|
||||
},
|
||||
"gorgon" :
|
||||
{
|
||||
"index": 102,
|
||||
"level": 5,
|
||||
"faction": "fortress",
|
||||
"upgrades": ["mightyGorgon"],
|
||||
"doubleWide" : true,
|
||||
"graphics" :
|
||||
{
|
||||
"animation": "CCGORG.DEF"
|
||||
},
|
||||
"sound" :
|
||||
{
|
||||
"attack": "CGORATTK.wav",
|
||||
"defend": "CGORDFND.wav",
|
||||
"killed": "CGORKILL.wav",
|
||||
"move": "CGORMOVE.wav",
|
||||
"wince": "CGORWNCE.wav"
|
||||
}
|
||||
},
|
||||
"mightyGorgon" :
|
||||
{
|
||||
"index": 103,
|
||||
"level": 5,
|
||||
"faction": "fortress",
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"deathStare" :
|
||||
{
|
||||
"type" : "DEATH_STARE",
|
||||
"subtype" : "deathStareGorgon",
|
||||
"val" : 10
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
{
|
||||
"animation": "CBGOG.DEF"
|
||||
},
|
||||
"sound" :
|
||||
{
|
||||
"attack": "BGORATTK.wav",
|
||||
"defend": "BGORDFND.wav",
|
||||
"killed": "BGORKILL.wav",
|
||||
"move": "BGORMOVE.wav",
|
||||
"wince": "BGORWNCE.wav"
|
||||
}
|
||||
},
|
||||
"serpentFly" :
|
||||
{
|
||||
"index": 104,
|
||||
@ -276,6 +230,54 @@
|
||||
"wince": "GBASWNCE.wav"
|
||||
}
|
||||
},
|
||||
"gorgon" :
|
||||
{
|
||||
"index": 102,
|
||||
"level": 5,
|
||||
"faction": "fortress",
|
||||
"upgrades": ["mightyGorgon"],
|
||||
"doubleWide" : true,
|
||||
"graphics" :
|
||||
{
|
||||
"animation": "CCGORG.DEF"
|
||||
},
|
||||
"sound" :
|
||||
{
|
||||
"attack": "CGORATTK.wav",
|
||||
"defend": "CGORDFND.wav",
|
||||
"killed": "CGORKILL.wav",
|
||||
"move": "CGORMOVE.wav",
|
||||
"wince": "CGORWNCE.wav"
|
||||
}
|
||||
},
|
||||
"mightyGorgon" :
|
||||
{
|
||||
"index": 103,
|
||||
"level": 5,
|
||||
"faction": "fortress",
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"deathStare" :
|
||||
{
|
||||
"type" : "DEATH_STARE",
|
||||
"subtype" : "deathStareGorgon",
|
||||
"val" : 10
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
{
|
||||
"animation": "CBGOG.DEF"
|
||||
},
|
||||
"sound" :
|
||||
{
|
||||
"attack": "BGORATTK.wav",
|
||||
"defend": "BGORDFND.wav",
|
||||
"killed": "BGORKILL.wav",
|
||||
"move": "BGORMOVE.wav",
|
||||
"wince": "BGORWNCE.wav"
|
||||
}
|
||||
},
|
||||
"wyvern" :
|
||||
{
|
||||
"index": 108,
|
||||
@ -343,11 +345,6 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
},
|
||||
"attackAllAdjacent" :
|
||||
{
|
||||
"type" : "ATTACKS_ALL_ADJACENT"
|
||||
@ -355,6 +352,11 @@
|
||||
"noRetaliation" :
|
||||
{
|
||||
"type" : "BLOCKS_RETALIATION"
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"upgrades": ["chaosHydra"],
|
||||
@ -379,11 +381,6 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
},
|
||||
"attackAllAdjacent" :
|
||||
{
|
||||
"type" : "ATTACKS_ALL_ADJACENT"
|
||||
@ -391,6 +388,11 @@
|
||||
"noRetaliation" :
|
||||
{
|
||||
"type" : "BLOCKS_RETALIATION"
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
|
@ -51,6 +51,7 @@
|
||||
"faction": "inferno",
|
||||
"upgrades": ["magog"],
|
||||
"hasDoubleWeek": true,
|
||||
"shots" : 12,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" :
|
||||
@ -81,6 +82,7 @@
|
||||
"index": 45,
|
||||
"level": 2,
|
||||
"faction": "inferno",
|
||||
"shots" : 24,
|
||||
"abilities":
|
||||
{
|
||||
"shooter" :
|
||||
@ -353,12 +355,7 @@
|
||||
"faction": "inferno",
|
||||
"abilities":
|
||||
{
|
||||
"KING_2" : // Will be affected by Advanced Slayer or better
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 2
|
||||
},
|
||||
"canFly" :
|
||||
"teleports" :
|
||||
{
|
||||
"type" : "FLYING",
|
||||
"subtype" : "movementTeleporting"
|
||||
@ -376,6 +373,11 @@
|
||||
"propagationUpdater" : "BONUS_OWNER_UPDATER",
|
||||
"limiters" : [ "OPPOSITE_SIDE" ]
|
||||
},
|
||||
"KING_2" : // Will be affected by Advanced Slayer or better
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 2
|
||||
},
|
||||
"hateAngels" :
|
||||
{
|
||||
"type" : "HATE",
|
||||
@ -413,12 +415,7 @@
|
||||
"faction": "inferno",
|
||||
"abilities" :
|
||||
{
|
||||
"KING_2" : // Will be affected by Advanced Slayer or better
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 2
|
||||
},
|
||||
"canFly" :
|
||||
"teleports" :
|
||||
{
|
||||
"type" : "FLYING",
|
||||
"subtype" : "movementTeleporting"
|
||||
@ -436,6 +433,11 @@
|
||||
"propagationUpdater" : "BONUS_OWNER_UPDATER",
|
||||
"limiters" : [ "OPPOSITE_SIDE" ]
|
||||
},
|
||||
"KING_2" : // Will be affected by Advanced Slayer or better
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 2
|
||||
},
|
||||
"hateAngels" :
|
||||
{
|
||||
"type" : "HATE",
|
||||
|
@ -265,6 +265,7 @@
|
||||
"index": 64,
|
||||
"level": 5,
|
||||
"faction": "necropolis",
|
||||
"shots" : 12,
|
||||
"abilities":
|
||||
{
|
||||
"undead" :
|
||||
@ -305,6 +306,7 @@
|
||||
"index": 65,
|
||||
"level": 5,
|
||||
"faction": "necropolis",
|
||||
"shots" : 24,
|
||||
"abilities":
|
||||
{
|
||||
"undead" :
|
||||
@ -421,19 +423,14 @@
|
||||
{
|
||||
"type" : "UNDEAD"
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
},
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"decreaseMorale" :
|
||||
{
|
||||
"type" : "MORALE",
|
||||
@ -442,6 +439,11 @@
|
||||
"propagator": "BATTLE_WIDE",
|
||||
"propagationUpdater" : "BONUS_OWNER_UPDATER",
|
||||
"limiters" : [ "OPPOSITE_SIDE" ]
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"upgrades": ["ghostDragon"],
|
||||
@ -470,19 +472,14 @@
|
||||
{
|
||||
"type" : "UNDEAD"
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
},
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"age" :
|
||||
{
|
||||
"type" : "SPELL_AFTER_ATTACK",
|
||||
@ -497,6 +494,11 @@
|
||||
"propagator": "BATTLE_WIDE",
|
||||
"propagationUpdater" : "BONUS_OWNER_UPDATER",
|
||||
"limiters" : [ "OPPOSITE_SIDE" ]
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
|
@ -71,19 +71,14 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
},
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"twoHexAttackBreath" :
|
||||
{
|
||||
"type" : "TWO_HEX_ATTACK_BREATH"
|
||||
@ -100,6 +95,11 @@
|
||||
{
|
||||
"type" : "LEVEL_SPELL_IMMUNITY",
|
||||
"val" : 3
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
@ -124,23 +124,23 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
},
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"crystals" :
|
||||
{
|
||||
"type" : "SPECIAL_CRYSTAL_GENERATION"
|
||||
},
|
||||
"magicResistance" :
|
||||
{
|
||||
"type" : "MAGIC_RESISTANCE",
|
||||
"val" : 20
|
||||
},
|
||||
"crystals" :
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "SPECIAL_CRYSTAL_GENERATION"
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
@ -165,15 +165,14 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
},
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
},
|
||||
"mirror" :
|
||||
{
|
||||
"type" : "MAGIC_MIRROR",
|
||||
@ -244,6 +243,11 @@
|
||||
"subtype" : "spell.meteorShower",
|
||||
"addInfo" : 5,
|
||||
"val" : 2
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
@ -269,19 +273,14 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
},
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"twoHexAttackBreath" :
|
||||
{
|
||||
"type" : "TWO_HEX_ATTACK_BREATH"
|
||||
@ -297,6 +296,11 @@
|
||||
"type" : "SPELL_AFTER_ATTACK",
|
||||
"subtype" : "spell.acidBreath",
|
||||
"val" : 100
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
@ -319,6 +323,7 @@
|
||||
"extraNames": [ "enchanters" ],
|
||||
"faction": "neutral",
|
||||
"excludeFromRandomization" : true,
|
||||
"shots" : 32,
|
||||
"abilities":
|
||||
{
|
||||
"shooter" :
|
||||
@ -406,6 +411,7 @@
|
||||
"extraNames": [ "sharpshooters" ],
|
||||
"faction": "neutral",
|
||||
"excludeFromRandomization" : true,
|
||||
"shots" : 32,
|
||||
"abilities":
|
||||
{
|
||||
"shooter" :
|
||||
@ -444,6 +450,7 @@
|
||||
"index": 138,
|
||||
"level": 1,
|
||||
"faction": "neutral",
|
||||
"shots" : 24,
|
||||
"abilities":
|
||||
{
|
||||
"shooter" :
|
||||
|
@ -101,6 +101,7 @@
|
||||
"level": 3,
|
||||
"faction": "rampart",
|
||||
"upgrades": ["grandElf"],
|
||||
"shots" : 24,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" :
|
||||
@ -131,7 +132,8 @@
|
||||
"index": 19,
|
||||
"level": 3,
|
||||
"faction": "rampart",
|
||||
"abilities":
|
||||
"shots" : 24,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" :
|
||||
{
|
||||
@ -359,19 +361,14 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
},
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"twoHexAttackBreath" :
|
||||
{
|
||||
"type" : "TWO_HEX_ATTACK_BREATH"
|
||||
@ -380,6 +377,11 @@
|
||||
{
|
||||
"type" : "LEVEL_SPELL_IMMUNITY",
|
||||
"val" : 3
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"upgrades": ["goldDragon"],
|
||||
@ -404,19 +406,14 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"canFly" :
|
||||
{
|
||||
"type" : "FLYING"
|
||||
},
|
||||
"dragon" :
|
||||
{
|
||||
"type" : "DRAGON_NATURE"
|
||||
},
|
||||
"twoHexAttackBreath" :
|
||||
{
|
||||
"type" : "TWO_HEX_ATTACK_BREATH"
|
||||
@ -425,6 +422,11 @@
|
||||
{
|
||||
"type" : "LEVEL_SPELL_IMMUNITY",
|
||||
"val" : 4
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
|
@ -37,8 +37,17 @@
|
||||
"level": 0,
|
||||
"faction": "neutral",
|
||||
"doubleWide" : true,
|
||||
"shots" : 24,
|
||||
"abilities" :
|
||||
{
|
||||
"siegeWeapon" :
|
||||
{
|
||||
"type" : "SIEGE_WEAPON"
|
||||
},
|
||||
"shooter" :
|
||||
{
|
||||
"type" : "SHOOTER"
|
||||
},
|
||||
"siegeMachine" :
|
||||
{
|
||||
"type" : "CATAPULT",
|
||||
@ -67,6 +76,18 @@
|
||||
"level": 0,
|
||||
"faction": "neutral",
|
||||
"doubleWide" : true,
|
||||
"shots" : 24,
|
||||
"abilities" :
|
||||
{
|
||||
"siegeWeapon" :
|
||||
{
|
||||
"type" : "SIEGE_WEAPON"
|
||||
},
|
||||
"shooter" :
|
||||
{
|
||||
"type" : "SHOOTER"
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
{
|
||||
"animation": "SMBAL.DEF",
|
||||
@ -91,7 +112,12 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"heals" : {
|
||||
"siegeWeapon" :
|
||||
{
|
||||
"type" : "SIEGE_WEAPON"
|
||||
},
|
||||
"heals" :
|
||||
{
|
||||
"type" : "HEALER" ,
|
||||
"subtype" : "spell.firstAid"
|
||||
}
|
||||
@ -112,7 +138,17 @@
|
||||
"index": 148,
|
||||
"level": 0,
|
||||
"faction": "neutral",
|
||||
"abilities": { "inactive" : { "type" : "NOT_ACTIVE" } },
|
||||
"abilities":
|
||||
{
|
||||
"siegeWeapon" :
|
||||
{
|
||||
"type" : "SIEGE_WEAPON"
|
||||
},
|
||||
"inactive" :
|
||||
{
|
||||
"type" : "NOT_ACTIVE"
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
{
|
||||
"animation": "SMCART.DEF"
|
||||
@ -129,8 +165,10 @@
|
||||
"index": 149,
|
||||
"level": 0,
|
||||
"faction": "neutral",
|
||||
"shots" : 99,
|
||||
"abilities":
|
||||
{
|
||||
"siegeWeapon" : { "type" : "SIEGE_WEAPON" },
|
||||
"shooter" : { "type" : "SHOOTER" },
|
||||
"ignoreDefence" : { "type" : "ENEMY_DEFENCE_REDUCTION", "val" : 100 },
|
||||
"noWallPenalty" : { "type" : "NO_WALL_PENALTY" },
|
||||
|
@ -92,6 +92,7 @@
|
||||
"level": 3,
|
||||
"faction": "stronghold",
|
||||
"upgrades": ["orcChieftain"],
|
||||
"shots" : 12,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" :
|
||||
@ -122,6 +123,7 @@
|
||||
"index": 89,
|
||||
"level": 3,
|
||||
"faction": "stronghold",
|
||||
"shots" : 24,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" :
|
||||
@ -274,6 +276,7 @@
|
||||
"index": 94,
|
||||
"level": 6,
|
||||
"faction": "stronghold",
|
||||
"shots" : 16,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" :
|
||||
@ -310,6 +313,7 @@
|
||||
"index": 95,
|
||||
"level": 6,
|
||||
"faction": "stronghold",
|
||||
"shots" : 24,
|
||||
"abilities":
|
||||
{
|
||||
"shooter" :
|
||||
@ -355,15 +359,15 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
},
|
||||
"reduceDefence" :
|
||||
{
|
||||
"type" : "ENEMY_DEFENCE_REDUCTION",
|
||||
"val" : 40
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"upgrades": ["ancientBehemoth"],
|
||||
@ -388,15 +392,15 @@
|
||||
"doubleWide" : true,
|
||||
"abilities":
|
||||
{
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
},
|
||||
"reduceDefence" :
|
||||
{
|
||||
"type" : "ENEMY_DEFENCE_REDUCTION",
|
||||
"val" : 80
|
||||
},
|
||||
"KING_1" : // Will be affected by Slayer with no expertise
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 0
|
||||
}
|
||||
},
|
||||
"graphics" :
|
||||
|
@ -26,6 +26,7 @@
|
||||
"index": 29,
|
||||
"level": 1,
|
||||
"faction": "tower",
|
||||
"shots" : 8,
|
||||
"abilities" :
|
||||
{
|
||||
"shooter" :
|
||||
@ -178,6 +179,7 @@
|
||||
"index": 34,
|
||||
"level": 4,
|
||||
"faction": "tower",
|
||||
"shots" : 24,
|
||||
"abilities":
|
||||
{
|
||||
"shooter" :
|
||||
@ -218,6 +220,7 @@
|
||||
"index": 35,
|
||||
"level": 4,
|
||||
"faction": "tower",
|
||||
"shots" : 24,
|
||||
"abilities":
|
||||
{
|
||||
"shooter" :
|
||||
@ -415,14 +418,14 @@
|
||||
"faction": "tower",
|
||||
"abilities" :
|
||||
{
|
||||
"immuneToMind" :
|
||||
{
|
||||
"type" : "MIND_IMMUNITY"
|
||||
},
|
||||
"KING_3" : // Will be affected by Expert Slayer only
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 3
|
||||
},
|
||||
"immuneToMind" :
|
||||
{
|
||||
"type" : "MIND_IMMUNITY"
|
||||
}
|
||||
},
|
||||
"upgrades": ["titan"],
|
||||
@ -444,13 +447,9 @@
|
||||
"index": 41,
|
||||
"level": 7,
|
||||
"faction": "tower",
|
||||
"shots" : 24,
|
||||
"abilities" :
|
||||
{
|
||||
"KING_3" : // Will be affected by Expert Slayer only
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 3
|
||||
},
|
||||
"shooter" :
|
||||
{
|
||||
"type" : "SHOOTER"
|
||||
@ -463,6 +462,11 @@
|
||||
{
|
||||
"type" : "MIND_IMMUNITY"
|
||||
},
|
||||
"KING_3" : // Will be affected by Expert Slayer only
|
||||
{
|
||||
"type" : "KING",
|
||||
"val" : 3
|
||||
},
|
||||
"hateBlackDragons" :
|
||||
{
|
||||
"type" : "HATE",
|
||||
|
@ -222,7 +222,8 @@
|
||||
"sounds" : {
|
||||
"ambient" : ["LOOPFACT"],
|
||||
"visit" : ["MILITARY"]
|
||||
}
|
||||
},
|
||||
"creatures": [ ["ballista"], ["firstAidTent"], ["ammoCart"] ]
|
||||
},
|
||||
"types" : {
|
||||
"object" : {
|
||||
|
@ -22,7 +22,8 @@
|
||||
"properties" : {
|
||||
"verticalPosition" : {"type" : "string", "enum" :["top","bottom"]},
|
||||
"defName" : {"type" : "string", "format" : "animationFile"},
|
||||
"effectName" : { "type" : "string" }
|
||||
"effectName" : { "type" : "string" },
|
||||
"transparency" : {"type" : "number", "minimum" : 0, "maximum" : 1}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
||||
|
@ -252,7 +252,7 @@
|
||||
"targetType": "NO_TARGET",
|
||||
|
||||
"animation":{
|
||||
"hit":["SP04_"]
|
||||
"hit":[{ "defName" : "SP04_", "transparency" : 0.5}]
|
||||
},
|
||||
"sounds": {
|
||||
"cast": "DEATHCLD"
|
||||
|
@ -44,7 +44,7 @@
|
||||
{"minimumAngle": 1.20 ,"defName":"C08SPW1"},
|
||||
{"minimumAngle": 1.50 ,"defName":"C08SPW0"}
|
||||
],
|
||||
"hit":["C08SPW5"]
|
||||
"hit":[ {"defName" : "C08SPW5", "transparency" : 0.5 }]
|
||||
},
|
||||
"sounds": {
|
||||
"cast": "ICERAY"
|
||||
@ -309,7 +309,7 @@
|
||||
"targetType" : "CREATURE",
|
||||
|
||||
"animation":{
|
||||
"affect":["C14SPA0"]
|
||||
"affect":[{"defName" : "C14SPA0", "transparency" : 0.5}]
|
||||
},
|
||||
"sounds": {
|
||||
"cast": "SACBRETH"
|
||||
|
@ -483,7 +483,7 @@
|
||||
"targetType" : "CREATURE",
|
||||
|
||||
"animation":{
|
||||
"affect":["C01SPE0"]
|
||||
"affect":[{ "defName" : "C01SPE0", "transparency" : 0.5}]
|
||||
},
|
||||
"sounds": {
|
||||
"cast": "RESURECT"
|
||||
|
@ -652,7 +652,7 @@
|
||||
"targetType" : "CREATURE",
|
||||
|
||||
"animation":{
|
||||
"affect":["C07SPA1"],
|
||||
"affect":[{"defName" : "C07SPA1", "transparency" : 0.5}],
|
||||
"projectile":[{"defName":"C07SPA0"}]//???
|
||||
},
|
||||
"sounds": {
|
||||
@ -696,7 +696,7 @@
|
||||
"targetType" : "CREATURE",
|
||||
|
||||
"animation":{
|
||||
"affect":[{"defName":"C10SPW", "verticalPosition":"bottom"}]
|
||||
"affect":[{"defName":"C10SPW", "verticalPosition":"bottom", "transparency" : 0.5}]
|
||||
},
|
||||
"sounds": {
|
||||
"cast": "PRAYER"
|
||||
|
@ -9,10 +9,10 @@
|
||||
VCMI is an open-source recreation of Heroes of Might & Magic III engine, giving it new and extended possibilities.
|
||||
|
||||
<p>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.3.0/Castle%20Siege.jpg?raw=true" alt="Vanilla town siege in extended window" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.3.0/Town%20Screen%20with%20Radial%20Menu.jpg?raw=true" alt="Vanilla town view with radial menu for touchscreen devices" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Big%20spellbook.jpg?raw=true" alt="Large Spellbook with German translation" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Quick%20Hero%20Select%20Bastion.jpg?raw=true" alt="New widget for Hero selection, featuring Pavillon Town" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.3.0/Castle%20Siege.jpg?raw=true" alt="Vanilla town siege in extended window" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.3.0/Town%20Screen%20with%20Radial%20Menu.jpg?raw=true" alt="Vanilla town view with radial menu for touchscreen devices" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Big%20spellbook.jpg?raw=true" alt="Large Spellbook with German translation" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Quick%20Hero%20Select%20Bastion.jpg?raw=true" alt="New widget for Hero selection, featuring Pavillon Town" style="height:120px;"/>
|
||||
</p>
|
||||
|
||||
|
||||
@ -37,10 +37,12 @@ Please see corresponding installation guide articles for details for your platfo
|
||||
- [Android](players/Installation_Android.md)
|
||||
- [iOS](players/Installation_iOS.md)
|
||||
|
||||
See also installation guide for [Heroes Chronicles](players/Heroes_Chronicles.md).
|
||||
|
||||
<p>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Antagarich%20Burning%20Battle.jpg?raw=true" alt="Forge Town in battle" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Town%20and%20Unit.jpg?raw=true" alt="Asylum town with new creature dialog" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Siege.jpg?raw=true" alt="Ruins town siege" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Antagarich%20Burning%20Battle.jpg?raw=true" alt="Forge Town in battle" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Town%20and%20Unit.jpg?raw=true" alt="Asylum town with new creature dialog" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Siege.jpg?raw=true" alt="Ruins town siege" style="height:120px;"/>
|
||||
<img src="https://github.com/vcmi/VCMI.eu/blob/master/static/img/screenshots/1.4.0/Editor.jpg?raw=true" alt="Map editor" style="height:120px;"/>
|
||||
</p>
|
||||
|
||||
|
@ -115,7 +115,7 @@ Extract `ccache` to a folder of your choosing, add the folder to the `PATH` envi
|
||||
### Compile VCMI with MinGW via MSYS2
|
||||
- Install MSYS2 from https://www.msys2.org/
|
||||
- Start the `MSYS MinGW x64`-shell
|
||||
- Install dependencies: `pacman -S mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_image mingw-w64-x86_64-SDL2_mixer mingw-w64-x86_64-SDL2_ttf mingw-w64-x86_64-boost mingw-w64-x86_64-gcc mingw-w64-x86_64-ninja mingw-w64-x86_64-qt5-static`
|
||||
- Install dependencies: `pacman -S mingw-w64-x86_64-SDL2 mingw-w64-x86_64-SDL2_image mingw-w64-x86_64-SDL2_mixer mingw-w64-x86_64-SDL2_ttf mingw-w64-x86_64-boost mingw-w64-x86_64-gcc mingw-w64-x86_64-ninja mingw-w64-x86_64-qt5-static mingw-w64-x86_64-qt5-tools mingw-w64-x86_64-tbb`
|
||||
- Generate and build solution from VCMI-root dir: `cmake --preset windows-mingw-release && cmake --build --preset windows-mingw-release`
|
||||
|
||||
**NOTE:** This will link Qt5 statically to `VCMI_launcher.exe` and `VCMI_Mapeditor.exe`. See [PR #3421](https://github.com/vcmi/vcmi/pull/3421) for some background.
|
||||
|
@ -167,7 +167,7 @@ TODO
|
||||
],
|
||||
"cast" : []
|
||||
"hit":["C20SPX"],
|
||||
"affect":[{"defName":"C03SPA0", "verticalPosition":"bottom"}, "C11SPA1"]
|
||||
"affect":[{"defName":"C03SPA0", "verticalPosition":"bottom", "transparency" : 0.5}, "C11SPA1"]
|
||||
}
|
||||
```
|
||||
|
||||
|
34
docs/modders/HD_Graphics.md
Normal file
34
docs/modders/HD_Graphics.md
Normal file
@ -0,0 +1,34 @@
|
||||
# HD Graphics
|
||||
|
||||
It's possible to provide alternative high-definition graphics within mods. They will be used if any upscaling filter is activated.
|
||||
|
||||
## Preconditions
|
||||
|
||||
It's still necessary to add 1x standard definition graphics as before. HD graphics are seperate from usual graphics. This allows to partitially use HD for a few graphics in mod. And avoid handling huge graphics if upscaling isn't enabled.
|
||||
|
||||
Currently following scaling factors are possible to use: 2x, 3x, 4x. You can also provide multiple of them (increases size of mod, but improves loading performance for player). It's recommend to provide 2x and 3x images.
|
||||
|
||||
If user for example selects 3x resolution and only 2x exists in mod then the 2x images are upscaled to 3x (same for other combinations > 1x).
|
||||
|
||||
## Mod
|
||||
|
||||
For upscaled images you have to use following folders (next to `sprites` and `data` folders):
|
||||
- `sprites2x`, `sprites3x`, `sprites4x` for sprites
|
||||
- `data2x`, `data3x`, `data4x` for images
|
||||
|
||||
The sprites should have the same name and folder structure as in `sprites` and `data` folder. All images that are missing in the upscaled folders are scaled with the selected upscaling filter instead of using prescaled images.
|
||||
|
||||
### Shadows / Overlays
|
||||
|
||||
It's also possible (but not necessary) to add high-definition shadows: Just place a image next to the normal upscaled image with the suffix `-shadow`. E.g. `TestImage.png` and `TestImage-shadow.png`.
|
||||
In future, such shadows will likely become required to correctly exclude shadow from effects such as Clone spell.
|
||||
|
||||
Shadow images are used only for animations of following objects:
|
||||
- All adventure map objects
|
||||
- All creature animations in combat
|
||||
|
||||
Same for overlays with `-overlay`. But overlays are **necessary** for some animation graphics. They will be colorized by VCMI.
|
||||
|
||||
Currently needed for:
|
||||
- Flaggable adventure map objects. Overlay must contain a transparent image with white flags on it and will be used to colorize flags to owning player
|
||||
- Creature battle animations, idle and mouse hover group. Overlay must contain a transparent image with white outline of creature for highlighting on mouse hover)
|
9
docs/players/Heroes_Chronicles.md
Normal file
9
docs/players/Heroes_Chronicles.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Heroes Chronicles
|
||||
|
||||
It also possible to play the Heroes Chronicles with VCMI. You still need a completly installed VCMI (with heroes 3 sod / complete files).
|
||||
|
||||
You also need Heroes Chronicles from [gog.com](https://www.gog.com/en/game/heroes_chronicles_all_chapters). You need to download the offline installer. CD installations are not supported yet.
|
||||
|
||||
You can use the "Install file" button in the launcher to select the downloaded exe files. This process can take a while (especially on mobile platforms) and need some temporary free space.
|
||||
|
||||
After that you can select Heroes Chronicles from Campaign selection menu (button or custom campaign).
|
@ -4,41 +4,74 @@ You can run VCMI on iOS 12.0 and later, all devices are supported. If you wish t
|
||||
|
||||
## Step 1: Download and install VCMI
|
||||
|
||||
The easiest and recommended way to install on a non-jailbroken device is to install the [AltStore Classic](https://altstore.io/) or [Sideloadly](https://sideloadly.io/). We will use AltStore as an example below. Using this method means the VCMI certificate is auto-signed automatically.
|
||||
|
||||
i) Use [AltStore Windows](https://faq.altstore.io/altstore-classic/how-to-install-altstore-windows) or [AltStore macOS](https://faq.altstore.io/altstore-classic/how-to-install-altstore-macos) instructions to install the store depending on the operating system you are using.
|
||||
|
||||
If you're having trouble enabling "sync with this iOS device over Wi-Fi" press on the rectangular shape below "Account". Example shown below.
|
||||
|
||||

|
||||
|
||||
ii) Download the VCMI-iOS.ipa file on your iOS device directly from the [latest releases](https://github.com/vcmi/vcmi/releases/latest).
|
||||
|
||||
iii) To install the .ipa file on your device do one of the following:
|
||||
|
||||
- In AltStore go to >My Apps > press + in the top left corner. Select VCMI-iOS.ipa to install,
|
||||
- or drag and drop the .ipa file into your iOS device in iTunes
|
||||
|
||||
|
||||
## Step 2: Installing Heroes III data files
|
||||
|
||||
If you bought HoMM3 on [GOG](https://www.gog.com/de/game/heroes_of_might_and_magic_3_complete_edition), you can download the files directly from the browser in the device.
|
||||
|
||||
Launch VCMI app on the device and the launcher will prompt two files to complete the installation. Select the **.bin** file first, then the **.exe** file. This may take a few seconds. Please be patient.
|
||||
|
||||
|
||||
## Step 3: Configuration settings
|
||||
Once you have installed VCMI and have the launcher opened, select Settings on the left bar. The following Video settings are recommended:
|
||||
|
||||
- Lower reserved screen area to zero.
|
||||
- Increase interface Scaling to maximum. This number will depend on your device. For 11" iPad Air it was at 273% as an example
|
||||
|
||||
Together, the two options should eliminate black bars and enable full screen VCMI experience. Enjoy!
|
||||
|
||||
## Alternative Step 1: Download and install VCMI
|
||||
|
||||
- The latest release (recommended): <https://github.com/vcmi/vcmi/releases/latest>
|
||||
- Daily builds: <https://builds.vcmi.download/branch/develop/iOS/>
|
||||
|
||||
To run on a non-jailbroken device you need to sign the IPA file, you
|
||||
have the following options:
|
||||
To run on a non-jailbroken device you need to sign the IPA file, you have the following aternative options:
|
||||
|
||||
- (Easiest way) [AltStore](https://altstore.io/) or [Sideloadly](https://sideloadly.io/) - can be installed on Windows or macOS, don't require dealing with signing on your own
|
||||
- if you're on iOS 14.0-15.4.1, you can try <https://github.com/opa334/TrollStore>
|
||||
- if you're on iOS 14.0-15.4.1, you can try <https://github.com/opa334/TrollStore>.
|
||||
- Get signer tool [here](https://dantheman827.github.io/ios-app-signer/) and a guide [here](https://forum.kodi.tv/showthread.php?tid=245978) (it's for Kodi, but the logic is the same). Signing with this app can only be done on macOS.
|
||||
- [Create signing assets on macOS from terminal](https://github.com/kambala-decapitator/xcode-auto-signing-assets). In the command replace `your.bundle.id` with something like `com.MY-NAME.vcmi`. After that use the above signer tool.
|
||||
- [Sign from any OS (Rust)](https://github.com/indygreg/PyOxidizer/tree/main/tugger-code-signing) / [alternative project (C++)](https://github.com/zhlynn/zsign). You'd still need to find a way to create signing assets (private key and provisioning profile) though.
|
||||
|
||||
To install the signed ipa on your device, you can use Xcode or Apple Configurator (available on the Mac App Store for free). The latter also allows installing ipa from the command line, here's an example that assumes you have only 1 device connected to your Mac and the signed ipa is on your desktop:
|
||||
The easiest way to install the ipa on your device is to do one of the following:
|
||||
|
||||
- In AltStore go to >My Apps > press + in the top left corner. Select VCMI-iOS.ipa to install or
|
||||
|
||||
- Drag and drop the .ipa file into your iOS device in iTunes
|
||||
|
||||
Alternatively, to install the signed ipa on your device, you can use Xcode or Apple Configurator (available on the Mac App Store for free). The latter also allows installing ipa from the command line, here's an example that assumes you have only 1 device connected to your Mac and the signed ipa is on your desktop:
|
||||
|
||||
/Applications/Apple\ Configurator.app/Contents/MacOS/cfgutil install-app ~/Desktop/vcmi.ipa
|
||||
|
||||
## Step 2: Installing Heroes III data files
|
||||
## Alternative Step 2: Installing Heroes III data files
|
||||
|
||||
Note: if you don't need in-game videos, you can omit downloading/copying file VIDEO.VID from data folder - it will save your time and space. The same applies to the Mp3 directory.
|
||||
Note: if you don't need in-game videos, you can omit downloading/copying file VIDEO.VID from the Data folder - it will save your time and space. The same applies to the Mp3 directory.
|
||||
|
||||
### Step 2.a: Installing data files with GOG offline installer
|
||||
|
||||
If you bought HoMM3 on [GOG](https://www.gog.com/de/game/heroes_of_might_and_magic_3_complete_edition), you can download the files directly from the browser and install them in the launcher. Select the .bin file first, then the .exe file. This may take a few seconds. Please be patient.
|
||||
|
||||
### Step 2.b: Installing data files with Finder or Windows explorer
|
||||
### Step 2.a: Installing data files with Finder or Windows explorer
|
||||
|
||||
To play the game, you need to upload HoMM3 data files - **Data**, **Maps** and **Mp3** directories - to the device. Use Finder (or iTunes, if you're on Windows or your macOS is 10.14 or earlier) for that. You can also add various mods by uploading **Mods** directory. Follow [official Apple guide](https://support.apple.com/en-us/HT210598) and place files into VCMI app. Unfortunately, Finder doesn't display copy progress, give it about 10 minutes to finish.
|
||||
|
||||
### Step 2.c: Installing data files using iOS device only
|
||||
### Step 2.b: Installing data files using iOS device only
|
||||
|
||||
If you have data somewhere on device or in shared folder or you have downloaded it, you can copy it directly on your iPhone/iPad using Files application.
|
||||
|
||||
Place **Data**, **Maps** and **Mp3** folders into vcmi application - it will be visible in Files along with other applications' folders.
|
||||
|
||||
### Step 2.d: Installing data files with Xcode on macOS
|
||||
### Step 2.c: Installing data files with Xcode on macOS
|
||||
|
||||
You can also upload files with Xcode. You need to prepare "container" for that.
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
# Installation macOS
|
||||
|
||||
For iOS installation look here: (Installation on iOS)[Installation_iOS.md]
|
||||
|
||||
## Step 1: Download and install VCMI
|
||||
|
||||
- The latest release (recommended): <https://github.com/vcmi/vcmi/releases/latest>
|
||||
- The latest release (recommended):
|
||||
- manually: <https://github.com/vcmi/vcmi/releases/latest>
|
||||
- via Homebrew: `brew install --cask --no-quarantine vcmi/vcmi/vcmi`
|
||||
- Daily builds (might be unstable)
|
||||
- Intel (x86_64) builds: <https://builds.vcmi.download/branch/develop/macOS/intel>
|
||||
- Apple Silicon (arm64) builds: <https://builds.vcmi.download/branch/develop/macOS/arm>
|
||||
- Intel (x86_64) builds: <https://builds.vcmi.download/branch/develop/macOS/intel>
|
||||
- Apple Silicon (arm64) builds: <https://builds.vcmi.download/branch/develop/macOS/arm>
|
||||
|
||||
If the app doesn't open, right-click the app bundle - select *Open* menu item - press *Open* button.
|
||||
If the app doesn't open, right-click the app bundle - select *Open* menu item - press *Open* button. You may also need to allow running it in System Settings - Privacy & Security.
|
||||
|
||||
Please report about gameplay problem on forums: [Help & Bugs](https://forum.vcmi.eu/c/international-board/help-bugs) Make sure to specify what hardware and macOS version you use.
|
||||
|
||||
@ -21,5 +21,5 @@ If you bought HoMM3 on [GOG](https://www.gog.com/de/game/heroes_of_might_and_mag
|
||||
|
||||
### Step 2.b: Installing by the classic way
|
||||
|
||||
1. Find a way to unpack Windows Heroes III or GOG installer. For example, use `vcmibuilder` script inside app bundle or install the game with [CrossOver](https://www.codeweavers.com/crossover) or [Wineskin](https://github.com/Gcenx/WineskinServer).
|
||||
1. Find a way to unpack Windows Heroes III or GOG installer. For example, use `vcmibuilder` script inside app bundle or install the game with [CrossOver](https://www.codeweavers.com/crossover) or [Kegworks](https://github.com/Kegworks-App/Kegworks).
|
||||
2. Place or symlink **Data**, **Maps** and **Mp3** directories from Heroes III to:`~/Library/Application\ Support/vcmi/`
|
||||
|
@ -1091,7 +1091,7 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="104"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="105"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="141"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="156"/>
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation>英雄无敌历代记</translation>
|
||||
</message>
|
||||
|
@ -1085,7 +1085,7 @@ Exkluzivní celá obrazovka - hra zakryje vaši celou obrazovku a použije vybra
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="104"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="105"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="141"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="156"/>
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation>Heroes Chronicles</translation>
|
||||
</message>
|
||||
|
@ -1071,7 +1071,7 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="104"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="105"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="141"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="156"/>
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -1090,7 +1090,7 @@ Mode exclusif plein écran - le jeu couvrira l"intégralité de votre écra
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="104"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="105"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="141"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="156"/>
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -442,17 +442,17 @@
|
||||
<message>
|
||||
<location filename="../modManager/cmodlistview_moc.cpp" line="648"/>
|
||||
<source>Gog files</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Gog-Dateien</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/cmodlistview_moc.cpp" line="650"/>
|
||||
<source>All files (*.*)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Alle Dateien (*.*)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/cmodlistview_moc.cpp" line="652"/>
|
||||
<source>Select files (configs, mods, maps, campaigns, gog files) to install...</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Wähle zu installierenden Dateien aus (Konfigs, Mods, Karten, Kampagnen, Gog-Dateien)...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/cmodlistview_moc.cpp" line="677"/>
|
||||
@ -499,7 +499,7 @@ Installation erfolgreich heruntergeladen?</translation>
|
||||
<message>
|
||||
<location filename="../modManager/cmodlistview_moc.cpp" line="852"/>
|
||||
<source>Installing chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Installation der Chronicles</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/cmodlistview_moc.cpp" line="925"/>
|
||||
@ -667,7 +667,7 @@ Installation erfolgreich heruntergeladen?</translation>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="484"/>
|
||||
<source>Downscaling Filter</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Herunterskalierungsfilter</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="597"/>
|
||||
@ -692,7 +692,7 @@ Installation erfolgreich heruntergeladen?</translation>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="908"/>
|
||||
<source>Automatic (Linear)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Automatisch (linear)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="274"/>
|
||||
@ -709,42 +709,42 @@ Installation erfolgreich heruntergeladen?</translation>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="539"/>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="1389"/>
|
||||
<source>Automatic</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Automatisch</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="75"/>
|
||||
<source>Mods Validation</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Mod-Validierung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="88"/>
|
||||
<source>None</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Keiner</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="93"/>
|
||||
<source>xBRZ x2</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>xBRZ x2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="98"/>
|
||||
<source>xBRZ x3</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>xBRZ x3</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="103"/>
|
||||
<source>xBRZ x4</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>xBRZ x4</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="316"/>
|
||||
<source>Full</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Voll</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="788"/>
|
||||
<source>Use scalable fonts</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Skalierbare Schriftarten verwenden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="859"/>
|
||||
@ -754,27 +754,27 @@ Installation erfolgreich heruntergeladen?</translation>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="1079"/>
|
||||
<source>Cursor Scaling</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Cursor-Skalierung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="1108"/>
|
||||
<source>Scalable</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Skalierbar</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="1144"/>
|
||||
<source>Miscellaneous</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Sonstiges</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="1303"/>
|
||||
<source>Font Scaling (experimental)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Schriftskalierung (experimentell)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="1367"/>
|
||||
<source>Original</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Original</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="1406"/>
|
||||
@ -784,7 +784,7 @@ Installation erfolgreich heruntergeladen?</translation>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="1439"/>
|
||||
<source>Basic</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Grundlegend</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../settingsView/csettingsview_moc.ui" line="656"/>
|
||||
@ -1059,35 +1059,35 @@ Exklusiver Vollbildmodus - das Spiel bedeckt den gesamten Bildschirm und verwend
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="48"/>
|
||||
<source>File cannot opened</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Datei kann nicht geöffnet werden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="56"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="71"/>
|
||||
<source>Invalid file selected</source>
|
||||
<translation type="unfinished">Ungültige Datei ausgewählt</translation>
|
||||
<translation>Ungültige Datei ausgewählt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="56"/>
|
||||
<source>You have to select an gog installer file!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Sie müssen eine Gog-Installer-Datei auswählen!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="71"/>
|
||||
<source>You have to select an chronicle installer file!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Sie müssen eine Chronicle-Installationsdatei auswählen!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="87"/>
|
||||
<source>Extracting error!</source>
|
||||
<translation type="unfinished">Fehler beim Extrahieren!</translation>
|
||||
<translation>Fehler beim Extrahieren!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="104"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="105"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="141"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="156"/>
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>Heroes Chronicles</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@ -1422,18 +1422,18 @@ Bitte wählen Sie ein Verzeichnis mit Heroes III: Complete Edition oder Heroes I
|
||||
<location filename="../innoextract.cpp" line="42"/>
|
||||
<source>Stream error while extracting files!
|
||||
error reason: </source>
|
||||
<translation type="unfinished">Stream-Fehler beim Extrahieren von Dateien!
|
||||
<translation>Stream-Fehler beim Extrahieren von Dateien!
|
||||
Fehlerursache: </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../innoextract.cpp" line="55"/>
|
||||
<source>Not a supported Inno Setup installer!</source>
|
||||
<translation type="unfinished">Kein unterstütztes Inno Setup Installationsprogramm!</translation>
|
||||
<translation>Kein unterstütztes Inno Setup Installationsprogramm!</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../innoextract.cpp" line="58"/>
|
||||
<source>VCMI was compiled without innoextract support, which is needed to extract exe files!</source>
|
||||
<translation type="unfinished"></translation>
|
||||
<translation>VCMI wurde ohne innoextract-Unterstützung kompiliert, die zum Extrahieren von exe-Dateien benötigt wird!</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
@ -1085,7 +1085,7 @@ Pełny ekran klasyczny - gra przysłoni cały ekran uruchamiając się w wybrane
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="104"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="105"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="141"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="156"/>
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -1085,7 +1085,7 @@ Modo de tela cheia exclusivo - o jogo cobrirá toda a sua tela e usará a resolu
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="104"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="105"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="141"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="156"/>
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation>Heroes Chronicles</translation>
|
||||
</message>
|
||||
|
@ -1071,7 +1071,7 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="104"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="105"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="141"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="156"/>
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -1084,7 +1084,7 @@ Pantalla completa - el juego cubrirá la totalidad de la pantalla y utilizará l
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="104"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="105"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="141"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="156"/>
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -1085,7 +1085,7 @@ Exklusivt helskärmsläge - spelet kommer att täcka hela skärmen och använda
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="104"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="105"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="141"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="156"/>
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -1085,7 +1085,7 @@ Fullscreen Exclusive Mode - game will cover entirety of your screen and will use
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="104"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="105"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="141"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="156"/>
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -1077,7 +1077,7 @@ Toàn màn hình riêng biệt - Trò chơi chạy toàn màn hình và dùng đ
|
||||
<message>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="104"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="105"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="141"/>
|
||||
<location filename="../modManager/chroniclesextractor.cpp" line="156"/>
|
||||
<source>Heroes Chronicles</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -236,7 +236,7 @@ DLL_LINKAGE CArtifactInstance * ArtifactUtils::createArtifact(const ArtifactID &
|
||||
assert(art);
|
||||
|
||||
auto * artInst = new CArtifactInstance(art);
|
||||
if(art->isCombined())
|
||||
if(art->isCombined() && !art->isFused())
|
||||
{
|
||||
for(const auto & part : art->getConstituents())
|
||||
artInst->addPart(createArtInst(part), ArtifactPosition::PRE_FIRST);
|
||||
|
@ -192,7 +192,6 @@ bool CArtifact::isTradable() const
|
||||
switch(id.toEnum())
|
||||
{
|
||||
case ArtifactID::SPELLBOOK:
|
||||
case ArtifactID::GRAIL:
|
||||
return false;
|
||||
default:
|
||||
return !isBig();
|
||||
|
@ -957,12 +957,12 @@ void CGameInfoCallback::calculatePaths( const CGHeroInstance *hero, CPathsInfo &
|
||||
|
||||
const CArtifactInstance * CGameInfoCallback::getArtInstance( ArtifactInstanceID aid ) const
|
||||
{
|
||||
return gs->map->artInstances[aid.num];
|
||||
return gs->map->artInstances.at(aid.num);
|
||||
}
|
||||
|
||||
const CGObjectInstance * CGameInfoCallback::getObjInstance( ObjectInstanceID oid ) const
|
||||
{
|
||||
return gs->map->objects[oid.num];
|
||||
return gs->map->objects.at(oid.num);
|
||||
}
|
||||
|
||||
const CArtifactSet * CGameInfoCallback::getArtSet(const ArtifactLocation & loc) const
|
||||
|
@ -790,6 +790,16 @@ if(WIN32)
|
||||
)
|
||||
endif()
|
||||
|
||||
# Use '-Wa,-mbig-obj' for files that generate very large object files
|
||||
# when compiling with MinGW lest you get "too many sections" assembler errors
|
||||
if(MINGW AND CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set_source_files_properties(
|
||||
serializer/SerializerReflection.cpp
|
||||
IGameCallback.cpp
|
||||
PROPERTIES
|
||||
COMPILE_OPTIONS "-Wa,-mbig-obj")
|
||||
endif()
|
||||
|
||||
vcmi_set_output_dir(vcmi "")
|
||||
|
||||
enable_pch(vcmi)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user