1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-03 13:01:33 +02:00

Merge remote-tracking branch 'vcmi/develop' into battle_improvements

This commit is contained in:
Ivan Savenko 2022-12-23 14:51:34 +02:00
commit 325c29da0d
124 changed files with 1084 additions and 406 deletions

View File

@ -4,6 +4,8 @@ on:
push:
branches:
- features/*
- beta
- master
pull_request:
schedule:
- cron: '0 2 * * *'
@ -158,6 +160,7 @@ jobs:
fi
echo VCMI_PACKAGE_FILE_NAME="$VCMI_PACKAGE_FILE_NAME" >> $GITHUB_ENV
echo VCMI_PACKAGE_NAME_SUFFIX="$VCMI_PACKAGE_NAME_SUFFIX" >> $GITHUB_ENV
echo VCMI_PACKAGE_GITVERSION="$VCMI_PACKAGE_GITVERSION" >> $GITHUB_ENV
env:
PULL_REQUEST: ${{ github.event.pull_request.number }}
@ -171,7 +174,8 @@ jobs:
${{matrix.cmake_args}} -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} \
-DENABLE_TEST=${{matrix.test}} \
-DPACKAGE_NAME_SUFFIX:STRING="$VCMI_PACKAGE_NAME_SUFFIX" \
-DPACKAGE_FILE_NAME:STRING="$VCMI_PACKAGE_FILE_NAME"
-DPACKAGE_FILE_NAME:STRING="$VCMI_PACKAGE_FILE_NAME" \
-DENABLE_GITVERSION="$VCMI_PACKAGE_GITVERSION"
env:
CC: ${{ matrix.cc }}
CXX: ${{ matrix.cxx }}
@ -222,7 +226,7 @@ jobs:
${{github.workspace}}/**/${{ env.VCMI_PACKAGE_FILE_NAME }}.${{ matrix.extension }}
- name: Upload build
if: ${{ matrix.pack == 1 && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/beta' || startsWith(github.ref, 'refs/heads/features/')) && matrix.platform != 'msvc' }}
if: ${{ matrix.pack == 1 && (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/beta' || github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/heads/features/')) && matrix.platform != 'msvc' }}
run: |
cd '${{github.workspace}}/out/build/${{matrix.preset}}'
source '${{github.workspace}}/CI/upload_package.sh'
@ -240,7 +244,7 @@ jobs:
- name: Trigger Android
uses: peter-evans/repository-dispatch@v1
if: ${{ github.ref == 'refs/heads/develop' && matrix.platform == 'mxe' }}
if: ${{ (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/master') && matrix.platform == 'mxe' }}
with:
token: ${{ secrets.VCMI_ANDROID_ACCESS_TOKEN }}
repository: vcmi/vcmi-android

View File

@ -204,10 +204,14 @@ MoveTarget BattleExchangeEvaluator::findMoveTowardsUnreachable(const battle::Uni
if(targets.unreachableEnemies.empty())
return result;
auto speed = activeStack->Speed();
if(speed == 0)
return result;
updateReachabilityMap(hb);
auto dists = cb->getReachability(activeStack);
auto speed = activeStack->Speed();
for(const battle::Unit * enemy : targets.unreachableEnemies)
{

View File

@ -40,4 +40,6 @@ add_subdirectory(BattleAI)
add_subdirectory(StupidAI)
add_subdirectory(EmptyAI)
add_subdirectory(VCAI)
add_subdirectory(Nullkiller)
if(ENABLE_NULLKILLER_AI)
add_subdirectory(Nullkiller)
endif()

View File

@ -4,4 +4,6 @@
// This header should be treated as a pre compiled header file(PCH) in the compiler building settings.
// Here you can add specific libraries and macros which are specific to this project.
// Here you can add specific libraries and macros which are specific to this project.
VCMI_LIB_USING_NAMESPACE

View File

@ -30,6 +30,7 @@ namespace NKAI
// our to enemy strength ratio constants
const float SAFE_ATTACK_CONSTANT = 1.2;
const float RETREAT_THRESHOLD = 0.3;
const double RETREAT_ABSOLUTE_THRESHOLD = 10000.;
//one thread may be turn of AI and another will be handling a side effect for AI2
boost::thread_specific_ptr<CCallback> cb;
@ -500,10 +501,11 @@ boost::optional<BattleAction> AIGateway::makeSurrenderRetreatDecision(
LOG_TRACE(logAi);
NET_EVENT_HANDLER;
double fightRatio = battleState.getOurStrength() / (double)battleState.getEnemyStrength();
double ourStrength = battleState.getOurStrength();
double fightRatio = ourStrength / (double)battleState.getEnemyStrength();
// if we have no towns - things are already bad, so retreat is not an option.
if(cb->getTownsInfo().size() && fightRatio < RETREAT_THRESHOLD && battleState.canFlee)
if(cb->getTownsInfo().size() && ourStrength < RETREAT_ABSOLUTE_THRESHOLD && fightRatio < RETREAT_THRESHOLD && battleState.canFlee)
{
return BattleAction::makeRetreat(battleState.ourSide);
}
@ -533,6 +535,7 @@ void AIGateway::yourTurn()
LOG_TRACE(logAi);
NET_EVENT_HANDLER;
status.startedTurn();
makingTurn = make_unique<boost::thread>(&AIGateway::makeTurn, this);
}
@ -1426,7 +1429,15 @@ void AIGateway::endTurn()
{
logAi->error("Not having turn at the end of turn???");
}
logAi->debug("Resources at the end of turn: %s", cb->getResourceAmount().toString());
if(cb->getPlayerStatus(playerID) != EPlayerStatus::INGAME)
{
logAi->info("Ending turn is not needed because we already lost");
return;
}
do
{
cb->endTurn();
@ -1599,7 +1610,7 @@ void AIStatus::waitTillFree()
{
boost::unique_lock<boost::mutex> lock(mx);
while(battle != NO_BATTLE || !remainingQueries.empty() || !objectsBeingVisited.empty() || ongoingHeroMovement)
cv.timed_wait(lock, boost::posix_time::milliseconds(100));
cv.timed_wait(lock, boost::posix_time::milliseconds(10));
}
bool AIStatus::haveTurn()

View File

@ -74,6 +74,8 @@ Goals::TTask Nullkiller::choseBestTask(Goals::TTaskVec & tasks) const
Goals::TTask Nullkiller::choseBestTask(Goals::TSubgoal behavior, int decompositionMaxDepth) const
{
boost::this_thread::interruption_point();
logAi->debug("Checking behavior %s", behavior->toString());
auto start = std::chrono::high_resolution_clock::now();
@ -160,8 +162,12 @@ void Nullkiller::updateAiState(int pass, bool fast)
cfg.mainTurnDistanceLimit = MAIN_TURN_DISTANCE_LIMIT * ((int)scanDepth + 1);
}
boost::this_thread::interruption_point();
pathfinder->updatePaths(activeHeroes, cfg);
boost::this_thread::interruption_point();
objectClusterizer->clusterize();
}
@ -212,6 +218,8 @@ HeroLockedReason Nullkiller::getHeroLockedReason(const CGHeroInstance * hero) co
void Nullkiller::makeTurn()
{
boost::lock_guard<boost::mutex> sharedStorageLock(AISharedStorage::locker);
const int MAX_DEPTH = 10;
resetAiState();

View File

@ -127,7 +127,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
continue;
}
}
catch(cannotFulfillGoalException)
catch(cannotFulfillGoalException &)
{
if(!heroPtr.validAndSet())
{
@ -173,7 +173,7 @@ void ExecuteHeroChain::accept(AIGateway * ai)
ai->nullkiller->lockHero(hero, HeroLockedReason::HERO_CHAIN);
blockedIndexes.insert(node.parentIndex);
}
catch(goalFulfilledException)
catch(goalFulfilledException &)
{
if(!heroPtr.validAndSet())
{

View File

@ -23,6 +23,7 @@ namespace NKAI
{
std::shared_ptr<boost::multi_array<AIPathNode, 5>> AISharedStorage::shared;
boost::mutex AISharedStorage::locker;
std::set<int3> commitedTiles;
std::set<int3> commitedTilesInitial;
@ -119,18 +120,6 @@ void AINodeStorage::clear()
turnDistanceLimit[HeroRole::SCOUT] = 255;
}
const AIPathNode * AINodeStorage::getAINode(const CGPathNode * node) const
{
return static_cast<const AIPathNode *>(node);
}
void AINodeStorage::updateAINode(CGPathNode * node, std::function<void(AIPathNode *)> updater)
{
auto aiNode = static_cast<AIPathNode *>(node);
updater(aiNode);
}
boost::optional<AIPathNode *> AINodeStorage::getOrCreateNode(
const int3 & pos,
const EPathfindingLayer layer,
@ -823,13 +812,6 @@ ExchangeCandidate HeroChainCalculationTask::calculateExchange(
return candidate;
}
const CGHeroInstance * AINodeStorage::getHero(const CGPathNode * node) const
{
auto aiNode = getAINode(node);
return aiNode->actor->hero;
}
const std::set<const CGHeroInstance *> AINodeStorage::getAllHeroes() const
{
std::set<const CGHeroInstance *> heroes;

View File

@ -135,6 +135,8 @@ class AISharedStorage
static std::shared_ptr<boost::multi_array<AIPathNode, 5>> shared;
std::shared_ptr<boost::multi_array<AIPathNode, 5>> nodes;
public:
static boost::mutex locker;
AISharedStorage(int3 mapSize);
~AISharedStorage();
@ -196,8 +198,24 @@ public:
int movementLeft,
float cost) const;
const AIPathNode * getAINode(const CGPathNode * node) const;
void updateAINode(CGPathNode * node, std::function<void (AIPathNode *)> updater);
inline const AIPathNode * getAINode(const CGPathNode * node) const
{
return static_cast<const AIPathNode *>(node);
}
inline void updateAINode(CGPathNode * node, std::function<void (AIPathNode *)> updater)
{
auto aiNode = static_cast<AIPathNode *>(node);
updater(aiNode);
}
inline const CGHeroInstance * getHero(const CGPathNode * node) const
{
auto aiNode = getAINode(node);
return aiNode->actor->hero;
}
bool hasBetterChain(const PathNodeInfo & source, CDestinationNodeInfo & destination) const;
@ -223,18 +241,17 @@ public:
void setTownsAndDwellings(
const std::vector<const CGTownInstance *> & towns,
const std::set<const CGObjectInstance *> & visitableObjs);
const CGHeroInstance * getHero(const CGPathNode * node) const;
const std::set<const CGHeroInstance *> getAllHeroes() const;
void clear();
bool calculateHeroChain();
bool calculateHeroChainFinal();
uint64_t evaluateDanger(const int3 & tile, const CGHeroInstance * hero, bool checkGuards) const
inline uint64_t evaluateDanger(const int3 & tile, const CGHeroInstance * hero, bool checkGuards) const
{
return dangerEvaluator->evaluateDanger(tile, hero, checkGuards);
}
uint64_t evaluateArmyLoss(const CGHeroInstance * hero, uint64_t armyValue, uint64_t danger) const
inline uint64_t evaluateArmyLoss(const CGHeroInstance * hero, uint64_t armyValue, uint64_t danger) const
{
double ratio = (double)danger / (armyValue * hero->getFightingStrength());
@ -243,6 +260,7 @@ public:
STRONG_INLINE
void resetTile(const int3 & tile, EPathfindingLayer layer, CGPathNode::EAccessibility accessibility);
STRONG_INLINE int getBucket(const ChainActor * actor) const
{
return ((uintptr_t)actor * 395) % AIPathfinding::BUCKET_COUNT;

View File

@ -80,6 +80,8 @@ void AIPathfinder::updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes
do
{
boost::this_thread::interruption_point();
while(storage->calculateHeroChain())
{
boost::this_thread::interruption_point();
@ -91,6 +93,8 @@ void AIPathfinder::updatePaths(std::map<const CGHeroInstance *, HeroRole> heroes
logAi->trace("Select next actor");
} while(storage->selectNextActor());
boost::this_thread::interruption_point();
if(storage->calculateHeroChainFinal())
{
boost::this_thread::interruption_point();

View File

@ -80,7 +80,7 @@ Dmitry Orlov, <shubus.corporation@gmail.com>
* special buildings support in fan towns, new features and bug fixes
Andrey Cherkas aka nordsoft, <nordsoft@yahoo.com>
* new terrain support, random map generator features and various bug fixes
* new terrain support, rmg features, map editor, multiplayer improvements, bug fixes
Andrey Filipenkov aka kambala-decapitator, <decapitator@ukr.net>
* iOS support, macOS improvements, various bug fixes

View File

@ -27,16 +27,27 @@ fi
VCMI_PACKAGE_FILE_NAME="${TMP_JOBID}-vcmi"
VCMI_PACKAGE_NAME_SUFFIX=""
VCMI_PACKAGE_GITVERSION="ON"
if [ -z "$TMP_PRID" ] || [ "$TMP_PRID" == "false" ];
then
branch_name=$(echo "$TMP_BRANCH" | sed 's/[^[:alnum:]]\+/_/g')
VCMI_PACKAGE_FILE_NAME="${VCMI_PACKAGE_FILE_NAME}-branch-${branch_name}-${TMP_COMMIT}"
VCMI_PACKAGE_NAME_SUFFIX="branch ${branch_name}"
if [ "${branch_name}" != "master" ];
then
VCMI_PACKAGE_NAME_SUFFIX="branch ${branch_name}"
else
VCMI_PACKAGE_GITVERSION="OFF"
fi
else
VCMI_PACKAGE_FILE_NAME="${VCMI_PACKAGE_FILE_NAME}-PR-${TMP_PRID}-${TMP_COMMIT}"
VCMI_PACKAGE_NAME_SUFFIX="PR ${TMP_PRID}"
fi
VCMI_PACKAGE_NAME_SUFFIX="(${VCMI_PACKAGE_NAME_SUFFIX})"
if [ "${VCMI_PACKAGE_NAME_SUFFIX}" != "" ];
then
VCMI_PACKAGE_NAME_SUFFIX="(${VCMI_PACKAGE_NAME_SUFFIX})"
fi
export VCMI_PACKAGE_FILE_NAME
export VCMI_PACKAGE_NAME_SUFFIX
export VCMI_PACKAGE_GITVERSION

View File

@ -33,10 +33,6 @@ if(APPLE)
endif()
endif()
if(APPLE_IOS)
set(BUILD_SINGLE_APP 1)
endif()
############################################
# User-provided options #
############################################
@ -52,6 +48,7 @@ option(ENABLE_ERM "Enable compilation of ERM scripting module" OFF)
option(ENABLE_LUA "Enable compilation of LUA scripting module" OFF)
option(ENABLE_LAUNCHER "Enable compilation of launcher" ON)
option(ENABLE_EDITOR "Enable compilation of map editor" ON)
option(ENABLE_NULLKILLER_AI "Enable compilation of Nullkiller AI library" ON)
if(APPLE_IOS)
set(BUNDLE_IDENTIFIER_PREFIX "" CACHE STRING "Bundle identifier prefix")
set(APP_DISPLAY_NAME "VCMI" CACHE STRING "App name on the home screen")
@ -64,6 +61,8 @@ endif(NOT ${CMAKE_VERSION} VERSION_LESS "3.16.0")
option(ENABLE_GITVERSION "Enable Version.cpp with Git commit hash" ON)
option(ENABLE_DEBUG_CONSOLE "Enable debug console for Windows builds" ON)
option(ENABLE_MULTI_PROCESS_BUILDS "Enable /MP flag for MSVS solution" ON)
option(ENABLE_SINGLE_APP_BUILD "Builds client and server as single executable" OFF)
option(COPY_CONFIG_ON_BUILD "Copies config folder into output directory at building phase" ON)
# Used for Snap packages and also useful for debugging
if(NOT APPLE_IOS)
@ -74,11 +73,20 @@ endif()
set(PACKAGE_NAME_SUFFIX "" CACHE STRING "Suffix for CPack package name")
set(PACKAGE_FILE_NAME "" CACHE STRING "Override for CPack package filename")
if(APPLE_IOS AND NOT ENABLE_SINGLE_APP_BUILD)
set(ENABLE_SINGLE_APP_BUILD ON)
endif()
# ERM depends on LUA implicitly
if(ENABLE_ERM AND NOT ENABLE_LUA)
set(ENABLE_LUA ON)
endif()
# We don't want to deploy assets into build directory for iOS build
if(APPLE_IOS AND COPY_CONFIG_ON_BUILD)
set(COPY_CONFIG_ON_BUILD OFF)
endif()
############################################
# Miscellaneous options #
############################################
@ -162,8 +170,8 @@ set(CMAKE_XCODE_ATTRIBUTE_MARKETING_VERSION ${APP_SHORT_VERSION})
set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH NO)
set(CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH[variant=Debug] YES)
if(BUILD_SINGLE_APP)
add_compile_definitions(SINGLE_PROCESS_APP=1)
if(ENABLE_SINGLE_APP_BUILD)
add_definitions(-DSINGLE_PROCESS_APP=1)
endif()
if(APPLE_IOS)
@ -274,7 +282,7 @@ if(NOT WIN32 AND NOT APPLE_IOS)
endif()
if(ENABLE_LUA)
add_compile_definitions(SCRIPTING_ENABLED=1)
add_definitions(-DSCRIPTING_ENABLED=1)
endif()
############################################
@ -316,7 +324,6 @@ find_package(SDL2_ttf REQUIRED)
if(TARGET SDL2_ttf::SDL2_ttf)
add_library(SDL2::TTF ALIAS SDL2_ttf::SDL2_ttf)
endif()
find_package(TBB REQUIRED)
if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
# Widgets finds its own dependencies (QtGui and QtCore).
@ -324,6 +331,10 @@ if(ENABLE_LAUNCHER OR ENABLE_EDITOR)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)
endif()
if(ENABLE_NULLKILLER_AI)
find_package(TBB REQUIRED)
endif()
if(ENABLE_LUA)
find_package(luajit)
# MXE paths hardcoded for current dependencies pack - tried and could not make it work another way
@ -386,26 +397,34 @@ else()
include(GNUInstallDirs)
if(ENABLE_MONOLITHIC_INSTALL)
set(CMAKE_INSTALL_RPATH "$ORIGIN/")
set(BIN_DIR "." CACHE STRING "Where to install binaries")
set(LIB_DIR "." CACHE STRING "Where to install main library")
set(DATA_DIR "." CACHE STRING "Where to install data files")
# following constants only used for platforms using XDG (Linux, BSD, etc)
add_definitions(-DM_DATA_DIR="${DATA_DIR}")
add_definitions(-DM_BIN_DIR="${BIN_DIR}")
add_definitions(-DM_LIB_DIR="${LIB_DIR}")
set(CMAKE_INSTALL_RPATH "$ORIGIN/")
else()
if(NOT BIN_DIR)
set(BIN_DIR "bin" CACHE STRING "Where to install binaries")
set(BIN_DIR "${CMAKE_INSTALL_BINDIR}" CACHE STRING "Where to install binaries")
endif()
if(NOT LIB_DIR)
set(LIB_DIR "${CMAKE_INSTALL_LIBDIR}/vcmi" CACHE STRING "Where to install main library")
endif()
if(NOT DATA_DIR)
set(DATA_DIR "share/vcmi" CACHE STRING "Where to install data files")
set(DATA_DIR "${CMAKE_INSTALL_DATAROOTDIR}/vcmi" CACHE STRING "Where to install data files")
endif()
endif()
# following constants only used for platforms using XDG (Linux, BSD, etc)
add_definitions(-DM_DATA_DIR="${CMAKE_INSTALL_PREFIX}/${DATA_DIR}")
add_definitions(-DM_BIN_DIR="${CMAKE_INSTALL_PREFIX}/${BIN_DIR}")
add_definitions(-DM_LIB_DIR="${CMAKE_INSTALL_PREFIX}/${LIB_DIR}")
# following constants only used for platforms using XDG (Linux, BSD, etc)
add_definitions(-DM_DATA_DIR="${CMAKE_INSTALL_PREFIX}/${DATA_DIR}")
add_definitions(-DM_BIN_DIR="${CMAKE_INSTALL_PREFIX}/${BIN_DIR}")
add_definitions(-DM_LIB_DIR="${CMAKE_INSTALL_PREFIX}/${LIB_DIR}")
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_DIR}")
endif()
endif()
# iOS has flat libs directory structure
@ -426,13 +445,10 @@ if(APPLE_IOS)
endif()
include(VCMI_lib)
if(BUILD_SINGLE_APP)
add_subdirectory(lib_client)
add_subdirectory(lib)
set(VCMI_LIB_TARGET vcmi)
if(ENABLE_SINGLE_APP_BUILD)
add_subdirectory(lib_server)
set(VCMI_LIB_TARGET vcmi_lib_client)
else()
add_subdirectory(lib)
set(VCMI_LIB_TARGET vcmi)
endif()
if(ENABLE_ERM)

View File

@ -23,7 +23,8 @@
"PACKAGE_FILE_NAME" : "$env{VCMI_PACKAGE_FILE_NAME}",
"PACKAGE_NAME_SUFFIX" : "$env{VCMI_PACKAGE_NAME_SUFFIX}",
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"ENABLE_TEST": "OFF"
"ENABLE_TEST": "OFF",
"ENABLE_GITVERSION": "$env{VCMI_PACKAGE_GITVERSION}"
}
},
{

View File

@ -1,21 +1,69 @@
1.0.0 -> 1.1.0
GENERAL:
* Mods and their versions and serialized into save files. Game checks mod compatibility before loading
* Logs are stored in system default logs directory
* LUA/ERM libs are not compiled by default
* FFMpeg dependency is optional now
MODS:
* Supported rewardable objects customization
* Battleground obstacles are extendable now with VLC mechanism
* Introduced "compatibility" section into mods settings
LAUNCHER:
* Fixed problem with duplicated mods in the list
* Launcher shows compatible mods only
* Uninstall button was moved to the left of layout
1.0.0 -> 1.1.0
GENERAL:
* iOS is supported
* Mods and their versions and serialized into save files. Game checks mod compatibility before loading
* Logs are stored in system default logs directory
* LUA/ERM libs are not compiled by default
* FFMpeg dependency is optional now
* Conan package manager is supported for MacOS and iOS
MULTIPLAYER:
* Map is passed over network, so different platforms are compatible with each other
* Server self-killing is more robust
* Unlock in-game console while opponent's turn
* Host can control game session by using console commands
* Control over player is transferred to AI if client escaped the game
* Reconnection mode for crashed client processes
* Playing online is available using proxy server
ADVENTURE MAP:
* Fix for digging while opponent's turn
* Supported right click for quick recruit window
* Fixed problem with quests are requiring identical artefacts
* Bulk move and swap artifacts
* Pause & resume for towns and terrains music themes
* Feature to assemble/disassemble artefacts in backpack
* Clickable status bar to send messages
* Heroes no longer have chance to receive forbidden skill on leveling up
* Fixed visibility of newly recruited heroes near town
* Fixed missing artifact slot in Artifact Merchant window
BATTLES:
* Fix healing/regeneration behaviour and effect
* Fix crashes related to auto battle
* Implemented ray projectiles for shooters
* Introduced default tower shooter icons
* Towers destroyed during battle will no longer be listed as casualties
AI:
* BattleAI: Target prioritizing is now based on damage difference instead of health difference
* Nullkiller AI can retreat and surrender
* Nullkiller AI doesn't visit allied dwellings anymore
* Fixed a few freezes in Nullkiller AI
RANDOM MAP GENERATOR:
* Speedup generation of random maps
* Necromancy cannot be learned in Witch Hut on random maps
MODS:
* Supported rewardable objects customization
* Battleground obstacles are extendable now with VLC mechanism
* Introduced "compatibility" section into mods settings
* Fixed bonus system for custom advmap spells
* Supported customisable town entrance placement
* Fixed validation of mods with new adventure map objects
LAUNCHER:
* Fixed problem with duplicated mods in the list
* Launcher shows compatible mods only
* Uninstall button was moved to the left of layout
* Unsupported resolutions are not shown
* Lobby for online gameplay is implemented
MAP EDITOR:
* Basic version of Qt-based map editor
0.99 -> 1.0.0
GENERAL:
@ -98,10 +146,10 @@ BATTLES:
ADVENTURE MAP:
* Added buttons and keyboard shortcuts to quickly exchange army and artifacts between heroes
* Fix: Captured town should not be duplicated on the UI
LAUNCHER:
* Implemented notifications about updates
* Fix: Captured town should not be duplicated on the UI
LAUNCHER:
* Implemented notifications about updates
* Supported redirection links for downloading mods
0.98 -> 0.99

View File

@ -15,18 +15,6 @@
// Fixed width bool data type is important for serialization
static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
#ifdef __GNUC__
# define GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__ * 10 + __GNUC_PATCHLEVEL__)
#endif
#if !defined(__clang__) && defined(__GNUC__) && (GCC_VERSION < 470)
# error VCMI requires at least gcc-4.7.2 for successful compilation or clang-3.1. Please update your compiler
#endif
#if defined(__GNUC__) && (GCC_VERSION == 470 || GCC_VERSION == 471)
# error This GCC version has buggy std::array::at version and should not be used. Please update to 4.7.2 or later
#endif
/* ---------------------------------------------------------------------------- */
/* Suppress some compiler warnings */
/* ---------------------------------------------------------------------------- */
@ -122,10 +110,6 @@ static_assert(sizeof(bool) == 1, "Bool needs to be 1 byte in size.");
# define STRONG_INLINE inline
#endif
#define TO_STRING_HELPER(x) #x
#define TO_STRING(x) TO_STRING_HELPER(x)
#define LINE_IN_FILE __FILE__ ":" TO_STRING(__LINE__)
#define _USE_MATH_DEFINES
#include <cstdio>
@ -233,7 +217,10 @@ typedef boost::lock_guard<boost::recursive_mutex> TLockGuardRec;
/* ---------------------------------------------------------------------------- */
// Import + Export macro declarations
#ifdef VCMI_WINDOWS
# ifdef __GNUC__
#ifdef VCMI_DLL_STATIC
# define DLL_IMPORT
# define DLL_EXPORT
#elif defined(__GNUC__)
# define DLL_IMPORT __attribute__((dllimport))
# define DLL_EXPORT __attribute__((dllexport))
# else
@ -503,9 +490,6 @@ namespace vstd
ptr = nullptr;
}
#if _MSC_VER >= 1800
using std::make_unique;
#else
template<typename T>
std::unique_ptr<T> make_unique()
{
@ -531,7 +515,6 @@ namespace vstd
{
return std::unique_ptr<T>(new T(std::forward<Arg1>(arg1), std::forward<Arg2>(arg2), std::forward<Arg3>(arg3), std::forward<Arg4>(arg4)));
}
#endif
template <typename Container>
typename Container::const_reference circularAt(const Container &r, size_t index)

View File

@ -0,0 +1,10 @@
{
"core:arrowTower" :
{
"graphics" :
{
"iconSmall" : "vcmi/creatureIcons/towerSmall",
"iconLarge" : "vcmi/creatureIcons/towerLarge",
}
}
}

View File

@ -0,0 +1,101 @@
{
"core:castle" :
{
"town" :
{
"siege" :
{
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
}
}
},
"core:rampart" :
{
"town" :
{
"siege" :
{
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
}
}
},
"core:tower" :
{
"town" :
{
"siege" :
{
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
}
}
},
"core:inferno" :
{
"town" :
{
"siege" :
{
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
}
}
},
"core:necropolis" :
{
"town" :
{
"siege" :
{
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
}
}
},
"core:dungeon" :
{
"town" :
{
"siege" :
{
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
}
}
},
"core:stronghold" :
{
"town" :
{
"siege" :
{
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
}
}
},
"core:fortress" :
{
"town" :
{
"siege" :
{
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
}
}
},
"core:conflux" :
{
"town" :
{
"siege" :
{
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
}
}
}
}

View File

@ -2,13 +2,20 @@
"name" : "VCMI essential files",
"description" : "Essential files required for VCMI to run correctly",
"version" : "1.0",
"version" : "1.1",
"author" : "VCMI Team",
"contact" : "http://forum.vcmi.eu/index.php",
"modType" : "Graphical",
"factions" : [ "config/vcmi/towerFactions" ],
"creatures" : [ "config/vcmi/towerCreature" ],
"filesystem":
{
"CONFIG/" :
[
{"type" : "dir", "path" : "/Config"}
],
"DATA/" :
[
{"type" : "dir", "path" : "/Data"}

View File

@ -721,7 +721,7 @@ void processCommand(const std::string &message)
{
const JsonNode & object = nameAndObject.second;
std::string name = CModHandler::normalizeIdentifier(object.meta, "core", nameAndObject.first);
std::string name = CModHandler::normalizeIdentifier(object.meta, CModHandler::scopeBuiltin(), nameAndObject.first);
boost::algorithm::replace_all(name,":","_");
@ -1085,7 +1085,6 @@ static bool recreateWindow(int w, int h, int bpp, bool fullscreen, int displayIn
if(!checkVideoMode(displayIndex, w, h))
{
logGlobal->error("Error: SDL says that %dx%d resolution is not available!", w, h);
return false;
}
#endif

View File

@ -200,7 +200,10 @@ else()
add_executable(vcmiclient WIN32 ${client_SRCS} ${client_HEADERS} ${client_ICON})
endif(ENABLE_DEBUG_CONSOLE)
add_dependencies(vcmiclient vcmiserver BattleAI StupidAI VCAI Nullkiller)
add_dependencies(vcmiclient vcmiserver BattleAI StupidAI VCAI)
if(ENABLE_NULLKILLER_AI)
add_dependencies(vcmiclient Nullkiller)
endif()
if(APPLE_IOS)
if(ENABLE_ERM)
add_dependencies(vcmiclient vcmiERM)
@ -266,7 +269,7 @@ elseif(APPLE_IOS)
set(CMAKE_EXE_LINKER_FLAGS "-Wl,-e,_client_main")
endif()
if(BUILD_SINGLE_APP)
if(ENABLE_SINGLE_APP_BUILD)
target_link_libraries(vcmiclient PRIVATE vcmiserver)
if(ENABLE_LAUNCHER)
target_link_libraries(vcmiclient PRIVATE vcmilauncher)

View File

@ -25,12 +25,15 @@
#include "../lib/CAndroidVMHelper.h"
#elif defined(VCMI_IOS)
#include "ios/utils.h"
#include "../server/CVCMIServer.h"
#include <dispatch/dispatch.h>
#else
#include "../lib/Interprocess.h"
#endif
#ifdef SINGLE_PROCESS_APP
#include "../server/CVCMIServer.h"
#endif
#include "../lib/CConfigHandler.h"
#include "../lib/CGeneralTextHandler.h"
#include "../lib/CThreadHelper.h"
@ -142,7 +145,7 @@ void CServerHandler::resetStateForLobby(const StartInfo::EMode mode, const std::
else
myNames.push_back(settings["general"]["playerName"].String());
#if !defined(VCMI_ANDROID) && !defined(VCMI_IOS)
#if !defined(VCMI_ANDROID) && !defined(SINGLE_PROCESS_APP)
shm.reset();
if(!settings["session"]["disable-shm"].Bool())

View File

@ -30,8 +30,6 @@ template<typename T> class CApplier;
VCMI_LIB_NAMESPACE_END
struct SharedMemory;
class CClient;
class CBaseForLobbyApply;

View File

@ -100,7 +100,7 @@ void Graphics::loadPaletteAndColors()
void Graphics::initializeBattleGraphics()
{
auto allConfigs = VLC->modh->getActiveMods();
allConfigs.insert(allConfigs.begin(), "core");
allConfigs.insert(allConfigs.begin(), CModHandler::scopeBuiltin());
for(auto & mod : allConfigs)
{
if(!CResourceHandler::get(mod)->existsResource(ResourceID("config/battles_graphics.json")))

View File

@ -28,6 +28,7 @@
#include "../gui/Canvas.h"
#include "../gui/CCursorHandler.h"
#include "../gui/CGuiHandler.h"
#include "../widgets/AdventureMapClasses.h"
#include "../widgets/Buttons.h"
#include "../widgets/Images.h"
#include "../widgets/TextControls.h"
@ -123,10 +124,12 @@ BattleConsole::BattleConsole(std::shared_ptr<CPicture> backgroundSource, const P
background = std::make_shared<CPicture>(backgroundSource->getSurface(), Rect(imagePos, size), 0, 0 );
}
BattleConsole::~BattleConsole()
void BattleConsole::deactivate()
{
if (enteringText)
setEnteringMode(false);
LOCPLINT->cingconsole->endEnteringText(false);
CIntObject::deactivate();
}
void BattleConsole::setEnteringMode(bool on)
@ -548,7 +551,11 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface
int yPos = 344 + step * 97;
for(auto & elem : br.casualties[step])
{
icons.push_back(std::make_shared<CAnimImage>("CPRSMALL", CGI->creatures()->getByIndex(elem.first)->getIconIndex(), 0, xPos, yPos));
auto creature = CGI->creatures()->getByIndex(elem.first);
if (creature->getId() == CreatureID::ARROW_TOWERS )
continue; // do not show destroyed towers in battle results
icons.push_back(std::make_shared<CAnimImage>("CPRSMALL", creature->getIconIndex(), 0, xPos, yPos));
std::ostringstream amount;
amount<<elem.second;
labels.push_back(std::make_shared<CLabel>(xPos + 16, yPos + 42, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, amount.str()));

View File

@ -64,8 +64,9 @@ private:
bool enteringText;
public:
BattleConsole(std::shared_ptr<CPicture> backgroundSource, const Point & objectPos, const Point & imagePos, const Point &size);
~BattleConsole();
void showAll(SDL_Surface * to) override;
void deactivate() override;
bool addText(const std::string &text); //adds text at the last position; returns false if failed (e.g. text longer than 70 characters)
void scrollUp(ui32 by = 1); //scrolls console up by 'by' positions

View File

@ -77,6 +77,8 @@ Point CCursorHandler::position() const
void CCursorHandler::changeGraphic(Cursor::Type type, size_t index)
{
assert(dndObject == nullptr);
if(type != this->type)
{
this->type = type;

View File

@ -66,10 +66,10 @@ void CSavingScreen::changeSelection(std::shared_ptr<CMapInfo> to)
void CSavingScreen::saveGame()
{
if(!(tabSel && tabSel->inputName && tabSel->inputName->text.size()))
if(!(tabSel && tabSel->inputName && tabSel->inputName->getText().size()))
return;
std::string path = "Saves/" + tabSel->inputName->text;
std::string path = "Saves/" + tabSel->inputName->getText();
auto overWrite = [this, path]() -> void
{
@ -82,7 +82,7 @@ void CSavingScreen::saveGame()
if(CResourceHandler::get("local")->existsResource(ResourceID(path, EResType::CLIENT_SAVEGAME)))
{
std::string hlp = CGI->generaltexth->allTexts[493]; //%s exists. Overwrite?
boost::algorithm::replace_first(hlp, "%s", tabSel->inputName->text);
boost::algorithm::replace_first(hlp, "%s", tabSel->inputName->getText());
LOCPLINT->showYesNoDialog(hlp, overWrite, nullptr);
}
else

View File

@ -321,9 +321,9 @@ CChatBox::CChatBox(const Rect & rect)
void CChatBox::keyPressed(const SDL_KeyboardEvent & key)
{
if(key.keysym.sym == SDLK_RETURN && key.state == SDL_PRESSED && inputBox->text.size())
if(key.keysym.sym == SDLK_RETURN && key.state == SDL_PRESSED && inputBox->getText().size())
{
CSH->sendMessage(inputBox->text);
CSH->sendMessage(inputBox->getText());
inputBox->setText("");
}
else
@ -333,7 +333,7 @@ void CChatBox::keyPressed(const SDL_KeyboardEvent & key)
void CChatBox::addNewMessage(const std::string & text)
{
CCS->soundh->playSound("CHAT");
chatHistory->setText(chatHistory->label->text + text + "\n");
chatHistory->setText(chatHistory->label->getText() + text + "\n");
if(chatHistory->slider)
chatHistory->slider->moveToMax();
}

View File

@ -438,7 +438,7 @@ void CMultiPlayers::onChange(std::string newText)
size_t namesCount = 0;
for(auto & elem : inputNames)
if(!elem->text.empty())
if(!elem->getText().empty())
namesCount++;
}
@ -447,8 +447,8 @@ void CMultiPlayers::enterSelectionScreen()
std::vector<std::string> names;
for(auto name : inputNames)
{
if(name->text.length())
names.push_back(name->text);
if(name->getText().length())
names.push_back(name->getText());
}
Settings name = settings.write["general"]["playerName"];
@ -494,7 +494,7 @@ void CSimpleJoinScreen::connectToServer()
buttonOk->block(true);
CSDL_Ext::stopTextInput();
boost::thread(&CSimpleJoinScreen::connectThread, this, inputAddress->text, boost::lexical_cast<ui16>(inputPort->text));
boost::thread(&CSimpleJoinScreen::connectThread, this, inputAddress->getText(), boost::lexical_cast<ui16>(inputPort->getText()));
}
void CSimpleJoinScreen::leaveScreen()
@ -512,7 +512,7 @@ void CSimpleJoinScreen::leaveScreen()
void CSimpleJoinScreen::onChange(const std::string & newText)
{
buttonOk->block(inputAddress->text.empty() || inputPort->text.empty());
buttonOk->block(inputAddress->getText().empty() || inputPort->getText().empty());
}
void CSimpleJoinScreen::connectThread(const std::string addr, const ui16 port)

View File

@ -1123,7 +1123,17 @@ void CInGameConsole::textEdited(const SDL_TextEditingEvent & event)
void CInGameConsole::startEnteringText()
{
if (!active)
return;
if (captureAllKeys)
return;
assert(GH.statusbar);
assert(currentStatusBar.expired());//effectively, nullptr check
currentStatusBar = GH.statusbar;
captureAllKeys = true;
enteredText = "_";
@ -1142,12 +1152,23 @@ void CInGameConsole::endEnteringText(bool printEnteredText)
previouslyEntered.push_back(txt);
}
enteredText.clear();
GH.statusbar->setEnteringMode(false);
auto statusbar = currentStatusBar.lock();
assert(statusbar);
if (statusbar)
statusbar->setEnteringMode(false);
currentStatusBar.reset();
}
void CInGameConsole::refreshEnteredText()
{
GH.statusbar->setEnteredText(enteredText);
auto statusbar = currentStatusBar.lock();
assert(statusbar);
if (statusbar)
statusbar->setEnteredText(enteredText);
}
CAdvMapPanel::CAdvMapPanel(SDL_Surface * bg, Point position)

View File

@ -413,6 +413,8 @@ private:
int prevEntDisp; //displayed entry from previouslyEntered - if none it's -1
int defaultTimeout; //timeout for new texts (in ms)
int maxDisplayedTexts; //hiw many texts can be displayed simultaneously
std::weak_ptr<IStatusBar> currentStatusBar;
public:
std::string enteredText;
void show(SDL_Surface * to) override;

View File

@ -254,6 +254,22 @@ void CHeroArtPlace::clickRight(tribool down, bool previousState)
}
}
void CArtifactsOfHero::activate()
{
if (commonInfo->src.AOH == this && commonInfo->src.art)
CCS->curh->dragAndDropCursor(make_unique<CAnimImage>("artifact", commonInfo->src.art->artType->getIconIndex()));
CIntObject::activate();
}
void CArtifactsOfHero::deactivate()
{
if (commonInfo->src.AOH == this && commonInfo->src.art)
CCS->curh->dragAndDropCursor(nullptr);
CIntObject::deactivate();
}
/**
* Selects artifact slot so that the containing artifact looks like it's picked up.
*/
@ -675,10 +691,8 @@ void CArtifactsOfHero::updateParentWindow()
if(!updateState)
{
cew->deactivate();
cew->updateWidgets();
cew->redraw();
cew->activate();
}
}
}

View File

@ -148,6 +148,9 @@ public:
void dispose(); //free resources not needed after closing windows and reset state
void scrollBackpack(int dir); //dir==-1 => to left; dir==1 => to right
void activate() override;
void deactivate() override;
void safeRedraw();
void markPossibleSlots(const CArtifactInstance* art);
void unmarkSlots(bool withRedraw = true); //unmarks slots in all visible AOHs

View File

@ -14,7 +14,9 @@
#include "Images.h"
#include "../CMessage.h"
#include "../CPlayerInterface.h"
#include "../gui/CGuiHandler.h"
#include "../widgets/AdventureMapClasses.h"
#include "../../lib/CGeneralTextHandler.h" //for Unicode related stuff
@ -428,14 +430,17 @@ void CGStatusBar::clickLeft(tribool down, bool previousState)
{
if(!down && onClick)
{
onClick();
if(LOCPLINT && LOCPLINT->cingconsole->active)
LOCPLINT->cingconsole->startEnteringText();
}
}
void CGStatusBar::setOnClick(std::function<void()> handler)
void CGStatusBar::deactivate()
{
onClick = handler;
addUsedEvents(LCLICK);
if (enteringText)
LOCPLINT->cingconsole->endEnteringText(false);
CIntObject::deactivate();
}
Point CGStatusBar::getBorderSize()

View File

@ -41,11 +41,11 @@ protected:
virtual std::string visibleText();
std::shared_ptr<CPicture> background;
public:
std::string text;
bool autoRedraw; //whether control will redraw itself on setTxt
public:
std::string getText();
virtual void setAutoRedraw(bool option);
virtual void setText(const std::string & Txt);
@ -124,6 +124,14 @@ class CGStatusBar : public CLabel, public std::enable_shared_from_this<CGStatusB
CGStatusBar(std::shared_ptr<CPicture> background_, EFonts Font = FONT_SMALL, ETextAlignment Align = ETextAlignment::CENTER, const SDL_Color & Color = Colors::WHITE);
CGStatusBar(int x, int y, std::string name, int maxw = -1);
//make CLabel API private
using CLabel::getText;
using CLabel::setAutoRedraw;
using CLabel::setText;
using CLabel::setColor;
using CLabel::getWidth;
protected:
Point getBorderSize() override;
@ -141,9 +149,8 @@ public:
return ret;
}
void setOnClick(std::function<void()> handler);
void show(SDL_Surface * to) override;
void deactivate() override;
// IStatusBar interface
void write(const std::string & Text) override;

View File

@ -716,11 +716,6 @@ CAdvMapInt::CAdvMapInt():
worldViewUnderground->block(!CGI->mh->map->twoLevel);
addUsedEvents(MOVE);
statusbar->setOnClick([&]
{
if(LOCPLINT) LOCPLINT->cingconsole->startEnteringText();
});
}
CAdvMapInt::~CAdvMapInt()
@ -981,6 +976,7 @@ void CAdvMapInt::deactivate()
}
minimap.deactivate();
terrain.deactivate();
statusbar->deactivate();
}
}
@ -1670,13 +1666,13 @@ void CAdvMapInt::tileHovered(const int3 &mapPos)
objRelations = LOCPLINT->cb->getPlayerRelations(LOCPLINT->playerID, objAtTile->tempOwner);
std::string text = curHero() ? objAtTile->getHoverText(curHero()) : objAtTile->getHoverText(LOCPLINT->playerID);
boost::replace_all(text,"\n"," ");
statusbar->setText(text);
statusbar->write(text);
}
else
{
std::string hlp;
CGI->mh->getTerrainDescr(mapPos, hlp, false);
statusbar->setText(hlp);
statusbar->write(hlp);
}
if(spellBeingCasted)

View File

@ -1019,7 +1019,7 @@ void CCreaInfo::update()
else
value = boost::lexical_cast<std::string>(town->creatureGrowth(level));
if(value != label->text)
if(value != label->getText())
label->setText(value);
}
}

View File

@ -799,7 +799,7 @@ void CTownItem::updateGarrisons()
void CTownItem::update()
{
std::string incomeVal = boost::lexical_cast<std::string>(town->dailyIncome()[Res::GOLD]);
if (incomeVal != income->text)
if (incomeVal != income->getText())
income->setText(incomeVal);
heroes->update();

View File

@ -66,7 +66,7 @@ void CSpellWindow::InteractiveArea::clickRight(tribool down, bool previousState)
void CSpellWindow::InteractiveArea::hover(bool on)
{
if(on)
owner->statusBar->setText(hoverText);
owner->statusBar->write(hoverText);
else
owner->statusBar->clear();
}
@ -513,7 +513,7 @@ CSpellWindow::SpellArea::SpellArea(SDL_Rect pos, CSpellWindow * owner)
cost = std::make_shared<CLabel>(39, 94, FONT_TINY, ETextAlignment::CENTER);
for(auto l : {name, level, cost})
l->autoRedraw = false;
l->setAutoRedraw(false);
}
CSpellWindow::SpellArea::~SpellArea() = default;
@ -609,7 +609,7 @@ void CSpellWindow::SpellArea::hover(bool on)
if(mySpell)
{
if(on)
owner->statusBar->setText(boost::to_string(boost::format("%s (%s)") % mySpell->name % CGI->generaltexth->allTexts[171+mySpell->level]));
owner->statusBar->write(boost::to_string(boost::format("%s (%s)") % mySpell->name % CGI->generaltexth->allTexts[171+mySpell->level]));
else
owner->statusBar->clear();
}

View File

@ -676,6 +676,10 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta
break;
case EMarketMode::ARTIFACT_RESOURCE:
title = (*CGI->townh)[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name();
// create image that copies part of background containing slot MISC_1 into position of slot MISC_5
// this is workaround for bug in H3 files where this slot for ragdoll on this screen is missing
images.push_back(std::make_shared<CPicture>(background->bg, Rect(20, 187, 47, 47), 18, 339 ));
sliderNeeded = false;
break;
default:

View File

@ -21,6 +21,7 @@ VCMI_LIB_NAMESPACE_END
class CSlider;
class CTextBox;
class CPicture;
class CGStatusBar;
class CTradeWindow : public CWindowObject, public CWindowWithArtifacts //base for markets and altar of sacrifice
@ -108,6 +109,7 @@ public:
protected:
std::shared_ptr<CGStatusBar> statusBar;
std::vector<std::shared_ptr<CLabel>> labels;
std::vector<std::shared_ptr<CPicture>> images;
std::vector<std::shared_ptr<CButton>> buttons;
std::vector<std::shared_ptr<CTextBox>> texts;
};

View File

@ -579,7 +579,7 @@ void CSystemOptionsWindow::selectGameRes()
#endif
auto resolutionStr = resolutionToString(resolution.first, resolution.second);
if(gameResLabel->text == resolutionStr)
if(gameResLabel->getText() == resolutionStr)
currentResolutionIndex = i;
items.push_back(std::move(resolutionStr));
++i;

View File

@ -487,7 +487,7 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
enable_pch(${TARGET_NAME})
# We want to deploy assets into build directory for easier debugging without install
if(NOT APPLE_IOS)
if(COPY_CONFIG_ON_BUILD)
add_custom_command(TARGET ${TARGET_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/config
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/bin/${CMAKE_CFG_INTDIR}/Mods

View File

@ -1,5 +1,5 @@
set(VCMI_VERSION_MAJOR 1)
set(VCMI_VERSION_MINOR 1)
set(VCMI_VERSION_MINOR 2)
set(VCMI_VERSION_PATCH 0)
add_definitions(
-DVCMI_VERSION_MAJOR=${VCMI_VERSION_MAJOR}

View File

@ -123,8 +123,6 @@
},
"graphics" :
{
"iconSmall" : "vcmi/creatureIcons/towerSmall",
"iconLarge" : "vcmi/creatureIcons/towerLarge",
"animation": "CLCBOW.DEF" // needed to pass validation, never used
},
"sound": {}

View File

@ -203,8 +203,6 @@
"siege" :
{
"shooter" : "archer",
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
"imagePrefix" : "SGCS",
"gate" :
{

View File

@ -210,8 +210,6 @@
"siege" :
{
"shooter" : "stormElemental",
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
"imagePrefix" : "SGEL",
"gate" :
{

View File

@ -204,8 +204,6 @@
"siege" :
{
"shooter" : "medusa",
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
"imagePrefix" : "SGDN",
"gate" :
{

View File

@ -209,8 +209,6 @@
"siege" :
{
"shooter" : "lizardman",
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
"imagePrefix" : "SGFR",
"gate" :
{

View File

@ -204,8 +204,6 @@
"siege" :
{
"shooter" : "gog",
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
"imagePrefix" : "SGIN",
"gate" :
{

View File

@ -214,8 +214,6 @@
"siege" :
{
"shooter" : "lich",
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
"imagePrefix" : "SGNC",
"gate" :
{

View File

@ -211,8 +211,6 @@
"siege" :
{
"shooter" : "woodElf",
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
"imagePrefix" : "SGRM",
"gate" :
{

View File

@ -203,8 +203,6 @@
{
"shooter" : "orc",
"imagePrefix" : "SGST",
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
"gate" :
{
"arch" : { "x" : 478, "y" : 235 },

View File

@ -202,8 +202,6 @@
"siege" :
{
"shooter" : "mage",
"towerIconSmall" : "vcmi/creatureIcons/towerSmall",
"towerIconLarge" : "vcmi/creatureIcons/towerLarge",
"imagePrefix" : "SGTW",
"gate" :
{

View File

@ -56,6 +56,23 @@
"description": "List of mods that can't be enabled in the same time as this one",
"items": { "type":"string" }
},
"compatibility" : {
"type":"object",
"description": "Supported versions of vcmi engine",
"additionalProperties" : false,
"properties" : {
"min" : {
"type" : "string",
"description" : "minimal compatible vcmi engine version in a format major.minor.patch. When specified, earlier versions won't be supported"
//"pattern" : "^\\d+\\.\\d+\\.\\d+$" // Not implemented in schema support
},
"max" : {
"type" : "string",
"description" : "maximum compatible vcmi engine version in a format major.minor.patch. When specified, later versions won't be supported"
//"pattern" : "^\\d+\\.\\d+\\.\\d+$" // Not implemented in schema support
}
}
},
"keepDisabled" : {
"type":"boolean",
@ -118,6 +135,12 @@
"description": "List of configuration files for battlefields",
"items": { "type":"string", "format" : "textFile" }
},
"obstacles":{
"type":"array",
"description": "List of configuration files for obstacles",
"items": { "type":"string", "format" : "textFile" }
},
"changelog" : {

6
debian/changelog vendored
View File

@ -1,3 +1,9 @@
vcmi (1.1.0) jammy; urgency=medium
* New upstream release
-- Ivan Savenko <saven.ivan@gmail.com> Fri, 23 Dec 2022 12:00:00 +0200
vcmi (1.0.0) jammy; urgency=medium
* New upstream release

View File

@ -91,7 +91,7 @@ if(WIN32)
set(launcher_ICON VCMI_launcher.rc)
endif()
if(BUILD_SINGLE_APP)
if(ENABLE_SINGLE_APP_BUILD)
add_library(vcmilauncher STATIC ${launcher_SRCS} ${launcher_HEADERS} ${launcher_UI_HEADERS})
else()
add_executable(vcmilauncher WIN32 ${launcher_SRCS} ${launcher_HEADERS} ${launcher_UI_HEADERS} ${launcher_ICON})

View File

@ -38,6 +38,7 @@
<url type="bugtracker">https://github.com/vcmi/vcmi/issues</url>
<url type="faq">https://vcmi.eu/faq/</url>
<releases>
<release version="1.1.0" date="2022-12-23" />
<release version="1.0.0" date="2022-09-11" />
<release version="0.99" date="2016-11-01" />
</releases>

View File

@ -31,7 +31,7 @@ bool isCompatible(const QString & verMin, const QString & verMax)
if(ver.segmentCount() < maxSections)
{
auto segments = ver.segments();
for(int i = segments.size() - 1; i < maxSections; ++i)
for(int i = segments.size(); i < maxSections; ++i)
segments.append(0);
ver = QVersionNumber(segments);
}

View File

@ -280,7 +280,7 @@ std::vector<JsonNode> CArtHandler::loadLegacyData(size_t dataSize)
void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), objects.size());
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name), objects.size());
object->iconIndex = object->getIndex() + 5;
@ -291,7 +291,7 @@ void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode
void CArtHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), index);
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name), index);
object->iconIndex = object->getIndex();
@ -861,8 +861,6 @@ void CArtifactInstance::removeFrom(ArtifactLocation al)
al.getHolderArtSet()->eraseArtSlot(al.slot);
if(!ArtifactUtils::isSlotBackpack(al.slot))
al.getHolderNode()->detachFrom(*this);
//TODO delete me?
}
bool CArtifactInstance::canBeDisassembled() const
@ -1062,7 +1060,9 @@ void CCombinedArtifactInstance::createConstituents()
void CCombinedArtifactInstance::addAsConstituent(CArtifactInstance *art, ArtifactPosition slot)
{
assert(vstd::contains(*artType->constituents, art->artType.get()));
assert(vstd::contains_if(*artType->constituents, [=](const CArtifact * constituent){
return constituent->id == art->artType->id;
}));
assert(art->getParentNodes().size() == 1 && art->getParentNodes().front() == art->artType);
constituentsInfo.push_back(ConstituentInfo(art, slot));
attachTo(*art);
@ -1354,11 +1354,16 @@ bool CArtifactSet::isPositionFree(ArtifactPosition pos, bool onlyLockCheck) cons
ArtSlotInfo & CArtifactSet::retrieveNewArtSlot(ArtifactPosition slot)
{
assert(!vstd::contains(artifactsWorn, slot));
ArtSlotInfo &ret = !ArtifactUtils::isSlotBackpack(slot)
? artifactsWorn[slot]
: *artifactsInBackpack.insert(artifactsInBackpack.begin() + (slot - GameConstants::BACKPACK_START), ArtSlotInfo());
return ret;
if (!ArtifactUtils::isSlotBackpack(slot))
return artifactsWorn[slot];
ArtSlotInfo newSlot;
size_t index = slot - GameConstants::BACKPACK_START;
auto position = artifactsInBackpack.begin() + index;
auto inserted = artifactsInBackpack.insert(position, newSlot);
return *inserted;
}
void CArtifactSet::setNewArtSlot(ArtifactPosition slot, CArtifactInstance *art, bool locked)
@ -1372,9 +1377,10 @@ void CArtifactSet::eraseArtSlot(ArtifactPosition slot)
{
if(ArtifactUtils::isSlotBackpack(slot))
{
assert(artifactsInBackpack.begin() + slot < artifactsInBackpack.end());
slot = ArtifactPosition(slot - GameConstants::BACKPACK_START);
artifactsInBackpack.erase(artifactsInBackpack.begin() + slot);
auto backpackSlot = ArtifactPosition(slot - GameConstants::BACKPACK_START);
assert(artifactsInBackpack.begin() + backpackSlot < artifactsInBackpack.end());
artifactsInBackpack.erase(artifactsInBackpack.begin() + backpackSlot);
}
else
{
@ -1547,16 +1553,18 @@ DLL_LINKAGE ArtifactPosition ArtifactUtils::getArtifactDstPosition( const CArtif
DLL_LINKAGE const std::vector<ArtifactPosition::EArtifactPosition> & ArtifactUtils::unmovableSlots()
{
return
static const std::vector<ArtifactPosition::EArtifactPosition> positions =
{
ArtifactPosition::SPELLBOOK,
ArtifactPosition::MACH4
};
return positions;
}
DLL_LINKAGE const std::vector<ArtifactPosition::EArtifactPosition> & ArtifactUtils::constituentWornSlots()
{
return
static const std::vector<ArtifactPosition::EArtifactPosition> positions =
{
ArtifactPosition::HEAD,
ArtifactPosition::SHOULDERS,
@ -1573,6 +1581,8 @@ DLL_LINKAGE const std::vector<ArtifactPosition::EArtifactPosition> & ArtifactUti
ArtifactPosition::MISC4,
ArtifactPosition::MISC5,
};
return positions;
}
DLL_LINKAGE bool ArtifactUtils::isArtRemovable(const std::pair<ArtifactPosition, ArtSlotInfo> & slot)

View File

@ -19,6 +19,8 @@ boost::mutex CConsoleHandler::smx;
DLL_LINKAGE CConsoleHandler * console = nullptr;
VCMI_LIB_NAMESPACE_END
#ifndef VCMI_WINDOWS
typedef std::string TColor;
#define CONSOLE_GREEN "\x1b[1;32m"
@ -51,6 +53,8 @@ DLL_LINKAGE CConsoleHandler * console = nullptr;
static TColor defColor;
VCMI_LIB_NAMESPACE_BEGIN
#ifdef VCMI_WINDOWS
void printWinError()

View File

@ -425,7 +425,7 @@ const CCreature * CCreatureHandler::getCreature(const std::string & scope, const
void CCreatureHandler::loadCommanders()
{
JsonNode data(ResourceID("config/commanders.json"));
data.setMeta("core"); // assume that commanders are in core mod (for proper bonuses resolution)
data.setMeta(CModHandler::scopeBuiltin()); // assume that commanders are in core mod (for proper bonuses resolution)
const JsonNode & config = data; // switch to const data accessors

View File

@ -1053,7 +1053,7 @@ void CStackBasicDescriptor::serializeJson(JsonSerializeFormat & handler)
std::string typeName("");
handler.serializeString("type", typeName);
if(!typeName.empty())
setType(VLC->creh->getCreature("core", typeName));
setType(VLC->creh->getCreature(CModHandler::scopeMap(), typeName));
}
}

View File

@ -760,6 +760,7 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, bool allow
initHeroes();
initStartingBonus();
initTowns();
placeHeroesInTowns();
initMapObjects();
buildBonusSystemTree();
initVisitingAndGarrisonedHeroes();
@ -1363,7 +1364,8 @@ void CGameState::placeStartingHeroes()
{
if(auto campaignBonus = scenarioOps->campState->getBonusForCurrentMap())
{
if(campaignBonus->type == CScenarioTravel::STravelBonus::HERO && playerColor == PlayerColor(campaignBonus->info1)) continue;
if(campaignBonus->type == CScenarioTravel::STravelBonus::HERO && playerColor == PlayerColor(campaignBonus->info1))
continue;
}
}
@ -1861,6 +1863,37 @@ void CGameState::initMapObjects()
map->calculateGuardingGreaturePositions(); //calculate once again when all the guards are placed and initialized
}
void CGameState::placeHeroesInTowns()
{
for(auto k=players.begin(); k!=players.end(); ++k)
{
if(k->first==PlayerColor::NEUTRAL)
continue;
for(CGHeroInstance *h : k->second.heroes)
{
for(CGTownInstance *t : k->second.towns)
{
bool heroOnTownBlockableTile = t->blockingAt(h->visitablePos().x, h->visitablePos().y);
// current hero position is at one of blocking tiles of current town
// assume that this hero should be visiting the town (H3M format quirk) and move hero to correct position
if (heroOnTownBlockableTile)
{
int3 townVisitablePos = t->visitablePos();
int3 correctedPos = townVisitablePos + h->getVisitableOffset();
map->removeBlockVisTiles(h);
h->pos = correctedPos;
map->addBlockVisTiles(h);
assert(t->visitableAt(h->visitablePos().x, h->visitablePos().y));
}
}
}
}
}
void CGameState::initVisitingAndGarrisonedHeroes()
{
for(auto k=players.begin(); k!=players.end(); ++k)
@ -1873,17 +1906,10 @@ void CGameState::initVisitingAndGarrisonedHeroes()
{
for(CGTownInstance *t : k->second.towns)
{
int3 vistile = t->visitablePos(); vistile.x++; //tile next to the entrance
if(vistile == h->pos || h->pos==t->visitablePos())
if (t->visitableAt(h->visitablePos().x, h->visitablePos().y))
{
assert(t->visitingHero == nullptr);
t->setVisitingHero(h);
if(h->pos == t->pos) //visiting hero placed in the editor has same pos as the town - we need to correct it
{
map->removeBlockVisTiles(h);
h->pos.x -= 1;
map->addBlockVisTiles(h);
}
break;
}
}
}

View File

@ -269,6 +269,7 @@ private:
void placeStartingHero(PlayerColor playerColor, HeroTypeID heroTypeId, int3 townPos);
void initStartingResources();
void initHeroes();
void placeHeroesInTowns();
void giveCampaignBonusToHero(CGHeroInstance * hero);
void initFogOfWar();
void initStartingBonus();
@ -297,8 +298,6 @@ private:
CRandomGenerator rand;
Services * services;
friend class CCallback;
friend class CClient;
friend class IGameCallback;
friend class CMapHandler;
friend class CGameHandler;

View File

@ -348,7 +348,7 @@ CHeroHandler::CHeroHandler()
loadTerrains();
for(const auto & terrain : VLC->terrainTypeHandler->terrains())
{
VLC->modh->identifiers.registerObject("core", "terrain", terrain.name, terrain.id);
VLC->modh->identifiers.registerObject(CModHandler::scopeBuiltin(), "terrain", terrain.name, terrain.id);
}
loadBallistics();
loadExperience();
@ -868,7 +868,7 @@ std::vector<JsonNode> CHeroHandler::loadLegacyData(size_t dataSize)
void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
size_t index = objects.size();
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), index);
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name), index);
object->imageIndex = (si32)index + GameConstants::HERO_PORTRAIT_SHIFT; // 2 special frames + some extra portraits
objects.push_back(object);
@ -878,7 +878,7 @@ void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNod
void CHeroHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), index);
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name), index);
object->imageIndex = static_cast<si32>(index);
assert(objects[index] == nullptr); // ensure that this id was not loaded before

View File

@ -1 +1,4 @@
add_main_lib(vcmi SHARED)
if(ENABLE_SINGLE_APP_BUILD)
target_compile_definitions(vcmi PUBLIC VCMI_LIB_NAMESPACE=LIB_CLIENT)
endif()

View File

@ -209,16 +209,15 @@ std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdent
// called have not specified destination mod explicitly
if (request.remoteScope.empty())
{
// FIXME: temporary, for queries from map loader allow access to any identifer
// should be changed to list of mods that are marked as required by current map
if (request.localScope == "map")
// special scope that should have access to all in-game objects
if (request.localScope == CModHandler::scopeGame())
{
for (auto const & modName : VLC->modh->getActiveMods())
allowedScopes.insert(modName);
}
// normally ID's from all required mods, own mod and virtual "core" mod are allowed
else if(request.localScope != "core" && !request.localScope.empty())
// normally ID's from all required mods, own mod and virtual built-in mod are allowed
else if(request.localScope != CModHandler::scopeBuiltin() && !request.localScope.empty())
{
allowedScopes = VLC->modh->getModDependencies(request.localScope, isValidScope);
@ -229,15 +228,19 @@ std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdent
}
// all mods can access built-in mod
allowedScopes.insert("core");
allowedScopes.insert(CModHandler::scopeBuiltin());
}
else
{
//if destination mod was specified explicitly, restrict lookup to this mod
if(request.remoteScope == "core" )
if(request.remoteScope == CModHandler::scopeBuiltin() )
{
//"core" mod is an implicit dependency for all mods, allow access into it
//built-in mod is an implicit dependency for all mods, allow access into it
allowedScopes.insert(request.remoteScope);
}
else if ( request.localScope == CModHandler::scopeGame() )
{
// allow access, this is special scope that should have access to all in-game objects
allowedScopes.insert(request.remoteScope);
}
else if(request.remoteScope == request.localScope )
@ -340,7 +343,7 @@ ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, std::string objec
{
for(auto & node : originalData)
{
node.setMeta("core");
node.setMeta(CModHandler::scopeBuiltin());
}
}
@ -401,6 +404,9 @@ bool ContentTypeHandler::loadMod(std::string modName, bool validate)
if (vstd::contains(data.Struct(), "index") && !data["index"].isNull())
{
if (modName != "core")
logMod->warn("Mod %s is attempting to load original data! This should be reserved for built-in mod.", modName);
// try to add H3 object data
size_t index = static_cast<size_t>(data["index"].Float());
@ -413,7 +419,7 @@ bool ContentTypeHandler::loadMod(std::string modName, bool validate)
}
else
{
logMod->warn("no original data in loadMod(%s) at index %d", name, index);
logMod->trace("no original data in loadMod(%s) at index %d", name, index);
}
performValidate(data, name);
handler->loadObject(modName, name, data, index);
@ -506,7 +512,7 @@ void CContentHandler::preloadData(CModInfo & mod)
// print message in format [<8-symbols checksum>] <modname>
logMod->info("\t\t[%08x]%s", mod.checksum, mod.name);
if (validate && mod.identifier != "core")
if (validate && mod.identifier != CModHandler::scopeBuiltin())
{
if (!JsonUtils::validate(mod.config, "vcmi:mod", mod.identifier))
mod.validation = CModInfo::FAILED;
@ -698,13 +704,13 @@ CModHandler::CModHandler() : content(std::make_shared<CContentHandler>())
modules.MITHRIL = false;
for (int i = 0; i < GameConstants::RESOURCE_QUANTITY; ++i)
{
identifiers.registerObject("core", "resource", GameConstants::RESOURCE_NAMES[i], i);
identifiers.registerObject(CModHandler::scopeBuiltin(), "resource", GameConstants::RESOURCE_NAMES[i], i);
}
for(int i=0; i<GameConstants::PRIMARY_SKILLS; ++i)
{
identifiers.registerObject("core", "primSkill", PrimarySkill::names[i], i);
identifiers.registerObject("core", "primarySkill", PrimarySkill::names[i], i);
identifiers.registerObject(CModHandler::scopeBuiltin(), "primSkill", PrimarySkill::names[i], i);
identifiers.registerObject(CModHandler::scopeBuiltin(), "primarySkill", PrimarySkill::names[i], i);
}
}
@ -905,6 +911,35 @@ std::vector<std::string> CModHandler::getModList(std::string path)
return foundMods;
}
bool CModHandler::isScopeReserved(const TModID & scope)
{
static const std::array<TModID, 3> reservedScopes = {
"core", "map", "game"
};
return std::find(reservedScopes.begin(), reservedScopes.end(), scope) != reservedScopes.end();
}
const TModID & CModHandler::scopeBuiltin()
{
static const TModID scope = "core";
return scope;
}
const TModID & CModHandler::scopeGame()
{
static const TModID scope = "game";
return scope;
}
const TModID & CModHandler::scopeMap()
{
//TODO: implement accessing map dependencies for both H3 and VCMI maps
// for now, allow access to any identifiers
static const TModID scope = "game";
return scope;
}
void CModHandler::loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods)
{
for(std::string modName : getModList(path))
@ -916,6 +951,12 @@ void CModHandler::loadOneMod(std::string modName, std::string parent, const Json
boost::to_lower(modName);
std::string modFullName = parent.empty() ? modName : parent + '.' + modName;
if ( isScopeReserved(modFullName))
{
logMod->error("Can not load mod %s - this name is reserved for internal use!", modFullName);
return;
}
if(CResourceHandler::get("initial")->existsResource(ResourceID(CModInfo::getModFile(modFullName))))
{
CModInfo mod(modFullName, modSettings[modName], JsonNode(ResourceID(CModInfo::getModFile(modFullName))));
@ -944,7 +985,7 @@ void CModHandler::loadMods(bool onlyEssential)
loadMods("", "", modConfig["activeMods"], true);
}
coreMod = CModInfo("core", modConfig["core"], JsonNode(ResourceID("config/gameConfig.json")));
coreMod = CModInfo(CModHandler::scopeBuiltin(), modConfig[CModHandler::scopeBuiltin()], JsonNode(ResourceID("config/gameConfig.json")));
coreMod.name = "Original game files";
}
@ -991,7 +1032,7 @@ static ui32 calculateModChecksum(const std::string modName, ISimpleResourceLoade
// second - add mod.json into checksum because filesystem does not contains this file
// FIXME: remove workaround for core mod
if (modName != "core")
if (modName != CModHandler::scopeBuiltin())
{
ResourceID modConfFile(CModInfo::getModFile(modName), EResType::TEXT);
ui32 configChecksum = CResourceHandler::get("initial")->load(modConfFile)->calculateCRC32();
@ -1017,7 +1058,7 @@ void CModHandler::loadModFilesystems()
{
activeMods = validateAndSortDependencies(activeMods);
coreMod.updateChecksum(calculateModChecksum("core", CResourceHandler::get("core")));
coreMod.updateChecksum(calculateModChecksum(CModHandler::scopeBuiltin(), CResourceHandler::get(CModHandler::scopeBuiltin())));
for(std::string & modName : activeMods)
{
@ -1057,7 +1098,7 @@ void CModHandler::load()
allMods[modName].updateChecksum(calculateModChecksum(modName, CResourceHandler::get(modName)));
}
// first - load virtual "core" mod that contains all data
// first - load virtual builtin mod that contains all data
// TODO? move all data into real mods? RoE, AB, SoD, WoG
content->preloadData(coreMod);
for(const TModID & modName : activeMods)
@ -1096,7 +1137,7 @@ void CModHandler::afterLoad(bool onlyEssential)
modSettings["activeMods"].resolvePointer(pointer) = modEntry.second.saveLocalData();
}
modSettings["core"] = coreMod.saveLocalData();
modSettings[CModHandler::scopeBuiltin()] = coreMod.saveLocalData();
if(!onlyEssential)
{

View File

@ -14,6 +14,12 @@
#include "VCMI_Lib.h"
#include "JsonNode.h"
#ifdef __UCLIBC__
#undef major
#undef minor
#undef patch
#endif
VCMI_LIB_NAMESPACE_BEGIN
class CModHandler;
@ -277,7 +283,19 @@ class DLL_LINKAGE CModHandler
void loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods);
void loadOneMod(std::string modName, std::string parent, const JsonNode & modSettings, bool enableMods);
public:
/// returns true if scope is reserved for internal use and can not be used by mods
static bool isScopeReserved(const TModID & scope);
/// reserved scope name for referencing built-in (e.g. H3) objects
static const TModID & scopeBuiltin();
/// reserved scope name for accessing objects from any loaded mod
static const TModID & scopeGame();
/// reserved scope name for accessing object for map loading
static const TModID & scopeMap();
class DLL_LINKAGE Incompatibility: public std::exception
{
public:

View File

@ -625,7 +625,7 @@ void LayerTransitionRule::process(
else if(destination.node->accessible != CGPathNode::ACCESSIBLE)
{
/// Hero that fly can only land on accessible tiles
if(!destination.isGuardianTile && destination.nodeObject)
if(destination.nodeObject)
destination.blocked = true;
}

View File

@ -267,7 +267,7 @@ std::vector<bool> CSkillHandler::getDefaultAllowed() const
si32 CSkillHandler::decodeSkill(const std::string & identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "skill", identifier);
auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "skill", identifier);
if(rawId)
return rawId.get();
else

View File

@ -1010,7 +1010,7 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode
void CTownHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), objects.size());
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name), objects.size());
objects.push_back(object);
@ -1049,7 +1049,7 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
void CTownHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), index);
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name), index);
if (objects.size() > index)
assert(objects[index] == nullptr); // ensure that this id was not loaded before
@ -1083,7 +1083,7 @@ void CTownHandler::loadRandomFaction()
static const ResourceID randomFactionPath("config/factions/random.json");
JsonNode randomFactionJson(randomFactionPath);
randomFactionJson.setMeta("core", true);
randomFactionJson.setMeta(CModHandler::scopeBuiltin(), true);
loadBuildings(randomTown, randomFactionJson["random"]["town"]["buildings"]);
}

View File

@ -62,7 +62,7 @@ namespace GameConstants
si32 HeroTypeID::decode(const std::string & identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "hero", identifier);
auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "hero", identifier);
if(rawId)
return rawId.get();
else
@ -86,7 +86,7 @@ const Artifact * ArtifactID::toArtifact(const ArtifactService * service) const
si32 ArtifactID::decode(const std::string & identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "artifact", identifier);
auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "artifact", identifier);
if(rawId)
return rawId.get();
else
@ -110,7 +110,7 @@ const Creature * CreatureID::toCreature(const CreatureService * creatures) const
si32 CreatureID::decode(const std::string & identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "creature", identifier);
auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "creature", identifier);
if(rawId)
return rawId.get();
else
@ -139,7 +139,7 @@ const spells::Spell * SpellID::toSpell(const spells::Service * service) const
si32 SpellID::decode(const std::string & identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "spell", identifier);
auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "spell", identifier);
if(rawId)
return rawId.get();
else
@ -201,7 +201,7 @@ const FactionID FactionID::NEUTRAL = FactionID(9);
si32 FactionID::decode(const std::string & identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "faction", identifier);
auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeGame(), "faction", identifier);
if(rawId)
return rawId.get();
else
@ -288,7 +288,7 @@ const BattleFieldInfo * BattleField::getInfo() const
BattleField BattleField::fromString(std::string identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "battlefield", identifier);
auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeBuiltin(), "battlefield", identifier);
if(rawId)
return BattleField(rawId.get());
@ -308,7 +308,7 @@ Obstacle::operator std::string() const
Obstacle Obstacle::fromString(std::string identifier)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "obstacle", identifier);
auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeBuiltin(), "obstacle", identifier);
if(rawId)
return Obstacle(rawId.get());

View File

@ -14,6 +14,10 @@
VCMI_LIB_NAMESPACE_BEGIN
std::string IHandlerBase::getScopeBuiltin() const
{
return CModHandler::scopeBuiltin();
}
void IHandlerBase::registerObject(std::string scope, std::string type_name, std::string name, si32 index)
{

View File

@ -21,6 +21,8 @@ class Entity;
class DLL_LINKAGE IHandlerBase
{
protected:
std::string getScopeBuiltin() const;
/// Calls modhandler. Mostly needed to avoid large number of includes in headers
void registerObject(std::string scope, std::string type_name, std::string name, si32 index);
std::string normalizeIdentifier(const std::string & scope, const std::string & remoteScope, const std::string & identifier) const;
@ -92,7 +94,7 @@ public:
void loadObject(std::string scope, std::string name, const JsonNode & data) override
{
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), objects.size());
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, getScopeBuiltin(), name), objects.size());
objects.push_back(object);
@ -102,7 +104,7 @@ public:
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override
{
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name), index);
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, getScopeBuiltin(), name), index);
assert(objects[index] == nullptr); // ensure that this id was not loaded before
objects[index] = object;

