1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

- support for new heroes and hero classes

- moved hero-specific data from text handler to CHero
- moved hero classes-specific data into heroClasses.json
This commit is contained in:
Ivan Savenko 2012-12-16 13:47:53 +00:00
parent 99e7177d57
commit e36bc50504
18 changed files with 572 additions and 430 deletions

View File

@ -239,10 +239,13 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
//loading hero animations
if(hero1) // attacking hero
{
int type = hero1->type->heroClass->id;
if ( type % 2 ) type--;
if ( hero1->sex ) type++;
attackingHero = new CBattleHero(graphics->battleHeroes[type], false, hero1->tempOwner, hero1->tempOwner == curInt->playerID ? hero1 : NULL, this);
std::string battleImage;
if ( hero1->sex )
battleImage = hero1->type->heroClass->imageBattleFemale;
else
battleImage = hero1->type->heroClass->imageBattleMale;
attackingHero = new CBattleHero(battleImage, false, hero1->tempOwner, hero1->tempOwner == curInt->playerID ? hero1 : NULL, this);
attackingHero->pos = genRect(attackingHero->dh->ourImages[0].bitmap->h, attackingHero->dh->ourImages[0].bitmap->w, pos.x - 43, pos.y - 19);
}
else
@ -251,10 +254,13 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
}
if(hero2) // defending hero
{
int type = hero2->type->heroClass->id;
if ( type % 2 ) type--;
if ( hero2->sex ) type++;
defendingHero = new CBattleHero(graphics->battleHeroes[type ], true, hero2->tempOwner, hero2->tempOwner == curInt->playerID ? hero2 : NULL, this);
std::string battleImage;
if ( hero2->sex )
battleImage = hero1->type->heroClass->imageBattleFemale;
else
battleImage = hero1->type->heroClass->imageBattleMale;
defendingHero = new CBattleHero(battleImage, true, hero2->tempOwner, hero2->tempOwner == curInt->playerID ? hero2 : NULL, this);
defendingHero->pos = genRect(defendingHero->dh->ourImages[0].bitmap->h, defendingHero->dh->ourImages[0].bitmap->w, pos.x + 693, pos.y - 19);
}
else

View File

