mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Merge branch 'develop' into NewAbilities
This commit is contained in:
commit
14c07afd3c
3
AUTHORS
3
AUTHORS
@ -66,3 +66,6 @@ Dydzio, <blood990@gmail.com>
|
||||
|
||||
Piotr Wójcik aka Chocimier, <chocimier@tlen.pl>
|
||||
* Various bug fixes
|
||||
|
||||
Henning Koehler, <henning.koehler.nz@gmail.com>
|
||||
* skill modding
|
||||
|
@ -54,7 +54,7 @@ option(ENABLE_PCH "Enable compilation using precompiled headers" ON)
|
||||
option(ENABLE_GITVERSION "Enable Version.cpp with Git commit hash" ON)
|
||||
option(ENABLE_DEBUG_CONSOLE "Enable debug console for Windows builds" ON)
|
||||
|
||||
# Useful for debugging
|
||||
# Used for Snap packages and also useful for debugging
|
||||
option(ENABLE_MONOLITHIC_INSTALL "Install everything in single directory on Linux and Mac" OFF)
|
||||
|
||||
# Allow to pass package name from Travis CI
|
||||
@ -68,6 +68,7 @@ set(PACKAGE_FILE_NAME "" CACHE STRING "Override for CPack package filename")
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_HOME_DIRECTORY}/cmake_modules)
|
||||
# Contains custom functions and macros, but don't altering any options
|
||||
include(VCMIUtils)
|
||||
vcmi_print_important_variables()
|
||||
|
||||
# Options to enable folders in CMake generated projects for Visual Studio, Xcode, etc
|
||||
# Very useful to put 3rd-party libraries such as Minizip, GoogleTest and FuzzyLite in their own folders
|
||||
@ -90,6 +91,7 @@ if(ENABLE_GITVERSION)
|
||||
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)
|
||||
vcmi_print_git_commit_hash()
|
||||
else()
|
||||
add_definitions(-DVCMI_NO_EXTRA_VERSION)
|
||||
endif(ENABLE_GITVERSION)
|
||||
@ -248,6 +250,7 @@ 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")
|
||||
@ -262,16 +265,16 @@ else()
|
||||
set(DATA_DIR "share/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}")
|
||||
endif()
|
||||
|
||||
set(AI_LIB_DIR "${LIB_DIR}/AI")
|
||||
set(SCRIPTING_LIB_DIR "${LIB_DIR}/scripting")
|
||||
|
||||
#define required constants
|
||||
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}")
|
||||
|
||||
#######################################
|
||||
# Add subdirectories #
|
||||
#######################################
|
||||
@ -414,7 +417,15 @@ elseif(APPLE AND NOT ENABLE_MONOLITHIC_INSTALL)
|
||||
set(CPACK_MONOLITHIC_INSTALL 1)
|
||||
set(CPACK_GENERATOR "DragNDrop")
|
||||
set(CPACK_DMG_BACKGROUND_IMAGE "${CMAKE_SOURCE_DIR}/osx/dmg_background.png")
|
||||
set(CPACK_DMG_DS_STORE "${CMAKE_SOURCE_DIR}/osx/dmg_DS_Store")
|
||||
# CMake code for CPACK_DMG_DS_STORE executed before DS_STORE_SETUP_SCRIPT
|
||||
# So we can keep both enabled and this shouldn't hurt
|
||||
# set(CPACK_DMG_DS_STORE "${CMAKE_SOURCE_DIR}/osx/dmg_DS_Store")
|
||||
set(CPACK_DMG_DS_STORE_SETUP_SCRIPT "${CMAKE_SOURCE_DIR}/osx/DS_Store_Setup.scpt")
|
||||
|
||||
# Always use "VCMI" as volume name so full path will be /Volumes/VCMI/
|
||||
# Otherwise DMG background image wouldn't work
|
||||
# Pre-generated DS_Store use absolute path to background image
|
||||
set(CPACK_DMG_VOLUME_NAME "${CMAKE_PROJECT_NAME}")
|
||||
|
||||
set(MACOSX_BUNDLE_NAME "${CMAKE_PROJECT_NAME}")
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "${CMAKE_PROJECT_NAME}")
|
||||
|
@ -11,12 +11,15 @@ GENERAL:
|
||||
- CATAPULT_EXTRA_SHOTS - defines number of extra wall attacks for units that can do so
|
||||
- RANGED_RETALIATION - allows ranged counterattack
|
||||
- BLOCKS_RANGED_RETALIATION - disallow enemy ranged counterattack
|
||||
- SECONDARY_SKILL_VAL2 - set additional parameter for certain secondary skills
|
||||
- MANUAL_CONTROL - grant manual control over war machine
|
||||
|
||||
SPELLS:
|
||||
* Implemented cumulative effects for spells
|
||||
|
||||
MODS:
|
||||
* Improve support for WoG commander artifacts and skill descriptions
|
||||
* Added basic support for secondary skill modding
|
||||
|
||||
0.98 -> 0.99
|
||||
|
||||
|
12
Global.h
12
Global.h
@ -676,6 +676,18 @@ namespace vstd
|
||||
return v3;
|
||||
}
|
||||
|
||||
template <typename Key, typename V>
|
||||
bool containsMapping(const std::multimap<Key,V> & map, const std::pair<const Key,V> & mapping)
|
||||
{
|
||||
auto range = map.equal_range(mapping.first);
|
||||
for(auto contained = range.first; contained != range.second; contained++)
|
||||
{
|
||||
if(mapping.second == contained->second)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
using boost::math::round;
|
||||
}
|
||||
using vstd::operator-=;
|
||||
|
112
README.linux
112
README.linux
@ -1,112 +0,0 @@
|
||||
This readme covers VCMI compilation on Unix-like systems.
|
||||
|
||||
To run the game you will need:
|
||||
1) Heroes 3 data files (SoD or Complete editions);
|
||||
2) VCMI data pack (http://download.vcmi.eu/core.zip)
|
||||
All of them can be installed manually or using vcmibuilder script
|
||||
|
||||
For complete installation instructions see VCMI wiki:
|
||||
http://wiki.vcmi.eu/index.php?title=Installation_on_Linux#Preparing_data
|
||||
|
||||
I. Prerequisites
|
||||
|
||||
To compile, the following packages (and their development counterparts) are needed to build:
|
||||
* libstdc++ devel
|
||||
* CMake build system
|
||||
* SDL and SDL-devel
|
||||
* SDL_mixer and SDL_mixer-devel
|
||||
* SDL_image and SDL_image-devel
|
||||
* SDL_ttf and SDL_ttf-devel
|
||||
* zlib and zlib-devel
|
||||
* (optional) Qt 5, widget and network modules
|
||||
* the ffmpeg libraries (libavformat and libswscale). Their name could be libavformat-devel and libswscale-devel, or ffmpeg-libs-devel or similar names.
|
||||
* boost c++ libraries v1.50+ (www.boost.org):
|
||||
- program-options
|
||||
- filesystem
|
||||
- system
|
||||
- thread
|
||||
- locale
|
||||
|
||||
On Debian-based systems (e.g. Ubuntu) run:
|
||||
sudo apt-get install cmake g++ libsdl2-dev libsdl2-image-dev libsdl2-ttf-dev libsdl2-mixer-dev zlib1g-dev libavformat-dev libswscale-dev libboost-dev libboost-filesystem-dev libboost-system-dev libboost-thread-dev libboost-program-options-dev libboost-locale-dev qtbase5-dev
|
||||
|
||||
On RPM-based distributions (e.g. Fedora) run:
|
||||
sudo yum install cmake gcc-c++ SDL2-devel SDL2_image-devel SDL2_ttf-devel SDL2_mixer-devel boost boost-devel boost-filesystem boost-system boost-thread boost-program-options boost-locale zlib-devel ffmpeg-devel ffmpeg-libs qt5-qtbase-devel
|
||||
|
||||
On Arch-based distributions, there is a development package available for VCMI on the AUR. It can be found at:
|
||||
https://aur.archlinux.org/packages/vcmi-git/
|
||||
|
||||
Information about building packages from the Arch User Repository (AUR) can be
|
||||
found at the Arch wiki.
|
||||
|
||||
II. Getting the sources
|
||||
|
||||
VCMI is still in development. We recommend the following initial directory structure:
|
||||
.
|
||||
├── vcmi -> contains sources and is under git control
|
||||
└── build -> contains build output, makefiles, object files,...
|
||||
|
||||
You can get latest sources with:
|
||||
git clone https://github.com/vcmi/vcmi.git
|
||||
|
||||
III. Compilation
|
||||
|
||||
Run configure:
|
||||
mkdir build && cd build
|
||||
cmake ../vcmi <any other options, see below>
|
||||
|
||||
Additional options that you may want to use:
|
||||
To enable debugging: -DCMAKE_BUILD_TYPE=Debug
|
||||
To change installation directory: -DCMAKE_INSTALL_PREFIX=$absolute_path_to_directory
|
||||
|
||||
Notice:
|
||||
The ../vcmi/ is not a typo, it will place makefile scripts into the build dir
|
||||
as the build dir is your working dir when calling CMake.
|
||||
|
||||
Then build vcmi:
|
||||
make -j2 (j2 = compile with 2 threads, you can specify any value)
|
||||
|
||||
That will generate vcmiclient, vcmiserver, vcmilauncher as well as 3 .so libraries.
|
||||
|
||||
III. Installing binaries
|
||||
|
||||
To install VCMI you can use "make install" command however generation of distribution-specific packages is usually a better idea. In most cases this can be achieved using tool called "checkinstall"
|
||||
|
||||
If you're compiling vcmi for development puposes, the easiest is to use cmake prefix and then make install:
|
||||
|
||||
# mkdir .../trunk/install
|
||||
# cmake -DCMAKE_INSTALL_PREFIX=.../trunk/install ../vcmi
|
||||
# make && make install
|
||||
# .../trunk/install/bin/vcmiclient
|
||||
|
||||
|
||||
it's better to use links instead.
|
||||
Go to /BIN_PATH/, and type:
|
||||
|
||||
ln -s .../trunk/build/client/vcmiclient
|
||||
ln -s .../trunk/build/server/vcmiserver
|
||||
ln -s .../trunk/build/launcher/vcmilauncher
|
||||
|
||||
Go to /LIB_PATH/vcmi, and type:
|
||||
|
||||
ln -s .../trunk/build/lib/libvcmi.so libvcmi.so
|
||||
|
||||
Go to /LIB_PATH/vcmi/AI, and type:
|
||||
ln -s .../trunk/build/AI/VCAI/libVCAI.so
|
||||
ln -s .../trunk/build/AI/StupidAI/libStupidAI.so
|
||||
ln -s .../trunk/build/AI/BattleAI/libBattleAI.so
|
||||
|
||||
Go to /DATA_PATH/vcmi, and type:
|
||||
ln -s .../trunk/source/config
|
||||
ln -s .../trunk/source/Mods
|
||||
|
||||
IV. Compiling documentation
|
||||
|
||||
To compile using Doxygen, the UseDoxygen CMake module must be installed. It can
|
||||
be fetched from: http://tobias.rautenkranz.ch/cmake/doxygen/
|
||||
|
||||
Once UseDoxygen is installed, run:
|
||||
cmake .
|
||||
make doc
|
||||
|
||||
The built documentation will be available from ./doc
|
29
README.md
29
README.md
@ -2,20 +2,33 @@
|
||||
[![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/github/vcmi/vcmi?branch=develop&svg=true)](https://ci.appveyor.com/project/vcmi/vcmi)
|
||||
[![Coverity Scan Build Status](https://scan.coverity.com/projects/vcmi/badge.svg)](https://scan.coverity.com/projects/vcmi)
|
||||
# VCMI Project
|
||||
VCMI is work-in-progress attempt to recreate engine for Heroes III, giving it new and extended possibilities. To use VCMI you need to own original data files.
|
||||
VCMI is work-in-progress attempt to recreate engine for Heroes III, giving it new and extended possibilities.
|
||||
|
||||
## Links
|
||||
|
||||
* Homepage: http://vcmi.eu/
|
||||
* Wiki: http://wiki.vcmi.eu/
|
||||
* Forums: http://forum.vcmi.eu/
|
||||
* Bugtracker: http://bugs.vcmi.eu/
|
||||
* Homepage: https://vcmi.eu/
|
||||
* Wiki: https://wiki.vcmi.eu/
|
||||
* Forums: https://forum.vcmi.eu/
|
||||
* Bugtracker: https://bugs.vcmi.eu/
|
||||
* Slack: https://slack.vcmi.eu/
|
||||
|
||||
## Installation
|
||||
For installation of latest release see release announcement on http://vcmi.eu/
|
||||
## Installation guides
|
||||
|
||||
For building from source see project wiki at http://wiki.vcmi.eu/
|
||||
To use VCMI you need to own original data files.
|
||||
|
||||
* [Android](https://wiki.vcmi.eu/Installation_on_Android)
|
||||
* [Linux](https://wiki.vcmi.eu/Installation_on_Linux)
|
||||
* [macOS](https://wiki.vcmi.eu/Installation_on_macOS)
|
||||
* [Windows](https://wiki.vcmi.eu/Installation_on_Windows)
|
||||
|
||||
## Building from source
|
||||
|
||||
Platform support is constantly tested by continuous integration and CMake configuration adjusted to generate nice looking projects for all major IDE. Following guides will help you to setup build environment with no effort:
|
||||
|
||||
* [On Linux](https://wiki.vcmi.eu/How_to_build_VCMI_(Linux))
|
||||
* [On Linux for Windows with MXE](https://wiki.vcmi.eu/How_to_build_VCMI_(Linux/Cmake/MXE))
|
||||
* [On macOS](https://wiki.vcmi.eu/How_to_build_VCMI_(macOS))
|
||||
* [On Windows using MSVC and Vcpkg](https://wiki.vcmi.eu/How_to_build_VCMI_(Windows/Vcpkg))
|
||||
|
||||
## Copyright and license
|
||||
|
||||
|
@ -29,12 +29,9 @@ static long long pow(long long a, int b)
|
||||
|
||||
CDefHandler::CDefHandler()
|
||||
{
|
||||
notFreeImgs = false;
|
||||
}
|
||||
CDefHandler::~CDefHandler()
|
||||
{
|
||||
if (notFreeImgs)
|
||||
return;
|
||||
for (auto & elem : ourImages)
|
||||
{
|
||||
if (elem.bitmap)
|
||||
@ -44,11 +41,6 @@ CDefHandler::~CDefHandler()
|
||||
}
|
||||
}
|
||||
}
|
||||
CDefEssential::~CDefEssential()
|
||||
{
|
||||
for(auto & elem : ourImages)
|
||||
SDL_FreeSurface(elem.bitmap);
|
||||
}
|
||||
|
||||
void CDefHandler::openFromMemory(ui8 *table, const std::string & name)
|
||||
{
|
||||
@ -350,14 +342,6 @@ SDL_Surface * CDefHandler::getSprite (int SIndex, const ui8 * FDef, const SDL_Co
|
||||
return ret;
|
||||
}
|
||||
|
||||
CDefEssential * CDefHandler::essentialize()
|
||||
{
|
||||
auto ret = new CDefEssential();
|
||||
ret->ourImages = ourImages;
|
||||
notFreeImgs = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
CDefHandler * CDefHandler::giveDef(const std::string & defName)
|
||||
{
|
||||
ResourceID resID(std::string("SPRITES/") + defName, EResType::ANIMATION);
|
||||
@ -369,12 +353,4 @@ CDefHandler * CDefHandler::giveDef(const std::string & defName)
|
||||
nh->openFromMemory(data.get(), defName);
|
||||
return nh;
|
||||
}
|
||||
CDefEssential * CDefHandler::giveDefEss(const std::string & defName)
|
||||
{
|
||||
CDefEssential * ret;
|
||||
CDefHandler * temp = giveDef(defName);
|
||||
ret = temp->essentialize();
|
||||
delete temp;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -66,12 +66,6 @@ struct SSpriteDef
|
||||
ui32 TopMargin;
|
||||
} PACKED_STRUCT;
|
||||
|
||||
class CDefEssential //DefHandler with images only
|
||||
{
|
||||
public:
|
||||
std::vector<Cimage> ourImages;
|
||||
~CDefEssential();
|
||||
};
|
||||
|
||||
class CDefHandler
|
||||
{
|
||||
@ -84,20 +78,17 @@ private:
|
||||
int group;
|
||||
} ;
|
||||
std::vector<SEntry> SEntries ;
|
||||
|
||||
void openFromMemory(ui8 * table, const std::string & name);
|
||||
|
||||
void openFromMemory(ui8 * table, const std::string & name);
|
||||
SDL_Surface * getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const;
|
||||
public:
|
||||
int width, height; //width and height
|
||||
std::string defName;
|
||||
std::vector<Cimage> ourImages;
|
||||
bool notFreeImgs;
|
||||
|
||||
CDefHandler();
|
||||
~CDefHandler();
|
||||
|
||||
CDefEssential * essentialize();
|
||||
|
||||
|
||||
static CDefHandler * giveDef(const std::string & defName);
|
||||
static CDefEssential * giveDefEss(const std::string & defName);
|
||||
};
|
||||
|
@ -9,6 +9,8 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "CGameInfo.h"
|
||||
#include "../lib/CSkillHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
|
||||
@ -32,5 +34,6 @@ void CGameInfo::setFromLib()
|
||||
heroh = VLC->heroh;
|
||||
objh = VLC->objh;
|
||||
spellh = VLC->spellh;
|
||||
skillh = VLC->skillh;
|
||||
objtypeh = VLC->objtypeh;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ class CArtHandler;
|
||||
class CHeroHandler;
|
||||
class CCreatureHandler;
|
||||
class CSpellHandler;
|
||||
class CSkillHandler;
|
||||
class CBuildingHandler;
|
||||
class CObjectHandler;
|
||||
class CSoundHandler;
|
||||
@ -55,6 +56,7 @@ public:
|
||||
ConstTransitivePtr<CHeroHandler> heroh;
|
||||
ConstTransitivePtr<CCreatureHandler> creh;
|
||||
ConstTransitivePtr<CSpellHandler> spellh;
|
||||
ConstTransitivePtr<CSkillHandler> skillh;
|
||||
ConstTransitivePtr<CObjectHandler> objh;
|
||||
ConstTransitivePtr<CObjectClassesHandler> objtypeh;
|
||||
CGeneralTextHandler * generaltexth;
|
||||
|
@ -938,9 +938,9 @@ void processCommand(const std::string &message)
|
||||
//plays intro, ends when intro is over or button has been pressed (handles events)
|
||||
void playIntro()
|
||||
{
|
||||
if(CCS->videoh->openAndPlayVideo("3DOLOGO.SMK", 60, 40, screen, true, true))
|
||||
if(CCS->videoh->openAndPlayVideo("3DOLOGO.SMK", 0, 1, screen, true, true))
|
||||
{
|
||||
CCS->videoh->openAndPlayVideo("AZVS.SMK", 60, 80, screen, true, true);
|
||||
CCS->videoh->openAndPlayVideo("AZVS.SMK", 0, 1, screen, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "CGameInfo.h"
|
||||
#include "gui/CCursorHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CSkillHandler.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/mapping/CCampaignHandler.h"
|
||||
@ -3624,7 +3625,7 @@ void CBonusSelection::updateBonusSelection()
|
||||
desc = CGI->generaltexth->allTexts[718];
|
||||
|
||||
boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->levels[bonDescs[i].info3 - 1]); //skill level
|
||||
boost::algorithm::replace_first(desc, "%s", CGI->generaltexth->skillName[bonDescs[i].info2]); //skill name
|
||||
boost::algorithm::replace_first(desc, "%s", CGI->skillh->skillName(bonDescs[i].info2)); //skill name
|
||||
picNumber = bonDescs[i].info2 * 3 + bonDescs[i].info3 - 1;
|
||||
|
||||
break;
|
||||
|
@ -232,16 +232,10 @@ std::shared_ptr<CAnimation> Graphics::loadHeroFlagAnimation(const std::string &
|
||||
|
||||
for(const auto & rotation : rotations)
|
||||
{
|
||||
const int sourceGroup = rotation.first;
|
||||
const int targetGroup = rotation.second;
|
||||
const int sourceGroup = rotation.first;
|
||||
const int targetGroup = rotation.second;
|
||||
|
||||
for(size_t frame = 0; frame < anim->size(sourceGroup); ++frame)
|
||||
{
|
||||
anim->duplicateImage(sourceGroup, frame, targetGroup);
|
||||
|
||||
IImage * image = anim->getImage(frame, targetGroup);
|
||||
image->verticalFlip();
|
||||
}
|
||||
anim->createFlippedGroup(sourceGroup, targetGroup);
|
||||
}
|
||||
|
||||
return anim;
|
||||
@ -262,15 +256,10 @@ std::shared_ptr<CAnimation> Graphics::loadHeroAnimation(const std::string &name)
|
||||
|
||||
for(const auto & rotation : rotations)
|
||||
{
|
||||
const int sourceGroup = rotation.first;
|
||||
const int targetGroup = rotation.second;
|
||||
const int sourceGroup = rotation.first;
|
||||
const int targetGroup = rotation.second;
|
||||
|
||||
for(size_t frame = 0; frame < anim->size(sourceGroup); ++frame)
|
||||
{
|
||||
anim->duplicateImage(sourceGroup, frame, targetGroup);
|
||||
IImage * image = anim->getImage(frame, targetGroup);
|
||||
image->verticalFlip();
|
||||
}
|
||||
anim->createFlippedGroup(sourceGroup, targetGroup);
|
||||
}
|
||||
|
||||
return anim;
|
||||
|
@ -16,11 +16,11 @@
|
||||
#include "CBattleInterface.h"
|
||||
#include "CCreatureAnimation.h"
|
||||
|
||||
#include "../CDefHandler.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../Graphics.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
@ -58,13 +58,13 @@ bool CBattleAnimation::isEarliest(bool perStackConcurrency)
|
||||
{
|
||||
int lowestMoveID = owner->animIDhelper + 5;
|
||||
CBattleStackAnimation * thAnim = dynamic_cast<CBattleStackAnimation *>(this);
|
||||
CSpellEffectAnimation * thSen = dynamic_cast<CSpellEffectAnimation *>(this);
|
||||
CEffectAnimation * thSen = dynamic_cast<CEffectAnimation *>(this);
|
||||
|
||||
for(auto & elem : owner->pendingAnims)
|
||||
{
|
||||
|
||||
CBattleStackAnimation * stAnim = dynamic_cast<CBattleStackAnimation *>(elem.first);
|
||||
CSpellEffectAnimation * sen = dynamic_cast<CSpellEffectAnimation *>(elem.first);
|
||||
CEffectAnimation * sen = dynamic_cast<CEffectAnimation *>(elem.first);
|
||||
if(perStackConcurrency && stAnim && thAnim && stAnim->stack->ID != thAnim->stack->ID)
|
||||
continue;
|
||||
|
||||
@ -171,7 +171,7 @@ bool CDefenceAnimation::init()
|
||||
if(attAnim && attAnim->stack->ID != stack->ID)
|
||||
continue;
|
||||
|
||||
CSpellEffectAnimation * sen = dynamic_cast<CSpellEffectAnimation *>(elem.first);
|
||||
CEffectAnimation * sen = dynamic_cast<CEffectAnimation *>(elem.first);
|
||||
if (sen)
|
||||
continue;
|
||||
|
||||
@ -243,7 +243,7 @@ CCreatureAnim::EAnimType CDefenceAnimation::getMyAnimType()
|
||||
if(killed)
|
||||
return CCreatureAnim::DEATH;
|
||||
|
||||
if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM))
|
||||
if(vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM))
|
||||
return CCreatureAnim::DEFENCE;
|
||||
|
||||
return CCreatureAnim::HITTED;
|
||||
@ -270,10 +270,15 @@ void CDefenceAnimation::nextFrame()
|
||||
|
||||
void CDefenceAnimation::endAnim()
|
||||
{
|
||||
if (killed)
|
||||
if(killed)
|
||||
{
|
||||
myAnim->setType(CCreatureAnim::DEAD);
|
||||
}
|
||||
else
|
||||
{
|
||||
myAnim->setType(CCreatureAnim::HOLDING);
|
||||
}
|
||||
|
||||
|
||||
CBattleAnimation::endAnim();
|
||||
|
||||
@ -785,13 +790,13 @@ bool CShootingAnimation::init()
|
||||
spi.dx = animSpeed;
|
||||
spi.dy = 0;
|
||||
|
||||
SDL_Surface * img = owner->idToProjectile[spi.creID]->ourImages[0].bitmap;
|
||||
IImage * img = owner->idToProjectile[spi.creID]->getImage(0);
|
||||
|
||||
// Add explosion anim
|
||||
Point animPos(destPos.x - 126 + img->w / 2,
|
||||
destPos.y - 105 + img->h / 2);
|
||||
Point animPos(destPos.x - 126 + img->width() / 2,
|
||||
destPos.y - 105 + img->height() / 2);
|
||||
|
||||
owner->addNewAnim( new CSpellEffectAnimation(owner, catapultDamage ? "SGEXPL.DEF" : "CSGRCK.DEF", animPos.x, animPos.y));
|
||||
owner->addNewAnim( new CEffectAnimation(owner, catapultDamage ? "SGEXPL.DEF" : "CSGRCK.DEF", animPos.x, animPos.y));
|
||||
}
|
||||
|
||||
auto & angles = shooterInfo->animation.missleFrameAngles;
|
||||
@ -801,7 +806,7 @@ bool CShootingAnimation::init()
|
||||
owner->initStackProjectile(shooter);
|
||||
|
||||
// only frames below maxFrame are usable: anything higher is either no present or we don't know when it should be used
|
||||
size_t maxFrame = std::min<size_t>(angles.size(), owner->idToProjectile.at(spi.creID)->ourImages.size());
|
||||
size_t maxFrame = std::min<size_t>(angles.size(), owner->idToProjectile.at(spi.creID)->size(0));
|
||||
|
||||
assert(maxFrame > 0);
|
||||
|
||||
@ -872,35 +877,40 @@ void CShootingAnimation::endAnim()
|
||||
delete this;
|
||||
}
|
||||
|
||||
CSpellEffectAnimation::CSpellEffectAnimation(CBattleInterface * _owner, ui32 _effect, BattleHex _destTile, int _dx, int _dy, bool _Vflip, bool _alignToBottom)
|
||||
:CBattleAnimation(_owner), effect(_effect), destTile(_destTile), customAnim(""), x(-1), y(-1), dx(_dx), dy(_dy), Vflip(_Vflip), alignToBottom(_alignToBottom)
|
||||
CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx, int _dy, bool _Vflip, bool _alignToBottom)
|
||||
: CBattleAnimation(_owner),
|
||||
destTile(BattleHex::INVALID),
|
||||
customAnim(_customAnim),
|
||||
x(_x),
|
||||
y(_y),
|
||||
dx(_dx),
|
||||
dy(_dy),
|
||||
Vflip(_Vflip),
|
||||
alignToBottom(_alignToBottom)
|
||||
{
|
||||
logAnim->debug("Created spell anim for effect #%d", effect);
|
||||
logAnim->debug("Created effect animation %s", customAnim);
|
||||
}
|
||||
|
||||
CSpellEffectAnimation::CSpellEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx, int _dy, bool _Vflip, bool _alignToBottom)
|
||||
:CBattleAnimation(_owner), effect(-1), destTile(BattleHex::INVALID), customAnim(_customAnim), x(_x), y(_y), dx(_dx), dy(_dy), Vflip(_Vflip), alignToBottom(_alignToBottom)
|
||||
CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip, bool _alignToBottom)
|
||||
: CBattleAnimation(_owner),
|
||||
destTile(_destTile),
|
||||
customAnim(_customAnim),
|
||||
x(-1),
|
||||
y(-1),
|
||||
dx(0),
|
||||
dy(0),
|
||||
Vflip(_Vflip),
|
||||
alignToBottom(_alignToBottom)
|
||||
{
|
||||
logAnim->debug("Created spell anim for %s", customAnim);
|
||||
}
|
||||
|
||||
CSpellEffectAnimation::CSpellEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip, bool _alignToBottom)
|
||||
:CBattleAnimation(_owner), effect(-1), destTile(_destTile), customAnim(_customAnim), x(-1), y(-1), dx(0), dy(0), Vflip(_Vflip), alignToBottom(_alignToBottom)
|
||||
{
|
||||
logAnim->debug("Created spell anim for %s", customAnim);
|
||||
logAnim->debug("Created effect animation %s", customAnim);
|
||||
}
|
||||
|
||||
|
||||
bool CSpellEffectAnimation::init()
|
||||
bool CEffectAnimation::init()
|
||||
{
|
||||
if(!isEarliest(true))
|
||||
return false;
|
||||
|
||||
if(customAnim.empty() && effect != ui32(-1) && !graphics->battleACToDef[effect].empty())
|
||||
{
|
||||
customAnim = graphics->battleACToDef[effect][0];
|
||||
}
|
||||
|
||||
if(customAnim.empty())
|
||||
{
|
||||
endAnim();
|
||||
@ -909,97 +919,88 @@ bool CSpellEffectAnimation::init()
|
||||
|
||||
const bool areaEffect = (!destTile.isValid() && x == -1 && y == -1);
|
||||
|
||||
std::shared_ptr<CAnimation> animation = std::make_shared<CAnimation>(customAnim);
|
||||
|
||||
animation->preload();
|
||||
if(Vflip)
|
||||
animation->verticalFlip();
|
||||
|
||||
IImage * first = animation->getImage(0, 0, true);
|
||||
if(!first)
|
||||
{
|
||||
endAnim();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(areaEffect) //f.e. armageddon
|
||||
{
|
||||
CDefHandler * anim = CDefHandler::giveDef(customAnim);
|
||||
|
||||
for(int i=0; i * anim->width < owner->pos.w ; ++i)
|
||||
for(int i=0; i * first->width() < owner->pos.w ; ++i)
|
||||
{
|
||||
for(int j=0; j * anim->height < owner->pos.h ; ++j)
|
||||
for(int j=0; j * first->height() < owner->pos.h ; ++j)
|
||||
{
|
||||
BattleEffect be;
|
||||
be.effectID = ID;
|
||||
be.anim = CDefHandler::giveDef(customAnim);
|
||||
if (Vflip)
|
||||
{
|
||||
for (auto & elem : be.anim->ourImages)
|
||||
{
|
||||
CSDL_Ext::VflipSurf(elem.bitmap);
|
||||
}
|
||||
}
|
||||
be.animation = animation;
|
||||
be.currentFrame = 0;
|
||||
be.maxFrame = be.anim->ourImages.size();
|
||||
be.x = i * anim->width + owner->pos.x;
|
||||
be.y = j * anim->height + owner->pos.y;
|
||||
|
||||
be.x = i * first->width() + owner->pos.x;
|
||||
be.y = j * first->height() + owner->pos.y;
|
||||
be.position = BattleHex::INVALID;
|
||||
|
||||
owner->battleEffects.push_back(be);
|
||||
}
|
||||
}
|
||||
|
||||
delete anim;
|
||||
}
|
||||
else // Effects targeted at a specific creature/hex.
|
||||
{
|
||||
const CStack * destStack = owner->getCurrentPlayerInterface()->cb->battleGetStackByPos(destTile, false);
|
||||
Rect & tilePos = owner->bfield[destTile]->pos;
|
||||
BattleEffect be;
|
||||
be.effectID = ID;
|
||||
be.animation = animation;
|
||||
be.currentFrame = 0;
|
||||
|
||||
const CStack* destStack = owner->getCurrentPlayerInterface()->cb->battleGetStackByPos(destTile, false);
|
||||
Rect & tilePos = owner->bfield[destTile]->pos;
|
||||
BattleEffect be;
|
||||
be.effectID = ID;
|
||||
be.anim = CDefHandler::giveDef(customAnim);
|
||||
|
||||
if (Vflip)
|
||||
{
|
||||
for (auto & elem : be.anim->ourImages)
|
||||
{
|
||||
CSDL_Ext::VflipSurf(elem.bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
be.currentFrame = 0;
|
||||
be.maxFrame = be.anim->ourImages.size();
|
||||
|
||||
//todo: lightning anim frame count override
|
||||
//todo: lightning anim frame count override
|
||||
|
||||
// if(effect == 1)
|
||||
// be.maxFrame = 3;
|
||||
|
||||
if(x == -1)
|
||||
{
|
||||
be.x = tilePos.x + tilePos.w/2 - be.anim->width/2;
|
||||
}
|
||||
if(x == -1)
|
||||
{
|
||||
be.x = tilePos.x + tilePos.w/2 - first->width()/2;
|
||||
}
|
||||
else
|
||||
{
|
||||
be.x = x;
|
||||
}
|
||||
|
||||
if(y == -1)
|
||||
{
|
||||
if(alignToBottom)
|
||||
be.y = tilePos.y + tilePos.h - first->height();
|
||||
else
|
||||
{
|
||||
be.x = x;
|
||||
}
|
||||
be.y = tilePos.y - first->height()/2;
|
||||
}
|
||||
else
|
||||
{
|
||||
be.y = y;
|
||||
}
|
||||
|
||||
if(y == -1)
|
||||
{
|
||||
if(alignToBottom)
|
||||
be.y = tilePos.y + tilePos.h - be.anim->height;
|
||||
else
|
||||
be.y = tilePos.y - be.anim->height/2;
|
||||
}
|
||||
else
|
||||
{
|
||||
be.y = y;
|
||||
}
|
||||
// Correction for 2-hex creatures.
|
||||
if(destStack != nullptr && destStack->doubleWide())
|
||||
be.x += (destStack->side == BattleSide::ATTACKER ? -1 : 1)*tilePos.w/2;
|
||||
|
||||
// Correction for 2-hex creatures.
|
||||
if (destStack != nullptr && destStack->doubleWide())
|
||||
be.x += (destStack->side == BattleSide::ATTACKER ? -1 : 1)*tilePos.w/2;
|
||||
|
||||
//Indicate if effect should be drawn on top of everything or just on top of the hex
|
||||
be.position = destTile;
|
||||
|
||||
owner->battleEffects.push_back(be);
|
||||
//Indicate if effect should be drawn on top of everything or just on top of the hex
|
||||
be.position = destTile;
|
||||
|
||||
owner->battleEffects.push_back(be);
|
||||
}
|
||||
//battleEffects
|
||||
return true;
|
||||
}
|
||||
|
||||
void CSpellEffectAnimation::nextFrame()
|
||||
void CEffectAnimation::nextFrame()
|
||||
{
|
||||
//notice: there may be more than one effect in owner->battleEffects correcponding to this animation (ie. armageddon)
|
||||
for(auto & elem : owner->battleEffects)
|
||||
@ -1008,7 +1009,7 @@ void CSpellEffectAnimation::nextFrame()
|
||||
{
|
||||
elem.currentFrame += AnimationControls::getSpellEffectSpeed() * GH.mainFPSmng->getElapsedMilliseconds() / 1000;
|
||||
|
||||
if(elem.currentFrame >= elem.maxFrame)
|
||||
if(elem.currentFrame >= elem.animation->size())
|
||||
{
|
||||
endAnim();
|
||||
break;
|
||||
@ -1022,7 +1023,7 @@ void CSpellEffectAnimation::nextFrame()
|
||||
}
|
||||
}
|
||||
|
||||
void CSpellEffectAnimation::endAnim()
|
||||
void CEffectAnimation::endAnim()
|
||||
{
|
||||
CBattleAnimation::endAnim();
|
||||
|
||||
@ -1038,7 +1039,6 @@ void CSpellEffectAnimation::endAnim()
|
||||
|
||||
for(auto & elem : toDel)
|
||||
{
|
||||
delete elem->anim;
|
||||
owner->battleEffects.erase(elem);
|
||||
}
|
||||
|
||||
|
@ -208,16 +208,15 @@ public:
|
||||
void endAnim() override;
|
||||
|
||||
//last two params only for catapult attacks
|
||||
CShootingAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest,
|
||||
const CStack * _attacked, bool _catapult = false, int _catapultDmg = 0);
|
||||
CShootingAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest,
|
||||
const CStack * _attacked, bool _catapult = false, int _catapultDmg = 0);
|
||||
virtual ~CShootingAnimation(){};
|
||||
};
|
||||
|
||||
/// This class manages a spell effect animation
|
||||
class CSpellEffectAnimation : public CBattleAnimation
|
||||
/// This class manages effect animation
|
||||
class CEffectAnimation : public CBattleAnimation
|
||||
{
|
||||
private:
|
||||
ui32 effect;
|
||||
BattleHex destTile;
|
||||
std::string customAnim;
|
||||
int x, y, dx, dy;
|
||||
@ -228,8 +227,7 @@ public:
|
||||
void nextFrame() override;
|
||||
void endAnim() override;
|
||||
|
||||
CSpellEffectAnimation(CBattleInterface * _owner, ui32 _effect, BattleHex _destTile, int _dx = 0, int _dy = 0, bool _Vflip = false, bool _alignToBottom = false);
|
||||
CSpellEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx = 0, int _dy = 0, bool _Vflip = false, bool _alignToBottom = false);
|
||||
CSpellEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip = false, bool _alignToBottom = false);
|
||||
virtual ~CSpellEffectAnimation(){};
|
||||
CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx = 0, int _dy = 0, bool _Vflip = false, bool _alignToBottom = false);
|
||||
CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip = false, bool _alignToBottom = false);
|
||||
virtual ~CEffectAnimation(){};
|
||||
};
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CVideoHandler.h"
|
||||
#include "../Graphics.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
@ -263,7 +264,10 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
|
||||
battleImage = hero1->type->heroClass->imageBattleMale;
|
||||
|
||||
attackingHero = new CBattleHero(battleImage, false, hero1->tempOwner, hero1->tempOwner == curInt->playerID ? hero1 : nullptr, this);
|
||||
attackingHero->pos = genRect(attackingHero->dh->ourImages[0].bitmap->h, attackingHero->dh->ourImages[0].bitmap->w, pos.x - 43, pos.y - 19);
|
||||
|
||||
IImage * img = attackingHero->animation->getImage(0, 0, true);
|
||||
if(img)
|
||||
attackingHero->pos = genRect(img->height(), img->width(), pos.x - 43, pos.y - 19);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -278,7 +282,10 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
|
||||
battleImage = hero2->type->heroClass->imageBattleMale;
|
||||
|
||||
defendingHero = new CBattleHero(battleImage, true, hero2->tempOwner, hero2->tempOwner == curInt->playerID ? hero2 : nullptr, this);
|
||||
defendingHero->pos = genRect(defendingHero->dh->ourImages[0].bitmap->h, defendingHero->dh->ourImages[0].bitmap->w, pos.x + 693, pos.y - 19);
|
||||
|
||||
IImage * img = defendingHero->animation->getImage(0, 0, true);
|
||||
if(img)
|
||||
defendingHero->pos = genRect(img->height(), img->width(), pos.x + 693, pos.y - 19);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -425,9 +432,6 @@ CBattleInterface::~CBattleInterface()
|
||||
for (auto & elem : creAnims)
|
||||
delete elem.second;
|
||||
|
||||
for (auto & elem : idToProjectile)
|
||||
delete elem.second;
|
||||
|
||||
for (auto & elem : idToObstacle)
|
||||
delete elem.second;
|
||||
|
||||
@ -999,20 +1003,21 @@ void CBattleInterface::newStack(const CStack *stack)
|
||||
|
||||
void CBattleInterface::initStackProjectile(const CStack * stack)
|
||||
{
|
||||
CDefHandler *&projectile = idToProjectile[stack->getCreature()->idNumber];
|
||||
|
||||
const CCreature *creature;//creature whose shots should be loaded
|
||||
if (stack->getCreature()->idNumber == CreatureID::ARROW_TOWERS)
|
||||
const CCreature * creature;//creature whose shots should be loaded
|
||||
if(stack->getCreature()->idNumber == CreatureID::ARROW_TOWERS)
|
||||
creature = CGI->creh->creatures[siegeH->town->town->clientInfo.siegeShooter];
|
||||
else
|
||||
creature = stack->getCreature();
|
||||
|
||||
projectile = CDefHandler::giveDef(creature->animation.projectileImageName);
|
||||
std::shared_ptr<CAnimation> projectile = std::make_shared<CAnimation>(creature->animation.projectileImageName);
|
||||
projectile->preload();
|
||||
|
||||
for (auto & elem : projectile->ourImages) //alpha transforming
|
||||
{
|
||||
CSDL_Ext::alphaTransform(elem.bitmap);
|
||||
}
|
||||
if(projectile->size(1) != 0)
|
||||
logAnim->error("Expected empty group 1 in stack projectile");
|
||||
else
|
||||
projectile->createFlippedGroup(0, 1);
|
||||
|
||||
idToProjectile[stack->getCreature()->idNumber] = projectile;
|
||||
}
|
||||
|
||||
void CBattleInterface::stackRemoved(int stackID)
|
||||
@ -1216,7 +1221,7 @@ void CBattleInterface::stackIsCatapulting(const CatapultAttack & ca)
|
||||
{
|
||||
Point destPos = CClickableHex::getXYUnitAnim(attackInfo.destinationTile, nullptr, this) + Point(99, 120);
|
||||
|
||||
addNewAnim(new CSpellEffectAnimation(this, "SGEXPL.DEF", destPos.x, destPos.y));
|
||||
addNewAnim(new CEffectAnimation(this, "SGEXPL.DEF", destPos.x, destPos.y));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1304,20 +1309,23 @@ void CBattleInterface::spellCast(const BattleSpellCast *sc)
|
||||
|
||||
std::string animToDisplay = spell.animationInfo.selectProjectile(angle);
|
||||
|
||||
if (!animToDisplay.empty())
|
||||
if(!animToDisplay.empty())
|
||||
{
|
||||
//TODO: calculate inside CEffectAnimation
|
||||
std::shared_ptr<CAnimation> tmp = std::make_shared<CAnimation>(animToDisplay);
|
||||
tmp->load(0, 0);
|
||||
IImage * first = tmp->getImage(0, 0);
|
||||
|
||||
//displaying animation
|
||||
CDefEssential *animDef = CDefHandler::giveDefEss(animToDisplay);
|
||||
double diffX = (destcoord.x - srccoord.x)*(destcoord.x - srccoord.x);
|
||||
double diffY = (destcoord.y - srccoord.y)*(destcoord.y - srccoord.y);
|
||||
double distance = sqrt(diffX + diffY);
|
||||
|
||||
int steps = distance / AnimationControls::getSpellEffectSpeed() + 1;
|
||||
int dx = (destcoord.x - srccoord.x - animDef->ourImages[0].bitmap->w)/steps;
|
||||
int dy = (destcoord.y - srccoord.y - animDef->ourImages[0].bitmap->h)/steps;
|
||||
int dx = (destcoord.x - srccoord.x - first->width())/steps;
|
||||
int dy = (destcoord.y - srccoord.y - first->height())/steps;
|
||||
|
||||
delete animDef;
|
||||
addNewAnim(new CSpellEffectAnimation(this, animToDisplay, srccoord.x, srccoord.y, dx, dy, Vflip));
|
||||
addNewAnim(new CEffectAnimation(this, animToDisplay, srccoord.x, srccoord.y, dx, dy, Vflip));
|
||||
}
|
||||
}
|
||||
waitForAnims();
|
||||
@ -1349,8 +1357,8 @@ void CBattleInterface::spellCast(const BattleSpellCast *sc)
|
||||
{
|
||||
Point leftHero = Point(15, 30) + pos;
|
||||
Point rightHero = Point(755, 30) + pos;
|
||||
addNewAnim(new CSpellEffectAnimation(this, sc->side ? "SP07_A.DEF" : "SP07_B.DEF", leftHero.x, leftHero.y, 0, 0, false));
|
||||
addNewAnim(new CSpellEffectAnimation(this, sc->side ? "SP07_B.DEF" : "SP07_A.DEF", rightHero.x, rightHero.y, 0, 0, false));
|
||||
addNewAnim(new CEffectAnimation(this, sc->side ? "SP07_A.DEF" : "SP07_B.DEF", leftHero.x, leftHero.y, 0, 0, false));
|
||||
addNewAnim(new CEffectAnimation(this, sc->side ? "SP07_B.DEF" : "SP07_A.DEF", rightHero.x, rightHero.y, 0, 0, false));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1413,9 +1421,11 @@ void CBattleInterface::castThisSpell(SpellID spellID)
|
||||
}
|
||||
}
|
||||
|
||||
void CBattleInterface::displayEffect(ui32 effect, int destTile)
|
||||
void CBattleInterface::displayEffect(ui32 effect, BattleHex destTile)
|
||||
{
|
||||
addNewAnim(new CSpellEffectAnimation(this, effect, destTile, 0, 0, false));
|
||||
std::string customAnim = graphics->battleACToDef[effect][0];
|
||||
|
||||
addNewAnim(new CEffectAnimation(this, customAnim, destTile));
|
||||
}
|
||||
|
||||
void CBattleInterface::displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile)
|
||||
@ -1426,7 +1436,7 @@ void CBattleInterface::displaySpellAnimation(const CSpell::TAnimation & animatio
|
||||
}
|
||||
else
|
||||
{
|
||||
addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM));
|
||||
addNewAnim(new CEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM));
|
||||
}
|
||||
}
|
||||
|
||||
@ -2732,7 +2742,7 @@ void CBattleInterface::obstaclePlaced(const CObstacleInstance & oi)
|
||||
//we assume here that effect graphics have the same size as the usual obstacle image
|
||||
// -> if we know how to blit obstacle, let's blit the effect in the same place
|
||||
Point whereTo = getObstaclePosition(getObstacleImage(oi), oi);
|
||||
addNewAnim(new CSpellEffectAnimation(this, defname, whereTo.x, whereTo.y));
|
||||
addNewAnim(new CEffectAnimation(this, defname, whereTo.x, whereTo.y));
|
||||
|
||||
//TODO we need to wait after playing sound till it's finished, otherwise it overlaps and sounds really bad
|
||||
//CCS->soundh->playSound(sound);
|
||||
@ -3166,23 +3176,18 @@ void CBattleInterface::showProjectiles(SDL_Surface *to)
|
||||
continue; // wait...
|
||||
}
|
||||
|
||||
SDL_Surface *image = idToProjectile[it->creID]->ourImages[it->frameNum].bitmap;
|
||||
size_t group = it->reverse ? 1 : 0;
|
||||
IImage * image = idToProjectile[it->creID]->getImage(it->frameNum, group, true);
|
||||
|
||||
SDL_Rect dst;
|
||||
dst.h = image->h;
|
||||
dst.w = image->w;
|
||||
dst.x = it->x - dst.w / 2;
|
||||
dst.y = it->y - dst.h / 2;
|
||||
if(image)
|
||||
{
|
||||
SDL_Rect dst;
|
||||
dst.h = image->height();
|
||||
dst.w = image->width();
|
||||
dst.x = it->x - dst.w / 2;
|
||||
dst.y = it->y - dst.h / 2;
|
||||
|
||||
if (it->reverse)
|
||||
{
|
||||
SDL_Surface *rev = CSDL_Ext::verticalFlip(image);
|
||||
CSDL_Ext::blit8bppAlphaTo24bpp(rev, nullptr, to, &dst);
|
||||
SDL_FreeSurface(rev);
|
||||
}
|
||||
else
|
||||
{
|
||||
CSDL_Ext::blit8bppAlphaTo24bpp(image, nullptr, to, &dst);
|
||||
image->draw(to, &dst, nullptr);
|
||||
}
|
||||
|
||||
// Update projectile
|
||||
@ -3200,7 +3205,7 @@ void CBattleInterface::showProjectiles(SDL_Surface *to)
|
||||
it->y = it->catapultInfo->calculateY(it->x);
|
||||
|
||||
++(it->frameNum);
|
||||
it->frameNum %= idToProjectile[it->creID]->ourImages.size();
|
||||
it->frameNum %= idToProjectile[it->creID]->size(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -3368,11 +3373,13 @@ void CBattleInterface::showBattleEffects(SDL_Surface *to, const std::vector<cons
|
||||
for (auto & elem : battleEffects)
|
||||
{
|
||||
int currentFrame = floor(elem->currentFrame);
|
||||
currentFrame %= elem->anim->ourImages.size();
|
||||
currentFrame %= elem->animation->size();
|
||||
|
||||
SDL_Surface *bitmapToBlit = elem->anim->ourImages[currentFrame].bitmap;
|
||||
SDL_Rect temp_rect = genRect(bitmapToBlit->h, bitmapToBlit->w, elem->x, elem->y);
|
||||
SDL_BlitSurface(bitmapToBlit, nullptr, to, &temp_rect);
|
||||
IImage * img = elem->animation->getImage(currentFrame);
|
||||
|
||||
SDL_Rect temp_rect = genRect(img->height(), img->width(), elem->x, elem->y);
|
||||
|
||||
img->draw(to, &temp_rect, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,6 +48,7 @@ struct BattleHex;
|
||||
struct InfoAboutHero;
|
||||
struct BattleAction;
|
||||
class CBattleGameInterface;
|
||||
class CAnimation;
|
||||
|
||||
/// Small struct which contains information about the id of the attacked stack, the damage dealt,...
|
||||
struct StackAttackedInfo
|
||||
@ -67,8 +68,7 @@ struct BattleEffect
|
||||
{
|
||||
int x, y; //position on the screen
|
||||
float currentFrame;
|
||||
int maxFrame;
|
||||
CDefHandler *anim; //animation to display
|
||||
std::shared_ptr<CAnimation> animation;
|
||||
int effectID; //uniqueID equal ot ID of appropriate CSpellEffectAnim
|
||||
BattleHex position; //Indicates if effect which hex the effect is drawn on
|
||||
};
|
||||
@ -120,6 +120,7 @@ class CBattleInterface : public CIntObject
|
||||
};
|
||||
private:
|
||||
SDL_Surface *background, *menu, *amountNormal, *amountNegative, *amountPositive, *amountEffNeutral, *cellBorders, *backgroundWithHexes;
|
||||
|
||||
CButton *bOptions, *bSurrender, *bFlee, *bAutofight, *bSpell,
|
||||
* bWait, *bDefence, *bConsoleUp, *bConsoleDown, *btactNext, *btactEnd;
|
||||
CBattleConsole *console;
|
||||
@ -128,7 +129,9 @@ private:
|
||||
const CCreatureSet *army1, *army2; //copy of initial armies (for result window)
|
||||
const CGHeroInstance *attackingHeroInstance, *defendingHeroInstance;
|
||||
std::map<int, CCreatureAnimation *> creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID)
|
||||
std::map<int, CDefHandler *> idToProjectile; //projectiles of creatures (creatureID, defhandler)
|
||||
|
||||
std::map<int, std::shared_ptr<CAnimation>> idToProjectile;
|
||||
|
||||
std::map<int, CDefHandler *> idToObstacle; //obstacles located on the battlefield
|
||||
std::map<int, SDL_Surface *> idToAbsoluteObstacle; //obstacles located on the battlefield
|
||||
|
||||
@ -340,7 +343,7 @@ public:
|
||||
void spellCast(const BattleSpellCast *sc); //called when a hero casts a spell
|
||||
void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
|
||||
void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook
|
||||
void displayEffect(ui32 effect, int destTile); //displays custom effect on the battlefield
|
||||
void displayEffect(ui32 effect, BattleHex destTile); //displays custom effect on the battlefield
|
||||
|
||||
void displaySpellCast(SpellID spellID, BattleHex destinationTile); //displays spell`s cast animation
|
||||
void displaySpellEffect(SpellID spellID, BattleHex destinationTile); //displays spell`s affected animation
|
||||
@ -376,7 +379,7 @@ public:
|
||||
|
||||
friend class CBattleResultWindow;
|
||||
friend class CBattleHero;
|
||||
friend class CSpellEffectAnimation;
|
||||
friend class CEffectAnimation;
|
||||
friend class CBattleStackAnimation;
|
||||
friend class CReverseAnimation;
|
||||
friend class CDefenceAnimation;
|
||||
|
@ -13,13 +13,13 @@
|
||||
#include "CBattleInterface.h"
|
||||
|
||||
#include "../CBitmapHandler.h"
|
||||
#include "../CDefHandler.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMessage.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../CVideoHandler.h"
|
||||
#include "../Graphics.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../gui/CCursorHandler.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
@ -129,13 +129,18 @@ CBattleConsole::CBattleConsole() : lastShown(-1), alterTxt(""), whoSetAlter(0)
|
||||
|
||||
void CBattleHero::show(SDL_Surface * to)
|
||||
{
|
||||
IImage * flagFrame = flagAnimation->getImage(flagAnim, 0, true);
|
||||
|
||||
if(!flagFrame)
|
||||
return;
|
||||
|
||||
//animation of flag
|
||||
SDL_Rect temp_rect;
|
||||
if(flip)
|
||||
{
|
||||
temp_rect = genRect(
|
||||
flag->ourImages[flagAnim].bitmap->h,
|
||||
flag->ourImages[flagAnim].bitmap->w,
|
||||
flagFrame->height(),
|
||||
flagFrame->width(),
|
||||
pos.x + 61,
|
||||
pos.y + 39);
|
||||
|
||||
@ -143,28 +148,30 @@ void CBattleHero::show(SDL_Surface * to)
|
||||
else
|
||||
{
|
||||
temp_rect = genRect(
|
||||
flag->ourImages[flagAnim].bitmap->h,
|
||||
flag->ourImages[flagAnim].bitmap->w,
|
||||
flagFrame->height(),
|
||||
flagFrame->width(),
|
||||
pos.x + 72,
|
||||
pos.y + 39);
|
||||
}
|
||||
CSDL_Ext::blit8bppAlphaTo24bpp(
|
||||
flag->ourImages[flagAnim].bitmap,
|
||||
nullptr,
|
||||
screen,
|
||||
&temp_rect);
|
||||
|
||||
flagFrame->draw(screen, &temp_rect, nullptr); //FIXME: why screen?
|
||||
|
||||
//animation of hero
|
||||
SDL_Rect rect = pos;
|
||||
CSDL_Ext::blit8bppAlphaTo24bpp(dh->ourImages[currentFrame].bitmap, nullptr, to, &rect);
|
||||
|
||||
if ( ++animCount == 4 )
|
||||
IImage * heroFrame = animation->getImage(currentFrame, phase, true);
|
||||
if(!heroFrame)
|
||||
return;
|
||||
|
||||
heroFrame->draw(to, &rect, nullptr);
|
||||
|
||||
if(++animCount >= 4)
|
||||
{
|
||||
animCount = 0;
|
||||
if ( ++flagAnim >= flag->ourImages.size())
|
||||
if(++flagAnim >= flagAnimation->size(0))
|
||||
flagAnim = 0;
|
||||
|
||||
if ( ++currentFrame >= lastFrame)
|
||||
if(++currentFrame >= lastFrame)
|
||||
switchToNextPhase();
|
||||
}
|
||||
}
|
||||
@ -190,7 +197,13 @@ void CBattleHero::clickLeft(tribool down, bool previousState)
|
||||
if(myOwner->spellDestSelectMode) //we are casting a spell
|
||||
return;
|
||||
|
||||
if(myHero != nullptr && !down && myOwner->myTurn && myOwner->getCurrentPlayerInterface()->cb->battleCanCastSpell(myHero, ECastingMode::HERO_CASTING) == ESpellCastProblem::OK) //check conditions
|
||||
if(boost::logic::indeterminate(down))
|
||||
return;
|
||||
|
||||
if(!myHero || down || !myOwner->myTurn)
|
||||
return;
|
||||
|
||||
if(myOwner->getCurrentPlayerInterface()->cb->battleCanCastSpell(myHero, ECastingMode::HERO_CASTING) == ESpellCastProblem::OK) //check conditions
|
||||
{
|
||||
for(int it=0; it<GameConstants::BFIELD_SIZE; ++it) //do nothing when any hex is hovered - hero's animation overlaps battlefield
|
||||
{
|
||||
@ -205,6 +218,9 @@ void CBattleHero::clickLeft(tribool down, bool previousState)
|
||||
|
||||
void CBattleHero::clickRight(tribool down, bool previousState)
|
||||
{
|
||||
if(boost::logic::indeterminate(down))
|
||||
return;
|
||||
|
||||
Point windowPosition;
|
||||
windowPosition.x = (!flip) ? myOwner->pos.topLeft().x + 1 : myOwner->pos.topRight().x - 79;
|
||||
windowPosition.y = myOwner->pos.y + 135;
|
||||
@ -220,24 +236,19 @@ void CBattleHero::clickRight(tribool down, bool previousState)
|
||||
|
||||
void CBattleHero::switchToNextPhase()
|
||||
{
|
||||
if (phase != nextPhase)
|
||||
if(phase != nextPhase)
|
||||
{
|
||||
phase = nextPhase;
|
||||
|
||||
//find first and last frames of our animation
|
||||
for (firstFrame = 0;
|
||||
firstFrame < dh->ourImages.size() && dh->ourImages[firstFrame].groupNumber != phase;
|
||||
firstFrame++);
|
||||
firstFrame = 0;
|
||||
|
||||
for (lastFrame = firstFrame;
|
||||
lastFrame < dh->ourImages.size() && dh->ourImages[lastFrame].groupNumber == phase;
|
||||
lastFrame++);
|
||||
lastFrame = animation->size(phase);
|
||||
}
|
||||
|
||||
currentFrame = firstFrame;
|
||||
}
|
||||
|
||||
CBattleHero::CBattleHero(const std::string & defName, bool flipG, PlayerColor player, const CGHeroInstance * hero, const CBattleInterface * owner):
|
||||
CBattleHero::CBattleHero(const std::string & animationPath, bool flipG, PlayerColor player, const CGHeroInstance * hero, const CBattleInterface * owner):
|
||||
flip(flipG),
|
||||
myHero(hero),
|
||||
myOwner(owner),
|
||||
@ -246,39 +257,25 @@ CBattleHero::CBattleHero(const std::string & defName, bool flipG, PlayerColor pl
|
||||
flagAnim(0),
|
||||
animCount(0)
|
||||
{
|
||||
dh = CDefHandler::giveDef( defName );
|
||||
for(auto & elem : dh->ourImages) //transforming images
|
||||
{
|
||||
if(flip)
|
||||
{
|
||||
SDL_Surface * hlp = CSDL_Ext::verticalFlip(elem.bitmap);
|
||||
SDL_FreeSurface(elem.bitmap);
|
||||
elem.bitmap = hlp;
|
||||
}
|
||||
CSDL_Ext::alphaTransform(elem.bitmap);
|
||||
}
|
||||
animation = std::make_shared<CAnimation>(animationPath);
|
||||
animation->preload();
|
||||
if(flipG)
|
||||
animation->verticalFlip();
|
||||
|
||||
if(flip)
|
||||
flag = CDefHandler::giveDef("CMFLAGR.DEF");
|
||||
flagAnimation = std::make_shared<CAnimation>("CMFLAGR");
|
||||
else
|
||||
flag = CDefHandler::giveDef("CMFLAGL.DEF");
|
||||
flagAnimation = std::make_shared<CAnimation>("CMFLAGL");
|
||||
|
||||
flagAnimation->preload();
|
||||
flagAnimation->playerColored(player);
|
||||
|
||||
//coloring flag and adding transparency
|
||||
for(auto & elem : flag->ourImages)
|
||||
{
|
||||
CSDL_Ext::alphaTransform(elem.bitmap);
|
||||
graphics->blueToPlayersAdv(elem.bitmap, player);
|
||||
}
|
||||
addUsedEvents(LCLICK | RCLICK | HOVER);
|
||||
|
||||
switchToNextPhase();
|
||||
}
|
||||
|
||||
CBattleHero::~CBattleHero()
|
||||
{
|
||||
delete dh;
|
||||
delete flag;
|
||||
}
|
||||
CBattleHero::~CBattleHero() = default;
|
||||
|
||||
CBattleOptionsWindow::CBattleOptionsWindow(const SDL_Rect & position, CBattleInterface *owner)
|
||||
{
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include "../windows/CWindowObject.h"
|
||||
|
||||
struct SDL_Surface;
|
||||
class CDefHandler;
|
||||
class CGHeroInstance;
|
||||
class CBattleInterface;
|
||||
class CPicture;
|
||||
@ -53,19 +52,24 @@ class CBattleHero : public CIntObject
|
||||
void switchToNextPhase();
|
||||
public:
|
||||
bool flip; //false if it's attacking hero, true otherwise
|
||||
CDefHandler *dh, *flag; //animation and flag
|
||||
|
||||
std::shared_ptr<CAnimation> animation;
|
||||
std::shared_ptr<CAnimation> flagAnimation;
|
||||
|
||||
const CGHeroInstance * myHero; //this animation's hero instance
|
||||
const CBattleInterface * myOwner; //battle interface to which this animation is assigned
|
||||
int phase; //stage of animation
|
||||
int nextPhase; //stage of animation to be set after current phase is fully displayed
|
||||
int currentFrame, firstFrame, lastFrame; //frame of animation
|
||||
ui8 flagAnim, animCount; //for flag animation
|
||||
|
||||
size_t flagAnim;
|
||||
ui8 animCount; //for flag animation
|
||||
void show(SDL_Surface * to) override; //prints next frame of animation to to
|
||||
void setPhase(int newPhase); //sets phase of hero animation
|
||||
void hover(bool on) override;
|
||||
void clickLeft(tribool down, bool previousState) override; //call-in
|
||||
void clickRight(tribool down, bool previousState) override; //call-in
|
||||
CBattleHero(const std::string &defName, bool filpG, PlayerColor player, const CGHeroInstance *hero, const CBattleInterface *owner);
|
||||
CBattleHero(const std::string & animationPath, bool filpG, PlayerColor player, const CGHeroInstance * hero, const CBattleInterface * owner);
|
||||
~CBattleHero();
|
||||
};
|
||||
|
||||
|
@ -10,14 +10,10 @@
|
||||
#include "StdInc.h"
|
||||
#include "CCreatureAnimation.h"
|
||||
|
||||
#include "../../lib/vcmi_endian.h"
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/filesystem/Filesystem.h"
|
||||
#include "../../lib/filesystem/CBinaryReader.h"
|
||||
#include "../../lib/filesystem/CMemoryStream.h"
|
||||
|
||||
#include "../gui/SDL_Pixels.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
|
||||
static const SDL_Color creatureBlueBorder = { 0, 255, 255, 255 };
|
||||
static const SDL_Color creatureGoldBorder = { 255, 255, 0, 255 };
|
||||
@ -142,60 +138,43 @@ void CCreatureAnimation::setType(CCreatureAnim::EAnimType type)
|
||||
play();
|
||||
}
|
||||
|
||||
CCreatureAnimation::CCreatureAnimation(std::string name, TSpeedController controller)
|
||||
: defName(name),
|
||||
speed(0.1),
|
||||
currentFrame(0),
|
||||
elapsedTime(0),
|
||||
CCreatureAnimation::CCreatureAnimation(const std::string & name_, TSpeedController controller)
|
||||
: name(name_),
|
||||
speed(0.1),
|
||||
currentFrame(0),
|
||||
elapsedTime(0),
|
||||
type(CCreatureAnim::HOLDING),
|
||||
border(CSDL_Ext::makeColor(0, 0, 0, 0)),
|
||||
speedController(controller),
|
||||
once(false)
|
||||
speedController(controller),
|
||||
once(false)
|
||||
{
|
||||
// separate block to avoid accidental use of "data" after it was moved into "pixelData"
|
||||
{
|
||||
ResourceID resID(std::string("SPRITES/") + name, EResType::ANIMATION);
|
||||
forward = std::make_shared<CAnimation>(name_);
|
||||
reverse = std::make_shared<CAnimation>(name_);
|
||||
|
||||
auto data = CResourceHandler::get()->load(resID)->readAll();
|
||||
|
||||
pixelData = std::move(data.first);
|
||||
pixelDataSize = data.second;
|
||||
}
|
||||
|
||||
CMemoryStream stm(pixelData.get(), pixelDataSize);
|
||||
|
||||
CBinaryReader reader(&stm);
|
||||
|
||||
reader.readInt32(); // def type, unused
|
||||
|
||||
fullWidth = reader.readInt32();
|
||||
fullHeight = reader.readInt32();
|
||||
|
||||
int totalBlocks = reader.readInt32();
|
||||
|
||||
for (auto & elem : palette)
|
||||
{
|
||||
elem.r = reader.readUInt8();
|
||||
elem.g = reader.readUInt8();
|
||||
elem.b = reader.readUInt8();
|
||||
elem.a = SDL_ALPHA_OPAQUE;
|
||||
}
|
||||
|
||||
for (int i=0; i<totalBlocks; i++)
|
||||
{
|
||||
int groupID = reader.readInt32();
|
||||
|
||||
int totalInBlock = reader.readInt32();
|
||||
|
||||
reader.skip(4 + 4 + 13 * totalInBlock); // some unused data
|
||||
|
||||
for (int j=0; j<totalInBlock; j++)
|
||||
dataOffsets[groupID].push_back(reader.readUInt32());
|
||||
}
|
||||
//todo: optimize
|
||||
forward->preload();
|
||||
reverse->preload();
|
||||
|
||||
// if necessary, add one frame into vcmi-only group DEAD
|
||||
if (dataOffsets.count(CCreatureAnim::DEAD) == 0)
|
||||
dataOffsets[CCreatureAnim::DEAD].push_back(dataOffsets[CCreatureAnim::DEATH].back());
|
||||
if(forward->size(CCreatureAnim::DEAD) == 0)
|
||||
{
|
||||
forward->duplicateImage(CCreatureAnim::DEATH, forward->size(CCreatureAnim::DEATH)-1, CCreatureAnim::DEAD);
|
||||
reverse->duplicateImage(CCreatureAnim::DEATH, reverse->size(CCreatureAnim::DEATH)-1, CCreatureAnim::DEAD);
|
||||
}
|
||||
|
||||
//TODO: get dimensions form CAnimation
|
||||
IImage * first = forward->getImage(0, type, true);
|
||||
|
||||
if(!first)
|
||||
{
|
||||
fullWidth = 0;
|
||||
fullHeight = 0;
|
||||
return;
|
||||
}
|
||||
fullWidth = first->width();
|
||||
fullHeight = first->height();
|
||||
|
||||
reverse->verticalFlip();
|
||||
|
||||
play();
|
||||
}
|
||||
@ -285,142 +264,35 @@ static SDL_Color addColors(const SDL_Color & base, const SDL_Color & over)
|
||||
);
|
||||
}
|
||||
|
||||
std::array<SDL_Color, 8> CCreatureAnimation::genSpecialPalette()
|
||||
void CCreatureAnimation::genBorderPalette(IImage::BorderPallete & target)
|
||||
{
|
||||
std::array<SDL_Color, 8> ret;
|
||||
|
||||
ret[0] = genShadow(0);
|
||||
ret[1] = genShadow(64);
|
||||
ret[2] = genShadow(128);
|
||||
ret[3] = genShadow(128);
|
||||
ret[4] = genShadow(128);
|
||||
ret[5] = genBorderColor(getBorderStrength(elapsedTime), border);
|
||||
ret[6] = addColors(genShadow(128), genBorderColor(getBorderStrength(elapsedTime), border));
|
||||
ret[7] = addColors(genShadow(64), genBorderColor(getBorderStrength(elapsedTime), border));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
template<int bpp>
|
||||
void CCreatureAnimation::nextFrameT(SDL_Surface * dest, bool rotate)
|
||||
{
|
||||
assert(dataOffsets.count(type) && dataOffsets.at(type).size() > size_t(currentFrame));
|
||||
|
||||
ui32 offset = dataOffsets.at(type).at(floor(currentFrame));
|
||||
|
||||
CMemoryStream stm(pixelData.get(), pixelDataSize);
|
||||
|
||||
CBinaryReader reader(&stm);
|
||||
|
||||
reader.getStream()->seek(offset);
|
||||
|
||||
reader.readUInt32(); // unused, size of pixel data for this frame
|
||||
const ui32 defType2 = reader.readUInt32();
|
||||
const ui32 fullWidth = reader.readUInt32();
|
||||
/*const ui32 fullHeight =*/ reader.readUInt32();
|
||||
const ui32 spriteWidth = reader.readUInt32();
|
||||
const ui32 spriteHeight = reader.readUInt32();
|
||||
const int leftMargin = reader.readInt32();
|
||||
const int topMargin = reader.readInt32();
|
||||
|
||||
const int rightMargin = fullWidth - spriteWidth - leftMargin;
|
||||
//const int bottomMargin = fullHeight - spriteHeight - topMargin;
|
||||
|
||||
const size_t baseOffset = reader.getStream()->tell();
|
||||
|
||||
assert(defType2 == 1);
|
||||
UNUSED(defType2);
|
||||
|
||||
auto specialPalette = genSpecialPalette();
|
||||
|
||||
for (ui32 i=0; i<spriteHeight; i++)
|
||||
{
|
||||
//NOTE: if this loop will be optimized to skip empty lines - recheck this read access
|
||||
ui8 * lineData = pixelData.get() + baseOffset + reader.readUInt32();
|
||||
|
||||
size_t destX = pos.x;
|
||||
if (rotate)
|
||||
destX += rightMargin + spriteWidth - 1;
|
||||
else
|
||||
destX += leftMargin;
|
||||
|
||||
size_t destY = pos.y + topMargin + i;
|
||||
size_t currentOffset = 0;
|
||||
size_t totalRowLength = 0;
|
||||
|
||||
while (totalRowLength < spriteWidth)
|
||||
{
|
||||
ui8 type = lineData[currentOffset++];
|
||||
ui32 length = lineData[currentOffset++] + 1;
|
||||
|
||||
if (type==0xFF)//Raw data
|
||||
{
|
||||
for (size_t j=0; j<length; j++)
|
||||
putPixelAt<bpp>(dest, destX + (rotate?(-j):(j)), destY, lineData[currentOffset + j], specialPalette);
|
||||
|
||||
currentOffset += length;
|
||||
}
|
||||
else// RLE
|
||||
{
|
||||
if (type != 0) // transparency row, handle it here for speed
|
||||
{
|
||||
for (size_t j=0; j<length; j++)
|
||||
putPixelAt<bpp>(dest, destX + (rotate?(-j):(j)), destY, type, specialPalette);
|
||||
}
|
||||
}
|
||||
|
||||
destX += rotate ? (-length) : (length);
|
||||
totalRowLength += length;
|
||||
}
|
||||
}
|
||||
target[0] = genBorderColor(getBorderStrength(elapsedTime), border);
|
||||
target[1] = addColors(genShadow(128), genBorderColor(getBorderStrength(elapsedTime), border));
|
||||
target[2] = addColors(genShadow(64), genBorderColor(getBorderStrength(elapsedTime), border));
|
||||
}
|
||||
|
||||
void CCreatureAnimation::nextFrame(SDL_Surface *dest, bool attacker)
|
||||
{
|
||||
// Note: please notice that attacker value is inversed when passed further.
|
||||
// This is intended behavior because "attacker" actually does not needs rotation
|
||||
switch(dest->format->BytesPerPixel)
|
||||
{
|
||||
case 2: return nextFrameT<2>(dest, !attacker);
|
||||
case 3: return nextFrameT<3>(dest, !attacker);
|
||||
case 4: return nextFrameT<4>(dest, !attacker);
|
||||
default:
|
||||
logGlobal->error("%d bpp is not supported!", (int)dest->format->BitsPerPixel);
|
||||
}
|
||||
size_t frame = floor(currentFrame);
|
||||
|
||||
IImage * image = nullptr;
|
||||
|
||||
if(attacker)
|
||||
image = forward->getImage(frame, type);
|
||||
else
|
||||
image = reverse->getImage(frame, type);
|
||||
|
||||
IImage::BorderPallete borderPallete;
|
||||
genBorderPalette(borderPallete);
|
||||
|
||||
image->setBorderPallete(borderPallete);
|
||||
|
||||
image->draw(dest, pos.x, pos.y);
|
||||
}
|
||||
|
||||
int CCreatureAnimation::framesInGroup(CCreatureAnim::EAnimType group) const
|
||||
{
|
||||
if(dataOffsets.count(group) == 0)
|
||||
return 0;
|
||||
|
||||
return dataOffsets.at(group).size();
|
||||
}
|
||||
|
||||
ui8 * CCreatureAnimation::getPixelAddr(SDL_Surface * dest, int X, int Y) const
|
||||
{
|
||||
return (ui8*)dest->pixels + X * dest->format->BytesPerPixel + Y * dest->pitch;
|
||||
}
|
||||
|
||||
template<int bpp>
|
||||
inline void CCreatureAnimation::putPixelAt(SDL_Surface * dest, int X, int Y, size_t index, const std::array<SDL_Color, 8> & special) const
|
||||
{
|
||||
if ( X < pos.x + pos.w && Y < pos.y + pos.h && X >= 0 && Y >= 0)
|
||||
putPixel<bpp>(getPixelAddr(dest, X, Y), palette[index], index, special);
|
||||
}
|
||||
|
||||
template<int bpp>
|
||||
inline void CCreatureAnimation::putPixel(ui8 * dest, const SDL_Color & color, size_t index, const std::array<SDL_Color, 8> & special) const
|
||||
{
|
||||
if (index < 8)
|
||||
{
|
||||
const SDL_Color & pal = special[index];
|
||||
ColorPutter<bpp, 0>::PutColor(dest, pal.r, pal.g, pal.b, pal.a);
|
||||
}
|
||||
else
|
||||
{
|
||||
ColorPutter<bpp, 0>::PutColor(dest, color.r, color.g, color.b);
|
||||
}
|
||||
return forward->size(group);
|
||||
}
|
||||
|
||||
bool CCreatureAnimation::isDead() const
|
||||
@ -456,7 +328,8 @@ void CCreatureAnimation::pause()
|
||||
|
||||
void CCreatureAnimation::play()
|
||||
{
|
||||
//logAnim->trace("Play %s group %d at %d:%d", name, static_cast<int>(getType()), pos.x, pos.y);
|
||||
speed = 0;
|
||||
if (speedController(this, type) != 0)
|
||||
if(speedController(this, type) != 0)
|
||||
speed = 1 / speedController(this, type);
|
||||
}
|
||||
|
@ -10,8 +10,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "../../lib/FunctionList.h"
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../widgets/Images.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
|
||||
class CIntObject;
|
||||
class CCreatureAnimation;
|
||||
@ -52,21 +52,12 @@ public:
|
||||
typedef std::function<float(CCreatureAnimation *, size_t)> TSpeedController;
|
||||
|
||||
private:
|
||||
std::string defName;
|
||||
std::string name;
|
||||
std::shared_ptr<CAnimation> forward;
|
||||
std::shared_ptr<CAnimation> reverse;
|
||||
|
||||
int fullWidth, fullHeight;
|
||||
|
||||
// palette, as read from def file
|
||||
std::array<SDL_Color, 256> palette;
|
||||
|
||||
//key = id of group (note that some groups may be missing)
|
||||
//value = offset of pixel data for each frame, vector size = number of frames in group
|
||||
std::map<int, std::vector<unsigned int>> dataOffsets;
|
||||
|
||||
//animation raw data
|
||||
//TODO: use vector instead?
|
||||
std::unique_ptr<ui8[]> pixelData;
|
||||
size_t pixelDataSize;
|
||||
int fullWidth;
|
||||
int fullHeight;
|
||||
|
||||
// speed of animation, measure in frames per second
|
||||
float speed;
|
||||
@ -85,21 +76,10 @@ private:
|
||||
|
||||
bool once; // animation will be played once and the reset to idling
|
||||
|
||||
ui8 * getPixelAddr(SDL_Surface * dest, int ftcpX, int ftcpY) const;
|
||||
|
||||
template<int bpp>
|
||||
void putPixelAt(SDL_Surface * dest, int X, int Y, size_t index, const std::array<SDL_Color, 8> & special) const;
|
||||
|
||||
template<int bpp>
|
||||
void putPixel( ui8 * dest, const SDL_Color & color, size_t index, const std::array<SDL_Color, 8> & special) const;
|
||||
|
||||
template<int bpp>
|
||||
void nextFrameT(SDL_Surface * dest, bool rotate);
|
||||
|
||||
void endAnimation();
|
||||
|
||||
/// creates 8 special colors for current frame
|
||||
std::array<SDL_Color, 8> genSpecialPalette();
|
||||
|
||||
void genBorderPalette(IImage::BorderPallete & target);
|
||||
public:
|
||||
|
||||
// function(s) that will be called when animation ends, after reset to 1st frame
|
||||
@ -113,12 +93,12 @@ public:
|
||||
/// name - path to .def file, relative to SPRITES/ directory
|
||||
/// controller - function that will return for how long *each* frame
|
||||
/// in specified group of animation should be played, measured in seconds
|
||||
CCreatureAnimation(std::string name, TSpeedController speedController);
|
||||
CCreatureAnimation(const std::string & name_, TSpeedController speedController);
|
||||
|
||||
void setType(CCreatureAnim::EAnimType type); //sets type of animation and cleares framecount
|
||||
CCreatureAnim::EAnimType getType() const; //returns type of animation
|
||||
|
||||
void nextFrame(SDL_Surface * dest, bool rotate);
|
||||
void nextFrame(SDL_Surface * dest, bool attacker);
|
||||
|
||||
// should be called every frame, return true when animation was reset to beginning
|
||||
bool incrementFrame(float timePassed);
|
||||
|
@ -29,7 +29,7 @@ typedef std::map <size_t, std::vector <JsonNode> > source_map;
|
||||
typedef std::map<size_t, IImage* > image_map;
|
||||
typedef std::map<size_t, image_map > group_map;
|
||||
|
||||
/// Class for def loading, methods are based on CDefHandler
|
||||
/// Class for def loading
|
||||
/// After loading will store general info (palette and frame offsets) and pointer to file itself
|
||||
class CDefFile
|
||||
{
|
||||
@ -100,6 +100,8 @@ public:
|
||||
|
||||
void shiftPalette(int from, int howMany) override;
|
||||
|
||||
void setBorderPallete(const BorderPallete & borderPallete) override;
|
||||
|
||||
friend class SDLImageLoader;
|
||||
};
|
||||
|
||||
@ -157,6 +159,7 @@ public:
|
||||
void verticalFlip() override;
|
||||
|
||||
void shiftPalette(int from, int howMany) override;
|
||||
void setBorderPallete(const BorderPallete & borderPallete) override;
|
||||
|
||||
friend class CompImageLoader;
|
||||
};
|
||||
@ -247,16 +250,50 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
enum class DefType : uint32_t
|
||||
{
|
||||
SPELL = 0x40,
|
||||
SPRITE = 0x41,
|
||||
CREATURE = 0x42,
|
||||
MAP = 0x43,
|
||||
MAP_HERO = 0x44,
|
||||
TERRAIN = 0x45,
|
||||
CURSOR = 0x46,
|
||||
INTERFACE = 0x47,
|
||||
SPRITE_FRAME = 0x48,
|
||||
BATTLE_HERO = 0x49
|
||||
};
|
||||
|
||||
static CFileCache animationCache;
|
||||
|
||||
/*************************************************************************
|
||||
* DefFile, class used for def loading *
|
||||
*************************************************************************/
|
||||
|
||||
bool operator== (const SDL_Color & lhs, const SDL_Color & rhs)
|
||||
{
|
||||
return (lhs.a == rhs.a) && (lhs.b == rhs.b) &&(lhs.g == rhs.g) &&(lhs.r == rhs.r);
|
||||
}
|
||||
|
||||
CDefFile::CDefFile(std::string Name):
|
||||
data(nullptr),
|
||||
palette(nullptr)
|
||||
{
|
||||
|
||||
#if 0
|
||||
static SDL_Color H3_ORIG_PALETTE[8] =
|
||||
{
|
||||
{ 0, 255, 255, SDL_ALPHA_OPAQUE},
|
||||
{255, 150, 255, SDL_ALPHA_OPAQUE},
|
||||
{255, 100, 255, SDL_ALPHA_OPAQUE},
|
||||
{255, 50, 255, SDL_ALPHA_OPAQUE},
|
||||
{255, 0, 255, SDL_ALPHA_OPAQUE},
|
||||
{255, 255, 0, SDL_ALPHA_OPAQUE},
|
||||
{180, 0, 255, SDL_ALPHA_OPAQUE},
|
||||
{ 0, 255, 0, SDL_ALPHA_OPAQUE}
|
||||
};
|
||||
#endif // 0
|
||||
|
||||
//First 8 colors in def palette used for transparency
|
||||
static SDL_Color H3Palette[8] =
|
||||
{
|
||||
@ -289,10 +326,59 @@ CDefFile::CDefFile(std::string Name):
|
||||
palette[i].b = data[it++];
|
||||
palette[i].a = SDL_ALPHA_OPAQUE;
|
||||
}
|
||||
if (type == 71 || type == 64)//Buttons/buildings don't have shadows\semi-transparency
|
||||
memset(palette.get(), 0, sizeof(SDL_Color)*2);
|
||||
else
|
||||
memcpy(palette.get(), H3Palette, sizeof(SDL_Color)*8);//initialize shadow\selection colors
|
||||
|
||||
switch(static_cast<DefType>(type))
|
||||
{
|
||||
case DefType::SPELL:
|
||||
palette[0] = H3Palette[0];
|
||||
break;
|
||||
case DefType::SPRITE:
|
||||
case DefType::SPRITE_FRAME:
|
||||
for(ui32 i= 0; i<8; i++)
|
||||
palette[i] = H3Palette[i];
|
||||
break;
|
||||
case DefType::CREATURE:
|
||||
palette[0] = H3Palette[0];
|
||||
palette[1] = H3Palette[1];
|
||||
palette[4] = H3Palette[4];
|
||||
palette[5] = H3Palette[5];
|
||||
palette[6] = H3Palette[6];
|
||||
palette[7] = H3Palette[7];
|
||||
break;
|
||||
case DefType::MAP:
|
||||
case DefType::MAP_HERO:
|
||||
palette[0] = H3Palette[0];
|
||||
palette[1] = H3Palette[1];
|
||||
palette[4] = H3Palette[4];
|
||||
//5 = owner flag, handled separately
|
||||
break;
|
||||
case DefType::TERRAIN:
|
||||
palette[0] = H3Palette[0];
|
||||
palette[1] = H3Palette[1];
|
||||
palette[2] = H3Palette[2];
|
||||
palette[3] = H3Palette[3];
|
||||
palette[4] = H3Palette[4];
|
||||
break;
|
||||
case DefType::CURSOR:
|
||||
palette[0] = H3Palette[0];
|
||||
break;
|
||||
case DefType::INTERFACE:
|
||||
palette[0] = H3Palette[0];
|
||||
palette[1] = H3Palette[1];
|
||||
palette[4] = H3Palette[4];
|
||||
//player colors handled separately
|
||||
//TODO: disallow colorizing other def types
|
||||
break;
|
||||
case DefType::BATTLE_HERO:
|
||||
palette[0] = H3Palette[0];
|
||||
palette[1] = H3Palette[1];
|
||||
palette[4] = H3Palette[4];
|
||||
break;
|
||||
default:
|
||||
logAnim->error("Unknown def type %d in %s", type, Name);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
for (ui32 i=0; i<totalBlocks; i++)
|
||||
{
|
||||
@ -902,6 +988,15 @@ void SDLImage::shiftPalette(int from, int howMany)
|
||||
}
|
||||
}
|
||||
|
||||
void SDLImage::setBorderPallete(const IImage::BorderPallete & borderPallete)
|
||||
{
|
||||
if(surf->format->palette)
|
||||
{
|
||||
SDL_SetColors(surf, const_cast<SDL_Color *>(borderPallete.data()), 5, 3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SDLImage::~SDLImage()
|
||||
{
|
||||
SDL_FreeSurface(surf);
|
||||
@ -1155,22 +1250,27 @@ CompImage::~CompImage()
|
||||
|
||||
void CompImage::horizontalFlip()
|
||||
{
|
||||
logAnim->error("CompImage::horizontalFlip is not implemented");
|
||||
logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
|
||||
}
|
||||
|
||||
void CompImage::verticalFlip()
|
||||
{
|
||||
logAnim->error("CompImage::verticalFlip is not implemented");
|
||||
logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
|
||||
}
|
||||
|
||||
void CompImage::shiftPalette(int from, int howMany)
|
||||
{
|
||||
logAnim->error("CompImage::shiftPalette is not implemented");
|
||||
logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
|
||||
}
|
||||
|
||||
void CompImage::exportBitmap(const boost::filesystem::path& path) const
|
||||
void CompImage::setBorderPallete(const IImage::BorderPallete & borderPallete)
|
||||
{
|
||||
logAnim->error("CompImage::exportBitmap is not implemented");
|
||||
logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
|
||||
}
|
||||
|
||||
void CompImage::exportBitmap(const boost::filesystem::path & path) const
|
||||
{
|
||||
logAnim->error("%s is not implemented", BOOST_CURRENT_FUNCTION);
|
||||
}
|
||||
|
||||
|
||||
@ -1388,6 +1488,9 @@ CAnimation::CAnimation(std::string Name, bool Compressed):
|
||||
CDefFile * file = getFile();
|
||||
init(file);
|
||||
delete file;
|
||||
|
||||
if(source.empty())
|
||||
logAnim->error("Animation %s failed to load", Name);
|
||||
}
|
||||
|
||||
CAnimation::CAnimation():
|
||||
@ -1519,6 +1622,38 @@ size_t CAnimation::size(size_t group) const
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CAnimation::horizontalFlip()
|
||||
{
|
||||
for(auto & group : images)
|
||||
for(auto & image : group.second)
|
||||
image.second->horizontalFlip();
|
||||
}
|
||||
|
||||
void CAnimation::verticalFlip()
|
||||
{
|
||||
for(auto & group : images)
|
||||
for(auto & image : group.second)
|
||||
image.second->verticalFlip();
|
||||
}
|
||||
|
||||
void CAnimation::playerColored(PlayerColor player)
|
||||
{
|
||||
for(auto & group : images)
|
||||
for(auto & image : group.second)
|
||||
image.second->playerColored(player);
|
||||
}
|
||||
|
||||
void CAnimation::createFlippedGroup(const size_t sourceGroup, const size_t targetGroup)
|
||||
{
|
||||
for(size_t frame = 0; frame < size(sourceGroup); ++frame)
|
||||
{
|
||||
duplicateImage(sourceGroup, frame, targetGroup);
|
||||
|
||||
IImage * image = getImage(frame, targetGroup);
|
||||
image->verticalFlip();
|
||||
}
|
||||
}
|
||||
|
||||
float CFadeAnimation::initialCounter() const
|
||||
{
|
||||
if (fadingMode == EMode::OUT)
|
||||
|
@ -24,6 +24,7 @@ class IImage
|
||||
{
|
||||
int refCount;
|
||||
public:
|
||||
using BorderPallete = std::array<SDL_Color, 3>;
|
||||
|
||||
//draws image on surface "where" at position
|
||||
virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, Rect * src = nullptr, ui8 alpha = 255) const=0;
|
||||
@ -49,6 +50,9 @@ public:
|
||||
//only indexed bitmaps, 16 colors maximum
|
||||
virtual void shiftPalette(int from, int howMany) = 0;
|
||||
|
||||
//only indexed bitmaps, colors 5,6,7 must be special
|
||||
virtual void setBorderPallete(const BorderPallete & borderPallete) = 0;
|
||||
|
||||
virtual void horizontalFlip() = 0;
|
||||
virtual void verticalFlip() = 0;
|
||||
|
||||
@ -127,6 +131,12 @@ public:
|
||||
|
||||
//total count of frames in group (including not loaded)
|
||||
size_t size(size_t group=0) const;
|
||||
|
||||
void horizontalFlip();
|
||||
void verticalFlip();
|
||||
void playerColored(PlayerColor player);
|
||||
|
||||
void createFlippedGroup(const size_t sourceGroup, const size_t targetGroup);
|
||||
};
|
||||
|
||||
const float DEFAULT_DELTA = 0.05f;
|
||||
|
@ -23,11 +23,20 @@ void CCursorHandler::initCursor()
|
||||
xpos = ypos = 0;
|
||||
type = ECursor::DEFAULT;
|
||||
dndObject = nullptr;
|
||||
currentCursor = nullptr;
|
||||
|
||||
cursors =
|
||||
{
|
||||
make_unique<CAnimImage>("CRADVNTR", 0),
|
||||
make_unique<CAnimImage>("CRCOMBAT", 0),
|
||||
make_unique<CAnimImage>("CRDEFLT", 0),
|
||||
make_unique<CAnimImage>("CRSPELL", 0)
|
||||
};
|
||||
|
||||
currentCursor = cursors.at(int(ECursor::DEFAULT)).get();
|
||||
|
||||
help = CSDL_Ext::newSurface(40,40);
|
||||
//No blending. Ensure, that we are copying pixels during "screen restore draw"
|
||||
SDL_SetSurfaceBlendMode(help,SDL_BLENDMODE_NONE);
|
||||
SDL_SetSurfaceBlendMode(help,SDL_BLENDMODE_NONE);
|
||||
SDL_ShowCursor(SDL_DISABLE);
|
||||
|
||||
changeGraphic(ECursor::ADVENTURE, 0);
|
||||
@ -35,32 +44,23 @@ void CCursorHandler::initCursor()
|
||||
|
||||
void CCursorHandler::changeGraphic(ECursor::ECursorTypes type, int index)
|
||||
{
|
||||
std::string cursorDefs[4] = { "CRADVNTR.DEF", "CRCOMBAT.DEF", "CRDEFLT.DEF", "CRSPELL.DEF" };
|
||||
|
||||
if (type != this->type)
|
||||
if(type != this->type)
|
||||
{
|
||||
BLOCK_CAPTURING; // not used here
|
||||
|
||||
this->type = type;
|
||||
this->frame = index;
|
||||
|
||||
delete currentCursor;
|
||||
currentCursor = new CAnimImage(cursorDefs[int(type)], index);
|
||||
currentCursor = cursors.at(int(type)).get();
|
||||
currentCursor->setFrame(index);
|
||||
}
|
||||
|
||||
if (frame != index)
|
||||
else if(index != this->frame)
|
||||
{
|
||||
frame = index;
|
||||
this->frame = index;
|
||||
currentCursor->setFrame(index);
|
||||
}
|
||||
}
|
||||
|
||||
void CCursorHandler::dragAndDropCursor(CAnimImage * object)
|
||||
void CCursorHandler::dragAndDropCursor(std::unique_ptr<CAnimImage> object)
|
||||
{
|
||||
if (dndObject)
|
||||
delete dndObject;
|
||||
|
||||
dndObject = object;
|
||||
dndObject = std::move(object);
|
||||
}
|
||||
|
||||
void CCursorHandler::cursorMove(const int & x, const int & y)
|
||||
@ -101,13 +101,6 @@ void CCursorHandler::drawRestored()
|
||||
|
||||
SDL_Rect temp_rect = genRect(40, 40, x, y);
|
||||
SDL_BlitSurface(help, nullptr, screen, &temp_rect);
|
||||
//blitAt(help,x,y);
|
||||
}
|
||||
|
||||
void CCursorHandler::draw(SDL_Surface *to)
|
||||
{
|
||||
currentCursor->moveTo(Point(xpos, ypos));
|
||||
currentCursor->showAll(screen);
|
||||
}
|
||||
|
||||
void CCursorHandler::shiftPos( int &x, int &y )
|
||||
@ -233,12 +226,10 @@ void CCursorHandler::render()
|
||||
drawRestored();
|
||||
}
|
||||
|
||||
CCursorHandler::CCursorHandler() = default;
|
||||
|
||||
CCursorHandler::~CCursorHandler()
|
||||
{
|
||||
if(help)
|
||||
SDL_FreeSurface(help);
|
||||
|
||||
delete currentCursor;
|
||||
delete dndObject;
|
||||
}
|
||||
|
@ -17,27 +17,28 @@ namespace ECursor
|
||||
enum ECursorTypes { ADVENTURE, COMBAT, DEFAULT, SPELLBOOK };
|
||||
|
||||
enum EBattleCursors { COMBAT_BLOCKED, COMBAT_MOVE, COMBAT_FLY, COMBAT_SHOOT,
|
||||
COMBAT_HERO, COMBAT_QUERY, COMBAT_POINTER,
|
||||
COMBAT_HERO, COMBAT_QUERY, COMBAT_POINTER,
|
||||
//various attack frames
|
||||
COMBAT_SHOOT_PENALTY = 15, COMBAT_SHOOT_CATAPULT, COMBAT_HEAL,
|
||||
COMBAT_SACRIFICE, COMBAT_TELEPORT};
|
||||
}
|
||||
|
||||
/// handles mouse cursor
|
||||
class CCursorHandler
|
||||
class CCursorHandler final
|
||||
{
|
||||
SDL_Surface * help;
|
||||
CAnimImage * currentCursor;
|
||||
CAnimImage * dndObject; //if set, overrides currentCursor
|
||||
|
||||
std::unique_ptr<CAnimImage> dndObject; //if set, overrides currentCursor
|
||||
|
||||
std::array<std::unique_ptr<CAnimImage>, 4> cursors;
|
||||
|
||||
bool showing;
|
||||
|
||||
/// Draw cursor preserving original image below cursor
|
||||
void drawWithScreenRestore();
|
||||
/// Restore original image below cursor
|
||||
void drawRestored();
|
||||
/// Simple draw cursor
|
||||
void draw(SDL_Surface *to);
|
||||
|
||||
public:
|
||||
/// position of cursor
|
||||
int xpos, ypos;
|
||||
@ -58,8 +59,8 @@ public:
|
||||
* @param image Image to replace cursor with or nullptr to use the normal
|
||||
* cursor. CursorHandler takes ownership of object
|
||||
*/
|
||||
void dragAndDropCursor (CAnimImage * image);
|
||||
|
||||
void dragAndDropCursor (std::unique_ptr<CAnimImage> image);
|
||||
|
||||
void render();
|
||||
|
||||
void shiftPos( int &x, int &y );
|
||||
@ -71,5 +72,6 @@ public:
|
||||
/// Move cursor to screen center
|
||||
void centerCursor();
|
||||
|
||||
CCursorHandler();
|
||||
~CCursorHandler();
|
||||
};
|
||||
|
@ -459,8 +459,7 @@ void CGuiHandler::renderFrame()
|
||||
// draw the mouse cursor and update the screen
|
||||
CCS->curh->render();
|
||||
|
||||
if(0 != SDL_RenderCopy(mainRenderer, screenTexture, nullptr, nullptr))
|
||||
logGlobal->error("%s SDL_RenderCopy %s", __FUNCTION__, SDL_GetError());
|
||||
SDL_RenderCopy(mainRenderer, screenTexture, nullptr, nullptr);
|
||||
|
||||
SDL_RenderPresent(mainRenderer);
|
||||
}
|
||||
|
@ -290,7 +290,7 @@ void CHeroArtPlace::select ()
|
||||
}
|
||||
}
|
||||
|
||||
CCS->curh->dragAndDropCursor(new CAnimImage("artifact", ourArt->artType->iconIndex));
|
||||
CCS->curh->dragAndDropCursor(make_unique<CAnimImage>("artifact", ourArt->artType->iconIndex));
|
||||
ourOwner->commonInfo->src.setTo(this, false);
|
||||
ourOwner->markPossibleSlots(ourArt);
|
||||
|
||||
@ -766,7 +766,7 @@ void CArtifactsOfHero::artifactMoved(const ArtifactLocation &src, const Artifact
|
||||
commonInfo->src.art = dst.getArt();
|
||||
commonInfo->src.slotID = dst.slot;
|
||||
assert(commonInfo->src.AOH);
|
||||
CCS->curh->dragAndDropCursor(new CAnimImage("artifact", dst.getArt()->artType->iconIndex));
|
||||
CCS->curh->dragAndDropCursor(make_unique<CAnimImage>("artifact", dst.getArt()->artType->iconIndex));
|
||||
markPossibleSlots(dst.getArt());
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "../../lib/CArtHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/CSkillHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/NetPacksBase.h"
|
||||
@ -148,7 +149,7 @@ std::string CComponent::getDescription()
|
||||
{
|
||||
case primskill: return (subtype < 4)? CGI->generaltexth->arraytxt[2+subtype] //Primary skill
|
||||
: CGI->generaltexth->allTexts[149]; //mana
|
||||
case secskill: return CGI->generaltexth->skillInfoTexts[subtype][val-1];
|
||||
case secskill: return CGI->skillh->skillInfo(subtype, val);
|
||||
case resource: return CGI->generaltexth->allTexts[242];
|
||||
case creature: return "";
|
||||
case artifact:
|
||||
@ -192,7 +193,7 @@ std::string CComponent::getSubtitleInternal()
|
||||
switch(compType)
|
||||
{
|
||||
case primskill: return boost::str(boost::format("%+d %s") % val % (subtype < 4 ? CGI->generaltexth->primarySkillNames[subtype] : CGI->generaltexth->allTexts[387]));
|
||||
case secskill: return CGI->generaltexth->levels[val-1] + "\n" + CGI->generaltexth->skillName[subtype];
|
||||
case secskill: return CGI->generaltexth->levels[val-1] + "\n" + CGI->skillh->skillName(subtype);
|
||||
case resource: return boost::lexical_cast<std::string>(val);
|
||||
case creature: return (val? boost::lexical_cast<std::string>(val) + " " : "") + CGI->creh->creatures[subtype]->*(val != 1 ? &CCreature::namePl : &CCreature::nameSing);
|
||||
case artifact: return CGI->arth->artifacts[subtype]->Name();
|
||||
|
@ -24,6 +24,7 @@
|
||||
|
||||
#include "../gui/SDL_Extensions.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../gui/CAnimation.h"
|
||||
#include "../widgets/MiscWidgets.h"
|
||||
#include "../widgets/CComponent.h"
|
||||
|
||||
@ -33,6 +34,7 @@
|
||||
#include "../lib/CConfigHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CSkillHandler.h"
|
||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../lib/NetPacksBase.h"
|
||||
#include "../mapHandler.h"
|
||||
@ -106,7 +108,7 @@ CHeroWindow::CHeroWindow(const CGHeroInstance *hero):
|
||||
quitButton = new CButton(Point(609, 516), "hsbtns.def", CButton::tooltip(heroscrn[17]), [&](){ close(); }, SDLK_RETURN);
|
||||
quitButton->assignedKeys.insert(SDLK_ESCAPE);
|
||||
dismissButton = new CButton(Point(454, 429), "hsbtns2.def", CButton::tooltip(heroscrn[28]), [&](){ dismissCurrent(); }, SDLK_d);
|
||||
questlogButton = new CButton(Point(314, 429), "hsbtns4.def", CButton::tooltip(heroscrn[0]), [&](){ questlog(); }, SDLK_q);
|
||||
questlogButton = new CButton(Point(314, 429), "hsbtns4.def", CButton::tooltip(heroscrn[0]), [=](){ LOCPLINT->showQuestLog(); }, SDLK_q);
|
||||
|
||||
formations = new CToggleGroup(0);
|
||||
formations->addToggle(0, new CToggleButton(Point(481, 483), "hsbtns6.def", std::make_pair(heroscrn[23], heroscrn[29]), 0, SDLK_t));
|
||||
@ -143,11 +145,12 @@ CHeroWindow::CHeroWindow(const CGHeroInstance *hero):
|
||||
luck = new MoraleLuckBox(false, Rect(233,179,53,45));
|
||||
spellPointsArea = new LRClickableAreaWText(Rect(162,228, 136, 42), CGI->generaltexth->heroscrn[22]);
|
||||
|
||||
auto secSkills = std::make_shared<CAnimation>("SECSKILL");
|
||||
for(int i = 0; i < std::min<size_t>(hero->secSkills.size(), 8u); ++i)
|
||||
{
|
||||
Rect r = Rect(i%2 == 0 ? 18 : 162, 276 + 48 * (i/2), 136, 42);
|
||||
secSkillAreas.push_back(new LRClickableAreaWTextComp(r, CComponent::secskill));
|
||||
secSkillImages.push_back(new CAnimImage("SECSKILL", 0, 0, r.x, r.y));
|
||||
secSkillImages.push_back(new CAnimImage(secSkills, 0, 0, r.x, r.y));
|
||||
}
|
||||
|
||||
//dismiss / quest log
|
||||
@ -157,12 +160,14 @@ CHeroWindow::CHeroWindow(const CGHeroInstance *hero):
|
||||
//////////////////////////////////////////////////////////////////////////???????????????
|
||||
|
||||
//primary skills & exp and mana
|
||||
new CAnimImage("PSKIL42", 0, 0, 32, 111, false);
|
||||
new CAnimImage("PSKIL42", 1, 0, 102, 111, false);
|
||||
new CAnimImage("PSKIL42", 2, 0, 172, 111, false);
|
||||
new CAnimImage("PSKIL42", 3, 0, 162, 230, false);
|
||||
new CAnimImage("PSKIL42", 4, 0, 20, 230, false);
|
||||
new CAnimImage("PSKIL42", 5, 0, 242, 111, false);
|
||||
auto primSkills = std::make_shared<CAnimation>("PSKIL42");
|
||||
primSkills->preload();
|
||||
new CAnimImage(primSkills, 0, 0, 32, 111);
|
||||
new CAnimImage(primSkills, 1, 0, 102, 111);
|
||||
new CAnimImage(primSkills, 2, 0, 172, 111);
|
||||
new CAnimImage(primSkills, 3, 0, 162, 230);
|
||||
new CAnimImage(primSkills, 4, 0, 20, 230);
|
||||
new CAnimImage(primSkills, 5, 0, 242, 111);
|
||||
|
||||
// various texts
|
||||
new CLabel( 52, 99, FONT_SMALL, CENTER, Colors::YELLOW, CGI->generaltexth->jktexts[1]);
|
||||
@ -240,8 +245,8 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded)
|
||||
level = curHero->getSecSkillLevel(SecondarySkill(curHero->secSkills[g].first));
|
||||
secSkillAreas[g]->type = skill;
|
||||
secSkillAreas[g]->bonusValue = level;
|
||||
secSkillAreas[g]->text = CGI->generaltexth->skillInfoTexts[skill][level-1];
|
||||
secSkillAreas[g]->hoverText = boost::str(boost::format(heroscrn[21]) % CGI->generaltexth->levels[level-1] % CGI->generaltexth->skillName[skill]);
|
||||
secSkillAreas[g]->text = CGI->skillh->skillInfo(skill, level);
|
||||
secSkillAreas[g]->hoverText = boost::str(boost::format(heroscrn[21]) % CGI->generaltexth->levels[level-1] % CGI->skillh->skillName(skill));
|
||||
secSkillImages[g]->setFrame(skill*3 + level + 2);
|
||||
}
|
||||
|
||||
@ -305,11 +310,6 @@ void CHeroWindow::dismissCurrent()
|
||||
LOCPLINT->showYesNoDialog(CGI->generaltexth->allTexts[22], ony, 0, false);
|
||||
}
|
||||
|
||||
void CHeroWindow::questlog()
|
||||
{
|
||||
LOCPLINT->showQuestLog();
|
||||
}
|
||||
|
||||
void CHeroWindow::commanderWindow()
|
||||
{
|
||||
//TODO: allow equipping commander artifacts by drag / drop
|
||||
@ -372,7 +372,7 @@ void CHeroWindow::showAll(SDL_Surface * to)
|
||||
for(size_t v=0; v<std::min(secSkillAreas.size(), curHero->secSkills.size()); ++v)
|
||||
{
|
||||
printAtLoc(CGI->generaltexth->levels[curHero->secSkills[v].second-1], (v%2) ? 212 : 68, 280 + 48 * (v/2), FONT_SMALL, Colors::WHITE, to);
|
||||
printAtLoc(CGI->generaltexth->skillName[curHero->secSkills[v].first], (v%2) ? 212 : 68, 300 + 48 * (v/2), FONT_SMALL, Colors::WHITE, to);
|
||||
printAtLoc(CGI->skillh->skillName(curHero->secSkills[v].first), (v%2) ? 212 : 68, 300 + 48 * (v/2), FONT_SMALL, Colors::WHITE, to);
|
||||
}
|
||||
|
||||
//printing special ability
|
||||
|
@ -86,7 +86,6 @@ public:
|
||||
void showAll(SDL_Surface * to) override;
|
||||
|
||||
void dismissCurrent(); //dissmissed currently displayed hero (curHero)
|
||||
void questlog(); //show quest log in hero window
|
||||
void commanderWindow();
|
||||
void switchHero(); //changes displayed hero
|
||||
virtual void updateGarrisons() override; //updates the morale widget and calls the parent
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/CModHandler.h"
|
||||
#include "../../lib/CSkillHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
@ -170,7 +171,7 @@ std::string InfoBoxAbstractHeroData::getNameText()
|
||||
return CGI->heroh->heroes[getSubID()]->specName;
|
||||
case HERO_SECONDARY_SKILL:
|
||||
if (getValue())
|
||||
return CGI->generaltexth->skillName[getSubID()];
|
||||
return CGI->skillh->skillName(getSubID());
|
||||
else
|
||||
return "";
|
||||
default:
|
||||
@ -281,7 +282,7 @@ bool InfoBoxAbstractHeroData::prepareMessage(std::string &text, CComponent **com
|
||||
if (!value)
|
||||
return false;
|
||||
|
||||
text = CGI->generaltexth->skillInfoTexts[subID][value-1];
|
||||
text = CGI->skillh->skillInfo(subID, value);
|
||||
*comp = new CComponent(CComponent::secskill, subID, value);
|
||||
return true;
|
||||
}
|
||||
@ -356,7 +357,7 @@ std::string InfoBoxHeroData::getHoverText()
|
||||
if (hero->secSkills.size() > index)
|
||||
{
|
||||
std::string level = CGI->generaltexth->levels[hero->secSkills[index].second-1];
|
||||
std::string skill = CGI->generaltexth->skillName[hero->secSkills[index].first];
|
||||
std::string skill = CGI->skillh->skillName(hero->secSkills[index].first);
|
||||
return boost::str(boost::format(CGI->generaltexth->heroscrn[21]) % level % skill);
|
||||
}
|
||||
else
|
||||
|
@ -187,7 +187,7 @@ void CTradeWindow::CTradeableItem::clickLeft(tribool down, bool previousState)
|
||||
aw->arts->markPossibleSlots(art);
|
||||
|
||||
//aw->arts->commonInfo->dst.AOH = aw->arts;
|
||||
CCS->curh->dragAndDropCursor(new CAnimImage("artifact", art->artType->iconIndex));
|
||||
CCS->curh->dragAndDropCursor(make_unique<CAnimImage>("artifact", art->artType->iconIndex));
|
||||
|
||||
aw->arts->artifactsOnAltar.erase(art);
|
||||
setID(-1);
|
||||
|
@ -49,6 +49,7 @@
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/CondSh.h"
|
||||
#include "../lib/CSkillHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CStopWatch.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
@ -944,11 +945,11 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
|
||||
|
||||
secSkillAreas[b][g]->type = skill;
|
||||
secSkillAreas[b][g]->bonusValue = level;
|
||||
secSkillAreas[b][g]->text = CGI->generaltexth->skillInfoTexts[skill][level-1];
|
||||
secSkillAreas[b][g]->text = CGI->skillh->skillInfo(skill, level);
|
||||
|
||||
secSkillAreas[b][g]->hoverText = CGI->generaltexth->heroscrn[21];
|
||||
boost::algorithm::replace_first(secSkillAreas[b][g]->hoverText, "%s", CGI->generaltexth->levels[level - 1]);
|
||||
boost::algorithm::replace_first(secSkillAreas[b][g]->hoverText, "%s", CGI->generaltexth->skillName[skill]);
|
||||
boost::algorithm::replace_first(secSkillAreas[b][g]->hoverText, "%s", CGI->skillh->skillName(skill));
|
||||
}
|
||||
|
||||
portrait[b] = new CHeroArea(257 + 228*b, 13, heroInst[b]);
|
||||
@ -1222,7 +1223,7 @@ void CUniversityWindow::CItem::clickRight(tribool down, bool previousState)
|
||||
{
|
||||
if(down)
|
||||
{
|
||||
CRClickPopup::createAndPush(CGI->generaltexth->skillInfoTexts[ID][0],
|
||||
CRClickPopup::createAndPush(CGI->skillh->skillInfo(ID, 1),
|
||||
new CComponent(CComponent::secskill, ID, 1));
|
||||
}
|
||||
}
|
||||
@ -1230,7 +1231,7 @@ void CUniversityWindow::CItem::clickRight(tribool down, bool previousState)
|
||||
void CUniversityWindow::CItem::hover(bool on)
|
||||
{
|
||||
if (on)
|
||||
GH.statusbar->setText(CGI->generaltexth->skillName[ID]);
|
||||
GH.statusbar->setText(CGI->skillh->skillName(ID));
|
||||
else
|
||||
GH.statusbar->clear();
|
||||
}
|
||||
@ -1264,7 +1265,7 @@ void CUniversityWindow::CItem::showAll(SDL_Surface * to)
|
||||
|
||||
blitAtLoc(bar->bg, -28, -22, to);
|
||||
blitAtLoc(bar->bg, -28, 48, to);
|
||||
printAtMiddleLoc (CGI->generaltexth->skillName[ID], 22, -13, FONT_SMALL, Colors::WHITE,to);//Name
|
||||
printAtMiddleLoc (CGI->skillh->skillName(ID), 22, -13, FONT_SMALL, Colors::WHITE,to);//Name
|
||||
printAtMiddleLoc (CGI->generaltexth->levels[0], 22, 57, FONT_SMALL, Colors::WHITE,to);//Level(always basic)
|
||||
|
||||
CAnimImage::showAll(to);
|
||||
@ -1328,12 +1329,12 @@ CUnivConfirmWindow::CUnivConfirmWindow(CUniversityWindow * PARENT, int SKILL, bo
|
||||
|
||||
std::string text = CGI->generaltexth->allTexts[608];
|
||||
boost::replace_first(text, "%s", CGI->generaltexth->levels[0]);
|
||||
boost::replace_first(text, "%s", CGI->generaltexth->skillName[SKILL]);
|
||||
boost::replace_first(text, "%s", CGI->skillh->skillName(SKILL));
|
||||
boost::replace_first(text, "%d", "2000");
|
||||
|
||||
new CTextBox(text, Rect(24, 129, 413, 70), 0, FONT_SMALL, CENTER, Colors::WHITE);//Clerk speech
|
||||
|
||||
new CLabel(230, 37, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth-> skillName[SKILL]);//Skill name
|
||||
new CLabel(230, 37, FONT_SMALL, CENTER, Colors::WHITE, CGI->skillh->skillName(SKILL));//Skill name
|
||||
new CAnimImage("SECSKILL", SKILL*3+3, 0, 211, 51);//skill
|
||||
new CLabel(230, 107, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->levels[1]);//Skill level
|
||||
|
||||
@ -1341,11 +1342,11 @@ CUnivConfirmWindow::CUnivConfirmWindow(CUniversityWindow * PARENT, int SKILL, bo
|
||||
new CLabel(230, 267, FONT_SMALL, CENTER, Colors::WHITE, "2000");//Cost
|
||||
|
||||
std::string hoverText = CGI->generaltexth->allTexts[609];
|
||||
boost::replace_first(hoverText, "%s", CGI->generaltexth->levels[0]+ " " + CGI->generaltexth->skillName[SKILL]);
|
||||
boost::replace_first(hoverText, "%s", CGI->generaltexth->levels[0]+ " " + CGI->skillh->skillName(SKILL));
|
||||
|
||||
text = CGI->generaltexth->zelp[633].second;
|
||||
boost::replace_first(text, "%s", CGI->generaltexth->levels[0]);
|
||||
boost::replace_first(text, "%s", CGI->generaltexth->skillName[SKILL]);
|
||||
boost::replace_first(text, "%s", CGI->skillh->skillName(SKILL));
|
||||
boost::replace_first(text, "%d", "2000");
|
||||
|
||||
confirm= new CButton(Point(148, 299), "IBY6432.DEF", CButton::tooltip(hoverText, text), [=](){makeDeal(SKILL);}, SDLK_RETURN);
|
||||
|
@ -63,14 +63,64 @@ endif(${CMAKE_GENERATOR} MATCHES "Xcode")
|
||||
|
||||
# Can be called to see check cmake variables and environment variables
|
||||
# For "install" debugging just copy it here. There no easy way to include modules from source.
|
||||
function(vcmi_get_cmake_debug_info)
|
||||
function(vcmi_print_all_variables)
|
||||
|
||||
message(STATUS "Debug - Internal variables:")
|
||||
message(STATUS "-- -- Start of all internal variables")
|
||||
get_cmake_property(_variableNames VARIABLES)
|
||||
foreach(_variableName ${_variableNames})
|
||||
message(STATUS "${_variableName}=${${_variableName}}")
|
||||
endforeach()
|
||||
message(STATUS "Debug - Environment variables:")
|
||||
message(STATUS "-- -- End of all internal variables")
|
||||
message(STATUS "--")
|
||||
message(STATUS "-- -- Start of all environment variables")
|
||||
execute_process(COMMAND "${CMAKE_COMMAND}" "-E" "environment")
|
||||
message(STATUS "-- -- End of all environment variables")
|
||||
|
||||
endfunction()
|
||||
|
||||
# Print CMake variables most important for debugging
|
||||
function(vcmi_print_important_variables)
|
||||
|
||||
message(STATUS "-- -- Start of VCMI build debug information")
|
||||
|
||||
message(STATUS "CMAKE_VERSION: " ${CMAKE_VERSION})
|
||||
message(STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE})
|
||||
message(STATUS "CMAKE_BINARY_DIR: " ${CMAKE_BINARY_DIR})
|
||||
message(STATUS "CMAKE_SOURCE_DIR: " ${CMAKE_SOURCE_DIR})
|
||||
# message(STATUS "PROJECT_BINARY_DIR: " ${PROJECT_BINARY_DIR})
|
||||
# message(STATUS "PROJECT_SOURCE_DIR: " ${PROJECT_SOURCE_DIR})
|
||||
message(STATUS "CMAKE_MODULE_PATH: " ${CMAKE_MODULE_PATH})
|
||||
message(STATUS "CMAKE_COMMAND: " ${CMAKE_COMMAND})
|
||||
message(STATUS "CMAKE_ROOT: " ${CMAKE_ROOT})
|
||||
# message(STATUS "CMAKE_CURRENT_LIST_FILE and LINE: ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE}")
|
||||
# message(STATUS "CMAKE_INCLUDE_PATH: " ${CMAKE_INCLUDE_PATH})
|
||||
# message(STATUS "CMAKE_LIBRARY_PATH: " ${CMAKE_LIBRARY_PATH})
|
||||
|
||||
message(STATUS "UNIX: ${UNIX} - WIN32: ${WIN32} - APPLE: ${APPLE}")
|
||||
message(STATUS "MINGW: ${MINGW} - CYGWIN: ${CYGWIN} - MSVC: ${MSVC}")
|
||||
message(STATUS "CMAKE_CXX_COMPILER_ID: " ${CMAKE_CXX_COMPILER_ID})
|
||||
message(STATUS "CMAKE_CXX_COMPILER_VERSION: " ${CMAKE_CXX_COMPILER_VERSION})
|
||||
message(STATUS "CMAKE_C_COMPILER: " ${CMAKE_C_COMPILER})
|
||||
message(STATUS "CMAKE_CXX_COMPILER: " ${CMAKE_CXX_COMPILER})
|
||||
# message(STATUS "CMAKE_COMPILER_IS_GNUCC: " ${CMAKE_COMPILER_IS_GNUCC})
|
||||
# message(STATUS "CMAKE_COMPILER_IS_GNUCXX: " ${CMAKE_COMPILER_IS_GNUCXX})
|
||||
# message(STATUS "CMAKE_C_FLAGS: " ${CMAKE_C_FLAGS})
|
||||
# message(STATUS "CMAKE_CXX_FLAGS: " ${CMAKE_CXX_FLAGS})
|
||||
|
||||
message(STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM})
|
||||
message(STATUS "CMAKE_SYSTEM_NAME: " ${CMAKE_SYSTEM_NAME})
|
||||
message(STATUS "CMAKE_SYSTEM_VERSION: " ${CMAKE_SYSTEM_VERSION})
|
||||
message(STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR})
|
||||
|
||||
message(STATUS "-- -- End of VCMI build debug information")
|
||||
|
||||
endfunction()
|
||||
|
||||
# Print Git commit hash
|
||||
function(vcmi_print_git_commit_hash)
|
||||
|
||||
message(STATUS "-- -- Start of Git information")
|
||||
message(STATUS "GIT_SHA1: " ${GIT_SHA1})
|
||||
message(STATUS "-- -- End of Git information")
|
||||
|
||||
endfunction()
|
||||
|
@ -76,5 +76,9 @@
|
||||
"config/spells/other.json",
|
||||
"config/spells/timed.json",
|
||||
"config/spells/ability.json"
|
||||
],
|
||||
"skills" :
|
||||
[
|
||||
"config/skills.json"
|
||||
]
|
||||
}
|
||||
|
@ -97,6 +97,11 @@
|
||||
"description": "List of configuration files for spells",
|
||||
"items": { "type":"string", "format" : "textFile" }
|
||||
},
|
||||
"skills": {
|
||||
"type":"array",
|
||||
"description": "List of configuration files for skills",
|
||||
"items": { "type":"string", "format" : "textFile" }
|
||||
},
|
||||
"templates":{
|
||||
"type":"array",
|
||||
"description": "List of configuration files for RMG templates",
|
||||
|
57
config/schemas/skill.json
Normal file
57
config/schemas/skill.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
|
||||
"type" : "object",
|
||||
"$schema" : "http://json-schema.org/draft-04/schema",
|
||||
|
||||
"title" : "VCMI skill format",
|
||||
"description" : "Format used to replace bonuses provided by secondary skills in VCMI",
|
||||
|
||||
"definitions" : {
|
||||
|
||||
"skillBonus" : {
|
||||
"type" : "object",
|
||||
"description" : "Set of bonuses provided by skill at given level",
|
||||
"required" : ["description", "effects"],
|
||||
"properties" : {
|
||||
"description" : {
|
||||
"type" : "string",
|
||||
"description" : "localizable description"
|
||||
},
|
||||
"effects" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : {
|
||||
"$ref" : "vcmi:bonus"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"required" : ["name", "basic", "advanced", "expert"],
|
||||
|
||||
"properties" : {
|
||||
"index" : {
|
||||
"type": "number",
|
||||
"description": "numeric id of skill, required for existing skills"
|
||||
},
|
||||
"name" : {
|
||||
"type": "string",
|
||||
"description": "localizable skill name"
|
||||
},
|
||||
"base" : {
|
||||
"type" : "object",
|
||||
"description" : "will be merged with all levels",
|
||||
"additionalProperties" : true
|
||||
},
|
||||
"basic" : {
|
||||
"$ref" : "#/definitions/skillBonus"
|
||||
},
|
||||
"advanced" : {
|
||||
"$ref" : "#/definitions/skillBonus"
|
||||
},
|
||||
"expert" : {
|
||||
"$ref" : "#/definitions/skillBonus"
|
||||
}
|
||||
}
|
||||
}
|
805
config/skills.json
Normal file
805
config/skills.json
Normal file
@ -0,0 +1,805 @@
|
||||
{
|
||||
"pathfinding" : {
|
||||
"index" : 0,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.pathfinding",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 25 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 75 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"archery" : {
|
||||
"index" : 1,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.archery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 25 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"logistics" : {
|
||||
"index" : 2,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.logistics",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 20 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 30 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"scouting" : {
|
||||
"index" : 3,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"type" : "SIGHT_RADIOUS",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"diplomacy" : {
|
||||
"index" : 4,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.diplomacy",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"surr" : {
|
||||
"type" : "SURRENDER_DISCOUNT",
|
||||
"val" : 20,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 },
|
||||
"surr" : { "val" : 20 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val": 2 },
|
||||
"surr" : { "val" : 40 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val": 3 },
|
||||
"surr" : { "val" : 60 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"navigation" : {
|
||||
"index" : 5,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.navigation",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 100 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 150 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"leadership" : {
|
||||
"index" : 6,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"type" : "MORALE",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"wisdom" : {
|
||||
"index" : 7,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.wisdom",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"mysticism" : {
|
||||
"index" : 8,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"type" : "MANA_REGENERATION",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"luck" : {
|
||||
"index" : 9,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"type" : "LUCK",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"ballistics" : {
|
||||
"index" : 10,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.ballistics",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"ctrl" : {
|
||||
"subtype" : "creature.catapult",
|
||||
"type" : "MANUAL_CONTROL",
|
||||
"val" : 100,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"eagleEye" : {
|
||||
"index" : 11,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.eagleEye",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"val2" : {
|
||||
"subtype" : "skill.eagleEye",
|
||||
"type" : "SECONDARY_SKILL_VAL2",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 40 },
|
||||
"val2" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 },
|
||||
"val2" : { "val" : 3 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 60 },
|
||||
"val2" : { "val" : 4 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"necromancy" : {
|
||||
"index" : 12,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.necromancy",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 20 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 30 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"estates" : {
|
||||
"index" : 13,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.estates",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"description" : "{Basic Estates}\n\nYour hero contributes 125 gold per day to your cause.",
|
||||
"effects" : {
|
||||
"main" : { "val" : 125 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"description" : "{Advanced Estates}\n\nYour hero contributes 250 gold per day to your cause.",
|
||||
"effects" : {
|
||||
"main" : { "val" : 250 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"description" : "{Expert Estates}\n\nYour hero contributes 500 gold per day to your cause.",
|
||||
"effects" : {
|
||||
"main" : { "val" : 500 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"fireMagic" : {
|
||||
"index" : 14,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.fireMagic",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"airMagic" : {
|
||||
"index" : 15,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.airMagic",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"waterMagic" : {
|
||||
"index" : 16,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.waterMagic",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"earthMagic" : {
|
||||
"index" : 17,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.earthMagic",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"scholar" : {
|
||||
"index" : 18,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.scholar",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 4 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"tactics" : {
|
||||
"index" : 19,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.tactics",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 5 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 7 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"artillery" : {
|
||||
"index" : 20,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.artillery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"val2" : {
|
||||
"subtype" : "skill.artillery",
|
||||
"type" : "SECONDARY_SKILL_VAL2",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"ctrl" : {
|
||||
"subtype" : "creature.ballista",
|
||||
"type" : "MANUAL_CONTROL",
|
||||
"val" : 100,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"ctrl2" : {
|
||||
"subtype" : "creature.arrowTower",
|
||||
"type" : "MANUAL_CONTROL",
|
||||
"val" : 100,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 },
|
||||
"val2" : { "val" : 0 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 75 },
|
||||
"val2" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 100 },
|
||||
"val2" : { "val" : 1 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"learning" : {
|
||||
"index" : 21,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.learning",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 5 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 15 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"offence" : {
|
||||
"index" : 22,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.offence",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 20 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 30 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"armorer" : {
|
||||
"index" : 23,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.armorer",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 5 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 15 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"intelligence" : {
|
||||
"index" : 24,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.intelligence",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 25 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 100 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"sorcery" : {
|
||||
"index" : 25,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.sorcery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 5 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 15 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"resistance" : {
|
||||
"index" : 26,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.resistance",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 5 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 20 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"firstAid" : {
|
||||
"index" : 27,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.firstAid",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"ctrl" : {
|
||||
"subtype" : "creature.firstAidTent",
|
||||
"type" : "MANUAL_CONTROL",
|
||||
"val" : 100,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 75 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 100 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -190,3 +190,4 @@ extern DLL_LINKAGE vstd::CLoggerBase * logBonus;
|
||||
extern DLL_LINKAGE vstd::CLoggerBase * logNetwork;
|
||||
extern DLL_LINKAGE vstd::CLoggerBase * logAi;
|
||||
extern DLL_LINKAGE vstd::CLoggerBase * logAnim;
|
||||
extern DLL_LINKAGE vstd::CLoggerBase * logMod;
|
||||
|
@ -2,7 +2,6 @@
|
||||
# https://doc.qt.io/qt-5/cmake-manual.html
|
||||
include_directories(${CMAKE_HOME_DIRECTORY} ${CMAKE_HOME_DIRECTORY}/include ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
include_directories(${ZLIB_INCLUDE_DIR} ${Boost_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS})
|
||||
include_directories(${SDL2_INCLUDE_DIR})
|
||||
|
||||
set(launcher_modmanager_SRCS
|
||||
modManager/cdownloadmanager_moc.cpp
|
||||
@ -38,7 +37,6 @@ set(launcher_SRCS
|
||||
mainwindow_moc.cpp
|
||||
launcherdirs.cpp
|
||||
jsonutils.cpp
|
||||
sdldisplayquery.cpp
|
||||
)
|
||||
|
||||
set(launcher_HEADERS
|
||||
@ -48,7 +46,6 @@ set(launcher_HEADERS
|
||||
mainwindow_moc.h
|
||||
launcherdirs.h
|
||||
jsonutils.h
|
||||
sdldisplayquery.h
|
||||
)
|
||||
|
||||
set(launcher_FORMS
|
||||
@ -102,7 +99,7 @@ if(APPLE)
|
||||
set_property(GLOBAL PROPERTY AUTOGEN_TARGETS_FOLDER vcmilauncher)
|
||||
endif()
|
||||
|
||||
target_link_libraries(vcmilauncher vcmi Qt5::Widgets Qt5::Network ${SDL2_LIBRARY})
|
||||
target_link_libraries(vcmilauncher vcmi Qt5::Widgets Qt5::Network)
|
||||
|
||||
vcmi_set_output_dir(vcmilauncher "")
|
||||
|
||||
|
@ -10,13 +10,11 @@
|
||||
#include <QApplication>
|
||||
#include "StdInc.h"
|
||||
#include "mainwindow_moc.h"
|
||||
#include "sdldisplayquery.h"
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
QApplication vcmilauncher(argc, argv);
|
||||
auto displayList = getDisplays();
|
||||
MainWindow mainWindow(displayList);
|
||||
MainWindow mainWindow;
|
||||
mainWindow.show();
|
||||
return vcmilauncher.exec();
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ void MainWindow::load()
|
||||
settings.init();
|
||||
}
|
||||
|
||||
MainWindow::MainWindow(const QStringList& displayList, QWidget *parent) :
|
||||
MainWindow::MainWindow(QWidget * parent) :
|
||||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
@ -62,7 +62,7 @@ MainWindow::MainWindow(const QStringList& displayList, QWidget *parent) :
|
||||
ui->tabSelectList->setMaximumWidth(width + 4);
|
||||
}
|
||||
ui->tabListWidget->setCurrentIndex(0);
|
||||
ui->settingsView->setDisplayList(displayList);
|
||||
ui->settingsView->setDisplayList();
|
||||
|
||||
connect(ui->tabSelectList, SIGNAL(currentRowChanged(int)),
|
||||
ui->tabListWidget, SLOT(setCurrentIndex(int)));
|
||||
|
@ -25,7 +25,7 @@ private:
|
||||
void load();
|
||||
void startExecutable(QString name);
|
||||
public:
|
||||
explicit MainWindow(const QStringList& displayList, QWidget *parent = 0);
|
||||
explicit MainWindow(QWidget * parent = 0);
|
||||
~MainWindow();
|
||||
|
||||
private slots:
|
||||
|
@ -34,15 +34,15 @@
|
||||
<item row="0" column="0">
|
||||
<widget class="QListWidget" name="tabSelectList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Expanding">
|
||||
<horstretch>89</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>65</width>
|
||||
<height>16777215</height>
|
||||
<width>89</width>
|
||||
<height>89</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
@ -61,27 +61,24 @@
|
||||
<enum>QAbstractItemView::NoDragDrop</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
<enum>QAbstractItemView::SelectItems</enum>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>48</width>
|
||||
<height>64</height>
|
||||
<width>89</width>
|
||||
<height>89</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Static</enum>
|
||||
<property name="flow">
|
||||
<enum>QListView::TopToBottom</enum>
|
||||
</property>
|
||||
<property name="resizeMode">
|
||||
<enum>QListView::Fixed</enum>
|
||||
</property>
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
<enum>QListView::Adjust</enum>
|
||||
</property>
|
||||
<property name="gridSize">
|
||||
<size>
|
||||
<width>64</width>
|
||||
<height>64</height>
|
||||
<width>100</width>
|
||||
<height>100</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="viewMode">
|
||||
@ -93,12 +90,6 @@
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionRectVisible">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="currentRow">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>Mods</string>
|
||||
@ -119,32 +110,6 @@
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QToolButton" name="startGameButton">
|
||||
<property name="text">
|
||||
<string>Play</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:menu-game.png</normaloff>icons:menu-game.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonIconOnly</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="startGameTitle">
|
||||
<property name="font">
|
||||
@ -179,6 +144,44 @@
|
||||
<widget class="CSettingsView" name="settingsView"/>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QToolButton" name="startGameButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Maximum">
|
||||
<horstretch>89</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>89</width>
|
||||
<height>89</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Play</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>icons:menu-game.png</normaloff>icons:menu-game.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>60</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="toolButtonStyle">
|
||||
<enum>Qt::ToolButtonIconOnly</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
|
@ -1,42 +0,0 @@
|
||||
/*
|
||||
* sdldisplayquery.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "sdldisplayquery.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <SDL.h>
|
||||
#include <SDL_video.h>
|
||||
|
||||
QStringList getDisplays()
|
||||
{
|
||||
if(SDL_Init(SDL_INIT_VIDEO))
|
||||
return QStringList("default display");
|
||||
|
||||
const int displays = SDL_GetNumVideoDisplays();
|
||||
QStringList list;
|
||||
|
||||
for (int display = 0; display < displays; ++display)
|
||||
{
|
||||
SDL_Rect rect;
|
||||
|
||||
if (SDL_GetDisplayBounds (display, &rect))
|
||||
continue;
|
||||
|
||||
QString string;
|
||||
QTextStream(&string) << display << " - " << rect.w << "x" << rect.h << " (at " << rect.x << ", " << rect.y << ")";
|
||||
|
||||
list << string;
|
||||
}
|
||||
|
||||
SDL_Quit();
|
||||
return list;
|
||||
}
|
@ -1,14 +0,0 @@
|
||||
/*
|
||||
* sdldisplayquery.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
QStringList getDisplays();
|
@ -29,19 +29,30 @@ static const std::string knownEncodingsList[] = //TODO: remove hardcode
|
||||
"GB2312" // basic set for Simplified Chinese. Separate from GBK to allow proper detection of H3 fonts
|
||||
};
|
||||
|
||||
void CSettingsView::setDisplayList(const QStringList& displayList)
|
||||
void CSettingsView::setDisplayList()
|
||||
{
|
||||
if (displayList.count() < 2)
|
||||
{
|
||||
ui->comboBoxDisplayIndex->hide ();
|
||||
ui->labelDisplayIndex->hide ();
|
||||
}
|
||||
QStringList list;
|
||||
QDesktopWidget * widget = QApplication::desktop();
|
||||
for(int display = 0; display < widget->screenCount(); display++)
|
||||
{
|
||||
QString string;
|
||||
auto rect = widget->screenGeometry(display);
|
||||
QTextStream(&string) << display << " - " << rect.width() << "x" << rect.height();
|
||||
list << string;
|
||||
}
|
||||
|
||||
if(list.count() < 2)
|
||||
{
|
||||
ui->comboBoxDisplayIndex->hide();
|
||||
ui->labelDisplayIndex->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->comboBoxDisplayIndex->clear();
|
||||
ui->comboBoxDisplayIndex->addItems(displayList);
|
||||
ui->comboBoxDisplayIndex->setCurrentIndex(settings["video"]["displayIndex"].Float());
|
||||
}
|
||||
{
|
||||
int displayIndex = settings["video"]["displayIndex"].Integer();
|
||||
ui->comboBoxDisplayIndex->clear();
|
||||
ui->comboBoxDisplayIndex->addItems(list);
|
||||
ui->comboBoxDisplayIndex->setCurrentIndex(displayIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void CSettingsView::loadSettings()
|
||||
|
@ -22,7 +22,7 @@ public:
|
||||
~CSettingsView();
|
||||
|
||||
void loadSettings();
|
||||
void setDisplayList(const QStringList& displayList);
|
||||
void setDisplayList();
|
||||
|
||||
private slots:
|
||||
void on_checkBoxFullScreen_stateChanged(int state);
|
||||
|
@ -360,7 +360,7 @@ ArtifactPosition CArtHandler::stringToSlot(std::string slotName)
|
||||
if (it != artifactPositionMap.end())
|
||||
return it->second;
|
||||
|
||||
logGlobal->warn("Warning! Artifact slot %s not recognized!", slotName);
|
||||
logMod->warn("Warning! Artifact slot %s not recognized!", slotName);
|
||||
return ArtifactPosition::PRE_FIRST;
|
||||
}
|
||||
|
||||
@ -421,7 +421,7 @@ CArtifact::EartClass CArtHandler::stringToClass(std::string className)
|
||||
if (it != artifactClassMap.end())
|
||||
return it->second;
|
||||
|
||||
logGlobal->warn("Warning! Artifact rarity %s not recognized!", className);
|
||||
logMod->warn("Warning! Artifact rarity %s not recognized!", className);
|
||||
return CArtifact::ART_SPECIAL;
|
||||
}
|
||||
|
||||
@ -455,7 +455,7 @@ void CArtHandler::loadType(CArtifact * art, const JsonNode & node)
|
||||
}
|
||||
}
|
||||
else
|
||||
logGlobal->warn("Warning! Artifact type %s not recognized!", b.String());
|
||||
logMod->warn("Warning! Artifact type %s not recognized!", b.String());
|
||||
}
|
||||
}
|
||||
|
||||
@ -679,11 +679,11 @@ void CArtHandler::erasePickedArt(ArtifactID id)
|
||||
artifactList->erase(itr);
|
||||
}
|
||||
else
|
||||
logGlobal->warn("Problem: cannot erase artifact %s from list, it was not present", art->Name());
|
||||
logMod->warn("Problem: cannot erase artifact %s from list, it was not present", art->Name());
|
||||
|
||||
}
|
||||
else
|
||||
logGlobal->warn("Problem: cannot find list for artifact %s, strange class. (special?)", art->Name());
|
||||
logMod->warn("Problem: cannot find list for artifact %s, strange class. (special?)", art->Name());
|
||||
}
|
||||
|
||||
boost::optional<std::vector<CArtifact*>&> CArtHandler::listFromClass( CArtifact::EartClass artifactClass )
|
||||
@ -869,7 +869,7 @@ bool CArtifactInstance::canBePutAt(const CArtifactSet *artSet, ArtifactPosition
|
||||
auto possibleSlots = artType->possibleSlots.find(artSet->bearerType());
|
||||
if(possibleSlots == artType->possibleSlots.end())
|
||||
{
|
||||
logGlobal->warn("Warning: artifact %s doesn't have defined allowed slots for bearer of type %s", artType->Name(), artSet->bearerType());
|
||||
logMod->warn("Warning: artifact %s doesn't have defined allowed slots for bearer of type %s", artType->Name(), artSet->bearerType());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1007,7 +1007,7 @@ SpellID CArtifactInstance::getGivenSpellID() const
|
||||
const auto b = getBonusLocalFirst(Selector::type(Bonus::SPELL));
|
||||
if(!b)
|
||||
{
|
||||
logGlobal->warn("Warning: %s doesn't bear any spell!", nodeName());
|
||||
logMod->warn("Warning: %s doesn't bear any spell!", nodeName());
|
||||
return SpellID::NONE;
|
||||
}
|
||||
return SpellID(b->subtype);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "CHeroHandler.h"
|
||||
#include "mapObjects/CObjectHandler.h"
|
||||
#include "CModHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
#include "mapping/CMap.h"
|
||||
#include "mapping/CMapService.h"
|
||||
#include "StartInfo.h"
|
||||
@ -113,6 +114,10 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
|
||||
{
|
||||
dst = VLC->objtypeh->getObjectName(ser);
|
||||
}
|
||||
else if(type == SEC_SKILL_NAME)
|
||||
{
|
||||
dst = VLC->skillh->skillName(ser);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::string> *vec;
|
||||
@ -139,9 +144,6 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
|
||||
case ADVOB_TXT:
|
||||
vec = &VLC->generaltexth->advobtxt;
|
||||
break;
|
||||
case SEC_SKILL_NAME:
|
||||
vec = &VLC->generaltexth->skillName;
|
||||
break;
|
||||
case COLOR:
|
||||
vec = &VLC->generaltexth->capColors;
|
||||
break;
|
||||
|
@ -37,7 +37,6 @@ class CGObjectInstance;
|
||||
class CCreature;
|
||||
class CMap;
|
||||
struct StartInfo;
|
||||
struct SDL_Surface;
|
||||
class CMapHandler;
|
||||
struct SetObjectProperty;
|
||||
struct MetaString;
|
||||
|
@ -377,23 +377,6 @@ CGeneralTextHandler::CGeneralTextHandler()
|
||||
}
|
||||
while (parser.endLine());
|
||||
}
|
||||
{
|
||||
CLegacyConfigParser parser("DATA/SSTRAITS.TXT");
|
||||
|
||||
//skip header
|
||||
parser.endLine();
|
||||
parser.endLine();
|
||||
|
||||
do
|
||||
{
|
||||
skillName.push_back(parser.readString());
|
||||
|
||||
skillInfoTexts.push_back(std::vector<std::string>());
|
||||
for(int j = 0; j < 3; j++)
|
||||
skillInfoTexts.back().push_back(parser.readString());
|
||||
}
|
||||
while (parser.endLine());
|
||||
}
|
||||
{
|
||||
CLegacyConfigParser parser("DATA/SEERHUT.TXT");
|
||||
|
||||
|
@ -130,8 +130,6 @@ public:
|
||||
std::vector<std::string> tentColors;
|
||||
|
||||
//sec skills
|
||||
std::vector<std::string> skillName;
|
||||
std::vector<std::vector<std::string>> skillInfoTexts; //[id][level] : level 0 - basic; 2 - advanced
|
||||
std::vector<std::string> levels;
|
||||
std::vector<std::string> zcrexp; //more or less useful content of that file
|
||||
//commanders
|
||||
|
@ -362,7 +362,7 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node)
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->error("Unknown skill level: %s", set["level"].String());
|
||||
logMod->error("Unknown skill level: %s", set["level"].String());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,6 +115,7 @@ set(lib_SRCS
|
||||
CModHandler.cpp
|
||||
CPathfinder.cpp
|
||||
CRandomGenerator.cpp
|
||||
CSkillHandler.cpp
|
||||
CStack.cpp
|
||||
CThreadHelper.cpp
|
||||
CTownHandler.cpp
|
||||
@ -256,6 +257,7 @@ set(lib_HEADERS
|
||||
CPlayerState.h
|
||||
CRandomGenerator.h
|
||||
CScriptingModule.h
|
||||
CSkillHandler.h
|
||||
CSoundBase.h
|
||||
CStack.h
|
||||
CStopWatch.h
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "CStopWatch.h"
|
||||
#include "IHandlerBase.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
|
||||
CIdentifierStorage::CIdentifierStorage():
|
||||
state(LOADING)
|
||||
@ -36,7 +37,7 @@ CIdentifierStorage::~CIdentifierStorage()
|
||||
void CIdentifierStorage::checkIdentifier(std::string & ID)
|
||||
{
|
||||
if (boost::algorithm::ends_with(ID, "."))
|
||||
logGlobal->warn("BIG WARNING: identifier %s seems to be broken!", ID);
|
||||
logMod->warn("BIG WARNING: identifier %s seems to be broken!", ID);
|
||||
else
|
||||
{
|
||||
size_t pos = 0;
|
||||
@ -44,7 +45,7 @@ void CIdentifierStorage::checkIdentifier(std::string & ID)
|
||||
{
|
||||
if (std::tolower(ID[pos]) != ID[pos] ) //Not in camelCase
|
||||
{
|
||||
logGlobal->warn("Warning: identifier %s is not in camelCase!", ID);
|
||||
logMod->warn("Warning: identifier %s is not in camelCase!", ID);
|
||||
ID[pos] = std::tolower(ID[pos]);// Try to fix the ID
|
||||
}
|
||||
pos = ID.find('.', pos);
|
||||
@ -148,7 +149,7 @@ boost::optional<si32> CIdentifierStorage::getIdentifier(std::string scope, std::
|
||||
if (idList.size() == 1)
|
||||
return idList.front().id;
|
||||
if (!silent)
|
||||
logGlobal->error("Failed to resolve identifier %s of type %s from mod %s", name , type ,scope);
|
||||
logMod->error("Failed to resolve identifier %s of type %s from mod %s", name , type ,scope);
|
||||
|
||||
return boost::optional<si32>();
|
||||
}
|
||||
@ -161,7 +162,7 @@ boost::optional<si32> CIdentifierStorage::getIdentifier(std::string type, const
|
||||
if (idList.size() == 1)
|
||||
return idList.front().id;
|
||||
if (!silent)
|
||||
logGlobal->error("Failed to resolve identifier %s of type %s from mod %s", name.String(), type, name.meta);
|
||||
logMod->error("Failed to resolve identifier %s of type %s from mod %s", name.String(), type, name.meta);
|
||||
|
||||
return boost::optional<si32>();
|
||||
}
|
||||
@ -175,7 +176,7 @@ boost::optional<si32> CIdentifierStorage::getIdentifier(const JsonNode & name, b
|
||||
if (idList.size() == 1)
|
||||
return idList.front().id;
|
||||
if (!silent)
|
||||
logGlobal->error("Failed to resolve identifier %s of type %s from mod %s", name.String(), pair2.first, name.meta);
|
||||
logMod->error("Failed to resolve identifier %s of type %s from mod %s", name.String(), pair2.first, name.meta);
|
||||
|
||||
return boost::optional<si32>();
|
||||
}
|
||||
@ -189,7 +190,7 @@ boost::optional<si32> CIdentifierStorage::getIdentifier(std::string scope, std::
|
||||
if (idList.size() == 1)
|
||||
return idList.front().id;
|
||||
if (!silent)
|
||||
logGlobal->error("Failed to resolve identifier %s of type %s from mod %s", fullName, pair2.first, scope);
|
||||
logMod->error("Failed to resolve identifier %s of type %s from mod %s", fullName, pair2.first, scope);
|
||||
|
||||
return boost::optional<si32>();
|
||||
}
|
||||
@ -203,7 +204,12 @@ void CIdentifierStorage::registerObject(std::string scope, std::string type, std
|
||||
std::string fullID = type + '.' + name;
|
||||
checkIdentifier(fullID);
|
||||
|
||||
registeredObjects.insert(std::make_pair(fullID, data));
|
||||
std::pair<const std::string, ObjectData> mapping = std::make_pair(fullID, data);
|
||||
if(!vstd::containsMapping(registeredObjects, mapping))
|
||||
{
|
||||
logMod->trace("registered %s as %s:%s", fullID, scope, identifier);
|
||||
registeredObjects.insert(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdentifiers(const ObjectCallback & request)
|
||||
@ -274,15 +280,15 @@ bool CIdentifierStorage::resolveIdentifier(const ObjectCallback & request)
|
||||
|
||||
// error found. Try to generate some debug info
|
||||
if (identifiers.size() == 0)
|
||||
logGlobal->error("Unknown identifier!");
|
||||
logMod->error("Unknown identifier!");
|
||||
else
|
||||
logGlobal->error("Ambiguous identifier request!");
|
||||
logMod->error("Ambiguous identifier request!");
|
||||
|
||||
logGlobal->error("Request for %s.%s from mod %s", request.type, request.name, request.localScope);
|
||||
logMod->error("Request for %s.%s from mod %s", request.type, request.name, request.localScope);
|
||||
|
||||
for (auto id : identifiers)
|
||||
{
|
||||
logGlobal->error("\tID is available in mod %s", id.scope);
|
||||
logMod->error("\tID is available in mod %s", id.scope);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -302,9 +308,9 @@ void CIdentifierStorage::finalize()
|
||||
{
|
||||
for(auto object : registeredObjects)
|
||||
{
|
||||
logGlobal->trace("%s : %s -> %d", object.second.scope, object.first, object.second.id);
|
||||
logMod->trace("%s : %s -> %d", object.second.scope, object.first, object.second.id);
|
||||
}
|
||||
logGlobal->error("All known identifiers were dumped into log file");
|
||||
logMod->error("All known identifiers were dumped into log file");
|
||||
}
|
||||
assert(errorsFound == false);
|
||||
state = FINISHED;
|
||||
@ -345,9 +351,9 @@ bool CContentHandler::ContentTypeHandler::preloadModData(std::string modName, st
|
||||
|
||||
// patching this mod? Send warning and continue - this situation can be handled normally
|
||||
if (remoteName == modName)
|
||||
logGlobal->warn("Redundant namespace definition for %s", objectName);
|
||||
logMod->warn("Redundant namespace definition for %s", objectName);
|
||||
|
||||
logGlobal->trace("Patching object %s (%s) from %s", objectName, remoteName, modName);
|
||||
logMod->trace("Patching object %s (%s) from %s", objectName, remoteName, modName);
|
||||
JsonNode & remoteConf = modData[remoteName].patches[objectName];
|
||||
|
||||
JsonUtils::merge(remoteConf, entry.second);
|
||||
@ -383,17 +389,22 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali
|
||||
|
||||
if (originalData.size() > index)
|
||||
{
|
||||
logMod->trace("found original data in loadMod(%s) at index %d", name, index);
|
||||
JsonUtils::merge(originalData[index], data);
|
||||
|
||||
performValidate(originalData[index],name);
|
||||
handler->loadObject(modName, name, originalData[index], index);
|
||||
|
||||
originalData[index].clear(); // do not use same data twice (same ID)
|
||||
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
logMod->debug("no original data in loadMod(%s) at index %d", name, index);
|
||||
performValidate(data, name);
|
||||
handler->loadObject(modName, name, data, index);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// normal new object or one with index bigger that data size
|
||||
// normal new object
|
||||
logMod->trace("no index in loadMod(%s)", name);
|
||||
performValidate(data,name);
|
||||
handler->loadObject(modName, name, data);
|
||||
}
|
||||
@ -420,6 +431,7 @@ CContentHandler::CContentHandler()
|
||||
handlers.insert(std::make_pair("objects", ContentTypeHandler(VLC->objtypeh, "object")));
|
||||
handlers.insert(std::make_pair("heroes", ContentTypeHandler(VLC->heroh, "hero")));
|
||||
handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell")));
|
||||
handlers.insert(std::make_pair("skills", ContentTypeHandler(VLC->skillh, "skill")));
|
||||
handlers.insert(std::make_pair("templates", ContentTypeHandler((IHandlerBase *)VLC->tplh, "template")));
|
||||
|
||||
//TODO: any other types of moddables?
|
||||
@ -466,7 +478,7 @@ void CContentHandler::preloadData(CModInfo & mod)
|
||||
bool validate = (mod.validation != CModInfo::PASSED);
|
||||
|
||||
// print message in format [<8-symbols checksum>] <modname>
|
||||
logGlobal->info("\t\t[%08x]%s", mod.checksum, mod.name);
|
||||
logMod->info("\t\t[%08x]%s", mod.checksum, mod.name);
|
||||
|
||||
if (validate && mod.identifier != "core")
|
||||
{
|
||||
@ -487,12 +499,12 @@ void CContentHandler::load(CModInfo & mod)
|
||||
if (validate)
|
||||
{
|
||||
if (mod.validation != CModInfo::FAILED)
|
||||
logGlobal->info("\t\t[DONE] %s", mod.name);
|
||||
logMod->info("\t\t[DONE] %s", mod.name);
|
||||
else
|
||||
logGlobal->error("\t\t[FAIL] %s", mod.name);
|
||||
logMod->error("\t\t[FAIL] %s", mod.name);
|
||||
}
|
||||
else
|
||||
logGlobal->info("\t\t[SKIP] %s", mod.name);
|
||||
logMod->info("\t\t[SKIP] %s", mod.name);
|
||||
}
|
||||
|
||||
static JsonNode loadModSettings(std::string path)
|
||||
@ -618,39 +630,39 @@ void CModHandler::loadConfigFromFile (std::string name)
|
||||
paths += p.string() + ", ";
|
||||
}
|
||||
paths = paths.substr(0, paths.size() - 2);
|
||||
logGlobal->debug("Loading hardcoded features settings from [%s], result:", paths);
|
||||
logMod->debug("Loading hardcoded features settings from [%s], result:", paths);
|
||||
settings.data = JsonUtils::assembleFromFiles("config/" + name);
|
||||
const JsonNode & hardcodedFeatures = settings.data["hardcodedFeatures"];
|
||||
settings.MAX_HEROES_AVAILABLE_PER_PLAYER = hardcodedFeatures["MAX_HEROES_AVAILABLE_PER_PLAYER"].Integer();
|
||||
logGlobal->debug("\tMAX_HEROES_AVAILABLE_PER_PLAYER\t%d", settings.MAX_HEROES_AVAILABLE_PER_PLAYER);
|
||||
logMod->debug("\tMAX_HEROES_AVAILABLE_PER_PLAYER\t%d", settings.MAX_HEROES_AVAILABLE_PER_PLAYER);
|
||||
settings.MAX_HEROES_ON_MAP_PER_PLAYER = hardcodedFeatures["MAX_HEROES_ON_MAP_PER_PLAYER"].Integer();
|
||||
logGlobal->debug("\tMAX_HEROES_ON_MAP_PER_PLAYER\t%d", settings.MAX_HEROES_ON_MAP_PER_PLAYER);
|
||||
logMod->debug("\tMAX_HEROES_ON_MAP_PER_PLAYER\t%d", settings.MAX_HEROES_ON_MAP_PER_PLAYER);
|
||||
settings.CREEP_SIZE = hardcodedFeatures["CREEP_SIZE"].Integer();
|
||||
logGlobal->debug("\tCREEP_SIZE\t%d", settings.CREEP_SIZE);
|
||||
logMod->debug("\tCREEP_SIZE\t%d", settings.CREEP_SIZE);
|
||||
settings.WEEKLY_GROWTH = hardcodedFeatures["WEEKLY_GROWTH_PERCENT"].Integer();
|
||||
logGlobal->debug("\tWEEKLY_GROWTH\t%d", settings.WEEKLY_GROWTH);
|
||||
logMod->debug("\tWEEKLY_GROWTH\t%d", settings.WEEKLY_GROWTH);
|
||||
settings.NEUTRAL_STACK_EXP = hardcodedFeatures["NEUTRAL_STACK_EXP_DAILY"].Integer();
|
||||
logGlobal->debug("\tNEUTRAL_STACK_EXP\t%d", settings.NEUTRAL_STACK_EXP);
|
||||
logMod->debug("\tNEUTRAL_STACK_EXP\t%d", settings.NEUTRAL_STACK_EXP);
|
||||
settings.MAX_BUILDING_PER_TURN = hardcodedFeatures["MAX_BUILDING_PER_TURN"].Integer();
|
||||
logGlobal->debug("\tMAX_BUILDING_PER_TURN\t%d", settings.MAX_BUILDING_PER_TURN);
|
||||
logMod->debug("\tMAX_BUILDING_PER_TURN\t%d", settings.MAX_BUILDING_PER_TURN);
|
||||
settings.DWELLINGS_ACCUMULATE_CREATURES = hardcodedFeatures["DWELLINGS_ACCUMULATE_CREATURES"].Bool();
|
||||
logGlobal->debug("\tDWELLINGS_ACCUMULATE_CREATURES\t%d", static_cast<int>(settings.DWELLINGS_ACCUMULATE_CREATURES));
|
||||
logMod->debug("\tDWELLINGS_ACCUMULATE_CREATURES\t%d", static_cast<int>(settings.DWELLINGS_ACCUMULATE_CREATURES));
|
||||
settings.ALL_CREATURES_GET_DOUBLE_MONTHS = hardcodedFeatures["ALL_CREATURES_GET_DOUBLE_MONTHS"].Bool();
|
||||
logGlobal->debug("\tALL_CREATURES_GET_DOUBLE_MONTHS\t%d", static_cast<int>(settings.ALL_CREATURES_GET_DOUBLE_MONTHS));
|
||||
logMod->debug("\tALL_CREATURES_GET_DOUBLE_MONTHS\t%d", static_cast<int>(settings.ALL_CREATURES_GET_DOUBLE_MONTHS));
|
||||
settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS = hardcodedFeatures["WINNING_HERO_WITH_NO_TROOPS_RETREATS"].Bool();
|
||||
logGlobal->debug("\tWINNING_HERO_WITH_NO_TROOPS_RETREATS\t%d", static_cast<int>(settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS));
|
||||
logMod->debug("\tWINNING_HERO_WITH_NO_TROOPS_RETREATS\t%d", static_cast<int>(settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS));
|
||||
settings.BLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE = hardcodedFeatures["BLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE"].Bool();
|
||||
logGlobal->debug("\tBLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE\t%d", static_cast<int>(settings.BLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE));
|
||||
logMod->debug("\tBLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE\t%d", static_cast<int>(settings.BLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE));
|
||||
|
||||
const JsonNode & gameModules = settings.data["modules"];
|
||||
modules.STACK_EXP = gameModules["STACK_EXPERIENCE"].Bool();
|
||||
logGlobal->debug("\tSTACK_EXP\t%d", static_cast<int>(modules.STACK_EXP));
|
||||
logMod->debug("\tSTACK_EXP\t%d", static_cast<int>(modules.STACK_EXP));
|
||||
modules.STACK_ARTIFACT = gameModules["STACK_ARTIFACTS"].Bool();
|
||||
logGlobal->debug("\tSTACK_ARTIFACT\t%d", static_cast<int>(modules.STACK_ARTIFACT));
|
||||
logMod->debug("\tSTACK_ARTIFACT\t%d", static_cast<int>(modules.STACK_ARTIFACT));
|
||||
modules.COMMANDERS = gameModules["COMMANDERS"].Bool();
|
||||
logGlobal->debug("\tCOMMANDERS\t%d", static_cast<int>(modules.COMMANDERS));
|
||||
logMod->debug("\tCOMMANDERS\t%d", static_cast<int>(modules.COMMANDERS));
|
||||
modules.MITHRIL = gameModules["MITHRIL"].Bool();
|
||||
logGlobal->debug("\tMITHRIL\t%d", static_cast<int>(modules.MITHRIL));
|
||||
logMod->debug("\tMITHRIL\t%d", static_cast<int>(modules.MITHRIL));
|
||||
}
|
||||
|
||||
// currentList is passed by value to get current list of depending mods
|
||||
@ -661,8 +673,8 @@ bool CModHandler::hasCircularDependency(TModID modID, std::set <TModID> currentL
|
||||
// Mod already present? We found a loop
|
||||
if (vstd::contains(currentList, modID))
|
||||
{
|
||||
logGlobal->error("Error: Circular dependency detected! Printing dependency list:");
|
||||
logGlobal->error("\t%s -> ", mod.name);
|
||||
logMod->error("Error: Circular dependency detected! Printing dependency list:");
|
||||
logMod->error("\t%s -> ", mod.name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -673,7 +685,7 @@ bool CModHandler::hasCircularDependency(TModID modID, std::set <TModID> currentL
|
||||
{
|
||||
if (hasCircularDependency(dependency, currentList))
|
||||
{
|
||||
logGlobal->error("\t%s ->\n", mod.name); // conflict detected, print dependency list
|
||||
logMod->error("\t%s ->\n", mod.name); // conflict detected, print dependency list
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -690,7 +702,7 @@ bool CModHandler::checkDependencies(const std::vector <TModID> & input) const
|
||||
{
|
||||
if (!vstd::contains(input, dep))
|
||||
{
|
||||
logGlobal->error("Error: Mod %s requires missing %s!", mod.name, dep);
|
||||
logMod->error("Error: Mod %s requires missing %s!", mod.name, dep);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -699,7 +711,7 @@ bool CModHandler::checkDependencies(const std::vector <TModID> & input) const
|
||||
{
|
||||
if (vstd::contains(input, conflicting))
|
||||
{
|
||||
logGlobal->error("Error: Mod %s conflicts with %s!", mod.name, allMods.at(conflicting).name);
|
||||
logMod->error("Error: Mod %s conflicts with %s!", mod.name, allMods.at(conflicting).name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -930,11 +942,11 @@ void CModHandler::load()
|
||||
CStopWatch totalTime, timer;
|
||||
|
||||
CContentHandler content;
|
||||
logGlobal->info("\tInitializing content handler: %d ms", timer.getDiff());
|
||||
logMod->info("\tInitializing content handler: %d ms", timer.getDiff());
|
||||
|
||||
for(const TModID & modName : activeMods)
|
||||
{
|
||||
logGlobal->trace("Generating checksum for %s", modName);
|
||||
logMod->trace("Generating checksum for %s", modName);
|
||||
allMods[modName].updateChecksum(calculateModChecksum(modName, CResourceHandler::get(modName)));
|
||||
}
|
||||
|
||||
@ -943,7 +955,7 @@ void CModHandler::load()
|
||||
content.preloadData(coreMod);
|
||||
for(const TModID & modName : activeMods)
|
||||
content.preloadData(allMods[modName]);
|
||||
logGlobal->info("\tParsing mod data: %d ms", timer.getDiff());
|
||||
logMod->info("\tParsing mod data: %d ms", timer.getDiff());
|
||||
|
||||
content.load(coreMod);
|
||||
for(const TModID & modName : activeMods)
|
||||
@ -951,17 +963,17 @@ void CModHandler::load()
|
||||
|
||||
content.loadCustom();
|
||||
|
||||
logGlobal->info("\tLoading mod data: %d ms", timer.getDiff());
|
||||
logMod->info("\tLoading mod data: %d ms", timer.getDiff());
|
||||
|
||||
VLC->creh->loadCrExpBon();
|
||||
VLC->creh->buildBonusTreeForTiers(); //do that after all new creatures are loaded
|
||||
|
||||
identifiers.finalize();
|
||||
logGlobal->info("\tResolving identifiers: %d ms", timer.getDiff());
|
||||
logMod->info("\tResolving identifiers: %d ms", timer.getDiff());
|
||||
|
||||
content.afterLoadFinalization();
|
||||
logGlobal->info("\tHandlers post-load finalization: %d ms ", timer.getDiff());
|
||||
logGlobal->info("\tAll game content loaded in %d ms", totalTime.getDiff());
|
||||
logMod->info("\tHandlers post-load finalization: %d ms ", timer.getDiff());
|
||||
logMod->info("\tAll game content loaded in %d ms", totalTime.getDiff());
|
||||
}
|
||||
|
||||
void CModHandler::afterLoad()
|
||||
|
@ -51,6 +51,10 @@ class CIdentifierStorage
|
||||
si32 id;
|
||||
std::string scope; /// scope in which this ID located
|
||||
|
||||
bool operator==(const ObjectData & other) const
|
||||
{
|
||||
return id == other.id && scope == other.scope;
|
||||
}
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@ -59,7 +63,7 @@ class CIdentifierStorage
|
||||
}
|
||||
};
|
||||
|
||||
std::multimap<std::string, ObjectData > registeredObjects;
|
||||
std::multimap<std::string, ObjectData> registeredObjects;
|
||||
std::vector<ObjectCallback> scheduledRequests;
|
||||
|
||||
ELoadingState state;
|
||||
|
226
lib/CSkillHandler.cpp
Normal file
226
lib/CSkillHandler.cpp
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* CSkillHandler.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
#include "CSkillHandler.h"
|
||||
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
|
||||
#include "JsonNode.h"
|
||||
|
||||
#include "CModHandler.h"
|
||||
#include "StringConstants.h"
|
||||
|
||||
#include "CStack.h"
|
||||
#include "battle/BattleInfo.h"
|
||||
#include "battle/CBattleInfoCallback.h"
|
||||
|
||||
///CSkill
|
||||
CSkill::LevelInfo::LevelInfo()
|
||||
{
|
||||
}
|
||||
|
||||
CSkill::LevelInfo::~LevelInfo()
|
||||
{
|
||||
}
|
||||
|
||||
CSkill::CSkill(SecondarySkill id) : id(id)
|
||||
{
|
||||
if(id == SecondarySkill::DEFAULT)
|
||||
identifier = "default";
|
||||
else
|
||||
identifier = NSecondarySkill::names[id];
|
||||
// init levels
|
||||
LevelInfo emptyLevel;
|
||||
for(int level = 1; level < NSecondarySkill::levels.size(); level++)
|
||||
levels.push_back(emptyLevel);
|
||||
}
|
||||
|
||||
CSkill::~CSkill()
|
||||
{
|
||||
}
|
||||
|
||||
void CSkill::addNewBonus(const std::shared_ptr<Bonus> & b, int level)
|
||||
{
|
||||
b->source = Bonus::SECONDARY_SKILL;
|
||||
b->sid = id;
|
||||
b->duration = Bonus::PERMANENT;
|
||||
b->description = identifier;
|
||||
levels[level-1].effects.push_back(b);
|
||||
}
|
||||
|
||||
void CSkill::setDescription(const std::string & desc, int level)
|
||||
{
|
||||
levels[level-1].description = desc;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<Bonus>> & CSkill::getBonus(int level) const
|
||||
{
|
||||
return levels[level-1].effects;
|
||||
}
|
||||
|
||||
const std::string & CSkill::getDescription(int level) const
|
||||
{
|
||||
return levels[level-1].description;
|
||||
}
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill::LevelInfo & info)
|
||||
{
|
||||
out << "(\"" << info.description << "\", [";
|
||||
for(int i=0; i < info.effects.size(); i++)
|
||||
out << (i ? "," : "") << info.effects[i]->Description();
|
||||
return out << "])";
|
||||
}
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill & skill)
|
||||
{
|
||||
out << "Skill(" << (int)skill.id << "," << skill.identifier << "): [";
|
||||
for(int i=0; i < skill.levels.size(); i++)
|
||||
out << (i ? "," : "") << skill.levels[i];
|
||||
return out << "]";
|
||||
}
|
||||
|
||||
std::string CSkill::toString() const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
///CSkillHandler
|
||||
CSkillHandler::CSkillHandler()
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<JsonNode> CSkillHandler::loadLegacyData(size_t dataSize)
|
||||
{
|
||||
CLegacyConfigParser parser("DATA/SSTRAITS.TXT");
|
||||
|
||||
//skip header
|
||||
parser.endLine();
|
||||
parser.endLine();
|
||||
|
||||
std::vector<std::string> skillNames;
|
||||
std::vector<std::vector<std::string>> skillInfoTexts;
|
||||
do
|
||||
{
|
||||
skillNames.push_back(parser.readString());
|
||||
skillInfoTexts.push_back(std::vector<std::string>());
|
||||
for(int i = 0; i < 3; i++)
|
||||
skillInfoTexts.back().push_back(parser.readString());
|
||||
}
|
||||
while (parser.endLine());
|
||||
|
||||
assert(skillNames.size() == GameConstants::SKILL_QUANTITY);
|
||||
|
||||
//store & construct JSON
|
||||
std::vector<JsonNode> legacyData;
|
||||
for(int id = 0; id < GameConstants::SKILL_QUANTITY; id++)
|
||||
{
|
||||
JsonNode skillNode(JsonNode::DATA_STRUCT);
|
||||
skillNode["name"].String() = skillNames[id];
|
||||
for(int level = 1; level < NSecondarySkill::levels.size(); level++)
|
||||
{
|
||||
std::string & desc = skillInfoTexts[id][level-1];
|
||||
auto & levelNode = skillNode[NSecondarySkill::levels[level]].Struct();
|
||||
levelNode["description"].String() = desc;
|
||||
levelNode["effects"].Struct(); // create empty effects objects
|
||||
}
|
||||
legacyData.push_back(skillNode);
|
||||
}
|
||||
objects.resize(legacyData.size());
|
||||
return legacyData;
|
||||
}
|
||||
|
||||
const std::string CSkillHandler::getTypeName() const
|
||||
{
|
||||
return "skill";
|
||||
}
|
||||
|
||||
const std::string & CSkillHandler::skillInfo(int skill, int level) const
|
||||
{
|
||||
return objects[skill]->getDescription(level);
|
||||
}
|
||||
|
||||
const std::string & CSkillHandler::skillName(int skill) const
|
||||
{
|
||||
return objects[skill]->name;
|
||||
}
|
||||
|
||||
CSkill * CSkillHandler::loadFromJson(const JsonNode & json, const std::string & identifier)
|
||||
{
|
||||
CSkill * skill = nullptr;
|
||||
|
||||
for(int id = 0; id < GameConstants::SKILL_QUANTITY; id++)
|
||||
{
|
||||
if(NSecondarySkill::names[id].compare(identifier) == 0)
|
||||
{
|
||||
skill = new CSkill(SecondarySkill(id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!skill)
|
||||
{
|
||||
logMod->error("unknown secondary skill %s", identifier);
|
||||
throw std::runtime_error("invalid skill");
|
||||
}
|
||||
|
||||
skill->name = json["name"].String();
|
||||
for(int level = 1; level < NSecondarySkill::levels.size(); level++)
|
||||
{
|
||||
const std::string & levelName = NSecondarySkill::levels[level]; // basic, advanced, expert
|
||||
const JsonNode & levelNode = json[levelName];
|
||||
// parse bonus effects
|
||||
for(auto b : levelNode["effects"].Struct())
|
||||
{
|
||||
auto bonus = JsonUtils::parseBonus(b.second);
|
||||
bonus->sid = skill->id;
|
||||
skill->addNewBonus(bonus, level);
|
||||
}
|
||||
skill->setDescription(levelNode["description"].String(), level);
|
||||
}
|
||||
logMod->debug("loaded secondary skill %s(%d)", identifier, (int)skill->id);
|
||||
logMod->trace("%s", skill->toString());
|
||||
|
||||
return skill;
|
||||
}
|
||||
|
||||
void CSkillHandler::afterLoadFinalization()
|
||||
{
|
||||
}
|
||||
|
||||
void CSkillHandler::beforeValidate(JsonNode & object)
|
||||
{
|
||||
//handle "base" level info
|
||||
JsonNode & base = object["base"];
|
||||
|
||||
auto inheritNode = [&](const std::string & name){
|
||||
JsonUtils::inherit(object[name], base);
|
||||
};
|
||||
|
||||
inheritNode("basic");
|
||||
inheritNode("advanced");
|
||||
inheritNode("expert");
|
||||
}
|
||||
|
||||
CSkillHandler::~CSkillHandler()
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<bool> CSkillHandler::getDefaultAllowed() const
|
||||
{
|
||||
std::vector<bool> allowedSkills(objects.size(), true);
|
||||
return allowedSkills;
|
||||
}
|
87
lib/CSkillHandler.h
Normal file
87
lib/CSkillHandler.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* CSkillHandler.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../lib/HeroBonus.h"
|
||||
#include "GameConstants.h"
|
||||
#include "IHandlerBase.h"
|
||||
|
||||
class DLL_LINKAGE CSkill // secondary skill
|
||||
{
|
||||
protected:
|
||||
struct LevelInfo
|
||||
{
|
||||
std::string description; //descriptions of spell for skill level
|
||||
std::vector<std::shared_ptr<Bonus>> effects;
|
||||
|
||||
LevelInfo();
|
||||
~LevelInfo();
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & description;
|
||||
h & effects;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<LevelInfo> levels; // bonuses provided by basic, advanced and expert level
|
||||
|
||||
public:
|
||||
CSkill(SecondarySkill id = SecondarySkill::DEFAULT);
|
||||
~CSkill();
|
||||
|
||||
void addNewBonus(const std::shared_ptr<Bonus> & b, int level);
|
||||
void setDescription(const std::string & desc, int level);
|
||||
const std::vector<std::shared_ptr<Bonus>> & getBonus(int level) const;
|
||||
const std::string & getDescription(int level) const;
|
||||
std::string toString() const;
|
||||
|
||||
SecondarySkill id;
|
||||
std::string identifier;
|
||||
std::string name; //as displayed in GUI
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & id;
|
||||
h & identifier;
|
||||
h & name;
|
||||
h & levels;
|
||||
}
|
||||
|
||||
friend class CSkillHandler;
|
||||
friend DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill & skill);
|
||||
friend DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill::LevelInfo & info);
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CSkillHandler: public CHandlerBase<SecondarySkill, CSkill>
|
||||
{
|
||||
public:
|
||||
CSkillHandler();
|
||||
virtual ~CSkillHandler();
|
||||
|
||||
///IHandler base
|
||||
std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
|
||||
void afterLoadFinalization() override;
|
||||
void beforeValidate(JsonNode & object) override;
|
||||
|
||||
std::vector<bool> getDefaultAllowed() const override;
|
||||
const std::string getTypeName() const override;
|
||||
|
||||
const std::string & skillInfo(int skill, int level) const;
|
||||
const std::string & skillName(int skill) const;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & objects;
|
||||
}
|
||||
|
||||
protected:
|
||||
CSkill * loadFromJson(const JsonNode & json, const std::string & identifier) override;
|
||||
};
|
@ -748,7 +748,7 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
|
||||
auto & advMap = data["town"]["adventureMap"];
|
||||
if (!advMap.isNull())
|
||||
{
|
||||
logGlobal->warn("Outdated town mod. Will try to generate valid templates out of fort");
|
||||
logMod->warn("Outdated town mod. Will try to generate valid templates out of fort");
|
||||
JsonNode config;
|
||||
config["animation"] = advMap["castle"];
|
||||
VLC->objtypeh->getHandlerFor(index, object->index)->addTemplate(config);
|
||||
@ -763,7 +763,10 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
|
||||
{
|
||||
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
|
||||
object->index = index;
|
||||
assert(factions[index] == nullptr); // ensure that this id was not loaded before
|
||||
if (factions.size() > index)
|
||||
assert(factions[index] == nullptr); // ensure that this id was not loaded before
|
||||
else
|
||||
factions.resize(index + 1);
|
||||
factions[index] = object;
|
||||
|
||||
if (object->town)
|
||||
@ -815,9 +818,9 @@ void CTownHandler::initializeRequirements()
|
||||
{
|
||||
if (node.Vector().size() > 1)
|
||||
{
|
||||
logGlobal->warn("Unexpected length of town buildings requirements: %d", node.Vector().size());
|
||||
logGlobal->warn("Entry contains: ");
|
||||
logGlobal->warn(node.toJson());
|
||||
logMod->warn("Unexpected length of town buildings requirements: %d", node.Vector().size());
|
||||
logMod->warn("Entry contains: ");
|
||||
logMod->warn(node.toJson());
|
||||
}
|
||||
return BuildingID(VLC->modh->identifiers.getIdentifier(requirement.town->getBuildingScope(), node.Vector()[0]).get());
|
||||
});
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "CArtHandler.h"
|
||||
#include "CCreatureHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
#include "StringConstants.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
|
||||
@ -65,6 +66,11 @@ const CSpell * SpellID::toSpell() const
|
||||
return VLC->spellh->objects[*this];
|
||||
}
|
||||
|
||||
const CSkill * SecondarySkill::toSkill() const
|
||||
{
|
||||
return VLC->skillh->objects.at(*this);
|
||||
}
|
||||
|
||||
//template std::ostream & operator << <ArtifactInstanceID>(std::ostream & os, BaseForID<ArtifactInstanceID> id);
|
||||
//template std::ostream & operator << <ObjectInstanceID>(std::ostream & os, BaseForID<ObjectInstanceID> id);
|
||||
|
||||
|
@ -62,6 +62,7 @@ class CArtifactInstance;
|
||||
class CCreature;
|
||||
class CHero;
|
||||
class CSpell;
|
||||
class CSkill;
|
||||
class CGameInfoCallback;
|
||||
class CNonConstInfoCallback;
|
||||
|
||||
@ -320,6 +321,8 @@ public:
|
||||
SecondarySkill(ESecondarySkill _num = WRONG) : num(_num)
|
||||
{}
|
||||
|
||||
DLL_LINKAGE const CSkill * toSkill() const;
|
||||
|
||||
ID_LIKE_CLASS_COMMON(SecondarySkill, ESecondarySkill)
|
||||
|
||||
ESecondarySkill num;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "CCreatureSet.h"
|
||||
#include "CHeroHandler.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
#include "CStack.h"
|
||||
#include "CArtHandler.h"
|
||||
|
||||
@ -1078,7 +1079,7 @@ std::string Bonus::Description() const
|
||||
str << VLC->creh->creatures[sid]->namePl;
|
||||
break;
|
||||
case SECONDARY_SKILL:
|
||||
str << VLC->generaltexth->skillName[sid]/* << " secondary skill"*/;
|
||||
str << VLC->skillh->skillName(sid);
|
||||
break;
|
||||
default:
|
||||
//todo: handle all possible sources
|
||||
@ -1166,6 +1167,11 @@ namespace Selector
|
||||
return CSelectFieldEqual<Bonus::BonusSource>(&Bonus::source)(source);
|
||||
}
|
||||
|
||||
CSelector DLL_LINKAGE valueType(Bonus::ValueType valType)
|
||||
{
|
||||
return CSelectFieldEqual<Bonus::ValueType>(&Bonus::valType)(valType);
|
||||
}
|
||||
|
||||
DLL_LINKAGE CSelector all([](const Bonus * b){return true;});
|
||||
DLL_LINKAGE CSelector none([](const Bonus * b){return false;});
|
||||
|
||||
@ -1215,6 +1221,8 @@ const CCreature * retrieveCreature(const CBonusSystemNode *node)
|
||||
{
|
||||
case CBonusSystemNode::CREATURE:
|
||||
return (static_cast<const CCreature *>(node));
|
||||
case CBonusSystemNode::STACK_BATTLE:
|
||||
return (static_cast<const CStack*>(node))->type;
|
||||
default:
|
||||
const CStackInstance *csi = retreiveStackInstance(node);
|
||||
if(csi)
|
||||
|
@ -234,13 +234,16 @@ private:
|
||||
BONUS_NAME(SUMMON_GUARDIANS) /*val - amount in % of stack count, subtype = creature ID*/\
|
||||
BONUS_NAME(CATAPULT_EXTRA_SHOTS) /*val - number of additional shots, requires CATAPULT bonus to work*/\
|
||||
BONUS_NAME(RANGED_RETALIATION) /*allows shooters to perform ranged retaliation*/\
|
||||
BONUS_NAME(BLOCKS_RANGED_RETALIATION) /*disallows ranged retaliation for shooter unit, BLOCKS_RETALIATION bonus is for melee retaliation only*/\
|
||||
BONUS_NAME(BLOCKS_RANGED_RETALIATION) /*disallows ranged retaliation for shooter unit, BLOCKS_RETALIATION bonus is for melee retaliation only*/
|
||||
BONUS_NAME(SECONDARY_SKILL_VAL2) /*for secondary skills that have multiple effects, like eagle eye (max level and chance)*/ \
|
||||
BONUS_NAME(MANUAL_CONTROL) /* manually control warmachine with id = subtype, chance = val */ \
|
||||
BONUS_NAME(WIDE_BREATH) /* initial desigh: dragon breath affecting multiple nearby hexes */\
|
||||
BONUS_NAME(FIRST_STRIKE) /* first counterattack, then attack if possible */\
|
||||
BONUS_NAME(SYNERGY_TARGET) /* dummy skill for alternative upgrades mod */\
|
||||
BONUS_NAME(SHOOTS_ALL_ADJACENT) /* H4 Cyclops-like shoot (attacks all hexes neighboring with target) without spell-like mechanics */\
|
||||
BONUS_NAME(BLOCK_MAGIC_BELOW) /*blocks casting spells of the level < value */ \
|
||||
BONUS_NAME(TERMINATOR) /*kills extra units after hit, subtype = 0 - kill percentage of units, 1 - kill amount, val = chance in percent to trigger, additional info - amount/percentage to kill*/ \
|
||||
|
||||
/* end of list */
|
||||
|
||||
|
||||
@ -956,6 +959,7 @@ namespace Selector
|
||||
CSelector DLL_LINKAGE typeSubtypeInfo(Bonus::BonusType type, TBonusSubtype subtype, si32 info);
|
||||
CSelector DLL_LINKAGE source(Bonus::BonusSource source, ui32 sourceID);
|
||||
CSelector DLL_LINKAGE sourceTypeSel(Bonus::BonusSource source);
|
||||
CSelector DLL_LINKAGE valueType(Bonus::ValueType valType);
|
||||
|
||||
/**
|
||||
* Selects all bonuses
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "CHeroHandler.h" // for CHeroHandler
|
||||
#include "spells/CSpellHandler.h"// for CSpell
|
||||
#include "CSkillHandler.h"// for CSkill
|
||||
#include "NetPacks.h"
|
||||
#include "CBonusTypeHandler.h"
|
||||
#include "CModHandler.h"
|
||||
|
@ -82,12 +82,10 @@ public:
|
||||
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
|
||||
object->id = _ObjectID(index);
|
||||
|
||||
|
||||
assert(objects[index] == nullptr); // ensure that this id was not loaded before
|
||||
objects[index] = object;
|
||||
|
||||
registerObject(scope,type_name, name, object->id);
|
||||
|
||||
}
|
||||
|
||||
ConstTransitivePtr<_Object> operator[] (const _ObjectID id) const
|
||||
@ -96,7 +94,7 @@ public:
|
||||
|
||||
if (raw_id < 0 || raw_id >= objects.size())
|
||||
{
|
||||
logGlobal->error("%s id %d is invalid", getTypeName(), static_cast<si64>(raw_id));
|
||||
logMod->error("%s id %d is invalid", getTypeName(), static_cast<si64>(raw_id));
|
||||
throw std::runtime_error("internal error");
|
||||
}
|
||||
|
||||
|
@ -163,8 +163,8 @@ JsonNode JsonParser::parse(std::string fileName)
|
||||
|
||||
if (!errors.empty())
|
||||
{
|
||||
logGlobal->warn("File %s is not a valid JSON file!", fileName);
|
||||
logGlobal->warn(errors);
|
||||
logMod->warn("File %s is not a valid JSON file!", fileName);
|
||||
logMod->warn(errors);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
@ -395,7 +395,7 @@ std::shared_ptr<Bonus> JsonUtils::parseBonus (const JsonVector &ability_vec) //T
|
||||
auto it = bonusNameMap.find(type);
|
||||
if (it == bonusNameMap.end())
|
||||
{
|
||||
logGlobal->error("Error: invalid ability type %s", type);
|
||||
logMod->error("Error: invalid ability type %s", type);
|
||||
return b;
|
||||
}
|
||||
b->type = it->second;
|
||||
@ -413,7 +413,7 @@ const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val,
|
||||
auto it = map.find(type);
|
||||
if (it == map.end())
|
||||
{
|
||||
logGlobal->error("Error: invalid %s%s", err, type);
|
||||
logMod->error("Error: invalid %s%s", err, type);
|
||||
return defaultValue;
|
||||
}
|
||||
else
|
||||
@ -445,7 +445,7 @@ void JsonUtils::resolveIdentifier(si32 &var, const JsonNode &node, std::string n
|
||||
});
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("Error! Wrong identifier used for value of %s", name);
|
||||
logMod->error("Error! Wrong identifier used for value of %s", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -467,7 +467,7 @@ void JsonUtils::resolveIdentifier(const JsonNode &node, si32 &var)
|
||||
});
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("Error! Wrong identifier used for identifier!");
|
||||
logMod->error("Error! Wrong identifier used for identifier!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -489,7 +489,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
auto it = bonusNameMap.find(type);
|
||||
if (it == bonusNameMap.end())
|
||||
{
|
||||
logGlobal->error("Error: invalid ability type %s", type);
|
||||
logMod->error("Error: invalid ability type %s", type);
|
||||
return false;
|
||||
}
|
||||
b->type = it->second;
|
||||
@ -533,7 +533,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("Error! Wrong bonus duration format.");
|
||||
logMod->error("Error! Wrong bonus duration format.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,7 +580,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
auto it = bonusNameMap.find(anotherBonusType);
|
||||
if (it == bonusNameMap.end())
|
||||
{
|
||||
logGlobal->error("Error: invalid ability type %s", anotherBonusType);
|
||||
logMod->error("Error: invalid ability type %s", anotherBonusType);
|
||||
continue;
|
||||
}
|
||||
l2->type = it->second;
|
||||
@ -723,8 +723,8 @@ bool JsonUtils::validate(const JsonNode &node, std::string schemaName, std::stri
|
||||
std::string log = Validation::check(schemaName, node);
|
||||
if (!log.empty())
|
||||
{
|
||||
logGlobal->warn("Data in %s is invalid!", dataName);
|
||||
logGlobal->warn(log);
|
||||
logMod->warn("Data in %s is invalid!", dataName);
|
||||
logMod->warn(log);
|
||||
}
|
||||
return log.empty();
|
||||
}
|
||||
@ -745,7 +745,7 @@ const JsonNode & getSchemaByName(std::string name)
|
||||
return loadedSchemas[name];
|
||||
}
|
||||
|
||||
logGlobal->error("Error: missing schema with name %s!", name);
|
||||
logMod->error("Error: missing schema with name %s!", name);
|
||||
assert(0);
|
||||
return nullNode;
|
||||
}
|
||||
@ -756,7 +756,7 @@ const JsonNode & JsonUtils::getSchema(std::string URI)
|
||||
size_t posHash = URI.find('#');
|
||||
if(posColon == std::string::npos)
|
||||
{
|
||||
logGlobal->error("Invalid schema URI:%s", URI);
|
||||
logMod->error("Invalid schema URI:%s", URI);
|
||||
return nullNode;
|
||||
}
|
||||
|
||||
@ -765,7 +765,7 @@ const JsonNode & JsonUtils::getSchema(std::string URI)
|
||||
|
||||
if(protocolName != "vcmi")
|
||||
{
|
||||
logGlobal->error("Error: unsupported URI protocol for schema: %s", URI);
|
||||
logMod->error("Error: unsupported URI protocol for schema: %s", URI);
|
||||
return nullNode;
|
||||
}
|
||||
|
||||
|
@ -271,7 +271,7 @@ bfs::path VCMIDirsWIN32::userDataPath() const
|
||||
wchar_t profileDir[MAX_PATH];
|
||||
|
||||
if (SHGetSpecialFolderPathW(nullptr, profileDir, CSIDL_MYDOCUMENTS, FALSE) != FALSE)
|
||||
return bfs::path(profileDir) / "My Games\\vcmi";
|
||||
return bfs::path(profileDir) / "My Games" / "vcmi";
|
||||
|
||||
return ".";
|
||||
}
|
||||
@ -350,8 +350,16 @@ class IVCMIDirsUNIX : public IVCMIDirs
|
||||
boost::filesystem::path serverPath() const override;
|
||||
|
||||
std::string genHelpString() const override;
|
||||
|
||||
bool developmentMode() const;
|
||||
};
|
||||
|
||||
bool IVCMIDirsUNIX::developmentMode() const
|
||||
{
|
||||
// We want to be able to run VCMI from single directory. E.g to run from build output directory
|
||||
return bfs::exists("AI") && bfs::exists("config") && bfs::exists("Mods") && bfs::exists("vcmiserver") && bfs::exists("vcmiclient");
|
||||
}
|
||||
|
||||
bfs::path IVCMIDirsUNIX::clientPath() const { return binaryPath() / "vcmiclient"; }
|
||||
bfs::path IVCMIDirsUNIX::serverPath() const { return binaryPath() / "vcmiserver"; }
|
||||
|
||||
@ -454,7 +462,7 @@ std::vector<bfs::path> VCMIDirsOSX::dataPaths() const
|
||||
{
|
||||
std::vector<bfs::path> ret;
|
||||
//FIXME: need some proper codepath for detecting running from build output directory
|
||||
if(bfs::exists("config") && bfs::exists("Mods") && bfs::exists("vcmiserver"))
|
||||
if(developmentMode())
|
||||
{
|
||||
ret.push_back(".");
|
||||
}
|
||||
@ -489,9 +497,9 @@ bfs::path VCMIDirsXDG::userDataPath() const
|
||||
{
|
||||
// $XDG_DATA_HOME, default: $HOME/.local/share
|
||||
const char* homeDir;
|
||||
if ((homeDir = getenv("XDG_DATA_HOME")))
|
||||
return homeDir;
|
||||
else if ((homeDir = getenv("HOME")))
|
||||
if((homeDir = getenv("XDG_DATA_HOME")))
|
||||
return bfs::path(homeDir) / "vcmi";
|
||||
else if((homeDir = getenv("HOME")))
|
||||
return bfs::path(homeDir) / ".local" / "share" / "vcmi";
|
||||
else
|
||||
return ".";
|
||||
@ -499,7 +507,7 @@ bfs::path VCMIDirsXDG::userDataPath() const
|
||||
bfs::path VCMIDirsXDG::userCachePath() const
|
||||
{
|
||||
// $XDG_CACHE_HOME, default: $HOME/.cache
|
||||
const char* tempResult;
|
||||
const char * tempResult;
|
||||
if ((tempResult = getenv("XDG_CACHE_HOME")))
|
||||
return bfs::path(tempResult) / "vcmi";
|
||||
else if ((tempResult = getenv("HOME")))
|
||||
@ -510,7 +518,7 @@ bfs::path VCMIDirsXDG::userCachePath() const
|
||||
bfs::path VCMIDirsXDG::userConfigPath() const
|
||||
{
|
||||
// $XDG_CONFIG_HOME, default: $HOME/.config
|
||||
const char* tempResult;
|
||||
const char * tempResult;
|
||||
if ((tempResult = getenv("XDG_CONFIG_HOME")))
|
||||
return bfs::path(tempResult) / "vcmi";
|
||||
else if ((tempResult = getenv("HOME")))
|
||||
@ -528,34 +536,51 @@ std::vector<bfs::path> VCMIDirsXDG::dataPaths() const
|
||||
// in vcmi fs last directory has highest priority
|
||||
std::vector<bfs::path> ret;
|
||||
|
||||
const char* tempResult;
|
||||
ret.push_back(M_DATA_DIR);
|
||||
|
||||
//FIXME: need some proper codepath for detecting running from build output directory
|
||||
if(bfs::exists("config") && bfs::exists("Mods") && bfs::exists("vcmiserver"))
|
||||
if(developmentMode())
|
||||
{
|
||||
//For now we'll disable usage of system directories when VCMI running from bin directory
|
||||
ret.push_back(".");
|
||||
}
|
||||
else if((tempResult = getenv("XDG_DATA_DIRS")) != nullptr)
|
||||
{
|
||||
std::string dataDirsEnv = tempResult;
|
||||
std::vector<std::string> dataDirs;
|
||||
boost::split(dataDirs, dataDirsEnv, boost::is_any_of(":"));
|
||||
for (auto & entry : boost::adaptors::reverse(dataDirs))
|
||||
ret.push_back(entry + "/vcmi");
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.push_back("/usr/share/");
|
||||
ret.push_back("/usr/local/share/");
|
||||
ret.push_back(M_DATA_DIR);
|
||||
const char * tempResult;
|
||||
if((tempResult = getenv("XDG_DATA_DIRS")) != nullptr)
|
||||
{
|
||||
std::string dataDirsEnv = tempResult;
|
||||
std::vector<std::string> dataDirs;
|
||||
boost::split(dataDirs, dataDirsEnv, boost::is_any_of(":"));
|
||||
for (auto & entry : boost::adaptors::reverse(dataDirs))
|
||||
ret.push_back(bfs::path(entry) / "vcmi");
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.push_back(bfs::path("/usr/share") / "vcmi");
|
||||
ret.push_back(bfs::path("/usr/local/share") / "vcmi");
|
||||
}
|
||||
|
||||
// Debian and other distributions might want to use it while it's not part of XDG
|
||||
ret.push_back(bfs::path("/usr/share/games") / "vcmi");
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bfs::path VCMIDirsXDG::libraryPath() const { return M_LIB_DIR; }
|
||||
bfs::path VCMIDirsXDG::binaryPath() const { return M_BIN_DIR; }
|
||||
bfs::path VCMIDirsXDG::libraryPath() const
|
||||
{
|
||||
if(developmentMode())
|
||||
return ".";
|
||||
else
|
||||
return M_LIB_DIR;
|
||||
}
|
||||
|
||||
bfs::path VCMIDirsXDG::binaryPath() const
|
||||
{
|
||||
if(developmentMode())
|
||||
return ".";
|
||||
else
|
||||
return M_BIN_DIR;
|
||||
}
|
||||
|
||||
std::string VCMIDirsXDG::libraryName(const std::string& basename) const { return "lib" + basename + ".so"; }
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "CTownHandler.h"
|
||||
#include "CBuildingHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "CModHandler.h"
|
||||
#include "IGameEventsReceiver.h"
|
||||
@ -113,6 +114,8 @@ void LibClasses::init()
|
||||
|
||||
createHandler(spellh, "Spell", pomtime);
|
||||
|
||||
createHandler(skillh, "Skill", pomtime);
|
||||
|
||||
createHandler(terviewh, "Terrain view pattern", pomtime);
|
||||
|
||||
createHandler(tplh, "Template", pomtime); //templates need already resolved identifiers (refactor?)
|
||||
@ -137,6 +140,7 @@ void LibClasses::clear()
|
||||
delete objh;
|
||||
delete objtypeh;
|
||||
delete spellh;
|
||||
delete skillh;
|
||||
delete modh;
|
||||
delete bth;
|
||||
delete tplh;
|
||||
@ -154,6 +158,7 @@ void LibClasses::makeNull()
|
||||
objh = nullptr;
|
||||
objtypeh = nullptr;
|
||||
spellh = nullptr;
|
||||
skillh = nullptr;
|
||||
modh = nullptr;
|
||||
bth = nullptr;
|
||||
tplh = nullptr;
|
||||
|
@ -14,6 +14,7 @@ class CArtHandler;
|
||||
class CHeroHandler;
|
||||
class CCreatureHandler;
|
||||
class CSpellHandler;
|
||||
class CSkillHandler;
|
||||
class CBuildingHandler;
|
||||
class CObjectHandler;
|
||||
class CObjectClassesHandler;
|
||||
@ -41,6 +42,7 @@ public:
|
||||
CHeroHandler * heroh;
|
||||
CCreatureHandler * creh;
|
||||
CSpellHandler * spellh;
|
||||
CSkillHandler * skillh;
|
||||
CObjectHandler * objh;
|
||||
CObjectClassesHandler * objtypeh;
|
||||
CTownHandler * townh;
|
||||
@ -67,6 +69,10 @@ public:
|
||||
h & objh;
|
||||
h & objtypeh;
|
||||
h & spellh;
|
||||
if(version >= 777)
|
||||
{
|
||||
h & skillh;
|
||||
}
|
||||
h & modh;
|
||||
h & IS_AI_ENABLED;
|
||||
h & bth;
|
||||
|
@ -125,30 +125,6 @@
|
||||
<Unit filename="../Version.h" />
|
||||
<Unit filename="../include/vstd/CLoggerBase.h" />
|
||||
<Unit filename="AI_Base.h" />
|
||||
<Unit filename="battle/BattleAction.cpp" />
|
||||
<Unit filename="battle/BattleAction.h" />
|
||||
<Unit filename="battle/BattleHex.cpp" />
|
||||
<Unit filename="battle/BattleHex.h" />
|
||||
<Unit filename="battle/BattleInfo.cpp" />
|
||||
<Unit filename="battle/BattleInfo.h" />
|
||||
<Unit filename="battle/AccessibilityInfo.cpp" />
|
||||
<Unit filename="battle/AccessibilityInfo.h" />
|
||||
<Unit filename="battle/BattleAttackInfo.cpp" />
|
||||
<Unit filename="battle/BattleAttackInfo.h" />
|
||||
<Unit filename="battle/CBattleInfoCallback.cpp" />
|
||||
<Unit filename="battle/CBattleInfoCallback.h" />
|
||||
<Unit filename="battle/CBattleInfoEssentials.cpp" />
|
||||
<Unit filename="battle/CBattleInfoEssentials.h" />
|
||||
<Unit filename="battle/CCallbackBase.cpp" />
|
||||
<Unit filename="battle/CCallbackBase.h" />
|
||||
<Unit filename="battle/CPlayerBattleCallback.cpp" />
|
||||
<Unit filename="battle/CPlayerBattleCallback.h" />
|
||||
<Unit filename="battle/ReachabilityInfo.cpp" />
|
||||
<Unit filename="battle/ReachabilityInfo.h" />
|
||||
<Unit filename="battle/SideInBattle.cpp" />
|
||||
<Unit filename="battle/SideInBattle.h" />
|
||||
<Unit filename="battle/SiegeInfo.cpp" />
|
||||
<Unit filename="battle/SiegeInfo.h" />
|
||||
<Unit filename="CArtHandler.cpp" />
|
||||
<Unit filename="CArtHandler.h" />
|
||||
<Unit filename="CBonusTypeHandler.cpp" />
|
||||
@ -177,14 +153,14 @@
|
||||
<Unit filename="CMakeLists.txt" />
|
||||
<Unit filename="CModHandler.cpp" />
|
||||
<Unit filename="CModHandler.h" />
|
||||
<Unit filename="battle/CObstacleInstance.cpp" />
|
||||
<Unit filename="battle/CObstacleInstance.h" />
|
||||
<Unit filename="CPathfinder.cpp" />
|
||||
<Unit filename="CPathfinder.h" />
|
||||
<Unit filename="CPlayerState.h" />
|
||||
<Unit filename="CRandomGenerator.cpp" />
|
||||
<Unit filename="CRandomGenerator.h" />
|
||||
<Unit filename="CScriptingModule.h" />
|
||||
<Unit filename="CSkillHandler.cpp" />
|
||||
<Unit filename="CSkillHandler.h" />
|
||||
<Unit filename="CSoundBase.h" />
|
||||
<Unit filename="CStack.cpp" />
|
||||
<Unit filename="CStack.h" />
|
||||
@ -230,6 +206,32 @@
|
||||
<Unit filename="VCMIDirs.h" />
|
||||
<Unit filename="VCMI_Lib.cpp" />
|
||||
<Unit filename="VCMI_Lib.h" />
|
||||
<Unit filename="battle/AccessibilityInfo.cpp" />
|
||||
<Unit filename="battle/AccessibilityInfo.h" />
|
||||
<Unit filename="battle/BattleAction.cpp" />
|
||||
<Unit filename="battle/BattleAction.h" />
|
||||
<Unit filename="battle/BattleAttackInfo.cpp" />
|
||||
<Unit filename="battle/BattleAttackInfo.h" />
|
||||
<Unit filename="battle/BattleHex.cpp" />
|
||||
<Unit filename="battle/BattleHex.h" />
|
||||
<Unit filename="battle/BattleInfo.cpp" />
|
||||
<Unit filename="battle/BattleInfo.h" />
|
||||
<Unit filename="battle/CBattleInfoCallback.cpp" />
|
||||
<Unit filename="battle/CBattleInfoCallback.h" />
|
||||
<Unit filename="battle/CBattleInfoEssentials.cpp" />
|
||||
<Unit filename="battle/CBattleInfoEssentials.h" />
|
||||
<Unit filename="battle/CCallbackBase.cpp" />
|
||||
<Unit filename="battle/CCallbackBase.h" />
|
||||
<Unit filename="battle/CObstacleInstance.cpp" />
|
||||
<Unit filename="battle/CObstacleInstance.h" />
|
||||
<Unit filename="battle/CPlayerBattleCallback.cpp" />
|
||||
<Unit filename="battle/CPlayerBattleCallback.h" />
|
||||
<Unit filename="battle/ReachabilityInfo.cpp" />
|
||||
<Unit filename="battle/ReachabilityInfo.h" />
|
||||
<Unit filename="battle/SideInBattle.cpp" />
|
||||
<Unit filename="battle/SideInBattle.h" />
|
||||
<Unit filename="battle/SiegeInfo.cpp" />
|
||||
<Unit filename="battle/SiegeInfo.h" />
|
||||
<Unit filename="filesystem/AdapterLoaders.cpp" />
|
||||
<Unit filename="filesystem/AdapterLoaders.h" />
|
||||
<Unit filename="filesystem/CArchiveLoader.cpp" />
|
||||
|
@ -601,14 +601,14 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
|
||||
for(int i = 0; i < ARRAY_COUNT(tacticLvls); i++)
|
||||
{
|
||||
if(heroes[i])
|
||||
tacticLvls[i] += heroes[i]->getSecSkillLevel(SecondarySkill::TACTICS);
|
||||
tacticLvls[i] += heroes[i]->valOfBonuses(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::TACTICS));
|
||||
}
|
||||
int tacticsSkillDiff = tacticLvls[0] - tacticLvls[1];
|
||||
|
||||
if(tacticsSkillDiff && isTacticsAllowed)
|
||||
{
|
||||
curB->tacticsSide = tacticsSkillDiff < 0;
|
||||
curB->tacticDistance = std::abs(tacticsSkillDiff)*2 + 1;
|
||||
curB->tacticDistance = std::abs(tacticsSkillDiff);
|
||||
}
|
||||
else
|
||||
curB->tacticDistance = 0;
|
||||
|
@ -26,8 +26,9 @@ CFilesystemLoader::CFilesystemLoader(std::string _mountPoint, bfs::path baseDire
|
||||
std::unique_ptr<CInputStream> CFilesystemLoader::load(const ResourceID & resourceName) const
|
||||
{
|
||||
assert(fileList.count(resourceName));
|
||||
|
||||
return make_unique<CFileInputStream>(baseDirectory / fileList.at(resourceName));
|
||||
bfs::path file = baseDirectory / fileList.at(resourceName);
|
||||
logGlobal->trace("loading %s", file.string());
|
||||
return make_unique<CFileInputStream>(file);
|
||||
}
|
||||
|
||||
bool CFilesystemLoader::existsResource(const ResourceID & resourceName) const
|
||||
|
@ -83,6 +83,7 @@ DLL_LINKAGE vstd::CLoggerBase * logBonus = CLogger::getLogger(CLoggerDomain("bon
|
||||
DLL_LINKAGE vstd::CLoggerBase * logNetwork = CLogger::getLogger(CLoggerDomain("network"));
|
||||
DLL_LINKAGE vstd::CLoggerBase * logAi = CLogger::getLogger(CLoggerDomain("ai"));
|
||||
DLL_LINKAGE vstd::CLoggerBase * logAnim = CLogger::getLogger(CLoggerDomain("animation"));
|
||||
DLL_LINKAGE vstd::CLoggerBase * logMod = CLogger::getLogger(CLoggerDomain("mod"));
|
||||
|
||||
CLogger * CLogger::getLogger(const CLoggerDomain & domain)
|
||||
{
|
||||
|
@ -266,7 +266,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
{
|
||||
const CSpell * spell = spellId.toSpell();
|
||||
iw.text.addTxt (MetaString::SPELL_NAME, spellId);
|
||||
if(spell->level <= hero->getSecSkillLevel(SecondarySkill::WISDOM) + 2)
|
||||
if(spell->level <= hero->maxSpellLevel())
|
||||
{
|
||||
if(hero->canLearnSpell(spell))
|
||||
{
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "../CModHandler.h"
|
||||
#include "../CSoundBase.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CSkillHandler.h"
|
||||
#include "CObjectClassesHandler.h"
|
||||
#include "../IGameCallback.h"
|
||||
#include "../CGameState.h"
|
||||
@ -86,7 +87,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro
|
||||
else if(ti->nativeTerrain != from.terType && !ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType))
|
||||
{
|
||||
ret = VLC->heroh->terrCosts[from.terType];
|
||||
ret -= getSecSkillLevel(SecondarySkill::PATHFINDING) * 25;
|
||||
ret -= valOfBonuses(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::PATHFINDING));
|
||||
if(ret < GameConstants::BASE_MOVEMENT_COST)
|
||||
ret = GameConstants::BASE_MOVEMENT_COST;
|
||||
}
|
||||
@ -760,91 +761,26 @@ void CGHeroInstance::recreateSecondarySkillsBonuses()
|
||||
removeBonus(bonus);
|
||||
|
||||
for(auto skill_info : secSkills)
|
||||
updateSkill(SecondarySkill(skill_info.first), skill_info.second);
|
||||
for(int level = 1; level <= skill_info.second; level++)
|
||||
updateSkill(SecondarySkill(skill_info.first), level);
|
||||
}
|
||||
|
||||
void CGHeroInstance::updateSkill(SecondarySkill which, int val)
|
||||
{
|
||||
if(which == SecondarySkill::LEADERSHIP || which == SecondarySkill::LUCK)
|
||||
{ //luck-> VLC->generaltexth->arraytxt[73+luckSkill]; VLC->generaltexth->arraytxt[104+moraleSkill]
|
||||
bool luck = which == SecondarySkill::LUCK;
|
||||
Bonus::BonusType type[] = {Bonus::MORALE, Bonus::LUCK};
|
||||
|
||||
auto b = getBonusLocalFirst(Selector::type(type[luck]).And(Selector::sourceType(Bonus::SECONDARY_SKILL)));
|
||||
if(!b)
|
||||
{
|
||||
b = std::make_shared<Bonus>(Bonus::PERMANENT, type[luck], Bonus::SECONDARY_SKILL, +val, which, which, Bonus::BASE_NUMBER);
|
||||
addNewBonus(b);
|
||||
}
|
||||
auto skillBonus = (*VLC->skillh)[which]->getBonus(val);
|
||||
for (auto b : skillBonus)
|
||||
{
|
||||
// bonuses provided by different levels of a secondary skill are aggregated via max (not + as usual)
|
||||
// different secondary skills providing the same bonus (e.g. ballistics might improve archery as well) are kept separate
|
||||
std::shared_ptr<Bonus> existing = getBonusLocalFirst(
|
||||
Selector::typeSubtype(b->type, b->subtype).And(
|
||||
Selector::source(Bonus::SECONDARY_SKILL, b->sid).And(
|
||||
Selector::valueType(b->valType))));
|
||||
if(existing)
|
||||
vstd::amax(existing->val, b->val);
|
||||
else
|
||||
b->val = +val;
|
||||
addNewBonus(std::make_shared<Bonus>(*b));
|
||||
}
|
||||
else if(which == SecondarySkill::DIPLOMACY) //surrender discount: 20% per level
|
||||
{
|
||||
|
||||
if(auto b = getBonusLocalFirst(Selector::type(Bonus::SURRENDER_DISCOUNT).And(Selector::sourceType(Bonus::SECONDARY_SKILL))))
|
||||
b->val = +val;
|
||||
else
|
||||
addNewBonus(std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::SURRENDER_DISCOUNT, Bonus::SECONDARY_SKILL, val * 20, which));
|
||||
}
|
||||
|
||||
int skillVal = 0;
|
||||
switch (which)
|
||||
{
|
||||
case SecondarySkill::ARCHERY:
|
||||
switch (val)
|
||||
{
|
||||
case 1:
|
||||
skillVal = 10; break;
|
||||
case 2:
|
||||
skillVal = 25; break;
|
||||
case 3:
|
||||
skillVal = 50; break;
|
||||
}
|
||||
break;
|
||||
case SecondarySkill::LOGISTICS:
|
||||
skillVal = 10 * val; break;
|
||||
case SecondarySkill::NAVIGATION:
|
||||
skillVal = 50 * val; break;
|
||||
case SecondarySkill::MYSTICISM:
|
||||
skillVal = val; break;
|
||||
case SecondarySkill::EAGLE_EYE:
|
||||
skillVal = 30 + 10 * val; break;
|
||||
case SecondarySkill::NECROMANCY:
|
||||
skillVal = 10 * val; break;
|
||||
case SecondarySkill::LEARNING:
|
||||
skillVal = 5 * val; break;
|
||||
case SecondarySkill::OFFENCE:
|
||||
skillVal = 10 * val; break;
|
||||
case SecondarySkill::ARMORER:
|
||||
skillVal = 5 * val; break;
|
||||
case SecondarySkill::INTELLIGENCE:
|
||||
skillVal = 25 << (val-1); break;
|
||||
case SecondarySkill::SORCERY:
|
||||
skillVal = 5 * val; break;
|
||||
case SecondarySkill::RESISTANCE:
|
||||
skillVal = 5 << (val-1); break;
|
||||
case SecondarySkill::FIRST_AID:
|
||||
skillVal = 25 + 25*val; break;
|
||||
case SecondarySkill::ESTATES:
|
||||
skillVal = 125 << (val-1); break;
|
||||
}
|
||||
|
||||
|
||||
Bonus::ValueType skillValType = skillVal ? Bonus::BASE_NUMBER : Bonus::INDEPENDENT_MIN;
|
||||
if(auto b = getExportedBonusList().getFirst(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, which)
|
||||
.And(Selector::sourceType(Bonus::SECONDARY_SKILL)))) //only local hero bonus
|
||||
{
|
||||
b->val = skillVal;
|
||||
b->valType = skillValType;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto bonus = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::SECONDARY_SKILL_PREMY, Bonus::SECONDARY_SKILL, skillVal, id.getNum(), which, skillValType);
|
||||
bonus->source = Bonus::SECONDARY_SKILL;
|
||||
addNewBonus(bonus);
|
||||
}
|
||||
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
void CGHeroInstance::setPropertyDer( ui8 what, ui32 val )
|
||||
@ -890,7 +826,9 @@ ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell, int *outSelectedSc
|
||||
|
||||
spell->forEachSchool([&, this](const SpellSchoolInfo & cnf, bool & stop)
|
||||
{
|
||||
int thisSchool = std::max<int>(getSecSkillLevel(cnf.skill), valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << ((ui8)cnf.id))); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
|
||||
int thisSchool = std::max<int>(
|
||||
valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, cnf.skill),
|
||||
valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << ((ui8)cnf.id))); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
|
||||
if(thisSchool > skill)
|
||||
{
|
||||
skill = thisSchool;
|
||||
@ -1021,7 +959,7 @@ bool CGHeroInstance::canLearnSpell(const CSpell * spell) const
|
||||
if(!hasSpellbook())
|
||||
return false;
|
||||
|
||||
if(spell->level > getSecSkillLevel(SecondarySkill::WISDOM) + 2) //not enough wisdom
|
||||
if(spell->level > maxSpellLevel()) //not enough wisdom
|
||||
return false;
|
||||
|
||||
if(vstd::contains(spells, spell->id))//already known
|
||||
@ -1135,7 +1073,7 @@ int3 CGHeroInstance::getSightCenter() const
|
||||
|
||||
int CGHeroInstance::getSightRadius() const
|
||||
{
|
||||
return 5 + getSecSkillLevel(SecondarySkill::SCOUTING) + valOfBonuses(Bonus::SIGHT_RADIOUS); //default + scouting
|
||||
return 5 + valOfBonuses(Bonus::SIGHT_RADIOUS); // scouting gives SIGHT_RADIUS bonus
|
||||
}
|
||||
|
||||
si32 CGHeroInstance::manaRegain() const
|
||||
@ -1143,7 +1081,7 @@ si32 CGHeroInstance::manaRegain() const
|
||||
if (hasBonusOfType(Bonus::FULL_MANA_REGENERATION))
|
||||
return manaLimit();
|
||||
|
||||
return 1 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 8) + valOfBonuses(Bonus::MANA_REGENERATION); //1 + Mysticism level
|
||||
return 1 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::MYSTICISM) + valOfBonuses(Bonus::MANA_REGENERATION); //1 + Mysticism level
|
||||
}
|
||||
|
||||
si32 CGHeroInstance::getManaNewTurn() const
|
||||
@ -1239,6 +1177,11 @@ bool CGHeroInstance::hasSpellbook() const
|
||||
return getArt(ArtifactPosition::SPELLBOOK);
|
||||
}
|
||||
|
||||
int CGHeroInstance::maxSpellLevel() const
|
||||
{
|
||||
return std::min(GameConstants::SPELL_LEVELS, 2 + valOfBonuses(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::WISDOM)));
|
||||
}
|
||||
|
||||
void CGHeroInstance::deserializationFix()
|
||||
{
|
||||
artDeserializationFix(this);
|
||||
|
@ -146,6 +146,7 @@ public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool hasSpellbook() const;
|
||||
int maxSpellLevel() const;
|
||||
EAlignment::EAlignment getAlignment() const;
|
||||
const std::string &getBiography() const;
|
||||
bool needsLastStack()const override;
|
||||
@ -302,5 +303,7 @@ public:
|
||||
h & visitedObjects;
|
||||
BONUS_TREE_DESERIALIZATION_FIX
|
||||
//visitied town pointer will be restored by map serialization method
|
||||
if(version < 777 && !h.saving)
|
||||
recreateSecondarySkillsBonuses();
|
||||
}
|
||||
};
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "../CSoundBase.h"
|
||||
#include "../CModHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CSkillHandler.h"
|
||||
#include "CObjectClassesHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../IGameCallback.h"
|
||||
@ -323,17 +324,18 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
|
||||
if(count*2 > totalCount)
|
||||
sympathy++; // 2 - hero have similar creatures more that 50%
|
||||
|
||||
int charisma = powerFactor + h->getSecSkillLevel(SecondarySkill::DIPLOMACY) + sympathy;
|
||||
int diplomacy = h->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::DIPLOMACY);
|
||||
int charisma = powerFactor + diplomacy + sympathy;
|
||||
|
||||
if(charisma < character)
|
||||
return FIGHT;
|
||||
|
||||
if (allowJoin)
|
||||
{
|
||||
if(h->getSecSkillLevel(SecondarySkill::DIPLOMACY) + sympathy + 1 >= character)
|
||||
if(diplomacy + sympathy + 1 >= character)
|
||||
return JOIN_FOR_FREE;
|
||||
|
||||
else if(h->getSecSkillLevel(SecondarySkill::DIPLOMACY) * 2 + sympathy + 1 >= character)
|
||||
else if(diplomacy * 2 + sympathy + 1 >= character)
|
||||
return VLC->creh->creatures[subID]->cost[6] * getStackCount(SlotID(0)); //join for gold
|
||||
}
|
||||
|
||||
@ -1475,7 +1477,7 @@ std::string CGWitchHut::getHoverText(PlayerColor player) const
|
||||
if(wasVisited(player))
|
||||
{
|
||||
hoverName += "\n" + VLC->generaltexth->allTexts[356]; // + (learn %s)
|
||||
boost::algorithm::replace_first(hoverName,"%s",VLC->generaltexth->skillName[ability]);
|
||||
boost::algorithm::replace_first(hoverName, "%s", VLC->skillh->skillName(ability));
|
||||
}
|
||||
return hoverName;
|
||||
}
|
||||
@ -1601,7 +1603,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,174);
|
||||
}
|
||||
else if(ID == Obj::SHRINE_OF_MAGIC_THOUGHT && !h->getSecSkillLevel(SecondarySkill::WISDOM)) //it's third level spell and hero doesn't have wisdom
|
||||
else if(ID == Obj::SHRINE_OF_MAGIC_THOUGHT && h->maxSpellLevel() < 3) //it's third level spell and hero doesn't have wisdom
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,130);
|
||||
}
|
||||
|
@ -101,6 +101,8 @@ void ObjectTemplate::afterLoadFixup()
|
||||
usedTiles[0][0] = VISITABLE;
|
||||
visitDir = 0xFF;
|
||||
}
|
||||
boost::algorithm::replace_all(animationFile, "\\", "/");
|
||||
boost::algorithm::replace_all(editorAnimationFile, "\\", "/");
|
||||
}
|
||||
|
||||
void ObjectTemplate::readTxt(CLegacyConfigParser & parser)
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../ConstTransitivePtr.h"
|
||||
#include "../GameConstants.h"
|
||||
|
||||
const ui32 SERIALIZATION_VERSION = 776;
|
||||
const ui32 SERIALIZATION_VERSION = 777;
|
||||
const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
|
||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
||||
|
||||
|
@ -819,7 +819,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string &
|
||||
|
||||
spell->name = json["name"].String();
|
||||
|
||||
logGlobal->trace("%s: loading spell %s", __FUNCTION__, spell->name);
|
||||
logMod->trace("%s: loading spell %s", __FUNCTION__, spell->name);
|
||||
|
||||
const auto schoolNames = json["school"];
|
||||
|
||||
@ -854,7 +854,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string &
|
||||
else if(targetType == "LOCATION")
|
||||
spell->targetType = CSpell::LOCATION;
|
||||
else
|
||||
logGlobal->warn("Spell %s: target type %s - assumed NO_TARGET.", spell->name, (targetType.empty() ? "empty" : "unknown ("+targetType+")"));
|
||||
logMod->warn("Spell %s: target type %s - assumed NO_TARGET.", spell->name, (targetType.empty() ? "empty" : "unknown ("+targetType+")"));
|
||||
|
||||
for(const auto & counteredSpell: json["counters"].Struct())
|
||||
if (counteredSpell.second.Bool())
|
||||
@ -899,7 +899,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string &
|
||||
else if(!implicitPositiveness)
|
||||
{
|
||||
spell->positiveness = CSpell::NEUTRAL; //duplicates constructor but, just in case
|
||||
logGlobal->error("Spell %s: no positiveness specified, assumed NEUTRAL.", spell->name);
|
||||
logMod->error("Spell %s: no positiveness specified, assumed NEUTRAL.", spell->name);
|
||||
}
|
||||
|
||||
spell->isSpecial = flags["special"].Bool();
|
||||
@ -909,7 +909,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string &
|
||||
auto it = bonusNameMap.find(name);
|
||||
if(it == bonusNameMap.end())
|
||||
{
|
||||
logGlobal->error("Spell %s: invalid bonus name %s", spell->name, name);
|
||||
logMod->error("Spell %s: invalid bonus name %s", spell->name, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
75
osx/DS_Store_Setup.scpt
Normal file
75
osx/DS_Store_Setup.scpt
Normal file
@ -0,0 +1,75 @@
|
||||
-- Shamelessly taken from CMake source: Packaging/CMakeDMGSetup.scpt
|
||||
-- For licensing check cmake_modules/kitware license.txt
|
||||
--
|
||||
-- You can as well use this script to manually generate DS_Store
|
||||
-- First make DMG writable:
|
||||
-- hdiutil convert VCMI.dmg -format UDRW -o VCMI_writable.dmg
|
||||
-- Check current size of the image
|
||||
-- hdiutil resize -limits VCMI_writable.dmg
|
||||
-- Increase it size slightly so you can update .DS_Store
|
||||
-- Using 999999 will result in 512mb file though
|
||||
-- hdiutil resize -sectors 999999 VCMI_writable.dmg
|
||||
-- Attach it to /Volumes/VCMI/
|
||||
-- hdiutil attach VCMI_writable.dmg
|
||||
-- After run this script directly and it's will do the job
|
||||
-- osascript /path/to/vcmi/source/osx/DS_Store_Setup.scpt VCMI
|
||||
-- You should see icons moving and background appear
|
||||
-- Now /Volumes/VCMI/.DS_Store can be copied over to /path/to/vcmi/source/osx/dmg_DS_Stor
|
||||
|
||||
on run argv
|
||||
set image_name to item 1 of argv
|
||||
|
||||
tell application "Finder"
|
||||
tell disk image_name
|
||||
|
||||
-- wait for the image to finish mounting
|
||||
set open_attempts to 0
|
||||
repeat while open_attempts < 4
|
||||
try
|
||||
open
|
||||
delay 1
|
||||
set open_attempts to 5
|
||||
close
|
||||
on error errStr number errorNumber
|
||||
set open_attempts to open_attempts + 1
|
||||
delay 10
|
||||
end try
|
||||
end repeat
|
||||
delay 5
|
||||
|
||||
-- open the image the first time and save a DS_Store with just
|
||||
-- background and icon setup
|
||||
open
|
||||
set current view of container window to icon view
|
||||
set theViewOptions to the icon view options of container window
|
||||
set background picture of theViewOptions to file ".background:background.png"
|
||||
set arrangement of theViewOptions to not arranged
|
||||
set icon size of theViewOptions to 128
|
||||
delay 5
|
||||
close
|
||||
|
||||
-- next setup the position of the app and Applications symlink
|
||||
-- plus hide all the window decoration
|
||||
open
|
||||
update without registering applications
|
||||
tell container window
|
||||
set sidebar width to 0
|
||||
set statusbar visible to false
|
||||
set toolbar visible to false
|
||||
set the bounds to { 400, 100, 900, 423 }
|
||||
set position of item "VCMI.app" to { 133, 140 }
|
||||
set position of item "Applications" to { 378, 140 }
|
||||
end tell
|
||||
update without registering applications
|
||||
delay 5
|
||||
close
|
||||
|
||||
-- one last open and close so you can see everything looks correct
|
||||
open
|
||||
delay 5
|
||||
close
|
||||
|
||||
end tell
|
||||
delay 1
|
||||
end tell
|
||||
end run
|
BIN
osx/dmg_DS_Store
BIN
osx/dmg_DS_Store
Binary file not shown.
@ -592,12 +592,11 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
|
||||
|
||||
if (finishingBattle->winnerHero)
|
||||
{
|
||||
if (int eagleEyeLevel = finishingBattle->winnerHero->getSecSkillLevel(SecondarySkill::EAGLE_EYE))
|
||||
if (int eagleEyeLevel = finishingBattle->winnerHero->valOfBonuses(Bonus::SECONDARY_SKILL_VAL2, SecondarySkill::EAGLE_EYE))
|
||||
{
|
||||
int maxLevel = eagleEyeLevel + 1;
|
||||
double eagleEyeChance = finishingBattle->winnerHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::EAGLE_EYE);
|
||||
for (const CSpell *sp : gs->curB->sides.at(!battleResult.data->winner).usedSpellsHistory)
|
||||
if (sp->level <= maxLevel && !vstd::contains(finishingBattle->winnerHero->spells, sp->id) && getRandomGenerator().nextInt(99) < eagleEyeChance)
|
||||
if (sp->level <= eagleEyeLevel && !vstd::contains(finishingBattle->winnerHero->spells, sp->id) && getRandomGenerator().nextInt(99) < eagleEyeChance)
|
||||
cs.spells.insert(sp->id);
|
||||
}
|
||||
}
|
||||
@ -881,9 +880,8 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
|
||||
|
||||
if (att->getCreature()->idNumber == CreatureID::BALLISTA)
|
||||
{
|
||||
static const int artilleryLvlToChance[] = {0, 50, 75, 100};
|
||||
const CGHeroInstance * owner = gs->curB->getHero(att->owner);
|
||||
int chance = artilleryLvlToChance[owner->getSecSkillLevel(SecondarySkill::ARTILLERY)];
|
||||
int chance = owner->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::ARTILLERY);
|
||||
if (chance > getRandomGenerator().nextInt(99))
|
||||
{
|
||||
bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG;
|
||||
@ -1972,7 +1970,7 @@ void CGameHandler::giveSpells(const CGTownInstance *t, const CGHeroInstance *h)
|
||||
if (t->hasBuilt(BuildingID::GRAIL, ETownType::CONFLUX) && t->hasBuilt(BuildingID::MAGES_GUILD_1))
|
||||
{
|
||||
// Aurora Borealis give spells of all levels even if only level 1 mages guild built
|
||||
for (int i = 0; i < h->getSecSkillLevel(SecondarySkill::WISDOM)+2; i++)
|
||||
for (int i = 0; i < h->maxSpellLevel(); i++)
|
||||
{
|
||||
std::vector<SpellID> spells;
|
||||
getAllowedSpells(spells, i+1);
|
||||
@ -1982,7 +1980,7 @@ void CGameHandler::giveSpells(const CGTownInstance *t, const CGHeroInstance *h)
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < std::min(t->mageGuildLevel(), h->getSecSkillLevel(SecondarySkill::WISDOM)+2); i++)
|
||||
for (int i = 0; i < std::min(t->mageGuildLevel(), h->maxSpellLevel()); i++)
|
||||
{
|
||||
for (int j = 0; j < t->spellsAtLevel(i+1, true) && j < t->spells.at(i).size(); j++)
|
||||
{
|
||||
@ -2488,19 +2486,21 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t
|
||||
{
|
||||
const CGHeroInstance * h1 = getHero(fromHero);
|
||||
const CGHeroInstance * h2 = getHero(toHero);
|
||||
int h1_scholarLevel = h1->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::SCHOLAR);
|
||||
int h2_scholarLevel = h2->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::SCHOLAR);
|
||||
|
||||
if (h1->getSecSkillLevel(SecondarySkill::SCHOLAR) < h2->getSecSkillLevel(SecondarySkill::SCHOLAR))
|
||||
if (h1_scholarLevel < h2_scholarLevel)
|
||||
{
|
||||
std::swap (h1,h2);//1st hero need to have higher scholar level for correct message
|
||||
std::swap(fromHero, toHero);
|
||||
}
|
||||
|
||||
int ScholarLevel = h1->getSecSkillLevel(SecondarySkill::SCHOLAR);//heroes can trade up to this level
|
||||
int ScholarLevel = std::max(h1_scholarLevel, h2_scholarLevel);//heroes can trade up to this level
|
||||
if (!ScholarLevel || !h1->hasSpellbook() || !h2->hasSpellbook())
|
||||
return;//no scholar skill or no spellbook
|
||||
|
||||
int h1Lvl = std::min(ScholarLevel+1, h1->getSecSkillLevel(SecondarySkill::WISDOM)+2),
|
||||
h2Lvl = std::min(ScholarLevel+1, h2->getSecSkillLevel(SecondarySkill::WISDOM)+2);//heroes can receive this levels
|
||||
int h1Lvl = std::min(ScholarLevel, h1->maxSpellLevel()),
|
||||
h2Lvl = std::min(ScholarLevel, h2->maxSpellLevel());//heroes can receive this levels
|
||||
|
||||
ChangeSpells cs1;
|
||||
cs1.learn = true;
|
||||
@ -4019,19 +4019,18 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
handleAfterAttackCasting(bat);
|
||||
}
|
||||
|
||||
//second shot for ballista, only if hero has advanced artillery
|
||||
|
||||
const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side);
|
||||
|
||||
if(destinationStack->alive()
|
||||
&& (stack->getCreature()->idNumber == CreatureID::BALLISTA)
|
||||
&& (attackingHero->getSecSkillLevel(SecondarySkill::ARTILLERY) >= SecSkillLevel::ADVANCED)
|
||||
)
|
||||
//extra shot(s) for ballista, based on artillery skill
|
||||
if(stack->getCreature()->idNumber == CreatureID::BALLISTA)
|
||||
{
|
||||
BattleAttack bat2;
|
||||
bat2.flags |= BattleAttack::SHOT;
|
||||
prepareAttack(bat2, stack, destinationStack, 0, ba.destinationTile);
|
||||
sendAndApply(&bat2);
|
||||
const CGHeroInstance * attackingHero = gs->curB->battleGetFightingHero(ba.side);
|
||||
int ballistaBonusAttacks = attackingHero->valOfBonuses(Bonus::SECONDARY_SKILL_VAL2, SecondarySkill::ARTILLERY);
|
||||
while(destinationStack->alive() && ballistaBonusAttacks-- > 0)
|
||||
{
|
||||
BattleAttack bat2;
|
||||
bat2.flags |= BattleAttack::SHOT;
|
||||
prepareAttack(bat2, stack, destinationStack, 0, ba.destinationTile);
|
||||
sendAndApply(&bat2);
|
||||
}
|
||||
}
|
||||
//allow more than one additional attack
|
||||
|
||||
@ -4083,7 +4082,7 @@ bool CGameHandler::makeBattleAction(BattleAction &ba)
|
||||
|
||||
CHeroHandler::SBallisticsLevelInfo sbi;
|
||||
if(stack->getCreature()->idNumber == CreatureID::CATAPULT)
|
||||
sbi = VLC->heroh->ballistics.at(attackingHero->getSecSkillLevel(SecondarySkill::BALLISTICS));
|
||||
sbi = VLC->heroh->ballistics.at(attackingHero->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::BALLISTICS));
|
||||
else //may need to use higher ballistics level for creatures in future for some cases to match original H3 (upgraded cyclops etc)
|
||||
{
|
||||
sbi = VLC->heroh->ballistics.at(1);
|
||||
@ -5843,9 +5842,10 @@ void CGameHandler::runBattle()
|
||||
}
|
||||
|
||||
const CGHeroInstance * curOwner = battleGetOwnerHero(next);
|
||||
const int stackCreatureId = next->getCreature()->idNumber;
|
||||
|
||||
if ((next->position < 0 || next->getCreature()->idNumber == CreatureID::BALLISTA) //arrow turret or ballista
|
||||
&& (!curOwner || curOwner->getSecSkillLevel(SecondarySkill::ARTILLERY) == 0)) //hero has no artillery
|
||||
if ((stackCreatureId == CreatureID::ARROW_TOWERS || stackCreatureId == CreatureID::BALLISTA)
|
||||
&& (!curOwner || getRandomGenerator().nextInt(99) >= curOwner->valOfBonuses(Bonus::MANUAL_CONTROL, stackCreatureId)))
|
||||
{
|
||||
BattleAction attack;
|
||||
attack.actionType = Battle::SHOOT;
|
||||
@ -5875,7 +5875,7 @@ void CGameHandler::runBattle()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!curOwner || curOwner->getSecSkillLevel(SecondarySkill::BALLISTICS) == 0)
|
||||
if (!curOwner || getRandomGenerator().nextInt(99) >= curOwner->valOfBonuses(Bonus::MANUAL_CONTROL, CreatureID::CATAPULT))
|
||||
{
|
||||
BattleAction attack;
|
||||
attack.destinationTile = *RandomGeneratorUtil::nextItem(attackableBattleHexes,
|
||||
@ -5903,7 +5903,7 @@ void CGameHandler::runBattle()
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!curOwner || curOwner->getSecSkillLevel(SecondarySkill::FIRST_AID) == 0) //no hero or hero has no first aid
|
||||
if (!curOwner || getRandomGenerator().nextInt(99) >= curOwner->valOfBonuses(Bonus::MANUAL_CONTROL, CreatureID::FIRST_AID_TENT))
|
||||
{
|
||||
RandomGeneratorUtil::randomShuffle(possibleStacks, getRandomGenerator());
|
||||
const CStack * toBeHealed = possibleStacks.front();
|
||||
|
@ -609,11 +609,9 @@ void handleLinuxSignal(int sig)
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
#ifndef VCMI_WINDOWS
|
||||
#ifndef VCMI_ANDROID
|
||||
// Correct working dir executable folder (not bundle folder) so we can use executable relative paths
|
||||
std::string executablePath = argv[0];
|
||||
std::string workDir = executablePath.substr(0, executablePath.rfind('/'));
|
||||
chdir(workDir.c_str());
|
||||
boost::filesystem::current_path(boost::filesystem::system_complete(argv[0]).parent_path());
|
||||
#endif
|
||||
// Installs a sig sev segmentation violation handler
|
||||
// to log stacktrace
|
||||
|
Loading…
Reference in New Issue
Block a user