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

Merge remote-tracking branch 'upstream/develop' into develop

This commit is contained in:
Xilmi
2024-09-25 14:08:34 +02:00
38 changed files with 564 additions and 366 deletions

View File

@@ -45,11 +45,12 @@ void DamageCache::buildObstacleDamageCache(std::shared_ptr<HypotheticBattle> hb,
continue;
std::unique_ptr<spells::BattleCast> cast = nullptr;
std::unique_ptr<spells::ObstacleCasterProxy> caster = nullptr;
if(spellObstacle->obstacleType == SpellCreatedObstacle::EObstacleType::SPELL_CREATED)
{
const auto * hero = hb->battleGetFightingHero(spellObstacle->casterSide);
auto caster = spells::ObstacleCasterProxy(hb->getSidePlayer(spellObstacle->casterSide), hero, *spellObstacle);
cast = std::make_unique<spells::BattleCast>(spells::BattleCast(hb.get(), &caster, spells::Mode::PASSIVE, obst->getTrigger().toSpell()));
caster = std::make_unique<spells::ObstacleCasterProxy>(hb->getSidePlayer(spellObstacle->casterSide), hero, *spellObstacle);
cast = std::make_unique<spells::BattleCast>(spells::BattleCast(hb.get(), caster.get(), spells::Mode::PASSIVE, obst->getTrigger().toSpell()));
}
auto affectedHexes = obst->getAffectedTiles();

View File

@@ -21,6 +21,7 @@
"vcmi.adventureMap.playerAttacked" : "Player has been attacked: %s",
"vcmi.adventureMap.moveCostDetails" : "Movement points - Cost: %TURNS turns + %POINTS points, Remaining points: %REMAINING",
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Movement points - Cost: %POINTS points, Remaining points: %REMAINING",
"vcmi.adventureMap.movementPointsHeroInfo" : "(Movement points: %REMAINING / %POINTS)",
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Sorry, replay opponent turn is not implemented yet!",
"vcmi.capitalColors.0" : "Red",

View File

@@ -20,6 +20,7 @@
"vcmi.adventureMap.playerAttacked" : "Gracz został zaatakowany: %s",
"vcmi.adventureMap.moveCostDetails" : "Punkty ruchu - Koszt: %TURNS tury + %POINTS punktów, Pozostanie: %REMAINING punktów",
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Punkty ruchu - Koszt: %POINTS punktów, Pozostanie: %REMAINING punktów",
"vcmi.adventureMap.movementPointsHeroInfo" : "(Punkty ruchu: %REMAINING / %POINTS)",
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Wybacz, powtórka ruchu wroga nie została jeszcze zaimplementowana!",
"vcmi.capitalColors.0" : "Czerwony",

View File

@@ -9,9 +9,10 @@
"vcmi.adventureMap.monsterThreat.levels.6" : "Forte",
"vcmi.adventureMap.monsterThreat.levels.7" : "Muito Forte",
"vcmi.adventureMap.monsterThreat.levels.8" : "Desafiante",
"vcmi.adventureMap.monsterThreat.levels.9" : "Dominante",
"vcmi.adventureMap.monsterThreat.levels.9" : "Avassaladora",
"vcmi.adventureMap.monsterThreat.levels.10" : "Mortal",
"vcmi.adventureMap.monsterThreat.levels.11" : "Impossível",
"vcmi.adventureMap.monsterLevel" : "\n\nNível %LEVEL, unidade de %TOWN",
"vcmi.adventureMap.confirmRestartGame" : "Tem certeza de que deseja reiniciar o jogo?",
"vcmi.adventureMap.noTownWithMarket" : "Não há mercados disponíveis!",
@@ -20,6 +21,7 @@
"vcmi.adventureMap.playerAttacked" : "O jogador foi atacado: %s",
"vcmi.adventureMap.moveCostDetails" : "Pontos de movimento - Custo: %TURNS turnos + %POINTS pontos, Pontos restantes: %REMAINING",
"vcmi.adventureMap.moveCostDetailsNoTurns" : "Pontos de movimento - Custo: %POINTS pontos, Pontos restantes: %REMAINING",
"vcmi.adventureMap.movementPointsHeroInfo" : "(Pontos de movimento: %REMAINING / %POINTS)",
"vcmi.adventureMap.replayOpponentTurnNotImplemented" : "Desculpe, a repetição do turno do oponente ainda não está implementada!",
"vcmi.capitalColors.0" : "Vermelho",
@@ -234,8 +236,10 @@
"vcmi.adventureOptions.borderScroll.help" : "{Rolagem de Borda}\n\nFaz o mapa de aventura rolar quando o cursor está adjacente à borda da janela. Pode ser desativado mantendo pressionada a tecla CTRL.",
"vcmi.adventureOptions.infoBarCreatureManagement.hover" : "Gerenciar Criaturas no Painel de Info.",
"vcmi.adventureOptions.infoBarCreatureManagement.help" : "{Gerencia as Criaturas no Painel de Informações}\n\nPermite reorganizar criaturas no painel de informações em vez de alternar entre os componentes padrão.",
"vcmi.adventureOptions.leftButtonDrag.hover" : "Arrastar Mapa com o Botão Esquerdo",
"vcmi.adventureOptions.leftButtonDrag.hover" : "Botão Esq. Arrasta",
"vcmi.adventureOptions.leftButtonDrag.help" : "{Arrastar Mapa com o Botão Esquerdo}\n\nQuando ativado, mover o mouse com o botão esquerdo pressionado irá arrastar a visualização do mapa de aventura.",
"vcmi.adventureOptions.rightButtonDrag.hover" : "Botão Dir. Arrasta",
"vcmi.adventureOptions.rightButtonDrag.help" : "{Arrastar Mapa com o Botão Direito}\n\nQuando ativado, mover o mouse com o botão direito pressionado irá arrastar a visualização do mapa de aventura.",
"vcmi.adventureOptions.smoothDragging.hover" : "Arrastar Suavemente o Mapa",
"vcmi.adventureOptions.smoothDragging.help" : "{Arrasta o Mapa Suavemente}\n\nQuando ativado, o arrasto do mapa tem um efeito de movimento moderno.",
"vcmi.adventureOptions.skipAdventureMapAnimations.hover" : "Omitir Efeitos de Desvanecimento",
@@ -641,7 +645,7 @@
"core.bonus.SPELL_IMMUNITY.description" : "Imune a ${subtype.spell}",
"core.bonus.SPELL_LIKE_ATTACK.name" : "Ataque Similar a Feitiço",
"core.bonus.SPELL_LIKE_ATTACK.description" : "Ataques com ${subtype.spell}",
"core.bonus.SPELL_RESISTANCE_AURA.name" : "Aura de Resistência a Feitiços",
"core.bonus.SPELL_RESISTANCE_AURA.name" : "Aura de Resistência",
"core.bonus.SPELL_RESISTANCE_AURA.description" : "Pilhas próximas ganham ${val}% de resistência a magia",
"core.bonus.SUMMON_GUARDIANS.name" : "Invocar Guardas",
"core.bonus.SUMMON_GUARDIANS.description" : "No início da batalha, invoca ${subtype.creature} (${val}%)",
@@ -662,5 +666,7 @@
"core.bonus.WIDE_BREATH.name" : "Sopro Amplo",
"core.bonus.WIDE_BREATH.description" : "Ataque de sopro amplo (vários hexágonos)",
"core.bonus.DISINTEGRATE.name": "Desintegrar",
"core.bonus.DISINTEGRATE.description": "Nenhum corpo permanece após a morte"
"core.bonus.DISINTEGRATE.description": "Nenhum corpo permanece após a morte",
"core.bonus.INVINCIBLE.name": "Invencível",
"core.bonus.INVINCIBLE.description": "Não pode ser afetado por nada"
}

