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

Merge beta -> develop

This commit is contained in:
Ivan Savenko 2023-04-26 22:45:41 +03:00
commit fb739e7186
34 changed files with 307 additions and 72 deletions

View File

@ -130,7 +130,7 @@ jobs:
preset: android-conan-ninja-release
conan_profile: android-64
conan_options: --conf tools.android:ndk_path=$ANDROID_NDK_ROOT
artifact_platform: aarch64-v8a
artifact_platform: arm64-v8a
runs-on: ${{ matrix.os }}
defaults:
run:
@ -225,6 +225,7 @@ jobs:
name: ${{ env.VCMI_PACKAGE_FILE_NAME }} - ${{ matrix.platform }}
path: |
${{github.workspace}}/out/build/${{matrix.preset}}/${{ env.VCMI_PACKAGE_FILE_NAME }}.${{ matrix.extension }}
- name: Android artifacts
if: ${{ startsWith(matrix.platform, 'android') }}
uses: actions/upload-artifact@v3
@ -233,6 +234,14 @@ jobs:
path: |
${{ env.ANDROID_APK_PATH }}
- name: Android JNI ${{matrix.platform}}
if: ${{ startsWith(matrix.platform, 'android') && github.ref == 'refs/heads/master' }}
uses: actions/upload-artifact@v3
with:
name: Android JNI ${{matrix.platform}}
path: |
${{ github.workspace }}/android/vcmi-app/src/main/jniLibs
- name: Upload build
if: ${{ (matrix.pack == 1 || startsWith(matrix.platform, 'android')) && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/features/')) && matrix.platform != 'msvc' }}
continue-on-error: true
@ -254,3 +263,106 @@ jobs:
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
if: always()
# copy-pasted mostly
bundle_release:
needs: build
if: always() && github.ref == 'refs/heads/master'
strategy:
matrix:
include:
- platform: android-32
os: ubuntu-22.04
extension: aab
preset: android-conan-ninja-release
conan_profile: android-32
conan_options: --conf tools.android:ndk_path=$ANDROID_NDK_ROOT
artifact_platform: aab
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Dependencies
run: source '${{github.workspace}}/CI/${{matrix.platform}}/before_install.sh'
env:
VCMI_BUILD_PLATFORM: x64
- uses: actions/setup-python@v4
if: "${{ matrix.conan_profile != '' }}"
with:
python-version: '3.10'
- name: Conan setup
if: "${{ matrix.conan_profile != '' }}"
run: |
pip3 install 'conan<2.0'
conan profile new default --detect
conan install . \
--install-folder=conan-generated \
--no-imports \
--build=never \
--profile:build=default \
--profile:host=CI/conan/${{ matrix.conan_profile }} \
${{ matrix.conan_options }}
env:
GENERATE_ONLY_BUILT_CONFIG: 1
- name: Git branch name
id: git-branch-name
uses: EthanSK/git-branch-name-action@v1
- name: Build Number
run: |
source '${{github.workspace}}/CI/get_package_name.sh'
if [ '${{ matrix.artifact_platform }}' ]; then
VCMI_PACKAGE_FILE_NAME+="-${{ matrix.artifact_platform }}"
fi
echo VCMI_PACKAGE_FILE_NAME="$VCMI_PACKAGE_FILE_NAME" >> $GITHUB_ENV
echo VCMI_PACKAGE_NAME_SUFFIX="$VCMI_PACKAGE_NAME_SUFFIX" >> $GITHUB_ENV
echo VCMI_PACKAGE_GITVERSION="$VCMI_PACKAGE_GITVERSION" >> $GITHUB_ENV
env:
PULL_REQUEST: ${{ github.event.pull_request.number }}
- name: CMake Preset
run: |
cmake --preset ${{ matrix.preset }}
- name: Build Preset
run: |
cmake --build --preset ${{matrix.preset}}
- name: Download libs x64
uses: actions/download-artifact@v3
with:
name: Android JNI android-64
path: ${{ github.workspace }}/android/vcmi-app/src/main/jniLibs/
- name: Create android package
run: |
cd android
./gradlew bundleRelease --info
echo ANDROID_APK_PATH="$(ls ${{ github.workspace }}/android/vcmi-app/build/outputs/bundle/release/*.aab)" >> $GITHUB_ENV
env:
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
- name: Android artifacts
uses: actions/upload-artifact@v3
with:
name: ${{ env.VCMI_PACKAGE_FILE_NAME }}
path: |
${{ env.ANDROID_APK_PATH }}
- uses: act10ns/slack@v1
with:
status: ${{ job.status }}
channel: '#notifications'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
if: always()

Binary file not shown.

View File

@ -0,0 +1,2 @@
STORE_FILE=android-release.jks
KEY_ALIAS=vcmi

View File

@ -251,6 +251,7 @@ if(MINGW OR MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
add_definitions(-D_SCL_SECURE_NO_WARNINGS)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4250") # 4250: 'class1' : inherits 'class2::member' via dominance
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4251") # 4251: class 'xxx' needs to have dll-interface to be used by clients of class 'yyy'

View File

@ -1,6 +1,26 @@
# 1.2.0 -> 1.3.0
# 1.2.1 -> 1.3.0
(unreleased)
# 1.2.0 -> 1.2.1
### GENERAL:
* Implemented spell range overlay for Dimension Door and Scuttle Boat
* Fixed movement cost penalty from terrain
* Fixed empty Black Market on game start
* Fixed bad morale happening after waiting
* Fixed good morale happening after defeating last enemy unit
* Fixed death animation of Efreeti killed by petrification attack
* Fixed crash on leaving to main menu from battle in hotseat mode
* Adventure map spells are no longer visible on units in battle
* Attempt to cast spell with no valid targets in hotseat will show appropriate error message
* RMG settings will now show all existing in game templates and not just those suitable for current settings
* RMG settings (map size and two-level maps) that are not compatible with current template will be blocked
* Fixed centering of scenario information window
* Fixed crash on empty save game list after filtering
* Fixed blocked progress in Launcher on language detection failure
* Launcher will now correctly handle selection of Ddata directory in H3 install
* Map editor will now correctly save message property for events and pandoras
# 1.1.1 -> 1.2.0
### GENERAL:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

View File

@ -10,14 +10,16 @@ android {
applicationId "is.xyz.vcmi"
minSdk 19
targetSdk 31
versionCode 1103
versionName "1.1"
versionCode 1200
versionName "1.2"
setProperty("archivesBaseName", "vcmi")
}
signingConfigs {
releaseSigning
LoadSigningConfig()
dailySigning
LoadSigningConfig("releaseSigning")
LoadSigningConfig("dailySigning")
}
buildTypes {
@ -46,6 +48,7 @@ android {
daily {
initWith release
applicationIdSuffix '.daily'
signingConfig signingConfigs.dailySigning
manifestPlaceholders = [
applicationLabel: 'VCMI daily',
]
@ -118,38 +121,48 @@ def ResolveGitInfo() {
CommandOutput("git", ["describe", "--match=", "--always", "--abbrev=7"], ".")
}
def SigningPropertiesPath(final basePath) {
return file("${basePath}/signing.properties")
def SigningPropertiesPath(final basePath, final signingConfigKey) {
return file("${basePath}/${signingConfigKey}.properties")
}
def SigningKeystorePath(final basePath, final keystoreFileName) {
return file("${basePath}/${keystoreFileName}")
}
def LoadSigningConfig() {
def LoadSigningConfig(final signingConfigKey) {
final def projectRoot = "${project.projectDir}/../../CI/android"
final def props = new Properties()
final def propFile = SigningPropertiesPath(projectRoot)
final def propFile = SigningPropertiesPath(projectRoot, signingConfigKey)
def signingConfig = android.signingConfigs.getAt(signingConfigKey)
if (propFile.canRead()) {
props.load(new FileInputStream(propFile))
if (props != null
&& props.containsKey('STORE_FILE')
&& props.containsKey('STORE_PASSWORD')
&& props.containsKey('KEY_ALIAS')
&& props.containsKey('KEY_PASSWORD')) {
&& props.containsKey('KEY_ALIAS')) {
android.signingConfigs.releaseSigning.storeFile = SigningKeystorePath(projectRoot, props['STORE_FILE'])
android.signingConfigs.releaseSigning.storePassword = props['STORE_PASSWORD']
android.signingConfigs.releaseSigning.keyAlias = props['KEY_ALIAS']
android.signingConfigs.releaseSigning.keyPassword = props['KEY_PASSWORD']
signingConfig.storeFile = SigningKeystorePath(projectRoot, props['STORE_FILE'])
signingConfig.storePassword = props['STORE_PASSWORD']
signingConfig.keyAlias = props['KEY_ALIAS']
if(props.containsKey('STORE_PASSWORD'))
signingConfig.storePassword = props['STORE_PASSWORD']
else
signingConfig.storePassword = System.getenv("ANDROID_STORE_PASSWORD")
if(props.containsKey('KEY_PASSWORD'))
signingConfig.keyPassword = props['KEY_PASSWORD']
else
signingConfig.keyPassword = System.getenv("ANDROID_KEY_PASSWORD")
} else {
println("Some props from signing file are missing")
android.buildTypes.release.signingConfig = null
android.signingConfigs.putAt(signingConfigKey, null)
}
} else {
println("file with signing properties is missing")
android.buildTypes.release.signingConfig = null
android.signingConfigs.putAt(signingConfigKey, null)
}
}

View File

@ -374,6 +374,7 @@ void CClient::endGame()
//threads cleanup has to be after gs cleanup and before battleints cleanup to stop tacticThread
cleanThreads();
CPlayerInterface::battleInt.reset();
playerint.clear();
battleints.clear();
battleCallbacks.clear();

View File

@ -992,8 +992,11 @@ void CAdventureMapInterface::onTileLeftClicked(const int3 &mapPos)
const CGObjectInstance *topBlocking = getActiveObject(mapPos);
int3 selPos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
if(spellBeingCasted && isInScreenRange(selPos, mapPos))
if(spellBeingCasted)
{
if (!isInScreenRange(selPos, mapPos))
return;
const TerrainTile *heroTile = LOCPLINT->cb->getTile(selPos);
switch(spellBeingCasted->id)
@ -1099,11 +1102,15 @@ void CAdventureMapInterface::onTileHovered(const int3 &mapPos)
switch(spellBeingCasted->id)
{
case SpellID::SCUTTLE_BOAT:
if(objAtTile && objAtTile->ID == Obj::BOAT)
{
int3 hpos = LOCPLINT->localState->getCurrentArmy()->getSightCenter();
if(objAtTile && objAtTile->ID == Obj::BOAT && isInScreenRange(hpos, mapPos))
CCS->curh->set(Cursor::Map::SCUTTLE_BOAT);
else
CCS->curh->set(Cursor::Map::POINTER);
return;
}
case SpellID::DIMENSION_DOOR:
{
const TerrainTile * t = LOCPLINT->cb->getTile(mapPos, false);
@ -1264,6 +1271,8 @@ void CAdventureMapInterface::enterCastingMode(const CSpell * sp)
{
assert(sp->id == SpellID::SCUTTLE_BOAT || sp->id == SpellID::DIMENSION_DOOR);
spellBeingCasted = sp;
Settings config = settings.write["session"]["showSpellRange"];
config->Bool() = true;
deactivate();
terrain->activate();
@ -1276,6 +1285,9 @@ void CAdventureMapInterface::exitCastingMode()
spellBeingCasted = nullptr;
terrain->deactivate();
activate();
Settings config = settings.write["session"]["showSpellRange"];
config->Bool() = false;
}
void CAdventureMapInterface::abortCastingMode()

View File

@ -137,7 +137,7 @@ bool StackActionAnimation::init()
StackActionAnimation::~StackActionAnimation()
{
if (stack->isFrozen())
if (stack->isFrozen() && currGroup != ECreatureAnimType::DEATH && currGroup != ECreatureAnimType::DEATH_RANGED)
myAnim->setType(ECreatureAnimType::HOLDING);
else
myAnim->setType(nextGroup);

View File

@ -206,7 +206,7 @@ void CLobbyScreen::updateAfterStateChange()
}
}
if(curTab == tabRand && CSH->si->mapGenOptions)
if(curTab && curTab == tabRand && CSH->si->mapGenOptions)
tabRand->setMapGenOptions(CSH->si->mapGenOptions);
}

View File

@ -31,8 +31,6 @@ CSavingScreen::CSavingScreen()
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
center(pos);
// TODO: we should really use std::shared_ptr for passing StartInfo around.
localSi = new StartInfo(*LOCPLINT->cb->getStartInfo());
localMi = std::make_shared<CMapInfo>();
localMi->mapHeader = std::unique_ptr<CMapHeader>(new CMapHeader(*LOCPLINT->cb->getMapHeader()));
@ -52,7 +50,9 @@ const CMapInfo * CSavingScreen::getMapInfo()
const StartInfo * CSavingScreen::getStartInfo()
{
return localSi;
if (localMi)
return localMi->scenarioOptionsOfSave;
return LOCPLINT->cb->getStartInfo();
}
void CSavingScreen::changeSelection(std::shared_ptr<CMapInfo> to)
@ -61,7 +61,6 @@ void CSavingScreen::changeSelection(std::shared_ptr<CMapInfo> to)
return;
localMi = to;
localSi = localMi->scenarioOptionsOfSave;
card->changeSelection();
}

View File

@ -23,7 +23,6 @@ class CSelectionBase;
class CSavingScreen : public CSelectionBase
{
public:
const StartInfo * localSi;
std::shared_ptr<CMapInfo> localMi;
CSavingScreen();

View File

@ -26,6 +26,10 @@
CScenarioInfoScreen::CScenarioInfoScreen()
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
pos.w = 800;
pos.h = 600;
pos = center();
localSi = new StartInfo(*LOCPLINT->cb->getStartInfo());
localMi = new CMapInfo();
localMi->mapHeader = std::unique_ptr<CMapHeader>(new CMapHeader(*LOCPLINT->cb->getMapHeader()));

View File

@ -242,9 +242,29 @@ void RandomMapTab::setMapGenOptions(std::shared_ptr<CMapGenOptions> opts)
}
if(auto w = widget<CToggleGroup>("groupMapSize"))
{
for(auto toggle : w->buttons)
{
if(auto button = std::dynamic_pointer_cast<CToggleButton>(toggle.second))
{
const auto & mapSizes = getPossibleMapSizes();
int3 size( mapSizes[toggle.first], mapSizes[toggle.first], 1 + mapGenOptions->getHasTwoLevels());
bool sizeAllowed = !mapGenOptions->getMapTemplate() || mapGenOptions->getMapTemplate()->matchesSize(size);
button->block(!sizeAllowed);
}
}
w->setSelected(vstd::find_pos(getPossibleMapSizes(), opts->getWidth()));
}
if(auto w = widget<CToggleButton>("buttonTwoLevels"))
{
int3 size( opts->getWidth(), opts->getWidth(), 2);
bool undergoundAllowed = !mapGenOptions->getMapTemplate() || mapGenOptions->getMapTemplate()->matchesSize(size);
w->setSelected(opts->getHasTwoLevels());
w->block(!undergoundAllowed);
}
if(auto w = widget<CToggleGroup>("groupMaxPlayers"))
{
w->setSelected(opts->getPlayerCount());
@ -408,7 +428,11 @@ TemplatesDropBox::TemplatesDropBox(RandomMapTab & randomMapTab, int3 size):
REGISTER_BUILDER("templateListItem", &TemplatesDropBox::buildListItem);
curItems = VLC->tplh->getTemplates();
vstd::erase_if(curItems, [size](const CRmgTemplate * t){return !t->matchesSize(size);});
boost::range::sort(curItems, [](const CRmgTemplate * a, const CRmgTemplate * b){
return a->getName() < b->getName();
});
curItems.insert(curItems.begin(), nullptr); //default template
const JsonNode config(ResourceID("config/widgets/randomMapTemplateWidget.json"));

View File

@ -86,4 +86,8 @@ public:
virtual bool showGrid() const = 0;
virtual bool showVisitable() const = 0;
virtual bool showBlocked() const = 0;
/// if true, spell range for teleport / scuttle boat will be visible
virtual bool showSpellRange(const int3 & position) const = 0;
};

View File

@ -565,15 +565,16 @@ uint8_t MapRendererObjects::checksum(IMapRendererContext & context, const int3 &
return 0xff-1;
}
MapRendererDebug::MapRendererDebug()
MapRendererOverlay::MapRendererOverlay()
: imageGrid(IImage::createFromFile("debug/grid", EImageBlitMode::ALPHA))
, imageBlocked(IImage::createFromFile("debug/blocked", EImageBlitMode::ALPHA))
, imageVisitable(IImage::createFromFile("debug/visitable", EImageBlitMode::ALPHA))
, imageSpellRange(IImage::createFromFile("debug/spellRange", EImageBlitMode::ALPHA))
{
}
void MapRendererDebug::renderTile(IMapRendererContext & context, Canvas & target, const int3 & coordinates)
void MapRendererOverlay::renderTile(IMapRendererContext & context, Canvas & target, const int3 & coordinates)
{
if(context.showGrid())
target.draw(imageGrid, Point(0,0));
@ -599,9 +600,12 @@ void MapRendererDebug::renderTile(IMapRendererContext & context, Canvas & target
if (context.showVisitable() && visitable)
target.draw(imageVisitable, Point(0,0));
}
if (context.showSpellRange(coordinates))
target.draw(imageSpellRange, Point(0,0));
}
uint8_t MapRendererDebug::checksum(IMapRendererContext & context, const int3 & coordinates)
uint8_t MapRendererOverlay::checksum(IMapRendererContext & context, const int3 & coordinates)
{
uint8_t result = 0;
@ -614,6 +618,9 @@ uint8_t MapRendererDebug::checksum(IMapRendererContext & context, const int3 & c
if (context.showGrid())
result += 4;
if (context.showSpellRange(coordinates))
result += 8;
return result;
}
@ -747,7 +754,7 @@ MapRenderer::TileChecksum MapRenderer::getTileChecksum(IMapRendererContext & con
result[3] = rendererRoad.checksum(context, coordinates);
result[4] = rendererObjects.checksum(context, coordinates);
result[5] = rendererPath.checksum(context, coordinates);
result[6] = rendererDebug.checksum(context, coordinates);
result[6] = rendererOverlay.checksum(context, coordinates);
if(!context.isVisible(coordinates))
result[7] = rendererFow.checksum(context, coordinates);
@ -781,7 +788,7 @@ void MapRenderer::renderTile(IMapRendererContext & context, Canvas & target, con
rendererObjects.renderTile(context, target, coordinates);
rendererPath.renderTile(context, target, coordinates);
rendererDebug.renderTile(context, target, coordinates);
rendererOverlay.renderTile(context, target, coordinates);
if(!context.isVisible(coordinates))
rendererFow.renderTile(context, target, coordinates);

View File

@ -129,21 +129,12 @@ public:
void renderTile(IMapRendererContext & context, Canvas & target, const int3 & coordinates);
};
class MapRendererDebug
class MapRendererOverlay
{
std::shared_ptr<IImage> imageGrid;
std::shared_ptr<IImage> imageVisitable;
std::shared_ptr<IImage> imageBlocked;
public:
MapRendererDebug();
uint8_t checksum(IMapRendererContext & context, const int3 & coordinates);
void renderTile(IMapRendererContext & context, Canvas & target, const int3 & coordinates);
};
class MapRendererOverlay
{
std::unique_ptr<CAnimation> iconsStorage;
std::shared_ptr<IImage> imageSpellRange;
public:
MapRendererOverlay();
@ -160,7 +151,7 @@ class MapRenderer
MapRendererFow rendererFow;
MapRendererObjects rendererObjects;
MapRendererPath rendererPath;
MapRendererDebug rendererDebug;
MapRendererOverlay rendererOverlay;
public:
using TileChecksum = std::array<uint8_t, 8>;

View File

@ -22,6 +22,7 @@
#include "../../lib/CPathfinder.h"
#include "../../lib/Point.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/spells/CSpellHandler.h"
#include "../../lib/mapping/CMap.h"
MapRendererBaseContext::MapRendererBaseContext(const MapRendererContextState & viewState)
@ -199,6 +200,11 @@ bool MapRendererBaseContext::showBlocked() const
return false;
}
bool MapRendererBaseContext::showSpellRange(const int3 & position) const
{
return false;
}
MapRendererAdventureContext::MapRendererAdventureContext(const MapRendererContextState & viewState)
: MapRendererBaseContext(viewState)
{
@ -266,6 +272,19 @@ bool MapRendererAdventureContext::showBlocked() const
return settingShowBlocked;
}
bool MapRendererAdventureContext::showSpellRange(const int3 & position) const
{
if (!settingSpellRange)
return false;
auto hero = LOCPLINT->localState->getCurrentHero();
if (!hero)
return false;
return !isInScreenRange(hero->getSightCenter(), position);
}
MapRendererAdventureTransitionContext::MapRendererAdventureTransitionContext(const MapRendererContextState & viewState)
: MapRendererAdventureContext(viewState)
{

View File

@ -58,6 +58,7 @@ public:
bool showGrid() const override;
bool showVisitable() const override;
bool showBlocked() const override;
bool showSpellRange(const int3 & position) const override;
};
class MapRendererAdventureContext : public MapRendererBaseContext
@ -67,6 +68,7 @@ public:
bool settingShowGrid = false;
bool settingShowVisitable = false;
bool settingShowBlocked = false;
bool settingSpellRange= false;
bool settingsAdventureObjectAnimation = true;
bool settingsAdventureTerrainAnimation = true;
@ -80,6 +82,8 @@ public:
bool showGrid() const override;
bool showVisitable() const override;
bool showBlocked() const override;
bool showSpellRange(const int3 & position) const override;
};
class MapRendererAdventureTransitionContext : public MapRendererAdventureContext

View File

@ -160,6 +160,7 @@ void MapView::onViewMapActivated()
PuzzleMapView::PuzzleMapView(const Point & offset, const Point & dimensions, const int3 & tileToCenter)
: BasicMapView(offset, dimensions)
{
controller->setViewCenter(tileToCenter);
controller->activatePuzzleMapContext(tileToCenter);
controller->setViewCenter(tileToCenter);
}

View File

@ -63,7 +63,7 @@ void MapViewController::setViewCenter(const Point & position, int level)
model->setViewCenter(betterPosition);
model->setLevel(std::clamp(level, 0, context->getMapSize().z));
if(adventureInt) // may be called before adventureInt is initialized
if(adventureInt && !puzzleMapContext) // may be called before adventureInt is initialized
adventureInt->onMapViewMoved(model->getTilesTotalRect(), model->getLevel());
}
@ -154,6 +154,7 @@ void MapViewController::updateBefore(uint32_t timeDelta)
adventureContext->settingShowGrid = settings["gameTweaks"]["showGrid"].Bool();
adventureContext->settingShowVisitable = settings["session"]["showVisitable"].Bool();
adventureContext->settingShowBlocked = settings["session"]["showBlocked"].Bool();
adventureContext->settingSpellRange = settings["session"]["showSpellRange"].Bool();
}
}

View File

@ -510,7 +510,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
auto spellCost = owner->myInt->cb->getSpellCost(mySpell, owner->myHero);
if(spellCost > owner->myHero->mana) //insufficient mana
{
owner->myInt->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[206]) % spellCost % owner->myHero->mana));
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[206]) % spellCost % owner->myHero->mana));
return;
}
@ -530,7 +530,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
if((combatSpell ^ inCombat) || inCastle)
{
std::vector<std::shared_ptr<CComponent>> hlp(1, std::make_shared<CComponent>(CComponent::spell, mySpell->id, 0));
owner->myInt->showInfoDialog(mySpell->getDescriptionTranslated(schoolLevel), hlp);
LOCPLINT->showInfoDialog(mySpell->getDescriptionTranslated(schoolLevel), hlp);
}
else if(combatSpell)
{
@ -545,9 +545,9 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
std::vector<std::string> texts;
problem.getAll(texts);
if(!texts.empty())
owner->myInt->showInfoDialog(texts.front());
LOCPLINT->showInfoDialog(texts.front());
else
owner->myInt->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.spellUnknownProblem"));
LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.spellUnknownProblem"));
}
}
else //adventure spell