View File

@ -999,7 +999,7 @@ namespace
bool testFilePresence(std::string scope, ResourceID resource)
{
std::set<std::string> allowedScopes;
if(scope != "core" && !scope.empty()) // all real mods may have dependencies
if(scope != CModHandler::scopeBuiltin() && !scope.empty()) // all real mods may have dependencies
{
//NOTE: recursive dependencies are not allowed at the moment - update code if this changes
bool found = true;
@ -1008,7 +1008,7 @@ namespace
if(!found)
return false;
allowedScopes.insert("core"); // all mods can use H3 files
allowedScopes.insert(CModHandler::scopeBuiltin()); // all mods can use H3 files
}
allowedScopes.insert(scope); // mods can use their own files

View File

@ -703,13 +703,18 @@ DLL_LINKAGE void GiveHero::applyGs(CGameState *gs)
//bonus system
h->detachFrom(gs->globalEffects);
h->attachTo(*gs->getPlayerState(player));
h->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, h->type->heroClass->getIndex())->getTemplates().front();
auto oldOffset = h->getVisitableOffset();
gs->map->removeBlockVisTiles(h,true);
h->appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, h->type->heroClass->getIndex())->getTemplates().front();
auto newOffset = h->getVisitableOffset();
h->setOwner(player);
h->movement = h->maxMovePoints(true);
h->pos = h->pos - oldOffset + newOffset;
gs->map->heroesOnMap.push_back(h);
gs->getPlayerState(h->getOwner())->heroes.push_back(h);
gs->map->addBlockVisTiles(h);
h->inTownGarrison = false;
}
@ -1171,7 +1176,12 @@ DLL_LINKAGE void AssembledArtifact::applyGs(CGameState *gs)
const CArtifactInstance *transformedArt = al.getArt();
assert(transformedArt);
bool combineEquipped = !ArtifactUtils::isSlotBackpack(al.slot);
assert(vstd::contains(transformedArt->assemblyPossibilities(artSet, combineEquipped), builtArt));
assert(vstd::contains_if(transformedArt->assemblyPossibilities(artSet, combineEquipped), [=](const CArtifact * art)->bool
{
return art->id == builtArt->id;
}));
UNUSED(transformedArt);
auto combinedArt = new CCombinedArtifactInstance(builtArt);