@ -175,8 +175,8 @@ void CHeroWindow::update(const CGHeroInstance * hero, bool redrawNeeded /*= fals
assert(hero == curHero);
specArea->text = CGI->generaltexth->hTxts[curHero->subID].longBonus;
specImage->setFrame(curHero->subID);
specArea->text = curHero->type->specDescr;
specImage->setFrame(curHero->type->imageIndex);
tacticsButton->callback.clear();
tacticsButton->callback2.clear();
@ -378,7 +378,7 @@ void CHeroWindow::showAll(SDL_Surface * to)
//printing special ability
printAtLoc(CGI->generaltexth->jktexts[5].substr(1, CGI->generaltexth->jktexts[5].size()-2), 69, 183, FONT_SMALL, Colors::YELLOW, to);
printAtLoc(CGI->generaltexth->hTxts[curHero->subID].bonusName, 69, 205, FONT_SMALL, Colors::WHITE, to);
printAtLoc(curHero->type->specName, 69, 205, FONT_SMALL, Colors::WHITE, to);
//printing necessery texts
printAtLoc(CGI->generaltexth->jktexts[6].substr(1, CGI->generaltexth->jktexts[6].size()-2), 69, 232, FONT_SMALL, Colors::YELLOW, to);

View File

@ -171,7 +171,7 @@ std::string InfoBoxAbstractHeroData::getNameText()
return text.substr(begin, end-begin);
}
case HERO_SPECIAL:
return CGI->generaltexth->hTxts[getSubID()].bonusName;
return CGI->heroh->heroes[getSubID()]->specName;
case HERO_SECONDARY_SKILL:
if (getValue())
return CGI->generaltexth->skillName[getSubID()];
@ -237,6 +237,7 @@ size_t InfoBoxAbstractHeroData::getImageIndex()
switch (type)
{
case HERO_SPECIAL:
return VLC->heroh->heroes[getSubID()]->imageIndex;
case HERO_PRIMARY_SKILL:
return getSubID();
case HERO_MANA:
@ -262,7 +263,7 @@ bool InfoBoxAbstractHeroData::prepareMessage(std::string &text, CComponent **com
switch (type)
{
case HERO_SPECIAL:
text = CGI->generaltexth->hTxts[getSubID()].longBonus;
text = CGI->heroh->heroes[getSubID()]->specDescr;
*comp = NULL;
return true;
case HERO_PRIMARY_SKILL:
@ -918,7 +919,7 @@ CHeroItem::CHeroItem(const CGHeroInstance* Hero, CArtifactsOfHero::SCommonPart *
garr = new CGarrisonInt(6, 78, 4, Point(), NULL, Point(), hero, NULL, true, true);
portrait = new CAnimImage("PortraitsLarge", hero->subID, 0, 5, 6);
portrait = new CAnimImage("PortraitsLarge", hero->portrait, 0, 5, 6);
heroArea = new CHeroArea(5, 6, hero);
name = new CLabel(73, 7, FONT_SMALL, TOPLEFT, Colors::WHITE, hero->name);

View File

@ -2640,7 +2640,7 @@ size_t OptionsTab::CPlayerSettingsHelper::getImageIndex()
{
if(settings.heroPortrait >= 0)
return settings.heroPortrait;
return settings.hero;
return CGI->heroh->heroes[settings.hero]->imageIndex;
}
}
@ -2870,10 +2870,10 @@ void OptionsTab::CPregameTooltipBox::genHeroWindow()
genHeader();
// speciality
new CAnimImage("UN44", settings.hero, 0, pos.w / 2 - 22, 134);
new CAnimImage("UN44", CGI->heroh->heroes[settings.hero]->imageIndex, 0, pos.w / 2 - 22, 134);
new CLabel(pos.w / 2 + 4, 117, FONT_MEDIUM, CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[78]);
new CLabel(pos.w / 2, 188, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->hTxts[settings.hero].bonusName);
new CLabel(pos.w / 2, 188, FONT_SMALL, CENTER, Colors::WHITE, CGI->heroh->heroes[settings.hero]->specName);
}
void OptionsTab::CPregameTooltipBox::genBonusWindow()

View File

@ -943,7 +943,7 @@ size_t CComponent::getIndex()
case morale: return val+3;
case luck: return val+3;
case building: return val;
case hero: return subtype;
case hero: return CGI->heroh->heroes[subtype]->imageIndex;
case flag: return subtype;
}
assert(0);
@ -3706,7 +3706,7 @@ CTavernWindow::HeroPortrait::HeroPortrait(int &sel, int id, int x, int y, const
h->name.c_str(), h->level, h->type->heroClass->name.c_str(), artifs);
descr[sizeof(descr)-1] = '\0';
new CAnimImage("portraitsLarge", h->subID);
new CAnimImage("portraitsLarge", h->portrait);
}
}
@ -4965,7 +4965,7 @@ void CExchangeWindow::prepareBackground()
}
//hero's specialty
new CAnimImage("UN32", heroInst[b]->subID, 0, 67 + 490*b, 45);
new CAnimImage("UN32", heroInst[b]->type->imageIndex, 0, 67 + 490*b, 45);
//experience
new CAnimImage("PSKIL32", 4, 0, 103 + 490*b, 45);
@ -5042,7 +5042,7 @@ CExchangeWindow::CExchangeWindow(si32 hero1, si32 hero2):
speciality[b] = new LRClickableAreaWText();
speciality[b]->pos = genRect(32, 32, pos.x + 69 + 490*b, pos.y + 45);
speciality[b]->hoverText = CGI->generaltexth->heroscrn[27];
speciality[b]->text = CGI->generaltexth->hTxts[heroInst[b]->subID].longBonus;
speciality[b]->text = heroInst[b]->type->specDescr;
experience[b] = new LRClickableAreaWText();
experience[b]->pos = genRect(32, 32, pos.x + 105 + 490*b, pos.y + 45);

View File

@ -9,6 +9,7 @@
#include "CGameInfo.h"
#include "../lib/VCMI_Lib.h"
#include "../CCallback.h"
#include "../lib/CHeroHandler.h"
#include "../lib/CTownHandler.h"
#include "../lib/CObjectHandler.h"
#include "../lib/CGeneralTextHandler.h"
@ -103,16 +104,6 @@ void Graphics::initializeBattleGraphics()
idx++;
}
//initializing battle hero animation
idx = config["heroes"].Vector().size();
battleHeroes.resize(idx);
idx = 0;
BOOST_FOREACH(const JsonNode &h, config["heroes"].Vector()) {
battleHeroes[idx] = h.String();
idx ++;
}
//initialization of AC->def name mapping
BOOST_FOREACH(const JsonNode &ac, config["ac_mapping"].Vector()) {
int ACid = ac["id"].Float();
@ -169,22 +160,26 @@ void Graphics::loadHeroAnims()
std::vector<std::pair<int,int> > rotations; //first - group number to be rotated1, second - group number after rotation1
rotations += std::make_pair(6,10), std::make_pair(7,11), std::make_pair(8,12), std::make_pair(1,13),
std::make_pair(2,14), std::make_pair(3,15);
for(size_t i=0; i<GameConstants::F_NUMBER * 2; ++i)
for(size_t i=0; i<CGI->heroh->classes.heroClasses.size(); ++i)
{
std::ostringstream nm;
nm << "AH" << std::setw(2) << std::setfill('0') << i << "_.DEF";
loadHeroAnim(nm.str(), rotations, &Graphics::heroAnims);
const CHeroClass * hc = CGI->heroh->classes.heroClasses[i];
if (!vstd::contains(heroAnims, hc->imageMapFemale))
heroAnims[hc->imageMapFemale] = loadHeroAnim(hc->imageMapFemale, rotations);
if (!vstd::contains(heroAnims, hc->imageMapMale))
heroAnims[hc->imageMapMale] = loadHeroAnim(hc->imageMapMale, rotations);
}
loadHeroAnim("AB01_.DEF", rotations, &Graphics::boatAnims);
loadHeroAnim("AB02_.DEF", rotations, &Graphics::boatAnims);
loadHeroAnim("AB03_.DEF", rotations, &Graphics::boatAnims);
boatAnims.push_back(loadHeroAnim("AB01_.DEF", rotations));
boatAnims.push_back(loadHeroAnim("AB02_.DEF", rotations));
boatAnims.push_back(loadHeroAnim("AB03_.DEF", rotations));
}
void Graphics::loadHeroAnim( const std::string &name, const std::vector<std::pair<int,int> > &rotations, std::vector<CDefEssential *> Graphics::*dst )
CDefEssential * Graphics::loadHeroAnim( const std::string &name, const std::vector<std::pair<int,int> > &rotations)
{
CDefEssential *anim = CDefHandler::giveDefEss(name);
(this->*dst).push_back(anim);
int pom = 0; //how many groups has been rotated
for(int o=7; pom<6; ++o)
{
@ -216,6 +211,7 @@ void Graphics::loadHeroAnim( const std::string &name, const std::vector<std::pai
{
CSDL_Ext::alphaTransform(anim->ourImages[ff].bitmap);
}
return anim;
}
void Graphics::loadHeroFlags(std::pair<std::vector<CDefEssential *> Graphics::*, std::vector<const char *> > &pr, bool mode)

View File

@ -49,7 +49,7 @@ public:
CDefEssential * resources32; //resources 32x32
CDefEssential * flags;
CDefEssential * heroMoveArrows;
std::vector<CDefEssential *> heroAnims; // [class id: 0 - 17] //added group 10: up - left, 11 - left and 12 - left down // 13 - up-left standing; 14 - left standing; 15 - left down standing
std::map<std::string, CDefEssential *> heroAnims; // [hero class def name] //added group 10: up - left, 11 - left and 12 - left down // 13 - up-left standing; 14 - left standing; 15 - left down standing
std::vector<CDefEssential *> boatAnims; // [boat type: 0 - 3] //added group 10: up - left, 11 - left and 12 - left down // 13 - up-left standing; 14 - left standing; 15 - left down standing
CDefHandler * FoWfullHide; //for Fog of War
CDefHandler * FoWpartialHide; //for For of War
@ -63,7 +63,6 @@ public:
std::map<int, std::string> ERMUtoPicture[GameConstants::F_NUMBER]; //maps building ID to it's picture's name for each town type
//for battles
std::vector< std::vector< std::string > > battleBacks; //battleBacks[terType] - vector of possible names for certain terrain type
std::vector< std::string > battleHeroes; //battleHeroes[hero type] - name of def that has hero animation for battle
std::map< int, std::vector < std::string > > battleACToDef; //maps AC format to vector of appropriate def names
CDefEssential * spellEffectsPics; //bitmaps representing spells affecting a stack in battle
//spells
@ -75,7 +74,7 @@ public:
void loadHeroFlags();
void loadHeroFlags(std::pair<std::vector<CDefEssential *> Graphics::*, std::vector<const char *> > &pr, bool mode);
void loadHeroAnims();
void loadHeroAnim(const std::string &name, const std::vector<std::pair<int,int> > &rotations, std::vector<CDefEssential *> Graphics::*dst);
CDefEssential * loadHeroAnim(const std::string &name, const std::vector<std::pair<int,int> > &rotations);
void loadErmuToPicture();
void blueToPlayersAdv(SDL_Surface * sur, int player); //replaces blue interface colour with a color of player
void loadTrueType();

View File

@ -566,9 +566,12 @@ void CMapHandler::terrainRect( int3 top_tile, ui8 anim, const std::vector< std::
dir = themp->moveDir;
//pick graphics of hero (or boat if hero is sailing)
iv = (themp->boat)
? &graphics->boatAnims[themp->boat->subID]->ourImages
: &graphics->heroAnims[themp->type->heroClass->id]->ourImages;
if (themp->boat)
iv = &graphics->boatAnims[themp->boat->subID]->ourImages;
else if (themp->sex)
iv = &graphics->heroAnims[themp->type->heroClass->imageMapFemale]->ourImages;
else
iv = &graphics->heroAnims[themp->type->heroClass->imageMapMale]->ourImages;
//pick appropriate flag set
if(themp->boat)

View File

@ -28,29 +28,6 @@
"CMBKDECK.BMP"
],
// Hero animation used in battles.
// Each 2 def represent male and female heroes for each race
"heroes": [
"CH00.DEF",
"CH01.DEF",
"CH02.DEF",
"CH03.DEF",
"CH05.DEF",
"CH04.DEF",
"CH06.DEF",
"CH07.DEF",
"CH08.DEF",
"CH09.DEF",
"CH010.DEF",
"CH11.DEF",
"CH013.DEF",
"CH012.DEF",
"CH014.DEF",
"CH015.DEF",
"CH16.DEF",
"CH17.DEF"
],
// WoG_Ac_format_to_def_names_mapping
"ac_mapping": [
{ "id": 0, "defnames": [ "C10SPW.DEF" ] },

38
config/heroClasses.json Normal file
View File

@ -0,0 +1,38 @@
{
// battle animations for heroes, ordered by faction
"heroBattleAnim" :
[
{ "male" : "CH00.DEF", "female" : "CH01.DEF" },
{ "male" : "CH02.DEF", "female" : "CH03.DEF" },
{ "male" : "CH05.DEF", "female" : "CH04.DEF" },
{ "male" : "CH06.DEF", "female" : "CH07.DEF" },
{ "male" : "CH08.DEF", "female" : "CH09.DEF" },
{ "male" : "CH010.DEF", "female" : "CH11.DEF" },
{ "male" : "CH013.DEF", "female" : "CH012.DEF" },
{ "male" : "CH014.DEF", "female" : "CH015.DEF" },
{ "male" : "CH16.DEF", "female" : "CH17.DEF" }
],
// map animations for heroes, ordered by hero class
"heroMapAnim" :
[
"AH00_.def",
"AH01_.def",
"AH02_.def",
"AH03_.def",
"AH04_.def",
"AH05_.def",
"AH06_.def",
"AH07_.def",
"AH08_.def",
"AH09_.def",
"AH10_.def",
"AH11_.def",
"AH12_.def",
"AH13_.def",
"AH14_.def",
"AH15_.def",
"AH16_.def",
"AH17_.def"
]
}

File diff suppressed because it is too large Load Diff

View File

@ -189,25 +189,6 @@ void CGeneralTextHandler::load()
}
while (parser.endLine());
}
{
CLegacyConfigParser parser("DATA/HEROSPEC.TXT");
CLegacyConfigParser bioParser("DATA/HEROBIOS.TXT");
//skip header
parser.endLine();
parser.endLine();
do
{
HeroTexts texts;
texts.bonusName = parser.readString();
texts.shortBonus = parser.readString();
texts.longBonus = parser.readString();
texts.biography = bioParser.readString();
hTxts.push_back(texts);
}
while (parser.endLine() && bioParser.endLine());
}
{
CLegacyConfigParser nameParser("DATA/MINENAME.TXT");
CLegacyConfigParser eventParser("DATA/MINEEVNT.TXT");

View File

@ -58,14 +58,6 @@ public:
class DLL_LINKAGE CGeneralTextHandler //Handles general texts
{
public:
class HeroTexts
{
public:
std::string bonusName, shortBonus, longBonus; //for special abilities
std::string biography; //biography, of course
};
std::vector<HeroTexts> hTxts;
std::vector<std::string> allTexts;
std::vector<std::string> arraytxt;

View File

@ -20,12 +20,6 @@
*
*/
CHeroClass::CHeroClass()
{
}
CHeroClass::~CHeroClass()
{
}
int CHeroClass::chooseSecSkill(const std::set<int> & possibles) const //picks secondary skill out from given possibilities
{
if(possibles.size()==1)
@ -119,17 +113,81 @@ void CHeroClassHandler::load()
VLC->modh->identifiers.registerObject("heroClass." + GameConstants::HERO_CLASSES_NAMES[hc->id], hc->id);
}
while (parser.endLine() && !parser.isNextEntryEmpty());
const JsonNode & heroGraphics = JsonNode(ResourceID("config/heroClasses.json"));
for (size_t i=0; i<heroClasses.size(); i++)
{
const JsonNode & battle = heroGraphics["heroBattleAnim"].Vector()[i/2];
heroClasses[i]->imageBattleFemale = battle["female"].String();
heroClasses[i]->imageBattleMale = battle["male"].String();
const JsonNode & map = heroGraphics["heroMapAnim"].Vector()[i];
heroClasses[i]->imageMapMale = map.String();
heroClasses[i]->imageMapFemale = map.String();
}
}
void CHeroClassHandler::load(const JsonNode & classes)
{
//TODO
BOOST_FOREACH(auto & entry, classes.Struct())
{
if (!entry.second.isNull()) // may happens if mod removed creature by setting json entry to null
{
CHeroClass * heroClass = loadClass(entry.second);
heroClass->identifier = entry.first;
heroClass->id = heroClasses.size();
heroClasses.push_back(heroClass);
tlog3 << "Added hero class: " << entry.first << "\n";
VLC->modh->identifiers.registerObject("heroClass." + heroClass->identifier, heroClass->id);
}
}
}
CHeroClass *CHeroClassHandler::loadClass(const JsonNode & heroClass)
CHeroClass *CHeroClassHandler::loadClass(const JsonNode & node)
{
//TODO
return new CHeroClass;
CHeroClass * heroClass = new CHeroClass;
heroClass->imageBattleFemale = node["animation"]["battle"]["female"].String();
heroClass->imageBattleMale = node["animation"]["battle"]["male"].String();
heroClass->imageMapFemale = node["animation"]["map"]["female"].String();
heroClass->imageMapMale = node["animation"]["map"]["male"].String();
heroClass->name = node["name"].String();
BOOST_FOREACH(const std::string & pSkill, PrimarySkill::names)
{
heroClass->primarySkillInitial.push_back(node["primarySkills"][pSkill].Float());
heroClass->primarySkillLowLevel.push_back(node["lowLevelChance"][pSkill].Float());
heroClass->primarySkillHighLevel.push_back(node["highLevelChance"][pSkill].Float());
}
BOOST_FOREACH(const std::string & secSkill, SecondarySkill::names)
{
heroClass->secSkillProbability.push_back(node["secondarySkills"][secSkill].Float());
}
BOOST_FOREACH(auto & tavern, node["tavern"].Struct())
{
int value = tavern.second.Float();
VLC->modh->identifiers.requestIdentifier("faction." + tavern.first,
[=](si32 factionID)
{
heroClass->selectionProbability[factionID] = value;
});
}
VLC->modh->identifiers.requestIdentifier("faction." + node["faction"].String(),
[=](si32 factionID)
{
heroClass->faction = factionID;
});
return heroClass;
}
CHeroClassHandler::~CHeroClassHandler()
@ -149,21 +207,102 @@ CHeroHandler::~CHeroHandler()
CHeroHandler::CHeroHandler()
{}
void CHeroHandler::load(const JsonNode & heroes)
void CHeroHandler::load(const JsonNode & input)
{
//TODO
BOOST_FOREACH(auto & entry, input.Struct())
{
if (!entry.second.isNull()) // may happens if mod removed creature by setting json entry to null
{
CHero * hero = loadHero(entry.second);
hero->ID = heroes.size();
heroes.push_back(hero);
tlog3 << "Added hero : " << entry.first << "\n";
VLC->modh->identifiers.registerObject("hero." + entry.first, hero->ID);
}
}
}
CHero * CHeroHandler::loadHero(const JsonNode & hero)
CHero * CHeroHandler::loadHero(const JsonNode & node)
{
//TODO
return new CHero;
CHero * hero = new CHero;
hero->name = node["texts"]["name"].String();
hero->biography = node["texts"]["biography"].String();
hero->specName = node["texts"]["specialty"]["name"].String();
hero->specTooltip = node["texts"]["specialty"]["tooltip"].String();
hero->specDescr = node["texts"]["specialty"]["description"].String();
hero->imageIndex = node["images"]["index"].Float();
hero->iconSpecSmall = node["images"]["specialtySmall"].String();
hero->iconSpecLarge = node["images"]["specialtyLarge"].String();
hero->portraitSmall = node["images"]["small"].String();
hero->portraitLarge = node["images"]["large"].String();
assert(node["army"].Vector().size() <= 3); // anything bigger is useless - army initialization uses up to 3 slots
hero->initialArmy.resize(node["army"].Vector().size());
for (size_t i=0; i< hero->initialArmy.size(); i++)
{
const JsonNode & source = node["army"].Vector()[i];
hero->initialArmy[i].minAmount = source["min"].Float();
hero->initialArmy[i].maxAmount = source["max"].Float();
assert(hero->initialArmy[i].minAmount <= hero->initialArmy[i].maxAmount);
VLC->modh->identifiers.requestIdentifier(std::string("creature.") + source["creature"].String(), [=](si32 creature)
{
hero->initialArmy[i].creature = creature;
});
}
loadHeroJson(hero, node);
return hero;
}
void CHeroHandler::loadHeroJson(CHero * hero, const JsonNode & node)
{
// sex: 0=male, 1=female
hero->sex = !!node["female"].Bool();
BOOST_FOREACH(const JsonNode &set, node["skills"].Vector())
{
int skillID = boost::range::find(SecondarySkill::names, set["skill"].String()) - boost::begin(SecondarySkill::names);
int skillLevel = boost::range::find(SecondarySkill::levels, set["level"].String()) - boost::begin(SecondarySkill::levels);
hero->secSkillsInit.push_back(std::make_pair(skillID, skillLevel));
}
BOOST_FOREACH(const JsonNode & spell, node["spellbook"].Vector())
{
hero->spells.insert(spell.Float());
}
BOOST_FOREACH(const JsonNode &specialty, node["specialties"].Vector())
{
SSpecialtyInfo spec;
spec.type = specialty["type"].Float();
spec.val = specialty["val"].Float();
spec.subtype = specialty["subtype"].Float();
spec.additionalinfo = specialty["info"].Float();
hero->spec.push_back(spec); //put a copy of dummy
}
VLC->modh->identifiers.requestIdentifier("heroClass." + node["class"].String(),
[=](si32 classID)
{
hero->heroClass = classes.heroClasses[classID];
});
}
void CHeroHandler::load()
{
classes.load();
loadHeroes();
loadHeroTexts();
loadObstacles();
loadTerrains();
loadBallistics();
@ -233,6 +372,7 @@ void CHeroHandler::loadHeroes()
CHero * hero = new CHero;
hero->name = parser.readString();
hero->initialArmy.resize(3);
for(int x=0;x<3;x++)
{
hero->initialArmy[x].minAmount = parser.readNumber();
@ -248,6 +388,7 @@ void CHeroHandler::loadHeroes()
parser.endLine();
hero->ID = heroes.size();
hero->imageIndex = hero->ID;
heroes.push_back(hero);
}
@ -255,42 +396,31 @@ void CHeroHandler::loadHeroes()
const JsonNode config(ResourceID("config/heroes.json"));
BOOST_FOREACH(const JsonNode &hero, config["heroes"].Vector())
{
CHero * currentHero = heroes[hero["id"].Float()];
// sex: 0=male, 1=female
currentHero->sex = !!hero["female"].Bool();
BOOST_FOREACH(const JsonNode &set, hero["skill_set"].Vector())
{
int skillID = boost::range::find(SecondarySkill::names, set["skill"].String()) - boost::begin(SecondarySkill::names);
int skillLevel = boost::range::find(SecondarySkill::levels, set["level"].String()) - boost::begin(SecondarySkill::levels);
currentHero->secSkillsInit.push_back(std::make_pair(skillID, skillLevel));
}
if (!hero["spell"].isNull()) {
currentHero->startingSpell = hero["spell"].Float();
}
BOOST_FOREACH(const JsonNode &specialty, hero["specialties"].Vector())
{
SSpecialtyInfo dummy;
dummy.type = specialty["type"].Float();
dummy.val = specialty["val"].Float();
dummy.subtype = specialty["subtype"].Float();
dummy.additionalinfo = specialty["info"].Float();
currentHero->spec.push_back(dummy); //put a copy of dummy
}
VLC->modh->identifiers.requestIdentifier("heroClass." + hero["class"].String(),
[=](si32 classID)
{
currentHero->heroClass = classes.heroClasses[classID];
});
loadHeroJson(heroes[hero["id"].Float()], hero);
}
}
void CHeroHandler::loadHeroTexts()
{
CLegacyConfigParser parser("DATA/HEROSPEC.TXT");
CLegacyConfigParser bioParser("DATA/HEROBIOS.TXT");
//skip header
parser.endLine();
parser.endLine();
int i=0;
do
{
CHero * hero = heroes[i++];
hero->specName = parser.readString();
hero->specTooltip = parser.readString();
hero->specDescr = parser.readString();
hero->biography = bioParser.readString();
}
while (parser.endLine() && bioParser.endLine() && heroes.size() < i);
}
void CHeroHandler::loadBallistics()
{
CLegacyConfigParser ballParser("DATA/BALLIST.TXT");
@ -360,15 +490,4 @@ std::vector<ui8> CHeroHandler::getDefaultAllowedHeroes() const
allowedHeroes[4] = 0;
allowedHeroes[25] = 0;
return allowedHeroes;
}
CHero::CHero()
{
startingSpell = -1;
sex = 0xff;
}
CHero::~CHero()
{
}
}

