1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

Merge pull request #4227 from vcmi/beta

Merge beta -> master
This commit is contained in:
Ivan Savenko 2024-07-05 19:34:46 +03:00 committed by GitHub
commit e1ee8df2b9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
57 changed files with 1415 additions and 1372 deletions

View File

@ -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

View File

@ -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)

View File

@ -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);

View File

@ -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;
}
};

View File

@ -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();
}

View File

@ -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);
});
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;

View File

@ -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;
};
}

View File

@ -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);

View File

@ -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;

View File

@ -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);
}
}

View File

@ -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())
{

View File

@ -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);

View File

@ -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")

View File

@ -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")

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -395,8 +395,6 @@ void AdventureMapInterface::adjustActiveness()
void AdventureMapInterface::onCurrentPlayerChanged(PlayerColor playerID)
{
LOCPLINT->localState->setSelection(nullptr);
if (playerID == currentPlayerID)
return;

View File

@ -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:

View File

@ -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:
{

View File

@ -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);

View File

@ -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())));
}

View File

@ -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))

View File

@ -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"
},

View File

@ -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;
}

View File

@ -33,6 +33,7 @@ namespace Helper
void loadSettings()
{
settings.init("config/settings.json", "vcmi:settings");
persistentStorage.init("config/persistentStorage.json", "");
}
void enableScrollBySwiping(QObject * scrollTarget)

View File

@ -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;

View File

@ -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;
}

View File

@ -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);

File diff suppressed because it is too large Load Diff

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -157,7 +157,6 @@ void CLogger::log(ELogLevel::ELogLevel level, const boost::format & fmt) const
ELogLevel::ELogLevel CLogger::getLevel() const
{
TLockGuard _(mx);
return level;
}

View File

@ -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();

View File

@ -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)

View File

@ -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)
{

View File

@ -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;

View File

@ -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);

View File

@ -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++)

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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()

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -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();

View File

@ -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
View 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>

View File

@ -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))
{