View File

@ -242,7 +242,7 @@ ScriptPtr ScriptHandler::loadFromJson(vstd::CLoggerBase * logger, const std::str
void ScriptHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
auto object = loadFromJson(logMod, scope, data, normalizeIdentifier(scope, "core", name));
auto object = loadFromJson(logMod, scope, data, normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name));
objects[object->identifier] = object;
}

View File

@ -22,7 +22,7 @@ VCMI_LIB_NAMESPACE_BEGIN
TerrainTypeHandler::TerrainTypeHandler()
{
auto allConfigs = VLC->modh->getActiveMods();
allConfigs.insert(allConfigs.begin(), "core");
allConfigs.insert(allConfigs.begin(), CModHandler::scopeBuiltin());
initRivers(allConfigs);
recreateRiverMaps();

View File

@ -20,6 +20,7 @@
#include "../GameConstants.h"
#include "../VCMIDirs.h"
#include "../CStopWatch.h"
#include "../CModHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -207,7 +208,7 @@ void CResourceHandler::load(const std::string &fsConfigURI, bool extractArchives
const JsonNode fsConfig((char*)fsConfigData.first.get(), fsConfigData.second);
addFilesystem("data", "core", createFileSystem("", fsConfig["filesystem"], extractArchives));
addFilesystem("data", CModHandler::scopeBuiltin(), createFileSystem("", fsConfig["filesystem"], extractArchives));
}
void CResourceHandler::addFilesystem(const std::string & parent, const std::string & identifier, ISimpleResourceLoader * loader)

