diff --git a/.github/workflows/github.yml b/.github/workflows/github.yml index 1d82b23b0..d230fcafb 100644 --- a/.github/workflows/github.yml +++ b/.github/workflows/github.yml @@ -384,3 +384,34 @@ jobs: name: ${{ env.VCMI_PACKAGE_FILE_NAME }} path: | ${{ env.ANDROID_APK_PATH }} + + + deploy-src: + if: always() && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@v4 + with: + submodules: recursive + + - name: Build Number + run: | + source '${{github.workspace}}/CI/get_package_name.sh' + echo VCMI_PACKAGE_FILE_NAME="$VCMI_PACKAGE_FILE_NAME" >> $GITHUB_ENV + + - name: Create source code archive (including submodules) + run: | + git archive HEAD -o "release.tar" --worktree-attributes -v + git submodule update --init --recursive + git submodule --quiet foreach 'cd "$toplevel"; tar -rvf "release.tar" "$sm_path"' + gzip release.tar + + - name: Upload source code archive + uses: actions/upload-artifact@v4 + with: + name: ${{ env.VCMI_PACKAGE_FILE_NAME }} + path: | + ./release.tar.gz diff --git a/ChangeLog.md b/ChangeLog.md index 5dd3b17e1..449bcacdd 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -2,12 +2,61 @@ ### Stability * Fixed possible crash on accessing faction description -* Fixed possible thread race on leaving to main menu +* Fixed possible thread race on exit to main menu * Game will now show error message instead of silent crash on corrupted H3 data * Fixed possible crash on double-deletion of quest artifacts placed by RMG +* Fixed crash on loading save made in version 1.4 with removed from map Quest Guards +* Added workaround for crash on accessing Altar of Sacrifice on saves made in 1.4 +* Fixed possible crash on map restart request +* Fixed crash on attempt to open scenario list with no save or map selected +* Fixed crash on host resolving error when connecting to online lobby +* If json file specified in mod.json is missing, vcmi will now only log an error instead of crashing ### Interface -* Fixed possible freeze on attempt to move hero when hero has non-zero movement points but not enough to reach first tile in path +* Added retaliation damage and kills preview when hovering over units that can be attacked in melee during combat +* Clicking on combat log would now open a window with full combat log history +* Removed message length limit in text input fields, such as global lobby chat +* Tapping on already active text input field will display on-screen keyboard on systems with one +* Fixed possible freeze when trying to move hero if hero has non-zero movement points but not enough to reach first tile in path +* Fixed selection of the wrong reward in dialogs such as the level-up window when double-clicking on it +* Fixed launch of wrong map or save when double-clicking in scenario list screen +* Right-clicking on a hero in a tavern will now select that hero as well, in line with H3 +* Fixed slow map list parsing when hota map format is enabled +* MacOS and iOS can now use either Ctrl or Cmd key for all keyboard shortcuts +* Small windows no longer dim the entire screen by default + +### Mechanics +* Recruiting a hero will now immediately reveal the fog of war around him +* When both a visiting hero and a garrisoned hero are in town, the garrisoned hero will visit town buildings first. + +### Multiplayer +* Fixed in-game chat text not being visible after switching from achannel with a long history +* Fixed lag when switching to channel with long history +* Game now automatically scrolls in-game chat on new messages +* Game will now only plays chat sound for active channel and for private channels +* Cheats are now disabled by default in multiplayer +* Game will now show status of cheats and battle replays on map start +* It is possible to change cheats or battle replay on game loading +* It is now possible to join rooms hosted by different hotfix versions, e.g. 1.5.1 can join 1.5.0 games +* Fixed game rooms remaining visible in the lobby even after they have been closed +* Fixed possible lag when there is a player in lobby with a very slow (or dying) connection +* Game will show correctly if player has been invited into a room +* Fixed overflow in invite window when there are more than 8 players in the lobby + +### Random Maps Generator +* Generator will now prefer to place roads away from zone borders + +### AI +* Fixed possible crash when Nullkiller AI tries to upgrade army +* Nullkiller AI will now recruit new heroes if he left with 0 heroes +* AI in combat now knows when an enemy unit has used all of its retaliations. + +### Map Editor +* Fixed setting up hero types of heroes in Prisons placed in map editor +* Fixed crash on setting up Seer Hut in map editor +* Added text auto-completion hints for army widget +* Editor will now automatically add .vmap extensions when saving map +* Fixed text size in map validation window # 1.4.5 -> 1.5.0 diff --git a/Mods/vcmi/config/vcmi/spanish.json b/Mods/vcmi/config/vcmi/spanish.json index cf611e2f5..c9d6b9a31 100644 --- a/Mods/vcmi/config/vcmi/spanish.json +++ b/Mods/vcmi/config/vcmi/spanish.json @@ -180,16 +180,19 @@ "vcmi.adventureMap.revisitObject.help" : "{Revisitar objeto}\n\nSi un héroe se encuentra actualmente en un objeto del mapa, puede volver a visitar la ubicación.", "vcmi.battleWindow.pressKeyToSkipIntro" : "Presiona cualquier tecla para empezar la batalla inmediatamente", - "vcmi.battleWindow.damageEstimation.melee" : "Atacar %CREATURE (%DAÑO).", - "vcmi.battleWindow.damageEstimation.meleeKills" : "Atacar %CREATURE (%DAÑO, %BAJAS).", - "vcmi.battleWindow.damageEstimation.ranged" : "Disparar a %CREATURE (%DISPAROS, %DAÑO).", - "vcmi.battleWindow.damageEstimation.rangedKills" : "Disparar a %CREATURE (%DISPAROS, %DAÑO, %BAJAS).", + "vcmi.battleWindow.damageEstimation.melee" : "Atacar %CREATURE (%DAMAGE).", + "vcmi.battleWindow.damageEstimation.meleeKills" : "Atacar %CREATURE (%DAMAGE, %KILLS).", + "vcmi.battleWindow.damageEstimation.ranged" : "Disparar a %CREATURE (%SHOTS, %DAMAGE).", + "vcmi.battleWindow.damageEstimation.rangedKills" : "Disparar a %CREATURE (%SHOTS, %DAMAGE, %KILLS).", "vcmi.battleWindow.damageEstimation.shots" : "%d disparos restantes", "vcmi.battleWindow.damageEstimation.shots.1" : "%d disparo restante", "vcmi.battleWindow.damageEstimation.damage" : "%d daño", "vcmi.battleWindow.damageEstimation.damage.1" : "%d daño", "vcmi.battleWindow.damageEstimation.kills" : "%d perecerán", "vcmi.battleWindow.damageEstimation.kills.1" : "%d perecerá", + "vcmi.battleWindow.damageRetaliation.will" : "Contratacará ", + "vcmi.battleWindow.damageRetaliation.may" : "Puede contratacar ", + "vcmi.battleWindow.damageRetaliation.never" : "No contratacará.", "vcmi.battleWindow.killed" : "Eliminados", "vcmi.battleWindow.accurateShot.resultDescription.0" : "%d %s han sido eliminados por disparos certeros", "vcmi.battleWindow.accurateShot.resultDescription.1" : "%d %s ha sido eliminado por un disparo certero", @@ -352,16 +355,16 @@ "core.bonus.ADDITIONAL_ATTACK.name": "Doble Ataque", "core.bonus.ADDITIONAL_ATTACK.description": "Ataca dos veces", - "core.bonus.ADDITIONAL_RETALIATION.name": "Contraataques adicionales", - "core.bonus.ADDITIONAL_RETALIATION.description": "Puede contraatacar ${val} veces adicionales", + "core.bonus.ADDITIONAL_RETALIATION.name": "Contrataques adicionales", + "core.bonus.ADDITIONAL_RETALIATION.description": "Puede contratacar ${val} veces adicionales", "core.bonus.AIR_IMMUNITY.name": "Inmunidad al Aire", "core.bonus.AIR_IMMUNITY.description": "Inmune a todos los hechizos de la escuela de Aire", "core.bonus.ATTACKS_ALL_ADJACENT.name": "Ataque en todas las direcciones", "core.bonus.ATTACKS_ALL_ADJACENT.description": "Ataca a todos los enemigos adyacentes", - "core.bonus.BLOCKS_RETALIATION.name": "Sin contraataque", - "core.bonus.BLOCKS_RETALIATION.description": "El enemigo no puede contraatacar", - "core.bonus.BLOCKS_RANGED_RETALIATION.name": "Sin contraataque a distancia", - "core.bonus.BLOCKS_RANGED_RETALIATION.description": "El enemigo no puede contraatacar disparando", + "core.bonus.BLOCKS_RETALIATION.name": "Evita contrataque", + "core.bonus.BLOCKS_RETALIATION.description": "El enemigo no puede contratacar", + "core.bonus.BLOCKS_RANGED_RETALIATION.name": "Evita contrataque a distancia", + "core.bonus.BLOCKS_RANGED_RETALIATION.description": "El enemigo no puede contratacar disparando", "core.bonus.CATAPULT.name": "Catapulta", "core.bonus.CATAPULT.description": "Ataca a las paredes de asedio", "core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reducir coste del conjuro (${val})", @@ -397,7 +400,7 @@ "core.bonus.FIRE_SHIELD.name": "Escudo de Fuego (${val}%)", "core.bonus.FIRE_SHIELD.description": "Refleja una parte del daño cuerpo a cuerpo", "core.bonus.FIRST_STRIKE.name": "Primer Ataque", - "core.bonus.FIRST_STRIKE.description": "Esta criatura ataca primero en lugar de contraatacar", + "core.bonus.FIRST_STRIKE.description": "Esta criatura ataca primero en lugar de contratacar", "core.bonus.FEAR.name": "Miedo", "core.bonus.FEAR.description": "Causa miedo a un grupo enemigo", "core.bonus.FEARLESS.name": "Inmune al miedo", @@ -450,8 +453,8 @@ "core.bonus.NON_LIVING.description": "Inmunidad a muchos efectos", "core.bonus.RANDOM_SPELLCASTER.name": "Lanzador de hechizos aleatorio", "core.bonus.RANDOM_SPELLCASTER.description": "Puede lanzar hechizos aleatorios", - "core.bonus.RANGED_RETALIATION.name": "Contraataque a distancia", - "core.bonus.RANGED_RETALIATION.description": "Puede realizar un contraataque a distancia", + "core.bonus.RANGED_RETALIATION.name": "Contrataque a distancia", + "core.bonus.RANGED_RETALIATION.description": "Puede realizar un contrataque a distancia", "core.bonus.RECEPTIVE.name": "Receptivo", "core.bonus.RECEPTIVE.description": "No tiene inmunidad a hechizos amistosos", "core.bonus.REBIRTH.name": "Renacimiento (${val}%)", @@ -492,8 +495,8 @@ "core.bonus.TRANSMUTATION.description": "${val}% de probabilidad de transformar la unidad atacada en otro tipo", "core.bonus.UNDEAD.name": "No muerto", "core.bonus.UNDEAD.description": "La criatura es un no muerto", - "core.bonus.UNLIMITED_RETALIATIONS.name": "Contraataques ilimitados", - "core.bonus.UNLIMITED_RETALIATIONS.description": "Puede realizar un número ilimitado de contraataques", + "core.bonus.UNLIMITED_RETALIATIONS.name": "Contrataques ilimitados", + "core.bonus.UNLIMITED_RETALIATIONS.description": "Puede realizar un número ilimitado de contrataques", "core.bonus.WATER_IMMUNITY.name": "Inmunidad al agua", "core.bonus.WATER_IMMUNITY.description": "Inmune a todos los hechizos de la escuela del agua", "core.bonus.WIDE_BREATH.name": "Aliento amplio", diff --git a/android/vcmi-app/build.gradle b/android/vcmi-app/build.gradle index c8bb991f0..ee155cd8f 100644 --- a/android/vcmi-app/build.gradle +++ b/android/vcmi-app/build.gradle @@ -10,7 +10,7 @@ android { applicationId "is.xyz.vcmi" minSdk 19 targetSdk 33 - versionCode 1511 + versionCode 1513 versionName "1.5.1" setProperty("archivesBaseName", "vcmi") } diff --git a/docs/Readme.md b/docs/Readme.md index 70819ccae..b6c2d2d63 100644 --- a/docs/Readme.md +++ b/docs/Readme.md @@ -1,5 +1,6 @@ [![VCMI](https://github.com/vcmi/vcmi/actions/workflows/github.yml/badge.svg?branch=develop&event=push)](https://github.com/vcmi/vcmi/actions/workflows/github.yml?query=branch%3Adevelop+event%3Apush) [![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.5.0/total)](https://github.com/vcmi/vcmi/releases/tag/1.5.0) +[![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/1.5.1/total)](https://github.com/vcmi/vcmi/releases/tag/1.5.1) [![Github Downloads](https://img.shields.io/github/downloads/vcmi/vcmi/total)](https://github.com/vcmi/vcmi/releases) # VCMI Project diff --git a/lib/gameState/CGameStateCampaign.cpp b/lib/gameState/CGameStateCampaign.cpp index 46417dbe7..e044d345b 100644 --- a/lib/gameState/CGameStateCampaign.cpp +++ b/lib/gameState/CGameStateCampaign.cpp @@ -392,9 +392,15 @@ void CGameStateCampaign::transferMissingArtifacts(const CampaignTravel & travelO auto * donorHero = campaignHeroReplacement.hero; + 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) { 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()); + artifact->removeFrom(*donorHero, artLocation); if (receiver) diff --git a/server/GlobalLobbyProcessor.cpp b/server/GlobalLobbyProcessor.cpp index 68127e5e4..7f9a49820 100644 --- a/server/GlobalLobbyProcessor.cpp +++ b/server/GlobalLobbyProcessor.cpp @@ -40,26 +40,26 @@ void GlobalLobbyProcessor::onDisconnected(const std::shared_ptr