1
0
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:
Dydzio 2017-09-09 21:04:40 +02:00 committed by GitHub
commit 14c07afd3c
86 changed files with 2266 additions and 1099 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -76,5 +76,9 @@
"config/spells/other.json",
"config/spells/timed.json",
"config/spells/ability.json"
],
"skills" :
[
"config/skills.json"
]
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,7 +22,7 @@ public:
~CSettingsView();
void loadSettings();
void setDisplayList(const QStringList& displayList);
void setDisplayList();
private slots:
void on_checkBoxFullScreen_stateChanged(int state);

View File

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

View File

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

View File

@ -37,7 +37,6 @@ class CGObjectInstance;
class CCreature;
class CMap;
struct StartInfo;
struct SDL_Surface;
class CMapHandler;
struct SetObjectProperty;
struct MetaString;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

View File

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

View File

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