6
debian/changelog vendored
View File

@ -4,6 +4,12 @@ vcmi (1.3.0) jammy; urgency=medium
-- Ivan Savenko <saven.ivan@gmail.com> Sat, 01 Jul 2023 16:00:00 +0200
vcmi (1.2.1) jammy; urgency=medium
* New upstream release
-- Ivan Savenko <saven.ivan@gmail.com> Fri, 28 Apr 2023 16:00:00 +0200
vcmi (1.2.0) jammy; urgency=medium
* New upstream release

View File

@ -52,6 +52,7 @@
</categories>
<releases>
<release version="1.3.0" date="2023-07-01" type="development" />
<release version="1.2.1" date="2023-04-28" />
<release version="1.2.0" date="2023-04-14" />
<release version="1.1.1" date="2023-02-03" />
<release version="1.1.0" date="2022-12-23" />

View File

@ -204,6 +204,7 @@ void FirstLaunchView::heroesDataMissing()
ui->labelDataCopy->setVisible(true);
ui->labelDataFound->setVisible(false);
ui->pushButtonDataNext->setEnabled(false);
if(hasVCMIBuilderScript)
{
@ -232,6 +233,7 @@ void FirstLaunchView::heroesDataDetected()
}
ui->labelDataFound->setVisible(true);
ui->pushButtonDataNext->setEnabled(true);
heroesLanguageUpdate();
}
@ -261,7 +263,6 @@ void FirstLaunchView::heroesLanguageUpdate()
ui->labelDataFailure->setVisible(!success);
ui->labelDataSuccess->setVisible(success);
ui->pushButtonDataNext->setEnabled(success);
}
void FirstLaunchView::forceHeroesLanguage(const QString & language)
@ -278,6 +279,18 @@ void FirstLaunchView::copyHeroesData()
if(!sourceRoot.exists())
return;
if (sourceRoot.dirName().compare("data", Qt::CaseInsensitive) == 0)
{
// We got Data folder. Possibly user selected "Data" folder of Heroes III install. Check whether valid data might exist 1 level above
QStringList dirData = sourceRoot.entryList({"data"}, QDir::Filter::Dirs);
if (dirData.empty())
{
// This is "Data" folder without any "Data" folders inside. Try to check for data 1 level above
sourceRoot.cdUp();
}
}
QStringList dirData = sourceRoot.entryList({"data"}, QDir::Filter::Dirs);
QStringList dirMaps = sourceRoot.entryList({"maps"}, QDir::Filter::Dirs);
QStringList dirMp3 = sourceRoot.entryList({"mp3"}, QDir::Filter::Dirs);