View File

@@ -171,10 +171,11 @@ void CPlayerInterface::initGameInterface(std::shared_ptr<Environment> ENV, std::
void CPlayerInterface::closeAllDialogs()
{
// remove all active dialogs that do not expect query answer
for (;;)
while(true)
{
auto adventureWindow = GH.windows().topWindow<AdventureMapInterface>();
auto infoWindow = GH.windows().topWindow<CInfoWindow>();
auto topWindow = GH.windows().topWindow<WindowBase>();
if(adventureWindow != nullptr)
break;
@@ -182,16 +183,8 @@ void CPlayerInterface::closeAllDialogs()
if(infoWindow && infoWindow->ID != QueryID::NONE)
break;
if (infoWindow)
infoWindow->close();
else
GH.windows().popWindows(1);
topWindow->close();
}
if(castleInt)
castleInt->close();
castleInt = nullptr;
}
void CPlayerInterface::playerEndsTurn(PlayerColor player)
@@ -1460,6 +1453,7 @@ void CPlayerInterface::playerBlocked(int reason, bool start)
cmp.push_back(std::make_shared<CComponent>(ComponentType::FLAG, playerID));
makingTurn = true; //workaround for stiff showInfoDialog implementation
showInfoDialog(msg, cmp);
waitWhileDialog();
makingTurn = false;
}
}

