mirror of
https://github.com/vcmi/vcmi.git
synced 2025-01-02 00:10:22 +02:00
Merge pull request #369 from henningkoehlernz/skill_mod
Allow modding of secondary skills via skills.json
This commit is contained in:
commit
898f2908d8
3
AUTHORS
3
AUTHORS
@ -66,3 +66,6 @@ Dydzio, <blood990@gmail.com>
|
||||
|
||||
Piotr Wójcik aka Chocimier, <chocimier@tlen.pl>
|
||||
* Various bug fixes
|
||||
|
||||
Henning Koehler, <henning.koehler.nz@gmail.com>
|
||||
* skill modding
|
||||
|
@ -11,12 +11,15 @@ GENERAL:
|
||||
- CATAPULT_EXTRA_SHOTS - defines number of extra wall attacks for units that can do so
|
||||
- RANGED_RETALIATION - allows ranged counterattack
|
||||
- BLOCKS_RANGED_RETALIATION - disallow enemy ranged counterattack
|
||||
- SECONDARY_SKILL_VAL2 - set additional parameter for certain secondary skills
|
||||
- MANUAL_CONTROL - grant manual control over war machine
|
||||
|
||||
SPELLS:
|
||||
* Implemented cumulative effects for spells
|
||||
|
||||
MODS:
|
||||
* Improve support for WoG commander artifacts and skill descriptions
|
||||
* Added basic support for secondary skill modding
|
||||
|
||||
0.98 -> 0.99
|
||||
|
||||
|
12
Global.h
12
Global.h
@ -676,6 +676,18 @@ namespace vstd
|
||||
return v3;
|
||||
}
|
||||
|
||||
template <typename Key, typename V>
|
||||
bool containsMapping(const std::multimap<Key,V> & map, const std::pair<const Key,V> & mapping)
|
||||
{
|
||||
auto range = map.equal_range(mapping.first);
|
||||
for(auto contained = range.first; contained != range.second; contained++)
|
||||
{
|
||||
if(mapping.second == contained->second)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
using boost::math::round;
|
||||
}
|
||||
using vstd::operator-=;
|
||||
|
@ -9,6 +9,8 @@
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
#include "CGameInfo.h"
|
||||
#include "CSkillHandler.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
|
||||
@ -32,5 +34,6 @@ void CGameInfo::setFromLib()
|
||||
heroh = VLC->heroh;
|
||||
objh = VLC->objh;
|
||||
spellh = VLC->spellh;
|
||||
skillh = VLC->skillh;
|
||||
objtypeh = VLC->objtypeh;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ class CArtHandler;
|
||||
class CHeroHandler;
|
||||
class CCreatureHandler;
|
||||
class CSpellHandler;
|
||||
class CSkillHandler;
|
||||
class CBuildingHandler;
|
||||
class CObjectHandler;
|
||||
class CSoundHandler;
|
||||
@ -55,6 +56,7 @@ public:
|
||||
ConstTransitivePtr<CHeroHandler> heroh;
|
||||
ConstTransitivePtr<CCreatureHandler> creh;
|
||||
ConstTransitivePtr<CSpellHandler> spellh;
|
||||
ConstTransitivePtr<CSkillHandler> skillh;
|
||||
ConstTransitivePtr<CObjectHandler> objh;
|
||||
ConstTransitivePtr<CObjectClassesHandler> objtypeh;
|
||||
CGeneralTextHandler * generaltexth;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -33,6 +33,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"
|
||||
@ -240,8 +241,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);
|
||||
}
|
||||
|
||||
@ -372,7 +373,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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -76,5 +76,9 @@
|
||||
"config/spells/other.json",
|
||||
"config/spells/timed.json",
|
||||
"config/spells/ability.json"
|
||||
],
|
||||
"skills" :
|
||||
[
|
||||
"config/skills.json"
|
||||
]
|
||||
}
|
||||
|
@ -97,6 +97,11 @@
|
||||
"description": "List of configuration files for spells",
|
||||
"items": { "type":"string", "format" : "textFile" }
|
||||
},
|
||||
"skills": {
|
||||
"type":"array",
|
||||
"description": "List of configuration files for skills",
|
||||
"items": { "type":"string", "format" : "textFile" }
|
||||
},
|
||||
"templates":{
|
||||
"type":"array",
|
||||
"description": "List of configuration files for RMG templates",
|
||||
|
57
config/schemas/skill.json
Normal file
57
config/schemas/skill.json
Normal file
@ -0,0 +1,57 @@
|
||||
{
|
||||
|
||||
"type" : "object",
|
||||
"$schema" : "http://json-schema.org/draft-04/schema",
|
||||
|
||||
"title" : "VCMI skill format",
|
||||
"description" : "Format used to replace bonuses provided by secondary skills in VCMI",
|
||||
|
||||
"definitions" : {
|
||||
|
||||
"skillBonus" : {
|
||||
"type" : "object",
|
||||
"description" : "Set of bonuses provided by skill at given level",
|
||||
"required" : ["description", "effects"],
|
||||
"properties" : {
|
||||
"description" : {
|
||||
"type" : "string",
|
||||
"description" : "localizable description"
|
||||
},
|
||||
"effects" : {
|
||||
"type" : "object",
|
||||
"additionalProperties" : {
|
||||
"$ref" : "vcmi:bonus"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
"required" : ["name", "basic", "advanced", "expert"],
|
||||
|
||||
"properties" : {
|
||||
"index" : {
|
||||
"type": "number",
|
||||
"description": "numeric id of skill, required for existing skills"
|
||||
},
|
||||
"name" : {
|
||||
"type": "string",
|
||||
"description": "localizable skill name"
|
||||
},
|
||||
"base" : {
|
||||
"type" : "object",
|
||||
"description" : "will be merged with all levels",
|
||||
"additionalProperties" : true
|
||||
},
|
||||
"basic" : {
|
||||
"$ref" : "#/definitions/skillBonus"
|
||||
},
|
||||
"advanced" : {
|
||||
"$ref" : "#/definitions/skillBonus"
|
||||
},
|
||||
"expert" : {
|
||||
"$ref" : "#/definitions/skillBonus"
|
||||
}
|
||||
}
|
||||
}
|
805
config/skills.json
Normal file
805
config/skills.json
Normal file
@ -0,0 +1,805 @@
|
||||
{
|
||||
"pathfinding" : {
|
||||
"index" : 0,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.pathfinding",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 25 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 75 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"archery" : {
|
||||
"index" : 1,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.archery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 25 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"logistics" : {
|
||||
"index" : 2,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.logistics",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 20 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 30 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"scouting" : {
|
||||
"index" : 3,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"type" : "SIGHT_RADIOUS",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"diplomacy" : {
|
||||
"index" : 4,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.diplomacy",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"surr" : {
|
||||
"type" : "SURRENDER_DISCOUNT",
|
||||
"val" : 20,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 },
|
||||
"surr" : { "val" : 20 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val": 2 },
|
||||
"surr" : { "val" : 40 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val": 3 },
|
||||
"surr" : { "val" : 60 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"navigation" : {
|
||||
"index" : 5,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.navigation",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 100 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 150 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"leadership" : {
|
||||
"index" : 6,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"type" : "MORALE",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"wisdom" : {
|
||||
"index" : 7,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.wisdom",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"mysticism" : {
|
||||
"index" : 8,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"type" : "MANA_REGENERATION",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"luck" : {
|
||||
"index" : 9,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"type" : "LUCK",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"ballistics" : {
|
||||
"index" : 10,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.ballistics",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"ctrl" : {
|
||||
"subtype" : "creature.catapult",
|
||||
"type" : "MANUAL_CONTROL",
|
||||
"val" : 100,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"eagleEye" : {
|
||||
"index" : 11,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.eagleEye",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"val2" : {
|
||||
"subtype" : "skill.eagleEye",
|
||||
"type" : "SECONDARY_SKILL_VAL2",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 40 },
|
||||
"val2" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 },
|
||||
"val2" : { "val" : 3 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 60 },
|
||||
"val2" : { "val" : 4 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"necromancy" : {
|
||||
"index" : 12,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.necromancy",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 20 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 30 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"estates" : {
|
||||
"index" : 13,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.estates",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"description" : "{Basic Estates}\n\nYour hero contributes 125 gold per day to your cause.",
|
||||
"effects" : {
|
||||
"main" : { "val" : 125 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"description" : "{Advanced Estates}\n\nYour hero contributes 250 gold per day to your cause.",
|
||||
"effects" : {
|
||||
"main" : { "val" : 250 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"description" : "{Expert Estates}\n\nYour hero contributes 500 gold per day to your cause.",
|
||||
"effects" : {
|
||||
"main" : { "val" : 500 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"fireMagic" : {
|
||||
"index" : 14,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.fireMagic",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"airMagic" : {
|
||||
"index" : 15,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.airMagic",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"waterMagic" : {
|
||||
"index" : 16,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.waterMagic",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"earthMagic" : {
|
||||
"index" : 17,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.earthMagic",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"scholar" : {
|
||||
"index" : 18,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.scholar",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 2 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 4 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"tactics" : {
|
||||
"index" : 19,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.tactics",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 3 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 5 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 7 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"artillery" : {
|
||||
"index" : 20,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.artillery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"val2" : {
|
||||
"subtype" : "skill.artillery",
|
||||
"type" : "SECONDARY_SKILL_VAL2",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"ctrl" : {
|
||||
"subtype" : "creature.ballista",
|
||||
"type" : "MANUAL_CONTROL",
|
||||
"val" : 100,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"ctrl2" : {
|
||||
"subtype" : "creature.arrowTower",
|
||||
"type" : "MANUAL_CONTROL",
|
||||
"val" : 100,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 },
|
||||
"val2" : { "val" : 0 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 75 },
|
||||
"val2" : { "val" : 1 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 100 },
|
||||
"val2" : { "val" : 1 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"learning" : {
|
||||
"index" : 21,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.learning",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 5 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 15 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"offence" : {
|
||||
"index" : 22,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.offence",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 20 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 30 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"armorer" : {
|
||||
"index" : 23,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.armorer",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 5 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 15 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"intelligence" : {
|
||||
"index" : 24,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.intelligence",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 25 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 100 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"sorcery" : {
|
||||
"index" : 25,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.sorcery",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 5 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 15 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"resistance" : {
|
||||
"index" : 26,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.resistance",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 5 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 10 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 20 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"firstAid" : {
|
||||
"index" : 27,
|
||||
"base" : {
|
||||
"effects" : {
|
||||
"main" : {
|
||||
"subtype" : "skill.firstAid",
|
||||
"type" : "SECONDARY_SKILL_PREMY",
|
||||
"valueType" : "BASE_NUMBER"
|
||||
},
|
||||
"ctrl" : {
|
||||
"subtype" : "creature.firstAidTent",
|
||||
"type" : "MANUAL_CONTROL",
|
||||
"val" : 100,
|
||||
"valueType" : "BASE_NUMBER"
|
||||
}
|
||||
}
|
||||
},
|
||||
"basic" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 50 }
|
||||
}
|
||||
},
|
||||
"advanced" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 75 }
|
||||
}
|
||||
},
|
||||
"expert" : {
|
||||
"effects" : {
|
||||
"main" : { "val" : 100 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -190,3 +190,4 @@ extern DLL_LINKAGE vstd::CLoggerBase * logBonus;
|
||||
extern DLL_LINKAGE vstd::CLoggerBase * logNetwork;
|
||||
extern DLL_LINKAGE vstd::CLoggerBase * logAi;
|
||||
extern DLL_LINKAGE vstd::CLoggerBase * logAnim;
|
||||
extern DLL_LINKAGE vstd::CLoggerBase * logMod;
|
||||
|
@ -360,7 +360,7 @@ ArtifactPosition CArtHandler::stringToSlot(std::string slotName)
|
||||
if (it != artifactPositionMap.end())
|
||||
return it->second;
|
||||
|
||||
logGlobal->warn("Warning! Artifact slot %s not recognized!", slotName);
|
||||
logMod->warn("Warning! Artifact slot %s not recognized!", slotName);
|
||||
return ArtifactPosition::PRE_FIRST;
|
||||
}
|
||||
|
||||
@ -421,7 +421,7 @@ CArtifact::EartClass CArtHandler::stringToClass(std::string className)
|
||||
if (it != artifactClassMap.end())
|
||||
return it->second;
|
||||
|
||||
logGlobal->warn("Warning! Artifact rarity %s not recognized!", className);
|
||||
logMod->warn("Warning! Artifact rarity %s not recognized!", className);
|
||||
return CArtifact::ART_SPECIAL;
|
||||
}
|
||||
|
||||
@ -455,7 +455,7 @@ void CArtHandler::loadType(CArtifact * art, const JsonNode & node)
|
||||
}
|
||||
}
|
||||
else
|
||||
logGlobal->warn("Warning! Artifact type %s not recognized!", b.String());
|
||||
logMod->warn("Warning! Artifact type %s not recognized!", b.String());
|
||||
}
|
||||
}
|
||||
|
||||
@ -679,11 +679,11 @@ void CArtHandler::erasePickedArt(ArtifactID id)
|
||||
artifactList->erase(itr);
|
||||
}
|
||||
else
|
||||
logGlobal->warn("Problem: cannot erase artifact %s from list, it was not present", art->Name());
|
||||
logMod->warn("Problem: cannot erase artifact %s from list, it was not present", art->Name());
|
||||
|
||||
}
|
||||
else
|
||||
logGlobal->warn("Problem: cannot find list for artifact %s, strange class. (special?)", art->Name());
|
||||
logMod->warn("Problem: cannot find list for artifact %s, strange class. (special?)", art->Name());
|
||||
}
|
||||
|
||||
boost::optional<std::vector<CArtifact*>&> CArtHandler::listFromClass( CArtifact::EartClass artifactClass )
|
||||
@ -869,7 +869,7 @@ bool CArtifactInstance::canBePutAt(const CArtifactSet *artSet, ArtifactPosition
|
||||
auto possibleSlots = artType->possibleSlots.find(artSet->bearerType());
|
||||
if(possibleSlots == artType->possibleSlots.end())
|
||||
{
|
||||
logGlobal->warn("Warning: artifact %s doesn't have defined allowed slots for bearer of type %s", artType->Name(), artSet->bearerType());
|
||||
logMod->warn("Warning: artifact %s doesn't have defined allowed slots for bearer of type %s", artType->Name(), artSet->bearerType());
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -1007,7 +1007,7 @@ SpellID CArtifactInstance::getGivenSpellID() const
|
||||
const auto b = getBonusLocalFirst(Selector::type(Bonus::SPELL));
|
||||
if(!b)
|
||||
{
|
||||
logGlobal->warn("Warning: %s doesn't bear any spell!", nodeName());
|
||||
logMod->warn("Warning: %s doesn't bear any spell!", nodeName());
|
||||
return SpellID::NONE;
|
||||
}
|
||||
return SpellID(b->subtype);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "CHeroHandler.h"
|
||||
#include "mapObjects/CObjectHandler.h"
|
||||
#include "CModHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
#include "mapping/CMap.h"
|
||||
#include "mapping/CMapService.h"
|
||||
#include "StartInfo.h"
|
||||
@ -113,6 +114,10 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
|
||||
{
|
||||
dst = VLC->objtypeh->getObjectName(ser);
|
||||
}
|
||||
else if(type == SEC_SKILL_NAME)
|
||||
{
|
||||
dst = VLC->skillh->skillName(ser);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::string> *vec;
|
||||
@ -139,9 +144,6 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
|
||||
case ADVOB_TXT:
|
||||
vec = &VLC->generaltexth->advobtxt;
|
||||
break;
|
||||
case SEC_SKILL_NAME:
|
||||
vec = &VLC->generaltexth->skillName;
|
||||
break;
|
||||
case COLOR:
|
||||
vec = &VLC->generaltexth->capColors;
|
||||
break;
|
||||
|
@ -377,23 +377,6 @@ CGeneralTextHandler::CGeneralTextHandler()
|
||||
}
|
||||
while (parser.endLine());
|
||||
}
|
||||
{
|
||||
CLegacyConfigParser parser("DATA/SSTRAITS.TXT");
|
||||
|
||||
//skip header
|
||||
parser.endLine();
|
||||
parser.endLine();
|
||||
|
||||
do
|
||||
{
|
||||
skillName.push_back(parser.readString());
|
||||
|
||||
skillInfoTexts.push_back(std::vector<std::string>());
|
||||
for(int j = 0; j < 3; j++)
|
||||
skillInfoTexts.back().push_back(parser.readString());
|
||||
}
|
||||
while (parser.endLine());
|
||||
}
|
||||
{
|
||||
CLegacyConfigParser parser("DATA/SEERHUT.TXT");
|
||||
|
||||
|
@ -130,8 +130,6 @@ public:
|
||||
std::vector<std::string> tentColors;
|
||||
|
||||
//sec skills
|
||||
std::vector<std::string> skillName;
|
||||
std::vector<std::vector<std::string>> skillInfoTexts; //[id][level] : level 0 - basic; 2 - advanced
|
||||
std::vector<std::string> levels;
|
||||
std::vector<std::string> zcrexp; //more or less useful content of that file
|
||||
//commanders
|
||||
|
@ -362,7 +362,7 @@ void CHeroHandler::loadHeroSkills(CHero * hero, const JsonNode & node)
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->error("Unknown skill level: %s", set["level"].String());
|
||||
logMod->error("Unknown skill level: %s", set["level"].String());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,6 +115,7 @@ set(lib_SRCS
|
||||
CModHandler.cpp
|
||||
CPathfinder.cpp
|
||||
CRandomGenerator.cpp
|
||||
CSkillHandler.cpp
|
||||
CStack.cpp
|
||||
CThreadHelper.cpp
|
||||
CTownHandler.cpp
|
||||
@ -256,6 +257,7 @@ set(lib_HEADERS
|
||||
CPlayerState.h
|
||||
CRandomGenerator.h
|
||||
CScriptingModule.h
|
||||
CSkillHandler.h
|
||||
CSoundBase.h
|
||||
CStack.h
|
||||
CStopWatch.h
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "CStopWatch.h"
|
||||
#include "IHandlerBase.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
|
||||
CIdentifierStorage::CIdentifierStorage():
|
||||
state(LOADING)
|
||||
@ -36,7 +37,7 @@ CIdentifierStorage::~CIdentifierStorage()
|
||||
void CIdentifierStorage::checkIdentifier(std::string & ID)
|
||||
{
|
||||
if (boost::algorithm::ends_with(ID, "."))
|
||||
logGlobal->warn("BIG WARNING: identifier %s seems to be broken!", ID);
|
||||
logMod->warn("BIG WARNING: identifier %s seems to be broken!", ID);
|
||||
else
|
||||
{
|
||||
size_t pos = 0;
|
||||
@ -44,7 +45,7 @@ void CIdentifierStorage::checkIdentifier(std::string & ID)
|
||||
{
|
||||
if (std::tolower(ID[pos]) != ID[pos] ) //Not in camelCase
|
||||
{
|
||||
logGlobal->warn("Warning: identifier %s is not in camelCase!", ID);
|
||||
logMod->warn("Warning: identifier %s is not in camelCase!", ID);
|
||||
ID[pos] = std::tolower(ID[pos]);// Try to fix the ID
|
||||
}
|
||||
pos = ID.find('.', pos);
|
||||
@ -148,7 +149,7 @@ boost::optional<si32> CIdentifierStorage::getIdentifier(std::string scope, std::
|
||||
if (idList.size() == 1)
|
||||
return idList.front().id;
|
||||
if (!silent)
|
||||
logGlobal->error("Failed to resolve identifier %s of type %s from mod %s", name , type ,scope);
|
||||
logMod->error("Failed to resolve identifier %s of type %s from mod %s", name , type ,scope);
|
||||
|
||||
return boost::optional<si32>();
|
||||
}
|
||||
@ -161,7 +162,7 @@ boost::optional<si32> CIdentifierStorage::getIdentifier(std::string type, const
|
||||
if (idList.size() == 1)
|
||||
return idList.front().id;
|
||||
if (!silent)
|
||||
logGlobal->error("Failed to resolve identifier %s of type %s from mod %s", name.String(), type, name.meta);
|
||||
logMod->error("Failed to resolve identifier %s of type %s from mod %s", name.String(), type, name.meta);
|
||||
|
||||
return boost::optional<si32>();
|
||||
}
|
||||
@ -175,7 +176,7 @@ boost::optional<si32> CIdentifierStorage::getIdentifier(const JsonNode & name, b
|
||||
if (idList.size() == 1)
|
||||
return idList.front().id;
|
||||
if (!silent)
|
||||
logGlobal->error("Failed to resolve identifier %s of type %s from mod %s", name.String(), pair2.first, name.meta);
|
||||
logMod->error("Failed to resolve identifier %s of type %s from mod %s", name.String(), pair2.first, name.meta);
|
||||
|
||||
return boost::optional<si32>();
|
||||
}
|
||||
@ -189,7 +190,7 @@ boost::optional<si32> CIdentifierStorage::getIdentifier(std::string scope, std::
|
||||
if (idList.size() == 1)
|
||||
return idList.front().id;
|
||||
if (!silent)
|
||||
logGlobal->error("Failed to resolve identifier %s of type %s from mod %s", fullName, pair2.first, scope);
|
||||
logMod->error("Failed to resolve identifier %s of type %s from mod %s", fullName, pair2.first, scope);
|
||||
|
||||
return boost::optional<si32>();
|
||||
}
|
||||
@ -203,7 +204,12 @@ void CIdentifierStorage::registerObject(std::string scope, std::string type, std
|
||||
std::string fullID = type + '.' + name;
|
||||
checkIdentifier(fullID);
|
||||
|
||||
registeredObjects.insert(std::make_pair(fullID, data));
|
||||
std::pair<const std::string, ObjectData> mapping = std::make_pair(fullID, data);
|
||||
if(!vstd::containsMapping(registeredObjects, mapping))
|
||||
{
|
||||
logMod->trace("registered %s as %s:%s", fullID, scope, identifier);
|
||||
registeredObjects.insert(mapping);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CIdentifierStorage::ObjectData> CIdentifierStorage::getPossibleIdentifiers(const ObjectCallback & request)
|
||||
@ -274,15 +280,15 @@ bool CIdentifierStorage::resolveIdentifier(const ObjectCallback & request)
|
||||
|
||||
// error found. Try to generate some debug info
|
||||
if (identifiers.size() == 0)
|
||||
logGlobal->error("Unknown identifier!");
|
||||
logMod->error("Unknown identifier!");
|
||||
else
|
||||
logGlobal->error("Ambiguous identifier request!");
|
||||
logMod->error("Ambiguous identifier request!");
|
||||
|
||||
logGlobal->error("Request for %s.%s from mod %s", request.type, request.name, request.localScope);
|
||||
logMod->error("Request for %s.%s from mod %s", request.type, request.name, request.localScope);
|
||||
|
||||
for (auto id : identifiers)
|
||||
{
|
||||
logGlobal->error("\tID is available in mod %s", id.scope);
|
||||
logMod->error("\tID is available in mod %s", id.scope);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -302,9 +308,9 @@ void CIdentifierStorage::finalize()
|
||||
{
|
||||
for(auto object : registeredObjects)
|
||||
{
|
||||
logGlobal->trace("%s : %s -> %d", object.second.scope, object.first, object.second.id);
|
||||
logMod->trace("%s : %s -> %d", object.second.scope, object.first, object.second.id);
|
||||
}
|
||||
logGlobal->error("All known identifiers were dumped into log file");
|
||||
logMod->error("All known identifiers were dumped into log file");
|
||||
}
|
||||
assert(errorsFound == false);
|
||||
state = FINISHED;
|
||||
@ -345,9 +351,9 @@ bool CContentHandler::ContentTypeHandler::preloadModData(std::string modName, st
|
||||
|
||||
// patching this mod? Send warning and continue - this situation can be handled normally
|
||||
if (remoteName == modName)
|
||||
logGlobal->warn("Redundant namespace definition for %s", objectName);
|
||||
logMod->warn("Redundant namespace definition for %s", objectName);
|
||||
|
||||
logGlobal->trace("Patching object %s (%s) from %s", objectName, remoteName, modName);
|
||||
logMod->trace("Patching object %s (%s) from %s", objectName, remoteName, modName);
|
||||
JsonNode & remoteConf = modData[remoteName].patches[objectName];
|
||||
|
||||
JsonUtils::merge(remoteConf, entry.second);
|
||||
@ -383,17 +389,22 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali
|
||||
|
||||
if (originalData.size() > index)
|
||||
{
|
||||
logMod->trace("found original data in loadMod(%s) at index %d", name, index);
|
||||
JsonUtils::merge(originalData[index], data);
|
||||
|
||||
performValidate(originalData[index],name);
|
||||
handler->loadObject(modName, name, originalData[index], index);
|
||||
|
||||
originalData[index].clear(); // do not use same data twice (same ID)
|
||||
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
logMod->debug("no original data in loadMod(%s) at index %d", name, index);
|
||||
performValidate(data, name);
|
||||
handler->loadObject(modName, name, data, index);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// normal new object or one with index bigger that data size
|
||||
// normal new object
|
||||
logMod->trace("no index in loadMod(%s)", name);
|
||||
performValidate(data,name);
|
||||
handler->loadObject(modName, name, data);
|
||||
}
|
||||
@ -420,6 +431,7 @@ CContentHandler::CContentHandler()
|
||||
handlers.insert(std::make_pair("objects", ContentTypeHandler(VLC->objtypeh, "object")));
|
||||
handlers.insert(std::make_pair("heroes", ContentTypeHandler(VLC->heroh, "hero")));
|
||||
handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell")));
|
||||
handlers.insert(std::make_pair("skills", ContentTypeHandler(VLC->skillh, "skill")));
|
||||
handlers.insert(std::make_pair("templates", ContentTypeHandler((IHandlerBase *)VLC->tplh, "template")));
|
||||
|
||||
//TODO: any other types of moddables?
|
||||
@ -466,7 +478,7 @@ void CContentHandler::preloadData(CModInfo & mod)
|
||||
bool validate = (mod.validation != CModInfo::PASSED);
|
||||
|
||||
// print message in format [<8-symbols checksum>] <modname>
|
||||
logGlobal->info("\t\t[%08x]%s", mod.checksum, mod.name);
|
||||
logMod->info("\t\t[%08x]%s", mod.checksum, mod.name);
|
||||
|
||||
if (validate && mod.identifier != "core")
|
||||
{
|
||||
@ -487,12 +499,12 @@ void CContentHandler::load(CModInfo & mod)
|
||||
if (validate)
|
||||
{
|
||||
if (mod.validation != CModInfo::FAILED)
|
||||
logGlobal->info("\t\t[DONE] %s", mod.name);
|
||||
logMod->info("\t\t[DONE] %s", mod.name);
|
||||
else
|
||||
logGlobal->error("\t\t[FAIL] %s", mod.name);
|
||||
logMod->error("\t\t[FAIL] %s", mod.name);
|
||||
}
|
||||
else
|
||||
logGlobal->info("\t\t[SKIP] %s", mod.name);
|
||||
logMod->info("\t\t[SKIP] %s", mod.name);
|
||||
}
|
||||
|
||||
static JsonNode loadModSettings(std::string path)
|
||||
@ -618,39 +630,39 @@ void CModHandler::loadConfigFromFile (std::string name)
|
||||
paths += p.string() + ", ";
|
||||
}
|
||||
paths = paths.substr(0, paths.size() - 2);
|
||||
logGlobal->debug("Loading hardcoded features settings from [%s], result:", paths);
|
||||
logMod->debug("Loading hardcoded features settings from [%s], result:", paths);
|
||||
settings.data = JsonUtils::assembleFromFiles("config/" + name);
|
||||
const JsonNode & hardcodedFeatures = settings.data["hardcodedFeatures"];
|
||||
settings.MAX_HEROES_AVAILABLE_PER_PLAYER = hardcodedFeatures["MAX_HEROES_AVAILABLE_PER_PLAYER"].Integer();
|
||||
logGlobal->debug("\tMAX_HEROES_AVAILABLE_PER_PLAYER\t%d", settings.MAX_HEROES_AVAILABLE_PER_PLAYER);
|
||||
logMod->debug("\tMAX_HEROES_AVAILABLE_PER_PLAYER\t%d", settings.MAX_HEROES_AVAILABLE_PER_PLAYER);
|
||||
settings.MAX_HEROES_ON_MAP_PER_PLAYER = hardcodedFeatures["MAX_HEROES_ON_MAP_PER_PLAYER"].Integer();
|
||||
logGlobal->debug("\tMAX_HEROES_ON_MAP_PER_PLAYER\t%d", settings.MAX_HEROES_ON_MAP_PER_PLAYER);
|
||||
logMod->debug("\tMAX_HEROES_ON_MAP_PER_PLAYER\t%d", settings.MAX_HEROES_ON_MAP_PER_PLAYER);
|
||||
settings.CREEP_SIZE = hardcodedFeatures["CREEP_SIZE"].Integer();
|
||||
logGlobal->debug("\tCREEP_SIZE\t%d", settings.CREEP_SIZE);
|
||||
logMod->debug("\tCREEP_SIZE\t%d", settings.CREEP_SIZE);
|
||||
settings.WEEKLY_GROWTH = hardcodedFeatures["WEEKLY_GROWTH_PERCENT"].Integer();
|
||||
logGlobal->debug("\tWEEKLY_GROWTH\t%d", settings.WEEKLY_GROWTH);
|
||||
logMod->debug("\tWEEKLY_GROWTH\t%d", settings.WEEKLY_GROWTH);
|
||||
settings.NEUTRAL_STACK_EXP = hardcodedFeatures["NEUTRAL_STACK_EXP_DAILY"].Integer();
|
||||
logGlobal->debug("\tNEUTRAL_STACK_EXP\t%d", settings.NEUTRAL_STACK_EXP);
|
||||
logMod->debug("\tNEUTRAL_STACK_EXP\t%d", settings.NEUTRAL_STACK_EXP);
|
||||
settings.MAX_BUILDING_PER_TURN = hardcodedFeatures["MAX_BUILDING_PER_TURN"].Integer();
|
||||
logGlobal->debug("\tMAX_BUILDING_PER_TURN\t%d", settings.MAX_BUILDING_PER_TURN);
|
||||
logMod->debug("\tMAX_BUILDING_PER_TURN\t%d", settings.MAX_BUILDING_PER_TURN);
|
||||
settings.DWELLINGS_ACCUMULATE_CREATURES = hardcodedFeatures["DWELLINGS_ACCUMULATE_CREATURES"].Bool();
|
||||
logGlobal->debug("\tDWELLINGS_ACCUMULATE_CREATURES\t%d", static_cast<int>(settings.DWELLINGS_ACCUMULATE_CREATURES));
|
||||
logMod->debug("\tDWELLINGS_ACCUMULATE_CREATURES\t%d", static_cast<int>(settings.DWELLINGS_ACCUMULATE_CREATURES));
|
||||
settings.ALL_CREATURES_GET_DOUBLE_MONTHS = hardcodedFeatures["ALL_CREATURES_GET_DOUBLE_MONTHS"].Bool();
|
||||
logGlobal->debug("\tALL_CREATURES_GET_DOUBLE_MONTHS\t%d", static_cast<int>(settings.ALL_CREATURES_GET_DOUBLE_MONTHS));
|
||||
logMod->debug("\tALL_CREATURES_GET_DOUBLE_MONTHS\t%d", static_cast<int>(settings.ALL_CREATURES_GET_DOUBLE_MONTHS));
|
||||
settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS = hardcodedFeatures["WINNING_HERO_WITH_NO_TROOPS_RETREATS"].Bool();
|
||||
logGlobal->debug("\tWINNING_HERO_WITH_NO_TROOPS_RETREATS\t%d", static_cast<int>(settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS));
|
||||
logMod->debug("\tWINNING_HERO_WITH_NO_TROOPS_RETREATS\t%d", static_cast<int>(settings.WINNING_HERO_WITH_NO_TROOPS_RETREATS));
|
||||
settings.BLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE = hardcodedFeatures["BLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE"].Bool();
|
||||
logGlobal->debug("\tBLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE\t%d", static_cast<int>(settings.BLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE));
|
||||
logMod->debug("\tBLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE\t%d", static_cast<int>(settings.BLACK_MARKET_MONTHLY_ARTIFACTS_CHANGE));
|
||||
|
||||
const JsonNode & gameModules = settings.data["modules"];
|
||||
modules.STACK_EXP = gameModules["STACK_EXPERIENCE"].Bool();
|
||||
logGlobal->debug("\tSTACK_EXP\t%d", static_cast<int>(modules.STACK_EXP));
|
||||
logMod->debug("\tSTACK_EXP\t%d", static_cast<int>(modules.STACK_EXP));
|
||||
modules.STACK_ARTIFACT = gameModules["STACK_ARTIFACTS"].Bool();
|
||||
logGlobal->debug("\tSTACK_ARTIFACT\t%d", static_cast<int>(modules.STACK_ARTIFACT));
|
||||
logMod->debug("\tSTACK_ARTIFACT\t%d", static_cast<int>(modules.STACK_ARTIFACT));
|
||||
modules.COMMANDERS = gameModules["COMMANDERS"].Bool();
|
||||
logGlobal->debug("\tCOMMANDERS\t%d", static_cast<int>(modules.COMMANDERS));
|
||||
logMod->debug("\tCOMMANDERS\t%d", static_cast<int>(modules.COMMANDERS));
|
||||
modules.MITHRIL = gameModules["MITHRIL"].Bool();
|
||||
logGlobal->debug("\tMITHRIL\t%d", static_cast<int>(modules.MITHRIL));
|
||||
logMod->debug("\tMITHRIL\t%d", static_cast<int>(modules.MITHRIL));
|
||||
}
|
||||
|
||||
// currentList is passed by value to get current list of depending mods
|
||||
@ -661,8 +673,8 @@ bool CModHandler::hasCircularDependency(TModID modID, std::set <TModID> currentL
|
||||
// Mod already present? We found a loop
|
||||
if (vstd::contains(currentList, modID))
|
||||
{
|
||||
logGlobal->error("Error: Circular dependency detected! Printing dependency list:");
|
||||
logGlobal->error("\t%s -> ", mod.name);
|
||||
logMod->error("Error: Circular dependency detected! Printing dependency list:");
|
||||
logMod->error("\t%s -> ", mod.name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -673,7 +685,7 @@ bool CModHandler::hasCircularDependency(TModID modID, std::set <TModID> currentL
|
||||
{
|
||||
if (hasCircularDependency(dependency, currentList))
|
||||
{
|
||||
logGlobal->error("\t%s ->\n", mod.name); // conflict detected, print dependency list
|
||||
logMod->error("\t%s ->\n", mod.name); // conflict detected, print dependency list
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -690,7 +702,7 @@ bool CModHandler::checkDependencies(const std::vector <TModID> & input) const
|
||||
{
|
||||
if (!vstd::contains(input, dep))
|
||||
{
|
||||
logGlobal->error("Error: Mod %s requires missing %s!", mod.name, dep);
|
||||
logMod->error("Error: Mod %s requires missing %s!", mod.name, dep);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -699,7 +711,7 @@ bool CModHandler::checkDependencies(const std::vector <TModID> & input) const
|
||||
{
|
||||
if (vstd::contains(input, conflicting))
|
||||
{
|
||||
logGlobal->error("Error: Mod %s conflicts with %s!", mod.name, allMods.at(conflicting).name);
|
||||
logMod->error("Error: Mod %s conflicts with %s!", mod.name, allMods.at(conflicting).name);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -930,11 +942,11 @@ void CModHandler::load()
|
||||
CStopWatch totalTime, timer;
|
||||
|
||||
CContentHandler content;
|
||||
logGlobal->info("\tInitializing content handler: %d ms", timer.getDiff());
|
||||
logMod->info("\tInitializing content handler: %d ms", timer.getDiff());
|
||||
|
||||
for(const TModID & modName : activeMods)
|
||||
{
|
||||
logGlobal->trace("Generating checksum for %s", modName);
|
||||
logMod->trace("Generating checksum for %s", modName);
|
||||
allMods[modName].updateChecksum(calculateModChecksum(modName, CResourceHandler::get(modName)));
|
||||
}
|
||||
|
||||
@ -943,7 +955,7 @@ void CModHandler::load()
|
||||
content.preloadData(coreMod);
|
||||
for(const TModID & modName : activeMods)
|
||||
content.preloadData(allMods[modName]);
|
||||
logGlobal->info("\tParsing mod data: %d ms", timer.getDiff());
|
||||
logMod->info("\tParsing mod data: %d ms", timer.getDiff());
|
||||
|
||||
content.load(coreMod);
|
||||
for(const TModID & modName : activeMods)
|
||||
@ -951,17 +963,17 @@ void CModHandler::load()
|
||||
|
||||
content.loadCustom();
|
||||
|
||||
logGlobal->info("\tLoading mod data: %d ms", timer.getDiff());
|
||||
logMod->info("\tLoading mod data: %d ms", timer.getDiff());
|
||||
|
||||
VLC->creh->loadCrExpBon();
|
||||
VLC->creh->buildBonusTreeForTiers(); //do that after all new creatures are loaded
|
||||
|
||||
identifiers.finalize();
|
||||
logGlobal->info("\tResolving identifiers: %d ms", timer.getDiff());
|
||||
logMod->info("\tResolving identifiers: %d ms", timer.getDiff());
|
||||
|
||||
content.afterLoadFinalization();
|
||||
logGlobal->info("\tHandlers post-load finalization: %d ms ", timer.getDiff());
|
||||
logGlobal->info("\tAll game content loaded in %d ms", totalTime.getDiff());
|
||||
logMod->info("\tHandlers post-load finalization: %d ms ", timer.getDiff());
|
||||
logMod->info("\tAll game content loaded in %d ms", totalTime.getDiff());
|
||||
}
|
||||
|
||||
void CModHandler::afterLoad()
|
||||
|
@ -51,6 +51,10 @@ class CIdentifierStorage
|
||||
si32 id;
|
||||
std::string scope; /// scope in which this ID located
|
||||
|
||||
bool operator==(const ObjectData & other) const
|
||||
{
|
||||
return id == other.id && scope == other.scope;
|
||||
}
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@ -59,7 +63,7 @@ class CIdentifierStorage
|
||||
}
|
||||
};
|
||||
|
||||
std::multimap<std::string, ObjectData > registeredObjects;
|
||||
std::multimap<std::string, ObjectData> registeredObjects;
|
||||
std::vector<ObjectCallback> scheduledRequests;
|
||||
|
||||
ELoadingState state;
|
||||
|
226
lib/CSkillHandler.cpp
Normal file
226
lib/CSkillHandler.cpp
Normal file
@ -0,0 +1,226 @@
|
||||
/*
|
||||
* CSkillHandler.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
#include "CSkillHandler.h"
|
||||
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
|
||||
#include "JsonNode.h"
|
||||
|
||||
#include "CModHandler.h"
|
||||
#include "StringConstants.h"
|
||||
|
||||
#include "CStack.h"
|
||||
#include "battle/BattleInfo.h"
|
||||
#include "battle/CBattleInfoCallback.h"
|
||||
|
||||
///CSkill
|
||||
CSkill::LevelInfo::LevelInfo()
|
||||
{
|
||||
}
|
||||
|
||||
CSkill::LevelInfo::~LevelInfo()
|
||||
{
|
||||
}
|
||||
|
||||
CSkill::CSkill(SecondarySkill id) : id(id)
|
||||
{
|
||||
if(id == SecondarySkill::DEFAULT)
|
||||
identifier = "default";
|
||||
else
|
||||
identifier = NSecondarySkill::names[id];
|
||||
// init levels
|
||||
LevelInfo emptyLevel;
|
||||
for(int level = 1; level < NSecondarySkill::levels.size(); level++)
|
||||
levels.push_back(emptyLevel);
|
||||
}
|
||||
|
||||
CSkill::~CSkill()
|
||||
{
|
||||
}
|
||||
|
||||
void CSkill::addNewBonus(const std::shared_ptr<Bonus> & b, int level)
|
||||
{
|
||||
b->source = Bonus::SECONDARY_SKILL;
|
||||
b->sid = id;
|
||||
b->duration = Bonus::PERMANENT;
|
||||
b->description = identifier;
|
||||
levels[level-1].effects.push_back(b);
|
||||
}
|
||||
|
||||
void CSkill::setDescription(const std::string & desc, int level)
|
||||
{
|
||||
levels[level-1].description = desc;
|
||||
}
|
||||
|
||||
const std::vector<std::shared_ptr<Bonus>> & CSkill::getBonus(int level) const
|
||||
{
|
||||
return levels[level-1].effects;
|
||||
}
|
||||
|
||||
const std::string & CSkill::getDescription(int level) const
|
||||
{
|
||||
return levels[level-1].description;
|
||||
}
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill::LevelInfo & info)
|
||||
{
|
||||
out << "(\"" << info.description << "\", [";
|
||||
for(int i=0; i < info.effects.size(); i++)
|
||||
out << (i ? "," : "") << info.effects[i]->Description();
|
||||
return out << "])";
|
||||
}
|
||||
|
||||
DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill & skill)
|
||||
{
|
||||
out << "Skill(" << (int)skill.id << "," << skill.identifier << "): [";
|
||||
for(int i=0; i < skill.levels.size(); i++)
|
||||
out << (i ? "," : "") << skill.levels[i];
|
||||
return out << "]";
|
||||
}
|
||||
|
||||
std::string CSkill::toString() const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
///CSkillHandler
|
||||
CSkillHandler::CSkillHandler()
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<JsonNode> CSkillHandler::loadLegacyData(size_t dataSize)
|
||||
{
|
||||
CLegacyConfigParser parser("DATA/SSTRAITS.TXT");
|
||||
|
||||
//skip header
|
||||
parser.endLine();
|
||||
parser.endLine();
|
||||
|
||||
std::vector<std::string> skillNames;
|
||||
std::vector<std::vector<std::string>> skillInfoTexts;
|
||||
do
|
||||
{
|
||||
skillNames.push_back(parser.readString());
|
||||
skillInfoTexts.push_back(std::vector<std::string>());
|
||||
for(int i = 0; i < 3; i++)
|
||||
skillInfoTexts.back().push_back(parser.readString());
|
||||
}
|
||||
while (parser.endLine());
|
||||
|
||||
assert(skillNames.size() == GameConstants::SKILL_QUANTITY);
|
||||
|
||||
//store & construct JSON
|
||||
std::vector<JsonNode> legacyData;
|
||||
for(int id = 0; id < GameConstants::SKILL_QUANTITY; id++)
|
||||
{
|
||||
JsonNode skillNode(JsonNode::DATA_STRUCT);
|
||||
skillNode["name"].String() = skillNames[id];
|
||||
for(int level = 1; level < NSecondarySkill::levels.size(); level++)
|
||||
{
|
||||
std::string & desc = skillInfoTexts[id][level-1];
|
||||
auto & levelNode = skillNode[NSecondarySkill::levels[level]].Struct();
|
||||
levelNode["description"].String() = desc;
|
||||
levelNode["effects"].Struct(); // create empty effects objects
|
||||
}
|
||||
legacyData.push_back(skillNode);
|
||||
}
|
||||
objects.resize(legacyData.size());
|
||||
return legacyData;
|
||||
}
|
||||
|
||||
const std::string CSkillHandler::getTypeName() const
|
||||
{
|
||||
return "skill";
|
||||
}
|
||||
|
||||
const std::string & CSkillHandler::skillInfo(int skill, int level) const
|
||||
{
|
||||
return objects[skill]->getDescription(level);
|
||||
}
|
||||
|
||||
const std::string & CSkillHandler::skillName(int skill) const
|
||||
{
|
||||
return objects[skill]->name;
|
||||
}
|
||||
|
||||
CSkill * CSkillHandler::loadFromJson(const JsonNode & json, const std::string & identifier)
|
||||
{
|
||||
CSkill * skill = nullptr;
|
||||
|
||||
for(int id = 0; id < GameConstants::SKILL_QUANTITY; id++)
|
||||
{
|
||||
if(NSecondarySkill::names[id].compare(identifier) == 0)
|
||||
{
|
||||
skill = new CSkill(SecondarySkill(id));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!skill)
|
||||
{
|
||||
logMod->error("unknown secondary skill %s", identifier);
|
||||
throw std::runtime_error("invalid skill");
|
||||
}
|
||||
|
||||
skill->name = json["name"].String();
|
||||
for(int level = 1; level < NSecondarySkill::levels.size(); level++)
|
||||
{
|
||||
const std::string & levelName = NSecondarySkill::levels[level]; // basic, advanced, expert
|
||||
const JsonNode & levelNode = json[levelName];
|
||||
// parse bonus effects
|
||||
for(auto b : levelNode["effects"].Struct())
|
||||
{
|
||||
auto bonus = JsonUtils::parseBonus(b.second);
|
||||
bonus->sid = skill->id;
|
||||
skill->addNewBonus(bonus, level);
|
||||
}
|
||||
skill->setDescription(levelNode["description"].String(), level);
|
||||
}
|
||||
logMod->debug("loaded secondary skill %s(%d)", identifier, (int)skill->id);
|
||||
logMod->trace("%s", skill->toString());
|
||||
|
||||
return skill;
|
||||
}
|
||||
|
||||
void CSkillHandler::afterLoadFinalization()
|
||||
{
|
||||
}
|
||||
|
||||
void CSkillHandler::beforeValidate(JsonNode & object)
|
||||
{
|
||||
//handle "base" level info
|
||||
JsonNode & base = object["base"];
|
||||
|
||||
auto inheritNode = [&](const std::string & name){
|
||||
JsonUtils::inherit(object[name], base);
|
||||
};
|
||||
|
||||
inheritNode("basic");
|
||||
inheritNode("advanced");
|
||||
inheritNode("expert");
|
||||
}
|
||||
|
||||
CSkillHandler::~CSkillHandler()
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<bool> CSkillHandler::getDefaultAllowed() const
|
||||
{
|
||||
std::vector<bool> allowedSkills(objects.size(), true);
|
||||
return allowedSkills;
|
||||
}
|
87
lib/CSkillHandler.h
Normal file
87
lib/CSkillHandler.h
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* CSkillHandler.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "../lib/HeroBonus.h"
|
||||
#include "GameConstants.h"
|
||||
#include "IHandlerBase.h"
|
||||
|
||||
class DLL_LINKAGE CSkill // secondary skill
|
||||
{
|
||||
protected:
|
||||
struct LevelInfo
|
||||
{
|
||||
std::string description; //descriptions of spell for skill level
|
||||
std::vector<std::shared_ptr<Bonus>> effects;
|
||||
|
||||
LevelInfo();
|
||||
~LevelInfo();
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & description;
|
||||
h & effects;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<LevelInfo> levels; // bonuses provided by basic, advanced and expert level
|
||||
|
||||
public:
|
||||
CSkill(SecondarySkill id = SecondarySkill::DEFAULT);
|
||||
~CSkill();
|
||||
|
||||
void addNewBonus(const std::shared_ptr<Bonus> & b, int level);
|
||||
void setDescription(const std::string & desc, int level);
|
||||
const std::vector<std::shared_ptr<Bonus>> & getBonus(int level) const;
|
||||
const std::string & getDescription(int level) const;
|
||||
std::string toString() const;
|
||||
|
||||
SecondarySkill id;
|
||||
std::string identifier;
|
||||
std::string name; //as displayed in GUI
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & id;
|
||||
h & identifier;
|
||||
h & name;
|
||||
h & levels;
|
||||
}
|
||||
|
||||
friend class CSkillHandler;
|
||||
friend DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill & skill);
|
||||
friend DLL_LINKAGE std::ostream & operator<<(std::ostream & out, const CSkill::LevelInfo & info);
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CSkillHandler: public CHandlerBase<SecondarySkill, CSkill>
|
||||
{
|
||||
public:
|
||||
CSkillHandler();
|
||||
virtual ~CSkillHandler();
|
||||
|
||||
///IHandler base
|
||||
std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
|
||||
void afterLoadFinalization() override;
|
||||
void beforeValidate(JsonNode & object) override;
|
||||
|
||||
std::vector<bool> getDefaultAllowed() const override;
|
||||
const std::string getTypeName() const override;
|
||||
|
||||
const std::string & skillInfo(int skill, int level) const;
|
||||
const std::string & skillName(int skill) const;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & objects;
|
||||
}
|
||||
|
||||
protected:
|
||||
CSkill * loadFromJson(const JsonNode & json, const std::string & identifier) override;
|
||||
};
|
@ -748,7 +748,7 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
|
||||
auto & advMap = data["town"]["adventureMap"];
|
||||
if (!advMap.isNull())
|
||||
{
|
||||
logGlobal->warn("Outdated town mod. Will try to generate valid templates out of fort");
|
||||
logMod->warn("Outdated town mod. Will try to generate valid templates out of fort");
|
||||
JsonNode config;
|
||||
config["animation"] = advMap["castle"];
|
||||
VLC->objtypeh->getHandlerFor(index, object->index)->addTemplate(config);
|
||||
@ -763,7 +763,10 @@ void CTownHandler::loadObject(std::string scope, std::string name, const JsonNod
|
||||
{
|
||||
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
|
||||
object->index = index;
|
||||
assert(factions[index] == nullptr); // ensure that this id was not loaded before
|
||||
if (factions.size() > index)
|
||||
assert(factions[index] == nullptr); // ensure that this id was not loaded before
|
||||
else
|
||||
factions.resize(index + 1);
|
||||
factions[index] = object;
|
||||
|
||||
if (object->town)
|
||||
@ -815,9 +818,9 @@ void CTownHandler::initializeRequirements()
|
||||
{
|
||||
if (node.Vector().size() > 1)
|
||||
{
|
||||
logGlobal->warn("Unexpected length of town buildings requirements: %d", node.Vector().size());
|
||||
logGlobal->warn("Entry contains: ");
|
||||
logGlobal->warn(node.toJson());
|
||||
logMod->warn("Unexpected length of town buildings requirements: %d", node.Vector().size());
|
||||
logMod->warn("Entry contains: ");
|
||||
logMod->warn(node.toJson());
|
||||
}
|
||||
return BuildingID(VLC->modh->identifiers.getIdentifier(requirement.town->getBuildingScope(), node.Vector()[0]).get());
|
||||
});
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "CArtHandler.h"
|
||||
#include "CCreatureHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
#include "StringConstants.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
|
||||
@ -65,6 +66,11 @@ const CSpell * SpellID::toSpell() const
|
||||
return VLC->spellh->objects[*this];
|
||||
}
|
||||
|
||||
const CSkill * SecondarySkill::toSkill() const
|
||||
{
|
||||
return VLC->skillh->objects.at(*this);
|
||||
}
|
||||
|
||||
//template std::ostream & operator << <ArtifactInstanceID>(std::ostream & os, BaseForID<ArtifactInstanceID> id);
|
||||
//template std::ostream & operator << <ObjectInstanceID>(std::ostream & os, BaseForID<ObjectInstanceID> id);
|
||||
|
||||
|
@ -62,6 +62,7 @@ class CArtifactInstance;
|
||||
class CCreature;
|
||||
class CHero;
|
||||
class CSpell;
|
||||
class CSkill;
|
||||
class CGameInfoCallback;
|
||||
class CNonConstInfoCallback;
|
||||
|
||||
@ -320,6 +321,8 @@ public:
|
||||
SecondarySkill(ESecondarySkill _num = WRONG) : num(_num)
|
||||
{}
|
||||
|
||||
DLL_LINKAGE const CSkill * toSkill() const;
|
||||
|
||||
ID_LIKE_CLASS_COMMON(SecondarySkill, ESecondarySkill)
|
||||
|
||||
ESecondarySkill num;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "CCreatureSet.h"
|
||||
#include "CHeroHandler.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
#include "CStack.h"
|
||||
#include "CArtHandler.h"
|
||||
|
||||
@ -1078,7 +1079,7 @@ std::string Bonus::Description() const
|
||||
str << VLC->creh->creatures[sid]->namePl;
|
||||
break;
|
||||
case SECONDARY_SKILL:
|
||||
str << VLC->generaltexth->skillName[sid]/* << " secondary skill"*/;
|
||||
str << VLC->skillh->skillName(sid);
|
||||
break;
|
||||
default:
|
||||
//todo: handle all possible sources
|
||||
@ -1166,6 +1167,11 @@ namespace Selector
|
||||
return CSelectFieldEqual<Bonus::BonusSource>(&Bonus::source)(source);
|
||||
}
|
||||
|
||||
CSelector DLL_LINKAGE valueType(Bonus::ValueType valType)
|
||||
{
|
||||
return CSelectFieldEqual<Bonus::ValueType>(&Bonus::valType)(valType);
|
||||
}
|
||||
|
||||
DLL_LINKAGE CSelector all([](const Bonus * b){return true;});
|
||||
DLL_LINKAGE CSelector none([](const Bonus * b){return false;});
|
||||
|
||||
|
@ -235,6 +235,8 @@ private:
|
||||
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(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 */ \
|
||||
/* end of list */
|
||||
|
||||
|
||||
@ -950,6 +952,7 @@ namespace Selector
|
||||
CSelector DLL_LINKAGE typeSubtypeInfo(Bonus::BonusType type, TBonusSubtype subtype, si32 info);
|
||||
CSelector DLL_LINKAGE source(Bonus::BonusSource source, ui32 sourceID);
|
||||
CSelector DLL_LINKAGE sourceTypeSel(Bonus::BonusSource source);
|
||||
CSelector DLL_LINKAGE valueType(Bonus::ValueType valType);
|
||||
|
||||
/**
|
||||
* Selects all bonuses
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
#include "CHeroHandler.h" // for CHeroHandler
|
||||
#include "spells/CSpellHandler.h"// for CSpell
|
||||
#include "CSkillHandler.h"// for CSkill
|
||||
#include "NetPacks.h"
|
||||
#include "CBonusTypeHandler.h"
|
||||
#include "CModHandler.h"
|
||||
|
@ -82,12 +82,10 @@ public:
|
||||
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
|
||||
object->id = _ObjectID(index);
|
||||
|
||||
|
||||
assert(objects[index] == nullptr); // ensure that this id was not loaded before
|
||||
objects[index] = object;
|
||||
|
||||
registerObject(scope,type_name, name, object->id);
|
||||
|
||||
}
|
||||
|
||||
ConstTransitivePtr<_Object> operator[] (const _ObjectID id) const
|
||||
@ -96,7 +94,7 @@ public:
|
||||
|
||||
if (raw_id < 0 || raw_id >= objects.size())
|
||||
{
|
||||
logGlobal->error("%s id %d is invalid", getTypeName(), static_cast<si64>(raw_id));
|
||||
logMod->error("%s id %d is invalid", getTypeName(), static_cast<si64>(raw_id));
|
||||
throw std::runtime_error("internal error");
|
||||
}
|
||||
|
||||
|
@ -163,8 +163,8 @@ JsonNode JsonParser::parse(std::string fileName)
|
||||
|
||||
if (!errors.empty())
|
||||
{
|
||||
logGlobal->warn("File %s is not a valid JSON file!", fileName);
|
||||
logGlobal->warn(errors);
|
||||
logMod->warn("File %s is not a valid JSON file!", fileName);
|
||||
logMod->warn(errors);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
@ -395,7 +395,7 @@ std::shared_ptr<Bonus> JsonUtils::parseBonus (const JsonVector &ability_vec) //T
|
||||
auto it = bonusNameMap.find(type);
|
||||
if (it == bonusNameMap.end())
|
||||
{
|
||||
logGlobal->error("Error: invalid ability type %s", type);
|
||||
logMod->error("Error: invalid ability type %s", type);
|
||||
return b;
|
||||
}
|
||||
b->type = it->second;
|
||||
@ -413,7 +413,7 @@ const T & parseByMap(const std::map<std::string, T> & map, const JsonNode * val,
|
||||
auto it = map.find(type);
|
||||
if (it == map.end())
|
||||
{
|
||||
logGlobal->error("Error: invalid %s%s", err, type);
|
||||
logMod->error("Error: invalid %s%s", err, type);
|
||||
return defaultValue;
|
||||
}
|
||||
else
|
||||
@ -445,7 +445,7 @@ void JsonUtils::resolveIdentifier(si32 &var, const JsonNode &node, std::string n
|
||||
});
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("Error! Wrong identifier used for value of %s", name);
|
||||
logMod->error("Error! Wrong identifier used for value of %s", name);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -467,7 +467,7 @@ void JsonUtils::resolveIdentifier(const JsonNode &node, si32 &var)
|
||||
});
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("Error! Wrong identifier used for identifier!");
|
||||
logMod->error("Error! Wrong identifier used for identifier!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -489,7 +489,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
auto it = bonusNameMap.find(type);
|
||||
if (it == bonusNameMap.end())
|
||||
{
|
||||
logGlobal->error("Error: invalid ability type %s", type);
|
||||
logMod->error("Error: invalid ability type %s", type);
|
||||
return false;
|
||||
}
|
||||
b->type = it->second;
|
||||
@ -533,7 +533,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
}
|
||||
break;
|
||||
default:
|
||||
logGlobal->error("Error! Wrong bonus duration format.");
|
||||
logMod->error("Error! Wrong bonus duration format.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -580,7 +580,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
|
||||
auto it = bonusNameMap.find(anotherBonusType);
|
||||
if (it == bonusNameMap.end())
|
||||
{
|
||||
logGlobal->error("Error: invalid ability type %s", anotherBonusType);
|
||||
logMod->error("Error: invalid ability type %s", anotherBonusType);
|
||||
continue;
|
||||
}
|
||||
l2->type = it->second;
|
||||
@ -723,8 +723,8 @@ bool JsonUtils::validate(const JsonNode &node, std::string schemaName, std::stri
|
||||
std::string log = Validation::check(schemaName, node);
|
||||
if (!log.empty())
|
||||
{
|
||||
logGlobal->warn("Data in %s is invalid!", dataName);
|
||||
logGlobal->warn(log);
|
||||
logMod->warn("Data in %s is invalid!", dataName);
|
||||
logMod->warn(log);
|
||||
}
|
||||
return log.empty();
|
||||
}
|
||||
@ -745,7 +745,7 @@ const JsonNode & getSchemaByName(std::string name)
|
||||
return loadedSchemas[name];
|
||||
}
|
||||
|
||||
logGlobal->error("Error: missing schema with name %s!", name);
|
||||
logMod->error("Error: missing schema with name %s!", name);
|
||||
assert(0);
|
||||
return nullNode;
|
||||
}
|
||||
@ -756,7 +756,7 @@ const JsonNode & JsonUtils::getSchema(std::string URI)
|
||||
size_t posHash = URI.find('#');
|
||||
if(posColon == std::string::npos)
|
||||
{
|
||||
logGlobal->error("Invalid schema URI:%s", URI);
|
||||
logMod->error("Invalid schema URI:%s", URI);
|
||||
return nullNode;
|
||||
}
|
||||
|
||||
@ -765,7 +765,7 @@ const JsonNode & JsonUtils::getSchema(std::string URI)
|
||||
|
||||
if(protocolName != "vcmi")
|
||||
{
|
||||
logGlobal->error("Error: unsupported URI protocol for schema: %s", URI);
|
||||
logMod->error("Error: unsupported URI protocol for schema: %s", URI);
|
||||
return nullNode;
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "CTownHandler.h"
|
||||
#include "CBuildingHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "CModHandler.h"
|
||||
#include "IGameEventsReceiver.h"
|
||||
@ -113,6 +114,8 @@ void LibClasses::init()
|
||||
|
||||
createHandler(spellh, "Spell", pomtime);
|
||||
|
||||
createHandler(skillh, "Skill", pomtime);
|
||||
|
||||
createHandler(terviewh, "Terrain view pattern", pomtime);
|
||||
|
||||
createHandler(tplh, "Template", pomtime); //templates need already resolved identifiers (refactor?)
|
||||
@ -137,6 +140,7 @@ void LibClasses::clear()
|
||||
delete objh;
|
||||
delete objtypeh;
|
||||
delete spellh;
|
||||
delete skillh;
|
||||
delete modh;
|
||||
delete bth;
|
||||
delete tplh;
|
||||
@ -154,6 +158,7 @@ void LibClasses::makeNull()
|
||||
objh = nullptr;
|
||||
objtypeh = nullptr;
|
||||
spellh = nullptr;
|
||||
skillh = nullptr;
|
||||
modh = nullptr;
|
||||
bth = nullptr;
|
||||
tplh = nullptr;
|
||||
|
@ -14,6 +14,7 @@ class CArtHandler;
|
||||
class CHeroHandler;
|
||||
class CCreatureHandler;
|
||||
class CSpellHandler;
|
||||
class CSkillHandler;
|
||||
class CBuildingHandler;
|
||||
class CObjectHandler;
|
||||
class CObjectClassesHandler;
|
||||
@ -41,6 +42,7 @@ public:
|
||||
CHeroHandler * heroh;
|
||||
CCreatureHandler * creh;
|
||||
CSpellHandler * spellh;
|
||||
CSkillHandler * skillh;
|
||||
CObjectHandler * objh;
|
||||
CObjectClassesHandler * objtypeh;
|
||||
CTownHandler * townh;
|
||||
@ -67,6 +69,10 @@ public:
|
||||
h & objh;
|
||||
h & objtypeh;
|
||||
h & spellh;
|
||||
if(version >= 777)
|
||||
{
|
||||
h & skillh;
|
||||
}
|
||||
h & modh;
|
||||
h & IS_AI_ENABLED;
|
||||
h & bth;
|
||||
|
@ -601,14 +601,14 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
|
||||
for(int i = 0; i < ARRAY_COUNT(tacticLvls); i++)
|
||||
{
|
||||
if(heroes[i])
|
||||
tacticLvls[i] += heroes[i]->getSecSkillLevel(SecondarySkill::TACTICS);
|
||||
tacticLvls[i] += heroes[i]->valOfBonuses(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::TACTICS));
|
||||
}
|
||||
int tacticsSkillDiff = tacticLvls[0] - tacticLvls[1];
|
||||
|
||||
if(tacticsSkillDiff && isTacticsAllowed)
|
||||
{
|
||||
curB->tacticsSide = tacticsSkillDiff < 0;
|
||||
curB->tacticDistance = std::abs(tacticsSkillDiff)*2 + 1;
|
||||
curB->tacticDistance = std::abs(tacticsSkillDiff);
|
||||
}
|
||||
else
|
||||
curB->tacticDistance = 0;
|
||||
|
@ -26,8 +26,9 @@ CFilesystemLoader::CFilesystemLoader(std::string _mountPoint, bfs::path baseDire
|
||||
std::unique_ptr<CInputStream> CFilesystemLoader::load(const ResourceID & resourceName) const
|
||||
{
|
||||
assert(fileList.count(resourceName));
|
||||
|
||||
return make_unique<CFileInputStream>(baseDirectory / fileList.at(resourceName));
|
||||
bfs::path file = baseDirectory / fileList.at(resourceName);
|
||||
logGlobal->trace("loading %s", file.string());
|
||||
return make_unique<CFileInputStream>(file);
|
||||
}
|
||||
|
||||
bool CFilesystemLoader::existsResource(const ResourceID & resourceName) const
|
||||
|
@ -83,6 +83,7 @@ DLL_LINKAGE vstd::CLoggerBase * logBonus = CLogger::getLogger(CLoggerDomain("bon
|
||||
DLL_LINKAGE vstd::CLoggerBase * logNetwork = CLogger::getLogger(CLoggerDomain("network"));
|
||||
DLL_LINKAGE vstd::CLoggerBase * logAi = CLogger::getLogger(CLoggerDomain("ai"));
|
||||
DLL_LINKAGE vstd::CLoggerBase * logAnim = CLogger::getLogger(CLoggerDomain("animation"));
|
||||
DLL_LINKAGE vstd::CLoggerBase * logMod = CLogger::getLogger(CLoggerDomain("mod"));
|
||||
|
||||
CLogger * CLogger::getLogger(const CLoggerDomain & domain)
|
||||
{
|
||||
|
@ -266,7 +266,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
{
|
||||
const CSpell * spell = spellId.toSpell();
|
||||
iw.text.addTxt (MetaString::SPELL_NAME, spellId);
|
||||
if(spell->level <= hero->getSecSkillLevel(SecondarySkill::WISDOM) + 2)
|
||||
if(spell->level <= hero->maxSpellLevel())
|
||||
{
|
||||
if(hero->canLearnSpell(spell))
|
||||
{
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "../CModHandler.h"
|
||||
#include "../CSoundBase.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CSkillHandler.h"
|
||||
#include "CObjectClassesHandler.h"
|
||||
#include "../IGameCallback.h"
|
||||
#include "../CGameState.h"
|
||||
@ -86,7 +87,7 @@ ui32 CGHeroInstance::getTileCost(const TerrainTile &dest, const TerrainTile &fro
|
||||
else if(ti->nativeTerrain != from.terType && !ti->hasBonusOfType(Bonus::NO_TERRAIN_PENALTY, from.terType))
|
||||
{
|
||||
ret = VLC->heroh->terrCosts[from.terType];
|
||||
ret -= getSecSkillLevel(SecondarySkill::PATHFINDING) * 25;
|
||||
ret -= valOfBonuses(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::PATHFINDING));
|
||||
if(ret < GameConstants::BASE_MOVEMENT_COST)
|
||||
ret = GameConstants::BASE_MOVEMENT_COST;
|
||||
}
|
||||
@ -760,91 +761,26 @@ void CGHeroInstance::recreateSecondarySkillsBonuses()
|
||||
removeBonus(bonus);
|
||||
|
||||
for(auto skill_info : secSkills)
|
||||
updateSkill(SecondarySkill(skill_info.first), skill_info.second);
|
||||
for(int level = 1; level <= skill_info.second; level++)
|
||||
updateSkill(SecondarySkill(skill_info.first), level);
|
||||
}
|
||||
|
||||
void CGHeroInstance::updateSkill(SecondarySkill which, int val)
|
||||
{
|
||||
if(which == SecondarySkill::LEADERSHIP || which == SecondarySkill::LUCK)
|
||||
{ //luck-> VLC->generaltexth->arraytxt[73+luckSkill]; VLC->generaltexth->arraytxt[104+moraleSkill]
|
||||
bool luck = which == SecondarySkill::LUCK;
|
||||
Bonus::BonusType type[] = {Bonus::MORALE, Bonus::LUCK};
|
||||
|
||||
auto b = getBonusLocalFirst(Selector::type(type[luck]).And(Selector::sourceType(Bonus::SECONDARY_SKILL)));
|
||||
if(!b)
|
||||
{
|
||||
b = std::make_shared<Bonus>(Bonus::PERMANENT, type[luck], Bonus::SECONDARY_SKILL, +val, which, which, Bonus::BASE_NUMBER);
|
||||
addNewBonus(b);
|
||||
}
|
||||
auto skillBonus = (*VLC->skillh)[which]->getBonus(val);
|
||||
for (auto b : skillBonus)
|
||||
{
|
||||
// bonuses provided by different levels of a secondary skill are aggregated via max (not + as usual)
|
||||
// different secondary skills providing the same bonus (e.g. ballistics might improve archery as well) are kept separate
|
||||
std::shared_ptr<Bonus> existing = getBonusLocalFirst(
|
||||
Selector::typeSubtype(b->type, b->subtype).And(
|
||||
Selector::source(Bonus::SECONDARY_SKILL, b->sid).And(
|
||||
Selector::valueType(b->valType))));
|
||||
if(existing)
|
||||
vstd::amax(existing->val, b->val);
|
||||
else
|
||||
b->val = +val;
|
||||
addNewBonus(std::make_shared<Bonus>(*b));
|
||||
}
|
||||
else if(which == SecondarySkill::DIPLOMACY) //surrender discount: 20% per level
|
||||
{
|
||||
|
||||
if(auto b = getBonusLocalFirst(Selector::type(Bonus::SURRENDER_DISCOUNT).And(Selector::sourceType(Bonus::SECONDARY_SKILL))))
|
||||
b->val = +val;
|
||||
else
|
||||
addNewBonus(std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::SURRENDER_DISCOUNT, Bonus::SECONDARY_SKILL, val * 20, which));
|
||||
}
|
||||
|
||||
int skillVal = 0;
|
||||
switch (which)
|
||||
{
|
||||
case SecondarySkill::ARCHERY:
|
||||
switch (val)
|
||||
{
|
||||
case 1:
|
||||
skillVal = 10; break;
|
||||
case 2:
|
||||
skillVal = 25; break;
|
||||
case 3:
|
||||
skillVal = 50; break;
|
||||
}
|
||||
break;
|
||||
case SecondarySkill::LOGISTICS:
|
||||
skillVal = 10 * val; break;
|
||||
case SecondarySkill::NAVIGATION:
|
||||
skillVal = 50 * val; break;
|
||||
case SecondarySkill::MYSTICISM:
|
||||
skillVal = val; break;
|
||||
case SecondarySkill::EAGLE_EYE:
|
||||
skillVal = 30 + 10 * val; break;
|
||||
case SecondarySkill::NECROMANCY:
|
||||
skillVal = 10 * val; break;
|
||||
case SecondarySkill::LEARNING:
|
||||
skillVal = 5 * val; break;
|
||||
case SecondarySkill::OFFENCE:
|
||||
skillVal = 10 * val; break;
|
||||
case SecondarySkill::ARMORER:
|
||||
skillVal = 5 * val; break;
|
||||
case SecondarySkill::INTELLIGENCE:
|
||||
skillVal = 25 << (val-1); break;
|
||||
case SecondarySkill::SORCERY:
|
||||
skillVal = 5 * val; break;
|
||||
case SecondarySkill::RESISTANCE:
|
||||
skillVal = 5 << (val-1); break;
|
||||
case SecondarySkill::FIRST_AID:
|
||||
skillVal = 25 + 25*val; break;
|
||||
case SecondarySkill::ESTATES:
|
||||
skillVal = 125 << (val-1); break;
|
||||
}
|
||||
|
||||
|
||||
Bonus::ValueType skillValType = skillVal ? Bonus::BASE_NUMBER : Bonus::INDEPENDENT_MIN;
|
||||
if(auto b = getExportedBonusList().getFirst(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, which)
|
||||
.And(Selector::sourceType(Bonus::SECONDARY_SKILL)))) //only local hero bonus
|
||||
{
|
||||
b->val = skillVal;
|
||||
b->valType = skillValType;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto bonus = std::make_shared<Bonus>(Bonus::PERMANENT, Bonus::SECONDARY_SKILL_PREMY, Bonus::SECONDARY_SKILL, skillVal, id.getNum(), which, skillValType);
|
||||
bonus->source = Bonus::SECONDARY_SKILL;
|
||||
addNewBonus(bonus);
|
||||
}
|
||||
|
||||
CBonusSystemNode::treeHasChanged();
|
||||
}
|
||||
void CGHeroInstance::setPropertyDer( ui8 what, ui32 val )
|
||||
@ -890,7 +826,9 @@ ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell, int *outSelectedSc
|
||||
|
||||
spell->forEachSchool([&, this](const SpellSchoolInfo & cnf, bool & stop)
|
||||
{
|
||||
int thisSchool = std::max<int>(getSecSkillLevel(cnf.skill), valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << ((ui8)cnf.id))); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
|
||||
int thisSchool = std::max<int>(
|
||||
valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, cnf.skill),
|
||||
valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 1 << ((ui8)cnf.id))); //FIXME: Bonus shouldn't be additive (Witchking Artifacts : Crown of Skies)
|
||||
if(thisSchool > skill)
|
||||
{
|
||||
skill = thisSchool;
|
||||
@ -1021,7 +959,7 @@ bool CGHeroInstance::canLearnSpell(const CSpell * spell) const
|
||||
if(!hasSpellbook())
|
||||
return false;
|
||||
|
||||
if(spell->level > getSecSkillLevel(SecondarySkill::WISDOM) + 2) //not enough wisdom
|
||||
if(spell->level > maxSpellLevel()) //not enough wisdom
|
||||
return false;
|
||||
|
||||
if(vstd::contains(spells, spell->id))//already known
|
||||
@ -1135,7 +1073,7 @@ int3 CGHeroInstance::getSightCenter() const
|
||||
|
||||
int CGHeroInstance::getSightRadius() const
|
||||
{
|
||||
return 5 + getSecSkillLevel(SecondarySkill::SCOUTING) + valOfBonuses(Bonus::SIGHT_RADIOUS); //default + scouting
|
||||
return 5 + valOfBonuses(Bonus::SIGHT_RADIOUS); // scouting gives SIGHT_RADIUS bonus
|
||||
}
|
||||
|
||||
si32 CGHeroInstance::manaRegain() const
|
||||
@ -1143,7 +1081,7 @@ si32 CGHeroInstance::manaRegain() const
|
||||
if (hasBonusOfType(Bonus::FULL_MANA_REGENERATION))
|
||||
return manaLimit();
|
||||
|
||||
return 1 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, 8) + valOfBonuses(Bonus::MANA_REGENERATION); //1 + Mysticism level
|
||||
return 1 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::MYSTICISM) + valOfBonuses(Bonus::MANA_REGENERATION); //1 + Mysticism level
|
||||
}
|
||||
|
||||
si32 CGHeroInstance::getManaNewTurn() const
|
||||
@ -1239,6 +1177,11 @@ bool CGHeroInstance::hasSpellbook() const
|
||||
return getArt(ArtifactPosition::SPELLBOOK);
|
||||
}
|
||||
|
||||
int CGHeroInstance::maxSpellLevel() const
|
||||
{
|
||||
return std::min(GameConstants::SPELL_LEVELS, 2 + valOfBonuses(Selector::typeSubtype(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::WISDOM)));
|
||||
}
|
||||
|
||||
void CGHeroInstance::deserializationFix()
|
||||
{
|
||||
artDeserializationFix(this);
|
||||
|
@ -146,6 +146,7 @@ public:
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool hasSpellbook() const;
|
||||
int maxSpellLevel() const;
|
||||
EAlignment::EAlignment getAlignment() const;
|
||||
const std::string &getBiography() const;
|
||||
bool needsLastStack()const override;
|
||||
@ -302,5 +303,7 @@ public:
|
||||
h & visitedObjects;
|
||||
BONUS_TREE_DESERIALIZATION_FIX
|
||||
//visitied town pointer will be restored by map serialization method
|
||||
if(version < 777 && !h.saving)
|
||||
recreateSecondarySkillsBonuses();
|
||||
}
|
||||
};
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include "../CSoundBase.h"
|
||||
#include "../CModHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CSkillHandler.h"
|
||||
#include "CObjectClassesHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../IGameCallback.h"
|
||||
@ -323,17 +324,18 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
|
||||
if(count*2 > totalCount)
|
||||
sympathy++; // 2 - hero have similar creatures more that 50%
|
||||
|
||||
int charisma = powerFactor + h->getSecSkillLevel(SecondarySkill::DIPLOMACY) + sympathy;
|
||||
int diplomacy = h->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::DIPLOMACY);
|
||||
int charisma = powerFactor + diplomacy + sympathy;
|
||||
|
||||
if(charisma < character)
|
||||
return FIGHT;
|
||||
|
||||
if (allowJoin)
|
||||
{
|
||||
if(h->getSecSkillLevel(SecondarySkill::DIPLOMACY) + sympathy + 1 >= character)
|
||||
if(diplomacy + sympathy + 1 >= character)
|
||||
return JOIN_FOR_FREE;
|
||||
|
||||
else if(h->getSecSkillLevel(SecondarySkill::DIPLOMACY) * 2 + sympathy + 1 >= character)
|
||||
else if(diplomacy * 2 + sympathy + 1 >= character)
|
||||
return VLC->creh->creatures[subID]->cost[6] * getStackCount(SlotID(0)); //join for gold
|
||||
}
|
||||
|
||||
@ -1475,7 +1477,7 @@ std::string CGWitchHut::getHoverText(PlayerColor player) const
|
||||
if(wasVisited(player))
|
||||
{
|
||||
hoverName += "\n" + VLC->generaltexth->allTexts[356]; // + (learn %s)
|
||||
boost::algorithm::replace_first(hoverName,"%s",VLC->generaltexth->skillName[ability]);
|
||||
boost::algorithm::replace_first(hoverName, "%s", VLC->skillh->skillName(ability));
|
||||
}
|
||||
return hoverName;
|
||||
}
|
||||
@ -1601,7 +1603,7 @@ void CGShrine::onHeroVisit( const CGHeroInstance * h ) const
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,174);
|
||||
}
|
||||
else if(ID == Obj::SHRINE_OF_MAGIC_THOUGHT && !h->getSecSkillLevel(SecondarySkill::WISDOM)) //it's third level spell and hero doesn't have wisdom
|
||||
else if(ID == Obj::SHRINE_OF_MAGIC_THOUGHT && h->maxSpellLevel() < 3) //it's third level spell and hero doesn't have wisdom
|
||||
{
|
||||
iw.text.addTxt(MetaString::ADVOB_TXT,130);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../ConstTransitivePtr.h"
|
||||
#include "../GameConstants.h"
|
||||
|
||||
const ui32 SERIALIZATION_VERSION = 776;
|
||||
const ui32 SERIALIZATION_VERSION = 777;
|
||||
const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
|
||||
const std::string SAVEGAME_MAGIC = "VCMISVG";
|
||||
|
||||
|
@ -819,7 +819,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string &
|
||||
|
||||
spell->name = json["name"].String();
|
||||
|
||||
logGlobal->trace("%s: loading spell %s", __FUNCTION__, spell->name);
|
||||
logMod->trace("%s: loading spell %s", __FUNCTION__, spell->name);
|
||||
|
||||
const auto schoolNames = json["school"];
|
||||
|
||||
@ -854,7 +854,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string &
|
||||
else if(targetType == "LOCATION")
|
||||
spell->targetType = CSpell::LOCATION;
|
||||
else
|
||||
logGlobal->warn("Spell %s: target type %s - assumed NO_TARGET.", spell->name, (targetType.empty() ? "empty" : "unknown ("+targetType+")"));
|
||||
logMod->warn("Spell %s: target type %s - assumed NO_TARGET.", spell->name, (targetType.empty() ? "empty" : "unknown ("+targetType+")"));
|
||||
|
||||
for(const auto & counteredSpell: json["counters"].Struct())
|
||||
if (counteredSpell.second.Bool())
|
||||
@ -899,7 +899,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string &
|
||||
else if(!implicitPositiveness)
|
||||
{
|
||||
spell->positiveness = CSpell::NEUTRAL; //duplicates constructor but, just in case
|
||||
logGlobal->error("Spell %s: no positiveness specified, assumed NEUTRAL.", spell->name);
|
||||
logMod->error("Spell %s: no positiveness specified, assumed NEUTRAL.", spell->name);
|
||||
}
|
||||
|
||||
spell->isSpecial = flags["special"].Bool();
|
||||
@ -909,7 +909,7 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json, const std::string &
|
||||
auto it = bonusNameMap.find(name);
|
||||
if(it == bonusNameMap.end())
|
||||
{
|
||||
logGlobal->error("Spell %s: invalid bonus name %s", spell->name, name);
|
||||
logMod->error("Spell %s: invalid bonus name %s", spell->name, name);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -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;
|
||||
@ -1974,7 +1972,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);
|
||||
@ -1984,7 +1982,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++)
|
||||
{
|
||||
@ -2490,19 +2488,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;
|
||||
@ -4006,19 +4006,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
|
||||
|
||||
@ -4070,7 +4069,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);
|
||||
@ -5798,9 +5797,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;
|
||||
@ -5830,7 +5830,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,
|
||||
@ -5858,7 +5858,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();
|
||||
|
Loading…
Reference in New Issue
Block a user