View File

@ -1,6 +1,5 @@
#pragma once
#include "../lib/ConstTransitivePtr.h"
#include "GameConstants.h"
@ -13,6 +12,7 @@
* Full text of license available in license.txt file, in main folder
*
*/
class CHeroClass;
class CDefHandler;
class CGameInfo;
@ -46,23 +46,35 @@ public:
}
};
std::string name; //name of hero
si32 ID;
si32 imageIndex;
InitialArmyStack initialArmy[3];
std::vector<InitialArmyStack> initialArmy;
CHeroClass * heroClass;
std::vector<std::pair<ui8,ui8> > secSkillsInit; //initial secondary skills; first - ID of skill, second - level of skill (1 - basic, 2 - adv., 3 - expert)
std::vector<SSpecialtyInfo> spec;
si32 startingSpell; //-1 if none
std::set<si32> spells;
ui8 sex; // default sex: 0=male, 1=female
CHero();
~CHero();
/// Localized texts
std::string name; //name of hero
std::string biography;
std::string specName;
std::string specDescr;
std::string specTooltip;
/// Graphics
std::string iconSpecSmall;
std::string iconSpecLarge;
std::string portraitSmall;
std::string portraitLarge;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & name & ID & initialArmy & heroClass & secSkillsInit & spec & startingSpell & sex;
h & ID & imageIndex & initialArmy & heroClass & secSkillsInit & spec & spells & sex;
h & name & biography & specName & specDescr & specTooltip;
h & iconSpecSmall & iconSpecLarge & portraitSmall & portraitLarge;
}
};
@ -83,9 +95,12 @@ public:
std::map<TFaction, int> selectionProbability; //probability of selection in towns
std::string imageBattleMale;
std::string imageBattleFemale;
std::string imageMapMale;
std::string imageMapFemale;
int chooseSecSkill(const std::set<int> & possibles) const; //picks secondary skill out from given possibilities
CHeroClass(); //c-tor
~CHeroClass(); //d-tor
template <typename Handler> void serialize(Handler &h, const int version)
{
@ -93,6 +108,7 @@ public:
h & primarySkillInitial & primarySkillLowLevel;
h & primarySkillHighLevel & secSkillProbability;
h & selectionProbability;
h & imageBattleMale & imageBattleFemale & imageMapMale & imageMapFemale;
}
EAlignment::EAlignment getAlignment() const;
};
@ -130,7 +146,7 @@ public:
void load(const JsonNode & classes);
/// load one class from json
CHeroClass * loadClass(const JsonNode & heroClass);
CHeroClass * loadClass(const JsonNode & node);
~CHeroClassHandler();
@ -146,6 +162,8 @@ class DLL_LINKAGE CHeroHandler
/// consists of 201 values. Any higher levels require experience larger that ui64 can hold
std::vector<ui64> expPerLevel;
/// common function for loading heroes from mods and from H3
void loadHeroJson(CHero * hero, const JsonNode & node);
public:
CHeroClassHandler classes;
@ -177,12 +195,13 @@ public:
void load(const JsonNode & heroes);
/// Load single hero from json
CHero * loadHero(const JsonNode & hero);
CHero * loadHero(const JsonNode & node);
/// Load everything (calls functions below + classes.load())
void load();
void loadHeroes();
void loadHeroTexts();
void loadExperience();
void loadBallistics();
void loadTerrains();