View File

@ -96,14 +96,14 @@ JsonNode toJson(QVariant object)
{
JsonNode ret;
if(object.canConvert<QVariantMap>())
ret.Struct() = VariantToMap(object.toMap());
else if(object.canConvert<QVariantList>())
ret.Vector() = VariantToList(object.toList());
else if(object.userType() == QMetaType::QString)
if(object.userType() == QMetaType::QString)
ret.String() = object.toString().toUtf8().data();
else if(object.userType() == QMetaType::Bool)
ret.Bool() = object.toBool();
else if(object.canConvert<QVariantMap>())
ret.Struct() = VariantToMap(object.toMap());
else if(object.canConvert<QVariantList>())
ret.Vector() = VariantToList(object.toList());
else if(object.canConvert<int>())
ret.Integer() = object.toInt();
else if(object.canConvert<double>())

View File

@ -44,7 +44,7 @@ QString Languages::getHeroesDataLanguage()
QString language = QString::fromStdString(settings["session"]["language"].String());
double deviation = settings["session"]["languageDeviation"].Float();
if(deviation > 0.05)
if(deviation > 0.1)
return QString();
return language;
}

View File

@ -128,7 +128,7 @@ std::vector<si32> CStack::activeSpells() const
CSelector selector = Selector::sourceType()(Bonus::SPELL_EFFECT)
.And(CSelector([](const Bonus * b)->bool
{
return b->type != Bonus::NONE;
return b->type != Bonus::NONE && SpellID(b->sid).toSpell() && !SpellID(b->sid).toSpell()->isAdventure();
}));
TConstBonusListPtr spellEffects = getBonuses(selector, Selector::all, cachingStr.str());