View File

@ -1413,7 +1413,7 @@ void CGHeroInstance::setHeroTypeName(const std::string & identifier)
{
if(ID == Obj::HERO || ID == Obj::PRISON)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", "hero", identifier);
auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "hero", identifier);
if(rawId)
subID = rawId.get();
@ -1434,7 +1434,7 @@ void CGHeroInstance::serializeCommonOptions(JsonSerializeFormat & handler)
handler.serializeString("biography", biography);
handler.serializeInt("experience", exp, 0);
if (!handler.saving)
if(!handler.saving && exp != 0xffffffff) //do not gain levels if experience is not initialized
{
while (gainsLevel())
{

View File

@ -1466,7 +1466,7 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
{
auto decodeBuilding = [this](const std::string & identifier) -> si32
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", getTown()->getBuildingScope(), identifier);
auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), getTown()->getBuildingScope(), identifier);
if(rawId)
return rawId.get();

View File

@ -170,7 +170,7 @@ void CObjectClassesHandler::loadObjectEntry(const std::string & identifier, cons
logGlobal->error("Handler with name %s was not found!", obj->handlerName);
return;
}
const auto convertedId = VLC->modh->normalizeIdentifier(entry.meta, "core", identifier);
const auto convertedId = VLC->modh->normalizeIdentifier(entry.meta, CModHandler::scopeBuiltin(), identifier);
const auto & entryIndex = entry["index"];
bool useSelectNextID = !isSubobject || entryIndex.isNull();
@ -259,14 +259,14 @@ CObjectClassesHandler::ObjectContainter * CObjectClassesHandler::loadFromJson(co
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data)
{
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name));
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name));
objects[object->id] = object;
VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
}
void CObjectClassesHandler::loadObject(std::string scope, std::string name, const JsonNode & data, size_t index)
{
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, "core", name));
auto object = loadFromJson(scope, data, normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name));
assert(objects[(si32)index] == nullptr); // ensure that this id was not loaded before
objects[(si32)index] = object;
VLC->modh->identifiers.registerObject(scope, "object", name, object->id);
@ -308,8 +308,9 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(si32 type, si32 subtype)
if (objects.at(type)->subObjects.count(subtype))
return objects.at(type)->subObjects.at(subtype);
}
logGlobal->error("Failed to find object of type %d:%d", type, subtype);
throw std::runtime_error("Object type handler not found");
std::string errorString = "Failed to find object of type " + std::to_string(type) + "::" + std::to_string(subtype);
logGlobal->error(errorString);
throw std::runtime_error(errorString);
}
TObjectTypeHandler CObjectClassesHandler::getHandlerFor(std::string scope, std::string type, std::string subtype) const
@ -325,8 +326,9 @@ TObjectTypeHandler CObjectClassesHandler::getHandlerFor(std::string scope, std::
return object->subObjects.at(subId);
}
}
logGlobal->error("Failed to find object of type %s::%s", type, subtype);
throw std::runtime_error("Object type handler not found");
std::string errorString = "Failed to find object of type " + type + "::" + subtype;
logGlobal->error(errorString);
throw std::runtime_error(errorString);
}
TObjectTypeHandler CObjectClassesHandler::getHandlerFor(CompoundMapObjectID compoundIdentifier) const

