1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-28 08:48:48 +02:00

Part 2 of new spell configuration

1) spell handler refactored to support modding in general way
2) imunnity icons moved to WoG as they depends on wog`s graphics
3) introduced new class template for handlers (todo: use this in other handlers)
4) save format changed
5) introduced "absolute immunity" - unaffected by "the Orb" etc. (todo: use it in config)
6) new format documented on wiki, added json schema.

* more split of registertypes - fixes 32 mingw build
This commit is contained in:
alexvins 2014-03-07 13:21:09 +00:00
parent 5cbec833c2
commit 4203d69525
43 changed files with 4570 additions and 1551 deletions

View File

@ -44,7 +44,7 @@
<Linker>
<Add option="-lboost_system$(#boost.libsuffix)" />
<Add option="-lVCMI_lib" />
<Add directory="$(#boost.lib)" />
<Add directory="$(#boost.lib32)" />
<Add directory="../.." />
</Linker>
<Unit filename="BattleAI.cpp" />

View File

@ -112,7 +112,7 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
print("activeStack called for " + stack->nodeName());
if(stack->type->idNumber == CreatureID::CATAPULT)
return useCatapult(stack);
if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON) && stack->hasBonusOfType(Bonus::HEALER))
{
auto healingTargets = cb->battleGetStacks(CBattleInfoEssentials::ONLY_MINE);
@ -343,7 +343,7 @@ SpellTypes spellType(const CSpell *spell)
if (spell->isOffensiveSpell())
return OFFENSIVE_SPELL;
if (spell->hasEffects())
return TIMED_EFFECT;
return TIMED_EFFECT;
return OTHER;
}
@ -384,13 +384,13 @@ struct CurrentOffensivePotential
};
//
//
// //set has its own order, so remove_if won't work. TODO - reuse for map
// template<typename Elem, typename Predicate>
// void erase_if(std::set<Elem> &setContainer, Predicate pred)
// {
// auto itr = setContainer.begin();
// auto endItr = setContainer.end();
// auto endItr = setContainer.end();
// while(itr != endItr)
// {
// auto tmpItr = itr++;
@ -409,14 +409,14 @@ void CBattleAI::attemptCastingSpell()
//Get all spells we can cast
std::vector<const CSpell*> possibleSpells;
vstd::copy_if(VLC->spellh->spells, std::back_inserter(possibleSpells), [this] (const CSpell *s) -> bool
{
auto problem = cbc->battleCanCastThisSpell(s);
return problem == ESpellCastProblem::OK;
vstd::copy_if(VLC->spellh->objects, std::back_inserter(possibleSpells), [this] (const CSpell *s) -> bool
{
auto problem = cbc->battleCanCastThisSpell(s);
return problem == ESpellCastProblem::OK;
});
LOGFL("I can cast %d spells.", possibleSpells.size());
vstd::erase_if(possibleSpells, [](const CSpell *s)
vstd::erase_if(possibleSpells, [](const CSpell *s)
{return spellType(s) == OTHER; });
LOGFL("I know about workings of %d of them.", possibleSpells.size());
@ -606,7 +606,7 @@ ThreatMap::ThreatMap(const CStack *Endangered) : endangered(Endangered)
}
}
const TBonusListPtr StackWithBonuses::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= nullptr*/, const std::string &cachingStr /*= ""*/) const
const TBonusListPtr StackWithBonuses::getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root /*= nullptr*/, const std::string &cachingStr /*= ""*/) const
{
TBonusListPtr ret = make_shared<BonusList>();
const TBonusListPtr originalList = stack->getAllBonuses(selector, limit, root, cachingStr);
@ -658,7 +658,7 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo &AttackInfo
curBai.attackerCount = attacker->count - attacker->countKilledByAttack(ap.damageReceived).first;
curBai.defenderCount = enemy->count - enemy->countKilledByAttack(ap.damageDealt).first;
if(!curBai.attackerCount)
if(!curBai.attackerCount)
break;
//TODO what about defender? should we break? but in pessimistic scenario defender might be alive
}
@ -689,7 +689,7 @@ PotentialTargets::PotentialTargets(const CStack *attacker, const HypotheticChang
auto bai = BattleAttackInfo(attacker, enemy, shooting);
bai.attackerBonuses = getValOr(state.bonusesOfStacks, bai.attacker, bai.attacker);
bai.defenderBonuses = getValOr(state.bonusesOfStacks, bai.defender, bai.defender);
if(hex.isValid())
{
assert(dists[hex] <= attacker->Speed());

View File

@ -45,7 +45,7 @@
<Linker>
<Add option="-lboost_system$(#boost.libsuffix)" />
<Add option="-lVCMI_lib" />
<Add directory="$(#boost.lib)" />
<Add directory="$(#boost.lib32)" />
<Add directory="../.." />
</Linker>
<Unit filename="CEmptyAI.cpp" />

View File

@ -47,7 +47,7 @@
<Linker>
<Add option="-lboost_system$(#boost.libsuffix)" />
<Add option="-lVCMI_lib" />
<Add directory="$(#boost.lib)" />
<Add directory="$(#boost.lib32)" />
<Add directory="../.." />
</Linker>
<Unit filename="StdInc.h">

View File

@ -54,7 +54,7 @@
<Add option="-lboost_thread$(#boost.libsuffix)" />
<Add option="-lboost_chrono$(#boost.libsuffix)" />
<Add option="-lVCMI_lib" />
<Add directory="$(#boost.lib)" />
<Add directory="$(#boost.lib32)" />
<Add directory="../.." />
</Linker>
<Unit filename="AIUtility.cpp" />

View File

@ -0,0 +1,57 @@
{
"core:implosion" :
{
"graphics" : {
"iconImmune" : "ZVS/LIB1.RES/E_SPIMP"
}
},
"core:meteorShower" : {
"graphics" : {
"iconImmune" : "ZVS/LIB1.RES/E_SPMET"
}
},
"core:armageddon" : {
"graphics" : {
"iconImmune" : "ZVS/LIB1.RES/E_SPARM"
}
},
"core:dispel" : {
"graphics" : {
"iconImmune" : "ZVS/LIB1.RES/E_SPDISP"
}
},
"core:slow" : {
"graphics" : {
"iconImmune" : "ZVS/LIB1.RES/E_SPSLOW"
}
},
"core:berserk" : {
"graphics" : {
"iconImmune" : "ZVS/LIB1.RES/E_SPBERS"
}
},
"core:hypnotize" : {
"graphics" : {
"iconImmune" : "ZVS/LIB1.RES/E_SPHYPN"
}
},
"core:blind" : {
"graphics" : {
"iconImmune" : "ZVS/LIB1.RES/E_SPBLIND"
}
},
"core:dispelHelpful" : {
"graphics" : {
"iconImmune" : "ZVS/LIB1.RES/E_SPDISB"
}
}
}

View File

@ -22,6 +22,11 @@
"config/wog/heroClasses.json"
],
"spells" :
[
"config/wog/spells.json"
],
"filesystem":
{
"" :

View File

@ -264,11 +264,11 @@ CDwellingInfoBox::CDwellingInfoBox(int centerX, int centerY, const CGTownInstanc
title = new CLabel(80, 30, FONT_SMALL, CENTER, Colors::WHITE, creature->namePl);
animation = new CCreaturePic(30, 44, creature, true, true);
std::string text = boost::lexical_cast<std::string>(Town->creatures.at(level).first);
available = new CLabel(80,190, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[217] + text);
costPerTroop = new CLabel(80, 227, FONT_SMALL, CENTER, Colors::WHITE, CGI->generaltexth->allTexts[346]);
for(int i = 0; i<GameConstants::RESOURCE_QUANTITY; i++)
{
if(creature->cost[i])
@ -437,7 +437,7 @@ void CHeroGSlot::set(const CGHeroInstance *newHero)
image = new CAnimImage("PortraitsLarge", newHero->portrait, 0, 0, 0);
else if(!upg && owner->showEmpty) //up garrison
image = new CAnimImage("CREST58", LOCPLINT->castleInt->town->getOwner().getNum(), 0, 0, 0);
else
else
image = nullptr;
}
@ -900,7 +900,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
recreateIcons();
CCS->musich->playMusic(town->town->clientInfo.musicTheme, true);
bicons = CDefHandler::giveDefEss(town->town->clientInfo.buildingsIcons);
}
@ -987,7 +987,7 @@ CCreaInfo::CCreaInfo(Point position, const CGTownInstance *Town, int Level, bool
{
OBJ_CONSTRUCTION_CAPTURING_ALL;
pos += position;
if ( town->creatures.size() <= level || town->creatures[level].second.empty())
{
level = -1;
@ -1079,7 +1079,7 @@ std::string CCreaInfo::genGrowthText()
{
descr +="\n" + entry.description;
}
return descr;
}
@ -1275,7 +1275,7 @@ CHallInterface::CBuildingBox::CBuildingBox(int x, int y, const CGTownInstance *
pos.y += y;
pos.w = 154;
pos.h = 92;
state = LOCPLINT->cb->canBuildStructure(town,building->bid);
assert(state < EBuildingState::BUILDING_ERROR);
static int panelIndex[10] = { 3, 3, 3, 0, 0, 2, 2, 1, 2, 2};
@ -1301,7 +1301,7 @@ CHallInterface::CHallInterface(const CGTownInstance *Town):
statusBar = new CGStatusBar(new CPicture(*background, barRect, 5, 556, false));
title = new CLabel(399, 12, FONT_MEDIUM, CENTER, Colors::WHITE, town->town->buildings.at(BuildingID(town->hallLevel()+BuildingID::VILLAGE_HALL))->Name());
exit = new CAdventureMapButton(CGI->generaltexth->hcommands[8], "",
exit = new CAdventureMapButton(CGI->generaltexth->hcommands[8], "",
boost::bind(&CHallInterface::close,this), 748, 556, "TPMAGE1.DEF", SDLK_RETURN);
exit->assignedKeys.insert(SDLK_ESCAPE);
@ -1408,7 +1408,7 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
"", boost::bind(&CBuildWindow::buyFunc,this), 45, 446,"IBUY30", SDLK_RETURN);
buy->borderColor = Colors::METALLIC_GOLD;
buy->borderEnabled = true;
cancel = new CAdventureMapButton(boost::str(boost::format(CGI->generaltexth->allTexts[596]) % building->Name()),
"", boost::bind(&CBuildWindow::close,this), 290, 445, "ICANCEL", SDLK_ESCAPE);
cancel->borderColor = Colors::METALLIC_GOLD;
@ -1437,10 +1437,10 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
ui32 fortSize = town->creatures.size();
if (fortSize > GameConstants::CREATURES_PER_TOWN && town->creatures.back().second.empty())
fortSize--;
const CBuilding *fortBuilding = town->town->buildings.at(BuildingID(town->fortLevel()+6));
title = new CLabel(400, 12, FONT_BIG, CENTER, Colors::WHITE, fortBuilding->Name());
std::string text = boost::str(boost::format(CGI->generaltexth->fcommands[6]) % fortBuilding->Name());
exit = new CAdventureMapButton(text, "", boost::bind(&CFortScreen::close,this) ,748, 556, "TPMAGE1", SDLK_RETURN);
exit->assignedKeys.insert(SDLK_ESCAPE);
@ -1454,7 +1454,7 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
positions += Point(206,421);
else
positions += Point(10, 421), Point(404,421);
for (ui32 i=0; i<fortSize; i++)
{
BuildingID buildingID;
@ -1568,10 +1568,10 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *
pos.y +=posY;
pos.w = 386;
pos.h = 126;
if (!town->creatures[level].second.empty())
addUsedEvents(LCLICK | RCLICK | HOVER);//Activate only if dwelling is present
icons = new CPicture("TPCAINFO", 261, 3);
if (getMyBuilding() != nullptr)
{
@ -1640,18 +1640,18 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner):
CWindowObject(BORDERED, "TPMAGE")
{
OBJ_CONSTRUCTION_CAPTURING_ALL;
window = new CPicture(owner->town->town->clientInfo.guildWindow , 332, 76);
resdatabar = new CMinorResDataBar;
resdatabar->pos.x += pos.x;
resdatabar->pos.y += pos.y;
Rect barRect(7, 556, 737, 18);
statusBar = new CGStatusBar(new CPicture(*background, barRect, 7, 556, false));
exit = new CAdventureMapButton(CGI->generaltexth->allTexts[593],"",boost::bind(&CMageGuildScreen::close,this), 748, 556,"TPMAGE1.DEF",SDLK_RETURN);
exit->assignedKeys.insert(SDLK_ESCAPE);
std::vector<std::vector<Point> > positions;
positions.resize(5);
@ -1660,14 +1660,14 @@ CMageGuildScreen::CMageGuildScreen(CCastleInterface * owner):
positions[2] += Point(570,82), Point(672,82), Point(570,157), Point(672,157);
positions[3] += Point(183,42), Point(183,148), Point(183,253);
positions[4] += Point(491,325), Point(591,325);
for(size_t i=0; i<owner->town->town->mageLevel; i++)
{
size_t spellCount = owner->town->spellsAtLevel(i+1,false); //spell at level with -1 hmmm?
for(size_t j=0; j<spellCount; j++)
{
if(i<owner->town->mageGuildLevel() && owner->town->spells[i].size()>j)
spells.push_back( new Scroll(positions[i][j], CGI->spellh->spells[owner->town->spells[i][j]]));
spells.push_back( new Scroll(positions[i][j], CGI->spellh->objects[owner->town->spells[i][j]]));
else
new CAnimImage("TPMAGES.DEF", 1, 0, positions[i][j].x, positions[i][j].y);//closed scroll
}
@ -1711,15 +1711,15 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art
OBJ_CONSTRUCTION_CAPTURING_ALL;
statusBar = new CGStatusBar(new CPicture(*background, Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
animBG = new CPicture("TPSMITBK", 64, 50);
animBG->needRefresh = true;
const CCreature *creature = CGI->creh->creatures[creMachineID];
anim = new CCreatureAnim(64, 50, creature->animDefName, Rect());
anim->clipRect(113,125,200,150);
title = new CLabel(165, 28, FONT_BIG, CENTER, Colors::YELLOW,
title = new CLabel(165, 28, FONT_BIG, CENTER, Colors::YELLOW,
boost::str(boost::format(CGI->generaltexth->allTexts[274]) % creature->nameSing));
costText = new CLabel(165, 218, FONT_MEDIUM, CENTER, Colors::WHITE, CGI->generaltexth->jktexts[43]);
costValue = new CLabel(165, 290, FONT_MEDIUM, CENTER, Colors::WHITE,
@ -1727,7 +1727,7 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art
std::string text = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % creature->nameSing);
buy = new CAdventureMapButton(text,"",boost::bind(&CBlacksmithDialog::close, this), 42, 312,"IBUY30.DEF",SDLK_RETURN);
text = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % creature->nameSing);
cancel = new CAdventureMapButton(text,"",boost::bind(&CBlacksmithDialog::close, this), 224, 312,"ICANCEL.DEF",SDLK_ESCAPE);

View File

@ -240,7 +240,7 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
else
{
selectableSkill->pos = Rect (95, 256, 55, 55); //TODO: scroll
const Bonus *b = CGI->creh->skillRequirements[option-100].first;
const Bonus *b = CGI->creh->skillRequirements[option-100].first;
bonusItems.push_back (new CBonusItem (genRect(0, 0, 251, 57), stack->bonusToString(b, false), stack->bonusToString(b, true), stack->bonusToGraphics(b)));
selectableBonuses.push_back (selectableSkill); //insert these before other bonuses
}
@ -274,19 +274,19 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
//handle Magic resistance separately :/
const IBonusBearer *temp = stack;
if (battleStack)
{
temp = battleStack;
}
int magicResistance = temp->magicResistance();
int magicResistance = temp->magicResistance();
if (magicResistance)
{
Bonus b;
b.type = Bonus::MAGIC_RESISTANCE;
text = VLC->getBth()->bonusToString(&b,temp,false);
const std::string description = VLC->getBth()->bonusToString(&b,temp,true);
bonusItems.push_back (new CBonusItem(genRect(0, 0, 251, 57), text, description, stack->bonusToGraphics(&b)));
@ -423,7 +423,7 @@ void CCreatureWindow::init(const CStackInstance *Stack, const CBonusSystemNode *
if (effect < graphics->spellEffectsPics->ourImages.size()) //not all effects have graphics (for eg. Acid Breath)
{
spellText = CGI->generaltexth->allTexts[610]; //"%s, duration: %d rounds."
boost::replace_first (spellText, "%s", CGI->spellh->spells[effect]->name);
boost::replace_first (spellText, "%s", CGI->spellh->objects[effect]->name);
int duration = battleStack->getBonusLocalFirst(Selector::source(Bonus::SPELL_EFFECT,effect))->turnsRemain;
boost::replace_first (spellText, "%d", boost::lexical_cast<std::string>(duration));
@ -558,7 +558,7 @@ void CCreatureWindow::showAll(SDL_Surface * to)
if (upgradeOptions.size() && (type == COMMANDER_LEVEL_UP && upgradeOptions[selectedOption] >= 100)) //add frame to selected skill
{
int index = selectedOption - selectableSkills.size(); //this is screwed
CSDL_Ext::drawBorder(to, Rect::around(selectableBonuses[index]->pos), int3(Colors::METALLIC_GOLD.r, Colors::METALLIC_GOLD.g, Colors::METALLIC_GOLD.b));
CSDL_Ext::drawBorder(to, Rect::around(selectableBonuses[index]->pos), int3(Colors::METALLIC_GOLD.r, Colors::METALLIC_GOLD.g, Colors::METALLIC_GOLD.b));
}
}
@ -633,7 +633,7 @@ void CCreatureWindow::setArt(const CArtifactInstance *art)
}
else
artifactImage = nullptr;
redraw();
}
@ -664,7 +664,7 @@ void CCreatureWindow::artifactRemoved (const ArtifactLocation &artLoc)
//align artifacts to remove holes
for (auto al : stack->artifactsWorn)
{
ArtifactPosition freeSlot = al.second.artifact->firstAvailableSlot(stack);
ArtifactPosition freeSlot = al.second.artifact->firstAvailableSlot(stack);
if (freeSlot < al.first)
LOCPLINT->cb->swapArtifacts (ArtifactLocation(stack, al.first), ArtifactLocation(stack, freeSlot));
}

View File

@ -115,7 +115,7 @@ void startGameFromFile(const std::string &fname)
}
catch(std::exception &e)
{
logGlobal->errorStream() << "Failed to start from the file: " + fname << ". Error: " << e.what()
logGlobal->errorStream() << "Failed to start from the file: " + fname << ". Error: " << e.what()
<< " Falling back to main menu.";
GH.curInt = CGPreGame::create();
return;
@ -145,7 +145,7 @@ void init()
loadDLLClasses();
const_cast<CGameInfo*>(CGI)->setFromLib();
CCS->soundh->initSpellsSounds(CGI->spellh->spells);
CCS->soundh->initSpellsSounds(CGI->spellh->objects);
logGlobal->infoStream()<<"Initializing VCMI_Lib: "<<tmh.getDiff();
pomtime.getDiff();
@ -211,7 +211,7 @@ int main(int argc, char** argv)
std::string executablePath = argv[0];
std::string workDir = executablePath.substr(0, executablePath.rfind('/'));
chdir(workDir.c_str());
// Check for updates
OSX_checkForUpdates();
@ -244,7 +244,7 @@ int main(int argc, char** argv)
{
po::store(po::parse_command_line(argc, argv, opts), vm);
}
catch(std::exception &e)
catch(std::exception &e)
{
std::cerr << "Failure during parsing command-line options:\n" << e.what() << std::endl;
}
@ -322,7 +322,7 @@ int main(int argc, char** argv)
logGlobal->infoStream() << NAME;
srand ( time(nullptr) );
const JsonNode& video = settings["video"];
const JsonNode& res = video["screenRes"];
@ -345,6 +345,7 @@ int main(int argc, char** argv)
logGlobal->errorStream()<<"Something was wrong: "<< SDL_GetError();
exit(-1);
}
GH.mainFPSmng->init(); //(!)init here AFTER SDL_Init() while using SDL for FPS management
atexit(SDL_Quit);
setScreenRes(res["width"].Float(), res["height"].Float(), video["bitsPerPixel"].Float(), video["fullscreen"].Bool());
logGlobal->infoStream() <<"\tInitializing screen: "<<pomtime.getDiff();
@ -784,7 +785,7 @@ static void setScreenRes(int w, int h, int bpp, bool fullscreen, bool resetVideo
SDL_QuitSubSystem(SDL_INIT_VIDEO);
SDL_InitSubSystem(SDL_INIT_VIDEO);
}
if((screen = SDL_SetVideoMode(w, h, suggestedBpp, SDL_SWSURFACE|(fullscreen?SDL_FULLSCREEN:0))) == nullptr)
{
logGlobal->errorStream() << "Requested screen resolution is not available (" << w << "x" << h << "x" << suggestedBpp << "bpp)";
@ -918,9 +919,9 @@ static void listenForEvents()
}
continue;
}
}
{
boost::unique_lock<boost::mutex> lock(eventsM);
boost::unique_lock<boost::mutex> lock(eventsM);
events.push(ev);
}
}

View File

@ -186,7 +186,7 @@ void CSoundHandler::initSpellsSounds(const std::vector< ConstTransitivePtr<CSpel
for(const JsonNode &node : config["spell_sounds"].Vector())
{
int spellid = node["id"].Float();
const CSpell *s = CGI->spellh->spells[spellid];
const CSpell *s = CGI->spellh->objects[spellid];
if (vstd::contains(spellSounds, s))
logGlobal->errorStream() << "Spell << " << spellid << " already has a sound";
@ -425,7 +425,7 @@ void CMusicHandler::queueNext(CMusicHandler *owner, std::string setName, std::st
}
catch(std::exception &e)
{
logGlobal->errorStream() << "Failed to queue music. setName=" << setName << "\tmusicURI=" << musicURI;
logGlobal->errorStream() << "Failed to queue music. setName=" << setName << "\tmusicURI=" << musicURI;
logGlobal->errorStream() << "Exception: " << e.what();
}
}

View File

@ -267,7 +267,7 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details)
if(details.result == TryMoveHero::TELEPORTATION)
{
if(adventureInt->terrain.currentPath)
{
{
assert(adventureInt->terrain.currentPath->nodes.size() >= 2);
std::vector<CGPathNode>::const_iterator nodesIt = adventureInt->terrain.currentPath->nodes.end() - 1;
@ -491,7 +491,7 @@ void CPlayerInterface::commanderGotLevel (const CCommanderInstance * commander,
waitWhileDialog();
CCS->soundh->playSound(soundBase::heroNewLevel);
CCreatureWindow * cw = new CCreatureWindow(skills, commander,
CCreatureWindow * cw = new CCreatureWindow(skills, commander,
[=](ui32 selection){ cb->selectionMade(selection, queryID); });
GH.pushInt(cw);
}
@ -619,11 +619,11 @@ void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet
isAutoFightOn = true;
cb->registerBattleInterface(autofightingAI);
}
//Don't wait for dialogs when we are non-active hot-seat player
if(LOCPLINT == this)
waitForAllDialogs();
BATTLE_EVENT_POSSIBLE_RETURN;
}
@ -860,7 +860,7 @@ void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacke
{
EVENT_HANDLER_CALLED_BY_CLIENT;
BATTLE_EVENT_POSSIBLE_RETURN;
std::vector<StackAttackedInfo> arg;
for(auto & elem : bsa)
{
@ -1204,7 +1204,7 @@ void CPlayerInterface::availableCreaturesChanged( const CGDwelling *town )
void CPlayerInterface::heroBonusChanged( const CGHeroInstance *hero, const Bonus &bonus, bool gain )
{
EVENT_HANDLER_CALLED_BY_CLIENT;
if(bonus.type == Bonus::NONE)
if(bonus.type == Bonus::NONE)
return;
updateInfo(hero);
@ -1601,9 +1601,9 @@ bool CPlayerInterface::ctrlPressed() const
void CPlayerInterface::update()
{
// Updating GUI requires locking pim mutex (that protects screen and GUI state).
// When ending the game, the pim mutex might be hold bo other thread,
// When ending the game, the pim mutex might be hold bo other thread,
// that will notify us about the ending game by setting terminate_cond flag.
bool acquiredTheLockOnPim = false; //for tracking whether pim mutex locking succeeded
while(!terminate_cond.get() && !(acquiredTheLockOnPim = pim->try_lock())) //try acquiring long until it succeeds or we are told to terminate
boost::this_thread::sleep(boost::posix_time::milliseconds(15));
@ -1616,7 +1616,7 @@ void CPlayerInterface::update()
}
// If we are here, pim mutex has been successfully locked - let's store it in a safe RAII lock.
boost::unique_lock<boost::recursive_mutex> un(*pim, boost::adopt_lock);
boost::unique_lock<boost::recursive_mutex> un(*pim, boost::adopt_lock);
// While mutexes were locked away we may be have stopped being the active interface
if(LOCPLINT != this)
@ -2202,7 +2202,7 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI
{
eraseCurrentPathOf(caster, false);
}
const CSpell * spell = CGI->spellh->spells[spellID];
const CSpell * spell = CGI->spellh->objects[spellID];
if (vstd::contains(CCS->soundh->spellSounds, spell))
CCS->soundh->playSound(CCS->soundh->spellSounds[spell]);
}

View File

@ -481,7 +481,7 @@ CGPreGame::CGPreGame()
CGPreGame::~CGPreGame()
{
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
disposeGraphics();
if(CGP == this)
CGP = nullptr;
@ -512,7 +512,7 @@ void CGPreGame::disposeGraphics()
void CGPreGame::update()
{
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
boost::unique_lock<boost::recursive_mutex> lock(*CPlayerInterface::pim);
if(CGP != this) //don't update if you are not a main interface
return;
@ -3067,7 +3067,7 @@ void CMultiMode::joinTCP()
{
Settings name = settings.write["general"]["playerName"];
name->String() = txt->text;
GH.pushInt(new CSimpleJoinScreen);
GH.pushInt(new CSimpleJoinScreen);
}
CHotSeatPlayers::CHotSeatPlayers(const std::string &firstPlayer)
@ -3448,7 +3448,7 @@ void CBonusSelection::updateBonusSelection()
{
case CScenarioTravel::STravelBonus::SPELL:
desc = CGI->generaltexth->allTexts[715];
boost::algorithm::replace_first(desc, "%s", CGI->spellh->spells[bonDescs[i].info2]->name);
boost::algorithm::replace_first(desc, "%s", CGI->spellh->objects[bonDescs[i].info2]->name);
break;
case CScenarioTravel::STravelBonus::MONSTER:
picNumber = bonDescs[i].info2 + 2;
@ -3484,7 +3484,7 @@ void CBonusSelection::updateBonusSelection()
break;
case CScenarioTravel::STravelBonus::SPELL_SCROLL:
desc = CGI->generaltexth->allTexts[716];
boost::algorithm::replace_first(desc, "%s", CGI->spellh->spells[bonDescs[i].info2]->name);
boost::algorithm::replace_first(desc, "%s", CGI->spellh->objects[bonDescs[i].info2]->name);
break;
case CScenarioTravel::STravelBonus::PRIMARY_SKILL:
{

View File

@ -78,9 +78,9 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
myInt(_myInt)
{
//initializing castable spells
for(ui32 v=0; v<CGI->spellh->spells.size(); ++v)
for(ui32 v=0; v<CGI->spellh->objects.size(); ++v)
{
if( !CGI->spellh->spells[v]->creatureAbility && myHero->canCastThisSpell(CGI->spellh->spells[v]) )
if( !CGI->spellh->objects[v]->creatureAbility && myHero->canCastThisSpell(CGI->spellh->objects[v]) )
mySpells.insert(SpellID(v));
}
@ -92,7 +92,7 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
for(auto g : mySpells)
{
const CSpell &s = *CGI->spellh->spells[g];
const CSpell &s = *CGI->spellh->objects[g];
Uint8 *sitesPerOurTab = s.combatSpell ? sitesPerTabBattle : sitesPerTabAdv;
++sitesPerOurTab[4];
@ -123,7 +123,7 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
sitesPerTabAdv[v] = (sitesPerTabAdv[v] - 10) / 12 + 2;
}
}
if(sitesPerTabBattle[4] % 12 == 0)
sitesPerTabBattle[4]/=12;
else
@ -259,7 +259,7 @@ void CSpellWindow::fexitb()
void CSpellWindow::fadvSpellsb()
{
if (battleSpellsOnly == true)
if (battleSpellsOnly == true)
{
turnPageRight();
battleSpellsOnly = false;
@ -270,7 +270,7 @@ void CSpellWindow::fadvSpellsb()
void CSpellWindow::fbattleSpellsb()
{
if (battleSpellsOnly == false)
if (battleSpellsOnly == false)
{
turnPageLeft();
battleSpellsOnly = true;
@ -285,7 +285,7 @@ void CSpellWindow::fmanaPtsb()
void CSpellWindow::selectSchool(int school)
{
if (selectedTab != school)
if (selectedTab != school)
{
if (selectedTab < school)
turnPageLeft();
@ -299,7 +299,7 @@ void CSpellWindow::selectSchool(int school)
void CSpellWindow::fLcornerb()
{
if(currentPage>0)
if(currentPage>0)
{
turnPageLeft();
--currentPage;
@ -310,7 +310,7 @@ void CSpellWindow::fLcornerb()
void CSpellWindow::fRcornerb()
{
if((currentPage + 1) < (pagesWithinCurrentTab()))
if((currentPage + 1) < (pagesWithinCurrentTab()))
{
turnPageRight();
++currentPage;
@ -327,7 +327,7 @@ void CSpellWindow::showAll(SDL_Surface * to)
std::ostringstream mana;
mana<<myHero->mana;
printAtMiddleLoc(mana.str(), 435, 426, FONT_SMALL, Colors::YELLOW, to);
statusBar->showAll(to);
//printing school images
@ -363,8 +363,8 @@ class SpellbookSpellSorter
public:
bool operator()(const int & a, const int & b)
{
const CSpell & A = *CGI->spellh->spells[a];
const CSpell & B = *CGI->spellh->spells[b];
const CSpell & A = *CGI->spellh->objects[a];
const CSpell & B = *CGI->spellh->objects[b];
if(A.level<B.level)
return true;
if(A.level>B.level)
@ -394,11 +394,11 @@ void CSpellWindow::computeSpellsPerArea()
std::vector<SpellID> spellsCurSite;
for(auto it = mySpells.cbegin(); it != mySpells.cend(); ++it)
{
if(CGI->spellh->spells[*it]->combatSpell ^ !battleSpellsOnly
&& ((CGI->spellh->spells[*it]->air && selectedTab == 0) ||
(CGI->spellh->spells[*it]->fire && selectedTab == 1) ||
(CGI->spellh->spells[*it]->water && selectedTab == 2) ||
(CGI->spellh->spells[*it]->earth && selectedTab == 3) ||
if(CGI->spellh->objects[*it]->combatSpell ^ !battleSpellsOnly
&& ((CGI->spellh->objects[*it]->air && selectedTab == 0) ||
(CGI->spellh->objects[*it]->fire && selectedTab == 1) ||
(CGI->spellh->objects[*it]->water && selectedTab == 2) ||
(CGI->spellh->objects[*it]->earth && selectedTab == 3) ||
selectedTab == 4 )
)
{
@ -611,7 +611,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
{
if(!down && mySpell!=-1)
{
const CSpell *sp = CGI->spellh->spells[mySpell];
const CSpell *sp = CGI->spellh->objects[mySpell];
int spellCost = owner->myInt->cb->getSpellCost(sp, owner->myHero);
if(spellCost > owner->myHero->mana) //insufficient mana
@ -680,7 +680,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
{
std::string artName = CGI->arth->artifacts[b->sid]->Name();
//The %s prevents %s from casting 3rd level or higher spells.
owner->myInt->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[536])
owner->myInt->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[536])
% artName % owner->myHero->name));
}
else
@ -739,7 +739,7 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
return;
}
if (h->getSpellSchoolLevel(CGI->spellh->spells[spell]) < 2) //not advanced or expert - teleport to nearest available city
if (h->getSpellSchoolLevel(CGI->spellh->objects[spell]) < 2) //not advanced or expert - teleport to nearest available city
{
int nearest = -1; //nearest town's ID
double dist = -1;
@ -798,7 +798,7 @@ void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
{
std::string dmgInfo;
const CGHeroInstance * hero = owner->myHero;
int causedDmg = owner->myInt->cb->estimateSpellDamage( CGI->spellh->spells[mySpell], (hero ? hero : nullptr));
int causedDmg = owner->myInt->cb->estimateSpellDamage( CGI->spellh->objects[mySpell], (hero ? hero : nullptr));
if(causedDmg == 0 || mySpell == 57) //Titan's Lightning Bolt already has damage info included
dmgInfo = "";
else
@ -807,7 +807,7 @@ void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(causedDmg));
}
CRClickPopup::createAndPush(CGI->spellh->spells[mySpell]->descriptions[schoolLevel] + dmgInfo,
CRClickPopup::createAndPush(CGI->spellh->objects[mySpell]->descriptions[schoolLevel] + dmgInfo,
new CComponent(CComponent::spell, mySpell));
}
}
@ -820,7 +820,7 @@ void CSpellWindow::SpellArea::hover(bool on)
if(on)
{
std::ostringstream ss;
ss<<CGI->spellh->spells[mySpell]->name<<" ("<<CGI->generaltexth->allTexts[171+CGI->spellh->spells[mySpell]->level]<<")";
ss<<CGI->spellh->objects[mySpell]->name<<" ("<<CGI->generaltexth->allTexts[171+CGI->spellh->objects[mySpell]->level]<<")";
owner->statusBar->setText(ss.str());
}
else
@ -868,7 +868,7 @@ void CSpellWindow::SpellArea::setSpell(SpellID spellID)
if(mySpell < 0)
return;
const CSpell * spell = CGI->spellh->spells[mySpell];
const CSpell * spell = CGI->spellh->objects[mySpell];
schoolLevel = owner->myHero->getSpellSchoolLevel(spell, &whichSchool);
spellCost = owner->myInt->cb->getSpellCost(spell, owner->myHero);
}

View File

@ -977,7 +977,7 @@ std::string CComponent::getDescription()
case creature: return "";
case artifact: return CGI->arth->artifacts[subtype]->Description();
case experience: return CGI->generaltexth->allTexts[241];
case spell: return CGI->spellh->spells[subtype]->descriptions[val];
case spell: return CGI->spellh->objects[subtype]->descriptions[val];
case morale: return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)];
case luck: return CGI->generaltexth->heroscrn[ 7 - (val>0) + (val<0)];
case building: return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Description();
@ -1019,7 +1019,7 @@ std::string CComponent::getSubtitleInternal()
else
return boost::lexical_cast<std::string>(val); //amount of experience OR level required for seer hut;
}
case spell: return CGI->spellh->spells[subtype]->name;
case spell: return CGI->spellh->objects[subtype]->name;
case morale: return "";
case luck: return "";
case building: return CGI->townh->factions[subtype]->town->buildings[BuildingID(val)]->Name();
@ -4457,7 +4457,7 @@ void CArtPlace::setArtifact(const CArtifactInstance *art)
if(spellID >= 0)
{
if(nameStart != std::string::npos && nameEnd != std::string::npos)
text = text.replace(nameStart, nameEnd - nameStart + 1, CGI->spellh->spells[spellID]->name);
text = text.replace(nameStart, nameEnd - nameStart + 1, CGI->spellh->objects[spellID]->name);
//add spell component info (used to provide a pic in r-click popup)
baseType = CComponent::spell;

View File

@ -4,7 +4,6 @@
<Project>
<Option title="VCMI_client" />
<Option pch_mode="2" />
<Option default_target="Release" />
<Option compiler="gcc" />
<Build>
<Target title="Debug">
@ -64,7 +63,7 @@
<Add option="-lSDL_mixer" />
<Add option="-lSDL_ttf" />
<Add option="-lVCMI_lib" />
<Add directory="$(#boost.lib)" />
<Add directory="$(#boost.lib32)" />
<Add directory="$(#sdl.lib)" />
<Add directory="../" />
</Linker>

View File

@ -95,9 +95,9 @@ void CBattleInterface::addNewAnim(CBattleAnimation * anim)
animsAreDisplayed.setn(true);
}
CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2,
const CGHeroInstance *hero1, const CGHeroInstance *hero2,
const SDL_Rect & myRect,
CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2,
const CGHeroInstance *hero1, const CGHeroInstance *hero2,
const SDL_Rect & myRect,
shared_ptr<CPlayerInterface> att, shared_ptr<CPlayerInterface> defen)
: background(nullptr), queue(nullptr), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
activeStack(nullptr), mouseHoveredStack(nullptr), stackToActivate(nullptr), selectedStack(nullptr), previouslyHoveredHex(-1),
@ -110,7 +110,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
if(!curInt)
{
//May happen when we are defending during network MP game -> attacker interface is just not present
curInt = defenderInt;
curInt = defenderInt;
}
animsAreDisplayed.setn(false);
@ -463,7 +463,7 @@ CBattleInterface::~CBattleInterface()
delete bigForceField[1];
delete siegeH;
//TODO: play AI tracks if battle was during AI turn
//if (!curInt->makingTurn)
//CCS->musich->playMusicFromSet(CCS->musich->aiMusics, -1);
@ -865,7 +865,7 @@ void CBattleInterface::bAutofightf()
{
if(spellDestSelectMode) //we are casting a spell
return;
//Stop auto-fight mode
if(curInt->isAutoFightOn)
{
@ -875,7 +875,7 @@ void CBattleInterface::bAutofightf()
}
else
{
curInt->isAutoFightOn = true;
curInt->isAutoFightOn = true;
blockUI(true);
auto ai = CDynLibHandler::getNewBattleAI(settings["server"]["neutralAI"].String());
@ -911,7 +911,7 @@ void CBattleInterface::bSpellf()
auto blockingBonus = currentHero()->getBonusLocalFirst(Selector::type(Bonus::BLOCK_ALL_MAGIC));
if(!blockingBonus)
return;;
if(blockingBonus->source == Bonus::ARTIFACT)
{
const int artID = blockingBonus->sid;
@ -920,7 +920,7 @@ void CBattleInterface::bSpellf()
std::string heroName = myHero->hasArt(artID) ? myHero->name : enemyHero().name;
//%s wields the %s, an ancient artifact which creates a p dead to all magic.
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[683])
LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[683])
% heroName % CGI->arth->artifacts[artID]->Name()));
}
}
@ -1004,7 +1004,7 @@ void CBattleInterface::newStack(const CStack * stack)
creAnims[stack->ID]->pos.y = coords.y;
creAnims[stack->ID]->pos.w = creAnims[stack->ID]->getWidth();
creAnims[stack->ID]->setType(CCreatureAnim::HOLDING);
}
void CBattleInterface::stackRemoved(int stackID)
@ -1226,7 +1226,7 @@ void CBattleInterface::displayBattleFinished()
void CBattleInterface::spellCast( const BattleSpellCast * sc )
{
const CSpell &spell = *CGI->spellh->spells[sc->id];
const CSpell &spell = *CGI->spellh->objects[sc->id];
std::vector< std::string > anims; //for magic arrow and ice bolt
@ -1342,7 +1342,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
if(sc->castedByHero)
{
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetHeroInfo(sc->side).name);
boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id]->name); //spell name
boost::algorithm::replace_first(text, "%s", CGI->spellh->objects[sc->id]->name); //spell name
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->getCreature()->namePl ); //target
}
else
@ -1452,7 +1452,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
}
}
if (!customSpell && !sc->dmgToDisplay)
boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id]->name); //simple spell name
boost::algorithm::replace_first(text, "%s", CGI->spellh->objects[sc->id]->name); //simple spell name
if (text.size())
console->addText(text);
}
@ -1472,13 +1472,13 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
//TODO artifacts that cast spell; scripts some day
boost::algorithm::replace_first(text, "%s", "Something");
}
boost::algorithm::replace_first(text, "%s", CGI->spellh->spells[sc->id]->name);
boost::algorithm::replace_first(text, "%s", CGI->spellh->objects[sc->id]->name);
console->addText(text);
}
if(sc->dmgToDisplay && !customSpell)
{
std::string dmgInfo = CGI->generaltexth->allTexts[376];
boost::algorithm::replace_first(dmgInfo, "%s", CGI->spellh->spells[sc->id]->name); //simple spell name
boost::algorithm::replace_first(dmgInfo, "%s", CGI->spellh->objects[sc->id]->name); //simple spell name
boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(sc->dmgToDisplay));
console->addText(dmgInfo); //todo: casualties (?)
}
@ -1500,8 +1500,8 @@ void CBattleInterface::battleStacksEffectsSet(const SetStackEffect & sse)
{
for(auto & elem : sse.stacks)
{
bool areaEffect(CGI->spellh->spells[effID]->getTargetType() == CSpell::ETargetType::NO_TARGET);
displayEffect(CGI->spellh->spells[effID]->mainEffectAnim, curInt->cb->battleGetStackByID(elem)->position, areaEffect);
bool areaEffect(CGI->spellh->objects[effID]->getTargetType() == CSpell::ETargetType::NO_TARGET);
displayEffect(CGI->spellh->objects[effID]->mainEffectAnim, curInt->cb->battleGetStackByID(elem)->position, areaEffect);
}
}
else if (sse.stacks.size() == 1 && sse.effect.size() == 2)
@ -1545,7 +1545,7 @@ void CBattleInterface::castThisSpell(int spellID)
//choosing possible tragets
const CGHeroInstance * castingHero = (attackingHeroInstance->tempOwner == curInt->playerID) ? attackingHeroInstance : defendingHeroInstance;
assert(castingHero); // code below assumes non-null hero
sp = CGI->spellh->spells[spellID];
sp = CGI->spellh->objects[spellID];
spellSelMode = ANY_LOCATION;
if(sp->getTargetType() == CSpell::CREATURE)
{
@ -1755,7 +1755,7 @@ void CBattleInterface::getPossibleActionsForStack(const CStack * stack)
BonusList spellBonuses = *stack->getBonuses (Selector::type(Bonus::SPELLCASTER));
for (Bonus * spellBonus : spellBonuses)
{
spell = CGI->spellh->spells[spellBonus->subtype];
spell = CGI->spellh->objects[spellBonus->subtype];
switch (spellBonus->subtype)
{
case SpellID::REMOVE_OBSTACLE:
@ -1856,7 +1856,7 @@ void CBattleInterface::endAction(const BattleAction* action)
queue->update();
if(tacticsMode) //stack ended movement in tactics phase -> select the next one
bTacticNextStack(stack);
bTacticNextStack(stack);
if( action->actionType == Battle::HERO_SPELL) //we have activated next stack after sending request that has been just realized -> blockmap due to movement has changed
redrawBackgroundWithHexes(activeStack);
@ -2096,10 +2096,10 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
if(!myTurn) //we are not permit to do anything
return;
// This function handles mouse move over hexes and l-clicking on them.
// This function handles mouse move over hexes and l-clicking on them.
// First we decide what happens if player clicks on this hex and set appropriately
// consoleMsg, cursorFrame/Type and prepare lambda realizeAction.
//
//
// Then, depending whether it was hover/click we either call the action or set tooltip/cursor.
//used when hovering -> tooltip message and cursor to be set
@ -2107,7 +2107,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
bool setCursor = true; //if we want to suppress setting cursor
ECursor::ECursorTypes cursorType = ECursor::COMBAT;
int cursorFrame = ECursor::COMBAT_POINTER; //TODO: is this line used?
//used when l-clicking -> action to be called upon the click
std::function<void()> realizeAction;
@ -2123,7 +2123,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
bool ourStack = false;
if (shere)
ourStack = shere->owner == curInt->playerID;
//stack changed, update selection border
if (shere != mouseHoveredStack)
{
@ -2137,9 +2137,9 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
{
bool legalAction = false; //this action is legal and can be performed
bool notLegal = false; //this action is not legal and should display message
switch (action)
{
{
case CHOOSE_TACTICS_STACK:
if (shere && ourStack)
legalAction = true;
@ -2191,7 +2191,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
if (isCastingPossibleHere (sactive, shere, myNumber)) //need to be called before sp is determined
{
bool rise = false; //TODO: can you imagine rising hostile creatures?
sp = CGI->spellh->spells[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo];
sp = CGI->spellh->objects[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo];
if (sp && sp->isRisingSpell())
rise = true;
if (shere && (shere->alive() || rise) && ourStack)
@ -2221,7 +2221,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
if (creatureCasting)
skill = sactive->valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, SpellID::TELEPORT));
else
skill = getActiveHero()->getSpellSchoolLevel (CGI->spellh->spells[spellToCast->additionalInfo]);
skill = getActiveHero()->getSpellSchoolLevel (CGI->spellh->objects[spellToCast->additionalInfo]);
//TODO: explicitely save power, skill
if (curInt->cb->battleCanTeleportTo(selectedStack, myNumber, skill))
legalAction = true;
@ -2247,7 +2247,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
auto tilesThatMustBeClear = sp->rangeInHexes(myNumber, hero->getSpellSchoolLevel(sp), side, &hexesOutsideBattlefield);
for(BattleHex hex : tilesThatMustBeClear)
{
if(curInt->cb->battleGetStackByPos(hex, false) || !!curInt->cb->battleGetObstacleOnPos(hex, false)
if(curInt->cb->battleGetStackByPos(hex, false) || !!curInt->cb->battleGetObstacleOnPos(hex, false)
|| !hex.isAvailable())
{
legalAction = false;
@ -2377,7 +2377,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
break;
case HOSTILE_CREATURE_SPELL:
case FRIENDLY_CREATURE_SPELL:
sp = CGI->spellh->spells[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; //necessary if creature has random Genie spell at same time
sp = CGI->spellh->objects[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; //necessary if creature has random Genie spell at same time
consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[27]) % sp->name % shere->getName()); //Cast %s on %s
switch (sp->id)
{
@ -2390,7 +2390,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
isCastingPossible = true;
break;
case ANY_LOCATION:
sp = CGI->spellh->spells[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; //necessary if creature has random Genie spell at same time
sp = CGI->spellh->objects[creatureCasting ? creatureSpellToCast : spellToCast->additionalInfo]; //necessary if creature has random Genie spell at same time
consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % sp->name); //Cast %s
isCastingPossible = true;
break;
@ -2412,7 +2412,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
break;
case SACRIFICE:
consoleMsg = (boost::format(CGI->generaltexth->allTexts[549]) % shere->getName()).str(); //sacrifice the %s
cursorFrame = ECursor::COMBAT_SACRIFICE;
cursorFrame = ECursor::COMBAT_SACRIFICE;
spellToCast->selectedStack = shere->ID; //sacrificed creature is selected
isCastingPossible = true;
break;
@ -2487,7 +2487,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % sp->name); //Cast %s
break;
}
realizeAction = [=]
{
if (secondaryTarget) //select that target now
@ -2495,7 +2495,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
possibleActions.clear();
switch (sp->id.toEnum())
{
case SpellID::TELEPORT: //don't cast spell yet, only select target
case SpellID::TELEPORT: //don't cast spell yet, only select target
possibleActions.push_back (TELEPORT);
spellToCast->selectedStack = selectedStack->ID;
break;
@ -2548,14 +2548,14 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
}
if(eventType == LCLICK && realizeAction)
{
//opening creature window shouldn't affect myTurn...
//opening creature window shouldn't affect myTurn...
if ((currentAction != CREATURE_INFO) && !secondaryTarget)
{
myTurn = false; //tends to crash with empty calls
}
realizeAction();
if (!secondaryTarget) //do not replace teleport or sacrifice cursor
CCS->curh->changeGraphic(ECursor::COMBAT, ECursor::COMBAT_POINTER);
CCS->curh->changeGraphic(ECursor::COMBAT, ECursor::COMBAT_POINTER);
this->console->alterText("");
}
};
@ -2566,7 +2566,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
bool CBattleInterface::isCastingPossibleHere (const CStack * sactive, const CStack * shere, BattleHex myNumber)
{
creatureCasting = stackCanCastSpell && !spellDestSelectMode; //TODO: allow creatures to cast aimed spells
bool isCastingPossible = true;
int spellID = -1;
@ -2579,8 +2579,8 @@ bool CBattleInterface::isCastingPossibleHere (const CStack * sactive, const CSta
spellID = spellToCast->additionalInfo;
sp = nullptr;
if (spellID >= 0)
sp = CGI->spellh->spells[spellID];
if (spellID >= 0)
sp = CGI->spellh->objects[spellID];
if (sp)
{
@ -2846,7 +2846,7 @@ void CBattleInterface::requestAutofightingAIToTakeAction()
{
assert(curInt->isAutoFightOn);
boost::thread aiThread([&]
boost::thread aiThread([&]
{
auto ba = new BattleAction(curInt->autofightingAI->activeStack(activeStack));
@ -2874,7 +2874,7 @@ void CBattleInterface::requestAutofightingAIToTakeAction()
activateStack();
}
});
aiThread.detach();
}
@ -3116,7 +3116,7 @@ void CBattleInterface::showHighlightedHexes(SDL_Surface * to)
if(spellToCast) //when casting spell
{
//calculating spell school level
const CSpell & spToCast = *CGI->spellh->spells[spellToCast->additionalInfo];
const CSpell & spToCast = *CGI->spellh->objects[spellToCast->additionalInfo];
ui8 schoolLevel = 0;
auto caster = activeStack->attackerOwned ? attackingHeroInstance : defendingHeroInstance;
@ -3304,7 +3304,7 @@ void CBattleInterface::showAliveStacks(SDL_Surface * to, std::vector<const CStac
int pos = 0;
for(auto & spellId : stack->activeSpells())
{
pos += CGI->spellh->spells[ spellId ]->positiveness;
pos += CGI->spellh->objects[ spellId ]->positiveness;
}
return pos;
};

View File

@ -43,14 +43,14 @@ SSetCaptureState::~SSetCaptureState()
GH.defActionsDef = prevActions;
}
static inline void
static inline void
processList(const ui16 mask, const ui16 flag, std::list<CIntObject*> *lst, std::function<void (std::list<CIntObject*> *)> cb)
{
if (mask & flag)
cb(lst);
cb(lst);
}
void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb)
void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std::list<CIntObject*> *)> cb)
{
processList(CIntObject::LCLICK,activityFlag,&lclickable,cb);
processList(CIntObject::RCLICK,activityFlag,&rclickable,cb);
@ -58,14 +58,14 @@ void CGuiHandler::processLists(const ui16 activityFlag, std::function<void (std:
processList(CIntObject::MOVE,activityFlag,&motioninterested,cb);
processList(CIntObject::KEYBOARD,activityFlag,&keyinterested,cb);
processList(CIntObject::TIME,activityFlag,&timeinterested,cb);
processList(CIntObject::WHEEL,activityFlag,&wheelInterested,cb);
processList(CIntObject::DOUBLECLICK,activityFlag,&doubleClickInterested,cb);
processList(CIntObject::WHEEL,activityFlag,&wheelInterested,cb);
processList(CIntObject::DOUBLECLICK,activityFlag,&doubleClickInterested,cb);
}
void CGuiHandler::handleElementActivate(CIntObject * elem, ui16 activityFlag)
{
processLists(activityFlag,[&](std::list<CIntObject*> * lst){
lst->push_front(elem);
lst->push_front(elem);
});
elem->active_m |= activityFlag;
}
@ -75,7 +75,7 @@ void CGuiHandler::handleElementDeActivate(CIntObject * elem, ui16 activityFlag)
processLists(activityFlag,[&](std::list<CIntObject*> * lst){
auto hlp = std::find(lst->begin(),lst->end(),elem);
assert(hlp != lst->end());
lst->erase(hlp);
lst->erase(hlp);
});
elem->active_m &= ~activityFlag;
}
@ -105,7 +105,7 @@ void CGuiHandler::pushInt( IShowActivatable *newInt )
assert(boost::range::find(listInt, newInt) == listInt.end()); // do not add same object twice
//a new interface will be present, we'll need to use buffer surface (unless it's advmapint that will alter screenBuf on activate anyway)
screenBuf = screen2;
screenBuf = screen2;
if(!listInt.empty())
listInt.front()->deactivate();
@ -140,7 +140,7 @@ IShowActivatable * CGuiHandler::topInt()
{
if(listInt.empty())
return nullptr;
else
else
return listInt.front();
}
@ -345,7 +345,7 @@ void CGuiHandler::simpleRedraw()
}
void CGuiHandler::handleMoveInterested( const SDL_MouseMotionEvent & motion )
{
{
//sending active, MotionInterested objects mouseMoved() call
std::list<CIntObject*> miCopy = motioninterested;
for(auto & elem : miCopy)
@ -407,7 +407,7 @@ CGuiHandler::CGuiHandler()
// Creates the FPS manager and sets the framerate to 48 which is doubled the value of the original Heroes 3 FPS rate
mainFPSmng = new CFramerateManager(48);
mainFPSmng->init(); // resets internal clock, needed for FPS manager
//do not init CFramerateManager here --AVS
}
CGuiHandler::~CGuiHandler()

View File

@ -8,6 +8,7 @@
"creature" : 150,
"faction" : 9,
"hero" : 156,
"spell" : 81,
"mapVersion" : 28 // max supported version, SoD
},

View File

@ -59,5 +59,9 @@
[
"config/bonuses.json",
"config/bonuses_texts.json"
]
],
"spells" :
[
"config/spell_info.json"
]
}

View File

@ -72,6 +72,11 @@
"description": "List of configuration files for heroes",
"items": { "type":"string", "format" : "textFile" }
},
"spells": {
"type":"array",
"description": "List of configuration files for spells",
"items": { "type":"string", "format" : "textFile" }
},
"filesystem": {
"type":"object",

206
config/schemas/spell.json Normal file
View File

@ -0,0 +1,206 @@
{
"type":"object",
"$schema": "http://json-schema.org/draft-04/schema",
"title" : "VCMI spell format",
"description" : "Format used to define new spells in VCMI",
"definitions" : {
"flags" :{
"type" : "object",
"additionalProperties" : {
"type":"boolean"
}
},
"levelInfo":{
"type": "object",
"required":["range","description","cost","power","aiValue","range"],
"additionalProperties" : false,
"properties":{
"description":{
"type": "string",
"description": "Localizable description. Use {xxx} for formatting"
},
"cost":{
"type": "number",
"description":"Cost in mana points"
},
"power":{
"type": "number",
},
"aiValue":{
"type": "number",
},
"range":{
"type": "string",
"description": "spell range description in SRSL"
},
"effects":{
"type": "object",
"description": "Timed effects",
"additionalProperties" : {
"$ref" : "vcmi:bonus"
}
}
}
}
},
"required" : ["type", "name", "school", "level", "power","gainChance","flags","levels"],
"additionalProperties" : false,
"properties": {
"index":{
"type": "number",
"description": "numeric id of spell required only for original spells, prohibited for new spells"
},
"type":{
"type": "string",
"enum": ["adventure", "combat", "ability"],
"description":"Spell type"
},
"name":{
"type": "string",
"description": "Localizable name"
},
"school":{
"type": "object",
"description": "Spell schools",
"additionalProperties": false,
"properties":{
"air":{"type": "boolean"},
"fire":{"type": "boolean"},
"earth":{"type": "boolean"},
"water":{"type": "boolean"}
}
},
"level":{
"type": "number",
"description": "Spell level",
"minimum" : 0,
"maximum" : 5
},
"power":{
"type": "number",
"description": "Base power",
},
"defaultGainChance":{
"type": "nomber",
"description": "Gain chance by default for all factions"
},
"gainChance":{
"type": "object",
"description": "Chance in % to gain for faction. NOTE: this field is merged with faction config",
"additionalProperties" : {
"type": "number",
"minimum" : 0
}
},
"targetType":{
"type": "string",
"enum": ["NO_TARGET","CREATURE","OBSTACLE","CREATURE_EXPERT_MASSIVE"]
},
"anim":{
"type": "number",
"description": "Main effect animation (AC format), -1 - none",
"minimum": -1
},
"counters":{
"$ref" : "#/definitions/flags",
"description": "Flags structure ids of countering spells"
},
"flags":{
"type": "object",
"description": "Various flags",
"additionalProperties" : false,
"properties":{
"indifferent": {
"type":"boolean",
"description": "Spell is indifferent for target"
},
"negative": {
"type":"boolean",
"description": "Spell is negative for target"
},
"positive": {
"type":"boolean",
"description": "Spell is positive for target"
},
"damage": {
"type":"boolean",
"description": "Spell does damage (direct or indirect)"
},
"offensive": {
"type":"boolean",
"description": "Spell does direct damage (implicitly sets damage and negative)"
},
"rising":{
"type":"boolean",
"description": "Rising spell (implicitly sets positive)"
}
}
},
"immunity":{
"$ref" : "#/definitions/flags",
"description": "flags structure of bonus names, any one of these bonus grants immunity"
},
"absoluteImmunity":{
"$ref" : "#/definitions/flags",
"description": "flags structure of bonus names. Any one of these bonus grants immunity, cant be negated"
},
"limit":{
"$ref" : "#/definitions/flags",
"description": "flags structure of bonus names, presence of all bonuses required to be affected by"
},
"graphics":{
"type": "object",
"additionalProperties" : false,
"properties":{
"iconImmune":{
"type":"string",
"description":"Resourse path of icon for SPELL_IMMUNITY bonus (relative to DATA or SPRITES)",
"format" : "imageFile"
}
}
},
"levels":{
"type": "object",
"additionalProperties" : false,
"required" : ["none", "basic", "advanced", "expert"],
"properties":{
"none":{
"$ref" : "#/definitions/levelInfo"
},
"basic":{
"$ref" : "#/definitions/levelInfo"
},
"advanced":{
"$ref" : "#/definitions/levelInfo"
},
"expert":{
"$ref" : "#/definitions/levelInfo"
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -166,9 +166,9 @@ ui32 CBattleInfoCallback::calculateHealedHP(const CGHeroInstance * caster, const
bool resurrect = spell->isRisingSpell();
int healedHealth;
if (spell->id == SpellID::SACRIFICE && sacrificedStack)
healedHealth = (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + sacrificedStack->MaxHealth() + spell->powers[caster->getSpellSchoolLevel(spell)]) * sacrificedStack->count;
healedHealth = (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + sacrificedStack->MaxHealth() + spell->getPower(caster->getSpellSchoolLevel(spell))) * sacrificedStack->count;
else
healedHealth = caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) * spell->power + spell->powers[caster->getSpellSchoolLevel(spell)];
healedHealth = caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) * spell->power + spell->getPower(caster->getSpellSchoolLevel(spell)); //???
healedHealth = calculateSpellBonus(healedHealth, spell, caster, stack);
return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0));
}
@ -182,7 +182,7 @@ ui32 CBattleInfoCallback::calculateHealedHP(int healedHealth, const CSpell * spe
ui32 CBattleInfoCallback::calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const
{
bool resurrect = spell->isRisingSpell();
int healedHealth = usedSpellPower * spell->power + spell->powers[spellSchoolLevel];
int healedHealth = usedSpellPower * spell->power + spell->getPower(spellSchoolLevel);
return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0));
}
bool BattleInfo::resurrects(SpellID spellid) const

View File

@ -808,8 +808,8 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
auto battleBonusValue = [&](const IBonusBearer * bearer, CSelector selector) -> int
{
auto noLimit = Selector::effectRange(Bonus::NO_LIMIT);
auto limitMatches = info.shooting
? Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT)
auto limitMatches = info.shooting
? Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT)
: Selector::effectRange(Bonus::ONLY_MELEE_FIGHT);
//any regular bonuses or just ones for melee/ranged
@ -872,7 +872,7 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo &info) c
{
if(defenderType->idNumber == affectedId)
{
attackDefenceDifference += SpellID(SpellID::SLAYER).toSpell()->powers[spLevel];
attackDefenceDifference += SpellID(SpellID::SLAYER).toSpell()->getPower(spLevel);
break;
}
}
@ -1573,7 +1573,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
{
//can't clone already cloned creature
if (vstd::contains(subject->state, EBattleStackState::CLONED))
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
//TODO: how about stacks casting Clone?
//currently Clone casted by stack is assumed Expert level
ui8 schoolLevel;
@ -1585,14 +1585,14 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
{
schoolLevel = 3;
}
if (schoolLevel < 3)
{
int maxLevel = (std::max(schoolLevel, (ui8)1) + 4);
int creLevel = subject->getCreature()->level;
if (maxLevel < creLevel) //tier 1-5 for basic, 1-6 for advanced, any level for expert
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
}
break;
case SpellID::DISPEL_HELPFUL_SPELLS:
@ -1630,7 +1630,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleIsImmune(const C
ui64 subjectHealth = (subject->count - 1) * subject->MaxHealth() + subject->firstHPleft;
//apply 'damage' bonus for hypnotize, including hero specialty
ui64 maxHealth = calculateSpellBonus (caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
* spell->power + spell->powers[caster->getSpellSchoolLevel(spell)], spell, caster, subject);
* spell->power + spell->getPower(caster->getSpellSchoolLevel(spell)), spell, caster, subject);
if (subjectHealth > maxHealth)
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
}
@ -1689,7 +1689,7 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
bool allStacksImmune = true;
//we are interested only in enemy stacks when casting offensive spells
auto stacks = spell->isNegative() ? battleAliveStacks(!side) : battleAliveStacks();
for(auto stack : stacks)
for(auto stack : stacks)
{
if(!battleIsImmune(castingHero, spell, mode, stack->position))
{
@ -1866,19 +1866,19 @@ ESpellCastProblem::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell
switch (obstacle->obstacleType)
{
case CObstacleInstance::ABSOLUTE_OBSTACLE: //cliff-like obstacles cant be removed
case CObstacleInstance::MOAT:
case CObstacleInstance::MOAT:
return ESpellCastProblem::NO_APPROPRIATE_TARGET;
case CObstacleInstance::USUAL:
return ESpellCastProblem::OK;
// //TODO FIRE_WALL only for ADVANCED level casters
// case CObstacleInstance::FIRE_WALL:
// return
// return
// //TODO other magic obstacles for EXPERT
// case CObstacleInstance::QUICKSAND:
// case CObstacleInstance::LAND_MINE:
// case CObstacleInstance::FORCE_FIELD:
// return
// return
default:
// assert(0);
return ESpellCastProblem::OK;
@ -1959,7 +1959,7 @@ ui32 CBattleInfoCallback::calculateSpellDmg( const CSpell * sp, const CGHeroInst
return 0;
ret = usedSpellPower * sp->power;
ret += sp->powers[spellSchoolLevel];
ret += sp->getPower(spellSchoolLevel);
//affected creature-specific part
if(affectedCreature)
@ -2156,7 +2156,7 @@ SpellID CBattleInfoCallback::getRandomBeneficialSpell(const CStack * subject) co
RETURN_IF_NOT_BATTLE(SpellID::NONE);
std::vector<SpellID> possibleSpells;
for(const CSpell *spell : VLC->spellh->spells)
for(const CSpell *spell : VLC->spellh->objects)
{
if (spell->isPositive()) //only positive
{

View File

@ -8,7 +8,7 @@
*
*/
#pragma once
#pragma once
#include "IBonusTypeHandler.h"
#include "IHandlerBase.h"
@ -29,15 +29,15 @@ class MacroString
};
Item(ItemType _type, std::string _value): type(_type), value(_value){};
ItemType type;
std::string value; //consant string or macro name
std::string value; //constant string or macro name
};
std::vector<Item> items;
public:
typedef std::function <std::string(const std::string&)> GetValue;
MacroString(){};
MacroString(const std::string &format);
std::string build(const GetValue& getValue) const;
};
@ -46,49 +46,49 @@ class DLL_LINKAGE CBonusType
public:
CBonusType();
~CBonusType();
template <typename Handler> void serialize(Handler &h, const int version)
{
h & icon & nameTemplate & descriptionTemplate & hidden;
if (!h.saving)
buildMacros();
}
protected:
private:
void buildMacros();
MacroString name, description;
friend class CBonusTypeHandler;
std::string icon;
std::string nameTemplate, descriptionTemplate;
bool hidden;
};
class DLL_LINKAGE CBonusTypeHandler : public IBonusTypeHandler
{
public:
CBonusTypeHandler();
virtual ~CBonusTypeHandler();
std::string bonusToString(const Bonus *bonus, const IBonusBearer *bearer, bool description) const override;
std::string bonusToGraphics(const Bonus *bonus) const override;
void load();
void load(const JsonNode& config);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & bonusTypes;
}
private:
void loadItem(const JsonNode &source, CBonusType &dest);
std::vector<CBonusType> bonusTypes; //index = BonusTypeID
std::vector<CBonusType> bonusTypes; //index = BonusTypeID
};

View File

@ -82,7 +82,7 @@ static CApplier<CBaseForGSApply> *applierGs = nullptr;
// virtual void preInit()=0;
// virtual void postInit()=0;
// };
//
//
// template <typename T>
// class CObjectCaller : public IObjectCaller
// {
@ -101,29 +101,29 @@ static CApplier<CBaseForGSApply> *applierGs = nullptr;
// {
// public:
// std::vector<IObjectCaller*> apps;
//
//
// template<typename T> void registerType(const T * t=nullptr)
// {
// apps.push_back(new CObjectCaller<T>);
// }
//
//
// CObjectCallersHandler()
// {
// registerTypesMapObjects(*this);
// }
//
//
// ~CObjectCallersHandler()
// {
// for (auto & elem : apps)
// delete elem;
// }
//
//
// void preInit()
// {
// // for (size_t i = 0; i < apps.size(); i++)
// // apps[i]->preInit();
// }
//
//
// void postInit()
// {
// //for (size_t i = 0; i < apps.size(); i++)
@ -1319,7 +1319,7 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
}
}
if(!(travelOptions.whatHeroKeeps & 8))
if(!(travelOptions.whatHeroKeeps & 8))
{
for(CGHeroInstance * cgh : crossoverHeroes)
{
@ -1344,18 +1344,18 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
if(artifactPosition == ArtifactPosition::SPELLBOOK) continue; // do not handle spellbook this way
const ArtSlotInfo *info = hero->getSlot(artifactPosition);
if(!info)
if(!info)
continue;
// TODO: why would there be nullptr artifacts?
const CArtifactInstance *art = info->artifact;
if(!art)
if(!art)
continue;
int id = art->artType->id;
assert( 8*18 > id );//number of arts that fits into h3m format
bool takeable = travelOptions.artifsKeptByHero[id / 8] & ( 1 << (id%8) );
ArtifactLocation al(hero, artifactPosition);
if(!takeable && !al.getSlot()->locked) //don't try removing locked artifacts -> it crashes #1719
al.removeArtifact();
@ -1817,7 +1817,8 @@ void CGameState::initTowns()
ev.buildings.insert(BuildingID::HORDE_2);
}
}
//init spells
//init spells
logGlobal->debugStream() << "\t\tTown init spells";
vti->spells.resize(GameConstants::SPELL_LEVELS);
for(ui32 z=0; z<vti->obligatorySpells.size();z++)
@ -1825,14 +1826,15 @@ void CGameState::initTowns()
CSpell *s = vti->obligatorySpells[z].toSpell();
vti->spells[s->level-1].push_back(s->id);
vti->possibleSpells -= s->id;
}
}
logGlobal->debugStream() << "\t\tTown init spells2";
while(vti->possibleSpells.size())
{
ui32 total=0;
int sel = -1;
for(ui32 ps=0;ps<vti->possibleSpells.size();ps++)
total += vti->possibleSpells[ps].toSpell()->probabilities[vti->subID];
total += vti->possibleSpells[ps].toSpell()->getProbability(vti->subID);
if (total == 0) // remaining spells have 0 probability
break;
@ -1840,7 +1842,7 @@ void CGameState::initTowns()
int r = ran()%total;
for(ui32 ps=0; ps<vti->possibleSpells.size();ps++)
{
r -= vti->possibleSpells[ps].toSpell()->probabilities[vti->subID];
r -= vti->possibleSpells[ps].toSpell()->getProbability(vti->subID);
if(r<0)
{
sel = ps;
@ -1856,7 +1858,9 @@ void CGameState::initTowns()
}
vti->possibleSpells.clear();
if(vti->getOwner() != PlayerColor::NEUTRAL)
getPlayer(vti->getOwner())->towns.push_back(vti);
getPlayer(vti->getOwner())->towns.push_back(vti);
logGlobal->debugStream() << "\t\tTown init spells3";
}
}
@ -3259,7 +3263,7 @@ DuelParameters DuelParameters::fromJSON(const std::string &fname)
const JsonNode & spells = n["spells"];
if(spells.getType() == JsonNode::DATA_STRING && spells.String() == "all")
{
for(auto spell : VLC->spellh->spells)
for(auto spell : VLC->spellh->objects)
if(spell->id <= SpellID::SUMMON_AIR_ELEMENTAL)
ss.spells.insert(spell->id);
}

View File

@ -14,6 +14,7 @@
#include "StringConstants.h"
#include "CStopWatch.h"
#include "IHandlerBase.h"
#include "CSpellHandler.h"
/*
* CModHandler.cpp, part of VCMI engine
@ -337,13 +338,14 @@ void CContentHandler::ContentTypeHandler::afterLoadFinalization()
CContentHandler::CContentHandler()
{
handlers.insert(std::make_pair("heroClasses", ContentTypeHandler(&VLC->heroh->classes, "heroClass")));
handlers.insert(std::make_pair("heroClasses", ContentTypeHandler(&VLC->heroh->classes, "heroClass")));
handlers.insert(std::make_pair("artifacts", ContentTypeHandler(VLC->arth, "artifact")));
handlers.insert(std::make_pair("creatures", ContentTypeHandler(VLC->creh, "creature")));
handlers.insert(std::make_pair("factions", ContentTypeHandler(VLC->townh, "faction")));
handlers.insert(std::make_pair("heroes", ContentTypeHandler(VLC->heroh, "hero")));
handlers.insert(std::make_pair("spells", ContentTypeHandler(VLC->spellh, "spell")));
//TODO: spells, bonuses, something else?
//TODO: bonuses, something else?
}
bool CContentHandler::preloadModData(std::string modName, JsonNode modConfig, bool validate)

View File

@ -1521,7 +1521,7 @@ void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
int CGHeroInstance::getSpellCost(const CSpell *sp) const
{
return sp->costs[getSpellSchoolLevel(sp)];
return sp->getCost(getSpellSchoolLevel(sp));
}
void CGHeroInstance::pushPrimSkill( PrimarySkill::PrimarySkill which, int val )
@ -5538,7 +5538,7 @@ void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const
iw.text.addTxt(MetaString::ADVOB_TXT, 184); //%s learns a spell
}
iw.text.addReplacement(h->name);
std::vector<ConstTransitivePtr<CSpell> > * sp = &VLC->spellh->spells;
std::vector<ConstTransitivePtr<CSpell> > * sp = &VLC->spellh->objects;
for(auto i=spells.cbegin(); i != spells.cend(); i++)
{
if ((*sp)[*i]->level <= h->getSecSkillLevel(SecondarySkill::WISDOM) + 2) //enough wisdom

View File

@ -3,11 +3,12 @@
#include "CGeneralTextHandler.h"
#include "filesystem/Filesystem.h"
#include "VCMI_Lib.h"
#include "JsonNode.h"
#include <cctype>
#include "BattleHex.h"
#include "CModHandler.h"
#include "StringConstants.h"
/*
* CSpellHandler.cpp, part of VCMI engine
@ -18,6 +19,12 @@
* Full text of license available in license.txt file, in main folder
*
*/
namespace SpellConfigJson
{
static const std::string level_names[] = {"none","basic","advanced","expert"};
}
using namespace boost::assign;
namespace SRSLPraserHelpers
@ -124,11 +131,18 @@ namespace SRSLPraserHelpers
using namespace SRSLPraserHelpers;
CSpell::CSpell()
CSpell::CSpell():
id(SpellID::NONE), level(0),
earth(false),water(false),fire(false),air(false),
power(0),
combatSpell(false),creatureAbility(false),
positiveness(ESpellPositiveness::NEUTRAL),
mainEffectAnim(-1),
defaultProbability(0),
isRising(false),isDamage(false),isOffensive(false),targetType(ETargetType::NO_TARGET)
{
isDamage = false;
isRising = false;
isOffensive = false;
}
CSpell::~CSpell()
@ -263,11 +277,16 @@ void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
logGlobal->errorStream() << __FUNCTION__ << " This spell (" + name + ") is missing entry for level " << level;
return;
}
if (effects[level].empty())
{
logGlobal->errorStream() << __FUNCTION__ << " This spell (" + name + ") has no effects for level " << level;
return;
}
lst.reserve(lst.size() + effects[level].size());
for (Bonus *b : effects[level])
{
//TODO: value, add value
lst.push_back(Bonus(*b));
}
}
@ -275,15 +294,26 @@ void CSpell::getEffects(std::vector<Bonus>& lst, const int level) const
bool CSpell::isImmuneBy(const IBonusBearer* obj) const
{
//todo: use new bonus API
//1. Check limiters
for(auto b : limiters)
{
if (!obj->hasBonusOfType(b))
return true;
}
if (obj->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES)) //Orb of vulnerability
return false; //TODO: some creaures are unaffected always, for example undead to resurrection.
//2. Check absolute immunities
//todo: check config: some creatures are unaffected always, for example undead to resurrection.
for(auto b : absoluteImmunities)
{
if (obj->hasBonusOfType(b))
return true;
}
//3. Check negation
if (obj->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES)) //Orb of vulnerability
return false;
//4. Check negatable immunities
for(auto b : immunities)
{
if (obj->hasBonusOfType(b))
@ -302,6 +332,7 @@ bool CSpell::isImmuneBy(const IBonusBearer* obj) const
return false;
};
//4. Check elemental immunities
if (fire)
{
if (battleTestElementalImmunity(Bonus::FIRE_IMMUNITY))
@ -349,6 +380,28 @@ void CSpell::setAttributes(const std::string& newValue)
targetType = NO_TARGET;
}
void CSpell::setIsOffensive(const bool val)
{
isOffensive = val;
if (val)
{
positiveness = CSpell::NEGATIVE;
isDamage = true;
}
}
void CSpell::setIsRising(const bool val)
{
isRising = val;
if (val)
{
positiveness = CSpell::POSITIVE;
}
}
bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
{
@ -359,68 +412,108 @@ bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos)
return false;
}
CSpell * CSpellHandler::loadSpell(CLegacyConfigParser & parser, const SpellID id)
{
auto spell = new CSpell; //new currently being read spell
spell->id = id;
spell->name = parser.readString();
spell->abbName = parser.readString();
spell->level = parser.readNumber();
spell->earth = parser.readString() == "x";
spell->water = parser.readString() == "x";
spell->fire = parser.readString() == "x";
spell->air = parser.readString() == "x";
spell->costs = parser.readNumArray<si32>(4);
spell->power = parser.readNumber();
spell->powers = parser.readNumArray<si32>(4);
for (int i = 0; i < 9 ; i++)
spell->probabilities[i] = parser.readNumber();
spell->AIVals = parser.readNumArray<si32>(4);
for (int i = 0; i < 4 ; i++)
spell->descriptions.push_back(parser.readString());
std::string attributes = parser.readString();
//spell fixes
if (id == SpellID::FORGETFULNESS)
{
//forgetfulness needs to get targets automatically on expert level
boost::replace_first(attributes, "CREATURE_TARGET", "CREATURE_TARGET_2");
}
if (id == SpellID::DISRUPTING_RAY)
{
// disrupting ray will now affect single creature
boost::replace_first(attributes,"2", "");
}
spell->setAttributes(attributes);
spell->mainEffectAnim = -1;
return spell;
}
CSpellHandler::CSpellHandler()
{
}
std::vector<JsonNode> CSpellHandler::loadLegacyData(size_t dataSize)
{
using namespace SpellConfigJson;
std::vector<JsonNode> legacyData;
CLegacyConfigParser parser("DATA/SPTRAITS.TXT");
auto read = [&,this](bool combat, bool alility)
auto readSchool = [&](JsonMap& schools, const std::string& name)
{
if (parser.readString() == "x")
{
schools[name].Bool() = true;
}
};
auto read = [&,this](bool combat, bool ability)
{
do
{
const SpellID id = SpellID(spells.size());
CSpell * spell = loadSpell(parser,id);
spell->combatSpell = combat;
spell->creatureAbility = alility;
spells.push_back(spell);
JsonNode lineNode(JsonNode::DATA_STRUCT);
const si32 id = legacyData.size();
lineNode["index"].Float() = id;
lineNode["type"].String() = ability ? "ability" : (combat ? "combat" : "adventure");
lineNode["name"].String() = parser.readString();
parser.readString(); //ignored unused abbreviated name
lineNode["level"].Float() = parser.readNumber();
auto& schools = lineNode["school"].Struct();
readSchool(schools, "earth");
readSchool(schools, "water");
readSchool(schools, "fire");
readSchool(schools, "air");
auto& levels = lineNode["levels"].Struct();
auto getLevel = [&](const size_t idx)->JsonMap&
{
assert(idx < GameConstants::SPELL_SCHOOL_LEVELS);
return levels[level_names[idx]].Struct();
};
auto costs = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
lineNode["power"].Float() = parser.readNumber();
auto powers = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
auto& chances = lineNode["gainChance"].Struct();
for (size_t i = 0; i < GameConstants::F_NUMBER ; i++){
chances[ETownType::names[i]].Float() = parser.readNumber();
}
auto AIVals = parser.readNumArray<si32>(GameConstants::SPELL_SCHOOL_LEVELS);
std::vector<std::string> descriptions;
for (size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS ; i++)
descriptions.push_back(parser.readString());
std::string attributes = parser.readString();
std::string targetType = "NO_TARGET";
if(attributes.find("CREATURE_TARGET_1") != std::string::npos
|| attributes.find("CREATURE_TARGET_2") != std::string::npos)
targetType = "CREATURE_EXPERT_MASSIVE";
else if(attributes.find("CREATURE_TARGET") != std::string::npos)
targetType = "CREATURE";
else if(attributes.find("OBSTACLE_TARGET") != std::string::npos)
targetType = "OBSTACLE";
lineNode["targetType"].String() = targetType;
//save parsed level specific data
for (size_t i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS; i++)
{
auto& level = getLevel(i);
level["description"].String() = descriptions[i];
level["cost"].Float() = costs[i];
level["power"].Float() = powers[i];
level["aiValue"].Float() = AIVals[i];
}
// logGlobal->errorStream() << lineNode;
legacyData.push_back(lineNode);
}
while (parser.endLine() && !parser.isNextEntryEmpty());
};
@ -438,128 +531,379 @@ CSpellHandler::CSpellHandler()
skip(3);
read(true,true);//read creature abilities
spells.push_back(spells[SpellID::ACID_BREATH_DEFENSE]); //clone Acid Breath attributes for Acid Breath damage effect
//TODO: maybe move to config
//clone Acid Breath attributes for Acid Breath damage effect
JsonNode temp = legacyData[SpellID::ACID_BREATH_DEFENSE];
temp["index"].Float() = SpellID::ACID_BREATH_DAMAGE;
legacyData.push_back(temp);
//loading of additional spell traits
JsonNode config(ResourceID("config/spell_info.json"));
config.setMeta("core");
objects.resize(legacyData.size());
for(auto &spell : config["spells"].Struct())
{
//reading exact info
int spellID = spell.second["id"].Float();
CSpell *s = spells[spellID];
s->positiveness = spell.second["effect"].Float();
s->mainEffectAnim = spell.second["anim"].Float();
s->range.resize(4);
int idx = 0;
for(const JsonNode &range : spell.second["ranges"].Vector())
s->range[idx++] = range.String();
s->counteredSpells = spell.second["counters"].convertTo<std::vector<SpellID> >();
s->identifier = spell.first;
VLC->modh->identifiers.registerObject("core", "spell", spell.first, spellID);
const JsonNode & flags_node = spell.second["flags"];
if (!flags_node.isNull())
{
auto flags = flags_node.convertTo<std::vector<std::string> >();
for (const auto & flag : flags)
{
if (flag == "damage")
{
s->isDamage = true;
}
else if (flag == "rising")
{
s->isRising = true;
}
else if (flag == "offensive")
{
s->isOffensive = true;
}
}
}
const JsonNode & effects_node = spell.second["effects"];
for (const JsonNode & bonus_node : effects_node.Vector())
{
auto &v_node = bonus_node["values"];
auto &a_node = bonus_node["ainfos"];
auto v = v_node.convertTo<std::vector<int> >();
auto a = a_node.convertTo<std::vector<int> >();
if(v.size() && v.size() != GameConstants::SPELL_SCHOOL_LEVELS)
logGlobal->errorStream() << s->name << " should either have no values or exactly " << GameConstants::SPELL_SCHOOL_LEVELS;
if(a.size() && a.size() != GameConstants::SPELL_SCHOOL_LEVELS)
logGlobal->errorStream() << s->name << " should either have no ainfos or exactly " << GameConstants::SPELL_SCHOOL_LEVELS;
s->effects.resize(GameConstants::SPELL_SCHOOL_LEVELS);
for (int i = 0; i < GameConstants::SPELL_SCHOOL_LEVELS; i++)
{
Bonus * b = JsonUtils::parseBonus(bonus_node);
b->sid = s->id; //for all
b->source = Bonus::SPELL_EFFECT;//for all
b->val = s->powers[i];
if (!v.empty())
b->val = v[i];
if (!a.empty())
b->additionalInfo = a[i];
s->effects[i].push_back(b);
}
}
auto find_in_map = [](std::string name, std::vector<Bonus::BonusType> &vec)
{
auto it = bonusNameMap.find(name);
if (it == bonusNameMap.end())
{
logGlobal->errorStream() << "Error: invalid bonus name" << name;
}
else
{
vec.push_back((Bonus::BonusType)it->second);
}
};
auto read_node = [&](std::string name, std::vector<Bonus::BonusType> &vec)
{
const JsonNode & node = spell.second[name];
if (!node.isNull())
{
auto names = node.convertTo<std::vector<std::string> >();
for(auto name : names)
find_in_map(name, vec);
}
};
read_node("immunity",s->immunities);
read_node("limit",s->limiters);
const JsonNode & graphicsNode = spell.second["graphics"];
if (!graphicsNode.isNull())
{
s->iconImmune = graphicsNode["iconImmune"].String();
}
}
return legacyData;
}
const std::string CSpellHandler::getTypeName()
{
return "spell";
}
static void fatalConfigurationError()
{
throw std::runtime_error("SpellHandler: Fatal configuration error, See log for details");
}
CSpell * CSpellHandler::loadFromJson(const JsonNode& json)
{
using namespace SpellConfigJson;
CSpell * spell = new CSpell();
const auto type_str = json["type"].String();
if (type_str == "ability")
{
spell->creatureAbility = true;
spell->combatSpell = true;
}
else
{
spell->creatureAbility = false;
spell->combatSpell = type_str == "combat";
}
spell->name = json["name"].String();
logGlobal->traceStream() << __FUNCTION__ << ": loading spell " << spell->name;
auto readFlag = [](const JsonNode& flagsNode, const std::string& name)
{
if (flagsNode.getType() != JsonNode::DATA_STRUCT)
{
logGlobal->errorStream() << "Flags node shall be object";
return false;
}
const JsonNode& flag = flagsNode[name];
if (flag.isNull())
{
return false;
}
else if (flag.getType() == JsonNode::DATA_BOOL)
{
return flag.Bool();
}
else
{
logGlobal->errorStream() << "Flag shall be boolean: "<<name;
return false;
}
};
const auto school_names = json["school"];
spell->air = readFlag(school_names, "air");
spell->earth = readFlag(school_names, "earth");
spell->fire = readFlag(school_names, "fire");
spell->water = readFlag(school_names, "water");
spell->level = json["level"].Float();
spell->power = json["power"].Float();
//TODO: default chance
spell->defaultProbability = json["defaultGainChance"].Float();
auto chances = json["gainChance"].Struct();
for(auto &node : chances)
{
int chance = node.second.Float();
VLC->modh->identifiers.requestIdentifier(node.second.meta, "faction",node.first, [=](si32 factionID)
{
spell->probabilities[factionID] = chance;
});
}
auto target_type_str = json["targetType"].String();
if (target_type_str == "NO_TARGET")
spell->targetType = CSpell::NO_TARGET;
else if (target_type_str == "CREATURE")
spell->targetType = CSpell::CREATURE;
else if (target_type_str == "OBSTACLE")
spell->targetType = CSpell::OBSTACLE;
else if (target_type_str == "CREATURE_EXPERT_MASSIVE")
spell->targetType = CSpell::CREATURE_EXPERT_MASSIVE;
else
{
logGlobal->errorStream() << spell->name << ": invalid target type '" <<target_type_str<<"'";
fatalConfigurationError();
}
spell->mainEffectAnim = json["anim"].Float();
for(const auto& k_v: json["counters"].Struct())
{
if (k_v.second.Bool())
{
JsonNode tmp(JsonNode::DATA_STRING);
tmp.meta = json.meta;
tmp.String() = k_v.first;
VLC->modh->identifiers.requestIdentifier(tmp,[=](si32 id){
spell->counteredSpells.push_back(SpellID(id));
});
}
}
//TODO: more error checking - f.e. conflicting flags
const auto flags = json["flags"];
//by default all flags are set to false in constructor
if (readFlag(flags,"summoning"))
{
logGlobal->warnStream() << spell->name << ": summoning flag in unimplemented";
}
spell->isDamage = readFlag(flags,"damage"); //do this before "offensive"
if (readFlag(flags,"offensive"))
{
spell->setIsOffensive(true);
}
if (readFlag(flags,"rising"))
{
spell->setIsRising(true);
}
const bool implicit_positiveness = spell->isOffensive || spell->isRising; //(!) "damage" does not mean NEGATIVE --AVS
if (readFlag(flags,"indifferent"))
{
spell->positiveness = CSpell::NEUTRAL;
}
else if (readFlag(flags,"negative"))
{
spell->positiveness = CSpell::NEGATIVE;
}
else if (readFlag(flags,"positive"))
{
spell->positiveness = CSpell::POSITIVE;
}
else if(!implicit_positiveness)
{
spell->positiveness = CSpell::NEUTRAL; //duplicates constructor but, just in case
logGlobal->errorStream() << "No positiveness specified, assumed NEUTRAL";
}
auto find_in_map = [&](std::string name, std::vector<Bonus::BonusType> &vec)
{
auto it = bonusNameMap.find(name);
if (it == bonusNameMap.end())
{
logGlobal->errorStream() << spell->name << ": invalid bonus name" << name;
}
else
{
vec.push_back((Bonus::BonusType)it->second);
}
};
auto read_node = [&](std::string name, std::vector<Bonus::BonusType> &vec)
{
const JsonNode & node = json[name];
if (!node.isNull())
{
for (auto key_value: node.Struct())
{
const std::string bonus_id = key_value.first;
const bool flag = key_value.second.Bool();
if (flag)
{
find_in_map(bonus_id, vec);
}
}
}
};
read_node("immunity",spell->immunities);
read_node("absoluteImmunity", spell->absoluteImmunities);
read_node("limit",spell->limiters);
const JsonNode & graphicsNode = json["graphics"];
if (!graphicsNode.isNull())
{
spell->iconImmune = graphicsNode["iconImmune"].String();
}
//load level attributes
const int level_count = GameConstants::SPELL_SCHOOL_LEVELS;
spell->AIVals.resize(level_count);
spell->costs.resize(level_count);
spell->descriptions.resize(level_count);
spell->powers.resize(level_count);
spell->range.resize(level_count);
const JsonNode & levels_node = json["levels"];
if (levels_node.isNull())
{
logGlobal->errorStream() << spell->name << ": no level specific data";
fatalConfigurationError();
}
if (levels_node.getType()!=JsonNode::DATA_STRUCT)
{
logGlobal->errorStream() << spell->name << ": level specific data shall be JSON object";
fatalConfigurationError();
}
const JsonMap & levels = json["levels"].Struct();
for(int level_idx = 0; level_idx < level_count; level_idx++)
{
const auto& level_node = levels.at(level_names[level_idx]);
if (level_node.getType()!=JsonNode::DATA_STRUCT)
{
logGlobal->errorStream() << spell->name << ": level specific data shall be JSON object";
fatalConfigurationError();
}
auto ensure_field = [&](const std::string json_name,JsonNode::JsonType type)->JsonNode
{
const auto& node = level_node[json_name];
if (node.isNull())
{
logGlobal->errorStream() << spell->name << ": mandatory field "<<json_name<<" missing";
fatalConfigurationError();
}
if (node.getType()!=type)
{
logGlobal->errorStream() << spell->name << ": field "<<json_name<<" - type mismatch";
fatalConfigurationError();
}
return node;
};
auto get_string_mandatory = [&](const std::string json_name, std::vector<std::string>& target)
{
const auto& node = ensure_field(json_name, JsonNode::DATA_STRING);
target[level_idx] = node.String();
};
auto get_string = [&](const std::string json_name, std::vector<std::string>& target)
{
const auto& node = level_node[json_name];
if (node.getType() == JsonNode::DATA_STRING)
{
target[level_idx] = node.String();
}
};
auto get_nomber = [&](const std::string json_name, std::vector<si32>& target)
{
const auto& node = level_node[json_name];
if (node.getType() == JsonNode::DATA_FLOAT)
{
target[level_idx] = node.Float();
}
};
auto get_nomber_mandatory = [&](const std::string json_name, std::vector<si32>& target)
{
const auto& node = ensure_field(json_name, JsonNode::DATA_FLOAT);
target[level_idx] = node.Float();
};
if (spell->isCreatureAbility())
{
get_string("description", spell->descriptions);
get_nomber("cost", spell->costs);
get_nomber("power", spell->powers);
get_nomber("aiValue", spell->AIVals);
}
else
{
get_string_mandatory("description", spell->descriptions);
get_nomber_mandatory("cost", spell->costs);
get_nomber_mandatory("power", spell->powers);
get_nomber_mandatory("aiValue", spell->AIVals);
}
const JsonNode& effects_node = level_node["effects"];
if (!effects_node.isNull())
{
if (spell->effects.empty())
spell->effects.resize(level_count);
for (const auto& elem : effects_node.Struct())
{
const JsonNode& bonus_node = elem.second;
Bonus * b = JsonUtils::parseBonus(bonus_node);
const bool usePowerAsValue = bonus_node["val"].isNull();
//TODO: make this work. see CSpellHandler::afterLoadFinalization()
//b->sid = spell->id; //for all
b->source = Bonus::SPELL_EFFECT;//for all
if (usePowerAsValue)
{
b->val = spell->powers[level_idx];
}
spell->effects[level_idx].push_back(b);
}
}
}
return spell;
}
void CSpellHandler::afterLoadFinalization()
{
//FIXME: this a bad place for this code, should refactor loadFromJson to know object id during load
for (auto spell: objects)
for (auto & level: spell->effects)
for (auto * bonus: level)
bonus->sid = spell->id;
}
CSpellHandler::~CSpellHandler()
{
for(auto & spell : spells)
{
spell.dellNull();
}
}
std::vector<bool> CSpellHandler::getDefaultAllowed() const

View File

@ -1,10 +1,12 @@
#pragma once
#include "IHandlerBase.h"
#include "../lib/ConstTransitivePtr.h"
#include "int3.h"
#include "GameConstants.h"
#include "HeroBonus.h"
/*
* CSpellHandler.h, part of VCMI engine
*
@ -20,11 +22,24 @@ struct BattleHex;
class DLL_LINKAGE CSpell
{
public:
// struct LevelInfo
// {
//
// };
//
// /** \brief Low level accessor. Don`t use it if absolutely necessary
// *
// * \param level. spell school level
// * \return Spell level info structure
// *
// */
// const LevelInfo& getLevelInfo(const int level);
public:
enum ETargetType {NO_TARGET, CREATURE, CREATURE_EXPERT_MASSIVE, OBSTACLE};
enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
SpellID id;
std::string identifier;
std::string identifier; //???
std::string name;
std::string abbName; //abbreviated name
std::vector<std::string> descriptions; //descriptions of spell for skill levels: 0 - none, 1 - basic, etc
@ -34,10 +49,8 @@ public:
bool fire;
bool air;
si32 power; //spell's power
std::vector<si32> costs; //per skill level: 0 - none, 1 - basic, etc
std::vector<si32> powers; //[er skill level: 0 - none, 1 - basic, etc
std::map<TFaction, si32> probabilities; //% chance to gain for castles
std::vector<si32> AIVals; //AI values: per skill level: 0 - none, 1 - basic, etc
bool combatSpell; //is this spell combat (true) or adventure (false)
bool creatureAbility; //if true, only creatures can use this spell
@ -68,6 +81,21 @@ public:
bool isImmuneBy(const IBonusBearer *obj) const;
inline si32 getCost(const int skillLevel) const;
/**
* Returns spell level power, base power ignored
*/
inline si32 getPower(const int skillLevel) const;
// /**
// * Returns spell power, taking base power into account
// */
// inline si32 calculatePower(const int skillLevel) const;
inline si32 getProbability(const TFaction factionId) const;
/**
* Returns resource name of icon for SPELL_IMMUNITY bonus
*/
@ -81,29 +109,42 @@ public:
h & targetType;
h & effects & immunities & limiters;
h & iconImmune;
h & absoluteImmunities & defaultProbability;
}
friend class CSpellHandler;
private:
void setIsOffensive(const bool val);
void setIsRising(const bool val);
void setAttributes(const std::string& newValue);
private:
si32 defaultProbability;
std::vector<si32> costs; //per skill level: 0 - none, 1 - basic, etc
std::vector<si32> powers; //per skill level: 0 - none, 1 - basic, etc
std::vector<si32> AIVals; //AI values: per skill level: 0 - none, 1 - basic, etc
bool isRising;
bool isDamage;
bool isOffensive;
std::string attributes; //reference only attributes
std::string attributes; //reference only attributes //todo: remove or include in configuration format, currently unused
void setAttributes(const std::string& newValue);
ETargetType targetType;
std::vector<std::vector<Bonus *> > effects; // [level 0-3][list of effects]
std::vector<Bonus::BonusType> immunities; //any of these grants immunity
std::vector<Bonus::BonusType> absoluteImmunities; //any of these grants immunity, cant be negated
std::vector<Bonus::BonusType> limiters; //all of them are required to be affected
///graphics related stuff
std::string iconImmune;
};
///CSpell inlines
@ -150,7 +191,7 @@ bool CSpell::isOffensiveSpell() const
bool CSpell::hasEffects() const
{
return effects.size();
return effects.size() && effects[0].size();
}
const std::string& CSpell::getIconImmune() const
@ -158,27 +199,60 @@ const std::string& CSpell::getIconImmune() const
return iconImmune;
}
si32 CSpell::getCost(const int skillLevel) const
{
return costs[skillLevel];
}
si32 CSpell::getPower(const int skillLevel) const
{
return powers[skillLevel];
}
//si32 CSpell::calculatePower(const int skillLevel) const
//{
// return power + getPower(skillLevel);
//}
si32 CSpell::getProbability(const TFaction factionId) const
{
if (! vstd::contains(probabilities,factionId))
{
return defaultProbability;
}
return probabilities.at(factionId);
}
bool DLL_LINKAGE isInScreenRange(const int3 &center, const int3 &pos); //for spells like Dimension Door
class DLL_LINKAGE CSpellHandler
class DLL_LINKAGE CSpellHandler: public CHandlerBase<SpellID, CSpell>
{
CSpell * loadSpell(CLegacyConfigParser & parser, const SpellID id);
//CSpell * loadSpell(CLegacyConfigParser & parser, const SpellID id);
public:
CSpellHandler();
~CSpellHandler();
std::vector< ConstTransitivePtr<CSpell> > spells;
virtual ~CSpellHandler();
///IHandler base
std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
void afterLoadFinalization() override;
/**
* Gets a list of default allowed spells. OH3 spells are all allowed by default.
*
* @return a list of allowed spells, the index is the spell id and the value either 0 for not allowed or 1 for allowed
*/
std::vector<bool> getDefaultAllowed() const;
std::vector<bool> getDefaultAllowed() const override;
const std::string getTypeName() override;
template <typename Handler> void serialize(Handler &h, const int version)
{
h & spells ;
h & objects ;
}
protected:
CSpell * loadFromJson(const JsonNode & json) override;
};