View File

@@ -557,7 +557,8 @@ void AdventureMapInterface::onTileLeftClicked(const int3 &targetPosition)
else //still here? we need to move hero if we clicked end of already selected path or calculate a new path otherwise
{
if(LOCPLINT->localState->hasPath(currentHero) &&
LOCPLINT->localState->getPath(currentHero).endPos() == targetPosition)//we'll be moving
LOCPLINT->localState->getPath(currentHero).endPos() == targetPosition &&
!GH.isKeyboardShiftDown())//we'll be moving
{
assert(!CGI->mh->hasOngoingAnimations());
if(!CGI->mh->hasOngoingAnimations() && LOCPLINT->localState->getPath(currentHero).nextNode().turns == 0)

View File

@@ -266,7 +266,7 @@ void CHeroList::CHeroItem::showTooltip()
std::string CHeroList::CHeroItem::getHoverText()
{
return boost::str(boost::format(CGI->generaltexth->allTexts[15]) % hero->getNameTranslated() % hero->getClassNameTranslated());
return boost::str(boost::format(CGI->generaltexth->allTexts[15]) % hero->getNameTranslated() % hero->getClassNameTranslated()) + hero->getMovementPointsTextIfOwner(hero->getOwner());
}
void CHeroList::CHeroItem::gesture(bool on, const Point & initialPosition, const Point & finalPosition)

View File

@@ -72,7 +72,9 @@ void InputSourceMouse::handleEventMouseButtonDown(const SDL_MouseButtonEvent & b
void InputSourceMouse::handleEventMouseWheel(const SDL_MouseWheelEvent & wheel)
{
#if SDL_VERSION_ATLEAST(2,26,0)
//NOTE: while mouseX / mouseY properties are available since 2.26.0, they are not converted into logical coordinates so don't account for resolution scaling
// This SDL bug was fixed in 2.30.1: https://github.com/libsdl-org/SDL/issues/9097
#if SDL_VERSION_ATLEAST(2,30,1)
GH.events().dispatchMouseScrolled(Point(wheel.x, wheel.y), Point(wheel.mouseX, wheel.mouseY) / GH.screenHandler().getScalingFactor());
#else
GH.events().dispatchMouseScrolled(Point(wheel.x, wheel.y), GH.getCursorPosition());

View File

@@ -343,6 +343,6 @@ WindowBase::WindowBase(int used_, Point pos_)
void WindowBase::close()
{
if(!GH.windows().isTopWindow(this))
logGlobal->error("Only top interface must be closed");
throw std::runtime_error("Only top interface can be closed");
GH.windows().popWindows(1);
}

View File

@@ -148,7 +148,6 @@ class WindowBase : public CIntObject
{
public:
WindowBase(int used_ = 0, Point pos_ = Point());
protected:
virtual void close();
};

View File

@@ -83,7 +83,7 @@ std::vector<std::string> CMessage::breakText(std::string text, size_t maxLineWid
std::string printableString;
// loops till line is full or end of text reached
while(currPos < text.length() && text[currPos] != 0x0a)
while(currPos < text.length() && text[currPos] != 0x0a && graphics->fonts[font]->getStringWidth(printableString) <= maxLineWidth)
{
symbolSize = TextOperations::getUnicodeCharacterSize(text[currPos]);
@@ -115,22 +115,24 @@ std::vector<std::string> CMessage::breakText(std::string text, size_t maxLineWid
color = "";
}
else
{
std::string newPrintableString = printableString;
newPrintableString.append(text.data() + currPos, symbolSize);
if (graphics->fonts[font]->getStringWidth(newPrintableString) < maxLineWidth)
printableString.append(text.data() + currPos, symbolSize);
else
break;
}
printableString.append(text.data() + currPos, symbolSize);
currPos += symbolSize;
}
// long line, create line break
// not all line has been processed - it turned out to be too long, so erase everything after last word break
// if string consists from a single word (or this is Chinese/Korean) - erase only last symbol to bring line back to allowed length
if(currPos < text.length() && (text[currPos] != 0x0a))
{
if(wordBreak != ui32(-1))
{
currPos = wordBreak;
if(text.substr(0, currPos).find('{') == std::string::npos)
{
opened = false;
color = "";
}
}
else
currPos -= symbolSize;
}

View File

@@ -0,0 +1,151 @@
{
"type" : "object",
"$schema" : "http://json-schema.org/draft-04/schema",
"title" : "VCMI game settings format",
"description" : "Format used to define game settings in VCMI",
"additionalProperties" : false,
"properties" : {
"textData" : {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"heroClass" : { "type" : "number" },
"artifact" : { "type" : "number" },
"creature" : { "type" : "number" },
"faction" : { "type" : "number" },
"hero" : { "type" : "number" },
"spell" : { "type" : "number" },
"object" : { "type" : "number" },
"terrain" : { "type" : "number" },
"river" : { "type" : "number" },
"road" : { "type" : "number" }
}
},
"mapFormat" : {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"restorationOfErathia" : { "type" : "object" },
"armageddonsBlade" : { "type" : "object" },
"shadowOfDeath" : { "type" : "object" },
"chronicles" : { "type" : "object" },
"jsonVCMI" : { "type" : "object" },
"hornOfTheAbyss" : { "type" : "object" },
"inTheWakeOfGods" : { "type" : "object" }
}
},
"heroes" : {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"perPlayerOnMapCap" : { "type" : "number" },
"perPlayerTotalCap" : { "type" : "number" },
"retreatOnWinWithoutTroops" : { "type" : "boolean" },
"startingStackChances" : { "type" : "array" },
"backpackSize" : { "type" : "number" },
"tavernInvite" : { "type" : "boolean" },
"minimalPrimarySkills" : { "type" : "array" }
}
},
"towns" : {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"buildingsPerTurnCap" : { "type" : "number" },
"startingDwellingChances" : { "type" : "array" }
}
},
"combat": {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"goodMoraleDice" : { "type" : "array" },
"badMoraleDice" : { "type" : "array" },
"goodLuckDice" : { "type" : "array" },
"badLuckDice" : { "type" : "array" },
"backpackSize" : { "type" : "number" },
"attackPointDamageFactor" : { "type" : "number" },
"attackPointDamageFactorCap" : { "type" : "number" },
"defensePointDamageFactor" : { "type" : "number" },
"defensePointDamageFactorCap" : { "type" : "number" },
"oneHexTriggersObstacles" : { "type" : "boolean" },
"layouts" : { "type" : "object" }
}
},
"creatures": {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"weeklyGrowthPercent" : { "type" : "number" },
"weeklyGrowthCap" : { "type" : "number" },
"dailyStackExperience" : { "type" : "number" },
"allowRandomSpecialWeeks" : { "type" : "boolean" },
"allowAllForDoubleMonth" : { "type" : "boolean" }
}
},
"dwellings": {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"accumulateWhenNeutral" : { "type" : "boolean" },
"accumulateWhenOwned" : { "type" : "boolean" },
"mergeOnRecruit" : { "type" : "boolean" }
}
},
"markets": {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"blackMarketRestockPeriod" : { "type" : "number" }
}
},
"banks": {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"showGuardsComposition" : { "type" : "boolean" }
}
},
"modules": {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"stackExperience" : { "type" : "boolean" },
"stackArtifact" : { "type" : "boolean" },
"commanders" : { "type" : "boolean" }
}
},
"pathfinder": {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"ignoreGuards" : { "type" : "boolean" },
"useBoat" : { "type" : "boolean" },
"useMonolithTwoWay" : { "type" : "boolean" },
"useMonolithOneWayUnique" : { "type" : "boolean" },
"useMonolithOneWayRandom" : { "type" : "boolean" },
"useWhirlpool" : { "type" : "boolean" },
"originalFlyRules" : { "type" : "boolean" }
}
},
"spells": {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"dimensionDoorOnlyToUncoveredTiles" : { "type" : "boolean" },
"dimensionDoorExposesTerrainType" : { "type" : "boolean" },
"dimensionDoorFailureSpendsPoints" : { "type" : "boolean" },
"dimensionDoorTriggersGuards" : { "type" : "boolean" },
"dimensionDoorTournamentRulesLimit" : { "type" : "boolean" }
}
},
"bonuses": {
"type" : "object",
"additionalProperties" : false,
"properties" : {
"global" : { "type" : "object" },
"perHero" : { "type" : "object" }
}
},
}
}

