mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Merge branch 'develop' of https://github.com/vcmi/vcmi into develop
Trying to sort out git tree.
This commit is contained in:
commit
5a31cc831d
96
.travis.yml
96
.travis.yml
@ -1,52 +1,36 @@
|
||||
language: cpp
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- linux
|
||||
- osx
|
||||
dist: trusty
|
||||
sudo: required
|
||||
|
||||
before_install:
|
||||
- test $TRAVIS_BRANCH != coverity_scan -o ${TRAVIS_JOB_NUMBER##*.} = 1 || exit 0
|
||||
- if [[ $VCMI_PLATFORM == 'linux' ]]; then . .travis.linux; fi
|
||||
- if [[ $VCMI_PLATFORM == 'mac' ]]; then . .travis.osx; fi
|
||||
- if [[ $VCMI_PLATFORM == 'mxe' ]]; then . .travis.mxe; fi
|
||||
|
||||
before_script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if [[ $TRAVIS_BRANCH != 'coverity_scan' ]]; then cmake -G "Unix Makefiles" .. $VCMI_CMAKE_FLAGS; fi
|
||||
|
||||
script:
|
||||
- test $TRAVIS_BRANCH != coverity_scan || exit 0
|
||||
- if [[ $TRAVIS_OS_NAME == 'osx' ]]; then cd ..; xcodebuild -project osx/osx-vcmibuilder/vcmibuilder.xcodeproj/ -configuration Release CONFIGURATION_BUILD_DIR=..; cd build; fi
|
||||
- make -j2
|
||||
|
||||
env:
|
||||
matrix:
|
||||
- ignore=this
|
||||
global:
|
||||
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
|
||||
# via the "travis encrypt" command using the project repo's public key
|
||||
- secure: "NMg+qtQB4DIZ/KqlDeIn3K7A7Ydksdpnbv6Ha9n4bSSA0AT8wlPwbHXvQmiR8qYs6cnz4fyY6NVcBe7X3bdR8jWyPNAS0l0QByqG12q3dBpEtNNn0X5u/GS3wHse5+ObNAF9a83+xACTQj2UdxqHgJ3LFGzdBpQt3kLsc8NDnn8="
|
||||
- secure: NMg+qtQB4DIZ/KqlDeIn3K7A7Ydksdpnbv6Ha9n4bSSA0AT8wlPwbHXvQmiR8qYs6cnz4fyY6NVcBe7X3bdR8jWyPNAS0l0QByqG12q3dBpEtNNn0X5u/GS3wHse5+ObNAF9a83+xACTQj2UdxqHgJ3LFGzdBpQt3kLsc8NDnn8=
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
- env: ignore=this
|
||||
- env: ignore=this
|
||||
include:
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: VCMI_PLATFORM='linux' REAL_CC=clang-3.6 REAL_CXX=clang++-3.6 PACKAGE=clang-3.6 SUPPORT=libstdc++-4.8-dev VCMI_CMAKE_FLAGS='-DENABLE_TEST=0'
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: VCMI_PLATFORM='linux' REAL_CC=clang-3.4 REAL_CXX=clang++-3.4 PACKAGE=clang-3.4 SUPPORT=libstdc++-4.8-dev VCMI_CMAKE_FLAGS='-DENABLE_TEST=0'
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: VCMI_PLATFORM='linux' REAL_CC=gcc-4.8 REAL_CXX=g++-4.8 PACKAGE=g++-4.8 SUPPORT= VCMI_CMAKE_FLAGS='-DENABLE_TEST=0'
|
||||
- os: linux
|
||||
env: VCMI_PLATFORM='mxe' MXE_TARGET=i686-w64-mingw32.shared VCMI_CMAKE_FLAGS='-DENABLE_TEST=0'
|
||||
sudo: required
|
||||
- os: osx
|
||||
env: VCMI_PLATFORM='mac'
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: VCMI_PLATFORM='linux' REAL_CC=clang-3.6 REAL_CXX=clang++-3.6 PACKAGE=clang-3.6
|
||||
SUPPORT=libstdc++-4.8-dev VCMI_CMAKE_FLAGS='-DENABLE_TEST=0'
|
||||
- os: linux
|
||||
compiler: clang
|
||||
env: VCMI_PLATFORM='linux' REAL_CC=clang-3.4 REAL_CXX=clang++-3.4 PACKAGE=clang-3.4
|
||||
SUPPORT=libstdc++-4.8-dev VCMI_CMAKE_FLAGS='-DENABLE_TEST=0'
|
||||
- os: linux
|
||||
compiler: gcc
|
||||
env: VCMI_PLATFORM='linux' REAL_CC=gcc-4.8 REAL_CXX=g++-4.8 PACKAGE=g++-4.8 SUPPORT=
|
||||
VCMI_CMAKE_FLAGS='-DENABLE_TEST=0'
|
||||
- os: linux
|
||||
env: VCMI_PLATFORM='mxe' MXE_TARGET=i686-w64-mingw32.shared VCMI_CMAKE_FLAGS='-DENABLE_TEST=0'
|
||||
sudo: required
|
||||
- os: osx
|
||||
env: VCMI_PLATFORM='mac'
|
||||
|
||||
addons:
|
||||
coverity_scan:
|
||||
@ -54,15 +38,47 @@ addons:
|
||||
name: vcmi/vcmi
|
||||
description: Build submitted via Travis CI
|
||||
notification_email: coverity@arseniyshestakov.com
|
||||
build_command_prepend: "cov-configure --compiler clang-3.6 --comptype clangcc && cov-configure --comptype clangcxx --compiler clang++-3.6 && cmake -G Ninja .. -DCMAKE_BUILD_TYPE=DEBUG -DENABLE_LAUNCHER=0 -DENABLE_TEST=0"
|
||||
build_command_prepend: cov-configure --compiler clang-3.6 --comptype clangcc &&
|
||||
cov-configure --comptype clangcxx --compiler clang++-3.6 && cmake -G Ninja ..
|
||||
-DCMAKE_BUILD_TYPE=DEBUG -DENABLE_LAUNCHER=0 -DENABLE_TEST=0
|
||||
build_command: ninja -j 3
|
||||
branch_pattern: coverity_scan
|
||||
|
||||
notifications:
|
||||
email:
|
||||
recipients:
|
||||
- noreply@vcmi.eu
|
||||
- noreply@vcmi.eu
|
||||
on_success: change
|
||||
on_failure: always
|
||||
slack:
|
||||
secure: "KHXFe14FFKtw5mErWbj730+utqy7i/3AUobWfAMAGvWI5sJYlhbBU+KvvCoD2SlRQg3mQqgwVw8NBJF1Mffs7WcRmrFFFmuMqZxFLAfKBd3T0CxWpAGfnfNgDmlfV4OfEgQWk1pakEPOymhxbbmLUuCjykZDuTcioxAk0UAHDwY="
|
||||
secure: KHXFe14FFKtw5mErWbj730+utqy7i/3AUobWfAMAGvWI5sJYlhbBU+KvvCoD2SlRQg3mQqgwVw8NBJF1Mffs7WcRmrFFFmuMqZxFLAfKBd3T0CxWpAGfnfNgDmlfV4OfEgQWk1pakEPOymhxbbmLUuCjykZDuTcioxAk0UAHDwY=
|
||||
|
||||
before_install:
|
||||
- test $TRAVIS_BRANCH != coverity_scan -o ${TRAVIS_JOB_NUMBER##*.} = 1 || exit 0
|
||||
- . $TRAVIS_BUILD_DIR/CI/$VCMI_PLATFORM/before_install.sh
|
||||
|
||||
before_script:
|
||||
- mkdir build
|
||||
- cd build
|
||||
- if [[ $TRAVIS_BRANCH != 'coverity_scan' ]];
|
||||
then
|
||||
source $TRAVIS_BUILD_DIR/CI/get_package_name.sh;
|
||||
cmake -G "Unix Makefiles" .. $VCMI_CMAKE_FLAGS
|
||||
-DPACKAGE_NAME_SUFFIX:STRING="$VCMI_PACKAGE_NAME_SUFFIX"
|
||||
-DPACKAGE_FILE_NAME:STRING="$VCMI_PACKAGE_FILE_NAME";
|
||||
fi
|
||||
|
||||
script:
|
||||
- test $TRAVIS_BRANCH != coverity_scan || exit 0
|
||||
- if [[ $TRAVIS_OS_NAME == 'osx' ]];
|
||||
then
|
||||
cd ..;
|
||||
xcodebuild -project osx/osx-vcmibuilder/vcmibuilder.xcodeproj/
|
||||
-configuration Release CONFIGURATION_BUILD_DIR=..;
|
||||
cd build;
|
||||
fi
|
||||
- make -j2
|
||||
|
||||
after_success:
|
||||
- test $TRAVIS_BRANCH != coverity_scan || exit 0
|
||||
- . $TRAVIS_BUILD_DIR/CI/$VCMI_PLATFORM/upload_package.sh
|
||||
|
@ -259,11 +259,9 @@ void CBattleAI::attemptCastingSpell()
|
||||
{
|
||||
StackWithBonuses swb;
|
||||
swb.stack = sta;
|
||||
Bonus pseudoBonus;
|
||||
pseudoBonus.sid = ps.spell->id;
|
||||
pseudoBonus.val = skillLevel;
|
||||
pseudoBonus.turnsRemain = 1; //TODO
|
||||
CStack::stackEffectToFeature(swb.bonusesToAdd, pseudoBonus);
|
||||
//todo: handle effect actualization in HypotheticChangesToBattleState
|
||||
ps.spell->getEffects(swb.bonusesToAdd, skillLevel, false, hero->getEnchantPower(ps.spell));
|
||||
ps.spell->getEffects(swb.bonusesToAdd, skillLevel, true, hero->getEnchantPower(ps.spell));
|
||||
HypotheticChangesToBattleState state;
|
||||
state.bonusesOfStacks[swb.stack] = &swb;
|
||||
PotentialTargets pt(swb.stack, state);
|
||||
|
@ -746,7 +746,7 @@ void VCAI::makeTurn()
|
||||
logGlobal->info("Player %d (%s) starting turn", playerID, playerID.getStr());
|
||||
|
||||
MAKING_TURN;
|
||||
boost::shared_lock<boost::shared_mutex> gsLock(cb->getGsMutex());
|
||||
boost::shared_lock<boost::shared_mutex> gsLock(CGameState::mutex);
|
||||
setThreadName("VCAI::makeTurn");
|
||||
|
||||
switch(cb->getDate(Date::DAY_OF_WEEK))
|
||||
@ -1688,7 +1688,7 @@ void VCAI::battleEnd(const BattleResult *br)
|
||||
|
||||
void VCAI::waitTillFree()
|
||||
{
|
||||
auto unlock = vstd::makeUnlockSharedGuard(cb->getGsMutex());
|
||||
auto unlock = vstd::makeUnlockSharedGuard(CGameState::mutex);
|
||||
status.waitTillFree();
|
||||
}
|
||||
|
||||
@ -2787,7 +2787,7 @@ void VCAI::requestActionASAP(std::function<void()> whatToDo)
|
||||
{
|
||||
setThreadName("VCAI::requestActionASAP::whatToDo");
|
||||
SET_GLOBAL_STATE(this);
|
||||
boost::shared_lock<boost::shared_mutex> gsLock(cb->getGsMutex());
|
||||
boost::shared_lock<boost::shared_mutex> gsLock(CGameState::mutex);
|
||||
whatToDo();
|
||||
});
|
||||
}
|
||||
|
@ -180,7 +180,7 @@ int CBattleCallback::sendRequest(const CPack *request)
|
||||
if(waitTillRealize)
|
||||
{
|
||||
logGlobal->traceStream() << boost::format("We'll wait till request %d is answered.\n") % requestID;
|
||||
auto gsUnlocker = vstd::makeUnlockSharedGuardIf(getGsMutex(), unlockGsWhenWaiting);
|
||||
auto gsUnlocker = vstd::makeUnlockSharedGuardIf(CGameState::mutex, unlockGsWhenWaiting);
|
||||
cl->waitingRequest.waitWhileContains(requestID);
|
||||
}
|
||||
|
||||
|
BIN
CI/deploy_rsa.enc
Normal file
BIN
CI/deploy_rsa.enc
Normal file
Binary file not shown.
16
CI/get_package_name.sh
Normal file
16
CI/get_package_name.sh
Normal file
@ -0,0 +1,16 @@
|
||||
#!/bin/sh
|
||||
VCMI_PACKAGE_FILE_NAME="${TRAVIS_JOB_ID}-vcmi"
|
||||
VCMI_PACKAGE_NAME_SUFFIX=""
|
||||
if [ "$TRAVIS_PULL_REQUEST" = "false" ];
|
||||
then
|
||||
branch_name=$(echo "$TRAVIS_BRANCH" | sed 's/[^[:alnum:]]\+/_/g')
|
||||
VCMI_PACKAGE_FILE_NAME="${VCMI_PACKAGE_FILE_NAME}-branch-${branch_name}-${TRAVIS_COMMIT}"
|
||||
VCMI_PACKAGE_NAME_SUFFIX="branch ${branch_name}"
|
||||
else
|
||||
VCMI_PACKAGE_FILE_NAME="${VCMI_PACKAGE_FILE_NAME}-PR-${TRAVIS_PULL_REQUEST}-${TRAVIS_COMMIT}"
|
||||
VCMI_PACKAGE_NAME_SUFFIX="PR ${TRAVIS_PULL_REQUEST}"
|
||||
fi
|
||||
VCMI_PACKAGE_NAME_SUFFIX="(${VCMI_PACKAGE_NAME_SUFFIX})"
|
||||
|
||||
export VCMI_PACKAGE_FILE_NAME
|
||||
export VCMI_PACKAGE_NAME_SUFFIX
|
1
CI/linux/upload_package.sh
Normal file
1
CI/linux/upload_package.sh
Normal file
@ -0,0 +1 @@
|
||||
#!/bin/sh
|
@ -14,4 +14,4 @@ wget https://github.com/sparkle-project/Sparkle/releases/download/$sparkle_versi
|
||||
mkdir sparkle && cd sparkle
|
||||
tar -xf ../Sparkle-*.tar.bz2
|
||||
sudo mv Sparkle.framework /Library/Frameworks/
|
||||
cd .. && rm -rf sparkle
|
||||
cd .. && rm -rf sparkle
|
10
CI/mac/upload_package.sh
Normal file
10
CI/mac/upload_package.sh
Normal file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
cpack
|
||||
|
||||
touch /tmp/deploy_rsa
|
||||
chmod 600 /tmp/deploy_rsa
|
||||
openssl aes-256-cbc -K $encrypted_1d30f79f8582_key -iv $encrypted_1d30f79f8582_iv -in $TRAVIS_BUILD_DIR/CI/deploy_rsa.enc -out /tmp/deploy_rsa -d
|
||||
eval "$(ssh-agent -s)"
|
||||
ssh-add /tmp/deploy_rsa
|
||||
|
||||
sftp -r -o StrictHostKeyChecking=no travis@beholder.vcmi.eu <<< "put $VCMI_PACKAGE_FILE_NAME.dmg /incoming/$VCMI_PACKAGE_FILE_NAME.dmg"
|
@ -22,6 +22,9 @@ mxe-$MXE_TARGET-ffmpeg \
|
||||
mxe-$MXE_TARGET-qt \
|
||||
mxe-$MXE_TARGET-qtbase
|
||||
|
||||
# Install nsis for installer creation
|
||||
sudo apt-get install -qq nsis
|
||||
|
||||
# alias for CMake
|
||||
sudo mv /usr/bin/cmake /usr/bin/cmake.orig
|
||||
sudo ln -s /usr/lib/mxe/usr/bin/$MXE_TARGET-cmake /usr/bin/cmake
|
10
CI/mxe/upload_package.sh
Normal file
10
CI/mxe/upload_package.sh
Normal file
@ -0,0 +1,10 @@
|
||||
#!/bin/sh
|
||||
cpack
|
||||
|
||||
touch /tmp/deploy_rsa
|
||||
chmod 600 /tmp/deploy_rsa
|
||||
openssl aes-256-cbc -K $encrypted_1d30f79f8582_key -iv $encrypted_1d30f79f8582_iv -in $TRAVIS_BUILD_DIR/CI/deploy_rsa.enc -out /tmp/deploy_rsa -d
|
||||
eval "$(ssh-agent -s)"
|
||||
ssh-add /tmp/deploy_rsa
|
||||
|
||||
sftp -r -o StrictHostKeyChecking=no travis@beholder.vcmi.eu <<< "put $VCMI_PACKAGE_FILE_NAME.exe /incoming/$VCMI_PACKAGE_FILE_NAME.exe"
|
@ -16,6 +16,10 @@ set(VCMI_VERSION_MAJOR 0)
|
||||
set(VCMI_VERSION_MINOR 99)
|
||||
set(VCMI_VERSION_PATCH 0)
|
||||
|
||||
# Allow to pass package name from Travis CI
|
||||
set(PACKAGE_NAME_SUFFIX "" CACHE STRING "Suffix for CPack package name")
|
||||
set(PACKAGE_FILE_NAME "" CACHE STRING "Override for CPack package filename")
|
||||
|
||||
option(ENABLE_ERM "Enable compilation of ERM scripting module" OFF)
|
||||
option(ENABLE_EDITOR "Enable compilation of map editor" OFF)
|
||||
option(ENABLE_LAUNCHER "Enable compilation of launcher" ON)
|
||||
@ -238,14 +242,17 @@ endif()
|
||||
# For apple these files will be already inside vcmiclient bundle
|
||||
if (NOT APPLE)
|
||||
# copy whole directory but .svn control files
|
||||
install(DIRECTORY config DESTINATION ${DATA_DIR} PATTERN ".svn" EXCLUDE)
|
||||
install(DIRECTORY config DESTINATION ${DATA_DIR})
|
||||
# copy vcmi mod along with all its content
|
||||
install(DIRECTORY Mods/vcmi DESTINATION ${DATA_DIR}/Mods PATTERN ".svn" EXCLUDE)
|
||||
install(DIRECTORY Mods/vcmi DESTINATION ${DATA_DIR}/Mods)
|
||||
|
||||
install(FILES vcmibuilder DESTINATION ${BIN_DIR} PERMISSIONS
|
||||
OWNER_WRITE OWNER_READ OWNER_EXECUTE
|
||||
GROUP_READ GROUP_EXECUTE
|
||||
WORLD_READ WORLD_EXECUTE)
|
||||
# that script is useless for Windows
|
||||
if (NOT WIN32)
|
||||
install(FILES vcmibuilder DESTINATION ${BIN_DIR} PERMISSIONS
|
||||
OWNER_WRITE OWNER_READ OWNER_EXECUTE
|
||||
GROUP_READ GROUP_EXECUTE
|
||||
WORLD_READ WORLD_EXECUTE)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
@ -317,15 +324,28 @@ set(CPACK_PACKAGE_VERSION "${CPACK_PACKAGE_VERSION_MAJOR}.${CPACK_PACKAGE_VERSIO
|
||||
|
||||
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
|
||||
|
||||
if("${PACKAGE_NAME_SUFFIX}" STREQUAL "")
|
||||
set(CPACK_PACKAGE_NAME "VCMI")
|
||||
else()
|
||||
set(CPACK_PACKAGE_NAME "VCMI ${PACKAGE_NAME_SUFFIX}")
|
||||
endif()
|
||||
if("${PACKAGE_FILE_NAME}" STREQUAL "")
|
||||
set(CPACK_PACKAGE_FILE_NAME "vcmi-${CPACK_PACKAGE_VERSION}")
|
||||
else()
|
||||
set(CPACK_PACKAGE_FILE_NAME "${PACKAGE_FILE_NAME}")
|
||||
endif()
|
||||
set(CPACK_PACKAGE_VENDOR "VCMI team")
|
||||
|
||||
if(WIN32)
|
||||
set(CPACK_MONOLITHIC_INSTALL 1)
|
||||
set(CPACK_PACKAGE_NAME "VCMI")
|
||||
set(CPACK_PACKAGE_VENDOR "VCMI team")
|
||||
set(CPACK_PACKAGE_FILE_NAME "vcmi-${CPACK_PACKAGE_VERSION}-win32")
|
||||
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/license.txt")
|
||||
set(CPACK_PACKAGE_EXECUTABLES "VCMI_launcher;VCMI")
|
||||
set(CPACK_PACKAGE_INSTALL_DIRECTORY "${CPACK_PACKAGE_NAME}")
|
||||
set(CPACK_NSIS_PACKAGE_NAME "VCMI ${CPACK_PACKAGE_VERSION}")
|
||||
if("${PACKAGE_NAME_SUFFIX}" STREQUAL "")
|
||||
set(CPACK_NSIS_PACKAGE_NAME "VCMI ${CPACK_PACKAGE_VERSION}")
|
||||
else()
|
||||
set(CPACK_NSIS_PACKAGE_NAME "VCMI ${CPACK_PACKAGE_VERSION} ${PACKAGE_NAME_SUFFIX} ")
|
||||
endif()
|
||||
set(CPACK_NSIS_INSTALL_ROOT "$PROGRAMFILES")
|
||||
set(CPACK_NSIS_EXTRA_INSTALL_COMMANDS " CreateShortCut \\\"$DESKTOP\\\\VCMI.lnk\\\" \\\"$INSTDIR\\\\VCMI_launcher.exe\\\"")
|
||||
set(CPACK_NSIS_EXTRA_UNINSTALL_COMMANDS " Delete \\\"$DESKTOP\\\\VCMI.lnk\\\" ")
|
||||
@ -340,8 +360,9 @@ else()
|
||||
set(CPACK_GENERATOR TGZ)
|
||||
endif()
|
||||
|
||||
INCLUDE(CPack)
|
||||
|
||||
include(GetGitRevisionDescription)
|
||||
get_git_head_revision(GIT_REFSPEC GIT_SHA1)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Version.cpp.in" "${CMAKE_BINARY_DIR}/Version.cpp" @ONLY)
|
||||
|
||||
INCLUDE(CPack)
|
||||
|
@ -3,6 +3,9 @@
|
||||
GENERAL:
|
||||
* Spectator mode was implemented through command-line options
|
||||
|
||||
SPELLS:
|
||||
* Implemented cumulative effects for spells
|
||||
|
||||
0.98 -> 0.99
|
||||
|
||||
GENERAL:
|
||||
|
@ -123,7 +123,6 @@ CPlayerInterface::CPlayerInterface(PlayerColor Player)
|
||||
currentSelection = nullptr;
|
||||
castleInt = nullptr;
|
||||
battleInt = nullptr;
|
||||
//pim = new boost::recursive_mutex;
|
||||
makingTurn = false;
|
||||
showingDialog = new CondSh<bool>(false);
|
||||
cingconsole = new CInGameConsole;
|
||||
@ -140,8 +139,6 @@ CPlayerInterface::~CPlayerInterface()
|
||||
{
|
||||
logGlobal->traceStream() << "\tHuman player interface for player " << playerID << " being destructed";
|
||||
//howManyPeople--;
|
||||
//delete pim;
|
||||
//vstd::clear_pointer(pim);
|
||||
delete showingDialog;
|
||||
delete cingconsole;
|
||||
if (LOCPLINT == this)
|
||||
@ -821,10 +818,10 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i
|
||||
|
||||
CBattleInterface *b = battleInt;
|
||||
|
||||
if (b->givenCommand->get())
|
||||
if(CBattleInterface::givenCommand.get())
|
||||
{
|
||||
logGlobal->errorStream() << "Command buffer must be clean! (we don't want to use old command)";
|
||||
vstd::clear_pointer(b->givenCommand->data);
|
||||
vstd::clear_pointer(CBattleInterface::givenCommand.data);
|
||||
}
|
||||
|
||||
{
|
||||
@ -833,17 +830,17 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i
|
||||
//Regeneration & mana drain go there
|
||||
}
|
||||
//wait till BattleInterface sets its command
|
||||
boost::unique_lock<boost::mutex> lock(b->givenCommand->mx);
|
||||
while(!b->givenCommand->data)
|
||||
boost::unique_lock<boost::mutex> lock(CBattleInterface::givenCommand.mx);
|
||||
while(!CBattleInterface::givenCommand.data)
|
||||
{
|
||||
b->givenCommand->cond.wait(lock);
|
||||
CBattleInterface::givenCommand.cond.wait(lock);
|
||||
if (!battleInt) //battle ended while we were waiting for movement (eg. because of spell)
|
||||
throw boost::thread_interrupted(); //will shut the thread peacefully
|
||||
}
|
||||
|
||||
//tidy up
|
||||
BattleAction ret = *(b->givenCommand->data);
|
||||
vstd::clear_pointer(b->givenCommand->data);
|
||||
BattleAction ret = *(CBattleInterface::givenCommand.data);
|
||||
vstd::clear_pointer(CBattleInterface::givenCommand.data);
|
||||
|
||||
if (ret.actionType == Battle::CANCEL)
|
||||
{
|
||||
@ -1637,7 +1634,7 @@ void CPlayerInterface::setSelection(const CArmedInstance * obj)
|
||||
void CPlayerInterface::update()
|
||||
{
|
||||
// Make sure that gamestate won't change when GUI objects may obtain its parts on event processing or drawing request
|
||||
boost::shared_lock<boost::shared_mutex> gsLock(cb->getGsMutex());
|
||||
boost::shared_lock<boost::shared_mutex> gsLock(CGameState::mutex);
|
||||
|
||||
// While mutexes were locked away we may be have stopped being the active interface
|
||||
if (LOCPLINT != this)
|
||||
|
@ -47,6 +47,7 @@
|
||||
*/
|
||||
|
||||
CondSh<bool> CBattleInterface::animsAreDisplayed(false);
|
||||
CondSh<BattleAction *> CBattleInterface::givenCommand(nullptr);
|
||||
|
||||
static void onAnimationFinished(const CStack *stack, CCreatureAnimation *anim)
|
||||
{
|
||||
@ -101,7 +102,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
|
||||
currentlyHoveredHex(-1), attackingHex(-1), stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellToCast(nullptr), sp(nullptr),
|
||||
creatureSpellToCast(-1),
|
||||
siegeH(nullptr), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0),
|
||||
givenCommand(nullptr), myTurn(false), resWindow(nullptr), moveStarted(false), moveSoundHander(-1), bresult(nullptr)
|
||||
myTurn(false), resWindow(nullptr), moveStarted(false), moveSoundHander(-1), bresult(nullptr)
|
||||
{
|
||||
OBJ_CONSTRUCTION;
|
||||
|
||||
@ -117,7 +118,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
|
||||
animsAreDisplayed.setn(false);
|
||||
pos = myRect;
|
||||
strongInterest = true;
|
||||
givenCommand = new CondSh<BattleAction *>(nullptr);
|
||||
givenCommand.setn(nullptr);
|
||||
|
||||
//hot-seat -> check tactics for both players (defender may be local human)
|
||||
if (attackerInt && attackerInt->cb->battleGetTacticDist())
|
||||
@ -387,8 +388,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
|
||||
CBattleInterface::~CBattleInterface()
|
||||
{
|
||||
curInt->battleInt = nullptr;
|
||||
givenCommand->cond.notify_all(); //that two lines should make any activeStack waiting thread to finish
|
||||
|
||||
givenCommand.cond.notify_all(); //that two lines should make any activeStack waiting thread to finish
|
||||
|
||||
if (active) //dirty fix for #485
|
||||
{
|
||||
@ -416,7 +416,6 @@ CBattleInterface::~CBattleInterface()
|
||||
delete bConsoleUp;
|
||||
delete bConsoleDown;
|
||||
delete console;
|
||||
delete givenCommand;
|
||||
|
||||
delete attackingHero;
|
||||
delete defendingHero;
|
||||
@ -1028,7 +1027,7 @@ void CBattleInterface::stackRemoved(int stackID)
|
||||
action->side = defendingHeroInstance ? (curInt->playerID == defendingHeroInstance->tempOwner) : false;
|
||||
action->actionType = Battle::CANCEL;
|
||||
action->stackNumber = activeStack->ID;
|
||||
givenCommand->setn(action);
|
||||
givenCommand.setn(action);
|
||||
setActiveStack(nullptr);
|
||||
}
|
||||
}
|
||||
@ -1149,7 +1148,7 @@ void CBattleInterface::giveCommand(Battle::ActionType action, BattleHex tile, ui
|
||||
logGlobal->traceStream() << "Setting command for " << (stack ? stack->nodeName() : "hero");
|
||||
myTurn = false;
|
||||
setActiveStack(nullptr);
|
||||
givenCommand->setn(ba);
|
||||
givenCommand.setn(ba);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1359,52 +1358,44 @@ void CBattleInterface::spellCast(const BattleSpellCast *sc)
|
||||
|
||||
void CBattleInterface::battleStacksEffectsSet(const SetStackEffect & sse)
|
||||
{
|
||||
if (sse.effect.back().sid == -1 && sse.stacks.size() == 1 && sse.effect.size() == 2)
|
||||
if(sse.stacks.size() == 1 && sse.effect.size() == 2 && sse.effect.back().sid == -1)
|
||||
{
|
||||
const Bonus & bns = sse.effect.front();
|
||||
if (bns.source == Bonus::OTHER && bns.type == Bonus::PRIMARY_SKILL)
|
||||
if(bns.source == Bonus::OTHER && bns.type == Bonus::PRIMARY_SKILL)
|
||||
{
|
||||
//defensive stance
|
||||
const CStack *stack = LOCPLINT->cb->battleGetStackByID(*sse.stacks.begin());
|
||||
int txtid = 120;
|
||||
|
||||
if (stack->count != 1)
|
||||
if(stack->count != 1)
|
||||
txtid++; //move to plural text
|
||||
|
||||
BonusList defenseBonuses = *(stack->getBonuses(Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE)));
|
||||
defenseBonuses.remove_if (Bonus::UntilGetsTurn); //remove bonuses gained from defensive stance
|
||||
defenseBonuses.remove_if(Bonus::UntilGetsTurn); //remove bonuses gained from defensive stance
|
||||
int val = stack->Defense() - defenseBonuses.totalValue();
|
||||
auto txt = boost::format (CGI->generaltexth->allTexts[txtid]) % ((stack->count != 1) ? stack->getCreature()->namePl : stack->getCreature()->nameSing) % val;
|
||||
auto txt = boost::format(CGI->generaltexth->allTexts[txtid]) % ((stack->count != 1) ? stack->getCreature()->namePl : stack->getCreature()->nameSing) % val;
|
||||
console->addText(boost::to_string(txt));
|
||||
}
|
||||
}
|
||||
|
||||
if (activeStack != nullptr) //it can be -1 when a creature casts effect
|
||||
{
|
||||
if(activeStack != nullptr)
|
||||
redrawBackgroundWithHexes(activeStack);
|
||||
}
|
||||
}
|
||||
|
||||
CBattleInterface::PossibleActions CBattleInterface::getCasterAction(const CSpell *spell, const ISpellCaster *caster, ECastingMode::ECastingMode mode) const
|
||||
CBattleInterface::PossibleActions CBattleInterface::getCasterAction(const CSpell * spell, const ISpellCaster * caster, ECastingMode::ECastingMode mode) const
|
||||
{
|
||||
PossibleActions spellSelMode = ANY_LOCATION;
|
||||
|
||||
const CSpell::TargetInfo ti(spell, caster->getSpellSchoolLevel(spell), mode);
|
||||
|
||||
if (ti.massive || ti.type == CSpell::NO_TARGET)
|
||||
if(ti.massive || ti.type == CSpell::NO_TARGET)
|
||||
spellSelMode = NO_LOCATION;
|
||||
else if (ti.type == CSpell::LOCATION && ti.clearAffected)
|
||||
{
|
||||
else if(ti.type == CSpell::LOCATION && ti.clearAffected)
|
||||
spellSelMode = FREE_LOCATION;
|
||||
}
|
||||
else if (ti.type == CSpell::CREATURE)
|
||||
{
|
||||
else if(ti.type == CSpell::CREATURE)
|
||||
spellSelMode = AIMED_SPELL_CREATURE;
|
||||
}
|
||||
else if (ti.type == CSpell::OBSTACLE)
|
||||
{
|
||||
else if(ti.type == CSpell::OBSTACLE)
|
||||
spellSelMode = OBSTACLE;
|
||||
}
|
||||
|
||||
return spellSelMode;
|
||||
}
|
||||
@ -2848,7 +2839,7 @@ void CBattleInterface::requestAutofightingAIToTakeAction()
|
||||
}
|
||||
else
|
||||
{
|
||||
givenCommand->setn(ba.release());
|
||||
givenCommand.setn(ba.release());
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -264,10 +264,13 @@ private:
|
||||
|
||||
PossibleActions getCasterAction(const CSpell *spell, const ISpellCaster *caster, ECastingMode::ECastingMode mode) const;
|
||||
public:
|
||||
static CondSh<bool> animsAreDisplayed; //for waiting with the end of battle for end of anims
|
||||
static CondSh<BattleAction *> givenCommand; //data != nullptr if we have i.e. moved current unit
|
||||
|
||||
std::list<std::pair<CBattleAnimation *, bool>> pendingAnims; //currently displayed animations <anim, initialized>
|
||||
void addNewAnim(CBattleAnimation *anim); //adds new anim to pendingAnims
|
||||
ui32 animIDhelper; //for giving IDs for animations
|
||||
static CondSh<bool> animsAreDisplayed; //for waiting with the end of battle for end of anims
|
||||
|
||||
|
||||
CBattleInterface(const CCreatureSet *army1, const CCreatureSet *army2, const CGHeroInstance *hero1, const CGHeroInstance *hero2, const SDL_Rect & myRect, std::shared_ptr<CPlayerInterface> att, std::shared_ptr<CPlayerInterface> defen, std::shared_ptr<CPlayerInterface> spectatorInt = nullptr); //c-tor
|
||||
virtual ~CBattleInterface(); //d-tor
|
||||
@ -282,7 +285,7 @@ public:
|
||||
|
||||
std::vector<CClickableHex*> bfield; //11 lines, 17 hexes on each
|
||||
SDL_Surface *cellBorder, *cellShade;
|
||||
CondSh<BattleAction *> *givenCommand; //data != nullptr if we have i.e. moved current unit
|
||||
|
||||
bool myTurn; //if true, interface is active (commands can be ordered)
|
||||
CBattleResultWindow *resWindow; //window of end of battle
|
||||
|
||||
|
@ -19,25 +19,25 @@
|
||||
{
|
||||
//assumed verticalPosition: top
|
||||
"type": "string",
|
||||
"format": "defFile"
|
||||
"format": "defFile"
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"properties":{
|
||||
"verticalPosition": {"type":"string", "enum":["top","bottom"]},
|
||||
"defName": {"type":"string", "format": "defFile"}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"animation":{
|
||||
"type": "object",
|
||||
"additionalProperties" : false,
|
||||
"properties":{
|
||||
"affect":{"$ref" : "#/definitions/animationQueue"},
|
||||
"hit":{"$ref" : "#/definitions/animationQueue"},
|
||||
"affect":{"$ref" : "#/definitions/animationQueue"},
|
||||
"hit":{"$ref" : "#/definitions/animationQueue"},
|
||||
"cast":{"$ref" : "#/definitions/animationQueue"},
|
||||
"projectile":{
|
||||
"type":"array",
|
||||
@ -46,11 +46,11 @@
|
||||
"properties":{
|
||||
"minimumAngle": {"type":"number", "minimum" : 0},
|
||||
"defName": {"type":"string", "format": "defFile"}
|
||||
},
|
||||
"additionalProperties" : false
|
||||
},
|
||||
"additionalProperties" : false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"flags" :{
|
||||
"type" : "object",
|
||||
@ -85,7 +85,14 @@
|
||||
},
|
||||
"effects":{
|
||||
"type": "object",
|
||||
"description": "Timed effects",
|
||||
"description": "Timed effects (updated by prolongation)",
|
||||
"additionalProperties" : {
|
||||
"$ref" : "vcmi:bonus"
|
||||
}
|
||||
},
|
||||
"cumulativeEffects":{
|
||||
"type": "object",
|
||||
"description": "Timed effects (updated by unique bonus)",
|
||||
"additionalProperties" : {
|
||||
"$ref" : "vcmi:bonus"
|
||||
}
|
||||
@ -107,16 +114,16 @@
|
||||
{
|
||||
"type": "boolean",
|
||||
"description": "LOCATION target only. All affected hexes/tile must be clear"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
"texts":{
|
||||
"type": "object",
|
||||
|
||||
|
||||
|
||||
|
||||
"additionalProperties" : false
|
||||
}
|
||||
},
|
||||
@ -239,9 +246,9 @@
|
||||
"$ref" : "#/definitions/flags",
|
||||
"description": "flags structure of bonus names, presence of all bonuses required to be affected by, can't be negated."
|
||||
},
|
||||
|
||||
|
||||
"animation":{"$ref": "#/definitions/animation"},
|
||||
|
||||
|
||||
"graphics":{
|
||||
"type": "object",
|
||||
"additionalProperties" : false,
|
||||
@ -291,7 +298,7 @@
|
||||
"additionalProperties" : false,
|
||||
"required" : ["none", "basic", "advanced", "expert"],
|
||||
|
||||
"properties":{
|
||||
"properties":{
|
||||
"base":{
|
||||
"type": "object",
|
||||
"description": "will be merged with all levels",
|
||||
|
@ -101,6 +101,7 @@
|
||||
"levels" : {
|
||||
"base":{
|
||||
"range" : "0",
|
||||
//no cumulative effect even with mods here
|
||||
"effects" : {
|
||||
"bindEffect" : {
|
||||
"val" : 0,
|
||||
@ -335,7 +336,7 @@
|
||||
"base":{
|
||||
"range" : "0",
|
||||
"targetModifier":{"smart":true},
|
||||
"effects" : {
|
||||
"cumulativeEffects" : {
|
||||
"primarySkill" : {
|
||||
"val" : -3,
|
||||
"type" : "PRIMARY_SKILL",
|
||||
|
@ -13,6 +13,7 @@
|
||||
"base":{
|
||||
"range" : "0",
|
||||
"targetModifier":{"smart":true},
|
||||
//no cumulative effect even with mods here
|
||||
"effects" : {
|
||||
"generalDamageReduction" : {
|
||||
"type" : "GENERAL_DAMAGE_REDUCTION",
|
||||
@ -43,6 +44,7 @@
|
||||
"base":{
|
||||
"range" : "0",
|
||||
"targetModifier":{"smart":true},
|
||||
//no cumulative effect even with mods here
|
||||
"effects" : {
|
||||
"generalDamageReduction" : {
|
||||
"type" : "GENERAL_DAMAGE_REDUCTION",
|
||||
@ -558,7 +560,7 @@
|
||||
"base":{
|
||||
"range" : "0",
|
||||
"targetModifier":{"smart":true},
|
||||
"effects" : {
|
||||
"cumulativeEffects" : {
|
||||
"primarySkill" : {
|
||||
"type" : "PRIMARY_SKILL",
|
||||
"subtype" : "primSkill.defence",
|
||||
@ -569,14 +571,14 @@
|
||||
}
|
||||
},
|
||||
"advanced":{
|
||||
"effects" : {
|
||||
"cumulativeEffects" : {
|
||||
"primarySkill" : {
|
||||
"val" : -4
|
||||
}
|
||||
}
|
||||
},
|
||||
"expert":{
|
||||
"effects" : {
|
||||
"cumulativeEffects" : {
|
||||
"primarySkill" : {
|
||||
"val" : -5
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ void CArtifact::addNewBonus(const std::shared_ptr<Bonus>& b)
|
||||
|
||||
void CArtifact::fillWarMachine()
|
||||
{
|
||||
switch (id)
|
||||
switch(id)
|
||||
{
|
||||
case ArtifactID::CATAPULT:
|
||||
warMachine = CreatureID::CATAPULT;
|
||||
@ -144,8 +144,10 @@ void CArtifact::fillWarMachine()
|
||||
case ArtifactID::AMMO_CART:
|
||||
warMachine = CreatureID::AMMO_CART;
|
||||
break;
|
||||
default:
|
||||
warMachine = CreatureID::NONE;
|
||||
break;
|
||||
}
|
||||
warMachine = CreatureID::NONE; //this artifact is not a creature
|
||||
}
|
||||
|
||||
void CGrowingArtifact::levelUpArtifact (CArtifactInstance * art)
|
||||
|
@ -100,11 +100,6 @@ namespace SiegeStuffThatShouldBeMovedToHandlers // <=== TODO
|
||||
|
||||
using namespace SiegeStuffThatShouldBeMovedToHandlers;
|
||||
|
||||
boost::shared_mutex& CCallbackBase::getGsMutex()
|
||||
{
|
||||
return *gs->mx;
|
||||
}
|
||||
|
||||
bool CCallbackBase::duringBattle() const
|
||||
{
|
||||
return getBattle() != nullptr;
|
||||
|
@ -62,7 +62,6 @@ protected:
|
||||
bool duringBattle() const;
|
||||
|
||||
public:
|
||||
boost::shared_mutex &getGsMutex(); //just return a reference to mutex, does not lock nor anything
|
||||
boost::optional<PlayerColor> getPlayerID() const;
|
||||
|
||||
friend class CBattleInfoEssentials;
|
||||
|
@ -45,6 +45,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
boost::shared_mutex CGameState::mutex;
|
||||
|
||||
template <typename T> class CApplyOnGS;
|
||||
|
||||
class CBaseForGSApply
|
||||
@ -65,7 +67,7 @@ public:
|
||||
{
|
||||
T *ptr = static_cast<T*>(pack);
|
||||
|
||||
boost::unique_lock<boost::shared_mutex> lock(*gs->mx);
|
||||
boost::unique_lock<boost::shared_mutex> lock(CGameState::mutex);
|
||||
ptr->applyGs(gs);
|
||||
}
|
||||
};
|
||||
@ -675,7 +677,6 @@ int CGameState::getDate(Date::EDateType mode) const
|
||||
CGameState::CGameState()
|
||||
{
|
||||
gs = this;
|
||||
mx = new boost::shared_mutex();
|
||||
applierGs = new CApplier<CBaseForGSApply>;
|
||||
registerTypesClientPacks1(*applierGs);
|
||||
registerTypesClientPacks2(*applierGs);
|
||||
@ -687,7 +688,6 @@ CGameState::CGameState()
|
||||
|
||||
CGameState::~CGameState()
|
||||
{
|
||||
//delete mx;//TODO: crash on Linux (mutex must be unlocked before destruction)
|
||||
map.dellNull();
|
||||
curB.dellNull();
|
||||
//delete scenarioOps; //TODO: fix for loading ind delete
|
||||
|
@ -213,7 +213,7 @@ public:
|
||||
CBonusSystemNode globalEffects;
|
||||
RumorState rumor;
|
||||
|
||||
boost::shared_mutex *mx;
|
||||
static boost::shared_mutex mutex;
|
||||
|
||||
void giveHeroArtifact(CGHeroInstance *h, ArtifactID aid);
|
||||
|
||||
|
@ -104,21 +104,6 @@ si32 CStack::magicResistance() const
|
||||
return magicResistance;
|
||||
}
|
||||
|
||||
void CStack::stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse)
|
||||
{
|
||||
const CSpell * sp = SpellID(sse.sid).toSpell();
|
||||
|
||||
std::vector<Bonus> tmp;
|
||||
sp->getEffects(tmp, sse.val);
|
||||
|
||||
for(Bonus& b : tmp)
|
||||
{
|
||||
if(b.turnsRemain == 0)
|
||||
b.turnsRemain = sse.turnsRemain;
|
||||
sf.push_back(b);
|
||||
}
|
||||
}
|
||||
|
||||
bool CStack::willMove(int turn /*= 0*/) const
|
||||
{
|
||||
return ( turn ? true : !vstd::contains(state, EBattleStackState::DEFENDING) )
|
||||
|
@ -62,7 +62,6 @@ public:
|
||||
ui32 calculateHealedHealthPoints(ui32 toHeal, const bool resurrect) const;
|
||||
ui32 level() const;
|
||||
si32 magicResistance() const override; //include aura of resistance
|
||||
static void stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse);
|
||||
std::vector<si32> activeSpells() const; //returns vector of active spell IDs sorted by time of cast
|
||||
const CGHeroInstance *getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, nullptr otherwise
|
||||
ui32 totalHealth() const; // total health for all creatures in stack;
|
||||
|
@ -1519,11 +1519,19 @@ struct SetStackEffect : public CPackForClient
|
||||
void applyCl(CClient *cl);
|
||||
|
||||
std::vector<ui32> stacks; //affected stacks (IDs)
|
||||
|
||||
//regular effects
|
||||
std::vector<Bonus> effect; //bonuses to apply
|
||||
std::vector<std::pair<ui32, Bonus> > uniqueBonuses; //bonuses per single stack
|
||||
|
||||
//cumulative effects
|
||||
std::vector<Bonus> cumulativeEffects; //bonuses to apply
|
||||
std::vector<std::pair<ui32, Bonus> > cumulativeUniqueBonuses; //bonuses per single stack
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & stacks & effect & uniqueBonuses;
|
||||
h & cumulativeEffects & cumulativeUniqueBonuses;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1554,33 +1554,39 @@ void actualizeEffect(CStack * s, const std::vector<Bonus> & ef)
|
||||
|
||||
DLL_LINKAGE void SetStackEffect::applyGs(CGameState *gs)
|
||||
{
|
||||
if(effect.empty())
|
||||
if(effect.empty() && cumulativeEffects.empty())
|
||||
{
|
||||
logGlobal->errorStream() << "Trying to apply SetStackEffect with no effects";
|
||||
return;
|
||||
}
|
||||
|
||||
int spellid = effect.begin()->sid; //effects' source ID
|
||||
si32 spellid = effect.empty() ? cumulativeEffects.begin()->sid : effect.begin()->sid; //effects' source ID
|
||||
|
||||
auto processEffect = [spellid, this](CStack * sta, const Bonus & effect)
|
||||
auto processEffect = [spellid, this](CStack * sta, const Bonus & effect, bool cumulative)
|
||||
{
|
||||
if(!sta->hasBonus(Selector::source(Bonus::SPELL_EFFECT, spellid).And(Selector::typeSubtype(effect.type, effect.subtype)))
|
||||
|| spellid == SpellID::DISRUPTING_RAY || spellid == SpellID::ACID_BREATH_DEFENSE)
|
||||
if(cumulative || !sta->hasBonus(Selector::source(Bonus::SPELL_EFFECT, spellid).And(Selector::typeSubtype(effect.type, effect.subtype))))
|
||||
{
|
||||
//no such effect or cumulative - add new
|
||||
logBonus->traceStream() << sta->nodeName() << " receives a new bonus: " << effect.Description();
|
||||
sta->addNewBonus(std::make_shared<Bonus>(effect));
|
||||
}
|
||||
else
|
||||
{
|
||||
logBonus->traceStream() << sta->nodeName() << " updated bonus: " << effect.Description();
|
||||
actualizeEffect(sta, effect);
|
||||
}
|
||||
};
|
||||
|
||||
for(ui32 id : stacks)
|
||||
{
|
||||
CStack *s = gs->curB->getStack(id);
|
||||
if(s)
|
||||
{
|
||||
for(const Bonus & fromEffect : effect)
|
||||
processEffect(s, fromEffect);
|
||||
processEffect(s, fromEffect, false);
|
||||
for(const Bonus & fromEffect : cumulativeEffects)
|
||||
processEffect(s, fromEffect, true);
|
||||
}
|
||||
else
|
||||
logNetwork->errorStream() << "Cannot find stack " << id;
|
||||
}
|
||||
@ -1589,7 +1595,16 @@ DLL_LINKAGE void SetStackEffect::applyGs(CGameState *gs)
|
||||
{
|
||||
CStack *s = gs->curB->getStack(para.first);
|
||||
if(s)
|
||||
processEffect(s, para.second);
|
||||
processEffect(s, para.second, false);
|
||||
else
|
||||
logNetwork->errorStream() << "Cannot find stack " << para.first;
|
||||
}
|
||||
|
||||
for(auto & para : cumulativeUniqueBonuses)
|
||||
{
|
||||
CStack *s = gs->curB->getStack(para.first);
|
||||
if(s)
|
||||
processEffect(s, para.second, true);
|
||||
else
|
||||
logNetwork->errorStream() << "Cannot find stack " << para.first;
|
||||
}
|
||||
|
@ -98,7 +98,7 @@
|
||||
<AdditionalOptions>/MP4 %(AdditionalOptions) /bigobj
|
||||
/Zm150</AdditionalOptions>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>$(BOOSTDIR); $(ZLIBDIR);$(SDLDIR)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(BOOSTDIR);$(ZLIBDIR);$(SDLDIR)</AdditionalIncludeDirectories>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
@ -139,7 +139,7 @@
|
||||
<PreprocessorDefinitions>VCMI_DLL;VCMI_NO_EXTRA_VERSION;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PrecompiledHeaderFile>StdInc.h</PrecompiledHeaderFile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<AdditionalIncludeDirectories>$(BOOSTDIR); $(ZLIBDIR);$(SDLDIR)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>$(BOOSTDIR);$(ZLIBDIR);$(SDLDIR)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>minizip.lib;zlib.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
|
@ -7,7 +7,7 @@
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "CZipSaver.h"
|
||||
|
||||
@ -17,10 +17,10 @@ CZipOutputStream::CZipOutputStream(CZipSaver * owner_, zipFile archive, const st
|
||||
owner(owner_)
|
||||
{
|
||||
zip_fileinfo fileInfo;
|
||||
|
||||
|
||||
std::time_t t = time(nullptr);
|
||||
fileInfo.dosDate = 0;
|
||||
|
||||
fileInfo.dosDate = 0;
|
||||
|
||||
struct tm * localTime = std::localtime(&t);
|
||||
fileInfo.tmz_date.tm_hour = localTime->tm_hour;
|
||||
fileInfo.tmz_date.tm_mday = localTime->tm_mday;
|
||||
@ -28,10 +28,10 @@ CZipOutputStream::CZipOutputStream(CZipSaver * owner_, zipFile archive, const st
|
||||
fileInfo.tmz_date.tm_mon = localTime->tm_mon;
|
||||
fileInfo.tmz_date.tm_sec = localTime->tm_sec;
|
||||
fileInfo.tmz_date.tm_year = localTime->tm_year;
|
||||
|
||||
fileInfo.external_fa = 0; //???
|
||||
fileInfo.internal_fa = 0;
|
||||
|
||||
|
||||
fileInfo.external_fa = 0; //???
|
||||
fileInfo.internal_fa = 0;
|
||||
|
||||
int status = zipOpenNewFileInZip4_64(
|
||||
handle,
|
||||
archiveFilename.c_str(),
|
||||
@ -53,10 +53,10 @@ CZipOutputStream::CZipOutputStream(CZipSaver * owner_, zipFile archive, const st
|
||||
0,//flagBase
|
||||
0//zip64
|
||||
);
|
||||
|
||||
|
||||
if(status != ZIP_OK)
|
||||
throw new std::runtime_error("CZipOutputStream: zipOpenNewFileInZip failed");
|
||||
|
||||
|
||||
owner->activeStream = this;
|
||||
}
|
||||
|
||||
@ -71,7 +71,7 @@ CZipOutputStream::~CZipOutputStream()
|
||||
si64 CZipOutputStream::write(const ui8 * data, si64 size)
|
||||
{
|
||||
int ret = zipWriteInFileInZip(handle, (const void*)data, (unsigned)size);
|
||||
|
||||
|
||||
if (ret == ZIP_OK)
|
||||
return size;
|
||||
else
|
||||
@ -79,41 +79,41 @@ si64 CZipOutputStream::write(const ui8 * data, si64 size)
|
||||
}
|
||||
|
||||
///CZipSaver
|
||||
CZipSaver::CZipSaver(std::shared_ptr<CIOApi> api, const std::string & path):
|
||||
CZipSaver::CZipSaver(std::shared_ptr<CIOApi> api, const boost::filesystem::path & path):
|
||||
ioApi(api),
|
||||
zipApi(ioApi->getApiStructure()),
|
||||
handle(nullptr),
|
||||
activeStream(nullptr)
|
||||
{
|
||||
handle = zipOpen2_64(path.c_str(), APPEND_STATUS_CREATE, nullptr, &zipApi);
|
||||
|
||||
handle = zipOpen2_64((const void *) & path, APPEND_STATUS_CREATE, nullptr, &zipApi);
|
||||
|
||||
if (handle == nullptr)
|
||||
throw new std::runtime_error("CZipSaver: Failed to create archive");
|
||||
throw new std::runtime_error("CZipSaver: Failed to create archive");
|
||||
}
|
||||
|
||||
CZipSaver::~CZipSaver()
|
||||
{
|
||||
if(activeStream != nullptr)
|
||||
{
|
||||
logGlobal->error("CZipSaver::~CZipSaver: active stream found");
|
||||
logGlobal->error("CZipSaver::~CZipSaver: active stream found");
|
||||
zipCloseFileInZip(handle);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
if(handle != nullptr)
|
||||
{
|
||||
int status = zipClose(handle, nullptr);
|
||||
if (status != ZIP_OK)
|
||||
logGlobal->errorStream() << "CZipSaver: archive finalize failed: "<<status;
|
||||
logGlobal->errorStream() << "CZipSaver: archive finalize failed: "<<status;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
std::unique_ptr<COutputStream> CZipSaver::addFile(const std::string & archiveFilename)
|
||||
{
|
||||
if(activeStream != nullptr)
|
||||
throw new std::runtime_error("CZipSaver::addFile: stream already opened");
|
||||
|
||||
|
||||
std::unique_ptr<COutputStream> stream(new CZipOutputStream(this, handle, archiveFilename));
|
||||
return std::move(stream);
|
||||
}
|
||||
|
@ -23,16 +23,16 @@ public:
|
||||
* @brief constructs zip stream from already opened file
|
||||
* @param archive archive handle, must be opened
|
||||
* @param archiveFilename name of file to write
|
||||
*/
|
||||
*/
|
||||
explicit CZipOutputStream(CZipSaver * owner_, zipFile archive, const std::string & archiveFilename);
|
||||
~CZipOutputStream();
|
||||
|
||||
|
||||
si64 write(const ui8 * data, si64 size) override;
|
||||
|
||||
si64 seek(si64 position) override {return -1;};
|
||||
si64 tell() override {return 0;};
|
||||
si64 skip(si64 delta) override {return 0;};
|
||||
si64 getSize() override {return 0;};
|
||||
si64 getSize() override {return 0;};
|
||||
private:
|
||||
zipFile handle;
|
||||
CZipSaver * owner;
|
||||
@ -40,17 +40,17 @@ private:
|
||||
|
||||
class DLL_LINKAGE CZipSaver
|
||||
{
|
||||
public:
|
||||
explicit CZipSaver(std::shared_ptr<CIOApi> api, const std::string & path);
|
||||
public:
|
||||
explicit CZipSaver(std::shared_ptr<CIOApi> api, const boost::filesystem::path & path);
|
||||
virtual ~CZipSaver();
|
||||
|
||||
|
||||
std::unique_ptr<COutputStream> addFile(const std::string & archiveFilename);
|
||||
private:
|
||||
std::shared_ptr<CIOApi> ioApi;
|
||||
zlib_filefunc64_def zipApi;
|
||||
|
||||
zlib_filefunc64_def zipApi;
|
||||
|
||||
zipFile handle;
|
||||
|
||||
|
||||
///due to minizip design only one file stream may opened at a time
|
||||
COutputStream * activeStream;
|
||||
friend class CZipOutputStream;
|
||||
|
@ -204,7 +204,7 @@ zlib_filefunc64_def CProxyROIOApi::getApiStructure()
|
||||
|
||||
CInputStream * CProxyROIOApi::openFile(const boost::filesystem::path& filename, int mode)
|
||||
{
|
||||
logGlobal->traceStream() << "CProxyIOApi: stream opened for " <<filename.string() <<" with mode "<<mode;
|
||||
logGlobal->traceStream() << "CProxyROIOApi: stream opened for " <<filename.string() <<" with mode "<<mode;
|
||||
|
||||
data->seek(0);
|
||||
return data;
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "../ConstTransitivePtr.h"
|
||||
#include "../GameConstants.h"
|
||||
|
||||
const ui32 SERIALIZATION_VERSION = 771;
|
||||
const ui32 SERIALIZATION_VERSION = 773;
|
||||
const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
|
||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
||||
|
||||
|
@ -79,11 +79,12 @@ ESpellCastResult AdventureSpellMechanics::applyAdventureEffects(const SpellCastE
|
||||
{
|
||||
if(owner->hasEffects())
|
||||
{
|
||||
//todo: cumulative effects support
|
||||
const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||
|
||||
std::vector<Bonus> bonuses;
|
||||
|
||||
owner->getEffects(bonuses, schoolLevel);
|
||||
owner->getEffects(bonuses, schoolLevel, false, parameters.caster->getEnchantPower(owner));
|
||||
|
||||
for(Bonus b : bonuses)
|
||||
{
|
||||
|
@ -445,120 +445,133 @@ void DefaultSpellMechanics::battleLogDefault(std::vector<MetaString> & logLines,
|
||||
|
||||
void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
//applying effects
|
||||
if(owner->isOffensiveSpell())
|
||||
{
|
||||
const int rawDamage = parameters.getEffectValue();
|
||||
int chainLightningModifier = 0;
|
||||
for(auto & attackedCre : ctx.attackedCres)
|
||||
{
|
||||
BattleStackAttacked bsa;
|
||||
bsa.damageAmount = owner->adjustRawDamage(parameters.caster, attackedCre, rawDamage) >> chainLightningModifier;
|
||||
ctx.addDamageToDisplay(bsa.damageAmount);
|
||||
|
||||
bsa.stackAttacked = (attackedCre)->ID;
|
||||
if(parameters.mode == ECastingMode::ENCHANTER_CASTING) //multiple damage spells cast
|
||||
bsa.attackerID = parameters.casterStack->ID;
|
||||
else
|
||||
bsa.attackerID = -1;
|
||||
(attackedCre)->prepareAttacked(bsa, env->getRandomGenerator());
|
||||
ctx.si.stacks.push_back(bsa);
|
||||
|
||||
if(owner->id == SpellID::CHAIN_LIGHTNING)
|
||||
++chainLightningModifier;
|
||||
}
|
||||
}
|
||||
defaultDamageEffect(env, parameters, ctx);
|
||||
|
||||
if(owner->hasEffects())
|
||||
defaultTimedEffect(env, parameters, ctx);
|
||||
}
|
||||
|
||||
void DefaultSpellMechanics::defaultDamageEffect(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
const int rawDamage = parameters.getEffectValue();
|
||||
int chainLightningModifier = 0;
|
||||
for(auto & attackedCre : ctx.attackedCres)
|
||||
{
|
||||
SetStackEffect sse;
|
||||
//get default spell duration (spell power with bonuses for heroes)
|
||||
int duration = parameters.enchantPower;
|
||||
//generate actual stack bonuses
|
||||
{
|
||||
int maxDuration = 0;
|
||||
std::vector<Bonus> tmp;
|
||||
owner->getEffects(tmp, parameters.effectLevel);
|
||||
for(Bonus& b : tmp)
|
||||
{
|
||||
//use configured duration if present
|
||||
if(b.turnsRemain == 0)
|
||||
b.turnsRemain = duration;
|
||||
vstd::amax(maxDuration, b.turnsRemain);
|
||||
sse.effect.push_back(b);
|
||||
}
|
||||
//if all spell effects have special duration, use it
|
||||
duration = maxDuration;
|
||||
}
|
||||
//fix to original config: shield should display damage reduction
|
||||
if(owner->id == SpellID::SHIELD || owner->id == SpellID::AIR_SHIELD)
|
||||
{
|
||||
sse.effect.back().val = (100 - sse.effect.back().val);
|
||||
}
|
||||
//we need to know who cast Bind
|
||||
if(owner->id == SpellID::BIND && parameters.casterStack)
|
||||
{
|
||||
sse.effect.back().additionalInfo = parameters.casterStack->ID;
|
||||
}
|
||||
std::shared_ptr<Bonus> bonus = nullptr;
|
||||
if(parameters.casterHero)
|
||||
bonus = parameters.casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, owner->id));
|
||||
//TODO does hero specialty should affects his stack casting spells?
|
||||
BattleStackAttacked bsa;
|
||||
bsa.damageAmount = owner->adjustRawDamage(parameters.caster, attackedCre, rawDamage) >> chainLightningModifier;
|
||||
ctx.addDamageToDisplay(bsa.damageAmount);
|
||||
|
||||
si32 power = 0;
|
||||
for(const CStack * affected : ctx.attackedCres)
|
||||
{
|
||||
sse.stacks.push_back(affected->ID);
|
||||
bsa.stackAttacked = (attackedCre)->ID;
|
||||
if(parameters.mode == ECastingMode::ENCHANTER_CASTING) //multiple damage spells cast
|
||||
bsa.attackerID = parameters.casterStack->ID;
|
||||
else
|
||||
bsa.attackerID = -1;
|
||||
(attackedCre)->prepareAttacked(bsa, env->getRandomGenerator());
|
||||
ctx.si.stacks.push_back(bsa);
|
||||
|
||||
//Apply hero specials - peculiar enchants
|
||||
const ui8 tier = std::max((ui8)1, affected->getCreature()->level); //don't divide by 0 for certain creatures (commanders, war machines)
|
||||
if(bonus)
|
||||
{
|
||||
switch(bonus->additionalInfo)
|
||||
{
|
||||
case 0: //normal
|
||||
{
|
||||
switch(tier)
|
||||
{
|
||||
case 1: case 2:
|
||||
power = 3;
|
||||
break;
|
||||
case 3: case 4:
|
||||
power = 2;
|
||||
break;
|
||||
case 5: case 6:
|
||||
power = 1;
|
||||
break;
|
||||
}
|
||||
Bonus specialBonus(sse.effect.back());
|
||||
specialBonus.val = power; //it doesn't necessarily make sense for some spells, use it wisely
|
||||
sse.uniqueBonuses.push_back (std::pair<ui32,Bonus> (affected->ID, specialBonus)); //additional premy to given effect
|
||||
}
|
||||
break;
|
||||
case 1: //only Coronius as yet
|
||||
{
|
||||
power = std::max(5 - tier, 0);
|
||||
Bonus specialBonus(Bonus::N_TURNS, Bonus::PRIMARY_SKILL, Bonus::SPELL_EFFECT, power, owner->id, PrimarySkill::ATTACK);
|
||||
specialBonus.turnsRemain = duration;
|
||||
sse.uniqueBonuses.push_back(std::pair<ui32,Bonus> (affected->ID, specialBonus)); //additional attack to Slayer effect
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parameters.casterHero && parameters.casterHero->hasBonusOfType(Bonus::SPECIAL_BLESS_DAMAGE, owner->id)) //TODO: better handling of bonus percentages
|
||||
{
|
||||
int damagePercent = parameters.casterHero->level * parameters.casterHero->valOfBonuses(Bonus::SPECIAL_BLESS_DAMAGE, owner->id.toEnum()) / tier;
|
||||
Bonus specialBonus(Bonus::N_TURNS, Bonus::CREATURE_DAMAGE, Bonus::SPELL_EFFECT, damagePercent, owner->id, 0, Bonus::PERCENT_TO_ALL);
|
||||
specialBonus.turnsRemain = duration;
|
||||
sse.uniqueBonuses.push_back (std::pair<ui32,Bonus> (affected->ID, specialBonus));
|
||||
}
|
||||
}
|
||||
|
||||
if(!sse.stacks.empty())
|
||||
env->sendAndApply(&sse);
|
||||
if(owner->id == SpellID::CHAIN_LIGHTNING)
|
||||
++chainLightningModifier;
|
||||
}
|
||||
}
|
||||
|
||||
void DefaultSpellMechanics::defaultTimedEffect(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
SetStackEffect sse;
|
||||
//get default spell duration (spell power with bonuses for heroes)
|
||||
int duration = parameters.enchantPower;
|
||||
//generate actual stack bonuses
|
||||
{
|
||||
si32 maxDuration = 0;
|
||||
|
||||
owner->getEffects(sse.effect, parameters.effectLevel, false, duration, &maxDuration);
|
||||
owner->getEffects(sse.cumulativeEffects, parameters.effectLevel, true, duration, &maxDuration);
|
||||
|
||||
//if all spell effects have special duration, use it later for special bonuses
|
||||
duration = maxDuration;
|
||||
}
|
||||
//fix to original config: shield should display damage reduction
|
||||
if(owner->id == SpellID::SHIELD || owner->id == SpellID::AIR_SHIELD)
|
||||
{
|
||||
sse.effect.at(sse.effect.size() - 1).val = (100 - sse.effect.back().val);
|
||||
}
|
||||
//we need to know who cast Bind
|
||||
else if(owner->id == SpellID::BIND && parameters.casterStack)
|
||||
{
|
||||
sse.effect.at(sse.effect.size() - 1).additionalInfo = parameters.casterStack->ID;
|
||||
}
|
||||
std::shared_ptr<Bonus> bonus = nullptr;
|
||||
if(parameters.casterHero)
|
||||
bonus = parameters.casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, owner->id));
|
||||
//TODO does hero specialty should affects his stack casting spells?
|
||||
|
||||
for(const CStack * affected : ctx.attackedCres)
|
||||
{
|
||||
si32 power = 0;
|
||||
sse.stacks.push_back(affected->ID);
|
||||
|
||||
//Apply hero specials - peculiar enchants
|
||||
const ui8 tier = std::max((ui8)1, affected->getCreature()->level); //don't divide by 0 for certain creatures (commanders, war machines)
|
||||
if(bonus)
|
||||
{
|
||||
switch(bonus->additionalInfo)
|
||||
{
|
||||
case 0: //normal
|
||||
{
|
||||
switch(tier)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
power = 3;
|
||||
break;
|
||||
case 3:
|
||||
case 4:
|
||||
power = 2;
|
||||
break;
|
||||
case 5:
|
||||
case 6:
|
||||
power = 1;
|
||||
break;
|
||||
}
|
||||
for(const Bonus & b : sse.effect)
|
||||
{
|
||||
Bonus specialBonus(b);
|
||||
specialBonus.val = power; //it doesn't necessarily make sense for some spells, use it wisely
|
||||
specialBonus.turnsRemain = duration;
|
||||
sse.uniqueBonuses.push_back(std::pair<ui32, Bonus>(affected->ID, specialBonus)); //additional premy to given effect
|
||||
}
|
||||
for(const Bonus & b : sse.cumulativeEffects)
|
||||
{
|
||||
Bonus specialBonus(b);
|
||||
specialBonus.val = power; //it doesn't necessarily make sense for some spells, use it wisely
|
||||
specialBonus.turnsRemain = duration;
|
||||
sse.cumulativeUniqueBonuses.push_back(std::pair<ui32, Bonus>(affected->ID, specialBonus)); //additional premy to given effect
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 1: //only Coronius as yet
|
||||
{
|
||||
power = std::max(5 - tier, 0);
|
||||
Bonus specialBonus(Bonus::N_TURNS, Bonus::PRIMARY_SKILL, Bonus::SPELL_EFFECT, power, owner->id, PrimarySkill::ATTACK);
|
||||
specialBonus.turnsRemain = duration;
|
||||
sse.uniqueBonuses.push_back(std::pair<ui32,Bonus>(affected->ID, specialBonus)); //additional attack to Slayer effect
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(parameters.casterHero && parameters.casterHero->hasBonusOfType(Bonus::SPECIAL_BLESS_DAMAGE, owner->id)) //TODO: better handling of bonus percentages
|
||||
{
|
||||
int damagePercent = parameters.casterHero->level * parameters.casterHero->valOfBonuses(Bonus::SPECIAL_BLESS_DAMAGE, owner->id.toEnum()) / tier;
|
||||
Bonus specialBonus(Bonus::N_TURNS, Bonus::CREATURE_DAMAGE, Bonus::SPELL_EFFECT, damagePercent, owner->id, 0, Bonus::PERCENT_TO_ALL);
|
||||
specialBonus.turnsRemain = duration;
|
||||
sse.uniqueBonuses.push_back(std::pair<ui32,Bonus>(affected->ID, specialBonus));
|
||||
}
|
||||
}
|
||||
|
||||
if(!sse.stacks.empty())
|
||||
env->sendAndApply(&sse);
|
||||
}
|
||||
|
||||
std::vector<BattleHex> DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
|
||||
{
|
||||
using namespace SRSLPraserHelpers;
|
||||
|
@ -75,6 +75,9 @@ protected:
|
||||
protected:
|
||||
void doDispell(BattleInfo * battle, const BattleSpellCast * packet, const CSelector & selector) const;
|
||||
bool canDispell(const IBonusBearer * obj, const CSelector & selector, const std::string &cachingStr = "") const;
|
||||
|
||||
void defaultDamageEffect(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const;
|
||||
void defaultTimedEffect(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, SpellCastContext & ctx) const;
|
||||
private:
|
||||
void cast(const SpellCastEnvironment * env, const BattleSpellCastParameters & parameters, std::vector <const CStack*> & reflected) const;
|
||||
|
||||
|
@ -350,7 +350,7 @@ bool CSpell::isSpecialSpell() const
|
||||
|
||||
bool CSpell::hasEffects() const
|
||||
{
|
||||
return !levels[0].effects.empty();
|
||||
return !levels[0].effects.empty() || !levels[0].cumulativeEffects.empty();
|
||||
}
|
||||
|
||||
const std::string & CSpell::getIconImmune() const
|
||||
@ -382,7 +382,7 @@ si32 CSpell::getProbability(const TFaction factionId) const
|
||||
return probabilities.at(factionId);
|
||||
}
|
||||
|
||||
void CSpell::getEffects(std::vector<Bonus> & lst, const int level) const
|
||||
void CSpell::getEffects(std::vector<Bonus> & lst, const int level, const bool cumulative, const si32 duration, boost::optional<si32 *> maxDuration/* = boost::none*/) const
|
||||
{
|
||||
if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
|
||||
{
|
||||
@ -390,19 +390,29 @@ void CSpell::getEffects(std::vector<Bonus> & lst, const int level) const
|
||||
return;
|
||||
}
|
||||
|
||||
const std::vector<Bonus> & effects = levels[level].effects;
|
||||
const auto & levelObject = levels.at(level);
|
||||
|
||||
if(effects.empty())
|
||||
if(levelObject.effects.empty() && levelObject.cumulativeEffects.empty())
|
||||
{
|
||||
logGlobal->errorStream() << __FUNCTION__ << " This spell (" + name + ") has no effects for level " << level;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto & effects = cumulative ? levelObject.cumulativeEffects : levelObject.effects;
|
||||
|
||||
lst.reserve(lst.size() + effects.size());
|
||||
|
||||
for(const Bonus & b : effects)
|
||||
for(const auto b : effects)
|
||||
{
|
||||
lst.push_back(Bonus(b));
|
||||
Bonus nb(*b);
|
||||
|
||||
//use configured duration if present
|
||||
if(nb.turnsRemain == 0)
|
||||
nb.turnsRemain = duration;
|
||||
if(maxDuration)
|
||||
vstd::amax(*(maxDuration.get()), nb.turnsRemain);
|
||||
|
||||
lst.push_back(nb);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1029,9 +1039,25 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string &
|
||||
if(usePowerAsValue)
|
||||
b->val = levelPower;
|
||||
|
||||
levelObject.effectsTmp.push_back(b);
|
||||
levelObject.effects.push_back(b);
|
||||
}
|
||||
|
||||
for(const auto & elem : levelNode["cumulativeEffects"].Struct())
|
||||
{
|
||||
const JsonNode & bonusNode = elem.second;
|
||||
auto b = JsonUtils::parseBonus(bonusNode);
|
||||
const bool usePowerAsValue = bonusNode["val"].isNull();
|
||||
|
||||
//TODO: make this work. see CSpellHandler::afterLoadFinalization()
|
||||
//b->sid = spell->id; //for all
|
||||
|
||||
b->source = Bonus::SPELL_EFFECT;//for all
|
||||
|
||||
if(usePowerAsValue)
|
||||
b->val = levelPower;
|
||||
|
||||
levelObject.cumulativeEffects.push_back(b);
|
||||
}
|
||||
}
|
||||
|
||||
return spell;
|
||||
@ -1044,14 +1070,10 @@ void CSpellHandler::afterLoadFinalization()
|
||||
{
|
||||
for(auto & level: spell->levels)
|
||||
{
|
||||
for(auto bonus : level.effectsTmp)
|
||||
{
|
||||
level.effects.push_back(*bonus);
|
||||
}
|
||||
level.effectsTmp.clear();
|
||||
|
||||
for(auto & bonus: level.effects)
|
||||
bonus.sid = spell->id;
|
||||
bonus->sid = spell->id;
|
||||
for(auto & bonus: level.cumulativeEffects)
|
||||
bonus->sid = spell->id;
|
||||
}
|
||||
spell->setup();
|
||||
}
|
||||
|
@ -130,16 +130,35 @@ public:
|
||||
bool clearAffected;
|
||||
std::string range;
|
||||
|
||||
std::vector<Bonus> effects;
|
||||
|
||||
std::vector<std::shared_ptr<Bonus>> effectsTmp; //TODO: this should replace effects
|
||||
std::vector<std::shared_ptr<Bonus>> effects;
|
||||
std::vector<std::shared_ptr<Bonus>> cumulativeEffects;
|
||||
|
||||
LevelInfo();
|
||||
~LevelInfo();
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & description & cost & power & AIValue & smartTarget & range & effects;
|
||||
h & description & cost & power & AIValue & smartTarget & range;
|
||||
|
||||
if(version >= 773)
|
||||
{
|
||||
h & effects & cumulativeEffects;
|
||||
}
|
||||
else
|
||||
{
|
||||
//all old effects treated as not cumulative, special cases handled by CSpell::serialize
|
||||
std::vector<Bonus> old;
|
||||
h & old;
|
||||
|
||||
if(!h.saving)
|
||||
{
|
||||
effects.clear();
|
||||
cumulativeEffects.clear();
|
||||
for(const Bonus & oldBonus : old)
|
||||
effects.push_back(std::make_shared<Bonus>(oldBonus));
|
||||
}
|
||||
}
|
||||
|
||||
h & clearTarget & clearAffected;
|
||||
}
|
||||
};
|
||||
@ -180,7 +199,7 @@ public:
|
||||
|
||||
si32 level;
|
||||
|
||||
std::map<ESpellSchool, bool> school; //todo: use this instead of separate boolean fields
|
||||
std::map<ESpellSchool, bool> school;
|
||||
|
||||
si32 power; //spell's power
|
||||
|
||||
@ -215,8 +234,7 @@ public:
|
||||
bool isSpecialSpell() const;
|
||||
|
||||
bool hasEffects() const;
|
||||
void getEffects(std::vector<Bonus> &lst, const int level) const;
|
||||
|
||||
void getEffects(std::vector<Bonus> & lst, const int level, const bool cumulative, const si32 duration, boost::optional<si32 *> maxDuration = boost::none) const;
|
||||
|
||||
///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account
|
||||
ui32 calculateDamage(const ISpellCaster * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const;
|
||||
@ -264,6 +282,12 @@ public:
|
||||
|
||||
if(!h.saving)
|
||||
setup();
|
||||
//backward compatibility
|
||||
//can not be added to level structure as level structure does not know spell id
|
||||
if(!h.saving && version < 773)
|
||||
if(id == SpellID::DISRUPTING_RAY || id == SpellID::ACID_BREATH_DEFENSE)
|
||||
for(auto & level : levels)
|
||||
std::swap(level.effects, level.cumulativeEffects);
|
||||
}
|
||||
friend class CSpellHandler;
|
||||
friend class Graphics;
|
||||
|
@ -4489,30 +4489,32 @@ bool CGameHandler::makeCustomAction(BattleAction &ba)
|
||||
}
|
||||
|
||||
|
||||
void CGameHandler::stackAppearTrigger(const CStack *st)
|
||||
void CGameHandler::stackEnchantedTrigger(const CStack * st)
|
||||
{
|
||||
auto bl = *(st->getBonuses(Selector::type(Bonus::ENCHANTED)));
|
||||
for (auto b : bl)
|
||||
for(auto b : bl)
|
||||
{
|
||||
SetStackEffect sse;
|
||||
int val = bl.valOfBonuses(Selector::typeSubtype(b->type, b->subtype));
|
||||
if (val > 3)
|
||||
if(val > 3)
|
||||
{
|
||||
for (auto s : gs->curB->battleGetAllStacks())
|
||||
for(auto s : gs->curB->battleGetAllStacks())
|
||||
{
|
||||
if (battleMatchOwner(st, s, true) && s->isValidTarget()) //all allied
|
||||
if(battleMatchOwner(st, s, true) && s->isValidTarget()) //all allied
|
||||
sse.stacks.push_back (s->ID);
|
||||
}
|
||||
}
|
||||
else
|
||||
sse.stacks.push_back (st->ID);
|
||||
|
||||
Bonus pseudoBonus;
|
||||
pseudoBonus.sid = b->subtype;
|
||||
pseudoBonus.val = ((val > 3) ? (val - 3) : val);
|
||||
pseudoBonus.turnsRemain = 50;
|
||||
st->stackEffectToFeature(sse.effect, pseudoBonus);
|
||||
if (sse.effect.size())
|
||||
const CSpell * sp = SpellID(b->subtype).toSpell();
|
||||
const int level = ((val > 3) ? (val - 3) : val);
|
||||
|
||||
sp->getEffects(sse.effect, level, false, 50);
|
||||
//this makes effect accumulate for at most 50 turns by default, but effect may be permanent and last till the end of battle
|
||||
sp->getEffects(sse.cumulativeEffects, level, true, 50);
|
||||
|
||||
if(!sse.effect.empty() || !sse.cumulativeEffects.empty())
|
||||
sendAndApply(&sse);
|
||||
}
|
||||
}
|
||||
@ -4526,7 +4528,6 @@ void CGameHandler::stackTurnTrigger(const CStack *st)
|
||||
bte.additionalInfo = 0;
|
||||
if (st->alive())
|
||||
{
|
||||
stackAppearTrigger(st);
|
||||
//unbind
|
||||
if (st->hasBonus(Selector::type(Bonus::BIND_EFFECT)))
|
||||
{
|
||||
@ -5724,7 +5725,7 @@ void CGameHandler::runBattle()
|
||||
}
|
||||
}
|
||||
|
||||
stackAppearTrigger(stack);
|
||||
stackEnchantedTrigger(stack);
|
||||
}
|
||||
|
||||
//spells opening battle
|
||||
@ -5749,11 +5750,14 @@ void CGameHandler::runBattle()
|
||||
}
|
||||
}
|
||||
|
||||
bool firstRound = true;//FIXME: why first round is -1?
|
||||
|
||||
//main loop
|
||||
while (!battleResult.get()) //till the end of the battle ;]
|
||||
{
|
||||
BattleNextRound bnr;
|
||||
bnr.round = gs->curB->round + 1;
|
||||
logGlobal->debug("Round %d", bnr.round);
|
||||
sendAndApply(&bnr);
|
||||
|
||||
auto obstacles = gs->curB->obstacles; //we copy container, because we're going to modify it
|
||||
@ -5766,6 +5770,12 @@ void CGameHandler::runBattle()
|
||||
|
||||
const BattleInfo & curB = *gs->curB;
|
||||
|
||||
for(auto stack : curB.stacks)
|
||||
{
|
||||
if(stack->alive() && !firstRound)
|
||||
stackEnchantedTrigger(stack);
|
||||
}
|
||||
|
||||
//stack loop
|
||||
|
||||
const CStack *next;
|
||||
@ -5994,6 +6004,7 @@ void CGameHandler::runBattle()
|
||||
}
|
||||
|
||||
}
|
||||
firstRound = false;
|
||||
}
|
||||
|
||||
endBattle(gs->curB->tile, gs->curB->battleGetFightingHero(0), gs->curB->battleGetFightingHero(1));
|
||||
@ -6207,6 +6218,13 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
|
||||
smp.hid = hero->id;
|
||||
smp.val = 1000000;
|
||||
sendAndApply(&smp);
|
||||
|
||||
GiveBonus gb(GiveBonus::HERO);
|
||||
gb.bonus.type = Bonus::FREE_SHIP_BOARDING;
|
||||
gb.bonus.duration = Bonus::ONE_DAY;
|
||||
gb.bonus.source = Bonus::OTHER;
|
||||
gb.id = hero->id.getNum();
|
||||
giveHeroBonus(&gb);
|
||||
}
|
||||
else if (cheat == "vcmiformenos")
|
||||
{
|
||||
|
@ -196,7 +196,7 @@ public:
|
||||
bool makeBattleAction(BattleAction &ba);
|
||||
bool makeAutomaticAction(const CStack *stack, BattleAction &ba); //used when action is taken by stack without volition of player (eg. unguided catapult attack)
|
||||
bool makeCustomAction(BattleAction &ba);
|
||||
void stackAppearTrigger(const CStack *stack);
|
||||
void stackEnchantedTrigger(const CStack * stack);
|
||||
void stackTurnTrigger(const CStack *stack);
|
||||
void handleDamageFromObstacle(const CObstacleInstance &obstacle, const CStack * curStack); //checks if obstacle is land mine and handles possible consequences
|
||||
void removeObstacle(const CObstacleInstance &obstacle);
|
||||
|
@ -21,7 +21,9 @@
|
||||
#include "../lib/CRandomGenerator.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_Type)
|
||||
BOOST_AUTO_TEST_SUITE(CMapEditManager_Suite)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(DrawTerrain_Type)
|
||||
{
|
||||
logGlobal->info("CMapEditManager_DrawTerrain_Type start");
|
||||
try
|
||||
@ -110,7 +112,7 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_Type)
|
||||
logGlobal->info("CMapEditManager_DrawTerrain_Type finish");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View)
|
||||
BOOST_AUTO_TEST_CASE(DrawTerrain_View)
|
||||
{
|
||||
logGlobal->info("CMapEditManager_DrawTerrain_View start");
|
||||
try
|
||||
@ -178,3 +180,5 @@ BOOST_AUTO_TEST_CASE(CMapEditManager_DrawTerrain_View)
|
||||
}
|
||||
logGlobal->info("CMapEditManager_DrawTerrain_View finish");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -26,7 +26,9 @@
|
||||
|
||||
static const int TEST_RANDOM_SEED = 1337;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MapFormat_Random)
|
||||
BOOST_AUTO_TEST_SUITE(MapFormat_Suite)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(Random)
|
||||
{
|
||||
logGlobal->info("MapFormat_Random start");
|
||||
BOOST_TEST_CHECKPOINT("MapFormat_Random start");
|
||||
@ -115,7 +117,7 @@ static void addToArchive(CZipSaver & saver, const JsonNode & data, const std::st
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(MapFormat_Objects)
|
||||
BOOST_AUTO_TEST_CASE(Objects)
|
||||
{
|
||||
logGlobal->info("MapFormat_Objects start");
|
||||
|
||||
@ -202,3 +204,5 @@ BOOST_AUTO_TEST_CASE(MapFormat_Objects)
|
||||
|
||||
logGlobal->info("MapFormat_Objects finish");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
@ -9,44 +9,45 @@
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include "../lib/filesystem/CMemoryBuffer.h"
|
||||
|
||||
|
||||
struct CMemoryBufferFixture
|
||||
{
|
||||
CMemoryBuffer subject;
|
||||
};
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(CMemoryBuffer_Empty, CMemoryBufferFixture)
|
||||
BOOST_AUTO_TEST_SUITE(CMemoryBuffer_Suite)
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(empty, CMemoryBufferFixture)
|
||||
{
|
||||
BOOST_REQUIRE_EQUAL(0, subject.tell());
|
||||
BOOST_REQUIRE_EQUAL(0, subject.getSize());
|
||||
|
||||
|
||||
si32 dummy = 1337;
|
||||
|
||||
|
||||
auto ret = subject.read((ui8 *)&dummy, sizeof(si32));
|
||||
|
||||
BOOST_CHECK_EQUAL(0, ret);
|
||||
|
||||
BOOST_CHECK_EQUAL(0, ret);
|
||||
BOOST_CHECK_EQUAL(1337, dummy);
|
||||
BOOST_CHECK_EQUAL(0, subject.tell());
|
||||
}
|
||||
|
||||
BOOST_FIXTURE_TEST_CASE(CMemoryBuffer_Write, CMemoryBufferFixture)
|
||||
BOOST_FIXTURE_TEST_CASE(write, CMemoryBufferFixture)
|
||||
{
|
||||
const si32 initial = 1337;
|
||||
|
||||
|
||||
subject.write((const ui8 *)&initial, sizeof(si32));
|
||||
|
||||
|
||||
BOOST_CHECK_EQUAL(4, subject.tell());
|
||||
subject.seek(0);
|
||||
BOOST_CHECK_EQUAL(0, subject.tell());
|
||||
|
||||
|
||||
si32 current = 0;
|
||||
auto ret = subject.read((ui8 *)¤t, sizeof(si32));
|
||||
BOOST_CHECK_EQUAL(sizeof(si32), ret);
|
||||
BOOST_CHECK_EQUAL(sizeof(si32), ret);
|
||||
BOOST_CHECK_EQUAL(initial, current);
|
||||
BOOST_CHECK_EQUAL(4, subject.tell());
|
||||
BOOST_CHECK_EQUAL(4, subject.tell());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
Loading…
Reference in New Issue
Block a user