View File

@ -195,7 +195,9 @@ std::set<int3> CGObjectInstance::getBlockedOffsets() const
void CGObjectInstance::setType(si32 ID, si32 subID)
{
const TerrainTile &tile = cb->gameState()->map->getTile(visitablePos());
auto position = visitablePos();
auto oldOffset = getVisitableOffset();
auto &tile = cb->gameState()->map->getTile(position);
//recalculate blockvis tiles - new appearance might have different blockmap than before
cb->gameState()->map->removeBlockVisTiles(this, true);
@ -206,14 +208,23 @@ void CGObjectInstance::setType(si32 ID, si32 subID)
return;
}
if(!handler->getTemplates(tile.terType->id).empty())
{
appearance = handler->getTemplates(tile.terType->id)[0];
}
else
{
logGlobal->warn("Object %d:%d at %s has no templates suitable for terrain %s", ID, subID, visitablePos().toString(), tile.terType->name);
appearance = handler->getTemplates()[0]; // get at least some appearance since alternative is crash
}
if(this->ID == Obj::PRISON && ID == Obj::HERO)
{
//adjust for the prison offset
pos = visitablePos();
auto newOffset = getVisitableOffset();
// FIXME: potentially unused code - setType is NOT called when releasing hero from prison
// instead, appearance update & pos adjustment occurs in GiveHero::applyGs
// adjust position since hero and prison may have different visitable offset
pos = pos - oldOffset + newOffset;
}
this->ID = Obj(ID);