View File

@@ -124,12 +124,7 @@
"settings" : {
"type" : "object",
"description" : "List of changed game settings by mod",
"additionalProperties" : {
"type" : "object",
"properties" : {
"type" : "object"
}
}
"$ref" : "gameSettings.json"
},
"filesystem" : {
"type" : "object",

View File

@@ -134,9 +134,7 @@
"settings" : {
"description" : "List of changed game settings by template",
"type" : "object",
"additionalProperties" : {
"type" : "object"
}
"$ref" : "gameSettings.json"
},
"name" : {
"description" : "Optional name - useful to have several template variations with same name",

View File

@@ -52,12 +52,15 @@ Please see corresponding installation guide articles for details for your platfo
- [Cheat codes](players/Cheat_Codes.md)
- [Privacy Policy](players/Privacy_Policy.md)
## Documentation and guidelines for translators
- [Translations](translators/Translations.md)
## Documentation and guidelines for game modders
- [Modding Guidelines](modders/Readme.md)
- [Mod File Format](modders/Mod_File_Format.md)
- [Bonus Format](modders/Bonus_Format.md)
- [Translations](modders/Translations.md)
- [Map Editor](modders/Map_Editor.md)
- [Campaign Format](modders/Campaign_Format.md)
- [Configurable Widgets](modders/Configurable_Widgets.md)

View File

@@ -33,6 +33,7 @@ The page will be automatically updated once a week.
VCMI allows translating game data into languages other than English. In order to translate Heroes III in your language easiest approach is to:
- Copy existing translation, such as English translation from here: https://github.com/vcmi-mods/h3-for-vcmi-englisation (delete sound and video folders)
- Copy text-free images from here: https://github.com/vcmi-mods/empty-translation
- Rename mod to indicate your language, preferred form is "(language)-translation"
- Update mod.json to match your mod
- Translate all texts strings from `game.json`, `campaigns.json` and `maps.json`

View File

@@ -323,7 +323,7 @@ void FirstLaunchView::extractGogData()
QFile tmpFile(file);
if(!tmpFile.open(QIODevice::ReadOnly))
{
QMessageBox::critical(this, tr("File cannot opened"), tmpFile.errorString());
QMessageBox::critical(this, tr("File cannot be opened"), tmpFile.errorString());
return QString{};
}
QByteArray magicFile = tmpFile.read(magic.length());

View File

@@ -1248,7 +1248,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
<source>File cannot opened</source>
<source>File cannot be opened</source>
<translation></translation>
</message>
<message>

View File

@@ -1241,7 +1241,7 @@ Offline instalátor obsahuje dvě části, .exe a .bin. Ujistěte se, že stahuj
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
<source>File cannot opened</source>
<source>File cannot be opened</source>
<translation type="unfinished"></translation>
</message>
<message>

View File

@@ -1220,7 +1220,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
<source>File cannot opened</source>
<source>File cannot be opened</source>
<translation type="unfinished"></translation>
</message>
<message>

View File

@@ -1246,7 +1246,7 @@ Heroes® of Might and Magic® III HD n&quot;est actuellement pas pris en charge
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
<source>File cannot opened</source>
<source>File cannot be opened</source>
<translation>Le fichier ne peut pas être ouvert</translation>
</message>
<message>

View File

@@ -1241,7 +1241,7 @@ Der Offline-Installer besteht aus zwei Teilen, .exe und .bin. Stellen Sie sicher
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
<source>File cannot opened</source>
<source>File cannot be opened</source>
<translation>Datei kann nicht geöffnet werden</translation>
</message>
<message>

View File

@@ -1241,7 +1241,7 @@ Instalator offline składa się z dwóch części, .exe i .bin. Upewnij się, ż
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
<source>File cannot opened</source>
<source>File cannot be opened</source>
<translation>Nieudane otwarcie pliku</translation>
</message>
<message>

View File

@@ -11,7 +11,7 @@
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="29"/>
<source>Have a question? Found a bug? Want to help? Join us!</source>
<translation>Tem uma pergunta? Encontrou algum erro? Quer ajudar? Junte-se a nós</translation>
<translation>Tem uma pergunta? Encontrou algum erro? Quer ajudar? Junte-se a nós!</translation>
</message>
<message>
<location filename="../aboutProject/aboutproject_moc.ui" line="36"/>
@@ -648,11 +648,6 @@ Instalar o download realizado com sucesso?</translation>
<source>Additional repository</source>
<translation>Repositório adicional</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="893"/>
<source>Downscaling Filter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="931"/>
<source>Adventure Map Allies</source>
@@ -676,7 +671,7 @@ Instalar o download realizado com sucesso?</translation>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="618"/>
<source>Automatic (Linear)</source>
<translation type="unfinished"></translation>
<translation>Automático (Linear)</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="798"/>
@@ -691,27 +686,27 @@ Instalar o download realizado com sucesso?</translation>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="1166"/>
<source>Automatic</source>
<translation type="unfinished"></translation>
<translation>Automático</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="1171"/>
<source>None</source>
<translation type="unfinished"></translation>
<translation>Nenhum</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="1176"/>
<source>xBRZ x2</source>
<translation type="unfinished"></translation>
<translation>xBRZ x2</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="1181"/>
<source>xBRZ x3</source>
<translation type="unfinished"></translation>
<translation>xBRZ x3</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="1186"/>
<source>xBRZ x4</source>
<translation type="unfinished"></translation>
<translation>xBRZ x4</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="138"/>
@@ -758,6 +753,11 @@ Instalar o download realizado com sucesso?</translation>
<source>Reset</source>
<translation>Redefinir</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="1194"/>
<source>Use scalable fonts</source>
<translation>Usar Fontes Escaláveis</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="854"/>
<source>Network</source>
@@ -838,6 +838,11 @@ Instalar o download realizado com sucesso?</translation>
<source>Autosave limit (0 = off)</source>
<translation>Limite de salvamento automático (0 = sem limite)</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="893"/>
<source>Downscaling Filter</source>
<translation>Filtro de Redução de Escala</translation>
</message>
<message>
<location filename="../settingsView/csettingsview_moc.ui" line="1030"/>
<source>Framerate Limit</source>
@@ -1241,7 +1246,7 @@ O instalador offline consiste em duas partes, .exe e .bin. Certifique-se de baix
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
<source>File cannot opened</source>
<source>File cannot be opened</source>
<translation>O arquivo não pode ser aberto</translation>
</message>
<message>

View File

@@ -1226,7 +1226,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
<source>File cannot opened</source>
<source>File cannot be opened</source>
<translation type="unfinished"></translation>
</message>
<message>

View File

@@ -1239,7 +1239,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
<source>File cannot opened</source>
<source>File cannot be opened</source>
<translation type="unfinished"></translation>
</message>
<message>

File diff suppressed because it is too large Load Diff

View File

@@ -1241,7 +1241,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
<source>File cannot opened</source>
<source>File cannot be opened</source>
<translation>Не вдається відкрити файл</translation>
</message>
<message>

View File

@@ -1232,7 +1232,7 @@ Offline installer consists of two parts, .exe and .bin. Make sure you download b
</message>
<message>
<location filename="../firstLaunch/firstlaunch_moc.cpp" line="330"/>
<source>File cannot opened</source>
<source>File cannot be opened</source>
<translation type="unfinished"></translation>
</message>
<message>

View File

@@ -104,6 +104,8 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
void GameSettings::loadBase(const JsonNode & input)
{
JsonUtils::validate(input, "vcmi:gameSettings", input.getModScope());
for(const auto & option : settingProperties)
{
const JsonNode & optionValue = input[option.group][option.key];

View File

@@ -139,10 +139,14 @@ void CGameStateCampaign::trimCrossoverHeroesParameters(const CampaignTravel & tr
bool locked = hero.hero->getSlot(al.slot)->locked;
if (!locked && takeable)
{
logGlobal->debug("Artifact %s from slot %d of hero %s will be transferred to next scenario", art->artType->getJsonKey(), al.slot.getNum(), hero.hero->getHeroTypeName());
hero.transferrableArtifacts.push_back(artifactPosition);
}
if (!locked && !takeable)
{
logGlobal->debug("Removing artifact %s from slot %d of hero %s", art->artType->getJsonKey(), al.slot.getNum(), hero.hero->getHeroTypeName());
hero.hero->getArt(al.slot)->removeFrom(*hero.hero, al.slot);
return true;
}
@@ -413,16 +417,18 @@ void CGameStateCampaign::transferMissingArtifacts(const CampaignTravel & travelO
if (!donorHero)
throw std::runtime_error("Failed to find hero to take artifacts from! Scenario: " + gameState->map->name.toString());
for (auto const & artLocation : campaignHeroReplacement.transferrableArtifacts)
// process in reverse - 2nd artifact from a backpack must be processed before 1st one to avoid invalidation of artifact positions
for (auto const & artLocation : boost::adaptors::reverse(campaignHeroReplacement.transferrableArtifacts))
{
auto * artifact = donorHero->getArt(artLocation);
if (!donorHero)
throw std::runtime_error("Failed to find artifacts to transfer to travelling hero! Scenario: " + gameState->map->name.toString());
logGlobal->debug("Removing artifact %s from slot %d of hero %s for transfer", artifact->artType->getJsonKey(), artLocation.getNum(), donorHero->getHeroTypeName());
artifact->removeFrom(*donorHero, artLocation);
if (receiver)
{
logGlobal->debug("Granting artifact %s to hero %s for transfer", artifact->artType->getJsonKey(), receiver->getHeroTypeName());
const auto slot = ArtifactUtils::getArtAnyPosition(receiver, artifact->getTypeId());
if(ArtifactUtils::isSlotEquipment(slot) || ArtifactUtils::isSlotBackpack(slot))
artifact->putAt(*receiver, slot);

View File

@@ -203,6 +203,11 @@ TObjectTypeHandler CObjectClassesHandler::loadSubObjectFromJson(const std::strin
assert(handlerConstructors.count(handler) != 0);
}
// Compatibility with 1.5 mods for 1.6. To be removed in 1.7
// Detect banks that use old format and load them using old bank hander
if (baseObject->id == Obj::CREATURE_BANK && entry.Struct().count("levels") && !entry.Struct().count("rewards"))
handler = "bank";
auto createdObject = handlerConstructors.at(handler)();
createdObject->modScope = scope;

View File

@@ -567,6 +567,25 @@ std::string CGHeroInstance::getObjectName() const
return VLC->objtypeh->getObjectName(ID, 0);
}
std::string CGHeroInstance::getHoverText(PlayerColor player) const
{
std::string hoverText = CArmedInstance::getHoverText(player) + getMovementPointsTextIfOwner(player);
return hoverText;
}
std::string CGHeroInstance::getMovementPointsTextIfOwner(PlayerColor player) const
{
std::string output = "";
if(player == getOwner())
{
output += " " + VLC->generaltexth->translate("vcmi.adventureMap.movementPointsHeroInfo");
boost::replace_first(output, "%POINTS", std::to_string(movementPointsLimit(!boat)));
boost::replace_first(output, "%REMAINING", std::to_string(movementPointsRemaining()));
}
return output;
}
ui8 CGHeroInstance::maxlevelsToMagicSchool() const
{
return type->heroClass->isMagicHero() ? 3 : 4;

View File

@@ -303,6 +303,8 @@ public:
void pickRandomObject(vstd::RNG & rand) override;
void onHeroVisit(const CGHeroInstance * h) const override;
std::string getObjectName() const override;
std::string getHoverText(PlayerColor player) const override;
std::string getMovementPointsTextIfOwner(PlayerColor player) const;
void afterAddToMap(CMap * map) override;
void afterRemoveFromMap(CMap * map) override;

View File

@@ -105,6 +105,7 @@ void Rewardable::Info::init(const JsonNode & objectConfig, const std::string & o
loadString(parameters["visitedTooltip"], TextIdentifier(objectName, "visitedTooltip"));
loadString(parameters["onVisitedMessage"], TextIdentifier(objectName, "onVisited"));
loadString(parameters["onEmptyMessage"], TextIdentifier(objectName, "onEmpty"));
loadString(parameters["onGuardedMessage"], TextIdentifier(objectName, "onGuarded"));
}
Rewardable::LimitersList Rewardable::Info::configureSublimiters(Rewardable::Configuration & object, vstd::RNG & rng, IGameCallback * cb, const JsonNode & source) const

View File

@@ -244,6 +244,7 @@ void Rewardable::Limiter::serializeJson(JsonSerializeFormat & handler)
handler.serializeIdArray("colors", players);
handler.serializeInt("manaPoints", manaPoints);
handler.serializeIdArray("artifacts", artifacts);
handler.serializeIdArray("spells", spells);
handler.enterArray("creatures").serializeStruct(creatures);
handler.enterArray("primary").serializeArray(primary);
{

View File

@@ -48,48 +48,48 @@ QuestWidget::QuestWidget(MapController & _controller, CQuest & _sh, QWidget *par
}
//fill artifacts
for(int i = 0; i < controller.map()->allowedArtifact.size(); ++i)
for(const auto & artifactPtr : VLC->arth->objects)
{
auto * item = new QListWidgetItem(QString::fromStdString(VLC->artifacts()->getByIndex(i)->getNameTranslated()));
item->setData(Qt::UserRole, QVariant::fromValue(i));
auto artifactIndex = artifactPtr->getIndex();
auto * item = new QListWidgetItem(QString::fromStdString(artifactPtr->getNameTranslated()));
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Unchecked);
if(controller.map()->allowedArtifact.count(i) == 0)
if(controller.map()->allowedArtifact.count(artifactIndex) == 0)
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
ui->lArtifacts->addItem(item);
}
//fill spells
for(int i = 0; i < controller.map()->allowedSpells.size(); ++i)
for(const auto & spellPtr : VLC->spellh->objects)
{
auto * item = new QListWidgetItem(QString::fromStdString(VLC->spells()->getByIndex(i)->getNameTranslated()));
item->setData(Qt::UserRole, QVariant::fromValue(i));
auto spellIndex = spellPtr->getIndex();
auto * item = new QListWidgetItem(QString::fromStdString(spellPtr->getNameTranslated()));
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
item->setCheckState(Qt::Unchecked);
if(controller.map()->allowedSpells.count(i) == 0)
if(controller.map()->allowedSpells.count(spellIndex) == 0)
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
ui->lSpells->addItem(item);
}
//fill skills
ui->lSkills->setRowCount(controller.map()->allowedAbilities.size());
for(int i = 0; i < controller.map()->allowedAbilities.size(); ++i)
ui->lSkills->setRowCount(VLC->skillh->objects.size());
for(const auto & skillPtr : VLC->skillh->objects)
{
auto * item = new QTableWidgetItem(QString::fromStdString(VLC->skills()->getByIndex(i)->getNameTranslated()));
item->setData(Qt::UserRole, QVariant::fromValue(i));
auto skillIndex = skillPtr->getIndex();
auto * item = new QTableWidgetItem(QString::fromStdString(skillPtr->getNameTranslated()));
auto * widget = new QComboBox;
for(auto & s : NSecondarySkill::levels)
for(const auto & s : NSecondarySkill::levels)
widget->addItem(QString::fromStdString(s));
if(controller.map()->allowedAbilities.count(i) == 0)
if(controller.map()->allowedAbilities.count(skillIndex) == 0)
{
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
widget->setEnabled(false);
}
ui->lSkills->setItem(i, 0, item);
ui->lSkills->setCellWidget(i, 1, widget);
ui->lSkills->setItem(skillIndex, 0, item);
ui->lSkills->setCellWidget(skillIndex, 1, widget);
}
//fill creatures
@@ -156,7 +156,7 @@ void QuestWidget::obtainData()
for(auto i : quest.mission.artifacts)
ui->lArtifacts->item(VLC->artifacts()->getById(i)->getIndex())->setCheckState(Qt::Checked);
for(auto i : quest.mission.spells)
ui->lArtifacts->item(VLC->spells()->getById(i)->getIndex())->setCheckState(Qt::Checked);
ui->lSpells->item(VLC->spells()->getById(i)->getIndex())->setCheckState(Qt::Checked);
for(auto & i : quest.mission.secondary)
{
int index = VLC->skills()->getById(i.first)->getIndex();

View File

@@ -68,6 +68,8 @@ TownBuildingVisitQuery::TownBuildingVisitQuery(CGameHandler * owner, const CGTow
void TownBuildingVisitQuery::onExposure(QueryPtr topQuery)
{
topQuery->notifyObjectAboutRemoval(visitedObject, visitingHero);
onAdded(players.front());
}