175
.github/workflows/github.yml
vendored
@ -111,22 +111,6 @@ jobs:
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Ensure LF line endings
|
||||
if: ${{ startsWith(matrix.preset, 'linux-clang-test') }}
|
||||
run: |
|
||||
find . -path ./.git -prune -o -path ./AI/FuzzyLite -prune -o -path ./test/googletest \
|
||||
-o -path ./osx -prune -o -type f \
|
||||
-not -name '*.png' -and -not -name '*.vcxproj*' -and -not -name '*.props' -and -not -name '*.wav' -and -not -name '*.webm' -and -not -name '*.ico' -and -not -name '*.bat' -print0 | \
|
||||
{ ! xargs -0 grep -l -z -P '\r\n'; }
|
||||
|
||||
- name: Validate JSON
|
||||
# the Python yaml module doesn't seem to work on mac-arm
|
||||
# also, running it on multiple presets is redundant and slightly increases already long CI built times
|
||||
if: ${{ startsWith(matrix.preset, 'linux-clang-test') }}
|
||||
run: |
|
||||
sudo apt install python3-jstyleson
|
||||
python3 CI/linux-qt6/validate_json.py
|
||||
|
||||
- name: Dependencies
|
||||
run: source '${{github.workspace}}/CI/${{matrix.platform}}/before_install.sh'
|
||||
env:
|
||||
@ -210,6 +194,9 @@ jobs:
|
||||
if [[ ${{matrix.preset}} == linux-gcc-test ]]
|
||||
then
|
||||
cmake -DENABLE_CCACHE:BOOL=ON -DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14 --preset ${{ matrix.preset }}
|
||||
elif [[ (${{matrix.preset}} == android-conan-ninja-release) && (${{github.ref}} != 'refs/heads/master') ]]
|
||||
then
|
||||
cmake -DENABLE_CCACHE:BOOL=ON -DANDROID_GRADLE_PROPERTIES="applicationIdSuffix=.daily;signingConfig=dailySigning;applicationLabel=VCMI daily" --preset ${{ matrix.preset }}
|
||||
elif [[ ${{matrix.platform}} != msvc ]]
|
||||
then
|
||||
cmake -DENABLE_CCACHE:BOOL=ON --preset ${{ matrix.preset }}
|
||||
@ -250,12 +237,6 @@ jobs:
|
||||
&& '${{github.workspace}}/CI/${{matrix.platform}}/post_pack.sh' '${{github.workspace}}' "$(ls '${{ env.VCMI_PACKAGE_FILE_NAME }}'.*)"
|
||||
rm -rf _CPack_Packages
|
||||
|
||||
- name: Additional logs
|
||||
if: ${{ failure() && steps.cpack.outcome == 'failure' && matrix.platform == 'msvc' }}
|
||||
run: |
|
||||
cat '${{github.workspace}}/out/build/${{matrix.preset}}/_CPack_Packages/win32/NSIS/project.nsi'
|
||||
cat '${{github.workspace}}/out/build/${{matrix.preset}}/_CPack_Packages/win32/NSIS/NSISOutput.log'
|
||||
|
||||
- name: Artifacts
|
||||
if: ${{ matrix.pack == 1 }}
|
||||
uses: actions/upload-artifact@v4
|
||||
@ -268,10 +249,15 @@ jobs:
|
||||
if: ${{ startsWith(matrix.platform, 'android') }}
|
||||
run: |
|
||||
builtApkPath="$(ls ${{ github.workspace }}/out/build/${{ matrix.preset }}/android-build/vcmi-app/build/outputs/apk/release/*.${{ matrix.extension }})"
|
||||
builtAabPath="$(ls ${{ github.workspace }}/out/build/${{ matrix.preset }}/android-build/vcmi-app/build/outputs/bundle/release/*.aab)"
|
||||
ANDROID_APK_PATH="${{ github.workspace }}/$VCMI_PACKAGE_FILE_NAME.${{ matrix.extension }}"
|
||||
ANDROID_AAB_PATH="${{ github.workspace }}/$VCMI_PACKAGE_FILE_NAME.aab"
|
||||
mv "$builtApkPath" "$ANDROID_APK_PATH"
|
||||
mv "$builtAabPath" "$ANDROID_AAB_PATH"
|
||||
echo "ANDROID_APK_PATH=$ANDROID_APK_PATH" >> $GITHUB_ENV
|
||||
- name: Android artifacts
|
||||
echo "ANDROID_AAB_PATH=$ANDROID_AAB_PATH" >> $GITHUB_ENV
|
||||
|
||||
- name: Android apk artifacts
|
||||
if: ${{ startsWith(matrix.platform, 'android') }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@ -279,6 +265,14 @@ jobs:
|
||||
path: |
|
||||
${{ env.ANDROID_APK_PATH }}
|
||||
|
||||
- name: Android aab artifacts
|
||||
if: ${{ startsWith(matrix.platform, 'android') }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ env.VCMI_PACKAGE_FILE_NAME }} - ${{ matrix.platform }} - aab
|
||||
path: |
|
||||
${{ env.ANDROID_AAB_PATH }}
|
||||
|
||||
- name: Symbols
|
||||
if: ${{ matrix.platform == 'msvc' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
@ -287,14 +281,6 @@ jobs:
|
||||
path: |
|
||||
${{github.workspace}}/**/*.pdb
|
||||
|
||||
- name: Android JNI ${{matrix.platform}}
|
||||
if: ${{ startsWith(matrix.platform, 'android') && github.ref == 'refs/heads/master' }}
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Android JNI ${{matrix.platform}}
|
||||
path: |
|
||||
${{github.workspace}}/out/build/${{matrix.preset}}/android-build/libs
|
||||
|
||||
- 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' && matrix.platform != 'mingw-32' }}
|
||||
continue-on-error: true
|
||||
@ -307,107 +293,6 @@ jobs:
|
||||
DEPLOY_RSA: ${{ secrets.DEPLOY_RSA }}
|
||||
PACKAGE_EXTENSION: ${{ matrix.extension }}
|
||||
|
||||
# copy-pasted mostly
|
||||
bundle_release:
|
||||
|
||||
needs: build
|
||||
if: always() && github.ref == 'refs/heads/master'
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- platform: android-32
|
||||
os: macos-14
|
||||
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@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Dependencies
|
||||
run: source '${{github.workspace}}/CI/${{matrix.platform}}/before_install.sh'
|
||||
env:
|
||||
VCMI_BUILD_PLATFORM: x64
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
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
|
||||
|
||||
- uses: actions/setup-java@v4
|
||||
if: ${{ startsWith(matrix.platform, 'android') }}
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '11'
|
||||
|
||||
- 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_GOLDMASTER="$VCMI_PACKAGE_GOLDMASTER" >> $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}}
|
||||
env:
|
||||
ANDROID_STORE_PASSWORD: ${{ secrets.ANDROID_STORE_PASSWORD }}
|
||||
ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
|
||||
|
||||
- name: Download libs x64
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: Android JNI android-64
|
||||
path: ${{ github.workspace }}/out/build/${{ matrix.preset }}/android-build/libs
|
||||
|
||||
- name: Create Android package
|
||||
run: |
|
||||
cd out/build/${{ matrix.preset }}/android-build
|
||||
./gradlew bundleRelease --info
|
||||
echo ANDROID_APK_PATH="$(ls ${{ github.workspace }}/out/build/${{ matrix.preset }}/android-build/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@v4
|
||||
with:
|
||||
name: ${{ env.VCMI_PACKAGE_FILE_NAME }}
|
||||
path: |
|
||||
${{ env.ANDROID_APK_PATH }}
|
||||
|
||||
|
||||
deploy-src:
|
||||
if: always() && github.ref == 'refs/heads/master'
|
||||
runs-on: ubuntu-latest
|
||||
@ -437,3 +322,29 @@ jobs:
|
||||
name: ${{ env.VCMI_PACKAGE_FILE_NAME }}
|
||||
path: |
|
||||
./release.tar.gz
|
||||
|
||||
validate-code:
|
||||
if: always()
|
||||
runs-on: ubuntu-24.04
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v5
|
||||
if: "${{ matrix.conan_profile != '' }}"
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Ensure LF line endings
|
||||
run: |
|
||||
find . -path ./.git -prune -o -path ./AI/FuzzyLite -prune -o -path ./test/googletest \
|
||||
-o -path ./osx -prune -o -type f \
|
||||
-not -name '*.png' -and -not -name '*.vcxproj*' -and -not -name '*.props' -and -not -name '*.wav' -and -not -name '*.webm' -and -not -name '*.ico' -and -not -name '*.bat' -print0 | \
|
||||
{ ! xargs -0 grep -l -z -P '\r\n'; }
|
||||
|
||||
- name: Validate JSON
|
||||
run: |
|
||||
sudo apt install python3-jstyleson
|
||||
python3 CI/linux-qt6/validate_json.py
|
||||
|
@ -649,7 +649,7 @@ void AIGateway::showBlockingDialog(const std::string & text, const std::vector<C
|
||||
auto ratio = static_cast<float>(danger) / hero->getTotalStrength();
|
||||
|
||||
answer = topObj->id == goalObjectID; // no if we do not aim to visit this object
|
||||
logAi->trace("Query hook: %s(%s) by %s danger ratio %f", target.toString(), topObj->getObjectName(), hero.name, ratio);
|
||||
logAi->trace("Query hook: %s(%s) by %s danger ratio %f", target.toString(), topObj->getObjectName(), hero.name(), ratio);
|
||||
|
||||
if(cb->getObj(goalObjectID, false))
|
||||
{
|
||||
@ -1574,7 +1574,7 @@ void AIGateway::requestActionASAP(std::function<void()> whatToDo)
|
||||
|
||||
void AIGateway::lostHero(HeroPtr h)
|
||||
{
|
||||
logAi->debug("I lost my hero %s. It's best to forget and move on.", h.name);
|
||||
logAi->debug("I lost my hero %s. It's best to forget and move on.", h.name());
|
||||
}
|
||||
|
||||
void AIGateway::answerQuery(QueryID queryID, int selection)
|
||||
|
@ -67,7 +67,6 @@ HeroPtr::HeroPtr(const CGHeroInstance * H)
|
||||
}
|
||||
|
||||
h = H;
|
||||
name = h->getNameTranslated();
|
||||
hid = H->id;
|
||||
// infosCount[ai->playerID][hid]++;
|
||||
}
|
||||
@ -89,6 +88,14 @@ bool HeroPtr::operator<(const HeroPtr & rhs) const
|
||||
return hid < rhs.hid;
|
||||
}
|
||||
|
||||
std::string HeroPtr::name() const
|
||||
{
|
||||
if (h)
|
||||
return h->getNameTextID();
|
||||
else
|
||||
return "<NO HERO>";
|
||||
}
|
||||
|
||||
const CGHeroInstance * HeroPtr::get(bool doWeExpectNull) const
|
||||
{
|
||||
return get(cb, doWeExpectNull);
|
||||
|
@ -87,8 +87,7 @@ struct DLL_EXPORT HeroPtr
|
||||
ObjectInstanceID hid;
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
|
||||
std::string name() const;
|
||||
|
||||
HeroPtr();
|
||||
HeroPtr(const CGHeroInstance * H);
|
||||
@ -117,7 +116,6 @@ public:
|
||||
{
|
||||
handler & h;
|
||||
handler & hid;
|
||||
handler & name;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -176,7 +176,7 @@ int HeroManager::selectBestSkill(const HeroPtr & hero, const std::vector<Seconda
|
||||
|
||||
logAi->trace(
|
||||
"Hero %s is proposed to learn %d with score %f",
|
||||
hero.name,
|
||||
hero.name(),
|
||||
skills[i].toEnum(),
|
||||
score);
|
||||
}
|
||||
@ -204,6 +204,7 @@ float HeroManager::getFightingStrengthCached(const CGHeroInstance * hero) const
|
||||
{
|
||||
auto cached = knownFightingStrength.find(hero->id);
|
||||
|
||||
//FIXME: fallback to hero->getFightingStrength() is VERY slow on higher difficulties (no object graph? map reveal?)
|
||||
return cached != knownFightingStrength.end() ? cached->second : hero->getFightingStrength();
|
||||
}
|
||||
|
||||
|
@ -212,7 +212,7 @@ void CaptureObjectsBehavior::decomposeObjects(
|
||||
vstd::concatenate(tasksLocal, getVisitGoals(paths, nullkiller, objToVisit, specificObjects));
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(sync);
|
||||
std::lock_guard<std::mutex> lock(sync); // FIXME: consider using tbb::parallel_reduce instead to avoid mutex overhead
|
||||
vstd::concatenate(result, tasksLocal);
|
||||
});
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ namespace Goals
|
||||
{
|
||||
objid = obj->id.getNum();
|
||||
tile = obj->visitablePos();
|
||||
name = obj->getObjectName();
|
||||
name = obj->typeName;
|
||||
}
|
||||
|
||||
bool operator==(const CaptureObject & other) const override;
|
||||
|
@ -26,7 +26,7 @@ ExecuteHeroChain::ExecuteHeroChain(const AIPath & path, const CGObjectInstance *
|
||||
if(obj)
|
||||
{
|
||||
objid = obj->id.getNum();
|
||||
targetName = obj->getObjectName() + tile.toString();
|
||||
targetName = obj->typeName + tile.toString();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -106,7 +106,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->error("Hero %s was lost. Exit hero chain.", heroPtr.name);
|
||||
logAi->error("Hero %s was lost. Exit hero chain.", heroPtr.name());
|
||||
|
||||
return;
|
||||
}
|
||||
@ -143,7 +143,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->error("Hero %s was lost trying to execute special action. Exit hero chain.", heroPtr.name);
|
||||
logAi->error("Hero %s was lost trying to execute special action. Exit hero chain.", heroPtr.name());
|
||||
|
||||
return;
|
||||
}
|
||||
@ -204,7 +204,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
{
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->error("Hero %s was lost. Exit hero chain.", heroPtr.name);
|
||||
logAi->error("Hero %s was lost. Exit hero chain.", heroPtr.name());
|
||||
|
||||
return;
|
||||
}
|
||||
@ -250,7 +250,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
|
||||
{
|
||||
if(!heroPtr.validAndSet())
|
||||
{
|
||||
logAi->debug("Hero %s was killed while attempting to reach %s", heroPtr.name, node->coord.toString());
|
||||
logAi->debug("Hero %s was killed while attempting to reach %s", heroPtr.name(), node->coord.toString());
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -70,11 +70,8 @@ bool ExplorationHelper::scanMap()
|
||||
int3 mapSize = cbp->getMapSize();
|
||||
int perimeter = 2 * sightRadius * (mapSize.x + mapSize.y);
|
||||
|
||||
std::vector<int3> from;
|
||||
std::vector<int3> to;
|
||||
|
||||
from.reserve(perimeter);
|
||||
to.reserve(perimeter);
|
||||
std::vector<int3> edgeTiles;
|
||||
edgeTiles.reserve(perimeter);
|
||||
|
||||
foreach_tile_pos([&](const int3 & pos)
|
||||
{
|
||||
@ -91,13 +88,13 @@ bool ExplorationHelper::scanMap()
|
||||
});
|
||||
|
||||
if(hasInvisibleNeighbor)
|
||||
from.push_back(pos);
|
||||
edgeTiles.push_back(pos);
|
||||
}
|
||||
});
|
||||
|
||||
logAi->debug("Exploration scan visible area perimeter for hero %s", hero->getNameTranslated());
|
||||
|
||||
for(const int3 & tile : from)
|
||||
for(const int3 & tile : edgeTiles)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
@ -108,19 +105,36 @@ bool ExplorationHelper::scanMap()
|
||||
}
|
||||
|
||||
allowDeadEndCancellation = false;
|
||||
|
||||
for(int i = 0; i < sightRadius; i++)
|
||||
{
|
||||
getVisibleNeighbours(from, to);
|
||||
vstd::concatenate(from, to);
|
||||
vstd::removeDuplicates(from);
|
||||
}
|
||||
|
||||
logAi->debug("Exploration scan all possible tiles for hero %s", hero->getNameTranslated());
|
||||
|
||||
for(const int3 & tile : from)
|
||||
boost::multi_array<ui8, 3> potentialTiles = *ts->fogOfWarMap;
|
||||
std::vector<int3> tilesToExploreFrom = edgeTiles;
|
||||
|
||||
// WARNING: POTENTIAL BUG
|
||||
// AI attempts to move to any tile within sight radius to reveal some new tiles
|
||||
// however sight radius is circular, while this method assumes square radius
|
||||
// standing on the edge of a square will NOT reveal tile in opposite corner
|
||||
for(int i = 0; i < sightRadius; i++)
|
||||
{
|
||||
scanTile(tile);
|
||||
std::vector<int3> newTilesToExploreFrom;
|
||||
|
||||
for(const int3 & tile : tilesToExploreFrom)
|
||||
{
|
||||
foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if(potentialTiles[neighbour.z][neighbour.x][neighbour.y])
|
||||
{
|
||||
newTilesToExploreFrom.push_back(neighbour);
|
||||
potentialTiles[neighbour.z][neighbour.x][neighbour.y] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
for(const int3 & tile : newTilesToExploreFrom)
|
||||
{
|
||||
scanTile(tile);
|
||||
}
|
||||
|
||||
std::swap(tilesToExploreFrom, newTilesToExploreFrom);
|
||||
}
|
||||
|
||||
return !bestGoal->invalid();
|
||||
@ -172,20 +186,6 @@ void ExplorationHelper::scanTile(const int3 & tile)
|
||||
}
|
||||
}
|
||||
|
||||
void ExplorationHelper::getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const
|
||||
{
|
||||
for(const int3 & tile : tiles)
|
||||
{
|
||||
foreach_neighbour(cbp, tile, [&](CCallback * cbp, int3 neighbour)
|
||||
{
|
||||
if((*(ts->fogOfWarMap))[neighbour.z][neighbour.x][neighbour.y])
|
||||
{
|
||||
out.push_back(neighbour);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
int ExplorationHelper::howManyTilesWillBeDiscovered(const int3 & pos) const
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -46,7 +46,6 @@ public:
|
||||
private:
|
||||
void scanTile(const int3 & tile);
|
||||
bool hasReachableNeighbor(const int3 & pos) const;
|
||||
void getVisibleNeighbours(const std::vector<int3> & tiles, std::vector<int3> & out) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -320,11 +320,9 @@ void AINodeStorage::calculateNeighbours(
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
const CPathfinderHelper * pathfinderHelper)
|
||||
{
|
||||
std::vector<int3> accessibleNeighbourTiles;
|
||||
NeighbourTilesVector accessibleNeighbourTiles;
|
||||
|
||||
result.clear();
|
||||
accessibleNeighbourTiles.reserve(8);
|
||||
|
||||
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
||||
|
||||
const AIPathNode * srcNode = getAINode(source.node);
|
||||
|
@ -23,6 +23,8 @@ constexpr int NKAI_GRAPH_TRACE_LEVEL = 0;
|
||||
#include "Actions/SpecialAction.h"
|
||||
#include "Actors.h"
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
namespace NKAI
|
||||
{
|
||||
namespace AIPathfinding
|
||||
@ -85,7 +87,9 @@ struct AIPathNodeInfo
|
||||
|
||||
struct AIPath
|
||||
{
|
||||
std::vector<AIPathNodeInfo> nodes;
|
||||
using NodesVector = boost::container::small_vector<AIPathNodeInfo, 16>;
|
||||
|
||||
NodesVector nodes;
|
||||
uint64_t targetObjectDanger;
|
||||
uint64_t armyLoss;
|
||||
uint64_t targetObjectArmyLoss;
|
||||
|
@ -141,7 +141,8 @@ namespace AIPathfinding
|
||||
{
|
||||
SpellID summonBoat = SpellID::SUMMON_BOAT;
|
||||
|
||||
return hero->getSpellCost(summonBoat.toSpell());
|
||||
// FIXME: this should be hero->getSpellCost, however currently queries to bonus system are too slow
|
||||
return summonBoat.toSpell()->getCost(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,7 +118,7 @@ void GraphPaths::calculatePaths(const CGHeroInstance * targetHero, const Nullkil
|
||||
|
||||
targetNode.specialAction = compositeAction;
|
||||
|
||||
auto targetGraphNode = graph.getNode(target);
|
||||
const auto & targetGraphNode = graph.getNode(target);
|
||||
|
||||
if(targetGraphNode.objID.hasValue())
|
||||
{
|
||||
|
@ -162,10 +162,9 @@ void AINodeStorage::calculateNeighbours(
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
const CPathfinderHelper * pathfinderHelper)
|
||||
{
|
||||
std::vector<int3> accessibleNeighbourTiles;
|
||||
NeighbourTilesVector accessibleNeighbourTiles;
|
||||
|
||||
result.clear();
|
||||
accessibleNeighbourTiles.reserve(8);
|
||||
|
||||
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
||||
|
||||
|
@ -58,7 +58,7 @@ option(ENABLE_CCACHE "Speed up recompilation by caching previous compilations" O
|
||||
# Platform-specific options
|
||||
|
||||
if(ANDROID)
|
||||
set(ANDROID_TARGET_SDK_VERSION "33" CACHE STRING "Android target SDK version")
|
||||
set(ANDROID_TARGET_SDK_VERSION "34" CACHE STRING "Android target SDK version")
|
||||
set(ANDROIDDEPLOYQT_OPTIONS "" CACHE STRING "Additional androiddeployqt options separated by semi-colon")
|
||||
set(ANDROID_GRADLE_PROPERTIES "" CACHE STRING "Additional Gradle properties separated by semi-colon")
|
||||
|
||||
|
@ -26,7 +26,7 @@ android {
|
||||
minSdk = qtMinSdkVersion as Integer
|
||||
targetSdk = qtTargetSdkVersion as Integer // ANDROID_TARGET_SDK_VERSION in the CMake project
|
||||
|
||||
versionCode 1533
|
||||
versionCode 1535
|
||||
versionName "1.5.3"
|
||||
|
||||
setProperty("archivesBaseName", "vcmi")
|
||||
|
@ -1225,13 +1225,14 @@ void CPlayerInterface::loadGame( BinaryDeserializer & h )
|
||||
|
||||
void CPlayerInterface::moveHero( const CGHeroInstance *h, const CGPath& path )
|
||||
{
|
||||
LOG_TRACE(logGlobal);
|
||||
if (!LOCPLINT->makingTurn)
|
||||
return;
|
||||
|
||||
assert(h);
|
||||
assert(!showingDialog->isBusy());
|
||||
assert(dialogs.empty());
|
||||
|
||||
LOG_TRACE(logGlobal);
|
||||
if (!LOCPLINT->makingTurn)
|
||||
return;
|
||||
if (!h)
|
||||
return; //can't find hero
|
||||
|
||||
|
@ -648,14 +648,14 @@ void CServerHandler::startGameplay(VCMI_LIB_WRAP_NAMESPACE(CGameState) * gameSta
|
||||
if(CMM)
|
||||
CMM->disable();
|
||||
|
||||
campaignScoreCalculator = nullptr;
|
||||
|
||||
switch(si->mode)
|
||||
{
|
||||
case EStartMode::NEW_GAME:
|
||||
client->newGame(gameState);
|
||||
break;
|
||||
case EStartMode::CAMPAIGN:
|
||||
if(si->campState->conqueredScenarios().empty())
|
||||
campaignScoreCalculator.reset();
|
||||
client->newGame(gameState);
|
||||
break;
|
||||
case EStartMode::LOAD_GAME:
|
||||
|
@ -168,7 +168,7 @@ void PlayerLocalState::setSelection(const CArmedInstance * selection)
|
||||
|
||||
currentSelection = selection;
|
||||
|
||||
if (selection)
|
||||
if (adventureInt && selection)
|
||||
adventureInt->onSelectionChanged(selection);
|
||||
}
|
||||
|
||||
@ -212,6 +212,9 @@ void PlayerLocalState::addWanderingHero(const CGHeroInstance * hero)
|
||||
assert(hero);
|
||||
assert(!vstd::contains(wanderingHeroes, hero));
|
||||
wanderingHeroes.push_back(hero);
|
||||
|
||||
if (currentSelection == nullptr)
|
||||
setSelection(hero);
|
||||
}
|
||||
|
||||
void PlayerLocalState::removeWanderingHero(const CGHeroInstance * hero)
|
||||
@ -260,6 +263,9 @@ void PlayerLocalState::addOwnedTown(const CGTownInstance * town)
|
||||
assert(town);
|
||||
assert(!vstd::contains(ownedTowns, town));
|
||||
ownedTowns.push_back(town);
|
||||
|
||||
if (currentSelection == nullptr)
|
||||
setSelection(town);
|
||||
}
|
||||
|
||||
void PlayerLocalState::removeOwnedTown(const CGTownInstance * town)
|
||||
|
@ -395,8 +395,6 @@ void AdventureMapInterface::adjustActiveness()
|
||||
|
||||
void AdventureMapInterface::onCurrentPlayerChanged(PlayerColor playerID)
|
||||
{
|
||||
LOCPLINT->localState->setSelection(nullptr);
|
||||
|
||||
if (playerID == currentPlayerID)
|
||||
return;
|
||||
|
||||
|
@ -58,6 +58,9 @@ InputSourceTouch::InputSourceTouch()
|
||||
|
||||
void InputSourceTouch::handleEventFingerMotion(const SDL_TouchFingerEvent & tfinger)
|
||||
{
|
||||
if (CCS && CCS->curh && settings["video"]["cursor"].String() == "software" && state != TouchState::RELATIVE_MODE)
|
||||
CCS->curh->cursorMove(GH.getCursorPosition().x, GH.getCursorPosition().y);
|
||||
|
||||
switch(state)
|
||||
{
|
||||
case TouchState::RELATIVE_MODE:
|
||||
|
@ -215,7 +215,7 @@ void CBonusSelection::createBonusesIcons()
|
||||
break;
|
||||
case CampaignBonusType::SPELL_SCROLL:
|
||||
desc.appendLocalString(EMetaText::GENERAL_TXT, 716);
|
||||
desc.replaceName(ArtifactID(bonDescs[i].info2));
|
||||
desc.replaceName(SpellID(bonDescs[i].info2));
|
||||
break;
|
||||
case CampaignBonusType::PRIMARY_SKILL:
|
||||
{
|
||||
|
@ -137,7 +137,7 @@ InfoCard::InfoCard()
|
||||
|
||||
labelSaveDate = std::make_shared<CLabel>(310, 38, FONT_SMALL, ETextAlignment::BOTTOMRIGHT, Colors::WHITE);
|
||||
labelMapSize = std::make_shared<CLabel>(333, 56, FONT_TINY, ETextAlignment::CENTER, Colors::WHITE);
|
||||
mapName = std::make_shared<CLabel>(26, 39, FONT_BIG, ETextAlignment::TOPLEFT, Colors::YELLOW);
|
||||
mapName = std::make_shared<CLabel>(26, 39, FONT_BIG, ETextAlignment::TOPLEFT, Colors::YELLOW, "", 285);
|
||||
Rect descriptionRect(26, 149, 320, 115);
|
||||
mapDescription = std::make_shared<CTextBox>("", descriptionRect, 1);
|
||||
playerListBg = std::make_shared<CPicture>(ImagePath::builtin("CHATPLUG.bmp"), 16, 276);
|
||||
|
@ -179,23 +179,17 @@ void CHighScoreScreen::addHighScores()
|
||||
ColorRGBA color = (i == highlighted || currentGameNotInListEntry) ? Colors::YELLOW : Colors::WHITE;
|
||||
|
||||
texts.push_back(std::make_shared<CLabel>(115, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, std::to_string((currentGameNotInListEntry ? highlighted : i) + 1)));
|
||||
std::string tmp = curData["player"].String();
|
||||
TextOperations::trimRightUnicode(tmp, std::max(0, (int)TextOperations::getUnicodeCharactersCount(tmp) - 13));
|
||||
texts.push_back(std::make_shared<CLabel>(225, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, tmp));
|
||||
texts.push_back(std::make_shared<CLabel>(225, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, curData["player"].String(), 120));
|
||||
|
||||
if(highscorepage == HighScorePage::SCENARIO)
|
||||
{
|
||||
std::string tmp = curData["scenarioName"].String();
|
||||
TextOperations::trimRightUnicode(tmp, std::max(0, (int)TextOperations::getUnicodeCharactersCount(tmp) - 25));
|
||||
texts.push_back(std::make_shared<CLabel>(405, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, tmp));
|
||||
texts.push_back(std::make_shared<CLabel>(405, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, curData["scenarioName"].String(), 200));
|
||||
texts.push_back(std::make_shared<CLabel>(557, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, std::to_string(curData["days"].Integer())));
|
||||
texts.push_back(std::make_shared<CLabel>(627, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, std::to_string(curData["points"].Integer())));
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string tmp = curData["campaignName"].String();
|
||||
TextOperations::trimRightUnicode(tmp, std::max(0, (int)TextOperations::getUnicodeCharactersCount(tmp) - 25));
|
||||
texts.push_back(std::make_shared<CLabel>(405, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, tmp));
|
||||
texts.push_back(std::make_shared<CLabel>(405, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, curData["campaignName"].String(), 200));
|
||||
texts.push_back(std::make_shared<CLabel>(592, y + i * 50, FONT_MEDIUM, ETextAlignment::CENTER, color, std::to_string(curData["points"].Integer())));
|
||||
}
|
||||
|
||||
|
@ -1263,8 +1263,8 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
|
||||
resdatabar = std::make_shared<CResDataBar>(ImagePath::builtin("ARESBAR"), 3, 575, 37, 3, 84, 78);
|
||||
|
||||
townlist = std::make_shared<CTownList>(3, Rect(Point(743, 414), Point(48, 128)), Point(1,16), Point(0, 32), LOCPLINT->localState->getOwnedTowns().size() );
|
||||
townlist->setScrollUpButton( std::make_shared<CButton>( Point(744, 414), AnimationPath::builtin("IAM014"), CButton::tooltipLocalized("core.help.306"), 0, EShortcut::MOVE_UP));
|
||||
townlist->setScrollDownButton( std::make_shared<CButton>( Point(744, 526), AnimationPath::builtin("IAM015"), CButton::tooltipLocalized("core.help.307"), 0, EShortcut::MOVE_DOWN));
|
||||
townlist->setScrollUpButton( std::make_shared<CButton>( Point(744, 414), AnimationPath::builtin("IAM014"), CButton::tooltipLocalized("core.help.306"), 0));
|
||||
townlist->setScrollDownButton( std::make_shared<CButton>( Point(744, 526), AnimationPath::builtin("IAM015"), CButton::tooltipLocalized("core.help.307"), 0));
|
||||
|
||||
if(from)
|
||||
townlist->select(from);
|
||||
@ -1399,6 +1399,12 @@ void CCastleInterface::keyPressed(EShortcut key)
|
||||
{
|
||||
switch(key)
|
||||
{
|
||||
case EShortcut::MOVE_UP:
|
||||
townlist->selectPrev();
|
||||
break;
|
||||
case EShortcut::MOVE_DOWN:
|
||||
townlist->selectNext();
|
||||
break;
|
||||
case EShortcut::TOWN_OPEN_FORT:
|
||||
GH.windows().createAndPushWindow<CFortScreen>(town);
|
||||
break;
|
||||
@ -1540,8 +1546,11 @@ CHallInterface::CHallInterface(const CGTownInstance * Town):
|
||||
const CBuilding * building = nullptr;
|
||||
for(auto & buildingID : boxList[row][col])//we are looking for the first not built structure
|
||||
{
|
||||
if (town->town->buildings.count(buildingID) == 0)
|
||||
throw std::runtime_error("Town " + Town->town->faction->getJsonKey() + " has no building with ID " + std::to_string(buildingID.getNum()));
|
||||
if (!buildingID.hasValue())
|
||||
{
|
||||
logMod->warn("Invalid building ID found in hallSlots of town '%s'", town->town->faction->getJsonKey() );
|
||||
continue;
|
||||
}
|
||||
|
||||
const CBuilding * current = town->town->buildings.at(buildingID);
|
||||
if(vstd::contains(town->builtBuildings, buildingID))
|
||||
|
@ -10,6 +10,7 @@
|
||||
"anyOf" : [
|
||||
{
|
||||
"type" : "string",
|
||||
"enum" : [ "SHOOTER_ONLY", "DRAGON_NATURE", "IS_UNDEAD", "CREATURE_NATIVE_TERRAIN", "CREATURE_FACTION", "SAME_FACTION", "CREATURES_ONLY", "OPPOSITE_SIDE" ],
|
||||
"description" : "parameterless limiter or boolean operator at start of array"
|
||||
},
|
||||
{
|
||||
@ -18,6 +19,7 @@
|
||||
"properties" : {
|
||||
"type" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "CREATURE_TYPE_LIMITER", "HAS_ANOTHER_BONUS_LIMITER", "CREATURE_ALIGNMENT_LIMITER", "FACTION_LIMITER", "CREATURE_LEVEL_LIMITER", "CREATURE_TERRAIN_LIMITER", "UNIT_ON_HEXES" ],
|
||||
"description" : "type"
|
||||
},
|
||||
"parameters" : {
|
||||
@ -53,31 +55,24 @@
|
||||
},
|
||||
"sourceType" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "ARTIFACT", "ARTIFACT_INSTANCE", "OBJECT_TYPE", "OBJECT_INSTANCE", "CREATURE_ABILITY", "TERRAIN_NATIVE", "TERRAIN_OVERLAY", "SPELL_EFFECT", "TOWN_STRUCTURE", "HERO_BASE_SKILL", "SECONDARY_SKILL", "HERO_SPECIAL", "ARMY", "CAMPAIGN_BONUS", "STACK_EXPERIENCE", "COMMANDER", "GLOBAL", "OTHER", ],
|
||||
"description" : "sourceType"
|
||||
},
|
||||
"targetSourceType" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "ARTIFACT", "ARTIFACT_INSTANCE", "OBJECT_TYPE", "OBJECT_INSTANCE", "CREATURE_ABILITY", "TERRAIN_NATIVE", "TERRAIN_OVERLAY", "SPELL_EFFECT", "TOWN_STRUCTURE", "HERO_BASE_SKILL", "SECONDARY_SKILL", "HERO_SPECIAL", "ARMY", "CAMPAIGN_BONUS", "STACK_EXPERIENCE", "COMMANDER", "GLOBAL", "OTHER", ],
|
||||
"description" : "targetSourceType"
|
||||
},
|
||||
"propagator" : {
|
||||
"description" : "propagator",
|
||||
"anyOf" : [
|
||||
{
|
||||
"type" : "string"
|
||||
},
|
||||
{
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"type" : "string",
|
||||
"description" : "0"
|
||||
}
|
||||
}
|
||||
]
|
||||
"type" : "string",
|
||||
"enum" : [ "BATTLE_WIDE", "VISITED_TOWN_AND_VISITOR", "PLAYER_PROPAGATOR", "HERO", "TEAM_PROPAGATOR", "GLOBAL_EFFECT" ]
|
||||
},
|
||||
"updater" : {
|
||||
"anyOf" : [
|
||||
{
|
||||
"type" : "string"
|
||||
"type" : "string",
|
||||
"enum" : [ "TIMES_HERO_LEVEL", "TIMES_STACK_LEVEL", "ARMY_MOVEMENT", "BONUS_OWNER_UPDATER" ]
|
||||
},
|
||||
{
|
||||
"description" : "updater",
|
||||
@ -87,6 +82,7 @@
|
||||
"properties" : {
|
||||
"type" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "GROWS_WITH_LEVEL", "ARMY_MOVEMENT" ],
|
||||
"description" : "type"
|
||||
},
|
||||
"parameters" : {
|
||||
@ -101,7 +97,8 @@
|
||||
"propagationUpdater" : {
|
||||
"anyOf" : [
|
||||
{
|
||||
"type" : "string"
|
||||
"type" : "string",
|
||||
"enum" : [ "TIMES_HERO_LEVEL", "TIMES_STACK_LEVEL", "ARMY_MOVEMENT", "BONUS_OWNER_UPDATER" ]
|
||||
},
|
||||
{
|
||||
"description" : "propagationUpdater",
|
||||
@ -111,6 +108,7 @@
|
||||
"properties" : {
|
||||
"type" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "GROWS_WITH_LEVEL", "ARMY_MOVEMENT" ],
|
||||
"description" : "type"
|
||||
},
|
||||
"parameters" : {
|
||||
@ -128,6 +126,7 @@
|
||||
},
|
||||
"effectRange" : {
|
||||
"type" : "string",
|
||||
"enum" : [ "NO_LIMIT", "ONLY_DISTANCE_FIGHT", "ONLY_MELEE_FIGHT" ],
|
||||
"description" : "effectRange"
|
||||
},
|
||||
"val" : {
|
||||
@ -136,6 +135,7 @@
|
||||
},
|
||||
"valueType" : {
|
||||
"type" : "string",
|
||||
"enum" : ["ADDITIVE_VALUE", "BASE_NUMBER", "PERCENT_TO_ALL", "PERCENT_TO_BASE", "PERCENT_TO_SOURCE", "PERCENT_TO_TARGET_TYPE", "INDEPENDENT_MAX", "INDEPENDENT_MIN" ],
|
||||
"description" : "valueType"
|
||||
},
|
||||
"addInfo" : {
|
||||
@ -156,8 +156,17 @@
|
||||
},
|
||||
"duration" : {
|
||||
"anyOf" : [
|
||||
{ "type" : "string"},
|
||||
{ "type" : "array", "items" : {"type" : "string"} }
|
||||
{
|
||||
"type" : "string",
|
||||
"enum" : ["PERMANENT", "ONE_BATTLE", "ONE_DAY", "ONE_WEEK", "N_TURNS", "N_DAYS", "UNTIL_BEING_ATTACKED", "UNTIL_ATTACK", "STACK_GETS_TURN", "COMMANDER_KILLED", "UNTIL_OWN_ATTACK" ]
|
||||
},
|
||||
{
|
||||
"type" : "array",
|
||||
"items" : {
|
||||
"type" : "string",
|
||||
"enum" : ["PERMANENT", "ONE_BATTLE", "ONE_DAY", "ONE_WEEK", "N_TURNS", "N_DAYS", "UNTIL_BEING_ATTACKED", "UNTIL_ATTACK", "STACK_GETS_TURN", "COMMANDER_KILLED", "UNTIL_OWN_ATTACK" ]
|
||||
}
|
||||
}
|
||||
],
|
||||
"description" : "duration"
|
||||
},
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#ifdef ENABLE_INNOEXTRACT
|
||||
#include "cli/extract.hpp"
|
||||
#include "setup/version.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef VCMI_IOS
|
||||
@ -331,12 +332,12 @@ void FirstLaunchView::extractGogData()
|
||||
return file;
|
||||
};
|
||||
|
||||
QString fileExe = fileSelection("exe", tr("GOG installer") + " (*.exe)");
|
||||
if(fileExe.isEmpty())
|
||||
return;
|
||||
QString fileBin = fileSelection("bin", tr("GOG data") + " (*.bin)", QFileInfo(fileExe).absolutePath());
|
||||
QString fileBin = fileSelection("bin", tr("GOG data") + " (*.bin)");
|
||||
if(fileBin.isEmpty())
|
||||
return;
|
||||
QString fileExe = fileSelection("exe", tr("GOG installer") + " (*.exe)", QFileInfo(fileBin).absolutePath());
|
||||
if(fileExe.isEmpty())
|
||||
return;
|
||||
|
||||
ui->progressBarGog->setVisible(true);
|
||||
ui->pushButtonGogInstall->setVisible(false);
|
||||
@ -344,6 +345,11 @@ void FirstLaunchView::extractGogData()
|
||||
|
||||
QTimer::singleShot(100, this, [this, fileExe, fileBin](){ // background to make sure FileDialog is closed...
|
||||
QDir tempDir(pathToQString(VCMIDirs::get().userDataPath()));
|
||||
if(tempDir.cd("tmp"))
|
||||
{
|
||||
tempDir.removeRecursively(); // remove if already exists (e.g. previous crash)
|
||||
tempDir.cdUp();
|
||||
}
|
||||
tempDir.mkdir("tmp");
|
||||
if(!tempDir.cd("tmp"))
|
||||
return; // should not happen - but avoid deleting wrong folder in any case
|
||||
@ -352,6 +358,26 @@ void FirstLaunchView::extractGogData()
|
||||
QFile(fileExe).copy(tmpFileExe);
|
||||
QFile(fileBin).copy(tempDir.filePath("h3_gog-1.bin"));
|
||||
|
||||
QString errorText{};
|
||||
|
||||
auto isGogGalaxyExe = [](QString fileExe) {
|
||||
QFile file(fileExe);
|
||||
quint64 fileSize = file.size();
|
||||
|
||||
if(fileSize > 10 * 1024 * 1024)
|
||||
return false; // avoid to load big files; galaxy exe is smaller...
|
||||
|
||||
if(!file.open(QIODevice::ReadOnly))
|
||||
return false;
|
||||
QByteArray data = file.readAll();
|
||||
|
||||
const QByteArray magicId{(const char*)u"GOG Galaxy", 20};
|
||||
return data.contains(magicId);
|
||||
};
|
||||
|
||||
if(isGogGalaxyExe(tmpFileExe))
|
||||
errorText = tr("You've provided GOG Galaxy installer! This file doesn't contain the game. Please download the offline backup game installer!");
|
||||
|
||||
::extract_options o;
|
||||
o.extract = true;
|
||||
|
||||
@ -364,20 +390,44 @@ void FirstLaunchView::extractGogData()
|
||||
o.filenames.set_expand(true);
|
||||
|
||||
o.preserve_file_times = true; // also correctly closes file -> without it: on Windows the files are not written completly
|
||||
|
||||
process_file(tmpFileExe.toStdString(), o, [this](float progress) {
|
||||
ui->progressBarGog->setValue(progress * 100);
|
||||
qApp->processEvents();
|
||||
});
|
||||
|
||||
try
|
||||
{
|
||||
if(errorText.isEmpty())
|
||||
process_file(tmpFileExe.toStdString(), o, [this](float progress) {
|
||||
ui->progressBarGog->setValue(progress * 100);
|
||||
qApp->processEvents();
|
||||
});
|
||||
}
|
||||
catch(const std::ios_base::failure & e)
|
||||
{
|
||||
errorText = tr("Stream error while extracting files!\nerror reason: ");
|
||||
errorText += e.what();
|
||||
}
|
||||
catch(const format_error & e)
|
||||
{
|
||||
errorText = e.what();
|
||||
}
|
||||
catch(const std::runtime_error & e)
|
||||
{
|
||||
errorText = e.what();
|
||||
}
|
||||
catch(const setup::version_error &)
|
||||
{
|
||||
errorText = tr("Not a supported Inno Setup installer!");
|
||||
}
|
||||
|
||||
ui->progressBarGog->setVisible(false);
|
||||
ui->pushButtonGogInstall->setVisible(true);
|
||||
setEnabled(true);
|
||||
|
||||
QStringList dirData = tempDir.entryList({"data"}, QDir::Filter::Dirs);
|
||||
if(dirData.empty() || QDir(tempDir.filePath(dirData.front())).entryList({"*.lod"}, QDir::Filter::Files).empty())
|
||||
if(!errorText.isEmpty() || dirData.empty() || QDir(tempDir.filePath(dirData.front())).entryList({"*.lod"}, QDir::Filter::Files).empty())
|
||||
{
|
||||
QMessageBox::critical(this, tr("No Heroes III data!"), tr("Selected files do not contain Heroes III data!"), QMessageBox::Ok, QMessageBox::Ok);
|
||||
if(!errorText.isEmpty())
|
||||
QMessageBox::critical(this, tr("Extracting error!"), errorText, QMessageBox::Ok, QMessageBox::Ok);
|
||||
else
|
||||
QMessageBox::critical(this, tr("No Heroes III data!"), tr("Selected files do not contain Heroes III data!"), QMessageBox::Ok, QMessageBox::Ok);
|
||||
tempDir.removeRecursively();
|
||||
return;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ namespace Helper
|
||||
void loadSettings()
|
||||
{
|
||||
settings.init("config/settings.json", "vcmi:settings");
|
||||
persistentStorage.init("config/persistentStorage.json", "");
|
||||
}
|
||||
|
||||
void enableScrollBySwiping(QObject * scrollTarget)
|
||||
|
@ -26,9 +26,15 @@ namespace
|
||||
{
|
||||
QString detectModArchive(QString path, QString modName, std::vector<std::string> & filesToExtract)
|
||||
{
|
||||
ZipArchive archive(qstringToPath(path));
|
||||
|
||||
filesToExtract = archive.listFiles();
|
||||
try {
|
||||
ZipArchive archive(qstringToPath(path));
|
||||
filesToExtract = archive.listFiles();
|
||||
}
|
||||
catch (const std::runtime_error & e)
|
||||
{
|
||||
logGlobal->error("Failed to open zip archive. Reason: %s", e.what());
|
||||
return "";
|
||||
}
|
||||
|
||||
QString modDirName;
|
||||
|
||||
|
@ -90,6 +90,12 @@ void CSettingsView::loadSettings()
|
||||
#ifdef VCMI_MOBILE
|
||||
ui->comboBoxFullScreen->hide();
|
||||
ui->labelFullScreen->hide();
|
||||
|
||||
if(!persistentStorage["gui"]["tutorialCompleted0"].Bool() && !persistentStorage["gui"]["tutorialCompleted1"].Bool())
|
||||
{
|
||||
ui->labelResetTutorialTouchscreen->hide();
|
||||
ui->pushButtonResetTutorialTouchscreen->hide();
|
||||
}
|
||||
#else
|
||||
ui->labelReservedArea->hide();
|
||||
ui->sliderReservedArea->hide();
|
||||
@ -99,6 +105,8 @@ void CSettingsView::loadSettings()
|
||||
ui->labelRelativeCursorSpeed->hide();
|
||||
ui->buttonHapticFeedback->hide();
|
||||
ui->labelHapticFeedback->hide();
|
||||
ui->labelResetTutorialTouchscreen->hide();
|
||||
ui->pushButtonResetTutorialTouchscreen->hide();
|
||||
if (settings["video"]["realFullscreen"].Bool())
|
||||
ui->comboBoxFullScreen->setCurrentIndex(2);
|
||||
else
|
||||
@ -525,6 +533,16 @@ void CSettingsView::on_pushButtonTranslation_clicked()
|
||||
}
|
||||
}
|
||||
|
||||
void CSettingsView::on_pushButtonResetTutorialTouchscreen_clicked()
|
||||
{
|
||||
Settings node0 = persistentStorage.write["gui"]["tutorialCompleted0"];
|
||||
node0->Bool() = false;
|
||||
Settings node1 = persistentStorage.write["gui"]["tutorialCompleted1"];
|
||||
node1->Bool() = false;
|
||||
|
||||
ui->pushButtonResetTutorialTouchscreen->hide();
|
||||
}
|
||||
|
||||
void CSettingsView::on_buttonRepositoryDefault_toggled(bool value)
|
||||
{
|
||||
Settings node = settings.write["launcher"]["defaultRepositoryEnabled"];
|
||||
@ -705,12 +723,12 @@ void CSettingsView::on_spinBoxNetworkPortLobby_valueChanged(int arg1)
|
||||
|
||||
void CSettingsView::on_sliderControllerSticksAcceleration_valueChanged(int value)
|
||||
{
|
||||
Settings node = settings.write["input"]["configAxisScale"];
|
||||
Settings node = settings.write["input"]["controllerAxisScale"];
|
||||
node->Integer() = value / 100.0;
|
||||
}
|
||||
|
||||
void CSettingsView::on_sliderControllerSticksSensitivity_valueChanged(int value)
|
||||
{
|
||||
Settings node = settings.write["input"]["configAxisSpeed"];
|
||||
Settings node = settings.write["input"]["controllerAxisSpeed"];
|
||||
node->Integer() = value;
|
||||
}
|
||||
|
@ -49,6 +49,7 @@ private slots:
|
||||
void on_comboBoxLanguage_currentIndexChanged(int index);
|
||||
void on_buttonCursorType_toggled(bool value);
|
||||
void on_pushButtonTranslation_clicked();
|
||||
void on_pushButtonResetTutorialTouchscreen_clicked();
|
||||
void on_buttonRepositoryDefault_toggled(bool value);
|
||||
void on_buttonRepositoryExtra_toggled(bool value);
|
||||
void on_lineEditRepositoryExtra_textEdited(const QString &arg1);
|
||||
|
@ -85,6 +85,9 @@ void BonusList::stackBonuses()
|
||||
|
||||
int BonusList::totalValue() const
|
||||
{
|
||||
if (bonuses.empty())
|
||||
return 0;
|
||||
|
||||
struct BonusCollection
|
||||
{
|
||||
int base = 0;
|
||||
@ -96,63 +99,65 @@ int BonusList::totalValue() const
|
||||
int indepMax = std::numeric_limits<int>::min();
|
||||
};
|
||||
|
||||
auto percent = [](int64_t base, int64_t percent) -> int {
|
||||
return static_cast<int>(std::clamp<int64_t>((base * (100 + percent)) / 100, std::numeric_limits<int>::min(), std::numeric_limits<int>::max()));
|
||||
auto percent = [](int base, int percent) -> int {
|
||||
return (static_cast<int64_t>(base) * (100 + percent)) / 100;
|
||||
};
|
||||
std::array <BonusCollection, vstd::to_underlying(BonusSource::NUM_BONUS_SOURCE)> sources = {};
|
||||
BonusCollection any;
|
||||
|
||||
BonusCollection accumulated;
|
||||
bool hasIndepMax = false;
|
||||
bool hasIndepMin = false;
|
||||
|
||||
std::array<int, vstd::to_underlying(BonusSource::NUM_BONUS_SOURCE)> percentToSource = {};
|
||||
|
||||
for(const auto & b : bonuses)
|
||||
{
|
||||
switch(b->valType)
|
||||
{
|
||||
case BonusValueType::BASE_NUMBER:
|
||||
sources[vstd::to_underlying(b->source)].base += b->val;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_ALL:
|
||||
sources[vstd::to_underlying(b->source)].percentToAll += b->val;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_BASE:
|
||||
sources[vstd::to_underlying(b->source)].percentToBase += b->val;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_SOURCE:
|
||||
sources[vstd::to_underlying(b->source)].percentToSource += b->val;
|
||||
break;
|
||||
percentToSource[vstd::to_underlying(b->source)] += b->val;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_TARGET_TYPE:
|
||||
sources[vstd::to_underlying(b->targetSourceType)].percentToSource += b->val;
|
||||
break;
|
||||
case BonusValueType::ADDITIVE_VALUE:
|
||||
sources[vstd::to_underlying(b->source)].additive += b->val;
|
||||
break;
|
||||
case BonusValueType::INDEPENDENT_MAX:
|
||||
hasIndepMax = true;
|
||||
vstd::amax(sources[vstd::to_underlying(b->source)].indepMax, b->val);
|
||||
break;
|
||||
case BonusValueType::INDEPENDENT_MIN:
|
||||
hasIndepMin = true;
|
||||
vstd::amin(sources[vstd::to_underlying(b->source)].indepMin, b->val);
|
||||
percentToSource[vstd::to_underlying(b->targetSourceType)] += b->val;
|
||||
break;
|
||||
}
|
||||
}
|
||||
for(const auto & src : sources)
|
||||
{
|
||||
any.base += percent(src.base, src.percentToSource);
|
||||
any.percentToBase += percent(src.percentToBase, src.percentToSource);
|
||||
any.percentToAll += percent(src.percentToAll, src.percentToSource);
|
||||
any.additive += percent(src.additive, src.percentToSource);
|
||||
if(hasIndepMin)
|
||||
vstd::amin(any.indepMin, percent(src.indepMin, src.percentToSource));
|
||||
if(hasIndepMax)
|
||||
vstd::amax(any.indepMax, percent(src.indepMax, src.percentToSource));
|
||||
}
|
||||
any.base = percent(any.base, any.percentToBase);
|
||||
any.base += any.additive;
|
||||
auto valFirst = percent(any.base ,any.percentToAll);
|
||||
|
||||
if(hasIndepMin && hasIndepMax && any.indepMin < any.indepMax)
|
||||
any.indepMax = any.indepMin;
|
||||
for(const auto & b : bonuses)
|
||||
{
|
||||
int sourceIndex = vstd::to_underlying(b->source);
|
||||
int valModified = percent(b->val, percentToSource[sourceIndex]);
|
||||
|
||||
switch(b->valType)
|
||||
{
|
||||
case BonusValueType::BASE_NUMBER:
|
||||
accumulated.base += valModified;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_ALL:
|
||||
accumulated.percentToAll += valModified;
|
||||
break;
|
||||
case BonusValueType::PERCENT_TO_BASE:
|
||||
accumulated.percentToBase += valModified;
|
||||
break;
|
||||
case BonusValueType::ADDITIVE_VALUE:
|
||||
accumulated.additive += valModified;
|
||||
break;
|
||||
case BonusValueType::INDEPENDENT_MAX:
|
||||
hasIndepMax = true;
|
||||
vstd::amax(accumulated.indepMax, valModified);
|
||||
break;
|
||||
case BonusValueType::INDEPENDENT_MIN:
|
||||
hasIndepMin = true;
|
||||
vstd::amin(accumulated.indepMin, valModified);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
accumulated.base = percent(accumulated.base, accumulated.percentToBase);
|
||||
accumulated.base += accumulated.additive;
|
||||
auto valFirst = percent(accumulated.base ,accumulated.percentToAll);
|
||||
|
||||
if(hasIndepMin && hasIndepMax && accumulated.indepMin < accumulated.indepMax)
|
||||
accumulated.indepMax = accumulated.indepMin;
|
||||
|
||||
const int notIndepBonuses = static_cast<int>(std::count_if(bonuses.cbegin(), bonuses.cend(), [](const std::shared_ptr<Bonus>& b)
|
||||
{
|
||||
@ -160,9 +165,9 @@ int BonusList::totalValue() const
|
||||
}));
|
||||
|
||||
if(notIndepBonuses)
|
||||
return std::clamp(valFirst, any.indepMax, any.indepMin);
|
||||
return std::clamp(valFirst, accumulated.indepMax, accumulated.indepMin);
|
||||
|
||||
return hasIndepMin ? any.indepMin : hasIndepMax ? any.indepMax : 0;
|
||||
return hasIndepMin ? accumulated.indepMin : hasIndepMax ? accumulated.indepMax : 0;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bonus> BonusList::getFirst(const CSelector &select)
|
||||
|
@ -191,7 +191,7 @@ ZipArchive::ZipArchive(const boost::filesystem::path & from)
|
||||
#endif
|
||||
|
||||
if (archive == nullptr)
|
||||
throw std::runtime_error("Failed to open file '" + from.string() + "' - unable to list files!");
|
||||
throw std::runtime_error("Failed to open file '" + from.string());
|
||||
}
|
||||
|
||||
ZipArchive::~ZipArchive()
|
||||
|
@ -102,7 +102,10 @@ static std::string enumCheck(JsonValidator & validator, const JsonNode & baseSch
|
||||
if (data == enumEntry)
|
||||
return "";
|
||||
}
|
||||
return validator.makeErrorMessage("Key must have one of predefined values");
|
||||
|
||||
std::string errorMessage = "Key must have one of predefined values:" + schema.toCompactString();
|
||||
|
||||
return validator.makeErrorMessage(errorMessage);
|
||||
}
|
||||
|
||||
static std::string constCheck(JsonValidator & validator, const JsonNode & baseSchema, const JsonNode & schema, const JsonNode & data)
|
||||
|
@ -157,7 +157,6 @@ void CLogger::log(ELogLevel::ELogLevel level, const boost::format & fmt) const
|
||||
|
||||
ELogLevel::ELogLevel CLogger::getLevel() const
|
||||
{
|
||||
TLockGuard _(mx);
|
||||
return level;
|
||||
}
|
||||
|
||||
|
@ -38,6 +38,7 @@ void CMapUndoManager::redo()
|
||||
|
||||
void CMapUndoManager::clearAll()
|
||||
{
|
||||
//FIXME: Will crash if an object was added twice to actions
|
||||
undoStack.clear();
|
||||
redoStack.clear();
|
||||
onUndoRedo();
|
||||
|
@ -1231,7 +1231,7 @@ void RemoveObject::applyGs(CGameState *gs)
|
||||
|
||||
gs->map->instanceNames.erase(obj->instanceName);
|
||||
gs->map->objects[objectID.getNum()].dellNull();
|
||||
gs->map->calculateGuardingGreaturePositions();
|
||||
gs->map->calculateGuardingGreaturePositions();//FIXME: excessive, update only affected tiles
|
||||
}
|
||||
|
||||
static int getDir(const int3 & src, const int3 & dst)
|
||||
|
@ -49,7 +49,7 @@ bool CPathfinderHelper::canMoveFromNode(const PathNodeInfo & source) const
|
||||
return true;
|
||||
}
|
||||
|
||||
void CPathfinderHelper::calculateNeighbourTiles(std::vector<int3> & result, const PathNodeInfo & source) const
|
||||
void CPathfinderHelper::calculateNeighbourTiles(NeighbourTilesVector & result, const PathNodeInfo & source) const
|
||||
{
|
||||
result.clear();
|
||||
|
||||
@ -239,9 +239,9 @@ void CPathfinder::calculatePaths()
|
||||
logAi->trace("CPathfinder finished with %s iterations", std::to_string(counter));
|
||||
}
|
||||
|
||||
std::vector<int3> CPathfinderHelper::getAllowedTeleportChannelExits(const TeleportChannelID & channelID) const
|
||||
TeleporterTilesVector CPathfinderHelper::getAllowedTeleportChannelExits(const TeleportChannelID & channelID) const
|
||||
{
|
||||
std::vector<int3> allowedExits;
|
||||
TeleporterTilesVector allowedExits;
|
||||
|
||||
for(const auto & objId : getTeleportChannelExits(channelID, hero->tempOwner))
|
||||
{
|
||||
@ -262,9 +262,9 @@ std::vector<int3> CPathfinderHelper::getAllowedTeleportChannelExits(const Telepo
|
||||
return allowedExits;
|
||||
}
|
||||
|
||||
std::vector<int3> CPathfinderHelper::getCastleGates(const PathNodeInfo & source) const
|
||||
TeleporterTilesVector CPathfinderHelper::getCastleGates(const PathNodeInfo & source) const
|
||||
{
|
||||
std::vector<int3> allowedExits;
|
||||
TeleporterTilesVector allowedExits;
|
||||
|
||||
auto towns = getPlayerState(hero->tempOwner)->towns;
|
||||
for(const auto & town : towns)
|
||||
@ -279,9 +279,9 @@ std::vector<int3> CPathfinderHelper::getCastleGates(const PathNodeInfo & source)
|
||||
return allowedExits;
|
||||
}
|
||||
|
||||
std::vector<int3> CPathfinderHelper::getTeleportExits(const PathNodeInfo & source) const
|
||||
TeleporterTilesVector CPathfinderHelper::getTeleportExits(const PathNodeInfo & source) const
|
||||
{
|
||||
std::vector<int3> teleportationExits;
|
||||
TeleporterTilesVector teleportationExits;
|
||||
|
||||
const auto * objTeleport = dynamic_cast<const CGTeleport *>(source.nodeObject);
|
||||
if(isAllowedTeleportEntrance(objTeleport))
|
||||
@ -578,7 +578,7 @@ int CPathfinderHelper::getMaxMovePoints(const EPathfindingLayer & layer) const
|
||||
void CPathfinderHelper::getNeighbours(
|
||||
const TerrainTile & srcTile,
|
||||
const int3 & srcCoord,
|
||||
std::vector<int3> & vec,
|
||||
NeighbourTilesVector & vec,
|
||||
const boost::logic::tribool & onLand,
|
||||
const bool limitCoastSailing) const
|
||||
{
|
||||
@ -702,8 +702,8 @@ int CPathfinderHelper::getMovementCost(
|
||||
constexpr auto maxCostOfOneStep = static_cast<int>(175 * M_SQRT2); // diagonal move on Swamp - 247 MP
|
||||
if(checkLast && left > 0 && left <= maxCostOfOneStep) //it might be the last tile - if no further move possible we take all move points
|
||||
{
|
||||
std::vector<int3> vec;
|
||||
vec.reserve(8); //optimization
|
||||
NeighbourTilesVector vec;
|
||||
|
||||
getNeighbours(*dt, dst, vec, ct->terType->isLand(), true);
|
||||
for(const auto & elem : vec)
|
||||
{
|
||||
|
@ -13,12 +13,23 @@
|
||||
#include "../IGameCallback.h"
|
||||
#include "../bonuses/BonusEnum.h"
|
||||
|
||||
#include <boost/container/static_vector.hpp>
|
||||
#include <boost/container/small_vector.hpp>
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CGWhirlpool;
|
||||
struct TurnInfo;
|
||||
struct PathfinderOptions;
|
||||
|
||||
// Optimized storage - tile can have 0-8 neighbour tiles
|
||||
// static_vector uses fixed, preallocated storage (capacity) and dynamic size
|
||||
// this avoid dynamic allocations on huge number of neighbour list queries
|
||||
using NeighbourTilesVector = boost::container::static_vector<int3, 8>;
|
||||
|
||||
// Optimized storage to minimize dynamic allocations - most of teleporters have only one exit, but some may have more (premade maps, castle gates)
|
||||
using TeleporterTilesVector = boost::container::small_vector<int3, 4>;
|
||||
|
||||
class DLL_LINKAGE CPathfinder
|
||||
{
|
||||
public:
|
||||
@ -87,22 +98,22 @@ public:
|
||||
bool hasBonusOfType(BonusType type) const;
|
||||
int getMaxMovePoints(const EPathfindingLayer & layer) const;
|
||||
|
||||
std::vector<int3> getCastleGates(const PathNodeInfo & source) const;
|
||||
TeleporterTilesVector getCastleGates(const PathNodeInfo & source) const;
|
||||
bool isAllowedTeleportEntrance(const CGTeleport * obj) const;
|
||||
std::vector<int3> getAllowedTeleportChannelExits(const TeleportChannelID & channelID) const;
|
||||
TeleporterTilesVector getAllowedTeleportChannelExits(const TeleportChannelID & channelID) const;
|
||||
bool addTeleportTwoWay(const CGTeleport * obj) const;
|
||||
bool addTeleportOneWay(const CGTeleport * obj) const;
|
||||
bool addTeleportOneWayRandom(const CGTeleport * obj) const;
|
||||
bool addTeleportWhirlpool(const CGWhirlpool * obj) const;
|
||||
bool canMoveBetween(const int3 & a, const int3 & b) const; //checks only for visitable objects that may make moving between tiles impossible, not other conditions (like tiles itself accessibility)
|
||||
|
||||
void calculateNeighbourTiles(std::vector<int3> & result, const PathNodeInfo & source) const;
|
||||
std::vector<int3> getTeleportExits(const PathNodeInfo & source) const;
|
||||
void calculateNeighbourTiles(NeighbourTilesVector & result, const PathNodeInfo & source) const;
|
||||
TeleporterTilesVector getTeleportExits(const PathNodeInfo & source) const;
|
||||
|
||||
void getNeighbours(
|
||||
const TerrainTile & srcTile,
|
||||
const int3 & srcCoord,
|
||||
std::vector<int3> & vec,
|
||||
NeighbourTilesVector & vec,
|
||||
const boost::logic::tribool & onLand,
|
||||
const bool limitCoastSailing) const;
|
||||
|
||||
|
@ -40,7 +40,7 @@ void NodeStorage::initialize(const PathfinderOptions & options, const CGameState
|
||||
{
|
||||
for(pos.y=0; pos.y < sizes.y; ++pos.y)
|
||||
{
|
||||
const TerrainTile tile = gs->map->getTile(pos);
|
||||
const TerrainTile & tile = gs->map->getTile(pos);
|
||||
if(tile.terType->isWater())
|
||||
{
|
||||
resetTile(pos, ELayer::SAIL, PathfinderUtil::evaluateAccessibility<ELayer::SAIL>(pos, tile, fow, player, gs));
|
||||
@ -67,10 +67,9 @@ void NodeStorage::calculateNeighbours(
|
||||
const PathfinderConfig * pathfinderConfig,
|
||||
const CPathfinderHelper * pathfinderHelper)
|
||||
{
|
||||
std::vector<int3> accessibleNeighbourTiles;
|
||||
NeighbourTilesVector accessibleNeighbourTiles;
|
||||
|
||||
result.clear();
|
||||
accessibleNeighbourTiles.reserve(8);
|
||||
|
||||
pathfinderHelper->calculateNeighbourTiles(accessibleNeighbourTiles, source);
|
||||
|
||||
|
@ -106,12 +106,12 @@ void Rewardable::Interface::grantRewardBeforeLevelup(IGameCallback * cb, const R
|
||||
|
||||
for(const auto & entry : info.reward.secondary)
|
||||
{
|
||||
int current = hero->getSecSkillLevel(entry.first);
|
||||
if( (current != 0 && current < entry.second) ||
|
||||
(hero->canLearnSkill() ))
|
||||
{
|
||||
cb->changeSecSkill(hero, entry.first, entry.second);
|
||||
}
|
||||
auto currentLevel = static_cast<MasteryLevel::Type>(hero->getSecSkillLevel(entry.first));
|
||||
if(currentLevel == MasteryLevel::EXPERT)
|
||||
continue;
|
||||
|
||||
if(currentLevel != MasteryLevel::NONE || hero->canLearnSkill())
|
||||
cb->changeSecSkill(hero, entry.first, entry.second, false);
|
||||
}
|
||||
|
||||
for(int i=0; i< info.reward.primary.size(); i++)
|
||||
|
@ -103,7 +103,13 @@ void Rewardable::Reward::loadComponents(std::vector<Component> & comps, const CG
|
||||
}
|
||||
|
||||
for(const auto & entry : secondary)
|
||||
comps.emplace_back(ComponentType::SEC_SKILL, entry.first, entry.second);
|
||||
{
|
||||
auto skillID = entry.first;
|
||||
int levelsGained = entry.second;
|
||||
int currentLevel = h->getSecSkillLevel(skillID);
|
||||
int finalLevel = std::min(static_cast<int>(MasteryLevel::EXPERT), currentLevel + levelsGained);
|
||||
comps.emplace_back(ComponentType::SEC_SKILL, entry.first, finalLevel);
|
||||
}
|
||||
|
||||
for(const auto & entry : artifacts)
|
||||
comps.emplace_back(ComponentType::ARTIFACT, entry);
|
||||
|
@ -64,6 +64,22 @@ std::vector<CGObjectInstance*> QuestArtifactPlacer::getPossibleArtifactsToReplac
|
||||
return artifactsToReplace;
|
||||
}
|
||||
|
||||
CGObjectInstance * QuestArtifactPlacer::drawObjectToReplace()
|
||||
{
|
||||
RecursiveLock lock(externalAccessMutex);
|
||||
|
||||
if (artifactsToReplace.empty())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ret = *RandomGeneratorUtil::nextItem(artifactsToReplace, zone.getRand());
|
||||
vstd::erase_if_present(artifactsToReplace, ret);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
void QuestArtifactPlacer::findZonesForQuestArts()
|
||||
{
|
||||
const auto& distances = generator.getZonePlacer()->getDistanceMap().at(zone.getId());
|
||||
@ -87,14 +103,14 @@ void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator & rand)
|
||||
for (auto zone : questArtZones)
|
||||
{
|
||||
auto* qap = zone->getModificator<QuestArtifactPlacer>();
|
||||
std::vector<CGObjectInstance *> artifactsToReplace = qap->getPossibleArtifactsToReplace();
|
||||
if (artifactsToReplace.empty())
|
||||
|
||||
auto objectToReplace = qap->drawObjectToReplace();
|
||||
if (!objectToReplace)
|
||||
continue;
|
||||
|
||||
auto artifactToReplace = *RandomGeneratorUtil::nextItem(artifactsToReplace, rand);
|
||||
logGlobal->trace("Replacing %s at %s with the quest artifact %s",
|
||||
artifactToReplace->getObjectName(),
|
||||
artifactToReplace->getPosition().toString(),
|
||||
objectToReplace->getObjectName(),
|
||||
objectToReplace->getPosition().toString(),
|
||||
VLC->artifacts()->getById(artifactToPlace)->getNameTranslated());
|
||||
|
||||
//Update appearance. Terrain is irrelevant.
|
||||
@ -103,24 +119,15 @@ void QuestArtifactPlacer::placeQuestArtifacts(CRandomGenerator & rand)
|
||||
auto templates = handler->getTemplates();
|
||||
//artifactToReplace->appearance = templates.front();
|
||||
newObj->appearance = templates.front();
|
||||
newObj->pos = artifactToReplace->pos;
|
||||
newObj->pos = objectToReplace->pos;
|
||||
mapProxy->insertObject(newObj);
|
||||
|
||||
for (auto z : map.getZones())
|
||||
{
|
||||
//Every qap has its OWN collection of artifacts
|
||||
auto * localQap = zone->getModificator<QuestArtifactPlacer>();
|
||||
if (localQap)
|
||||
{
|
||||
localQap->dropReplacedArtifact(artifactToReplace);
|
||||
}
|
||||
}
|
||||
mapProxy->removeObject(artifactToReplace);
|
||||
mapProxy->removeObject(objectToReplace);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Unused?
|
||||
void QuestArtifactPlacer::dropReplacedArtifact(CGObjectInstance* obj)
|
||||
{
|
||||
RecursiveLock lock(externalAccessMutex);
|
||||
|
@ -31,6 +31,7 @@ public:
|
||||
void addQuestArtifact(const ArtifactID& id);
|
||||
void removeQuestArtifact(const ArtifactID& id);
|
||||
void rememberPotentialArtifactToReplace(CGObjectInstance* obj);
|
||||
CGObjectInstance * drawObjectToReplace();
|
||||
std::vector<CGObjectInstance*> getPossibleArtifactsToReplace() const;
|
||||
void placeQuestArtifacts(CRandomGenerator & rand);
|
||||
void dropReplacedArtifact(CGObjectInstance* obj);
|
||||
|
@ -87,8 +87,8 @@ bool RoadPlacer::createRoad(const int3 & destination)
|
||||
{
|
||||
ret *= VISITABLE_PENALTY;
|
||||
}
|
||||
float dist = border.distance(dst);
|
||||
if(dist > 1)
|
||||
float dist = border.distanceSqr(dst);
|
||||
if(dist > 1.0f)
|
||||
{
|
||||
ret /= dist;
|
||||
}
|
||||
|
@ -107,24 +107,57 @@ set(editor_FORMS
|
||||
inspector/portraitwidget.ui
|
||||
)
|
||||
|
||||
set(editor_TS
|
||||
translation/chinese.ts
|
||||
translation/czech.ts
|
||||
translation/english.ts
|
||||
translation/french.ts
|
||||
translation/german.ts
|
||||
translation/polish.ts
|
||||
translation/portuguese.ts
|
||||
translation/russian.ts
|
||||
translation/spanish.ts
|
||||
translation/ukrainian.ts
|
||||
translation/vietnamese.ts
|
||||
set(editor_RESOURCES
|
||||
resources.qrc
|
||||
)
|
||||
|
||||
assign_source_group(${editor_SRCS} ${editor_HEADERS} mapeditor.rc)
|
||||
set(translationsDir "translation")
|
||||
set(editor_TS
|
||||
"${translationsDir}/chinese.ts"
|
||||
"${translationsDir}/czech.ts"
|
||||
"${translationsDir}/english.ts"
|
||||
"${translationsDir}/french.ts"
|
||||
"${translationsDir}/german.ts"
|
||||
"${translationsDir}/polish.ts"
|
||||
"${translationsDir}/portuguese.ts"
|
||||
"${translationsDir}/russian.ts"
|
||||
"${translationsDir}/spanish.ts"
|
||||
"${translationsDir}/ukrainian.ts"
|
||||
"${translationsDir}/vietnamese.ts"
|
||||
)
|
||||
if(ENABLE_TRANSLATIONS)
|
||||
if(TARGET Qt5::Core)
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${translationsDir}")
|
||||
set_source_files_properties(${editor_TS} PROPERTIES OUTPUT_LOCATION "${translationsDir}")
|
||||
qt5_add_translation(editor_QM ${editor_TS})
|
||||
|
||||
set(translationsResource "${CMAKE_CURRENT_BINARY_DIR}/translations.qrc")
|
||||
list(APPEND editor_RESOURCES "${translationsResource}")
|
||||
|
||||
set(rccQmFiles "")
|
||||
foreach(qmFile ${editor_QM})
|
||||
string(APPEND rccQmFiles "<file>${qmFile}</file>\n")
|
||||
endforeach()
|
||||
file(WRITE "${translationsResource}"
|
||||
"<!DOCTYPE RCC>
|
||||
<RCC version=\"1.0\">
|
||||
<qresource prefix=\"/\">
|
||||
${rccQmFiles}
|
||||
</qresource>
|
||||
</RCC>"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(editor_ICON mapeditor.rc)
|
||||
endif()
|
||||
|
||||
assign_source_group(${editor_SRCS} ${editor_HEADERS} ${editor_RESOURCES} ${editor_TS} ${editor_ICON})
|
||||
|
||||
# Tell CMake to run moc when necessary:
|
||||
set(CMAKE_AUTOMOC ON)
|
||||
set(CMAKE_AUTOUIC ON)
|
||||
set(CMAKE_AUTORCC ON)
|
||||
|
||||
if(POLICY CMP0071)
|
||||
cmake_policy(SET CMP0071 NEW)
|
||||
@ -134,33 +167,28 @@ endif()
|
||||
# to always look for includes there:
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
if(TARGET Qt6::Core)
|
||||
qt_wrap_ui(editor_UI_HEADERS ${editor_FORMS})
|
||||
if(ENABLE_SINGLE_APP_BUILD OR ANDROID)
|
||||
add_library(vcmieditor OBJECT ${editor_QM})
|
||||
else()
|
||||
qt5_wrap_ui(editor_UI_HEADERS ${editor_FORMS})
|
||||
if(ENABLE_TRANSLATIONS)
|
||||
set_source_files_properties(${editor_TS} PROPERTIES OUTPUT_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/translation)
|
||||
qt5_add_translation( editor_QM ${editor_TS} )
|
||||
endif()
|
||||
add_executable(vcmieditor WIN32 ${editor_QM} ${editor_SRCS} ${editor_HEADERS} ${editor_UI_HEADERS} ${editor_ICON})
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
set(editor_ICON mapeditor.rc)
|
||||
endif()
|
||||
|
||||
add_executable(vcmieditor WIN32 ${editor_QM} ${editor_SRCS} ${editor_HEADERS} ${editor_UI_HEADERS} ${editor_ICON})
|
||||
|
||||
if(TARGET Qt6::Core)
|
||||
if(ENABLE_TRANSLATIONS)
|
||||
set_source_files_properties(${editor_TS} PROPERTIES OUTPUT_LOCATION ${CMAKE_CURRENT_BINARY_DIR}/translation)
|
||||
if(ENABLE_TRANSLATIONS)
|
||||
if(TARGET Qt6::Core)
|
||||
qt_add_translations(vcmieditor
|
||||
TS_FILES ${editor_TS}
|
||||
QM_FILES_OUTPUT_VARIABLE editor_QM
|
||||
RESOURCE_PREFIX "/${translationsDir}"
|
||||
INCLUDE_DIRECTORIES
|
||||
${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_sources(vcmieditor PRIVATE
|
||||
${editor_SRCS}
|
||||
${editor_HEADERS}
|
||||
${editor_RESOURCES}
|
||||
)
|
||||
|
||||
if(WIN32)
|
||||
set_target_properties(vcmieditor
|
||||
PROPERTIES
|
||||
@ -189,23 +217,15 @@ target_include_directories(vcmieditor
|
||||
vcmi_set_output_dir(vcmieditor "")
|
||||
enable_pch(vcmieditor)
|
||||
|
||||
# Copy to build directory for easier debugging
|
||||
add_custom_command(TARGET vcmieditor POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/mapeditor/
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${CMAKE_SOURCE_DIR}/mapeditor/icons ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/mapeditor/icons
|
||||
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_SOURCE_DIR}/cmake_modules/create_link.cmake ${CMAKE_CURRENT_BINARY_DIR}/translation ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/mapeditor/translation
|
||||
)
|
||||
|
||||
install(TARGETS vcmieditor DESTINATION ${BIN_DIR})
|
||||
# copy whole directory
|
||||
install(DIRECTORY icons DESTINATION ${DATA_DIR}/mapeditor)
|
||||
install(FILES ${editor_QM} DESTINATION ${DATA_DIR}/mapeditor/translation)
|
||||
|
||||
# Install icons and desktop file on Linux
|
||||
if(NOT WIN32 AND NOT APPLE)
|
||||
foreach(iconSize 32 48 64 128 256)
|
||||
install(FILES "icons/mapeditor.${iconSize}x${iconSize}.png"
|
||||
DESTINATION "share/icons/hicolor/${iconSize}x${iconSize}/apps"
|
||||
RENAME vcmieditor.png
|
||||
)
|
||||
endforeach()
|
||||
install(FILES "vcmieditor.desktop" DESTINATION share/applications)
|
||||
install(FILES "icons/mapeditor.32x32.png" DESTINATION share/icons/hicolor/32x32/apps RENAME vcmieditor.png)
|
||||
install(FILES "icons/mapeditor.48x48.png" DESTINATION share/icons/hicolor/48x48/apps RENAME vcmieditor.png)
|
||||
install(FILES "icons/mapeditor.64x64.png" DESTINATION share/icons/hicolor/64x64/apps RENAME vcmieditor.png)
|
||||
install(FILES "icons/mapeditor.128x128.png" DESTINATION share/icons/hicolor/128x128/apps RENAME vcmieditor.png)
|
||||
install(FILES "icons/mapeditor.256x256.png" DESTINATION share/icons/hicolor/256x256/apps RENAME vcmieditor.png)
|
||||
endif()
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
@ -133,25 +133,17 @@ void MainWindow::parseCommandLine(ExtractionOptions & extractionOptions)
|
||||
void MainWindow::loadTranslation()
|
||||
{
|
||||
#ifdef ENABLE_QT_TRANSLATIONS
|
||||
std::string translationFile = settings["general"]["language"].String() + ".qm";
|
||||
const std::string translationFile = settings["general"]["language"].String() + ".qm";
|
||||
logGlobal->info("Loading translation '%s'", translationFile);
|
||||
|
||||
QVector<QString> searchPaths;
|
||||
|
||||
for(auto const & string : VCMIDirs::get().dataPaths())
|
||||
searchPaths.push_back(pathToQString(string / "mapeditor" / "translation" / translationFile));
|
||||
searchPaths.push_back(pathToQString(VCMIDirs::get().userDataPath() / "mapeditor" / "translation" / translationFile));
|
||||
|
||||
for(auto const & string : boost::adaptors::reverse(searchPaths))
|
||||
if (!translator.load(QString{":/translation/%1"}.arg(translationFile.c_str())))
|
||||
{
|
||||
if (translator.load(string))
|
||||
{
|
||||
if (!qApp->installTranslator(&translator))
|
||||
logGlobal->error("Failed to install translator");
|
||||
return;
|
||||
}
|
||||
logGlobal->error("Failed to load translation");
|
||||
return;
|
||||
}
|
||||
|
||||
logGlobal->error("Failed to find translation");
|
||||
if (!qApp->installTranslator(&translator))
|
||||
logGlobal->error("Failed to install translator");
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -163,13 +155,9 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
// Set current working dir to executable folder.
|
||||
// This is important on Mac for relative paths to work inside DMG.
|
||||
QDir::setCurrent(QApplication::applicationDirPath());
|
||||
|
||||
for(auto & string : VCMIDirs::get().dataPaths())
|
||||
QDir::addSearchPath("icons", pathToQString(string / "mapeditor" / "icons"));
|
||||
QDir::addSearchPath("icons", pathToQString(VCMIDirs::get().userDataPath() / "mapeditor" / "icons"));
|
||||
|
||||
|
||||
new QShortcut(QKeySequence("Backspace"), this, SLOT(on_actionErase_triggered()));
|
||||
|
||||
|
||||
ExtractionOptions extractionOptions;
|
||||
parseCommandLine(extractionOptions);
|
||||
|
||||
@ -206,6 +194,36 @@ MainWindow::MainWindow(QWidget* parent) :
|
||||
loadTranslation();
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
setWindowIcon(QIcon{":/icons/menu-game.png"});
|
||||
ui->toolBrush->setIcon(QIcon{":/icons/brush-1.png"});
|
||||
ui->toolBrush2->setIcon(QIcon{":/icons/brush-2.png"});
|
||||
ui->toolBrush4->setIcon(QIcon{":/icons/brush-4.png"});
|
||||
ui->toolLasso->setIcon(QIcon{":/icons/tool-lasso.png"});
|
||||
ui->toolLine->setIcon(QIcon{":/icons/tool-line.png"});
|
||||
ui->toolArea->setIcon(QIcon{":/icons/tool-area.png"});
|
||||
ui->toolFill->setIcon(QIcon{":/icons/tool-fill.png"});
|
||||
ui->toolSelect->setIcon(QIcon{":/icons/tool-select.png"});
|
||||
ui->actionOpen->setIcon(QIcon{":/icons/document-open.png"});
|
||||
ui->actionSave->setIcon(QIcon{":/icons/document-save.png"});
|
||||
ui->actionNew->setIcon(QIcon{":/icons/document-new.png"});
|
||||
ui->actionLevel->setIcon(QIcon{":/icons/toggle-underground.png"});
|
||||
ui->actionPass->setIcon(QIcon{":/icons/toggle-pass.png"});
|
||||
ui->actionCut->setIcon(QIcon{":/icons/edit-cut.png"});
|
||||
ui->actionCopy->setIcon(QIcon{":/icons/edit-copy.png"});
|
||||
ui->actionPaste->setIcon(QIcon{":/icons/edit-paste.png"});
|
||||
ui->actionFill->setIcon(QIcon{":/icons/fill-obstacles.png"});
|
||||
ui->actionGrid->setIcon(QIcon{":/icons/toggle-grid.png"});
|
||||
ui->actionUndo->setIcon(QIcon{":/icons/edit-undo.png"});
|
||||
ui->actionRedo->setIcon(QIcon{":/icons/edit-redo.png"});
|
||||
ui->actionErase->setIcon(QIcon{":/icons/edit-clear.png"});
|
||||
ui->actionTranslations->setIcon(QIcon{":/icons/translations.png"});
|
||||
ui->actionLock->setIcon(QIcon{":/icons/lock-closed.png"});
|
||||
ui->actionUnlock->setIcon(QIcon{":/icons/lock-open.png"});
|
||||
ui->actionZoom_in->setIcon(QIcon{":/icons/zoom_plus.png"});
|
||||
ui->actionZoom_out->setIcon(QIcon{":/icons/zoom_minus.png"});
|
||||
ui->actionZoom_reset->setIcon(QIcon{":/icons/zoom_zero.png"});
|
||||
|
||||
loadUserSettings(); //For example window size
|
||||
setTitle();
|
||||
|
||||
|
@ -511,10 +511,6 @@
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:brush-1.png</normaloff>icons:brush-1.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
@ -558,10 +554,6 @@
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:brush-2.png</normaloff>icons:brush-2.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
@ -605,10 +597,6 @@
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:brush-4.png</normaloff>icons:brush-4.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
@ -652,10 +640,6 @@
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:brush-3.png</normaloff>icons:brush-3.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -697,10 +681,6 @@
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:brush-7.png</normaloff>icons:brush-7.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -732,10 +712,6 @@
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:brush-5.png</normaloff>icons:brush-5.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -770,10 +746,6 @@
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:brush-6.png</normaloff>icons:brush-6.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -808,10 +780,6 @@
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:brush-0.png</normaloff>icons:brush-0.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@ -1044,10 +1012,6 @@
|
||||
</widget>
|
||||
</widget>
|
||||
<action name="actionOpen">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:document-open.png</normaloff>icons:document-open.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open</string>
|
||||
</property>
|
||||
@ -1056,10 +1020,6 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSave">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:document-save.png</normaloff>icons:document-save.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Save</string>
|
||||
</property>
|
||||
@ -1068,10 +1028,6 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionNew">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:document-new.png</normaloff>icons:document-new.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>New</string>
|
||||
</property>
|
||||
@ -1088,10 +1044,6 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLevel">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:toggle-underground.png</normaloff>icons:toggle-underground.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>U/G</string>
|
||||
</property>
|
||||
@ -1106,10 +1058,6 @@
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:toggle-pass.png</normaloff>icons:toggle-pass.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Pass</string>
|
||||
</property>
|
||||
@ -1118,10 +1066,6 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCut">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:edit-cut.png</normaloff>icons:edit-cut.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cut</string>
|
||||
</property>
|
||||
@ -1130,10 +1074,6 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCopy">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:edit-copy.png</normaloff>icons:edit-copy.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy</string>
|
||||
</property>
|
||||
@ -1142,10 +1082,6 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPaste">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:edit-paste.png</normaloff>icons:edit-paste.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Paste</string>
|
||||
</property>
|
||||
@ -1154,10 +1090,6 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionFill">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:fill-obstacles.png</normaloff>icons:fill-obstacles.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Fill</string>
|
||||
</property>
|
||||
@ -1172,10 +1104,6 @@
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:toggle-grid.png</normaloff>icons:toggle-grid.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Grid</string>
|
||||
</property>
|
||||
@ -1212,10 +1140,6 @@
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:edit-undo.png</normaloff>icons:edit-undo.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Undo</string>
|
||||
</property>
|
||||
@ -1233,10 +1157,6 @@
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:edit-redo.png</normaloff>icons:edit-redo.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Redo</string>
|
||||
</property>
|
||||
@ -1251,10 +1171,6 @@
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:edit-clear.png</normaloff>icons:edit-clear.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Erase</string>
|
||||
</property>
|
||||
@ -1400,10 +1316,6 @@
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:translations.png</normaloff>icons:translations.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Translations</string>
|
||||
</property>
|
||||
@ -1420,10 +1332,6 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionLock">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:lock-closed.png</normaloff>icons:lock-closed.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Lock</string>
|
||||
</property>
|
||||
@ -1435,10 +1343,6 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionUnlock">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:lock-open.png</normaloff>icons:lock-open.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Unlock</string>
|
||||
</property>
|
||||
@ -1450,10 +1354,6 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionZoom_in">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:zoom_plus.png</normaloff>icons:zoom_plus.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Zoom in</string>
|
||||
</property>
|
||||
@ -1462,10 +1362,6 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionZoom_out">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:zoom_minus.png</normaloff>icons:zoom_minus.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Zoom out</string>
|
||||
</property>
|
||||
@ -1474,10 +1370,6 @@
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionZoom_reset">
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:zoom_zero.png</normaloff>icons:zoom_zero.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Zoom reset</string>
|
||||
</property>
|
||||
|
45
mapeditor/resources.qrc
Normal file
@ -0,0 +1,45 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>icons/brush-1.png</file>
|
||||
<file>icons/brush-2.png</file>
|
||||
<file>icons/brush-4.png</file>
|
||||
<file>icons/document-new.png</file>
|
||||
<file>icons/document-open.png</file>
|
||||
<file>icons/document-save.png</file>
|
||||
<file>icons/edit-clear.png</file>
|
||||
<file>icons/edit-copy.png</file>
|
||||
<file>icons/edit-cut.png</file>
|
||||
<file>icons/edit-paste.png</file>
|
||||
<file>icons/edit-redo.png</file>
|
||||
<file>icons/edit-undo.png</file>
|
||||
<file>icons/fill-obstacles.png</file>
|
||||
<file>icons/lock-closed.png</file>
|
||||
<file>icons/lock-open.png</file>
|
||||
<file>icons/mapeditor.32x32.png</file>
|
||||
<file>icons/mapeditor.48x48.png</file>
|
||||
<file>icons/mapeditor.64x64.png</file>
|
||||
<file>icons/mapeditor.128x128.png</file>
|
||||
<file>icons/mapeditor.256x256.png</file>
|
||||
<file>icons/menu-game.png</file>
|
||||
<file>icons/menu-mods.png</file>
|
||||
<file>icons/menu-settings.png</file>
|
||||
<file>icons/mod-delete.png</file>
|
||||
<file>icons/mod-disabled.png</file>
|
||||
<file>icons/mod-download.png</file>
|
||||
<file>icons/mod-enabled.png</file>
|
||||
<file>icons/mod-update.png</file>
|
||||
<file>icons/toggle-grid.png</file>
|
||||
<file>icons/toggle-pass.png</file>
|
||||
<file>icons/toggle-underground.png</file>
|
||||
<file>icons/tool-area.png</file>
|
||||
<file>icons/tool-fill.png</file>
|
||||
<file>icons/tool-lasso.png</file>
|
||||
<file>icons/tool-line.png</file>
|
||||
<file>icons/tool-select.png</file>
|
||||
<file>icons/translations.png</file>
|
||||
<file>icons/zoom_base.png</file>
|
||||
<file>icons/zoom_minus.png</file>
|
||||
<file>icons/zoom_plus.png</file>
|
||||
<file>icons/zoom_zero.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
@ -29,7 +29,7 @@ Validator::Validator(const CMap * map, QWidget *parent) :
|
||||
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
|
||||
std::array<QString, 2> icons{"mapeditor/icons/mod-update.png", "mapeditor/icons/mod-delete.png"};
|
||||
std::array<QString, 2> icons{":/icons/mod-update.png", ":/icons/mod-delete.png"};
|
||||
|
||||
for(auto & issue : Validator::validate(map))
|
||||
{
|
||||
|