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:
commit
fb739e7186
114
.github/workflows/github.yml
vendored
114
.github/workflows/github.yml
vendored
@ -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()
|
||||
|
BIN
CI/android/android-release.jks
Normal file
BIN
CI/android/android-release.jks
Normal file
Binary file not shown.
2
CI/android/releaseSigning.properties
Normal file
2
CI/android/releaseSigning.properties
Normal file
@ -0,0 +1,2 @@
|
||||
STORE_FILE=android-release.jks
|
||||
KEY_ALIAS=vcmi
|
@ -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'
|
||||
|
22
ChangeLog.md
22
ChangeLog.md
@ -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 |
BIN
Mods/vcmi/Data/debug/spellRange.png
Normal file
BIN
Mods/vcmi/Data/debug/spellRange.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 96 B |
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@ class CSelectionBase;
|
||||
class CSavingScreen : public CSelectionBase
|
||||
{
|
||||
public:
|
||||
const StartInfo * localSi;
|
||||
std::shared_ptr<CMapInfo> localMi;
|
||||
|
||||
CSavingScreen();
|
||||
|
@ -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()));
|
||||
|
@ -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"));
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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>;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
6
debian/changelog
vendored
@ -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
|
||||
|
@ -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" />
|
||||
|
@ -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);
|
||||
|
@ -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>())
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
{
|
||||
@ -6517,9 +6517,9 @@ void CGameHandler::runBattle()
|
||||
if(!removeGhosts.changedStacks.empty())
|
||||
sendAndApply(&removeGhosts);
|
||||
|
||||
//check for bad morale => freeze
|
||||
// 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,12 +6705,13 @@ 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
|
||||
&& next->alive()
|
||||
&& nextStackMorale > 0)
|
||||
&& next->alive()
|
||||
&& nextStackMorale > 0)
|
||||
{
|
||||
auto diceSize = VLC->settings()->getVector(EGameSettings::COMBAT_GOOD_MORALE_DICE);
|
||||
size_t diceIndex = std::min<size_t>(diceSize.size()-1, nextStackMorale);
|
||||
|
Loading…
Reference in New Issue
Block a user