View File

@ -28,7 +28,7 @@
#include "mapping/CCampaignHandler.h" //for CCampaignState
#include "rmg/CMapGenerator.h" // for CMapGenOptions
const ui32 version = 745;
const ui32 version = 746;
const ui32 minSupportedVersion = version;
class CConnection;
@ -120,11 +120,11 @@ struct PointerCaster : IPointerCaster
}
}
virtual boost::any castSharedPtr(const boost::any &ptr) const override
virtual boost::any castSharedPtr(const boost::any &ptr) const override
{
return castSmartPtr<std::shared_ptr<From>>(ptr);
}
virtual boost::any castWeakPtr(const boost::any &ptr) const override
virtual boost::any castWeakPtr(const boost::any &ptr) const override
{
auto from = boost::any_cast<std::weak_ptr<From>>(ptr);
return castSmartPtr<std::shared_ptr<From>>(from.lock());
@ -155,7 +155,7 @@ private:
{
// This type is non-copyable.
// Unfortunately on Windows it is required for DLL_EXPORT-ed type to provide copy c-tor, so we can't =delete it.
assert(0);
assert(0);
}
CTypeList &operator=(CTypeList &)
{
@ -170,7 +170,7 @@ public:
TypeInfoPtr registerType(const std::type_info *type);
template <typename Base, typename Derived>
template <typename Base, typename Derived>
void registerType(const Base * b = nullptr, const Derived * d = nullptr)
{
static_assert(std::is_base_of<Base, Derived>::value, "First registerType template parameter needs to ba a base class of the second one.");
@ -189,12 +189,12 @@ public:
ui16 getTypeID(const std::type_info *type);
TypeInfoPtr getTypeDescriptor(const std::type_info *type, bool throws = true); //if not throws, failure returns nullptr
template <typename T>
template <typename T>
ui16 getTypeID(const T * t = nullptr)
{
return getTypeID(getTypeInfo(t));
}
// Returns sequence of types starting from "from" and ending on "to". Every next type is derived from the previous.
// Throws if there is no link registered.
@ -1301,7 +1301,7 @@ public:
typedef typename boost::remove_const<T>::type NonConstT;
NonConstT *internalPtr;
*this >> internalPtr;
void *internalPtrDerived = typeList.castToMostDerived(internalPtr);
if(internalPtr)
@ -1309,7 +1309,7 @@ public:
auto itr = loadedSharedPointers.find(internalPtrDerived);
if(itr != loadedSharedPointers.end())
{
// This pointers is already loaded. The "data" needs to be pointed to it,
// This pointers is already loaded. The "data" needs to be pointed to it,
// so their shared state is actually shared.
try
{
@ -1329,8 +1329,8 @@ public:
}
catch(std::exception &e)
{
logGlobal->errorStream() << e.what();
logGlobal->errorStream() << boost::format("Failed to cast stored shared ptr. Real type: %s. Needed type %s. FIXME FIXME FIXME")
logGlobal->errorStream() << e.what();
logGlobal->errorStream() << boost::format("Failed to cast stored shared ptr. Real type: %s. Needed type %s. FIXME FIXME FIXME")
% itr->second.type().name() % typeid(std::shared_ptr<T>).name();
//TODO scenario with inheritance -> we can have stored ptr to base and load ptr to derived (or vice versa)
assert(0);
@ -1549,7 +1549,7 @@ class DLL_LINKAGE CLoadIntegrityValidator : public CISer<CLoadIntegrityValidator
public:
unique_ptr<CLoadFile> primaryFile, controlFile;
bool foundDesync;
CLoadIntegrityValidator(const std::string &primaryFileName, const std::string &controlFileName, int minimalVersion = version); //throws!
int read( void * data, unsigned size); //throws!
@ -1661,7 +1661,7 @@ public:
}
}
template<typename Base, typename Derived>
template<typename Base, typename Derived>
void registerType(const Base * b = nullptr, const Derived * d = nullptr)
{
typeList.registerType(b, d);

View File

@ -87,7 +87,7 @@ CCreature * CreatureID::toCreature() const
CSpell * SpellID::toSpell() const
{
return VLC->spellh->spells[*this];
return VLC->spellh->objects[*this];
}
//template std::ostream & operator << <ArtifactInstanceID>(std::ostream & os, BaseForID<ArtifactInstanceID> id);

View File

@ -9,6 +9,9 @@
* Full text of license available in license.txt file, in main folder
*
*/
#include "../lib/ConstTransitivePtr.h"
#include "VCMI_Lib.h"
#include "CModHandler.h"
class JsonNode;
@ -40,4 +43,59 @@ public:
virtual std::vector<bool> getDefaultAllowed() const = 0;
virtual ~IHandlerBase(){}
};
};
template <class _ObjectID, class _Object> class CHandlerBase: public IHandlerBase
{
public:
virtual ~CHandlerBase()
{
for(auto & o : objects)
{
o.dellNull();
}
}
void loadObject(std::string scope, std::string name, const JsonNode & data) override
{
auto type_name = getTypeName();
auto object = loadFromJson(data);
object->id = _ObjectID(objects.size());
objects.push_back(object);
VLC->modh->identifiers.registerObject(scope, type_name, name, object->id);
}
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override
{
auto type_name = getTypeName();
auto object = loadFromJson(data);
object->id = _ObjectID(index);
assert(objects[index] == nullptr); // ensure that this id was not loaded before
objects[index] = object;
VLC->modh->identifiers.registerObject(scope,type_name, name, object->id);
}
ConstTransitivePtr<_Object> operator[] (const _ObjectID id) const
{
const auto raw_id = id.toEnum();
if (raw_id < 0 || raw_id >= objects.size())
{
logGlobal->errorStream() << getTypeName() << " id " << static_cast<si64>(raw_id) << "is invalid";
throw std::runtime_error ("internal error");
}
return objects[raw_id];
}
protected:
virtual _Object * loadFromJson(const JsonNode & json) = 0;
virtual const std::string getTypeName() = 0;
public: //todo: make private
std::vector<ConstTransitivePtr<_Object>> objects;
};

View File

@ -1378,6 +1378,12 @@ void actualizeEffect(CStack * s, const Bonus & ef)
DLL_LINKAGE void SetStackEffect::applyGs( CGameState *gs )
{
if (effect.empty())
{
logGlobal->errorStream() << "Trying to apply SetStackEffect with no effects";
return;
}
int spellid = effect.begin()->sid; //effects' source ID
for(ui32 id : stacks)

View File

@ -17,8 +17,8 @@
<Option run_host_application_in_terminal="1" />
<Option createStaticLib="1" />
<Compiler>
<Add option="-O1" />
<Add option="-O" />
<Add option="-Og" />
<Add option="-g" />
</Compiler>
</Target>
<Target title="Release">
@ -64,7 +64,7 @@
<Add option="-lboost_thread$(#boost.libsuffix)" />
<Add option="-lboost_chrono$(#boost.libsuffix)" />
<Add option="-lboost_locale$(#boost.libsuffix)" />
<Add directory="$(#boost.lib)" />
<Add directory="$(#boost.lib32)" />
<Add directory="$(#sdl.lib)" />
<Add directory="../" />
</Linker>
@ -140,8 +140,6 @@
<Unit filename="NetPacks.h" />
<Unit filename="NetPacksBase.h" />
<Unit filename="NetPacksLib.cpp" />
<Unit filename="RegisterTypes.cpp" />
<Unit filename="RegisterTypes.h" />
<Unit filename="ResourceSet.cpp" />
<Unit filename="ResourceSet.h" />
<Unit filename="ScopeGuard.h" />
@ -198,6 +196,14 @@
<Unit filename="mapping/MapFormatH3M.h" />
<Unit filename="mapping/MapFormatJson.cpp" />
<Unit filename="mapping/MapFormatJson.h" />
<Unit filename="registerTypes/RegisterTypes.cpp" />
<Unit filename="registerTypes/RegisterTypes.h" />
<Unit filename="registerTypes/TypesClientPacks1.cpp" />
<Unit filename="registerTypes/TypesClientPacks2.cpp" />
<Unit filename="registerTypes/TypesMapObjects1.cpp" />
<Unit filename="registerTypes/TypesMapObjects2.cpp" />
<Unit filename="registerTypes/TypesPregamePacks.cpp" />
<Unit filename="registerTypes/TypesServerPacks.cpp" />
<Unit filename="rmg/CMapGenOptions.cpp" />
<Unit filename="rmg/CMapGenOptions.h" />
<Unit filename="rmg/CMapGenerator.cpp" />

View File

@ -37,7 +37,9 @@ extern template DLL_LINKAGE void METHODNAME<CLoadFile>(CLoadFile & s); \
extern template DLL_LINKAGE void METHODNAME<CTypeList>(CTypeList & s); \
extern template DLL_LINKAGE void METHODNAME<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);
DEFINE_EXTERNAL_METHOD(registerTypesMapObjects)
//DEFINE_EXTERNAL_METHOD(registerTypesMapObjects)
DEFINE_EXTERNAL_METHOD(registerTypesMapObjects1)
DEFINE_EXTERNAL_METHOD(registerTypesMapObjects2)
DEFINE_EXTERNAL_METHOD(registerTypesClientPacks1)
DEFINE_EXTERNAL_METHOD(registerTypesClientPacks2)
DEFINE_EXTERNAL_METHOD(registerTypesServerPacks)

View File

@ -20,12 +20,14 @@
*
*/
template<typename Serializer>
void registerTypesMapObjects(Serializer &s)
void registerTypesMapObjects1(Serializer &s)
{
//////////////////////////////////////////////////////////////////////////
// Adventure map objects (and related)
//////////////////////////////////////////////////////////////////////////
// Adventure map objects
//////////////////////////////////////////////////////////////////////////
s.template registerType<IObjectInterface, CGObjectInstance>();
// Non-armed objects
@ -68,8 +70,11 @@ void registerTypesMapObjects(Serializer &s)
s.template registerType<CBank, CGPyramid>();
s.template registerType<CArmedInstance, CGSeerHut>(); s.template registerType<IQuestObject, CGSeerHut>();
s.template registerType<CGSeerHut, CGQuestGuard>();
}
template<typename Serializer>
void registerTypesMapObjects2(Serializer &s)
{
//Other object-related
s.template registerType<IObjectInterface, CGTownBuilding>();
s.template registerType<CGTownBuilding, CTownBonus>();
@ -95,7 +100,7 @@ void registerTypesMapObjects(Serializer &s)
//////////////////////////////////////////////////////////////////////////
// Bonus system
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//s.template registerType<IPropagator>();
s.template registerType<IPropagator, CPropagatorNodeType>();
@ -109,7 +114,7 @@ void registerTypesMapObjects(Serializer &s)
s.template registerType<ILimiter, CreatureAlignmentLimiter>();
s.template registerType<ILimiter, RankRangeLimiter>();
s.template registerType<ILimiter, StackOwnerLimiter>();
// s.template registerType<CBonusSystemNode>();
s.template registerType<CBonusSystemNode, CArtifact>();
s.template registerType<CArtifact, CGrowingArtifact>();
@ -131,7 +136,6 @@ void registerTypesMapObjects(Serializer &s)
s.template registerType<CObstacleInstance, MoatObstacle>();
s.template registerType<CObstacleInstance, SpellCreatedObstacle>();
}
template<typename Serializer>
void registerTypesClientPacks1(Serializer &s)
{
@ -295,7 +299,8 @@ void registerTypesPregamePacks(Serializer &s)
template<typename Serializer>
void registerTypes(Serializer &s)
{
registerTypesMapObjects(s);
registerTypesMapObjects1(s);
registerTypesMapObjects2(s);
registerTypesClientPacks1(s);
registerTypesClientPacks2(s);
registerTypesServerPacks(s);

View File

@ -1,29 +0,0 @@
#include "StdInc.h"
#include "RegisterTypes.h"
#include "../mapping/CMapInfo.h"
#include "../StartInfo.h"
#include "../BattleState.h"
#include "../CGameState.h"
#include "../mapping/CMap.h"
#include "../CModHandler.h"
#include "../CObjectHandler.h"
#include "../CCreatureHandler.h"
#include "../VCMI_Lib.h"
#include "../CArtHandler.h"
#include "../CHeroHandler.h"
#include "../CSpellHandler.h"
#include "../CTownHandler.h"
#include "../mapping/CCampaignHandler.h"
#include "../NetPacks.h"
#include "../CDefObjInfoHandler.h"
template void registerTypesMapObjects<CISer<CConnection>>(CISer<CConnection>& s);
template void registerTypesMapObjects<COSer<CConnection>>(COSer<CConnection>& s);
template void registerTypesMapObjects<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
template void registerTypesMapObjects<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
template void registerTypesMapObjects<CSaveFile>(CSaveFile & s);
template void registerTypesMapObjects<CLoadFile>(CLoadFile & s);
template void registerTypesMapObjects<CTypeList>(CTypeList & s);
template void registerTypesMapObjects<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);

View File

@ -0,0 +1,30 @@
#include "StdInc.h"
#include "RegisterTypes.h"
#include "mapping/CMapInfo.h"
#include "StartInfo.h"
#include "BattleState.h"
#include "CGameState.h"
#include "mapping/CMap.h"
#include "CModHandler.h"
#include "CObjectHandler.h"
#include "CCreatureHandler.h"
#include "VCMI_Lib.h"
#include "CArtHandler.h"
#include "CHeroHandler.h"
#include "CSpellHandler.h"
#include "CTownHandler.h"
#include "mapping/CCampaignHandler.h"
#include "NetPacks.h"
#include "CDefObjInfoHandler.h"
template void registerTypesMapObjects1<CISer<CConnection>>(CISer<CConnection>& s);
template void registerTypesMapObjects1<COSer<CConnection>>(COSer<CConnection>& s);
template void registerTypesMapObjects1<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
template void registerTypesMapObjects1<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
template void registerTypesMapObjects1<CSaveFile>(CSaveFile & s);
template void registerTypesMapObjects1<CLoadFile>(CLoadFile & s);
template void registerTypesMapObjects1<CTypeList>(CTypeList & s);
template void registerTypesMapObjects1<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);

View File

@ -0,0 +1,30 @@
#include "StdInc.h"
#include "RegisterTypes.h"
#include "mapping/CMapInfo.h"
#include "StartInfo.h"
#include "BattleState.h"
#include "CGameState.h"
#include "mapping/CMap.h"
#include "CModHandler.h"
#include "CObjectHandler.h"
#include "CCreatureHandler.h"
#include "VCMI_Lib.h"
#include "CArtHandler.h"
#include "CHeroHandler.h"
#include "CSpellHandler.h"
#include "CTownHandler.h"
#include "mapping/CCampaignHandler.h"
#include "NetPacks.h"
#include "CDefObjInfoHandler.h"
template void registerTypesMapObjects2<CISer<CConnection>>(CISer<CConnection>& s);
template void registerTypesMapObjects2<COSer<CConnection>>(COSer<CConnection>& s);
template void registerTypesMapObjects2<CISer<CMemorySerializer>>(CISer<CMemorySerializer>& s);
template void registerTypesMapObjects2<COSer<CMemorySerializer>>(COSer<CMemorySerializer>& s);
template void registerTypesMapObjects2<CSaveFile>(CSaveFile & s);
template void registerTypesMapObjects2<CLoadFile>(CLoadFile & s);
template void registerTypesMapObjects2<CTypeList>(CTypeList & s);
template void registerTypesMapObjects2<CLoadIntegrityValidator>(CLoadIntegrityValidator & s);

View File

@ -493,11 +493,11 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
}
if(battleQuery != queries.topQuery(gs->curB->sides[0].color))
complain("Player " + boost::lexical_cast<std::string>(gs->curB->sides[0].color) + " although in battle has no battle query at the top!");
battleQuery->result = *battleResult.data;
//Check how many battle queries were created (number of players blocked by battle)
const int queriedPlayers = battleQuery ? boost::count(queries.allQueries(), battleQuery) : 0;
const int queriedPlayers = battleQuery ? boost::count(queries.allQueries(), battleQuery) : 0;
finishingBattle = make_unique<FinishingBattleHelper>(battleQuery, gs->initialOpts->mode == StartInfo::DUEL, queriedPlayers);
@ -651,7 +651,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance *hero1, const CGHer
sendAndApply(&iw);
sendAndApply(&cs);
}
cab1.takeFromArmy(this);
cab2.takeFromArmy(this); //take casualties after battle is deleted
@ -691,7 +691,7 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result )
return;
//TODO consider if we really want it to work like above. ATM each player as unblocked as soon as possible
// but the battle consequences are applied after final player is unblocked. Hard to abuse...
// but the battle consequences are applied after final player is unblocked. Hard to abuse...
// Still, it looks like a hole.
// Necromancy if applicable.
@ -704,8 +704,8 @@ void CGameHandler::battleAfterLevelUp( const BattleResult &result )
{
finishingBattle->winnerHero->showNecromancyDialog(raisedStack);
addToSlot(StackLocation(finishingBattle->winnerHero, necroSlot), raisedStack.type, raisedStack.count);
}
}
BattleResultsApplied resultsApplied;
resultsApplied.player1 = finishingBattle->victor;
resultsApplied.player2 = finishingBattle->loser;
@ -749,7 +749,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
bat.bsa.clear();
bat.stackAttacking = att->ID;
const int attackerLuck = att->LuckVal();
auto sideHeroBlocksLuck = [](const SideInBattle &side){ return NBonus::hasOfType(side.hero, Bonus::BLOCK_LUCK); };
if(!vstd::contains_if(gs->curB->sides, sideHeroBlocksLuck))
@ -802,7 +802,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
if (bonus && (bat.shot())) //TODO: make it work in meele?
{
bat.bsa.front().flags |= BattleStackAttacked::EFFECT;
bat.bsa.front().effect = VLC->spellh->spells.at(bonus->subtype)->mainEffectAnim; //hopefully it does not interfere with any other effect?
bat.bsa.front().effect = VLC->spellh->objects.at(bonus->subtype)->mainEffectAnim; //hopefully it does not interfere with any other effect?
std::set<const CStack*> attackedCreatures = gs->curB->getAffectedCreatures(SpellID(bonus->subtype).toSpell(), bonus->val, att->owner, targetHex);
//TODO: get exact attacked hex for defender
@ -1712,7 +1712,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, Pl
this->getTilesInRange(tmh.fowRevealed, h->getSightCenter()+(tmh.end-tmh.start), h->getSightRadious(), h->tempOwner, 1);
};
auto doMove = [&](TryMoveHero::EResult result, EGuardLook lookForGuards,
auto doMove = [&](TryMoveHero::EResult result, EGuardLook lookForGuards,
EVisitDest visitDest, ELEaveTile leavingTile) -> bool
{
LOG_TRACE_PARAMS(logGlobal, "Hero %s starts movement from %s to %s", h->name % tmh.start % tmh.end);
@ -1732,7 +1732,7 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, Pl
const TerrainTile &guardTile = *gs->getTile(guardPos);
objectVisited(guardTile.visitableObjects.back(), h);
moveQuery->visitDestAfterVictory = visitDest==VISIT_DEST;
}
else if(visitDest == VISIT_DEST)
@ -1794,8 +1794,8 @@ bool CGameHandler::moveHero( ObjectInstanceID hid, int3 dst, ui8 teleporting, Pl
//still here? it is standard movement!
{
tmh.movePoints = h->movement >= cost
? h->movement - cost
tmh.movePoints = h->movement >= cost
? h->movement - cost
: 0;
if(blockingVisit())
@ -1982,8 +1982,8 @@ void CGameHandler::removeArtifact(const ArtifactLocation &al)
ea.al = al;
sendAndApply(&ea);
}
void CGameHandler::startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile,
const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank,
void CGameHandler::startBattlePrimary(const CArmedInstance *army1, const CArmedInstance *army2, int3 tile,
const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool creatureBank,
const CGTownInstance *town) //use hero=nullptr for no hero
{
engageIntoBattle(army1->tempOwner);
@ -3267,7 +3267,7 @@ static EndAction end_action;
bool CGameHandler::makeBattleAction( BattleAction &ba )
{
bool ok = true;
const CStack *stack = battleGetStackByID(ba.stackNumber); //may be nullptr if action is not about stack
const CStack *destinationStack = ba.actionType == Battle::WALK_AND_ATTACK ? gs->curB->battleGetStackByPos(ba.additionalInfo)
: ba.actionType == Battle::SHOOT ? gs->curB->battleGetStackByPos(ba.destinationTile)
@ -3276,7 +3276,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
logGlobal->traceStream() << boost::format(
"Making action: type=%d; side=%d; stack=%s; dst=%s; additionalInfo=%d; stackAtDst=%s")
% ba.actionType % (int)ba.side % (stack ? stack->getName() : std::string("none"))
% ba.actionType % (int)ba.side % (stack ? stack->getName() : std::string("none"))
% ba.destinationTile % ba.additionalInfo % (destinationStack ? destinationStack->getName() : std::string("none"));
switch(ba.actionType)
@ -3446,7 +3446,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
{
BattleAttack bat;
prepareAttack(bat, stack, destinationStack, (i ? 0 : distance), ba.additionalInfo); //no distance travelled on second attack
//prepareAttack(bat, stack, stackAtEnd, 0, ba.additionalInfo);
//prepareAttack(bat, stack, stackAtEnd, 0, ba.additionalInfo);
handleAttackBeforeCasting(bat); //only before first attack
sendAndApply(&bat);
handleAfterAttackCasting(bat);
@ -3819,7 +3819,7 @@ void CGameHandler::playerMessage( PlayerColor player, const std::string &message
//give all spells
cs.learn = 1;
for(auto spell : VLC->spellh->spells)
for(auto spell : VLC->spellh->objects)
{
if(!spell->creatureAbility)
cs.spells.insert(spell->id);
@ -4060,7 +4060,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
}
}
}
for (auto cre : attackedCres)
{
sc.affectedCres.insert (cre->ID);
@ -4354,7 +4354,7 @@ void CGameHandler::handleSpellCasting( SpellID spellID, int spellLvl, BattleHex
int percentBonus = caster ? caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, spellID.toEnum()) : 0;
bsa.amount = usedSpellPower
* SpellID(spellID).toSpell()->powers.at(spellLvl)
* SpellID(spellID).toSpell()->getPower(spellLvl)
* (100 + percentBonus) / 100.0; //new feature - percentage bonus
if(bsa.amount)
sendAndApply(&bsa);
@ -4488,7 +4488,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
case Battle::HERO_SPELL:
{
COMPLAIN_RET_FALSE_IF(ba.side > 1, "Side must be 0 or 1!");
const CGHeroInstance *h = gs->curB->battleGetFightingHero(ba.side);
const CGHeroInstance *secondHero = gs->curB->battleGetFightingHero(!ba.side);
@ -4497,7 +4497,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
logGlobal->warnStream() << "Wrong caster!";
return false;
}
if(ba.additionalInfo >= VLC->spellh->spells.size())
if(ba.additionalInfo >= VLC->spellh->objects.size())
{
logGlobal->warnStream() << "Wrong spell id (" << ba.additionalInfo << ")!";
return false;
@ -4920,7 +4920,7 @@ void CGameHandler::showGarrisonDialog( ObjectInstanceID upobj, ObjectInstanceID
//PlayerColor player = getOwner(hid);
auto upperArmy = dynamic_cast<const CArmedInstance*>(getObj(upobj));
auto lowerArmy = dynamic_cast<const CArmedInstance*>(getObj(hid));
assert(lowerArmy);
assert(upperArmy);
@ -5007,7 +5007,7 @@ void CGameHandler::objectVisited( const CGObjectInstance * obj, const CGHeroInst
obj->onHeroVisit(h);
queries.popIfTop(visitQuery); //visit ends here if no queries were created
queries.popIfTop(visitQuery); //visit ends here if no queries were created
}
void CGameHandler::objectVisitEnded(const CObjectVisitQuery &query)
@ -5223,7 +5223,7 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
checkVictoryLossConditions(playerColors);
}
auto playerInfo = gs->getPlayer(gs->currentPlayer, false);
auto playerInfo = gs->getPlayer(gs->currentPlayer, false);
// If we are called before the actual game start, there might be no current player
if(playerInfo && playerInfo->status != EPlayerStatus::INGAME)
{
@ -5433,7 +5433,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, SpellID spellID, const int
case SpellID::SUMMON_BOAT:
{
//check if spell works at all
if(rand() % 100 >= s->powers.at(schoolLevel)) //power is % chance of success
if(rand() % 100 >= s->getPower(schoolLevel)) //power is % chance of success
{
InfoWindow iw;
iw.player = h->tempOwner;
@ -5495,7 +5495,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, SpellID spellID, const int
case SpellID::SCUTTLE_BOAT:
{
//check if spell works at all
if(rand() % 100 >= s->powers.at(schoolLevel)) //power is % chance of success
if(rand() % 100 >= s->getPower(schoolLevel)) //power is % chance of success
{
InfoWindow iw;
iw.player = h->tempOwner;
@ -5526,7 +5526,7 @@ bool CGameHandler::castSpell(const CGHeroInstance *h, SpellID spellID, const int
COMPLAIN_RET("Destination tile doesn't exist!");
if(!h->movement)
COMPLAIN_RET("Hero needs movement points to cast Dimension Door!");
if(h->getBonusesCount(Bonus::SPELL_EFFECT, SpellID::DIMENSION_DOOR) >= s->powers.at(schoolLevel)) //limit casts per turn
if(h->getBonusesCount(Bonus::SPELL_EFFECT, SpellID::DIMENSION_DOOR) >= s->getPower(schoolLevel)) //limit casts per turn
{
InfoWindow iw;
iw.player = h->tempOwner;
@ -5890,7 +5890,7 @@ void CGameHandler::runBattle()
std::set <const CStack*> stacksToRemove;
for (auto stack : curB.stacks)
{
if (stack->idDeadClone())
if (stack->idDeadClone())
stacksToRemove.insert(stack);
}
for (auto stack : stacksToRemove)
@ -6276,7 +6276,7 @@ void CGameHandler::duelFinished()
auto getName = [&](int i){ return si->getIthPlayersSettings(gs->curB->sides.at(i).color).name; };
int casualtiesPoints = 0;
logGlobal->debugStream() << boost::format("Winner side %d\nWinner casualties:")
logGlobal->debugStream() << boost::format("Winner side %d\nWinner casualties:")
% (int)battleResult.data->winner;
for(auto & elem : battleResult.data->casualties[battleResult.data->winner])
@ -6295,7 +6295,7 @@ void CGameHandler::duelFinished()
if(out)
{
out << boost::format("%s\t%s\t%s\t%d\t%d\t%d\t%s\n") % si->mapname % getName(0) % getName(1)
% battleResult.data->winner % battleResult.data->result % casualtiesPoints
% battleResult.data->winner % battleResult.data->result % casualtiesPoints
% asctime(localtime(&timeNow));
}
else

View File

@ -57,7 +57,7 @@
<Add option="-lboost_thread$(#boost.libsuffix)" />
<Add option="-lboost_chrono$(#boost.libsuffix)" />
<Add option="-lVCMI_lib" />
<Add directory="$(#boost.lib)" />
<Add directory="$(#boost.lib32)" />
<Add directory="../" />
</Linker>
<Unit filename="CGameHandler.cpp" />