View File

@ -1028,7 +1028,7 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler)
if(doRequest)
{
auto rawId = VLC->modh->identifiers.getIdentifier("core", fullIdentifier, false);
auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), fullIdentifier, false);
if(rawId)
{

View File

@ -970,22 +970,22 @@ void CGVisitableOPH::initObj(CRandomGenerator & rand)
break;
case Obj::LIBRARY_OF_ENLIGHTENMENT:
{
selectMode = SELECT_FIRST;
onVisited.addTxt(MetaString::ADVOB_TXT, 67);
onEmpty.addTxt(MetaString::ADVOB_TXT, 68);
// Don't like this one but don't see any easier approach
CVisitInfo visit;
visit.reward.primary[PrimarySkill::ATTACK] = 2;
visit.reward.primary[PrimarySkill::DEFENSE] = 2;
visit.reward.primary[PrimarySkill::KNOWLEDGE] = 2;
visit.reward.primary[PrimarySkill::SPELL_POWER] = 2;
visit.message.addTxt(MetaString::ADVOB_TXT, 66);
static_assert(SecSkillLevel::LEVELS_SIZE == 4, "Behavior of Library of Enlignment may not be correct");
for (int i=0; i<SecSkillLevel::LEVELS_SIZE; i++)
{
visit.limiter.minLevel = 10 - i * 2;
visit.limiter.secondary[SecondarySkill::DIPLOMACY] = i;
visit.message.addTxt(MetaString::ADVOB_TXT, 66);
info.push_back(visit);
}
break;

View File

@ -1804,7 +1804,7 @@ void CGScholar::serializeJsonOptions(JsonSerializeFormat & handler)
bonusType = RANDOM;
if(!json["rewardPrimSkill"].String().empty())
{
auto raw = VLC->modh->identifiers.getIdentifier("core", "primSkill", json["rewardPrimSkill"].String());
auto raw = VLC->modh->identifiers.getIdentifier(CModHandler::scopeBuiltin(), "primSkill", json["rewardPrimSkill"].String());
if(raw)
{
bonusType = PRIM_SKILL;
@ -1813,7 +1813,7 @@ void CGScholar::serializeJsonOptions(JsonSerializeFormat & handler)
}
else if(!json["rewardSkill"].String().empty())
{
auto raw = VLC->modh->identifiers.getIdentifier("core", "skill", json["rewardSkill"].String());
auto raw = VLC->modh->identifiers.getIdentifier(CModHandler::scopeBuiltin(), "skill", json["rewardSkill"].String());
if(raw)
{
bonusType = SECONDARY_SKILL;
@ -1822,7 +1822,7 @@ void CGScholar::serializeJsonOptions(JsonSerializeFormat & handler)
}
else if(!json["rewardSpell"].String().empty())
{
auto raw = VLC->modh->identifiers.getIdentifier("core", "spell", json["rewardSpell"].String());
auto raw = VLC->modh->identifiers.getIdentifier(CModHandler::scopeBuiltin(), "spell", json["rewardSpell"].String());
if(raw)
{
bonusType = SPELL;

View File

@ -287,7 +287,15 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
if(withTerrain && !node["allowedTerrains"].isNull())
{
for(auto& entry : node["allowedTerrains"].Vector())
allowedTerrains.insert(VLC->terrainTypeHandler->getInfoByName(entry.String())->id);
{
try {
allowedTerrains.insert(VLC->terrainTypeHandler->getInfoByName(entry.String())->id);
}
catch (const std::out_of_range & )
{
logGlobal->warn("Failed to find terrain '%s' for object '%s'", entry.String(), animationFile);
}
}
}
else
{
@ -300,7 +308,7 @@ void ObjectTemplate::readJson(const JsonNode &node, const bool withTerrain)
}
if(withTerrain && allowedTerrains.empty())
logGlobal->warn("Loaded template without allowed terrains!");
logGlobal->warn("Loaded template %s without allowed terrains!", animationFile);
auto charToTile = [&](const char & ch) -> ui8
{

View File

@ -256,6 +256,8 @@ CMap::CMap()
CMap::~CMap()
{
getEditManager()->getUndoManager().clearAll();
if(terrain)
{
for(int z = 0; z < levels(); z++)

View File

@ -215,7 +215,7 @@ namespace TriggeredEventsDetail
event.metaType = decodeMetaclass(metaTypeName);
auto type = VLC->modh->identifiers.getIdentifier("core", fullIdentifier, false);
auto type = VLC->modh->identifiers.getIdentifier(CModHandler::scopeBuiltin(), fullIdentifier, false);
if(type)
event.objectType = type.get();
@ -1121,7 +1121,7 @@ void CMapLoaderJson::MapObjectLoader::construct()
return;
}
auto handler = VLC->objtypeh->getHandlerFor( "map", typeName, subtypeName);
auto handler = VLC->objtypeh->getHandlerFor( CModHandler::scopeMap(), typeName, subtypeName);
auto appearance = new ObjectTemplate;
@ -1158,7 +1158,7 @@ void CMapLoaderJson::MapObjectLoader::configure()
if(art->ID == Obj::SPELL_SCROLL)
{
auto spellIdentifier = configuration["options"]["spell"].String();
auto rawId = VLC->modh->identifiers.getIdentifier("core", "spell", spellIdentifier);
auto rawId = VLC->modh->identifiers.getIdentifier(CModHandler::scopeBuiltin(), "spell", spellIdentifier);
if(rawId)
spellID = rawId.get();
else

View File

@ -14,6 +14,7 @@
#include "CRmgTemplate.h"
#include "../serializer/JsonDeserializer.h"
#include "../CModHandler.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -30,7 +31,7 @@ void CRmgTemplateStorage::loadObject(std::string scope, std::string name, const
try
{
JsonDeserializer handler(nullptr, data);
auto fullKey = normalizeIdentifier(scope, "core", name); //actually it's not used
auto fullKey = normalizeIdentifier(scope, CModHandler::scopeBuiltin(), name); //actually it's not used
templates[fullKey].setId(name);
templates[fullKey].serializeJson(handler);
templates[fullKey].validate();

View File

@ -529,7 +529,12 @@ void CZonePlacer::assignZones(CRandomGenerator * rand)
}
for (auto zone : zones)
{
if(zone.second->area().empty())
throw rmgException("Empty zone is generated, probably RMG template is inappropriate for map size");
moveZoneToCenterOfMass(zone.second);
}
//assign actual tiles to each zone using nonlinear norm for fine edges

View File

@ -764,7 +764,7 @@ CSpell * CSpellHandler::loadFromJson(const std::string & scope, const JsonNode &
{
if(counteredSpell.second.Bool())
{
VLC->modh->identifiers.requestIdentifier(json.meta, counteredSpell.first, [=](si32 id)
VLC->modh->identifiers.requestIdentifier(counteredSpell.second.meta, counteredSpell.first, [=](si32 id)
{
spell->counteredSpells.push_back(SpellID(id));
});

View File

@ -1,2 +0,0 @@
add_main_lib(vcmi_lib_client SHARED)
target_compile_definitions(vcmi_lib_client PUBLIC VCMI_LIB_NAMESPACE=LIB_CLIENT)

View File

@ -1,2 +1,3 @@
add_main_lib(vcmi_lib_server STATIC)
target_compile_definitions(vcmi_lib_server PUBLIC VCMI_LIB_NAMESPACE=LIB_SERVER)
target_compile_definitions(vcmi_lib_server PUBLIC VCMI_DLL_STATIC=1)

View File

@ -116,7 +116,7 @@ if(APPLE)
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER vcmieditor)
endif()
target_link_libraries(vcmieditor vcmi Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)
target_link_libraries(vcmieditor ${VCMI_LIB_TARGET} Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)
target_include_directories(vcmieditor
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
)

View File

@ -13,16 +13,21 @@
#include "../lib/GameConstants.h"
#include <QImage>
VCMI_LIB_NAMESPACE_BEGIN
class CGHeroInstance;
class CGTownInstance;
class CGObjectInstance;
class EntityService;
class JsonNode;
class ObjectTemplate;
VCMI_LIB_NAMESPACE_END
class CHeroClass;
struct InfoAboutHero;
struct InfoAboutTown;
class CGObjectInstance;
class ObjectTemplate;
class Animation;
class EntityService;
class JsonNode;
/// Handles fonts, hero images, town images, various graphics
class Graphics

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Some files were not shown because too many files have changed in this diff Show More