View File

@ -708,19 +708,23 @@ void CGHeroInstance::initHero()
initHeroDefInfo();
if(!type)
type = VLC->heroh->heroes[subID];
if(!vstd::contains(spells, 0xffffffff) && type->startingSpell >= 0) //hero starts with a spell
spells.insert(type->startingSpell);
if(!vstd::contains(spells, 0xffffffff)) //hero starts with a spell
{
BOOST_FOREACH(auto spellID, type->spells)
spells.insert(spellID);
}
else //remove placeholder
spells -= 0xffffffff;
if(!getArt(ArtifactPosition::MACH4) && !getArt(ArtifactPosition::SPELLBOOK) && type->startingSpell >= 0) //no catapult means we haven't read pre-existent set -> use default rules for spellbook
if(!getArt(ArtifactPosition::MACH4) && !getArt(ArtifactPosition::SPELLBOOK) && !type->spells.empty()) //no catapult means we haven't read pre-existent set -> use default rules for spellbook
putArtifact(ArtifactPosition::SPELLBOOK, CArtifactInstance::createNewArtifactInstance(0));
if(!getArt(ArtifactPosition::MACH4))
putArtifact(ArtifactPosition::MACH4, CArtifactInstance::createNewArtifactInstance(3)); //everyone has a catapult
if(portrait < 0 || portrait == 255)
portrait = subID;
portrait = type->imageIndex;
if(!hasBonus(Selector::sourceType(Bonus::HERO_BASE_SKILL)))
{
for(int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
@ -781,6 +785,8 @@ void CGHeroInstance::initArmy(IArmyDescriptor *dst /*= NULL*/)
else
howManyStacks = 3;
vstd::amin(howManyStacks, type->initialArmy.size());
for(int stackNo=0; stackNo < howManyStacks; stackNo++)
{
auto & stack = type->initialArmy[stackNo];
@ -892,8 +898,7 @@ const std::string & CGHeroInstance::getBiography() const
{
if (biography.length())
return biography;
else
return VLC->generaltexth->hTxts[subID].biography;
return type->biography;
}
void CGHeroInstance::initObj()
{

View File

@ -472,7 +472,13 @@ void CTownHandler::load(const JsonNode &source)
{
BOOST_FOREACH(auto & node, source.Struct())
{
int id = node.second["index"].Float();
int id;
if (node.second["index"].isNull())
id = factions.rbegin()->first + 1;
else
id = node.second["index"].Float();
CFaction & faction = factions[id];
faction.factionID = id;

View File

@ -416,16 +416,16 @@ bool JsonParser::extractWhitespace(bool verbose)
bool JsonParser::extractEscaping(std::string &str)
{
switch(input[pos++])
switch(input[pos])
{
break; case '\"': str += '\"';
break; case '\\': str += '\\';
break; case '/': str += '/';
break; case '\b': str += '\b';
break; case '\f': str += '\f';
break; case '\n': str += '\n';
break; case '\r': str += '\r';
break; case '\t': str += '\t';
break; case 'b': str += '\b';
break; case 'f': str += '\f';
break; case 'n': str += '\n';
break; case 'r': str += '\r';
break; case 't': str += '\t';
break; default: return error("Unknown escape sequence!", true);
};
return true;