View File

@ -76,7 +76,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile & dest, const TerrainTile & f
!ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType->getIndex())) //no special movement bonus
{
ret = VLC->heroh->terrCosts[from.terType->getId()];
ret = VLC->terrainTypeHandler->getById(dest.terType->getId())->moveCost;
ret -= ti->valOfBonuses(Bonus::ROUGH_TERRAIN_DISCOUNT);
if(ret < GameConstants::BASE_MOVEMENT_COST)
ret = GameConstants::BASE_MOVEMENT_COST;

View File

@ -279,10 +279,10 @@ void CGBlackMarket::newTurn(CRandomGenerator & rand) const
{
int resetPeriod = VLC->settings()->getInteger(EGameSettings::MARKETS_BLACK_MARKET_RESTOCK_PERIOD);
if(resetPeriod == 0) //check if feature changing OH3 behavior is enabled
return;
bool isFirstDay = cb->getDate(Date::DAY) == 1;
bool regularResetTriggered = resetPeriod != 0 && ((cb->getDate(Date::DAY)-1) % resetPeriod) != 0;
if (((cb->getDate(Date::DAY)-1) % resetPeriod) != 0)
if (!isFirstDay && !regularResetTriggered)
return;
SetAvailableArtifacts saa;

View File

@ -676,12 +676,12 @@ void CGameHandler::endBattleConfirm(const BattleInfo * battleInfo)
sendMoveArtifact(art, &ma);
}
}
while(!finishingBattle->loserHero->artifactsInBackpack.empty())
for(int slotNumber = finishingBattle->loserHero->artifactsInBackpack.size() - 1; slotNumber >= 0; slotNumber--)
{
//we assume that no big artifacts can be found
MoveArtifact ma;
ma.src = ArtifactLocation(finishingBattle->loserHero,
ArtifactPosition(GameConstants::BACKPACK_START)); //backpack automatically shifts arts to beginning
ArtifactPosition(GameConstants::BACKPACK_START + slotNumber)); //backpack automatically shifts arts to beginning
const CArtifactInstance * art = ma.src.getArt();
if (art->artType->getId() != ArtifactID::GRAIL) //grail may not be won
{
@ -6519,7 +6519,7 @@ void CGameHandler::runBattle()
// check for bad morale => freeze
int nextStackMorale = next->MoraleVal();
if (nextStackMorale < 0)
if(!next->hadMorale && !next->waited() && nextStackMorale < 0)
{
auto diceSize = VLC->settings()->getVector(EGameSettings::COMBAT_BAD_MORALE_DICE);
size_t diceIndex = std::min<size_t>(diceSize.size()-1, -nextStackMorale);
@ -6705,7 +6705,8 @@ void CGameHandler::runBattle()
{
//check for good morale
nextStackMorale = next->MoraleVal();
if(!next->hadMorale //only one extra move per turn possible
if( !battleResult.get()
&& !next->hadMorale
&& !next->defending
&& !next->waited()
&& !next->fear