mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-28 08:48:48 +02:00
Attempt to compile refactored spells in MSVS.
This commit is contained in:
commit
f32849a73e
@ -4,7 +4,7 @@
|
||||
#include "../../lib/BattleState.h"
|
||||
#include "../../CCallback.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/CSpellHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/VCMI_Lib.h"
|
||||
|
||||
using boost::optional;
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "../../lib/CBuildingHandler.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/CSpellHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/Connection.h"
|
||||
#include "../../lib/CGameState.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
|
@ -530,6 +530,13 @@ void VCAI::showMarketWindow(const IMarket *market, const CGHeroInstance *visitor
|
||||
NET_EVENT_HANDLER;
|
||||
}
|
||||
|
||||
void VCAI::showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions)
|
||||
{
|
||||
//TODO: AI support for ViewXXX spell
|
||||
LOG_TRACE(logAi);
|
||||
NET_EVENT_HANDLER;
|
||||
}
|
||||
|
||||
void VCAI::init(shared_ptr<CCallback> CB)
|
||||
{
|
||||
LOG_TRACE(logAi);
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../../lib/CBuildingHandler.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/CSpellHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/Connection.h"
|
||||
#include "../../lib/CGameState.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
@ -241,6 +241,7 @@ public:
|
||||
virtual void buildChanged(const CGTownInstance *town, BuildingID buildingID, int what) override;
|
||||
virtual void heroBonusChanged(const CGHeroInstance *hero, const Bonus &bonus, bool gain) override;
|
||||
virtual void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor) override;
|
||||
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions) override;
|
||||
|
||||
virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override;
|
||||
virtual void battleEnd(const BattleResult *br) override;
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "lib/Connection.h"
|
||||
#include "lib/NetPacks.h"
|
||||
#include "client/mapHandler.h"
|
||||
#include "lib/CSpellHandler.h"
|
||||
#include "lib/spells/CSpellHandler.h"
|
||||
#include "lib/CArtHandler.h"
|
||||
#include "lib/GameConstants.h"
|
||||
#ifdef min
|
||||
|
@ -19,7 +19,7 @@
|
||||
#include "CVideoHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CCreatureHandler.h"
|
||||
#include "../lib/CSpellHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "CMusicHandler.h"
|
||||
#include "CVideoHandler.h"
|
||||
#include "CDefHandler.h"
|
||||
|
@ -4,7 +4,7 @@
|
||||
#include "CMusicHandler.h"
|
||||
#include "CGameInfo.h"
|
||||
#include "../lib/CCreatureHandler.h"
|
||||
#include "../lib/CSpellHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/JsonNode.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
#include "../lib/filesystem/Filesystem.h"
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/Connection.h"
|
||||
#include "../lib/CSpellHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/mapObjects/CObjectClassesHandler.h" // For displaying correct UI when interacting with objects
|
||||
#include "../lib/BattleState.h"
|
||||
@ -2189,6 +2189,13 @@ void CPlayerInterface::advmapSpellCast(const CGHeroInstance * caster, int spellI
|
||||
}
|
||||
const CSpell * spell = CGI->spellh->objects[spellID];
|
||||
|
||||
if(spellID == SpellID::VIEW_EARTH)
|
||||
{
|
||||
//TODO: implement on server side
|
||||
int level = caster->getSpellSchoolLevel(spell);
|
||||
adventureInt->worldViewOptions.showAllTerrain = (level>2);
|
||||
}
|
||||
|
||||
auto castSoundPath = spell->getCastSound();
|
||||
if (!castSoundPath.empty())
|
||||
CCS->soundh->playSound(castSoundPath);
|
||||
@ -2703,3 +2710,13 @@ void CPlayerInterface::doMoveHero(const CGHeroInstance* h, CGPath path)
|
||||
|
||||
duringMovement = false;
|
||||
}
|
||||
|
||||
void CPlayerInterface::showWorldViewEx(const std::vector<ObjectPosInfo>& objectPositions)
|
||||
{
|
||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||
//TODO: showWorldViewEx
|
||||
|
||||
std::copy(objectPositions.begin(), objectPositions.end(), std::back_inserter(adventureInt->worldViewOptions.iconPositions));
|
||||
|
||||
viewWorldMap();
|
||||
}
|
||||
|
@ -196,6 +196,7 @@ public:
|
||||
void showComp(const Component &comp, std::string message) override; //display component in the advmapint infobox
|
||||
void saveGame(COSer & h, const int version) override; //saving
|
||||
void loadGame(CISer & h, const int version) override; //loading
|
||||
void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions) override;
|
||||
|
||||
//for battles
|
||||
void actionFinished(const BattleAction& action) override;//occurs AFTER action taken by active stack or by the hero
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include "CPlayerInterface.h"
|
||||
#include "../CCallback.h"
|
||||
#include "CMessage.h"
|
||||
#include "../lib/CSpellHandler.h" /*for campaign bonuses*/
|
||||
#include "../lib/spells/CSpellHandler.h" /*for campaign bonuses*/
|
||||
#include "../lib/CArtHandler.h" /*for campaign bonuses*/
|
||||
#include "../lib/CBuildingHandler.h" /*for campaign bonuses*/
|
||||
#include "CBitmapHandler.h"
|
||||
|
@ -18,7 +18,7 @@
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/CBuildingHandler.h"
|
||||
#include "../lib/CSpellHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/Connection.h"
|
||||
#ifndef VCMI_ANDROID
|
||||
#include "../lib/Interprocess.h"
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CCreatureHandler.h"
|
||||
#include "CBitmapHandler.h"
|
||||
#include "../lib/CSpellHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CGameState.h"
|
||||
#include "../lib/JsonNode.h"
|
||||
#include "../lib/vcmi_endian.h"
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/VCMIDirs.h"
|
||||
#include "../lib/CSpellHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CSoundBase.h"
|
||||
#include "../lib/StartInfo.h"
|
||||
#include "mapHandler.h"
|
||||
@ -834,6 +834,11 @@ void AdvmapSpellCast::applyCl(CClient *cl)
|
||||
INTERFACE_CALL_IF_PRESENT(caster->getOwner(),advmapSpellCast, caster, spellID);
|
||||
}
|
||||
|
||||
void ShowWorldViewEx::applyCl(CClient * cl)
|
||||
{
|
||||
CALL_ONLY_THAT_INTERFACE(player, showWorldViewEx, objectPositions);
|
||||
}
|
||||
|
||||
void OpenWindow::applyCl(CClient *cl)
|
||||
{
|
||||
switch(window)
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "../../lib/BattleState.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
#include "../../lib/CSpellHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
|
||||
/*
|
||||
* CBattleAnimations.cpp, part of VCMI engine
|
||||
|
@ -28,7 +28,7 @@
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/CondSh.h"
|
||||
#include "../../lib/CRandomGenerator.h"
|
||||
#include "../../lib/CSpellHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/CGameState.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
@ -1228,13 +1228,18 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
||||
{
|
||||
const SpellID spellID(sc->id);
|
||||
const CSpell &spell = * spellID.toSpell();
|
||||
const std::string & spellName = spell.name;
|
||||
|
||||
const std::string& castSoundPath = spell.getCastSound();
|
||||
|
||||
|
||||
std::string casterName("Something");
|
||||
|
||||
if(sc->castedByHero)
|
||||
casterName = curInt->cb->battleGetHeroInfo(sc->side).name;
|
||||
|
||||
if(!castSoundPath.empty())
|
||||
CCS->soundh->playSound(castSoundPath);
|
||||
|
||||
std::string casterCreatureName = "";
|
||||
Point srccoord = (sc->side ? Point(770, 60) : Point(30, 60)) + pos; //hero position by default
|
||||
{
|
||||
const auto casterStackID = sc->casterStack;
|
||||
@ -1244,8 +1249,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
||||
const CStack * casterStack = curInt->cb->battleGetStackByID(casterStackID);
|
||||
if(casterStack != nullptr)
|
||||
{
|
||||
casterCreatureName = casterStack->type->namePl;
|
||||
|
||||
casterName = casterStack->type->namePl;
|
||||
srccoord = CClickableHex::getXYUnitAnim(casterStack->position, casterStack, this);
|
||||
srccoord.x += 250;
|
||||
srccoord.y += 240;
|
||||
@ -1255,7 +1259,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
||||
|
||||
//TODO: play custom cast animation
|
||||
{
|
||||
|
||||
|
||||
}
|
||||
|
||||
//playing projectile animation
|
||||
@ -1269,9 +1273,9 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
||||
bool Vflip = (angle < 0);
|
||||
if(Vflip)
|
||||
angle = -angle;
|
||||
|
||||
|
||||
std::string animToDisplay = spell.animationInfo.selectProjectile(angle);
|
||||
|
||||
|
||||
if(!animToDisplay.empty())
|
||||
{
|
||||
//displaying animation
|
||||
@ -1285,7 +1289,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
||||
int dy = (destcoord.y - srccoord.y - animDef->ourImages[0].bitmap->h)/steps;
|
||||
|
||||
delete animDef;
|
||||
addNewAnim(new CSpellEffectAnimation(this, animToDisplay, srccoord.x, srccoord.y, dx, dy, Vflip));
|
||||
addNewAnim(new CSpellEffectAnimation(this, animToDisplay, srccoord.x, srccoord.y, dx, dy, Vflip));
|
||||
}
|
||||
}
|
||||
waitForAnims();
|
||||
@ -1296,11 +1300,11 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
||||
for (auto & elem : sc->affectedCres)
|
||||
{
|
||||
BattleHex position = curInt->cb->battleGetStackByID(elem, false)->position;
|
||||
|
||||
|
||||
if(vstd::contains(sc->resisted,elem))
|
||||
displayEffect(78, position);
|
||||
else
|
||||
displaySpellEffect(spellID, position);
|
||||
displaySpellEffect(spellID, position);
|
||||
}
|
||||
|
||||
switch(sc->id)
|
||||
@ -1319,15 +1323,26 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
||||
bool customSpell = false;
|
||||
if(sc->affectedCres.size() == 1)
|
||||
{
|
||||
const CStack * attackedStack = curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false);
|
||||
|
||||
const std::string attackedName = attackedStack->getName();
|
||||
const std::string attackedNameSing = attackedStack->getCreature()->nameSing;
|
||||
const std::string attackedNamePl = attackedStack->getCreature()->namePl;
|
||||
|
||||
std::string text = CGI->generaltexth->allTexts[195];
|
||||
if(sc->castedByHero)
|
||||
{
|
||||
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetHeroInfo(sc->side).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
|
||||
boost::algorithm::replace_first(text, "%s", casterName);
|
||||
boost::algorithm::replace_first(text, "%s", spellName);
|
||||
boost::algorithm::replace_first(text, "%s", attackedNamePl); //target
|
||||
}
|
||||
else
|
||||
{
|
||||
auto getPluralText = [attackedStack](const int baseTextID) -> std::string
|
||||
{
|
||||
return CGI->generaltexth->allTexts[(attackedStack->count > 1 ? baseTextID+1 : baseTextID)];
|
||||
};
|
||||
|
||||
bool plural = false; //add singular / plural form of creature text if this is true
|
||||
int textID = 0;
|
||||
switch(sc->id)
|
||||
@ -1345,8 +1360,8 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
||||
case SpellID::BIND:
|
||||
customSpell = true;
|
||||
text = CGI->generaltexth->allTexts[560];
|
||||
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->getCreature()->namePl );
|
||||
break; //Roots and vines bind the %s to the ground!
|
||||
boost::algorithm::replace_first(text, "%s", attackedNamePl);
|
||||
break;//Roots and vines bind the %s to the ground!
|
||||
case SpellID::DISEASE:
|
||||
customSpell = true;
|
||||
plural = true;
|
||||
@ -1360,25 +1375,17 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
||||
case SpellID::AGE:
|
||||
{
|
||||
customSpell = true;
|
||||
if (curInt->cb->battleGetStackByID(*sc->affectedCres.begin())->count > 1)
|
||||
{
|
||||
text = CGI->generaltexth->allTexts[552];
|
||||
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin())->type->namePl);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = CGI->generaltexth->allTexts[551];
|
||||
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin())->type->nameSing);
|
||||
}
|
||||
text = getPluralText(551);
|
||||
boost::algorithm::replace_first(text, "%s", attackedName);
|
||||
//The %s shrivel with age, and lose %d hit points."
|
||||
TBonusListPtr bl = curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->getBonuses(Selector::type(Bonus::STACK_HEALTH));
|
||||
TBonusListPtr bl = attackedStack->getBonuses(Selector::type(Bonus::STACK_HEALTH));
|
||||
bl->remove_if(Selector::source(Bonus::SPELL_EFFECT, SpellID::AGE));
|
||||
boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(bl->totalValue()/2));
|
||||
}
|
||||
break;
|
||||
case SpellID::THUNDERBOLT:
|
||||
text = CGI->generaltexth->allTexts[367];
|
||||
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin())->type->namePl);
|
||||
boost::algorithm::replace_first(text, "%s", attackedNamePl);
|
||||
console->addText(text);
|
||||
text = CGI->generaltexth->allTexts[343].substr(1, CGI->generaltexth->allTexts[343].size() - 1); //Does %d points of damage.
|
||||
boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(sc->dmgToDisplay)); //no more text afterwards
|
||||
@ -1388,7 +1395,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
||||
break;
|
||||
case SpellID::DISPEL_HELPFUL_SPELLS:
|
||||
text = CGI->generaltexth->allTexts[555];
|
||||
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin())->type->namePl);
|
||||
boost::algorithm::replace_first(text, "%s", attackedNamePl);
|
||||
customSpell = true;
|
||||
break;
|
||||
case SpellID::DEATH_STARE:
|
||||
@ -1399,67 +1406,45 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
||||
{
|
||||
text = CGI->generaltexth->allTexts[119]; //%d %s die under the terrible gaze of the %s.
|
||||
boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(sc->dmgToDisplay));
|
||||
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false)->getCreature()->namePl );
|
||||
boost::algorithm::replace_first(text, "%s", attackedNamePl);
|
||||
}
|
||||
else
|
||||
{
|
||||
text = CGI->generaltexth->allTexts[118]; //One %s dies under the terrible gaze of the %s.
|
||||
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin())->type->nameSing);
|
||||
boost::algorithm::replace_first(text, "%s", attackedNameSing);
|
||||
}
|
||||
boost::algorithm::replace_first(text, "%s", casterCreatureName); //casting stack
|
||||
boost::algorithm::replace_first(text, "%s", casterName); //casting stack
|
||||
}
|
||||
else
|
||||
text = "";
|
||||
break;
|
||||
default:
|
||||
text = CGI->generaltexth->allTexts[565]; //The %s casts %s
|
||||
if(casterCreatureName != "")
|
||||
boost::algorithm::replace_first(text, "%s", casterCreatureName); //casting stack
|
||||
else
|
||||
boost::algorithm::replace_first(text, "%s", "@Unknown caster@"); //should not happen
|
||||
boost::algorithm::replace_first(text, "%s", casterName); //casting stack
|
||||
|
||||
}
|
||||
if (plural)
|
||||
{
|
||||
if (curInt->cb->battleGetStackByID(*sc->affectedCres.begin())->count > 1)
|
||||
{
|
||||
text = CGI->generaltexth->allTexts[textID + 1];
|
||||
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin())->getName());
|
||||
}
|
||||
else
|
||||
{
|
||||
text = CGI->generaltexth->allTexts[textID];
|
||||
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetStackByID(*sc->affectedCres.begin())->getName());
|
||||
}
|
||||
text = getPluralText(textID);
|
||||
boost::algorithm::replace_first(text, "%s", attackedName);
|
||||
}
|
||||
}
|
||||
if (!customSpell && !sc->dmgToDisplay)
|
||||
boost::algorithm::replace_first(text, "%s", CGI->spellh->objects[sc->id]->name); //simple spell name
|
||||
boost::algorithm::replace_first(text, "%s", spellName); //simple spell name
|
||||
if (text.size())
|
||||
console->addText(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string text = CGI->generaltexth->allTexts[196];
|
||||
if(sc->castedByHero)
|
||||
{
|
||||
boost::algorithm::replace_first(text, "%s", curInt->cb->battleGetHeroInfo(sc->side).name);
|
||||
}
|
||||
if(casterCreatureName != "")
|
||||
{
|
||||
boost::algorithm::replace_first(text, "%s", casterCreatureName); //creature caster
|
||||
}
|
||||
else
|
||||
{
|
||||
//TODO artifacts that cast spell; scripts some day
|
||||
boost::algorithm::replace_first(text, "%s", "Something");
|
||||
}
|
||||
boost::algorithm::replace_first(text, "%s", CGI->spellh->objects[sc->id]->name);
|
||||
boost::algorithm::replace_first(text, "%s", casterName);
|
||||
boost::algorithm::replace_first(text, "%s", spellName);
|
||||
console->addText(text);
|
||||
}
|
||||
if(sc->dmgToDisplay && !customSpell)
|
||||
{
|
||||
std::string dmgInfo = CGI->generaltexth->allTexts[376];
|
||||
boost::algorithm::replace_first(dmgInfo, "%s", CGI->spellh->objects[sc->id]->name); //simple spell name
|
||||
boost::algorithm::replace_first(dmgInfo, "%s", spellName); //simple spell name
|
||||
boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(sc->dmgToDisplay));
|
||||
console->addText(dmgInfo); //todo: casualties (?)
|
||||
}
|
||||
@ -1502,7 +1487,7 @@ void CBattleInterface::battleStacksEffectsSet(const SetStackEffect & sse)
|
||||
}
|
||||
}
|
||||
|
||||
void CBattleInterface::castThisSpell(int spellID)
|
||||
void CBattleInterface::castThisSpell(SpellID spellID)
|
||||
{
|
||||
auto ba = new BattleAction;
|
||||
ba->actionType = Battle::HERO_SPELL;
|
||||
|
@ -317,7 +317,7 @@ public:
|
||||
void displayBattleFinished(); //displays battle result
|
||||
void spellCast(const BattleSpellCast * sc); //called when a hero casts a spell
|
||||
void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
|
||||
void castThisSpell(int spellID); //called when player has chosen a spell from spellbook
|
||||
void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook
|
||||
void displayEffect(ui32 effect, int destTile, bool areaEffect = true); //displays custom effect on the battlefield
|
||||
void displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s affected animation
|
||||
void displaySpellHit(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s affected animation
|
||||
|
@ -528,6 +528,40 @@ CMapHandler::CMapNormalBlitter::CMapNormalBlitter(CMapHandler * parent)
|
||||
defaultTileRect = Rect(0, 0, tileSize, tileSize);
|
||||
}
|
||||
|
||||
SDL_Surface * CMapHandler::CMapWorldViewBlitter::objectToIcon(Obj id, si32 subId, PlayerColor owner) const
|
||||
{
|
||||
int ownerIndex = 0;
|
||||
if(owner < PlayerColor::PLAYER_LIMIT)
|
||||
{
|
||||
ownerIndex = owner.getNum() * 19;
|
||||
}
|
||||
else if (owner == PlayerColor::NEUTRAL)
|
||||
{
|
||||
ownerIndex = PlayerColor::PLAYER_LIMIT.getNum() * 19;
|
||||
}
|
||||
|
||||
switch(id)
|
||||
{
|
||||
case Obj::MONOLITH_ONE_WAY_ENTRANCE:
|
||||
case Obj::MONOLITH_ONE_WAY_EXIT:
|
||||
case Obj::MONOLITH_TWO_WAY:
|
||||
return info->iconsDef->ourImages[(int)EWorldViewIcon::TELEPORT].bitmap;
|
||||
case Obj::SUBTERRANEAN_GATE:
|
||||
return info->iconsDef->ourImages[(int)EWorldViewIcon::GATE].bitmap;
|
||||
case Obj::ARTIFACT:
|
||||
return info->iconsDef->ourImages[(int)EWorldViewIcon::ARTIFACT].bitmap;
|
||||
case Obj::TOWN:
|
||||
return info->iconsDef->ourImages[(int)EWorldViewIcon::TOWN + ownerIndex].bitmap;
|
||||
case Obj::HERO:
|
||||
return info->iconsDef->ourImages[(int)EWorldViewIcon::HERO + ownerIndex].bitmap;
|
||||
case Obj::MINE:
|
||||
return info->iconsDef->ourImages[(int)EWorldViewIcon::MINE_WOOD + subId + ownerIndex].bitmap;
|
||||
case Obj::RESOURCE:
|
||||
return info->iconsDef->ourImages[(int)EWorldViewIcon::RES_WOOD + subId + ownerIndex].bitmap;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void CMapHandler::CMapWorldViewBlitter::calculateWorldViewCameraPos()
|
||||
{
|
||||
bool outsideLeft = topTile.x < 0;
|
||||
@ -609,65 +643,61 @@ void CMapHandler::CMapWorldViewBlitter::drawElement(EMapCacheType cacheType, SDL
|
||||
|
||||
void CMapHandler::CMapWorldViewBlitter::drawTileOverlay(SDL_Surface * targetSurf, const TerrainTile2 & tile) const
|
||||
{
|
||||
auto & objects = tile.objects;
|
||||
for(auto & object : objects)
|
||||
auto drawIcon = [this,targetSurf](Obj id, si32 subId, PlayerColor owner)
|
||||
{
|
||||
const CGObjectInstance * obj = object.obj;
|
||||
SDL_Surface * wvIcon = this->objectToIcon(id, subId, owner);
|
||||
|
||||
if (obj->pos.z != pos.z)
|
||||
continue;
|
||||
if (!(*info->visibilityMap)[pos.x][pos.y][pos.z])
|
||||
continue; // TODO needs to skip this check if we have view-air-like spell cast
|
||||
if (!obj->visitableAt(pos.x, pos.y))
|
||||
continue;
|
||||
|
||||
auto &ownerRaw = obj->tempOwner;
|
||||
int ownerIndex = 0;
|
||||
if (ownerRaw < PlayerColor::PLAYER_LIMIT)
|
||||
{
|
||||
ownerIndex = ownerRaw.getNum() * 19;
|
||||
}
|
||||
else if (ownerRaw == PlayerColor::NEUTRAL)
|
||||
{
|
||||
ownerIndex = PlayerColor::PLAYER_LIMIT.getNum() * 19;
|
||||
}
|
||||
|
||||
SDL_Surface * wvIcon = nullptr;
|
||||
switch (obj->ID)
|
||||
{
|
||||
default:
|
||||
continue;
|
||||
case Obj::MONOLITH_ONE_WAY_ENTRANCE:
|
||||
case Obj::MONOLITH_ONE_WAY_EXIT:
|
||||
case Obj::MONOLITH_TWO_WAY:
|
||||
wvIcon = info->iconsDef->ourImages[(int)EWorldViewIcon::TELEPORT].bitmap;
|
||||
break;
|
||||
case Obj::SUBTERRANEAN_GATE:
|
||||
wvIcon = info->iconsDef->ourImages[(int)EWorldViewIcon::GATE].bitmap;
|
||||
break;
|
||||
case Obj::ARTIFACT:
|
||||
wvIcon = info->iconsDef->ourImages[(int)EWorldViewIcon::ARTIFACT].bitmap;
|
||||
break;
|
||||
case Obj::TOWN:
|
||||
wvIcon = info->iconsDef->ourImages[(int)EWorldViewIcon::TOWN + ownerIndex].bitmap;
|
||||
break;
|
||||
case Obj::HERO:
|
||||
wvIcon = info->iconsDef->ourImages[(int)EWorldViewIcon::HERO + ownerIndex].bitmap;
|
||||
break;
|
||||
case Obj::MINE:
|
||||
wvIcon = info->iconsDef->ourImages[(int)EWorldViewIcon::MINE_WOOD + obj->subID + ownerIndex].bitmap;
|
||||
break;
|
||||
case Obj::RESOURCE:
|
||||
wvIcon = info->iconsDef->ourImages[(int)EWorldViewIcon::RES_WOOD + obj->subID + ownerIndex].bitmap;
|
||||
break;
|
||||
}
|
||||
|
||||
if (wvIcon)
|
||||
if (nullptr != wvIcon)
|
||||
{
|
||||
// centering icon on the object
|
||||
Rect destRect(realPos.x + tileSize / 2 - wvIcon->w / 2, realPos.y + tileSize / 2 - wvIcon->h / 2, wvIcon->w, wvIcon->h);
|
||||
CSDL_Ext::blitSurface(wvIcon, nullptr, targetSurf, &destRect);
|
||||
}
|
||||
};
|
||||
|
||||
auto & objects = tile.objects;
|
||||
for(auto & object : objects)
|
||||
{
|
||||
const CGObjectInstance * obj = object.obj;
|
||||
|
||||
const bool sameLevel = obj->pos.z == pos.z;
|
||||
const bool isVisible = (*info->visibilityMap)[pos.x][pos.y][pos.z];
|
||||
const bool isVisitable = obj->visitableAt(pos.x, pos.y);
|
||||
|
||||
if(sameLevel && isVisible && isVisitable)
|
||||
drawIcon(obj->ID, obj->subID, obj->tempOwner);
|
||||
}
|
||||
}
|
||||
|
||||
void CMapHandler::CMapWorldViewBlitter::drawOverlayEx(SDL_Surface * targetSurf)
|
||||
{
|
||||
if(nullptr == info->additionalIcons)
|
||||
return;
|
||||
|
||||
const int3 bottomRight = pos + tileCount;
|
||||
|
||||
for(const ObjectPosInfo & iconInfo : *(info->additionalIcons))
|
||||
{
|
||||
if(!iconInfo.pos.z == pos.z)
|
||||
continue;
|
||||
|
||||
if((iconInfo.pos.x < topTile.x) || (iconInfo.pos.y < topTile.y))
|
||||
continue;
|
||||
|
||||
if((iconInfo.pos.x > bottomRight.x) || (iconInfo.pos.y > bottomRight.y))
|
||||
continue;
|
||||
|
||||
realPos.x = initPos.x + (iconInfo.pos.x - topTile.x) * tileSize;
|
||||
realPos.y = initPos.x + (iconInfo.pos.y - topTile.y) * tileSize;
|
||||
|
||||
SDL_Surface * wvIcon = this->objectToIcon(iconInfo.id, iconInfo.subId, iconInfo.owner);
|
||||
|
||||
if (nullptr != wvIcon)
|
||||
{
|
||||
// centering icon on the object
|
||||
Rect destRect(realPos.x + tileSize / 2 - wvIcon->w / 2, realPos.y + tileSize / 2 - wvIcon->h / 2, wvIcon->w, wvIcon->h);
|
||||
CSDL_Ext::blitSurface(wvIcon, nullptr, targetSurf, &destRect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -785,6 +815,11 @@ void CMapHandler::CMapBlitter::drawFrame(SDL_Surface * targetSurf) const
|
||||
drawElement(EMapCacheType::FRAME, parent->ttiles[pos.x][pos.y][topTile.z].terbitmap, nullptr, targetSurf, &destRect);
|
||||
}
|
||||
|
||||
void CMapHandler::CMapBlitter::drawOverlayEx(SDL_Surface * targetSurf)
|
||||
{
|
||||
//nothing to do here
|
||||
}
|
||||
|
||||
void CMapHandler::CMapBlitter::drawHeroFlag(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, SDL_Rect * destRect, bool moving) const
|
||||
{
|
||||
drawElement(EMapCacheType::HERO_FLAGS, sourceSurf, sourceRect, targetSurf, destRect, false);
|
||||
@ -904,9 +939,8 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
|
||||
{
|
||||
if (pos.y < 0 || pos.y >= parent->sizes.y)
|
||||
continue;
|
||||
|
||||
if (!canDrawCurrentTile())
|
||||
continue;
|
||||
|
||||
const bool isVisible = canDrawCurrentTile();
|
||||
|
||||
realTileRect.x = realPos.x;
|
||||
realTileRect.y = realPos.y;
|
||||
@ -914,13 +948,17 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
|
||||
const TerrainTile2 & tile = parent->ttiles[pos.x][pos.y][pos.z];
|
||||
const TerrainTile & tinfo = parent->map->getTile(pos);
|
||||
const TerrainTile * tinfoUpper = pos.y > 0 ? &parent->map->getTile(int3(pos.x, pos.y - 1, pos.z)) : nullptr;
|
||||
|
||||
if(isVisible || info->showAllTerrain)
|
||||
{
|
||||
drawTileTerrain(targetSurf, tinfo, tile);
|
||||
if (tinfo.riverType)
|
||||
drawRiver(targetSurf, tinfo);
|
||||
drawRoad(targetSurf, tinfo, tinfoUpper);
|
||||
}
|
||||
|
||||
drawTileTerrain(targetSurf, tinfo, tile);
|
||||
if (tinfo.riverType)
|
||||
drawRiver(targetSurf, tinfo);
|
||||
drawRoad(targetSurf, tinfo, tinfoUpper);
|
||||
|
||||
drawObjects(targetSurf, tile);
|
||||
if(isVisible)
|
||||
drawObjects(targetSurf, tile);
|
||||
}
|
||||
}
|
||||
|
||||
@ -940,7 +978,7 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
|
||||
{
|
||||
const TerrainTile2 & tile = parent->ttiles[pos.x][pos.y][pos.z];
|
||||
|
||||
if (!(*info->visibilityMap)[pos.x][pos.y][topTile.z])
|
||||
if (!(*info->visibilityMap)[pos.x][pos.y][topTile.z] && !info->showAllTerrain)
|
||||
drawFow(targetSurf);
|
||||
|
||||
// overlay needs to be drawn over fow, because of artifacts-aura-like spells
|
||||
@ -972,6 +1010,8 @@ void CMapHandler::CMapBlitter::blit(SDL_Surface * targetSurf, const MapDrawingIn
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
drawOverlayEx(targetSurf);
|
||||
|
||||
// drawDebugGrid()
|
||||
if (settings["session"]["showGrid"].Bool())
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
|
||||
#include "../lib/int3.h"
|
||||
#include "../lib/spells/ViewSpellInt.h"
|
||||
#include "gui/Geometries.h"
|
||||
#include "SDL.h"
|
||||
|
||||
@ -100,7 +101,11 @@ struct MapDrawingInfo
|
||||
|
||||
bool puzzleMode;
|
||||
int3 grailPos; // location of grail for puzzle mode [in tiles]
|
||||
|
||||
|
||||
const std::vector<ObjectPosInfo> * additionalIcons;
|
||||
|
||||
bool showAllTerrain; //for expert viewEarth
|
||||
|
||||
MapDrawingInfo(int3 &topTile_, const std::vector< std::vector< std::vector<ui8> > > * visibilityMap_, SDL_Rect * drawBounds_, CDefHandler * iconsDef_ = nullptr)
|
||||
: scaled(false),
|
||||
topTile(topTile_),
|
||||
@ -113,7 +118,9 @@ struct MapDrawingInfo
|
||||
heroAnim(0u),
|
||||
movement(int3()),
|
||||
puzzleMode(false),
|
||||
grailPos(int3())
|
||||
grailPos(int3()),
|
||||
additionalIcons(nullptr),
|
||||
showAllTerrain(false)
|
||||
{}
|
||||
|
||||
ui8 getHeroAnim() const { return otherheroAnim ? anim : heroAnim; }
|
||||
@ -238,6 +245,8 @@ class CMapHandler
|
||||
virtual void drawFow(SDL_Surface * targetSurf) const;
|
||||
/// draws map border frame on current position
|
||||
virtual void drawFrame(SDL_Surface * targetSurf) const;
|
||||
/// draws additional icons (for VIEW_AIR, VIEW_EARTH spells atm)
|
||||
virtual void drawOverlayEx(SDL_Surface * targetSurf);
|
||||
|
||||
// third drawing pass
|
||||
|
||||
@ -293,6 +302,8 @@ class CMapHandler
|
||||
|
||||
class CMapWorldViewBlitter : public CMapBlitter
|
||||
{
|
||||
private:
|
||||
SDL_Surface * objectToIcon(Obj id, si32 subId, PlayerColor owner) const;
|
||||
protected:
|
||||
void drawElement(EMapCacheType cacheType, SDL_Surface * sourceSurf, SDL_Rect * sourceRect,
|
||||
SDL_Surface * targetSurf, SDL_Rect * destRect, bool alphaBlit = false, ui8 rotationInfo = 0u) const override;
|
||||
@ -301,6 +312,7 @@ class CMapHandler
|
||||
void drawHeroFlag(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, SDL_Rect * destRect, bool moving) const override;
|
||||
void drawObject(SDL_Surface * targetSurf, SDL_Surface * sourceSurf, SDL_Rect * sourceRect, bool moving) const override;
|
||||
void drawFrame(SDL_Surface * targetSurf) const override {}
|
||||
void drawOverlayEx(SDL_Surface * targetSurf);
|
||||
void init(const MapDrawingInfo * info) override;
|
||||
SDL_Rect clip(SDL_Surface * targetSurf) const override;
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../../lib/CArtHandler.h"
|
||||
#include "../../lib/CSpellHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../../lib/CArtHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/CSpellHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/NetPacksBase.h"
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/CSoundBase.h"
|
||||
#include "../../lib/CSpellHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/JsonNode.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
@ -310,7 +310,7 @@ void CTerrainRect::showAll(SDL_Surface * to)
|
||||
MapDrawingInfo info(adventureInt->position, &LOCPLINT->cb->getVisibilityMap(), &pos, adventureInt->worldViewIconsDef);
|
||||
info.scaled = true;
|
||||
info.scale = adventureInt->worldViewScale;
|
||||
|
||||
adventureInt->worldViewOptions.adjustDrawingInfo(info);
|
||||
CGI->mh->drawTerrainRectNew(to, &info);
|
||||
}
|
||||
}
|
||||
@ -1783,6 +1783,9 @@ void CAdvMapInt::changeMode(EAdvMapMode newMode, float newScale /* = 0.4f */)
|
||||
townList.activate();
|
||||
heroList.activate();
|
||||
infoBar.activate();
|
||||
|
||||
worldViewOptions.clear();
|
||||
|
||||
break;
|
||||
case EAdvMapMode::WORLD_VIEW:
|
||||
panelMain->deactivate();
|
||||
@ -1843,3 +1846,23 @@ void CAdventureOptions::showScenarioInfo()
|
||||
GH.pushInt(new CScenarioInfo(LOCPLINT->cb->getMapHeader(), LOCPLINT->cb->getStartInfo()));
|
||||
}
|
||||
}
|
||||
|
||||
CAdvMapInt::WorldViewOptions::WorldViewOptions()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
void CAdvMapInt::WorldViewOptions::clear()
|
||||
{
|
||||
showAllTerrain = false;
|
||||
|
||||
iconPositions.clear();
|
||||
}
|
||||
|
||||
void CAdvMapInt::WorldViewOptions::adjustDrawingInfo(MapDrawingInfo& info)
|
||||
{
|
||||
info.showAllTerrain = showAllTerrain;
|
||||
|
||||
info.additionalIcons = &iconPositions;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
#include "../widgets/TextControls.h"
|
||||
#include "../widgets/Buttons.h"
|
||||
|
||||
#include "../../lib/spells/ViewSpellInt.h"
|
||||
|
||||
class CDefHandler;
|
||||
class CCallback;
|
||||
struct CGPath;
|
||||
@ -18,6 +20,8 @@ class IShipyard;
|
||||
enum class EMapAnimRedrawStatus;
|
||||
class CFadeAnimation;
|
||||
|
||||
struct MapDrawingInfo;
|
||||
|
||||
/*****************************/
|
||||
|
||||
/*
|
||||
@ -126,6 +130,21 @@ public:
|
||||
|
||||
EAdvMapMode mode;
|
||||
float worldViewScale;
|
||||
|
||||
struct WorldViewOptions
|
||||
{
|
||||
bool showAllTerrain; //for expert viewEarth
|
||||
|
||||
std::vector<ObjectPosInfo> iconPositions;
|
||||
|
||||
WorldViewOptions();
|
||||
|
||||
void clear();
|
||||
|
||||
void adjustDrawingInfo(MapDrawingInfo & info);
|
||||
};
|
||||
|
||||
WorldViewOptions worldViewOptions;
|
||||
|
||||
SDL_Surface * bg;
|
||||
SDL_Surface * bgWorldView;
|
||||
|
@ -26,7 +26,7 @@
|
||||
#include "../../lib/CCreatureHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CModHandler.h"
|
||||
#include "../../lib/CSpellHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/CTownHandler.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CModHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/CSpellHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/CGameState.h"
|
||||
|
||||
using namespace CSDL_Ext;
|
||||
|
@ -27,7 +27,7 @@
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CHeroHandler.h"
|
||||
#include "../../lib/CSpellHandler.h"
|
||||
#include "../../lib/spells/CSpellHandler.h"
|
||||
#include "../../lib/GameConstants.h"
|
||||
#include "../../lib/CGameState.h"
|
||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
||||
@ -107,15 +107,11 @@ CSpellWindow::CSpellWindow(const SDL_Rect &, const CGHeroInstance * _myHero, CPl
|
||||
Uint8 *sitesPerOurTab = s.combatSpell ? sitesPerTabBattle : sitesPerTabAdv;
|
||||
|
||||
++sitesPerOurTab[4];
|
||||
|
||||
if(s.air)
|
||||
++sitesPerOurTab[0];
|
||||
if(s.fire)
|
||||
++sitesPerOurTab[1];
|
||||
if(s.water)
|
||||
++sitesPerOurTab[2];
|
||||
if(s.earth)
|
||||
++sitesPerOurTab[3];
|
||||
|
||||
s.forEachSchool([&sitesPerOurTab](const SpellSchoolInfo & school, bool & stop)
|
||||
{
|
||||
++sitesPerOurTab[(ui8)school.id];
|
||||
});
|
||||
}
|
||||
if(sitesPerTabAdv[4] % 12 == 0)
|
||||
sitesPerTabAdv[4]/=12;
|
||||
@ -382,23 +378,17 @@ public:
|
||||
if(A.level<B.level)
|
||||
return true;
|
||||
if(A.level>B.level)
|
||||
return false;
|
||||
if(A.air && !B.air)
|
||||
return true;
|
||||
if(!A.air && B.air)
|
||||
return false;
|
||||
if(A.fire && !B.fire)
|
||||
return true;
|
||||
if(!A.fire && B.fire)
|
||||
return false;
|
||||
if(A.water && !B.water)
|
||||
return true;
|
||||
if(!A.water && B.water)
|
||||
return false;
|
||||
if(A.earth && !B.earth)
|
||||
return true;
|
||||
if(!A.earth && B.earth)
|
||||
return false;
|
||||
return false;
|
||||
|
||||
|
||||
for(ui8 schoolId = 0; schoolId < 4; schoolId++)
|
||||
{
|
||||
if(A.school.at((ESpellSchool)schoolId) && !B.school.at((ESpellSchool)schoolId))
|
||||
return true;
|
||||
if(!A.school.at((ESpellSchool)schoolId) && B.school.at((ESpellSchool)schoolId))
|
||||
return false;
|
||||
}
|
||||
|
||||
return A.name < B.name;
|
||||
}
|
||||
} spellsorter;
|
||||
@ -406,17 +396,15 @@ public:
|
||||
void CSpellWindow::computeSpellsPerArea()
|
||||
{
|
||||
std::vector<SpellID> spellsCurSite;
|
||||
for(auto it = mySpells.cbegin(); it != mySpells.cend(); ++it)
|
||||
for(const SpellID & spellID : mySpells)
|
||||
{
|
||||
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 )
|
||||
CSpell * s = spellID.toSpell();
|
||||
|
||||
if(s->combatSpell ^ !battleSpellsOnly
|
||||
&& ((selectedTab == 4) || (s->school[(ESpellSchool)selectedTab]))
|
||||
)
|
||||
{
|
||||
spellsCurSite.push_back(*it);
|
||||
spellsCurSite.push_back(spellID);
|
||||
}
|
||||
}
|
||||
std::sort(spellsCurSite.begin(), spellsCurSite.end(), spellsorter);
|
||||
@ -605,12 +593,6 @@ Uint8 CSpellWindow::pagesWithinCurrentTab()
|
||||
return battleSpellsOnly ? sitesPerTabBattle[selectedTab] : sitesPerTabAdv[selectedTab];
|
||||
}
|
||||
|
||||
void CSpellWindow::teleportTo( int town, const CGHeroInstance * hero )
|
||||
{
|
||||
const CGTownInstance * dest = LOCPLINT->cb->getTown(ObjectInstanceID(town));
|
||||
LOCPLINT->cb->castSpell(hero, SpellID::TOWN_PORTAL, dest->visitablePos());
|
||||
}
|
||||
|
||||
CSpellWindow::SpellArea::SpellArea(SDL_Rect pos, CSpellWindow * owner)
|
||||
{
|
||||
this->pos = pos;
|
||||
@ -623,9 +605,9 @@ CSpellWindow::SpellArea::SpellArea(SDL_Rect pos, CSpellWindow * owner)
|
||||
|
||||
void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
if(!down && mySpell!=-1)
|
||||
if(!down && mySpell != SpellID::NONE)
|
||||
{
|
||||
const CSpell *sp = CGI->spellh->objects[mySpell];
|
||||
const CSpell * sp = mySpell.toSpell();
|
||||
|
||||
int spellCost = owner->myInt->cb->getSpellCost(sp, owner->myHero);
|
||||
if(spellCost > owner->myHero->mana) //insufficient mana
|
||||
@ -637,8 +619,8 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
||||
}
|
||||
|
||||
//battle spell on adv map or adventure map spell during combat => display infowindow, not cast
|
||||
if((sp->combatSpell && !owner->myInt->battleInt)
|
||||
|| (!sp->combatSpell && owner->myInt->battleInt))
|
||||
if((sp->isCombatSpell() && !owner->myInt->battleInt)
|
||||
|| (sp->isAdventureSpell() && owner->myInt->battleInt))
|
||||
{
|
||||
std::vector<CComponent*> hlp(1, new CComponent(CComponent::spell, mySpell, 0));
|
||||
LOCPLINT->showInfoDialog(sp->getLevelInfo(schoolLevel).description, hlp);
|
||||
@ -653,9 +635,8 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
||||
{
|
||||
case ESpellCastProblem::OK:
|
||||
{
|
||||
int spell = mySpell;
|
||||
owner->fexitb();
|
||||
owner->myInt->battleInt->castThisSpell(spell);
|
||||
owner->myInt->battleInt->castThisSpell(mySpell);
|
||||
}
|
||||
break;
|
||||
case ESpellCastProblem::ANOTHER_ELEMENTAL_SUMMONED:
|
||||
@ -714,97 +695,102 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(!sp->combatSpell && !owner->myInt->battleInt) //adventure spell
|
||||
else if(sp->isAdventureSpell() && !owner->myInt->battleInt) //adventure spell and not in battle
|
||||
{
|
||||
SpellID spell = mySpell;
|
||||
const CGHeroInstance *h = owner->myHero;
|
||||
owner->fexitb();
|
||||
|
||||
|
||||
switch(spell)
|
||||
if(mySpell == SpellID::TOWN_PORTAL)
|
||||
{
|
||||
case SpellID::SUMMON_BOAT:
|
||||
//special case
|
||||
//todo: move to mechanics
|
||||
|
||||
std::vector <int> availableTowns;
|
||||
std::vector <const CGTownInstance*> Towns = LOCPLINT->cb->getTownsInfo(true);
|
||||
if (Towns.empty())
|
||||
{
|
||||
int3 pos = h->bestLocation();
|
||||
if(pos.x < 0)
|
||||
{
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[334]); //There is no place to put the boat.
|
||||
return;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SpellID::SCUTTLE_BOAT:
|
||||
case SpellID::DIMENSION_DOOR:
|
||||
adventureInt->enterCastingMode(sp);
|
||||
return;
|
||||
case SpellID::VISIONS:
|
||||
case SpellID::VIEW_EARTH:
|
||||
case SpellID::DISGUISE:
|
||||
case SpellID::VIEW_AIR:
|
||||
case SpellID::FLY:
|
||||
case SpellID::WATER_WALK:
|
||||
break;
|
||||
case SpellID::TOWN_PORTAL:
|
||||
{
|
||||
std::vector <int> availableTowns;
|
||||
std::vector <const CGTownInstance*> Towns = LOCPLINT->cb->getTownsInfo(true);
|
||||
if (Towns.empty())
|
||||
{
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[124]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (h->getSpellSchoolLevel(CGI->spellh->objects[spell]) < 2) //not advanced or expert - teleport to nearest available city
|
||||
{
|
||||
auto nearest = Towns.cbegin(); //nearest town's iterator
|
||||
si32 dist = LOCPLINT->cb->getTown((*nearest)->id)->pos.dist2dSQ(h->pos);
|
||||
|
||||
for (auto i = nearest + 1; i != Towns.cend(); ++i)
|
||||
{
|
||||
const CGTownInstance * dest = LOCPLINT->cb->getTown((*i)->id);
|
||||
si32 curDist = dest->pos.dist2dSQ(h->pos);
|
||||
|
||||
if (curDist < dist)
|
||||
{
|
||||
nearest = i;
|
||||
dist = curDist;
|
||||
}
|
||||
}
|
||||
|
||||
if ((*nearest)->visitingHero)
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[123]);
|
||||
else
|
||||
{
|
||||
const CGTownInstance * town = LOCPLINT->cb->getTown((*nearest)->id);
|
||||
LOCPLINT->cb->castSpell(h, spell, town->visitablePos());// - town->getVisitableOffset());
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //let the player choose
|
||||
for(auto & Town : Towns)
|
||||
{
|
||||
const CGTownInstance *t = Town;
|
||||
if (t->visitingHero == nullptr) //empty town and this is
|
||||
{
|
||||
availableTowns.push_back(t->id.getNum());//add to the list
|
||||
}
|
||||
}
|
||||
if (availableTowns.empty())
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[124]);
|
||||
else
|
||||
GH.pushInt (new CObjectListWindow(availableTowns,
|
||||
new CAnimImage("SPELLSCR",spell),
|
||||
CGI->generaltexth->jktexts[40], CGI->generaltexth->jktexts[41],
|
||||
std::bind (&CSpellWindow::teleportTo, owner, _1, h)));
|
||||
}
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[124]);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
|
||||
//can return earlier in some cases
|
||||
LOCPLINT->cb->castSpell(h, spell);
|
||||
if (h->getSpellSchoolLevel(sp) < 2) //not advanced or expert - teleport to nearest available city
|
||||
{
|
||||
auto nearest = Towns.cbegin(); //nearest town's iterator
|
||||
si32 dist = LOCPLINT->cb->getTown((*nearest)->id)->pos.dist2dSQ(h->pos);
|
||||
|
||||
for (auto i = nearest + 1; i != Towns.cend(); ++i)
|
||||
{
|
||||
const CGTownInstance * dest = LOCPLINT->cb->getTown((*i)->id);
|
||||
si32 curDist = dest->pos.dist2dSQ(h->pos);
|
||||
|
||||
if (curDist < dist)
|
||||
{
|
||||
nearest = i;
|
||||
dist = curDist;
|
||||
}
|
||||
}
|
||||
|
||||
if ((*nearest)->visitingHero)
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[123]);
|
||||
else
|
||||
{
|
||||
const CGTownInstance * town = LOCPLINT->cb->getTown((*nearest)->id);
|
||||
LOCPLINT->cb->castSpell(h, mySpell, town->visitablePos());// - town->getVisitableOffset());
|
||||
}
|
||||
}
|
||||
else
|
||||
{ //let the player choose
|
||||
for(auto & Town : Towns)
|
||||
{
|
||||
const CGTownInstance *t = Town;
|
||||
if (t->visitingHero == nullptr) //empty town and this is
|
||||
{
|
||||
availableTowns.push_back(t->id.getNum());//add to the list
|
||||
}
|
||||
}
|
||||
|
||||
auto castTownPortal = [h](int townId)
|
||||
{
|
||||
const CGTownInstance * dest = LOCPLINT->cb->getTown(ObjectInstanceID(townId));
|
||||
LOCPLINT->cb->castSpell(h, SpellID::TOWN_PORTAL, dest->visitablePos());
|
||||
};
|
||||
|
||||
if (availableTowns.empty())
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[124]);
|
||||
else
|
||||
GH.pushInt (new CObjectListWindow(availableTowns,
|
||||
new CAnimImage("SPELLSCR",mySpell),
|
||||
CGI->generaltexth->jktexts[40], CGI->generaltexth->jktexts[41],
|
||||
castTownPortal));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(mySpell == SpellID::SUMMON_BOAT)
|
||||
{
|
||||
//special case
|
||||
//todo: move to mechanics
|
||||
int3 pos = h->bestLocation();
|
||||
if(pos.x < 0)
|
||||
{
|
||||
LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[334]); //There is no place to put the boat.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(sp->getTargetType() == CSpell::LOCATION)
|
||||
{
|
||||
adventureInt->enterCastingMode(sp);
|
||||
}
|
||||
else if(sp->getTargetType() == CSpell::NO_TARGET)
|
||||
{
|
||||
LOCPLINT->cb->castSpell(h, mySpell);
|
||||
}
|
||||
else
|
||||
{
|
||||
logGlobal->error("Invalid spell target type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -816,7 +802,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->objects[mySpell], (hero ? hero : nullptr));
|
||||
if(causedDmg == 0 || mySpell == 57) //Titan's Lightning Bolt already has damage info included
|
||||
if(causedDmg == 0 || mySpell == SpellID::TITANS_LIGHTNING_BOLT) //Titan's Lightning Bolt already has damage info included
|
||||
dmgInfo = "";
|
||||
else
|
||||
{
|
||||
|
@ -112,6 +112,4 @@ public:
|
||||
void deactivate();
|
||||
void showAll(SDL_Surface * to);
|
||||
void show(SDL_Surface * to);
|
||||
|
||||
void teleportTo(int town, const CGHeroInstance * hero);
|
||||
};
|
||||
|
@ -42,7 +42,7 @@
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CModHandler.h"
|
||||
#include "../lib/CondSh.h"
|
||||
#include "../lib/CSpellHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CStopWatch.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/GameConstants.h"
|
||||
|
@ -411,7 +411,7 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGTownInstance * town):
|
||||
CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "TOWNQVBK", toScreen(position))
|
||||
{
|
||||
InfoAboutTown iah;
|
||||
LOCPLINT->cb->getTownInfo(town, iah);
|
||||
LOCPLINT->cb->getTownInfo(town, iah, adventureInt->selection); //todo: should this be nearest hero?
|
||||
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL;
|
||||
new CTownTooltip(Point(9, 10), iah);
|
||||
@ -421,7 +421,7 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGHeroInstance * hero):
|
||||
CWindowObject(RCLICK_POPUP | PLAYER_COLORED, "HEROQVBK", toScreen(position))
|
||||
{
|
||||
InfoAboutHero iah;
|
||||
LOCPLINT->cb->getHeroInfo(hero, iah);
|
||||
LOCPLINT->cb->getHeroInfo(hero, iah, adventureInt->selection);//todo: should this be nearest hero?
|
||||
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL;
|
||||
new CHeroTooltip(Point(9, 10), iah);
|
||||
@ -439,11 +439,15 @@ CInfoBoxPopup::CInfoBoxPopup(Point position, const CGGarrison * garr):
|
||||
|
||||
CIntObject * CRClickPopup::createInfoWin(Point position, const CGObjectInstance * specific) //specific=0 => draws info about selected town/hero
|
||||
{
|
||||
if(!specific)
|
||||
if(nullptr == specific)
|
||||
specific = adventureInt->selection;
|
||||
|
||||
assert(specific);
|
||||
|
||||
|
||||
if(nullptr == specific)
|
||||
{
|
||||
logGlobal->error("createInfoWin: no object to describe");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch(specific->ID)
|
||||
{
|
||||
case Obj::HERO:
|
||||
|
@ -128,6 +128,11 @@
|
||||
}
|
||||
},
|
||||
|
||||
"DISGUISED":
|
||||
{
|
||||
"hidden": true
|
||||
},
|
||||
|
||||
"EARTH_IMMUNITY":
|
||||
{
|
||||
"graphics":
|
||||
@ -328,6 +333,11 @@
|
||||
"icon": "zvs/Lib1.res/E_MIND"
|
||||
}
|
||||
},
|
||||
|
||||
"NONE":
|
||||
{
|
||||
"hidden": true
|
||||
},
|
||||
|
||||
"NO_DISTANCE_PENALTY":
|
||||
{
|
||||
@ -515,6 +525,11 @@
|
||||
"icon": "zvs/Lib1.res/E_RETAIL1"
|
||||
}
|
||||
},
|
||||
|
||||
"VISIONS":
|
||||
{
|
||||
"hidden": true
|
||||
},
|
||||
"WATER_IMMUNITY":
|
||||
{
|
||||
"graphics":
|
||||
|
@ -40,7 +40,52 @@
|
||||
},
|
||||
"levels" : {
|
||||
"base":{
|
||||
"range" : "X"
|
||||
"range" : "0",
|
||||
"effects" : {
|
||||
"visionsMonsters" : {
|
||||
"type" : "VISIONS",
|
||||
"subtype" : 0,
|
||||
"duration" : "ONE_DAY",
|
||||
"val" : 1,
|
||||
"valueType" : "INDEPENDENT_MAX"
|
||||
}
|
||||
}
|
||||
},
|
||||
"advanced":{
|
||||
"effects" : {
|
||||
"visionsMonsters" : {
|
||||
"val" : 2
|
||||
},
|
||||
"visionsHeroes" :{
|
||||
"type" : "VISIONS",
|
||||
"subtype" : 1,
|
||||
"duration" : "ONE_DAY",
|
||||
"val" : 2,
|
||||
"valueType" : "INDEPENDENT_MAX"
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
"expert":{
|
||||
"effects" : {
|
||||
"visionsMonsters" : {
|
||||
"val" : 3
|
||||
},
|
||||
"visionsHeroes" :{
|
||||
"type" : "VISIONS",
|
||||
"subtype" : 1,
|
||||
"duration" : "ONE_DAY",
|
||||
"val" : 3,
|
||||
"valueType" : "INDEPENDENT_MAX"
|
||||
},
|
||||
"visionsTowns" :{
|
||||
"type" : "VISIONS",
|
||||
"subtype" : 2,
|
||||
"duration" : "ONE_DAY",
|
||||
"val" : 3,
|
||||
"valueType" : "INDEPENDENT_MAX"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
@ -72,7 +117,30 @@
|
||||
},
|
||||
"levels" : {
|
||||
"base":{
|
||||
"range" : "X"
|
||||
"range" : "0",
|
||||
"effects" : {
|
||||
"stealth" : {
|
||||
"type" : "DISGUISED",
|
||||
"subtype" : 0, //required
|
||||
"duration" : "ONE_DAY",
|
||||
"val" : 1,
|
||||
"valueType" : "INDEPENDENT_MAX"
|
||||
}
|
||||
}
|
||||
},
|
||||
"advanced":{
|
||||
"effects" : {
|
||||
"stealth" : {
|
||||
"val" : 2
|
||||
}
|
||||
}
|
||||
},
|
||||
"expert":{
|
||||
"effects" : {
|
||||
"stealth" : {
|
||||
"val" : 3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
@ -104,7 +172,29 @@
|
||||
},
|
||||
"levels" : {
|
||||
"base":{
|
||||
"range" : "X"
|
||||
"range" : "0",
|
||||
"effects" : {
|
||||
"fly" : {
|
||||
"type" : "FLYING_MOVEMENT",
|
||||
"subtype" : 2,
|
||||
"duration" : "ONE_DAY",
|
||||
"val" : 0 //in fact unused
|
||||
}
|
||||
}
|
||||
},
|
||||
"advanced":{
|
||||
"effects" : {
|
||||
"fly" : {
|
||||
"subtype" : 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"expert":{
|
||||
"effects" : {
|
||||
"fly" : {
|
||||
"subtype" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
@ -120,7 +210,29 @@
|
||||
},
|
||||
"levels" : {
|
||||
"base":{
|
||||
"range" : "X"
|
||||
"range" : "0",
|
||||
"effects" : {
|
||||
"waterWalk" : {
|
||||
"type" : "WATER_WALKING",
|
||||
"subtype" : 2,
|
||||
"duration" : "ONE_DAY",
|
||||
"val" : 0 //in fact unused
|
||||
}
|
||||
}
|
||||
},
|
||||
"advanced":{
|
||||
"effects" : {
|
||||
"waterWalk" : {
|
||||
"subtype" : 1
|
||||
}
|
||||
}
|
||||
},
|
||||
"expert":{
|
||||
"effects" : {
|
||||
"waterWalk" : {
|
||||
"subtype" : 1
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"flags" : {
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "mapObjects/CObjectHandler.h"
|
||||
#include "CHeroHandler.h"
|
||||
#include "CCreatureHandler.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CTownHandler.h"
|
||||
#include "NetPacks.h"
|
||||
#include "JsonNode.h"
|
||||
|
@ -15,7 +15,7 @@
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "VCMI_Lib.h"
|
||||
#include "CModHandler.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "mapObjects/MapObjects.h"
|
||||
#include "NetPacksBase.h"
|
||||
#include "GameConstants.h"
|
||||
|
@ -3,7 +3,7 @@
|
||||
#include "BattleState.h"
|
||||
#include "CGameState.h"
|
||||
#include "NetPacks.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "VCMI_Lib.h"
|
||||
#include "CTownHandler.h"
|
||||
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
#include "GameConstants.h"
|
||||
#include "CCreatureHandler.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
|
||||
///MacroString
|
||||
|
||||
@ -127,7 +127,7 @@ CBonusTypeHandler::~CBonusTypeHandler()
|
||||
}
|
||||
|
||||
std::string CBonusTypeHandler::bonusToString(const Bonus *bonus, const IBonusBearer *bearer, bool description) const
|
||||
{
|
||||
{
|
||||
auto getValue = [=](const std::string &name) -> std::string
|
||||
{
|
||||
if (name == "val")
|
||||
@ -156,6 +156,8 @@ std::string CBonusTypeHandler::bonusToString(const Bonus *bonus, const IBonusBea
|
||||
};
|
||||
|
||||
const CBonusType& bt = bonusTypes[bonus->type];
|
||||
if(bt.hidden)
|
||||
return "";
|
||||
const MacroString& macro = description ? bt.description : bt.name;
|
||||
|
||||
return macro.build(getValue);
|
||||
|
@ -8,7 +8,7 @@
|
||||
#include "IGameCallback.h"
|
||||
#include "CGameState.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CHeroHandler.h"
|
||||
#include "IBonusTypeHandler.h"
|
||||
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "BattleState.h" // for BattleInfo
|
||||
#include "NetPacks.h" // for InfoWindow
|
||||
#include "CModHandler.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
|
||||
//TODO make clean
|
||||
#define ERROR_VERBOSE_OR_NOT_RET_VAL_IF(cond, verbose, txt, retVal) do {if(cond){if(verbose)logGlobal->errorStream() << BOOST_CURRENT_FUNCTION << ": " << txt; return retVal;}} while(0)
|
||||
@ -201,14 +201,22 @@ int CGameInfoCallback::howManyTowns(PlayerColor Player) const
|
||||
return gs->players[Player].towns.size();
|
||||
}
|
||||
|
||||
bool CGameInfoCallback::getTownInfo( const CGObjectInstance *town, InfoAboutTown &dest ) const
|
||||
bool CGameInfoCallback::getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject/* = nullptr*/) const
|
||||
{
|
||||
ERROR_RET_VAL_IF(!isVisible(town, player), "Town is not visible!", false); //it's not a town or it's not visible for layer
|
||||
bool detailed = hasAccess(town->tempOwner);
|
||||
|
||||
//TODO vision support
|
||||
if(town->ID == Obj::TOWN)
|
||||
{
|
||||
if(!detailed && nullptr != selectedObject)
|
||||
{
|
||||
const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
|
||||
if(nullptr != selectedHero)
|
||||
detailed = selectedHero->hasVisions(town, 1);
|
||||
}
|
||||
|
||||
dest.initFromTown(static_cast<const CGTownInstance *>(town), detailed);
|
||||
}
|
||||
else if(town->ID == Obj::GARRISON || town->ID == Obj::GARRISON2)
|
||||
dest.initFromArmy(static_cast<const CArmedInstance *>(town), detailed);
|
||||
else
|
||||
@ -233,15 +241,109 @@ std::vector<const CGObjectInstance*> CGameInfoCallback::getGuardingCreatures (in
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool CGameInfoCallback::getHeroInfo( const CGObjectInstance *hero, InfoAboutHero &dest ) const
|
||||
bool CGameInfoCallback::getHeroInfo(const CGObjectInstance * hero, InfoAboutHero & dest, const CGObjectInstance * selectedObject/* = nullptr*/) const
|
||||
{
|
||||
const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(hero);
|
||||
|
||||
ERROR_RET_VAL_IF(!h, "That's not a hero!", false);
|
||||
ERROR_RET_VAL_IF(!isVisible(h->getPosition(false)), "That hero is not visible!", false);
|
||||
|
||||
//TODO vision support
|
||||
dest.initFromHero(h, hasAccess(h->tempOwner));
|
||||
bool accessFlag = hasAccess(h->tempOwner);
|
||||
|
||||
if(!accessFlag && nullptr != selectedObject)
|
||||
{
|
||||
const CGHeroInstance * selectedHero = dynamic_cast<const CGHeroInstance *>(selectedObject);
|
||||
if(nullptr != selectedHero)
|
||||
accessFlag = selectedHero->hasVisions(hero, 1);
|
||||
}
|
||||
|
||||
dest.initFromHero(h, accessFlag);
|
||||
|
||||
//DISGUISED bonus implementation
|
||||
|
||||
if(getPlayerRelations(getLocalPlayer(), hero->tempOwner) == PlayerRelations::ENEMIES)
|
||||
{
|
||||
//todo: bonus cashing
|
||||
int disguiseLevel = h->valOfBonuses(Selector::typeSubtype(Bonus::DISGUISED, 0));
|
||||
|
||||
auto doBasicDisguise = [disguiseLevel](InfoAboutHero & info)
|
||||
{
|
||||
int maxAIValue = 0;
|
||||
const CCreature * mostStrong = nullptr;
|
||||
|
||||
for(auto & elem : info.army)
|
||||
{
|
||||
if(elem.second.type->AIValue > maxAIValue)
|
||||
{
|
||||
maxAIValue = elem.second.type->AIValue;
|
||||
mostStrong = elem.second.type;
|
||||
}
|
||||
}
|
||||
|
||||
if(nullptr == mostStrong)//just in case
|
||||
logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Unable to select most strong stack" << disguiseLevel;
|
||||
else
|
||||
for(auto & elem : info.army)
|
||||
{
|
||||
elem.second.type = mostStrong;
|
||||
}
|
||||
};
|
||||
|
||||
auto doAdvancedDisguise = [accessFlag, &doBasicDisguise](InfoAboutHero & info)
|
||||
{
|
||||
doBasicDisguise(info);
|
||||
|
||||
for(auto & elem : info.army)
|
||||
elem.second.count = 0;
|
||||
};
|
||||
|
||||
auto doExpertDisguise = [this,h](InfoAboutHero & info)
|
||||
{
|
||||
for(auto & elem : info.army)
|
||||
elem.second.count = 0;
|
||||
|
||||
const auto factionIndex = getStartInfo(false)->playerInfos.at(h->tempOwner).castle;
|
||||
|
||||
int maxAIValue = 0;
|
||||
const CCreature * mostStrong = nullptr;
|
||||
|
||||
for(auto creature : VLC->creh->creatures)
|
||||
{
|
||||
if(creature->faction == factionIndex && creature->AIValue > maxAIValue)
|
||||
{
|
||||
maxAIValue = creature->AIValue;
|
||||
mostStrong = creature;
|
||||
}
|
||||
}
|
||||
|
||||
if(nullptr != mostStrong) //possible, faction may have no creatures at all
|
||||
for(auto & elem : info.army)
|
||||
elem.second.type = mostStrong;
|
||||
};
|
||||
|
||||
|
||||
switch (disguiseLevel)
|
||||
{
|
||||
case 0:
|
||||
//no bonus at all - do nothing
|
||||
break;
|
||||
case 1:
|
||||
doBasicDisguise(dest);
|
||||
break;
|
||||
case 2:
|
||||
doAdvancedDisguise(dest);
|
||||
break;
|
||||
case 3:
|
||||
doExpertDisguise(dest);
|
||||
break;
|
||||
default:
|
||||
//invalid value
|
||||
logGlobal->errorStream() << "CGameInfoCallback::getHeroInfo: Invalid DISGUISED bonus value " << disguiseLevel;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ public:
|
||||
const CGHeroInstance* getHero(ObjectInstanceID objid) const;
|
||||
const CGHeroInstance* getHeroWithSubid(int subid) const;
|
||||
int getHeroCount(PlayerColor player, bool includeGarrisoned) const;
|
||||
bool getHeroInfo(const CGObjectInstance *hero, InfoAboutHero &dest) const;
|
||||
bool getHeroInfo(const CGObjectInstance * hero, InfoAboutHero & dest, const CGObjectInstance * selectedObject = nullptr) const;
|
||||
int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction
|
||||
int estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const; //estimates damage of given spell; returns 0 if spell causes no dmg
|
||||
const CArtifactInstance * getArtInstance(ArtifactInstanceID aid) const;
|
||||
@ -99,7 +99,7 @@ public:
|
||||
std::vector<const CGHeroInstance *> getAvailableHeroes(const CGObjectInstance * townOrTavern) const; //heroes that can be recruited
|
||||
std::string getTavernGossip(const CGObjectInstance * townOrTavern) const;
|
||||
EBuildingState::EBuildingState canBuildStructure(const CGTownInstance *t, BuildingID ID);//// 0 - no more than one capitol, 1 - lack of water, 2 - forbidden, 3 - Add another level to Mage Guild, 4 - already built, 5 - cannot build, 6 - cannot afford, 7 - build, 8 - lack of requirements
|
||||
virtual bool getTownInfo(const CGObjectInstance *town, InfoAboutTown &dest) const;
|
||||
virtual bool getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject = nullptr) const;
|
||||
const CTown *getNativeTown(PlayerColor color) const;
|
||||
|
||||
//from gs
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "IGameEventsReceiver.h"
|
||||
#include "CGameStateFwd.h"
|
||||
|
||||
#include "spells/ViewSpellInt.h"
|
||||
|
||||
/*
|
||||
* CGameInterface.h, part of VCMI engine
|
||||
*
|
||||
@ -93,6 +95,8 @@ public:
|
||||
// all stacks operations between these objects become allowed, interface has to call onEnd when done
|
||||
virtual void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) = 0;
|
||||
virtual void finish(){}; //if for some reason we want to end
|
||||
|
||||
virtual void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions){};
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CDynLibHandler
|
||||
|
@ -7,7 +7,7 @@
|
||||
#include "CBuildingHandler.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "CTownHandler.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CHeroHandler.h"
|
||||
#include "mapObjects/CObjectHandler.h"
|
||||
#include "CCreatureHandler.h"
|
||||
|
@ -67,7 +67,7 @@ namespace boost
|
||||
class shared_mutex;
|
||||
}
|
||||
|
||||
//numbers of creatures are exact numbers if detailed else they are quantity ids (0 - a few, 1 - several and so on; additionally -1 - unknown)
|
||||
//numbers of creatures are exact numbers if detailed else they are quantity ids (1 - a few, 2 - several and so on; additionally 0 - unknown)
|
||||
struct ArmyDescriptor : public std::map<SlotID, CStackBasicDescriptor>
|
||||
{
|
||||
bool isDetailed;
|
||||
|
@ -54,6 +54,14 @@ set(lib_SRCS
|
||||
rmg/CZoneGraphGenerator.cpp
|
||||
rmg/CZonePlacer.cpp
|
||||
|
||||
spells/CSpellHandler.cpp
|
||||
spells/ISpellMechanics.cpp
|
||||
spells/AdventureSpellMechanics.cpp
|
||||
spells/BattleSpellMechanics.cpp
|
||||
spells/CreatureSpellMechanics.cpp
|
||||
spells/CDefaultSpellMechanics.cpp
|
||||
spells/ViewSpellInt.cpp
|
||||
|
||||
BattleAction.cpp
|
||||
BattleHex.cpp
|
||||
BattleState.cpp
|
||||
@ -71,7 +79,7 @@ set(lib_SRCS
|
||||
CModHandler.cpp
|
||||
CObstacleInstance.cpp
|
||||
CRandomGenerator.cpp
|
||||
CSpellHandler.cpp
|
||||
|
||||
CThreadHelper.cpp
|
||||
CTownHandler.cpp
|
||||
GameConstants.cpp
|
||||
@ -83,7 +91,6 @@ set(lib_SRCS
|
||||
VCMI_Lib.cpp
|
||||
VCMIDirs.cpp
|
||||
IHandlerBase.cpp
|
||||
SpellMechanics.cpp
|
||||
|
||||
IGameCallback.cpp
|
||||
CGameInfoCallback.cpp
|
||||
@ -96,7 +103,7 @@ set(lib_SRCS
|
||||
registerTypes/TypesClientPacks2.cpp
|
||||
registerTypes/TypesMapObjects1.cpp
|
||||
registerTypes/TypesMapObjects2.cpp
|
||||
registerTypes/TypesMapObjects3.cpp
|
||||
registerTypes/TypesMapObjects3.cpp
|
||||
registerTypes/TypesPregamePacks.cpp
|
||||
registerTypes/TypesServerPacks.cpp
|
||||
)
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "StringConstants.h"
|
||||
#include "CStopWatch.h"
|
||||
#include "IHandlerBase.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
|
||||
/*
|
||||
* CModHandler.cpp, part of VCMI engine
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include "CObstacleInstance.h"
|
||||
#include "CHeroHandler.h"
|
||||
#include "VCMI_Lib.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
|
||||
/*
|
||||
* CObstacleInstance.cpp, part of VCMI engine
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "CModHandler.h"
|
||||
#include "CHeroHandler.h"
|
||||
#include "CArtHandler.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "filesystem/Filesystem.h"
|
||||
#include "mapObjects/CObjectClassesHandler.h"
|
||||
#include "mapObjects/CObjectHandler.h"
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "mapObjects/CObjectClassesHandler.h"
|
||||
#include "CArtHandler.h"
|
||||
#include "CCreatureHandler.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
|
||||
const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2);
|
||||
const PlayerColor PlayerColor::CANNOT_DETERMINE = PlayerColor(253);
|
||||
|
@ -895,6 +895,8 @@ public:
|
||||
ESpellID num;
|
||||
};
|
||||
|
||||
ID_LIKE_OPERATORS_DECLS(SpellID, SpellID::ESpellID)
|
||||
|
||||
enum class ESpellSchool: ui8
|
||||
{
|
||||
AIR = 0,
|
||||
@ -903,8 +905,6 @@ enum class ESpellSchool: ui8
|
||||
EARTH = 3
|
||||
};
|
||||
|
||||
ID_LIKE_OPERATORS_DECLS(SpellID, SpellID::ESpellID)
|
||||
|
||||
// Typedef declarations
|
||||
typedef ui8 TFaction;
|
||||
typedef si64 TExpType;
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "HeroBonus.h"
|
||||
|
||||
#include "VCMI_Lib.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CCreatureHandler.h"
|
||||
#include "CCreatureSet.h"
|
||||
#include "CHeroHandler.h"
|
||||
|
@ -215,7 +215,10 @@ public:
|
||||
BONUS_NAME(REBIRTH) /* val - percent of life restored, subtype = 0 - regular, 1 - at least one unit (sacred Phoenix) */\
|
||||
BONUS_NAME(ADDITIONAL_UNITS) /*val of units with id = subtype will be added to hero's army at the beginning of battle */\
|
||||
BONUS_NAME(SPOILS_OF_WAR) /*val * 10^-6 * gained exp resources of subtype will be given to hero after battle*/\
|
||||
BONUS_NAME(BLOCK)
|
||||
BONUS_NAME(BLOCK)\
|
||||
BONUS_NAME(DISGUISED) /* subtype - spell level */\
|
||||
BONUS_NAME(VISIONS) /* subtype - spell level */
|
||||
|
||||
|
||||
#define BONUS_SOURCE_LIST \
|
||||
BONUS_SOURCE(ARTIFACT)\
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "IGameCallback.h"
|
||||
|
||||
#include "CHeroHandler.h" // for CHeroHandler
|
||||
#include "CSpellHandler.h" // for CSpell
|
||||
#include "spells/CSpellHandler.h"// for CSpell
|
||||
#include "NetPacks.h"
|
||||
#include "CBonusTypeHandler.h"
|
||||
#include "CModHandler.h"
|
||||
|
@ -16,6 +16,8 @@
|
||||
#include "mapping/CMap.h"
|
||||
#include "CObstacleInstance.h"
|
||||
|
||||
#include "spells/ViewSpellInt.h"
|
||||
|
||||
/*
|
||||
* NetPacks.h, part of VCMI engine
|
||||
*
|
||||
@ -1685,6 +1687,22 @@ struct AdvmapSpellCast : public CPackForClient //108
|
||||
}
|
||||
};
|
||||
|
||||
struct ShowWorldViewEx : public CPackForClient //4000
|
||||
{
|
||||
PlayerColor player;
|
||||
|
||||
std::vector<ObjectPosInfo> objectPositions;
|
||||
|
||||
ShowWorldViewEx(){type = 4000;}
|
||||
|
||||
void applyCl(CClient *cl);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & player & objectPositions;
|
||||
}
|
||||
};
|
||||
|
||||
/***********************************************************************************************************/
|
||||
|
||||
struct CommitPackage : public CPackForServer
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "CModHandler.h"
|
||||
#include "VCMI_Lib.h"
|
||||
#include "mapping/CMap.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/SpellMechanics.h"
|
||||
#include "CCreatureHandler.h"
|
||||
#include "CGameState.h"
|
||||
#include "BattleState.h"
|
||||
@ -1339,7 +1339,7 @@ DLL_LINKAGE void BattleSpellCast::applyGs( CGameState *gs )
|
||||
|
||||
const CSpell * spell = SpellID(id).toSpell();
|
||||
|
||||
spell->afterCast(gs->curB, this);
|
||||
spell->applyBattle(gs->curB, this);
|
||||
}
|
||||
|
||||
void actualizeEffect(CStack * s, const std::vector<Bonus> & ef)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -19,7 +19,7 @@
|
||||
#include "mapObjects/CObjectHandler.h"
|
||||
#include "CTownHandler.h"
|
||||
#include "CBuildingHandler.h"
|
||||
#include "CSpellHandler.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CGeneralTextHandler.h"
|
||||
#include "CModHandler.h"
|
||||
#include "IGameEventsReceiver.h"
|
||||
|
@ -150,8 +150,6 @@
|
||||
<Unit filename="CRandomGenerator.h" />
|
||||
<Unit filename="CScriptingModule.h" />
|
||||
<Unit filename="CSoundBase.h" />
|
||||
<Unit filename="CSpellHandler.cpp" />
|
||||
<Unit filename="CSpellHandler.h" />
|
||||
<Unit filename="CStopWatch.h" />
|
||||
<Unit filename="CThreadHelper.cpp" />
|
||||
<Unit filename="CThreadHelper.h" />
|
||||
@ -185,8 +183,6 @@
|
||||
<Unit filename="ResourceSet.cpp" />
|
||||
<Unit filename="ResourceSet.h" />
|
||||
<Unit filename="ScopeGuard.h" />
|
||||
<Unit filename="SpellMechanics.cpp" />
|
||||
<Unit filename="SpellMechanics.h" />
|
||||
<Unit filename="StartInfo.h" />
|
||||
<Unit filename="StdInc.h">
|
||||
<Option weight="0" />
|
||||
@ -294,6 +290,20 @@
|
||||
<Unit filename="rmg/CZoneGraphGenerator.h" />
|
||||
<Unit filename="rmg/CZonePlacer.cpp" />
|
||||
<Unit filename="rmg/CZonePlacer.h" />
|
||||
<Unit filename="spells/AdventureSpellMechanics.cpp" />
|
||||
<Unit filename="spells/AdventureSpellMechanics.h" />
|
||||
<Unit filename="spells/BattleSpellMechanics.cpp" />
|
||||
<Unit filename="spells/BattleSpellMechanics.h" />
|
||||
<Unit filename="spells/CDefaultSpellMechanics.cpp" />
|
||||
<Unit filename="spells/CDefaultSpellMechanics.h" />
|
||||
<Unit filename="spells/CSpellHandler.cpp" />
|
||||
<Unit filename="spells/CSpellHandler.h" />
|
||||
<Unit filename="spells/CreatureSpellMechanics.cpp" />
|
||||
<Unit filename="spells/CreatureSpellMechanics.h" />
|
||||
<Unit filename="spells/ISpellMechanics.cpp" />
|
||||
<Unit filename="spells/ISpellMechanics.h" />
|
||||
<Unit filename="spells/ViewSpellInt.cpp" />
|
||||
<Unit filename="spells/ViewSpellInt.h" />
|
||||
<Unit filename="vcmi_endian.h" />
|
||||
<Extensions>
|
||||
<code_completion />
|
||||
|
@ -184,11 +184,16 @@
|
||||
<ClCompile Include="CModHandler.cpp" />
|
||||
<ClCompile Include="CObstacleInstance.cpp" />
|
||||
<ClCompile Include="Connection.cpp" />
|
||||
<ClCompile Include="CSpellHandler.cpp" />
|
||||
<ClCompile Include="SpellMechanics.cpp" />
|
||||
<ClCompile Include="CThreadHelper.cpp" />
|
||||
<ClCompile Include="CTownHandler.cpp" />
|
||||
<ClCompile Include="CRandomGenerator.cpp" />
|
||||
<ClCompile Include="spells\CSpellHandler.cpp" />
|
||||
<ClCompile Include="spells\ISpellMechanics.cpp" />
|
||||
<ClCompile Include="spells\AdventureSpellMechanics.cpp" />
|
||||
<ClCompile Include="spells\BattleSpellMechanics.cpp" />
|
||||
<ClCompile Include="spells\CreatureSpellMechanics.cpp" />
|
||||
<ClCompile Include="spells\CDefaultSpellMechanics.cpp" />
|
||||
<ClCompile Include="spells\ViewSpellInt.cpp" />
|
||||
<ClCompile Include="filesystem\AdapterLoaders.cpp" />
|
||||
<ClCompile Include="filesystem\CArchiveLoader.cpp" />
|
||||
<ClCompile Include="filesystem\CBinaryReader.cpp" />
|
||||
@ -287,7 +292,6 @@
|
||||
<ClInclude Include="ConstTransitivePtr.h" />
|
||||
<ClInclude Include="CRandomGenerator.h" />
|
||||
<ClInclude Include="CScriptingModule.h" />
|
||||
<ClInclude Include="CSpellHandler.h" />
|
||||
<ClInclude Include="CStopWatch.h" />
|
||||
<ClInclude Include="CThreadHelper.h" />
|
||||
<ClInclude Include="CTownHandler.h" />
|
||||
@ -353,7 +357,14 @@
|
||||
<ClInclude Include="rmg\CZoneGraphGenerator.h" />
|
||||
<ClInclude Include="rmg\CZonePlacer.h" />
|
||||
<ClInclude Include="rmg\float3.h" />
|
||||
<ClInclude Include="SpellMechanics.h" />
|
||||
<ClInclude Include="spells\AdventureSpellMechanics.h" />
|
||||
<ClInclude Include="spells\BattleSpellMechanics.h" />
|
||||
<ClInclude Include="spells\CDefaultSpellMechanics.h" />
|
||||
<ClInclude Include="spells\CreatureSpellMechanics.h" />
|
||||
<ClInclude Include="spells\CSpellHandler.h" />
|
||||
<ClInclude Include="spells\ISpellMechanics.h" />
|
||||
<ClInclude Include="spells\SpellMechanics.h" />
|
||||
<ClInclude Include="spells\ViewSpellInt.h" />
|
||||
<ClInclude Include="StartInfo.h" />
|
||||
<ClInclude Include="StdInc.h" />
|
||||
<ClInclude Include="UnlockGuard.h" />
|
||||
|
@ -26,6 +26,9 @@
|
||||
<Filter Include="mapObjects">
|
||||
<UniqueIdentifier>{ee24c7f7-f4e2-4d35-b994-94a6e29ea92f}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="spells">
|
||||
<UniqueIdentifier>{bda963b1-00e1-412a-9b44-f5cd3f8e9e33}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="BattleAction.cpp" />
|
||||
@ -35,7 +38,6 @@
|
||||
<ClCompile Include="CCreatureHandler.cpp" />
|
||||
<ClCompile Include="CGeneralTextHandler.cpp" />
|
||||
<ClCompile Include="CHeroHandler.cpp" />
|
||||
<ClCompile Include="CSpellHandler.cpp" />
|
||||
<ClCompile Include="CTownHandler.cpp" />
|
||||
<ClCompile Include="CCreatureSet.cpp" />
|
||||
<ClCompile Include="CGameState.cpp" />
|
||||
@ -205,7 +207,27 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="registerTypes\TypesMapObjects3.cpp" />
|
||||
<ClCompile Include="IHandlerBase.cpp" />
|
||||
<ClCompile Include="SpellMechanics.cpp" />
|
||||
<ClCompile Include="spells\AdventureSpellMechanics.cpp">
|
||||
<Filter>spells</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="spells\CDefaultSpellMechanics.cpp">
|
||||
<Filter>spells</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="spells\CSpellHandler.cpp">
|
||||
<Filter>spells</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="spells\ViewSpellInt.cpp">
|
||||
<Filter>spells</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="spells\ISpellMechanics.cpp">
|
||||
<Filter>spells</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="spells\BattleSpellMechanics.cpp">
|
||||
<Filter>spells</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="spells\CreatureSpellMechanics.cpp">
|
||||
<Filter>spells</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="CCreatureSet.h">
|
||||
@ -259,9 +281,6 @@
|
||||
<ClInclude Include="ConstTransitivePtr.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CSpellHandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CTownHandler.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -493,8 +512,29 @@
|
||||
<ClInclude Include="CGameStateFwd.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="SpellMechanics.h">
|
||||
<Filter>Header Files</Filter>
|
||||
<ClInclude Include="spells\AdventureSpellMechanics.h">
|
||||
<Filter>spells</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="spells\BattleSpellMechanics.h">
|
||||
<Filter>spells</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="spells\CreatureSpellMechanics.h">
|
||||
<Filter>spells</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="spells\CSpellHandler.h">
|
||||
<Filter>spells</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="spells\ISpellMechanics.h">
|
||||
<Filter>spells</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="spells\ViewSpellInt.h">
|
||||
<Filter>spells</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="spells\CDefaultSpellMechanics.h">
|
||||
<Filter>spells</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="spells\SpellMechanics.h">
|
||||
<Filter>spells</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
@ -15,7 +15,7 @@
|
||||
#include "../CGeneralTextHandler.h"
|
||||
#include "../CSoundBase.h"
|
||||
#include "CommonConstructors.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../IGameCallback.h"
|
||||
#include "../CGameState.h"
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CModHandler.h"
|
||||
#include "../CSoundBase.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "CObjectClassesHandler.h"
|
||||
#include "../IGameCallback.h"
|
||||
#include "../CGameState.h"
|
||||
@ -1343,3 +1343,24 @@ void CGHeroInstance::levelUpAutomatically()
|
||||
levelUp(proposedSecondarySkills);
|
||||
}
|
||||
}
|
||||
|
||||
bool CGHeroInstance::hasVisions(const CGObjectInstance * target, const int subtype) const
|
||||
{
|
||||
//VISIONS spell support
|
||||
|
||||
const std::string cached = boost::to_string((boost::format("type_%d__subtype_%d") % Bonus::VISIONS % subtype));
|
||||
|
||||
const int visionsMultiplier = valOfBonuses(Selector::typeSubtype(Bonus::VISIONS,subtype), cached);
|
||||
|
||||
int visionsRange = visionsMultiplier * getPrimSkillLevel(PrimarySkill::SPELL_POWER);
|
||||
|
||||
if (visionsMultiplier > 0)
|
||||
vstd::amax(visionsRange, 3); //minimum range is 3 tiles, but only if VISIONS bonus present
|
||||
|
||||
const int distance = target->pos.dist2d(getPosition(false));
|
||||
|
||||
logGlobal->debug(boost::to_string(boost::format("Visions: dist %d, mult %d, range %d") % distance % visionsMultiplier % visionsRange));
|
||||
|
||||
return (distance < visionsRange) && (target->pos.z == pos.z);
|
||||
}
|
||||
|
||||
|
@ -193,6 +193,8 @@ public:
|
||||
void Updatespecialty();
|
||||
void recreateSecondarySkillsBonuses();
|
||||
void updateSkill(SecondarySkill which, int val);
|
||||
|
||||
bool hasVisions(const CGObjectInstance * target, const int subtype) const;
|
||||
|
||||
CGHeroInstance();
|
||||
virtual ~CGHeroInstance();
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "../NetPacks.h"
|
||||
#include "../CSoundBase.h"
|
||||
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../StartInfo.h"
|
||||
#include "../IGameCallback.h"
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CCreatureHandler.h"
|
||||
#include "../CCreatureSet.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
|
||||
namespace JsonRandom
|
||||
{
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "../CModHandler.h"
|
||||
|
||||
#include "CObjectClassesHandler.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../IGameCallback.h"
|
||||
#include "../CGameState.h"
|
||||
|
||||
@ -101,7 +101,41 @@ std::string CGCreature::getHoverText(PlayerColor player) const
|
||||
|
||||
std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
|
||||
{
|
||||
std::string hoverName = getHoverText(hero->tempOwner);
|
||||
std::string hoverName;
|
||||
if(hero->hasVisions(this, 0))
|
||||
{
|
||||
MetaString ms;
|
||||
ms << stacks.begin()->second->count;
|
||||
ms << " " ;
|
||||
ms.addTxt(MetaString::CRE_PL_NAMES,subID);
|
||||
|
||||
ms << "\n";
|
||||
|
||||
int decision = takenAction(hero, true);
|
||||
|
||||
switch (decision)
|
||||
{
|
||||
case FIGHT:
|
||||
ms.addTxt(MetaString::GENERAL_TXT,246);
|
||||
break;
|
||||
case FLEE:
|
||||
ms.addTxt(MetaString::GENERAL_TXT,245);
|
||||
break;
|
||||
case JOIN_FOR_FREE:
|
||||
ms.addTxt(MetaString::GENERAL_TXT,243);
|
||||
break;
|
||||
default: //decision = cost in gold
|
||||
VLC->generaltexth->allTexts[244];
|
||||
ms << boost::to_string(boost::format(VLC->generaltexth->allTexts[244]) % decision);
|
||||
break;
|
||||
}
|
||||
|
||||
ms.toString(hoverName);
|
||||
}
|
||||
else
|
||||
{
|
||||
hoverName = getHoverText(hero->tempOwner);
|
||||
}
|
||||
|
||||
const JsonNode & texts = VLC->generaltexth->localizedTexts["adventureMap"]["monsterThreat"];
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "../mapObjects/CObjectClassesHandler.h"
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
#include "../CGeneralTextHandler.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "CMapEditManager.h"
|
||||
|
||||
SHeroName::SHeroName() : heroId(-1)
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
#include "../CStopWatch.h"
|
||||
#include "../filesystem/Filesystem.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CCreatureHandler.h"
|
||||
#include "../CGeneralTextHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../mapping/CCampaignHandler.h"
|
||||
#include "../NetPacks.h"
|
||||
|
@ -248,6 +248,7 @@ void registerTypesClientPacks1(Serializer &s)
|
||||
s.template registerType<CPackForClient, HeroVisit>();
|
||||
s.template registerType<CPackForClient, SetCommanderProperty>();
|
||||
s.template registerType<CPackForClient, ChangeObjectVisitors>();
|
||||
s.template registerType<CPackForClient, ShowWorldViewEx>();
|
||||
}
|
||||
|
||||
template<typename Serializer>
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../mapping/CCampaignHandler.h"
|
||||
#include "../NetPacks.h"
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../mapping/CCampaignHandler.h"
|
||||
#include "../NetPacks.h"
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../mapping/CCampaignHandler.h"
|
||||
#include "../NetPacks.h"
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../mapping/CCampaignHandler.h"
|
||||
#include "../NetPacks.h"
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../mapping/CCampaignHandler.h"
|
||||
#include "../NetPacks.h"
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../mapping/CCampaignHandler.h"
|
||||
#include "../NetPacks.h"
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CArtHandler.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CSpellHandler.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../mapping/CCampaignHandler.h"
|
||||
#include "../NetPacks.h"
|
||||
|
@ -17,7 +17,7 @@
|
||||
#include "../VCMI_Lib.h"
|
||||
#include "../CTownHandler.h"
|
||||
#include "../CCreatureHandler.h"
|
||||
#include "../CSpellHandler.h" //for choosing random spells
|
||||
#include "../spells/CSpellHandler.h" //for choosing random spells
|
||||
|
||||
#include "../mapObjects/CommonConstructors.h"
|
||||
#include "../mapObjects/MapObjects.h" //needed to resolve templates for CommonConstructors.h
|
||||
@ -2187,9 +2187,9 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
|
||||
}
|
||||
|
||||
//Pandora with 15 spells of certain school
|
||||
for (int i = 1; i <= 4; i++)
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
oi.generateObject = [i, gen]() -> CGObjectInstance *
|
||||
oi.generateObject = [i,gen]() -> CGObjectInstance *
|
||||
{
|
||||
auto obj = new CGPandoraBox();
|
||||
obj->ID = Obj::PANDORAS_BOX;
|
||||
@ -2198,27 +2198,9 @@ void CRmgTemplateZone::addAllPossibleObjects(CMapGenerator* gen)
|
||||
std::vector <CSpell *> spells;
|
||||
for (auto spell : VLC->spellh->objects)
|
||||
{
|
||||
if (!spell->isSpecialSpell())
|
||||
{
|
||||
bool school = false; //TODO: we could have better interface for iterating schools
|
||||
switch (i)
|
||||
{
|
||||
case 1:
|
||||
school = spell->air;
|
||||
break;
|
||||
case 2:
|
||||
school = spell->earth;
|
||||
break;
|
||||
case 3:
|
||||
school = spell->fire;
|
||||
break;
|
||||
case 4:
|
||||
school = spell->water;
|
||||
break;
|
||||
}
|
||||
if (school)
|
||||
spells.push_back(spell);
|
||||
}
|
||||
|
||||
if (!spell->isSpecialSpell() && spell->school[(ESpellSchool)i])
|
||||
spells.push_back(spell);
|
||||
}
|
||||
|
||||
RandomGeneratorUtil::randomShuffle(spells, gen->rand);
|
||||
|
272
lib/spells/AdventureSpellMechanics.cpp
Normal file
272
lib/spells/AdventureSpellMechanics.cpp
Normal file
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* AdventureSpellMechanics.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "AdventureSpellMechanics.h"
|
||||
|
||||
#include "../CRandomGenerator.h"
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
#include "../NetPacks.h"
|
||||
#include "../BattleState.h"
|
||||
#include "../CGameState.h"
|
||||
#include "../CGameInfoCallback.h"
|
||||
|
||||
///SummonBoatMechanics
|
||||
bool SummonBoatMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
|
||||
{
|
||||
const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||
//check if spell works at all
|
||||
if(env->getRandomGenerator().nextInt(99) >= owner->getPower(schoolLevel)) //power is % chance of success
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->tempOwner;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 336); //%s tried to summon a boat, but failed.
|
||||
iw.text.addReplacement(parameters.caster->name);
|
||||
env->sendAndApply(&iw);
|
||||
return true;
|
||||
}
|
||||
|
||||
//try to find unoccupied boat to summon
|
||||
const CGBoat * nearest = nullptr;
|
||||
double dist = 0;
|
||||
int3 summonPos = parameters.caster->bestLocation();
|
||||
if(summonPos.x < 0)
|
||||
{
|
||||
env->complain("There is no water tile available!");
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const CGObjectInstance * obj : env->getMap()->objects)
|
||||
{
|
||||
if(obj && obj->ID == Obj::BOAT)
|
||||
{
|
||||
const CGBoat *b = static_cast<const CGBoat*>(obj);
|
||||
if(b->hero)
|
||||
continue; //we're looking for unoccupied boat
|
||||
|
||||
double nDist = b->pos.dist2d(parameters.caster->getPosition());
|
||||
if(!nearest || nDist < dist) //it's first boat or closer than previous
|
||||
{
|
||||
nearest = b;
|
||||
dist = nDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(nullptr != nearest) //we found boat to summon
|
||||
{
|
||||
ChangeObjPos cop;
|
||||
cop.objid = nearest->id;
|
||||
cop.nPos = summonPos + int3(1,0,0);;
|
||||
cop.flags = 1;
|
||||
env->sendAndApply(&cop);
|
||||
}
|
||||
else if(schoolLevel < 2) //none or basic level -> cannot create boat :(
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->tempOwner;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 335); //There are no boats to summon.
|
||||
env->sendAndApply(&iw);
|
||||
}
|
||||
else //create boat
|
||||
{
|
||||
NewObject no;
|
||||
no.ID = Obj::BOAT;
|
||||
no.subID = parameters.caster->getBoatType();
|
||||
no.pos = summonPos + int3(1,0,0);;
|
||||
env->sendAndApply(&no);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
///ScuttleBoatMechanics
|
||||
bool ScuttleBoatMechanics::applyAdventureEffects(const SpellCastEnvironment* env, AdventureSpellCastParameters& parameters) const
|
||||
{
|
||||
const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||
//check if spell works at all
|
||||
if(env->getRandomGenerator().nextInt(99) >= owner->getPower(schoolLevel)) //power is % chance of success
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->tempOwner;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed
|
||||
iw.text.addReplacement(parameters.caster->name);
|
||||
env->sendAndApply(&iw);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!env->getMap()->isInTheMap(parameters.pos))
|
||||
{
|
||||
env->complain("Invalid dst tile for scuttle!");
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO: test range, visibility
|
||||
const TerrainTile *t = &env->getMap()->getTile(parameters.pos);
|
||||
if(!t->visitableObjects.size() || t->visitableObjects.back()->ID != Obj::BOAT)
|
||||
{
|
||||
env->complain("There is no boat to scuttle!");
|
||||
return false;
|
||||
}
|
||||
|
||||
RemoveObject ro;
|
||||
ro.id = t->visitableObjects.back()->id;
|
||||
env->sendAndApply(&ro);
|
||||
return true;
|
||||
}
|
||||
|
||||
///DimensionDoorMechanics
|
||||
bool DimensionDoorMechanics::applyAdventureEffects(const SpellCastEnvironment* env, AdventureSpellCastParameters& parameters) const
|
||||
{
|
||||
if(!env->getMap()->isInTheMap(parameters.pos))
|
||||
{
|
||||
env->complain("Destination is out of map!");
|
||||
return false;
|
||||
}
|
||||
|
||||
const TerrainTile * dest = env->getCb()->getTile(parameters.pos);
|
||||
const TerrainTile * curr = env->getCb()->getTile(parameters.caster->getSightCenter());
|
||||
|
||||
if(nullptr == dest)
|
||||
{
|
||||
env->complain("Destination tile doesn't exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(nullptr == curr)
|
||||
{
|
||||
env->complain("Source tile doesn't exist!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(parameters.caster->movement <= 0)
|
||||
{
|
||||
env->complain("Hero needs movement points to cast Dimension Door!");
|
||||
return false;
|
||||
}
|
||||
|
||||
const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||
|
||||
if(parameters.caster->getBonusesCount(Bonus::SPELL_EFFECT, SpellID::DIMENSION_DOOR) >= owner->getPower(schoolLevel)) //limit casts per turn
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->tempOwner;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 338); //%s is not skilled enough to cast this spell again today.
|
||||
iw.text.addReplacement(parameters.caster->name);
|
||||
env->sendAndApply(&iw);
|
||||
return true;
|
||||
}
|
||||
|
||||
GiveBonus gb;
|
||||
gb.id = parameters.caster->id.getNum();
|
||||
gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::NONE, Bonus::SPELL_EFFECT, 0, owner->id);
|
||||
env->sendAndApply(&gb);
|
||||
|
||||
if(!dest->isClear(curr)) //wrong dest tile
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = parameters.caster->tempOwner;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 70); //Dimension Door failed!
|
||||
env->sendAndApply(&iw);
|
||||
}
|
||||
else if(env->moveHero(parameters.caster->id, parameters.pos + parameters.caster->getVisitableOffset(), true))
|
||||
{
|
||||
SetMovePoints smp;
|
||||
smp.hid = parameters.caster->id;
|
||||
smp.val = std::max<ui32>(0, parameters.caster->movement - 300);
|
||||
env->sendAndApply(&smp);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
///TownPortalMechanics
|
||||
bool TownPortalMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters& parameters) const
|
||||
{
|
||||
if (!env->getMap()->isInTheMap(parameters.pos))
|
||||
{
|
||||
env->complain("Destination tile not present!");
|
||||
return false;
|
||||
}
|
||||
|
||||
TerrainTile tile = env->getMap()->getTile(parameters.pos);
|
||||
if (tile.visitableObjects.empty() || tile.visitableObjects.back()->ID != Obj::TOWN)
|
||||
{
|
||||
env->complain("Town not found for Town Portal!");
|
||||
return false;
|
||||
}
|
||||
|
||||
CGTownInstance * town = static_cast<CGTownInstance*>(tile.visitableObjects.back());
|
||||
if (town->tempOwner != parameters.caster->tempOwner)
|
||||
{
|
||||
env->complain("Can't teleport to another player!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (town->visitingHero)
|
||||
{
|
||||
env->complain("Can't teleport to occupied town!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (parameters.caster->getSpellSchoolLevel(owner) < 2)
|
||||
{
|
||||
si32 dist = town->pos.dist2dSQ(parameters.caster->pos);
|
||||
ObjectInstanceID nearest = town->id; //nearest town's ID
|
||||
for(const CGTownInstance * currTown : env->getCb()->getPlayer(parameters.caster->tempOwner)->towns)
|
||||
{
|
||||
si32 currDist = currTown->pos.dist2dSQ(parameters.caster->pos);
|
||||
if (currDist < dist)
|
||||
{
|
||||
nearest = currTown->id;
|
||||
dist = currDist;
|
||||
}
|
||||
}
|
||||
if (town->id != nearest)
|
||||
{
|
||||
env->complain("This hero can only teleport to nearest town!");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
env->moveHero(parameters.caster->id, town->visitablePos() + parameters.caster->getVisitableOffset() ,1);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ViewMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
|
||||
{
|
||||
ShowWorldViewEx pack;
|
||||
|
||||
pack.player = parameters.caster->tempOwner;
|
||||
|
||||
const int spellLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||
|
||||
for(const CGObjectInstance * obj : env->getMap()->objects)
|
||||
{
|
||||
//todo:we need to send only not visible objects
|
||||
|
||||
if(filterObject(obj, spellLevel))
|
||||
pack.objectPositions.push_back(ObjectPosInfo(obj));
|
||||
}
|
||||
|
||||
env->sendAndApply(&pack);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ViewAirMechanics::filterObject(const CGObjectInstance * obj, const int spellLevel) const
|
||||
{
|
||||
return (obj->ID == Obj::ARTIFACT) || (spellLevel>1 && obj->ID == Obj::HERO) || (spellLevel>2 && obj->ID == Obj::TOWN);
|
||||
}
|
||||
|
||||
bool ViewEarthMechanics::filterObject(const CGObjectInstance * obj, const int spellLevel) const
|
||||
{
|
||||
return (obj->ID == Obj::RESOURCE) || (spellLevel>1 && obj->ID == Obj::MINE);
|
||||
}
|
||||
|
75
lib/spells/AdventureSpellMechanics.h
Normal file
75
lib/spells/AdventureSpellMechanics.h
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* AdventureSpellMechanics.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CDefaultSpellMechanics.h"
|
||||
|
||||
class ISpellMechanics;
|
||||
class DefaultSpellMechanics;
|
||||
|
||||
class DLL_LINKAGE SummonBoatMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
SummonBoatMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
protected:
|
||||
bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE ScuttleBoatMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
ScuttleBoatMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
protected:
|
||||
bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE DimensionDoorMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
DimensionDoorMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
protected:
|
||||
bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE TownPortalMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
TownPortalMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
protected:
|
||||
bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE ViewMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
ViewMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
protected:
|
||||
bool applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override;
|
||||
virtual bool filterObject(const CGObjectInstance * obj, const int spellLevel) const = 0;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE ViewAirMechanics : public ViewMechanics
|
||||
{
|
||||
public:
|
||||
ViewAirMechanics(CSpell * s): ViewMechanics(s){};
|
||||
protected:
|
||||
bool filterObject(const CGObjectInstance * obj, const int spellLevel) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE ViewEarthMechanics : public ViewMechanics
|
||||
{
|
||||
public:
|
||||
ViewEarthMechanics(CSpell * s): ViewMechanics(s){};
|
||||
protected:
|
||||
bool filterObject(const CGObjectInstance * obj, const int spellLevel) const override;
|
||||
};
|
||||
|
||||
|
417
lib/spells/BattleSpellMechanics.cpp
Normal file
417
lib/spells/BattleSpellMechanics.cpp
Normal file
@ -0,0 +1,417 @@
|
||||
/*
|
||||
* BattleSpellMechanics.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "BattleSpellMechanics.h"
|
||||
|
||||
#include "../NetPacks.h"
|
||||
#include "../BattleState.h"
|
||||
|
||||
///ChainLightningMechanics
|
||||
std::set<const CStack *> ChainLightningMechanics::getAffectedStacks(SpellTargetingContext & ctx) const
|
||||
{
|
||||
std::set<const CStack* > attackedCres;
|
||||
|
||||
std::set<BattleHex> possibleHexes;
|
||||
for(auto stack : ctx.cb->battleGetAllStacks())
|
||||
{
|
||||
if(stack->isValidTarget())
|
||||
{
|
||||
for(auto hex : stack->getHexes())
|
||||
{
|
||||
possibleHexes.insert (hex);
|
||||
}
|
||||
}
|
||||
}
|
||||
int targetsOnLevel[4] = {4, 4, 5, 5};
|
||||
|
||||
BattleHex lightningHex = ctx.destination;
|
||||
for(int i = 0; i < targetsOnLevel[ctx.schoolLvl]; ++i)
|
||||
{
|
||||
auto stack = ctx.cb->battleGetStackByPos(lightningHex, true);
|
||||
if(!stack)
|
||||
break;
|
||||
attackedCres.insert (stack);
|
||||
for(auto hex : stack->getHexes())
|
||||
{
|
||||
possibleHexes.erase(hex); //can't hit same place twice
|
||||
}
|
||||
if(possibleHexes.empty()) //not enough targets
|
||||
break;
|
||||
lightningHex = BattleHex::getClosestTile(stack->attackerOwned, ctx.destination, possibleHexes);
|
||||
}
|
||||
|
||||
return attackedCres;
|
||||
}
|
||||
|
||||
///CloneMechanics
|
||||
void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
const CStack * clonedStack = nullptr;
|
||||
if(ctx.attackedCres.size())
|
||||
clonedStack = *ctx.attackedCres.begin();
|
||||
if(!clonedStack)
|
||||
{
|
||||
env->complain ("No target stack to clone!");
|
||||
return;
|
||||
}
|
||||
const int attacker = !(bool)parameters.casterSide;
|
||||
|
||||
BattleStackAdded bsa;
|
||||
bsa.creID = clonedStack->type->idNumber;
|
||||
bsa.attacker = attacker;
|
||||
bsa.summoned = true;
|
||||
bsa.pos = parameters.cb->getAvaliableHex(bsa.creID, attacker); //TODO: unify it
|
||||
bsa.amount = clonedStack->count;
|
||||
env->sendAndApply(&bsa);
|
||||
|
||||
BattleSetStackProperty ssp;
|
||||
ssp.stackID = bsa.newStackID;//we know stack ID after apply
|
||||
ssp.which = BattleSetStackProperty::CLONED;
|
||||
ssp.val = 0;
|
||||
ssp.absolute = 1;
|
||||
env->sendAndApply(&ssp);
|
||||
}
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
||||
{
|
||||
//can't clone already cloned creature
|
||||
if(vstd::contains(obj->state, EBattleStackState::CLONED))
|
||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||
//TODO: how about stacks casting Clone?
|
||||
//currently Clone casted by stack is assumed Expert level
|
||||
ui8 schoolLevel;
|
||||
if(caster)
|
||||
{
|
||||
schoolLevel = caster->getSpellSchoolLevel(owner);
|
||||
}
|
||||
else
|
||||
{
|
||||
schoolLevel = 3;
|
||||
}
|
||||
|
||||
if(schoolLevel < 3)
|
||||
{
|
||||
int maxLevel = (std::max(schoolLevel, (ui8)1) + 4);
|
||||
int creLevel = obj->getCreature()->level;
|
||||
if(maxLevel < creLevel) //tier 1-5 for basic, 1-6 for advanced, any level for expert
|
||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||
}
|
||||
//use default algorithm only if there is no mechanics-related problem
|
||||
return DefaultSpellMechanics::isImmuneByStack(caster, obj);
|
||||
}
|
||||
|
||||
///CureMechanics
|
||||
void CureMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
|
||||
{
|
||||
DefaultSpellMechanics::applyBattle(battle, packet);
|
||||
|
||||
for(auto stackID : packet->affectedCres)
|
||||
{
|
||||
if(vstd::contains(packet->resisted, stackID))
|
||||
{
|
||||
logGlobal->errorStream() << "Resistance to positive spell CURE";
|
||||
continue;
|
||||
}
|
||||
|
||||
CStack *s = battle->getStack(stackID);
|
||||
s->popBonuses([&](const Bonus *b) -> bool
|
||||
{
|
||||
if(b->source == Bonus::SPELL_EFFECT)
|
||||
{
|
||||
CSpell * sp = SpellID(b->sid).toSpell();
|
||||
return sp->isNegative();
|
||||
}
|
||||
return false; //not a spell effect
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
///DispellMechanics
|
||||
void DispellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
|
||||
{
|
||||
DefaultSpellMechanics::applyBattle(battle, packet);
|
||||
|
||||
for(auto stackID : packet->affectedCres)
|
||||
{
|
||||
if(vstd::contains(packet->resisted, stackID))
|
||||
continue;
|
||||
|
||||
CStack *s = battle->getStack(stackID);
|
||||
s->popBonuses([&](const Bonus *b) -> bool
|
||||
{
|
||||
return Selector::sourceType(Bonus::SPELL_EFFECT)(b);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///HypnotizeMechanics
|
||||
ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
||||
{
|
||||
if(nullptr != caster) //do not resist hypnotize casted after attack, for example
|
||||
{
|
||||
//TODO: what with other creatures casting hypnotize, Faerie Dragons style?
|
||||
ui64 subjectHealth = (obj->count - 1) * obj->MaxHealth() + obj->firstHPleft;
|
||||
//apply 'damage' bonus for hypnotize, including hero specialty
|
||||
ui64 maxHealth = owner->calculateBonus(caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
|
||||
* owner->power + owner->getPower(caster->getSpellSchoolLevel(owner)), caster, obj);
|
||||
if (subjectHealth > maxHealth)
|
||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||
}
|
||||
return DefaultSpellMechanics::isImmuneByStack(caster, obj);
|
||||
}
|
||||
|
||||
///ObstacleMechanics
|
||||
void ObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
auto placeObstacle = [&, this](BattleHex pos)
|
||||
{
|
||||
static int obstacleIdToGive = parameters.cb->obstacles.size()
|
||||
? (parameters.cb->obstacles.back()->uniqueID+1)
|
||||
: 0;
|
||||
|
||||
auto obstacle = make_shared<SpellCreatedObstacle>();
|
||||
switch(owner->id) // :/
|
||||
{
|
||||
case SpellID::QUICKSAND:
|
||||
obstacle->obstacleType = CObstacleInstance::QUICKSAND;
|
||||
obstacle->turnsRemaining = -1;
|
||||
obstacle->visibleForAnotherSide = false;
|
||||
break;
|
||||
case SpellID::LAND_MINE:
|
||||
obstacle->obstacleType = CObstacleInstance::LAND_MINE;
|
||||
obstacle->turnsRemaining = -1;
|
||||
obstacle->visibleForAnotherSide = false;
|
||||
break;
|
||||
case SpellID::FIRE_WALL:
|
||||
obstacle->obstacleType = CObstacleInstance::FIRE_WALL;
|
||||
obstacle->turnsRemaining = 2;
|
||||
obstacle->visibleForAnotherSide = true;
|
||||
break;
|
||||
case SpellID::FORCE_FIELD:
|
||||
obstacle->obstacleType = CObstacleInstance::FORCE_FIELD;
|
||||
obstacle->turnsRemaining = 2;
|
||||
obstacle->visibleForAnotherSide = true;
|
||||
break;
|
||||
default:
|
||||
//this function cannot be used with spells that do not create obstacles
|
||||
assert(0);
|
||||
}
|
||||
|
||||
obstacle->pos = pos;
|
||||
obstacle->casterSide = parameters.casterSide;
|
||||
obstacle->ID = owner->id;
|
||||
obstacle->spellLevel = parameters.spellLvl;
|
||||
obstacle->casterSpellPower = parameters.usedSpellPower;
|
||||
obstacle->uniqueID = obstacleIdToGive++;
|
||||
|
||||
BattleObstaclePlaced bop;
|
||||
bop.obstacle = obstacle;
|
||||
env->sendAndApply(&bop);
|
||||
};
|
||||
|
||||
switch(owner->id)
|
||||
{
|
||||
case SpellID::QUICKSAND:
|
||||
case SpellID::LAND_MINE:
|
||||
{
|
||||
std::vector<BattleHex> availableTiles;
|
||||
for(int i = 0; i < GameConstants::BFIELD_SIZE; i += 1)
|
||||
{
|
||||
BattleHex hex = i;
|
||||
if(hex.getX() > 2 && hex.getX() < 14 && !(parameters.cb->battleGetStackByPos(hex, false)) && !(parameters.cb->battleGetObstacleOnPos(hex, false)))
|
||||
availableTiles.push_back(hex);
|
||||
}
|
||||
boost::range::random_shuffle(availableTiles);
|
||||
|
||||
const int patchesForSkill[] = {4, 4, 6, 8};
|
||||
const int patchesToPut = std::min<int>(patchesForSkill[parameters.spellLvl], availableTiles.size());
|
||||
|
||||
//land mines or quicksand patches are handled as spell created obstacles
|
||||
for (int i = 0; i < patchesToPut; i++)
|
||||
placeObstacle(availableTiles.at(i));
|
||||
}
|
||||
|
||||
break;
|
||||
case SpellID::FORCE_FIELD:
|
||||
placeObstacle(parameters.destination);
|
||||
break;
|
||||
case SpellID::FIRE_WALL:
|
||||
{
|
||||
//fire wall is build from multiple obstacles - one fire piece for each affected hex
|
||||
auto affectedHexes = owner->rangeInHexes(parameters.destination, parameters.spellLvl, parameters.casterSide);
|
||||
for(BattleHex hex : affectedHexes)
|
||||
placeObstacle(hex);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///WallMechanics
|
||||
std::vector<BattleHex> WallMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes) const
|
||||
{
|
||||
std::vector<BattleHex> ret;
|
||||
|
||||
//Special case - shape of obstacle depends on caster's side
|
||||
//TODO make it possible through spell config
|
||||
|
||||
BattleHex::EDir firstStep, secondStep;
|
||||
if(side)
|
||||
{
|
||||
firstStep = BattleHex::TOP_LEFT;
|
||||
secondStep = BattleHex::TOP_RIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
firstStep = BattleHex::TOP_RIGHT;
|
||||
secondStep = BattleHex::TOP_LEFT;
|
||||
}
|
||||
|
||||
//Adds hex to the ret if it's valid. Otherwise sets output arg flag if given.
|
||||
auto addIfValid = [&](BattleHex hex)
|
||||
{
|
||||
if(hex.isValid())
|
||||
ret.push_back(hex);
|
||||
else if(outDroppedHexes)
|
||||
*outDroppedHexes = true;
|
||||
};
|
||||
|
||||
ret.push_back(centralHex);
|
||||
addIfValid(centralHex.moveInDir(firstStep, false));
|
||||
if(schoolLvl >= 2) //advanced versions of fire wall / force field cotnains of 3 hexes
|
||||
addIfValid(centralHex.moveInDir(secondStep, false)); //moveInDir function modifies subject hex
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
///RemoveObstacleMechanics
|
||||
void RemoveObstacleMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
if(auto obstacleToRemove = parameters.cb->battleGetObstacleOnPos(parameters.destination, false))
|
||||
{
|
||||
ObstaclesRemoved obr;
|
||||
obr.obstacles.insert(obstacleToRemove->uniqueID);
|
||||
env->sendAndApply(&obr);
|
||||
}
|
||||
else
|
||||
env->complain("There's no obstacle to remove!");
|
||||
}
|
||||
|
||||
///SpecialRisingSpellMechanics
|
||||
void SacrificeMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
RisingSpellMechanics::applyBattleEffects(env, parameters, ctx);
|
||||
|
||||
if(parameters.selectedStack == parameters.cb->battleActiveStack())
|
||||
//set another active stack than the one removed, or bad things will happen
|
||||
//TODO: make that part of BattleStacksRemoved? what about client update?
|
||||
{
|
||||
//makeStackDoNothing(gs->curB->getStack (selectedStack));
|
||||
|
||||
BattleSetActiveStack sas;
|
||||
|
||||
//std::vector<const CStack *> hlp;
|
||||
//battleGetStackQueue(hlp, 1, selectedStack); //next after this one
|
||||
|
||||
//if(hlp.size())
|
||||
//{
|
||||
// sas.stack = hlp[0]->ID;
|
||||
//}
|
||||
//else
|
||||
// complain ("No new stack to activate!");
|
||||
sas.stack = parameters.cb->getNextStack()->ID; //why the hell next stack has same ID as current?
|
||||
env->sendAndApply(&sas);
|
||||
|
||||
}
|
||||
BattleStacksRemoved bsr;
|
||||
bsr.stackIDs.insert(parameters.selectedStack->ID); //somehow it works for teleport?
|
||||
env->sendAndApply(&bsr);
|
||||
}
|
||||
|
||||
|
||||
///SpecialRisingSpellMechanics
|
||||
ESpellCastProblem::ESpellCastProblem SpecialRisingSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
||||
{
|
||||
// following does apply to resurrect and animate dead(?) only
|
||||
// for sacrifice health calculation and health limit check don't matter
|
||||
|
||||
if(obj->count >= obj->baseAmount)
|
||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||
|
||||
if(caster) //FIXME: Archangels can cast immune stack
|
||||
{
|
||||
auto maxHealth = calculateHealedHP(caster, obj, nullptr);
|
||||
if (maxHealth < obj->MaxHealth()) //must be able to rise at least one full creature
|
||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||
}
|
||||
|
||||
return DefaultSpellMechanics::isImmuneByStack(caster,obj);
|
||||
}
|
||||
|
||||
///SummonMechanics
|
||||
void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
//todo: make configurable
|
||||
CreatureID creID = CreatureID::NONE;
|
||||
switch(owner->id)
|
||||
{
|
||||
case SpellID::SUMMON_FIRE_ELEMENTAL:
|
||||
creID = CreatureID::FIRE_ELEMENTAL;
|
||||
break;
|
||||
case SpellID::SUMMON_EARTH_ELEMENTAL:
|
||||
creID = CreatureID::EARTH_ELEMENTAL;
|
||||
break;
|
||||
case SpellID::SUMMON_WATER_ELEMENTAL:
|
||||
creID = CreatureID::WATER_ELEMENTAL;
|
||||
break;
|
||||
case SpellID::SUMMON_AIR_ELEMENTAL:
|
||||
creID = CreatureID::AIR_ELEMENTAL;
|
||||
break;
|
||||
default:
|
||||
env->complain("Unable to determine summoned creature");
|
||||
return;
|
||||
}
|
||||
|
||||
BattleStackAdded bsa;
|
||||
bsa.creID = creID;
|
||||
bsa.attacker = !(bool)parameters.casterSide;
|
||||
bsa.summoned = true;
|
||||
bsa.pos = parameters.cb->getAvaliableHex(creID, !(bool)parameters.casterSide); //TODO: unify it
|
||||
|
||||
//TODO stack casting -> probably power will be zero; set the proper number of creatures manually
|
||||
int percentBonus = parameters.caster ? parameters.caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, owner->id.toEnum()) : 0;
|
||||
|
||||
bsa.amount = parameters.usedSpellPower
|
||||
* owner->getPower(parameters.spellLvl)
|
||||
* (100 + percentBonus) / 100.0; //new feature - percentage bonus
|
||||
if(bsa.amount)
|
||||
env->sendAndApply(&bsa);
|
||||
else
|
||||
env->complain("Summoning didn't summon any!");
|
||||
}
|
||||
|
||||
|
||||
///TeleportMechanics
|
||||
void TeleportMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
BattleStackMoved bsm;
|
||||
bsm.distance = -1;
|
||||
bsm.stack = parameters.selectedStack->ID;
|
||||
std::vector<BattleHex> tiles;
|
||||
tiles.push_back(parameters.destination);
|
||||
bsm.tilesToMove = tiles;
|
||||
bsm.teleporting = true;
|
||||
env->sendAndApply(&bsm);
|
||||
}
|
||||
|
116
lib/spells/BattleSpellMechanics.h
Normal file
116
lib/spells/BattleSpellMechanics.h
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* BattleSpellMechanics.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CDefaultSpellMechanics.h"
|
||||
|
||||
class DLL_LINKAGE ChainLightningMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
ChainLightningMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CloneMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
CloneMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE CureMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
CureMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
|
||||
void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE DispellMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
DispellMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
|
||||
void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE HypnotizeMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
HypnotizeMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE ObstacleMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
ObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE WallMechanics : public ObstacleMechanics
|
||||
{
|
||||
public:
|
||||
WallMechanics(CSpell * s): ObstacleMechanics(s){};
|
||||
std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE RemoveObstacleMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
RemoveObstacleMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
};
|
||||
|
||||
///all rising spells
|
||||
class DLL_LINKAGE RisingSpellMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
RisingSpellMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
|
||||
};
|
||||
|
||||
class DLL_LINKAGE SacrificeMechanics : public RisingSpellMechanics
|
||||
{
|
||||
public:
|
||||
SacrificeMechanics(CSpell * s): RisingSpellMechanics(s){};
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
};
|
||||
|
||||
///all rising spells but SACRIFICE
|
||||
class DLL_LINKAGE SpecialRisingSpellMechanics : public RisingSpellMechanics
|
||||
{
|
||||
public:
|
||||
SpecialRisingSpellMechanics(CSpell * s): RisingSpellMechanics(s){};
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE SummonMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
SummonMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE TeleportMechanics: public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
TeleportMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
};
|
719
lib/spells/CDefaultSpellMechanics.cpp
Normal file
719
lib/spells/CDefaultSpellMechanics.cpp
Normal file
@ -0,0 +1,719 @@
|
||||
/*
|
||||
* CDefaultSpellMechanics.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CDefaultSpellMechanics.h"
|
||||
|
||||
#include "../NetPacks.h"
|
||||
#include "../BattleState.h"
|
||||
|
||||
namespace SRSLPraserHelpers
|
||||
{
|
||||
static int XYToHex(int x, int y)
|
||||
{
|
||||
return x + GameConstants::BFIELD_WIDTH * y;
|
||||
}
|
||||
|
||||
static int XYToHex(std::pair<int, int> xy)
|
||||
{
|
||||
return XYToHex(xy.first, xy.second);
|
||||
}
|
||||
|
||||
static int hexToY(int battleFieldPosition)
|
||||
{
|
||||
return battleFieldPosition/GameConstants::BFIELD_WIDTH;
|
||||
}
|
||||
|
||||
static int hexToX(int battleFieldPosition)
|
||||
{
|
||||
int pos = battleFieldPosition - hexToY(battleFieldPosition) * GameConstants::BFIELD_WIDTH;
|
||||
return pos;
|
||||
}
|
||||
|
||||
static std::pair<int, int> hexToPair(int battleFieldPosition)
|
||||
{
|
||||
return std::make_pair(hexToX(battleFieldPosition), hexToY(battleFieldPosition));
|
||||
}
|
||||
|
||||
//moves hex by one hex in given direction
|
||||
//0 - left top, 1 - right top, 2 - right, 3 - right bottom, 4 - left bottom, 5 - left
|
||||
static std::pair<int, int> gotoDir(int x, int y, int direction)
|
||||
{
|
||||
switch(direction)
|
||||
{
|
||||
case 0: //top left
|
||||
return std::make_pair((y%2) ? x-1 : x, y-1);
|
||||
case 1: //top right
|
||||
return std::make_pair((y%2) ? x : x+1, y-1);
|
||||
case 2: //right
|
||||
return std::make_pair(x+1, y);
|
||||
case 3: //right bottom
|
||||
return std::make_pair((y%2) ? x : x+1, y+1);
|
||||
case 4: //left bottom
|
||||
return std::make_pair((y%2) ? x-1 : x, y+1);
|
||||
case 5: //left
|
||||
return std::make_pair(x-1, y);
|
||||
default:
|
||||
throw std::runtime_error("Disaster: wrong direction in SRSLPraserHelpers::gotoDir!\n");
|
||||
}
|
||||
}
|
||||
|
||||
static std::pair<int, int> gotoDir(std::pair<int, int> xy, int direction)
|
||||
{
|
||||
return gotoDir(xy.first, xy.second, direction);
|
||||
}
|
||||
|
||||
static bool isGoodHex(std::pair<int, int> xy)
|
||||
{
|
||||
return xy.first >=0 && xy.first < GameConstants::BFIELD_WIDTH && xy.second >= 0 && xy.second < GameConstants::BFIELD_HEIGHT;
|
||||
}
|
||||
|
||||
//helper function for rangeInHexes
|
||||
static std::set<ui16> getInRange(unsigned int center, int low, int high)
|
||||
{
|
||||
std::set<ui16> ret;
|
||||
if(low == 0)
|
||||
{
|
||||
ret.insert(center);
|
||||
}
|
||||
|
||||
std::pair<int, int> mainPointForLayer[6]; //A, B, C, D, E, F points
|
||||
for(auto & elem : mainPointForLayer)
|
||||
elem = hexToPair(center);
|
||||
|
||||
for(int it=1; it<=high; ++it) //it - distance to the center
|
||||
{
|
||||
for(int b=0; b<6; ++b)
|
||||
mainPointForLayer[b] = gotoDir(mainPointForLayer[b], b);
|
||||
|
||||
if(it>=low)
|
||||
{
|
||||
std::pair<int, int> curHex;
|
||||
|
||||
//adding lines (A-b, B-c, C-d, etc)
|
||||
for(int v=0; v<6; ++v)
|
||||
{
|
||||
curHex = mainPointForLayer[v];
|
||||
for(int h=0; h<it; ++h)
|
||||
{
|
||||
if(isGoodHex(curHex))
|
||||
ret.insert(XYToHex(curHex));
|
||||
curHex = gotoDir(curHex, (v+2)%6);
|
||||
}
|
||||
}
|
||||
|
||||
} //if(it>=low)
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
///DefaultSpellMechanics
|
||||
void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
|
||||
{
|
||||
if (packet->castedByHero)
|
||||
{
|
||||
if (packet->side < 2)
|
||||
{
|
||||
battle->sides[packet->side].castSpellsCount++;
|
||||
}
|
||||
}
|
||||
|
||||
//handle countering spells
|
||||
for(auto stackID : packet->affectedCres)
|
||||
{
|
||||
if(vstd::contains(packet->resisted, stackID))
|
||||
continue;
|
||||
|
||||
CStack * s = battle->getStack(stackID);
|
||||
s->popBonuses([&](const Bonus * b) -> bool
|
||||
{
|
||||
//check for each bonus if it should be removed
|
||||
const bool isSpellEffect = Selector::sourceType(Bonus::SPELL_EFFECT)(b);
|
||||
const int spellID = isSpellEffect ? b->sid : -1;
|
||||
|
||||
return isSpellEffect && vstd::contains(owner->counteredSpells, spellID);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool DefaultSpellMechanics::adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
|
||||
{
|
||||
if(!owner->isAdventureSpell())
|
||||
{
|
||||
env->complain("Attempt to cast non adventure spell in adventure mode");
|
||||
return false;
|
||||
}
|
||||
|
||||
const CGHeroInstance * caster = parameters.caster;
|
||||
const int cost = caster->getSpellCost(owner);
|
||||
|
||||
if(!caster->canCastThisSpell(owner))
|
||||
{
|
||||
env->complain("Hero cannot cast this spell!");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(caster->mana < cost)
|
||||
{
|
||||
env->complain("Hero doesn't have enough spell points to cast this spell!");
|
||||
return false;
|
||||
}
|
||||
|
||||
{
|
||||
AdvmapSpellCast asc;
|
||||
asc.caster = caster;
|
||||
asc.spellID = owner->id;
|
||||
env->sendAndApply(&asc);
|
||||
}
|
||||
|
||||
if(applyAdventureEffects(env, parameters))
|
||||
{
|
||||
SetMana sm;
|
||||
sm.hid = caster->id;
|
||||
sm.absolute = false;
|
||||
sm.val = -cost;
|
||||
env->sendAndApply(&sm);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DefaultSpellMechanics::applyAdventureEffects(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const
|
||||
{
|
||||
if(owner->hasEffects())
|
||||
{
|
||||
const int schoolLevel = parameters.caster->getSpellSchoolLevel(owner);
|
||||
|
||||
std::vector<Bonus> bonuses;
|
||||
|
||||
owner->getEffects(bonuses, schoolLevel);
|
||||
|
||||
for(Bonus b : bonuses)
|
||||
{
|
||||
GiveBonus gb;
|
||||
gb.id = parameters.caster->id.getNum();
|
||||
gb.bonus = b;
|
||||
env->sendAndApply(&gb);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//There is no generic algorithm of adventure cast
|
||||
env->complain("Unimplemented adventure spell");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const
|
||||
{
|
||||
BattleSpellCast sc;
|
||||
sc.side = parameters.casterSide;
|
||||
sc.id = owner->id;
|
||||
sc.skill = parameters.spellLvl;
|
||||
sc.tile = parameters.destination;
|
||||
sc.dmgToDisplay = 0;
|
||||
sc.castedByHero = nullptr != parameters.caster;
|
||||
sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1);
|
||||
sc.manaGained = 0;
|
||||
|
||||
int spellCost = 0;
|
||||
|
||||
//calculate spell cost
|
||||
if(parameters.caster)
|
||||
{
|
||||
spellCost = parameters.cb->battleGetSpellCost(owner, parameters.caster);
|
||||
|
||||
if(parameters.secHero && parameters.mode == ECastingMode::HERO_CASTING) //handle mana channel
|
||||
{
|
||||
int manaChannel = 0;
|
||||
for(const CStack * stack : parameters.cb->battleGetAllStacks(true)) //TODO: shouldn't bonus system handle it somehow?
|
||||
{
|
||||
if(stack->owner == parameters.secHero->tempOwner)
|
||||
{
|
||||
vstd::amax(manaChannel, stack->valOfBonuses(Bonus::MANA_CHANNELING));
|
||||
}
|
||||
}
|
||||
sc.manaGained = (manaChannel * spellCost) / 100;
|
||||
}
|
||||
}
|
||||
|
||||
//calculating affected creatures for all spells
|
||||
//must be vector, as in Chain Lightning order matters
|
||||
std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector
|
||||
|
||||
auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.casterColor, parameters.spellLvl, parameters.destination, parameters.caster);
|
||||
std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres));
|
||||
|
||||
for (auto cre : attackedCres)
|
||||
{
|
||||
sc.affectedCres.insert(cre->ID);
|
||||
}
|
||||
|
||||
//checking if creatures resist
|
||||
//resistance is applied only to negative spells
|
||||
if(owner->isNegative())
|
||||
{
|
||||
for(auto s : attackedCres)
|
||||
{
|
||||
const int prob = std::min((s)->magicResistance(), 100); //probability of resistance in %
|
||||
|
||||
if(env->getRandomGenerator().nextInt(99) < prob)
|
||||
{
|
||||
sc.resisted.push_back(s->ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StacksInjured si;
|
||||
SpellCastContext ctx(attackedCres, sc, si);
|
||||
|
||||
applyBattleEffects(env, parameters, ctx);
|
||||
|
||||
env->sendAndApply(&sc);
|
||||
|
||||
//spend mana
|
||||
if(parameters.caster)
|
||||
{
|
||||
SetMana sm;
|
||||
sm.absolute = false;
|
||||
|
||||
sm.hid = parameters.caster->id;
|
||||
sm.val = -spellCost;
|
||||
|
||||
env->sendAndApply(&sm);
|
||||
|
||||
if(sc.manaGained > 0)
|
||||
{
|
||||
assert(parameters.secHero);
|
||||
|
||||
sm.hid = parameters.secHero->id;
|
||||
sm.val = sc.manaGained;
|
||||
env->sendAndApply(&sm);
|
||||
}
|
||||
}
|
||||
|
||||
if(!si.stacks.empty()) //after spellcast info shows
|
||||
env->sendAndApply(&si);
|
||||
|
||||
//reduce number of casts remaining
|
||||
//TODO: this should be part of BattleSpellCast apply
|
||||
if (parameters.mode == ECastingMode::CREATURE_ACTIVE_CASTING || parameters.mode == ECastingMode::ENCHANTER_CASTING)
|
||||
{
|
||||
assert(parameters.casterStack);
|
||||
|
||||
BattleSetStackProperty ssp;
|
||||
ssp.stackID = parameters.casterStack->ID;
|
||||
ssp.which = BattleSetStackProperty::CASTS;
|
||||
ssp.val = -1;
|
||||
ssp.absolute = false;
|
||||
env->sendAndApply(&ssp);
|
||||
}
|
||||
|
||||
//Magic Mirror effect
|
||||
if(owner->isNegative() && parameters.mode != ECastingMode::MAGIC_MIRROR && owner->level && owner->getLevelInfo(0).range == "0") //it is actual spell and can be reflected to single target, no recurrence
|
||||
{
|
||||
for(auto & attackedCre : attackedCres)
|
||||
{
|
||||
int mirrorChance = (attackedCre)->valOfBonuses(Bonus::MAGIC_MIRROR);
|
||||
if(mirrorChance > env->getRandomGenerator().nextInt(99))
|
||||
{
|
||||
std::vector<const CStack *> mirrorTargets;
|
||||
auto battleStacks = parameters.cb->battleGetAllStacks(true);
|
||||
for(auto & battleStack : battleStacks)
|
||||
{
|
||||
if(battleStack->owner == parameters.casterColor) //get enemy stacks which can be affected by this spell
|
||||
{
|
||||
if (ESpellCastProblem::OK == owner->isImmuneByStack(nullptr, battleStack))
|
||||
mirrorTargets.push_back(battleStack);
|
||||
}
|
||||
}
|
||||
if(!mirrorTargets.empty())
|
||||
{
|
||||
int targetHex = (*RandomGeneratorUtil::nextItem(mirrorTargets, env->getRandomGenerator()))->position;
|
||||
|
||||
BattleSpellCastParameters mirrorParameters = parameters;
|
||||
mirrorParameters.spellLvl = 0;
|
||||
mirrorParameters.casterSide = 1-parameters.casterSide;
|
||||
mirrorParameters.casterColor = (attackedCre)->owner;
|
||||
mirrorParameters.caster = nullptr;
|
||||
mirrorParameters.destination = targetHex;
|
||||
mirrorParameters.secHero = parameters.caster;
|
||||
mirrorParameters.mode = ECastingMode::MAGIC_MIRROR;
|
||||
mirrorParameters.casterStack = (attackedCre);
|
||||
mirrorParameters.selectedStack = nullptr;
|
||||
|
||||
battleCast(env, mirrorParameters);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DefaultSpellMechanics::calculateDuration(const CGHeroInstance * caster, int usedSpellPower) const
|
||||
{
|
||||
if(!caster)
|
||||
{
|
||||
if (!usedSpellPower)
|
||||
return 3; //default duration of all creature spells
|
||||
else
|
||||
return usedSpellPower; //use creature spell power
|
||||
}
|
||||
switch(owner->id)
|
||||
{
|
||||
case SpellID::FRENZY:
|
||||
return 1;
|
||||
default: //other spells
|
||||
return caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + caster->valOfBonuses(Bonus::SPELL_DURATION);
|
||||
}
|
||||
}
|
||||
|
||||
ui32 DefaultSpellMechanics::calculateHealedHP(const CGHeroInstance* caster, const CStack* stack, const CStack* sacrificedStack) const
|
||||
{
|
||||
int healedHealth;
|
||||
|
||||
if(!owner->isHealingSpell())
|
||||
{
|
||||
logGlobal->errorStream() << "calculateHealedHP called for nonhealing spell "<< owner->name;
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int spellPowerSkill = caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER);
|
||||
const int levelPower = owner->getPower(caster->getSpellSchoolLevel(owner));
|
||||
|
||||
if (owner->id == SpellID::SACRIFICE && sacrificedStack)
|
||||
healedHealth = (spellPowerSkill + sacrificedStack->MaxHealth() + levelPower) * sacrificedStack->count;
|
||||
else
|
||||
healedHealth = spellPowerSkill * owner->power + levelPower; //???
|
||||
healedHealth = owner->calculateBonus(healedHealth, caster, stack);
|
||||
return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (owner->isRisingSpell() ? stack->baseAmount * stack->MaxHealth() : 0));
|
||||
}
|
||||
|
||||
|
||||
void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
//applying effects
|
||||
if(owner->isOffensiveSpell())
|
||||
{
|
||||
int spellDamage = 0;
|
||||
if(parameters.casterStack && parameters.mode != ECastingMode::MAGIC_MIRROR)
|
||||
{
|
||||
int unitSpellPower = parameters.casterStack->valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, owner->id.toEnum());
|
||||
if(unitSpellPower)
|
||||
ctx.sc.dmgToDisplay = spellDamage = parameters.casterStack->count * unitSpellPower; //TODO: handle immunities
|
||||
else //Faerie Dragon
|
||||
{
|
||||
parameters.usedSpellPower = parameters.casterStack->valOfBonuses(Bonus::CREATURE_SPELL_POWER) * parameters.casterStack->count / 100;
|
||||
ctx.sc.dmgToDisplay = 0;
|
||||
}
|
||||
}
|
||||
int chainLightningModifier = 0;
|
||||
for(auto & attackedCre : ctx.attackedCres)
|
||||
{
|
||||
if(vstd::contains(ctx.sc.resisted, (attackedCre)->ID)) //this creature resisted the spell
|
||||
continue;
|
||||
|
||||
BattleStackAttacked bsa;
|
||||
if(spellDamage)
|
||||
bsa.damageAmount = spellDamage >> chainLightningModifier;
|
||||
else
|
||||
bsa.damageAmount = owner->calculateDamage(parameters.caster, attackedCre, parameters.spellLvl, parameters.usedSpellPower) >> chainLightningModifier;
|
||||
|
||||
ctx.sc.dmgToDisplay += bsa.damageAmount;
|
||||
|
||||
bsa.stackAttacked = (attackedCre)->ID;
|
||||
if(parameters.mode == ECastingMode::ENCHANTER_CASTING) //multiple damage spells cast
|
||||
bsa.attackerID = parameters.casterStack->ID;
|
||||
else
|
||||
bsa.attackerID = -1;
|
||||
(attackedCre)->prepareAttacked(bsa, env->getRandomGenerator());
|
||||
ctx.si.stacks.push_back(bsa);
|
||||
|
||||
if(owner->id == SpellID::CHAIN_LIGHTNING)
|
||||
++chainLightningModifier;
|
||||
}
|
||||
}
|
||||
|
||||
if(owner->hasEffects())
|
||||
{
|
||||
int stackSpellPower = 0;
|
||||
if(parameters.casterStack && parameters.mode != ECastingMode::MAGIC_MIRROR)
|
||||
{
|
||||
stackSpellPower = parameters.casterStack->valOfBonuses(Bonus::CREATURE_ENCHANT_POWER);
|
||||
}
|
||||
SetStackEffect sse;
|
||||
Bonus pseudoBonus;
|
||||
pseudoBonus.sid = owner->id;
|
||||
pseudoBonus.val = parameters.spellLvl;
|
||||
pseudoBonus.turnsRemain = calculateDuration(parameters.caster, stackSpellPower ? stackSpellPower : parameters.usedSpellPower);
|
||||
CStack::stackEffectToFeature(sse.effect, pseudoBonus);
|
||||
if(owner->id == SpellID::SHIELD || owner->id == SpellID::AIR_SHIELD)
|
||||
{
|
||||
sse.effect.back().val = (100 - sse.effect.back().val); //fix to original config: shield should display damage reduction
|
||||
}
|
||||
if(owner->id == SpellID::BIND && parameters.casterStack)//bind
|
||||
{
|
||||
sse.effect.back().additionalInfo = parameters.casterStack->ID; //we need to know who casted Bind
|
||||
}
|
||||
const Bonus * bonus = nullptr;
|
||||
if(parameters.caster)
|
||||
bonus = parameters.caster->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, owner->id));
|
||||
//TODO does hero specialty should affects his stack casting spells?
|
||||
|
||||
si32 power = 0;
|
||||
for(const CStack * affected : ctx.attackedCres)
|
||||
{
|
||||
if(vstd::contains(ctx.sc.resisted, affected->ID)) //this creature resisted the spell
|
||||
continue;
|
||||
sse.stacks.push_back(affected->ID);
|
||||
|
||||
//Apply hero specials - peculiar enchants
|
||||
const ui8 tier = std::max((ui8)1, affected->getCreature()->level); //don't divide by 0 for certain creatures (commanders, war machines)
|
||||
if(bonus)
|
||||
{
|
||||
switch(bonus->additionalInfo)
|
||||
{
|
||||
case 0: //normal
|
||||
{
|
||||
switch(tier)
|
||||
{
|
||||
case 1: case 2:
|
||||
power = 3;
|
||||
break;
|
||||
case 3: case 4:
|
||||
power = 2;
|
||||
break;
|
||||
case 5: case 6:
|
||||
power = 1;
|
||||
break;
|
||||
}
|
||||
Bonus specialBonus(sse.effect.back());
|
||||
specialBonus.val = power; //it doesn't necessarily make sense for some spells, use it wisely
|
||||
sse.uniqueBonuses.push_back (std::pair<ui32,Bonus> (affected->ID, specialBonus)); //additional premy to given effect
|
||||
}
|
||||
break;
|
||||
case 1: //only Coronius as yet
|
||||
{
|
||||
power = std::max(5 - tier, 0);
|
||||
Bonus specialBonus = CStack::featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, pseudoBonus.turnsRemain);
|
||||
specialBonus.sid = owner->id;
|
||||
sse.uniqueBonuses.push_back(std::pair<ui32,Bonus> (affected->ID, specialBonus)); //additional attack to Slayer effect
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (parameters.caster && parameters.caster->hasBonusOfType(Bonus::SPECIAL_BLESS_DAMAGE, owner->id)) //TODO: better handling of bonus percentages
|
||||
{
|
||||
int damagePercent = parameters.caster->level * parameters.caster->valOfBonuses(Bonus::SPECIAL_BLESS_DAMAGE, owner->id.toEnum()) / tier;
|
||||
Bonus specialBonus = CStack::featureGenerator(Bonus::CREATURE_DAMAGE, 0, damagePercent, pseudoBonus.turnsRemain);
|
||||
specialBonus.valType = Bonus::PERCENT_TO_ALL;
|
||||
specialBonus.sid = owner->id;
|
||||
sse.uniqueBonuses.push_back (std::pair<ui32,Bonus> (affected->ID, specialBonus));
|
||||
}
|
||||
}
|
||||
|
||||
if(!sse.stacks.empty())
|
||||
env->sendAndApply(&sse);
|
||||
|
||||
}
|
||||
|
||||
if(owner->isHealingSpell())
|
||||
{
|
||||
int hpGained = 0;
|
||||
if(parameters.casterStack)
|
||||
{
|
||||
int unitSpellPower = parameters.casterStack->valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, owner->id.toEnum());
|
||||
if(unitSpellPower)
|
||||
hpGained = parameters.casterStack->count * unitSpellPower; //Archangel
|
||||
else //Faerie Dragon-like effect - unused so far
|
||||
parameters.usedSpellPower = parameters.casterStack->valOfBonuses(Bonus::CREATURE_SPELL_POWER) * parameters.casterStack->count / 100;
|
||||
}
|
||||
StacksHealedOrResurrected shr;
|
||||
shr.lifeDrain = false;
|
||||
shr.tentHealing = false;
|
||||
for(auto & attackedCre : ctx.attackedCres)
|
||||
{
|
||||
StacksHealedOrResurrected::HealInfo hi;
|
||||
hi.stackID = (attackedCre)->ID;
|
||||
if (parameters.casterStack) //casted by creature
|
||||
{
|
||||
const bool resurrect = owner->isRisingSpell();
|
||||
if (hpGained)
|
||||
{
|
||||
//archangel
|
||||
hi.healedHP = std::min<ui32>(hpGained, attackedCre->MaxHealth() - attackedCre->firstHPleft + (resurrect ? attackedCre->baseAmount * attackedCre->MaxHealth() : 0));
|
||||
}
|
||||
else
|
||||
{
|
||||
//any typical spell (commander's cure or animate dead)
|
||||
int healedHealth = parameters.usedSpellPower * owner->power + owner->getPower(parameters.spellLvl);
|
||||
hi.healedHP = std::min<ui32>(healedHealth, attackedCre->MaxHealth() - attackedCre->firstHPleft + (resurrect ? attackedCre->baseAmount * attackedCre->MaxHealth() : 0));
|
||||
}
|
||||
}
|
||||
else
|
||||
hi.healedHP = calculateHealedHP(parameters.caster, attackedCre, parameters.selectedStack); //Casted by hero
|
||||
hi.lowLevelResurrection = parameters.spellLvl <= 1;
|
||||
shr.healedStacks.push_back(hi);
|
||||
}
|
||||
if(!shr.healedStacks.empty())
|
||||
env->sendAndApply(&shr);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<BattleHex> DefaultSpellMechanics::rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes) const
|
||||
{
|
||||
using namespace SRSLPraserHelpers;
|
||||
|
||||
std::vector<BattleHex> ret;
|
||||
std::string rng = owner->getLevelInfo(schoolLvl).range + ','; //copy + artificial comma for easier handling
|
||||
|
||||
if(rng.size() >= 2 && rng[0] != 'X') //there is at lest one hex in range (+artificial comma)
|
||||
{
|
||||
std::string number1, number2;
|
||||
int beg, end;
|
||||
bool readingFirst = true;
|
||||
for(auto & elem : rng)
|
||||
{
|
||||
if(std::isdigit(elem) ) //reading number
|
||||
{
|
||||
if(readingFirst)
|
||||
number1 += elem;
|
||||
else
|
||||
number2 += elem;
|
||||
}
|
||||
else if(elem == ',') //comma
|
||||
{
|
||||
//calculating variables
|
||||
if(readingFirst)
|
||||
{
|
||||
beg = atoi(number1.c_str());
|
||||
number1 = "";
|
||||
}
|
||||
else
|
||||
{
|
||||
end = atoi(number2.c_str());
|
||||
number2 = "";
|
||||
}
|
||||
//obtaining new hexes
|
||||
std::set<ui16> curLayer;
|
||||
if(readingFirst)
|
||||
{
|
||||
curLayer = getInRange(centralHex, beg, beg);
|
||||
}
|
||||
else
|
||||
{
|
||||
curLayer = getInRange(centralHex, beg, end);
|
||||
readingFirst = true;
|
||||
}
|
||||
//adding abtained hexes
|
||||
for(auto & curLayer_it : curLayer)
|
||||
{
|
||||
ret.push_back(curLayer_it);
|
||||
}
|
||||
|
||||
}
|
||||
else if(elem == '-') //dash
|
||||
{
|
||||
beg = atoi(number1.c_str());
|
||||
number1 = "";
|
||||
readingFirst = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//remove duplicates (TODO check if actually needed)
|
||||
range::unique(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::set<const CStack *> DefaultSpellMechanics::getAffectedStacks(SpellTargetingContext & ctx) const
|
||||
{
|
||||
std::set<const CStack* > attackedCres;//std::set to exclude multiple occurrences of two hex creatures
|
||||
|
||||
const ui8 attackerSide = ctx.cb->playerToSide(ctx.casterColor) == 1;
|
||||
const auto attackedHexes = rangeInHexes(ctx.destination, ctx.schoolLvl, attackerSide);
|
||||
|
||||
const CSpell::TargetInfo ti(owner, ctx.schoolLvl, ctx.mode);
|
||||
|
||||
//TODO: more generic solution for mass spells
|
||||
if(owner->getLevelInfo(ctx.schoolLvl).range.size() > 1) //custom many-hex range
|
||||
{
|
||||
for(BattleHex hex : attackedHexes)
|
||||
{
|
||||
if(const CStack * st = ctx.cb->battleGetStackByPos(hex, ti.onlyAlive))
|
||||
{
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(ti.type == CSpell::CREATURE)
|
||||
{
|
||||
auto predicate = [=](const CStack * s){
|
||||
const bool positiveToAlly = owner->isPositive() && s->owner == ctx.casterColor;
|
||||
const bool negativeToEnemy = owner->isNegative() && s->owner != ctx.casterColor;
|
||||
const bool validTarget = s->isValidTarget(!ti.onlyAlive); //todo: this should be handled by spell class
|
||||
|
||||
//for single target spells select stacks covering destination tile
|
||||
const bool rangeCovers = ti.massive || s->coversPos(ctx.destination);
|
||||
//handle smart targeting
|
||||
const bool positivenessFlag = !ti.smart || owner->isNeutral() || positiveToAlly || negativeToEnemy;
|
||||
|
||||
return rangeCovers && positivenessFlag && validTarget;
|
||||
};
|
||||
|
||||
TStacks stacks = ctx.cb->battleGetStacksIf(predicate);
|
||||
|
||||
if(ti.massive)
|
||||
{
|
||||
//for massive spells add all targets
|
||||
for (auto stack : stacks)
|
||||
attackedCres.insert(stack);
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
//for single target spells we must select one target. Alive stack is preferred (issue #1763)
|
||||
for(auto stack : stacks)
|
||||
{
|
||||
if(stack->alive())
|
||||
{
|
||||
attackedCres.insert(stack);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(attackedCres.empty() && !stacks.empty())
|
||||
{
|
||||
attackedCres.insert(stacks.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
else //custom range from attackedHexes
|
||||
{
|
||||
for(BattleHex hex : attackedHexes)
|
||||
{
|
||||
if(const CStack * st = ctx.cb->battleGetStackByPos(hex, ti.onlyAlive))
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
}
|
||||
|
||||
return attackedCres;
|
||||
}
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem DefaultSpellMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
||||
{
|
||||
//by default use general algorithm
|
||||
return owner->isImmuneBy(obj);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,397 +1,386 @@
|
||||
#pragma once
|
||||
|
||||
#include "IHandlerBase.h"
|
||||
#include "../lib/ConstTransitivePtr.h"
|
||||
#include "int3.h"
|
||||
#include "GameConstants.h"
|
||||
#include "BattleHex.h"
|
||||
#include "HeroBonus.h"
|
||||
|
||||
|
||||
/*
|
||||
* CSpellHandler.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
class CSpell;
|
||||
class ISpellMechanics;
|
||||
|
||||
class CLegacyConfigParser;
|
||||
|
||||
class CGHeroInstance;
|
||||
class CStack;
|
||||
|
||||
class CBattleInfoCallback;
|
||||
class BattleInfo;
|
||||
|
||||
struct CPackForClient;
|
||||
struct BattleSpellCast;
|
||||
|
||||
class CRandomGenerator;
|
||||
|
||||
struct SpellSchoolInfo
|
||||
{
|
||||
ESpellSchool id; //backlink
|
||||
Bonus::BonusType damagePremyBonus;
|
||||
Bonus::BonusType immunityBonus;
|
||||
std::string jsonName;
|
||||
SecondarySkill::ESecondarySkill skill;
|
||||
Bonus::BonusType knoledgeBonus;
|
||||
};
|
||||
|
||||
///callback to be provided by server
|
||||
class DLL_LINKAGE SpellCastEnvironment
|
||||
{
|
||||
public:
|
||||
virtual ~SpellCastEnvironment(){};
|
||||
virtual void sendAndApply(CPackForClient * info) const = 0;
|
||||
|
||||
virtual CRandomGenerator & getRandomGenerator() const = 0;
|
||||
virtual void complain(const std::string & problem) const = 0;
|
||||
};
|
||||
|
||||
///helper struct
|
||||
struct DLL_LINKAGE BattleSpellCastParameters
|
||||
{
|
||||
public:
|
||||
BattleSpellCastParameters(const BattleInfo * cb);
|
||||
int spellLvl;
|
||||
BattleHex destination;
|
||||
ui8 casterSide;
|
||||
PlayerColor casterColor;
|
||||
const CGHeroInstance * caster;
|
||||
const CGHeroInstance * secHero;
|
||||
int usedSpellPower;
|
||||
ECastingMode::ECastingMode mode;
|
||||
const CStack * casterStack;
|
||||
const CStack * selectedStack;
|
||||
const BattleInfo * cb;
|
||||
};
|
||||
|
||||
enum class VerticalPosition : ui8{TOP, CENTER, BOTTOM};
|
||||
|
||||
class DLL_LINKAGE CSpell
|
||||
{
|
||||
public:
|
||||
|
||||
struct ProjectileInfo
|
||||
{
|
||||
///in radians. Only positive value. Negative angle is handled by vertical flip
|
||||
double minimumAngle;
|
||||
|
||||
///resource name
|
||||
std::string resourceName;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & minimumAngle & resourceName;
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimationItem
|
||||
{
|
||||
std::string resourceName;
|
||||
VerticalPosition verticalPosition;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & resourceName & verticalPosition;
|
||||
}
|
||||
};
|
||||
|
||||
typedef AnimationItem TAnimation;
|
||||
typedef std::vector<TAnimation> TAnimationQueue;
|
||||
|
||||
struct DLL_LINKAGE AnimationInfo
|
||||
{
|
||||
AnimationInfo();
|
||||
~AnimationInfo();
|
||||
|
||||
///displayed on all affected targets.
|
||||
TAnimationQueue affect;
|
||||
|
||||
///displayed on caster.
|
||||
TAnimationQueue cast;
|
||||
|
||||
///displayed on target hex. If spell was casted with no target selection displayed on entire battlefield (f.e. ARMAGEDDON)
|
||||
TAnimationQueue hit;
|
||||
|
||||
///displayed "between" caster and (first) target. Ignored if spell was casted with no target selection.
|
||||
///use selectProjectile to access
|
||||
std::vector<ProjectileInfo> projectile;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & projectile & hit & cast;
|
||||
}
|
||||
|
||||
std::string selectProjectile(const double angle) const;
|
||||
} animationInfo;
|
||||
|
||||
public:
|
||||
struct LevelInfo
|
||||
{
|
||||
std::string description; //descriptions of spell for skill level
|
||||
si32 cost;
|
||||
si32 power;
|
||||
si32 AIValue;
|
||||
|
||||
bool smartTarget;
|
||||
bool clearTarget;
|
||||
bool clearAffected;
|
||||
std::string range;
|
||||
|
||||
std::vector<Bonus> effects;
|
||||
|
||||
LevelInfo();
|
||||
~LevelInfo();
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & description & cost & power & AIValue & smartTarget & range & effects;
|
||||
h & clearTarget & clearAffected;
|
||||
}
|
||||
};
|
||||
|
||||
/** \brief Low level accessor. Don`t use it if absolutely necessary
|
||||
*
|
||||
* \param level. spell school level
|
||||
* \return Spell level info structure
|
||||
*
|
||||
*/
|
||||
const CSpell::LevelInfo& getLevelInfo(const int level) const;
|
||||
public:
|
||||
enum ETargetType {NO_TARGET, CREATURE, OBSTACLE, LOCATION};
|
||||
enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
|
||||
|
||||
struct TargetInfo
|
||||
{
|
||||
ETargetType type;
|
||||
bool smart;
|
||||
bool massive;
|
||||
bool onlyAlive;
|
||||
///no immunity on primary target (mostly spell-like attack)
|
||||
bool alwaysHitDirectly;
|
||||
|
||||
bool clearTarget;
|
||||
bool clearAffected;
|
||||
|
||||
TargetInfo(const CSpell * spell, const int level);
|
||||
TargetInfo(const CSpell * spell, const int level, ECastingMode::ECastingMode mode);
|
||||
|
||||
private:
|
||||
void init(const CSpell * spell, const int level);
|
||||
};
|
||||
|
||||
SpellID id;
|
||||
std::string identifier; //???
|
||||
std::string name;
|
||||
|
||||
si32 level;
|
||||
bool earth; //deprecated
|
||||
bool water; //deprecated
|
||||
bool fire; //deprecated
|
||||
bool air; //deprecated
|
||||
|
||||
std::map<ESpellSchool, bool> school; //todo: use this instead of separate boolean fields
|
||||
|
||||
si32 power; //spell's power
|
||||
|
||||
std::map<TFaction, si32> probabilities; //% chance to gain for castles
|
||||
|
||||
bool combatSpell; //is this spell combat (true) or adventure (false)
|
||||
bool creatureAbility; //if true, only creatures can use this spell
|
||||
si8 positiveness; //1 if spell is positive for influenced stacks, 0 if it is indifferent, -1 if it's negative
|
||||
|
||||
std::vector<SpellID> counteredSpells; //spells that are removed when effect of this spell is placed on creature (for bless-curse, haste-slow, and similar pairs)
|
||||
|
||||
CSpell();
|
||||
~CSpell();
|
||||
|
||||
bool isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const std::set<SpellID> & spellBook) const;
|
||||
|
||||
|
||||
std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr ) const; //convert range to specific hexes; last optional out parameter is set to true, if spell would cover unavailable hexes (that are not included in ret)
|
||||
ETargetType getTargetType() const; //deprecated
|
||||
|
||||
CSpell::TargetInfo getTargetInfo(const int level) const;
|
||||
|
||||
|
||||
bool isCombatSpell() const;
|
||||
bool isAdventureSpell() const;
|
||||
bool isCreatureAbility() const;
|
||||
|
||||
bool isPositive() const;
|
||||
bool isNegative() const;
|
||||
bool isNeutral() const;
|
||||
|
||||
bool isDamageSpell() const;
|
||||
bool isHealingSpell() const;
|
||||
bool isRisingSpell() const;
|
||||
bool isOffensiveSpell() const;
|
||||
|
||||
bool isSpecialSpell() const;
|
||||
|
||||
bool hasEffects() const;
|
||||
void getEffects(std::vector<Bonus> &lst, const int level) const;
|
||||
|
||||
///checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into account general problems such as not having spellbook or mana points etc.
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneAt(const CBattleInfoCallback * cb, const CGHeroInstance * caster, ECastingMode::ECastingMode mode, BattleHex destination) const;
|
||||
|
||||
//internal, for use only by Mechanics classes
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const;
|
||||
|
||||
//checks for creature immunity / anything that prevent casting *at given target* - doesn't take into account general problems such as not having spellbook or mana points etc.
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const;
|
||||
|
||||
//internal, for use only by Mechanics classes. applying secondary skills
|
||||
ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const;
|
||||
|
||||
///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account
|
||||
ui32 calculateDamage(const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const;
|
||||
|
||||
///calculate healed HP for all spells casted by hero
|
||||
ui32 calculateHealedHP(const CGHeroInstance * caster, const CStack * stack, const CStack * sacrificedStack = nullptr) const;
|
||||
|
||||
///selects from allStacks actually affected stacks
|
||||
std::set<const CStack *> getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const CGHeroInstance * caster = nullptr) const;
|
||||
|
||||
si32 getCost(const int skillLevel) const;
|
||||
|
||||
/**
|
||||
* Returns spell level power, base power ignored
|
||||
*/
|
||||
si32 getPower(const int skillLevel) const;
|
||||
|
||||
// /**
|
||||
// * Returns spell power, taking base power into account
|
||||
// */
|
||||
// si32 calculatePower(const int skillLevel) const;
|
||||
|
||||
|
||||
si32 getProbability(const TFaction factionId) const;
|
||||
|
||||
/**
|
||||
* Calls cb for each school this spell belongs to
|
||||
*
|
||||
* Set stop to true to abort looping
|
||||
*/
|
||||
void forEachSchool(const std::function<void (const SpellSchoolInfo &, bool &)> & cb) const;
|
||||
|
||||
/**
|
||||
* Returns resource name of icon for SPELL_IMMUNITY bonus
|
||||
*/
|
||||
const std::string& getIconImmune() const;
|
||||
|
||||
const std::string& getCastSound() const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & identifier & id & name & level & power
|
||||
& probabilities & attributes & combatSpell & creatureAbility & positiveness & counteredSpells;
|
||||
h & isRising & isDamage & isOffensive;
|
||||
h & targetType;
|
||||
h & immunities & limiters & absoluteImmunities & absoluteLimiters;
|
||||
h & iconImmune;
|
||||
h & defaultProbability;
|
||||
h & isSpecial;
|
||||
h & castSound & iconBook & iconEffect & iconScenarioBonus & iconScroll;
|
||||
h & levels;
|
||||
h & school;
|
||||
h & animationInfo;
|
||||
|
||||
if(!h.saving)
|
||||
setup();
|
||||
}
|
||||
friend class CSpellHandler;
|
||||
friend class Graphics;
|
||||
public:
|
||||
///Server logic. Has write access to GameState via packets.
|
||||
///May be executed on client side by (future) non-cheat-proof scripts.
|
||||
|
||||
//void adventureCast() const;
|
||||
void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const;
|
||||
|
||||
public:
|
||||
///Client-server logic. Has direct write access to GameState.
|
||||
///Shall be called (only) when applying packets on BOTH SIDES
|
||||
|
||||
///implementation of BattleSpellCast applying
|
||||
void afterCast(BattleInfo * battle, const BattleSpellCast * packet) const;
|
||||
|
||||
private:
|
||||
void setIsOffensive(const bool val);
|
||||
void setIsRising(const bool val);
|
||||
|
||||
//call this after load or deserialization. cant be done in constructor.
|
||||
void setup();
|
||||
void setupMechanics();
|
||||
private:
|
||||
si32 defaultProbability;
|
||||
|
||||
bool isRising;
|
||||
bool isDamage;
|
||||
bool isOffensive;
|
||||
bool isSpecial;
|
||||
|
||||
std::string attributes; //reference only attributes //todo: remove or include in configuration format, currently unused
|
||||
|
||||
ETargetType targetType;
|
||||
|
||||
std::vector<Bonus::BonusType> immunities; //any of these grants immunity
|
||||
std::vector<Bonus::BonusType> absoluteImmunities; //any of these grants immunity, can't be negated
|
||||
std::vector<Bonus::BonusType> limiters; //all of them are required to be affected
|
||||
std::vector<Bonus::BonusType> absoluteLimiters; //all of them are required to be affected, can't be negated
|
||||
|
||||
///graphics related stuff
|
||||
|
||||
std::string iconImmune;
|
||||
|
||||
std::string iconBook;
|
||||
std::string iconEffect;
|
||||
std::string iconScenarioBonus;
|
||||
std::string iconScroll;
|
||||
|
||||
///sound related stuff
|
||||
std::string castSound;
|
||||
|
||||
std::vector<LevelInfo> levels;
|
||||
|
||||
ISpellMechanics * mechanics;//(!) do not serialize
|
||||
};
|
||||
|
||||
bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos); //for spells like Dimension Door
|
||||
|
||||
class DLL_LINKAGE CSpellHandler: public CHandlerBase<SpellID, CSpell>
|
||||
{
|
||||
public:
|
||||
CSpellHandler();
|
||||
virtual ~CSpellHandler();
|
||||
|
||||
///IHandler base
|
||||
std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
|
||||
void afterLoadFinalization() override;
|
||||
void beforeValidate(JsonNode & object) 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 override;
|
||||
|
||||
const std::string getTypeName() const override;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & objects ;
|
||||
}
|
||||
|
||||
protected:
|
||||
CSpell * loadFromJson(const JsonNode & json) override;
|
||||
};
|
||||
/*
|
||||
* CSpellHandler.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../IHandlerBase.h"
|
||||
#include "../ConstTransitivePtr.h"
|
||||
#include "../int3.h"
|
||||
#include "../GameConstants.h"
|
||||
#include "../BattleHex.h"
|
||||
#include "../HeroBonus.h"
|
||||
|
||||
class CGObjectInstance;
|
||||
class CSpell;
|
||||
class ISpellMechanics;
|
||||
class CLegacyConfigParser;
|
||||
class CGHeroInstance;
|
||||
class CStack;
|
||||
class CBattleInfoCallback;
|
||||
class BattleInfo;
|
||||
struct CPackForClient;
|
||||
struct BattleSpellCast;
|
||||
class CGameInfoCallback;
|
||||
class CRandomGenerator;
|
||||
class CMap;
|
||||
|
||||
struct SpellSchoolInfo
|
||||
{
|
||||
ESpellSchool id; //backlink
|
||||
Bonus::BonusType damagePremyBonus;
|
||||
Bonus::BonusType immunityBonus;
|
||||
std::string jsonName;
|
||||
SecondarySkill::ESecondarySkill skill;
|
||||
Bonus::BonusType knoledgeBonus;
|
||||
};
|
||||
|
||||
///callback to be provided by server
|
||||
class DLL_LINKAGE SpellCastEnvironment
|
||||
{
|
||||
public:
|
||||
virtual ~SpellCastEnvironment(){};
|
||||
virtual void sendAndApply(CPackForClient * info) const = 0;
|
||||
|
||||
virtual CRandomGenerator & getRandomGenerator() const = 0;
|
||||
virtual void complain(const std::string & problem) const = 0;
|
||||
|
||||
virtual const CMap * getMap() const = 0;
|
||||
virtual const CGameInfoCallback * getCb() const = 0;
|
||||
|
||||
virtual bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) const =0; //TODO: remove
|
||||
};
|
||||
|
||||
///helper struct
|
||||
struct DLL_LINKAGE BattleSpellCastParameters
|
||||
{
|
||||
public:
|
||||
BattleSpellCastParameters(const BattleInfo * cb);
|
||||
int spellLvl;
|
||||
BattleHex destination;
|
||||
ui8 casterSide;
|
||||
PlayerColor casterColor;
|
||||
const CGHeroInstance * caster;
|
||||
const CGHeroInstance * secHero;
|
||||
int usedSpellPower;
|
||||
ECastingMode::ECastingMode mode;
|
||||
const CStack * casterStack;
|
||||
const CStack * selectedStack;
|
||||
const BattleInfo * cb;
|
||||
};
|
||||
|
||||
struct DLL_LINKAGE AdventureSpellCastParameters
|
||||
{
|
||||
const CGHeroInstance * caster;
|
||||
int3 pos;
|
||||
};
|
||||
|
||||
enum class VerticalPosition : ui8{TOP, CENTER, BOTTOM};
|
||||
|
||||
class DLL_LINKAGE CSpell
|
||||
{
|
||||
public:
|
||||
struct ProjectileInfo
|
||||
{
|
||||
///in radians. Only positive value. Negative angle is handled by vertical flip
|
||||
double minimumAngle;
|
||||
|
||||
///resource name
|
||||
std::string resourceName;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & minimumAngle & resourceName;
|
||||
}
|
||||
};
|
||||
|
||||
struct AnimationItem
|
||||
{
|
||||
std::string resourceName;
|
||||
VerticalPosition verticalPosition;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & resourceName & verticalPosition;
|
||||
}
|
||||
};
|
||||
|
||||
typedef AnimationItem TAnimation;
|
||||
typedef std::vector<TAnimation> TAnimationQueue;
|
||||
|
||||
struct DLL_LINKAGE AnimationInfo
|
||||
{
|
||||
AnimationInfo();
|
||||
~AnimationInfo();
|
||||
|
||||
///displayed on all affected targets.
|
||||
TAnimationQueue affect;
|
||||
|
||||
///displayed on caster.
|
||||
TAnimationQueue cast;
|
||||
|
||||
///displayed on target hex. If spell was casted with no target selection displayed on entire battlefield (f.e. ARMAGEDDON)
|
||||
TAnimationQueue hit;
|
||||
|
||||
///displayed "between" caster and (first) target. Ignored if spell was casted with no target selection.
|
||||
///use selectProjectile to access
|
||||
std::vector<ProjectileInfo> projectile;
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & projectile & hit & cast;
|
||||
}
|
||||
|
||||
std::string selectProjectile(const double angle) const;
|
||||
} animationInfo;
|
||||
public:
|
||||
struct LevelInfo
|
||||
{
|
||||
std::string description; //descriptions of spell for skill level
|
||||
si32 cost;
|
||||
si32 power;
|
||||
si32 AIValue;
|
||||
|
||||
bool smartTarget;
|
||||
bool clearTarget;
|
||||
bool clearAffected;
|
||||
std::string range;
|
||||
|
||||
std::vector<Bonus> effects;
|
||||
|
||||
LevelInfo();
|
||||
~LevelInfo();
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & description & cost & power & AIValue & smartTarget & range & effects;
|
||||
h & clearTarget & clearAffected;
|
||||
}
|
||||
};
|
||||
|
||||
/** \brief Low level accessor. Don`t use it if absolutely necessary
|
||||
*
|
||||
* \param level. spell school level
|
||||
* \return Spell level info structure
|
||||
*
|
||||
*/
|
||||
const CSpell::LevelInfo& getLevelInfo(const int level) const;
|
||||
public:
|
||||
enum ETargetType {NO_TARGET, CREATURE, OBSTACLE, LOCATION};
|
||||
enum ESpellPositiveness {NEGATIVE = -1, NEUTRAL = 0, POSITIVE = 1};
|
||||
|
||||
struct TargetInfo
|
||||
{
|
||||
ETargetType type;
|
||||
bool smart;
|
||||
bool massive;
|
||||
bool onlyAlive;
|
||||
///no immunity on primary target (mostly spell-like attack)
|
||||
bool alwaysHitDirectly;
|
||||
|
||||
bool clearTarget;
|
||||
bool clearAffected;
|
||||
|
||||
TargetInfo(const CSpell * spell, const int level);
|
||||
TargetInfo(const CSpell * spell, const int level, ECastingMode::ECastingMode mode);
|
||||
|
||||
private:
|
||||
void init(const CSpell * spell, const int level);
|
||||
};
|
||||
|
||||
SpellID id;
|
||||
std::string identifier; //???
|
||||
std::string name;
|
||||
|
||||
si32 level;
|
||||
|
||||
std::map<ESpellSchool, bool> school; //todo: use this instead of separate boolean fields
|
||||
|
||||
si32 power; //spell's power
|
||||
|
||||
std::map<TFaction, si32> probabilities; //% chance to gain for castles
|
||||
|
||||
bool combatSpell; //is this spell combat (true) or adventure (false)
|
||||
bool creatureAbility; //if true, only creatures can use this spell
|
||||
si8 positiveness; //1 if spell is positive for influenced stacks, 0 if it is indifferent, -1 if it's negative
|
||||
|
||||
std::vector<SpellID> counteredSpells; //spells that are removed when effect of this spell is placed on creature (for bless-curse, haste-slow, and similar pairs)
|
||||
|
||||
CSpell();
|
||||
~CSpell();
|
||||
|
||||
bool isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const std::set<SpellID> & spellBook) const;
|
||||
|
||||
std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr ) const; //convert range to specific hexes; last optional out parameter is set to true, if spell would cover unavailable hexes (that are not included in ret)
|
||||
ETargetType getTargetType() const; //deprecated
|
||||
|
||||
CSpell::TargetInfo getTargetInfo(const int level) const;
|
||||
|
||||
bool isCombatSpell() const;
|
||||
bool isAdventureSpell() const;
|
||||
bool isCreatureAbility() const;
|
||||
|
||||
bool isPositive() const;
|
||||
bool isNegative() const;
|
||||
bool isNeutral() const;
|
||||
|
||||
bool isDamageSpell() const;
|
||||
bool isHealingSpell() const;
|
||||
bool isRisingSpell() const;
|
||||
bool isOffensiveSpell() const;
|
||||
|
||||
bool isSpecialSpell() const;
|
||||
|
||||
bool hasEffects() const;
|
||||
void getEffects(std::vector<Bonus> &lst, const int level) const;
|
||||
|
||||
///checks for creature immunity / anything that prevent casting *at given hex* - doesn't take into account general problems such as not having spellbook or mana points etc.
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneAt(const CBattleInfoCallback * cb, const CGHeroInstance * caster, ECastingMode::ECastingMode mode, BattleHex destination) const;
|
||||
|
||||
//internal, for use only by Mechanics classes
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const;
|
||||
|
||||
//checks for creature immunity / anything that prevent casting *at given target* - doesn't take into account general problems such as not having spellbook or mana points etc.
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const;
|
||||
|
||||
//internal, for use only by Mechanics classes. applying secondary skills
|
||||
ui32 calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const;
|
||||
|
||||
///calculate spell damage on stack taking caster`s secondary skills and affectedCreature`s bonuses into account
|
||||
ui32 calculateDamage(const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const;
|
||||
|
||||
///selects from allStacks actually affected stacks
|
||||
std::set<const CStack *> getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const CGHeroInstance * caster = nullptr) const;
|
||||
|
||||
si32 getCost(const int skillLevel) const;
|
||||
|
||||
/**
|
||||
* Returns spell level power, base power ignored
|
||||
*/
|
||||
si32 getPower(const int skillLevel) const;
|
||||
|
||||
si32 getProbability(const TFaction factionId) const;
|
||||
|
||||
/**
|
||||
* Calls cb for each school this spell belongs to
|
||||
*
|
||||
* Set stop to true to abort looping
|
||||
*/
|
||||
void forEachSchool(const std::function<void (const SpellSchoolInfo &, bool &)> & cb) const;
|
||||
|
||||
/**
|
||||
* Returns resource name of icon for SPELL_IMMUNITY bonus
|
||||
*/
|
||||
const std::string& getIconImmune() const;
|
||||
|
||||
const std::string& getCastSound() const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & identifier & id & name & level & power
|
||||
& probabilities & attributes & combatSpell & creatureAbility & positiveness & counteredSpells;
|
||||
h & isRising & isDamage & isOffensive;
|
||||
h & targetType;
|
||||
h & immunities & limiters & absoluteImmunities & absoluteLimiters;
|
||||
h & iconImmune;
|
||||
h & defaultProbability;
|
||||
h & isSpecial;
|
||||
h & castSound & iconBook & iconEffect & iconScenarioBonus & iconScroll;
|
||||
h & levels;
|
||||
h & school;
|
||||
h & animationInfo;
|
||||
|
||||
if(!h.saving)
|
||||
setup();
|
||||
}
|
||||
friend class CSpellHandler;
|
||||
friend class Graphics;
|
||||
public:
|
||||
///Server logic. Has write access to GameState via packets.
|
||||
///May be executed on client side by (future) non-cheat-proof scripts.
|
||||
|
||||
bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const;
|
||||
void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const;
|
||||
|
||||
public:
|
||||
///Client-server logic. Has direct write access to GameState.
|
||||
///Shall be called (only) when applying packets on BOTH SIDES
|
||||
|
||||
///implementation of BattleSpellCast applying
|
||||
void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const;
|
||||
|
||||
private:
|
||||
void setIsOffensive(const bool val);
|
||||
void setIsRising(const bool val);
|
||||
|
||||
//call this after load or deserialization. cant be done in constructor.
|
||||
void setup();
|
||||
void setupMechanics();
|
||||
private:
|
||||
si32 defaultProbability;
|
||||
|
||||
bool isRising;
|
||||
bool isDamage;
|
||||
bool isOffensive;
|
||||
bool isSpecial;
|
||||
|
||||
std::string attributes; //reference only attributes //todo: remove or include in configuration format, currently unused
|
||||
|
||||
ETargetType targetType;
|
||||
|
||||
std::vector<Bonus::BonusType> immunities; //any of these grants immunity
|
||||
std::vector<Bonus::BonusType> absoluteImmunities; //any of these grants immunity, can't be negated
|
||||
std::vector<Bonus::BonusType> limiters; //all of them are required to be affected
|
||||
std::vector<Bonus::BonusType> absoluteLimiters; //all of them are required to be affected, can't be negated
|
||||
|
||||
///graphics related stuff
|
||||
std::string iconImmune;
|
||||
std::string iconBook;
|
||||
std::string iconEffect;
|
||||
std::string iconScenarioBonus;
|
||||
std::string iconScroll;
|
||||
|
||||
///sound related stuff
|
||||
std::string castSound;
|
||||
|
||||
std::vector<LevelInfo> levels;
|
||||
|
||||
ISpellMechanics * mechanics;//(!) do not serialize
|
||||
};
|
||||
|
||||
bool DLL_LINKAGE isInScreenRange(const int3 ¢er, const int3 &pos); //for spells like Dimension Door
|
||||
|
||||
class DLL_LINKAGE CSpellHandler: public CHandlerBase<SpellID, CSpell>
|
||||
{
|
||||
public:
|
||||
CSpellHandler();
|
||||
virtual ~CSpellHandler();
|
||||
|
||||
///IHandler base
|
||||
std::vector<JsonNode> loadLegacyData(size_t dataSize) override;
|
||||
void afterLoadFinalization() override;
|
||||
void beforeValidate(JsonNode & object) 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 override;
|
||||
|
||||
const std::string getTypeName() const override;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & objects ;
|
||||
}
|
||||
|
||||
protected:
|
||||
CSpell * loadFromJson(const JsonNode & json) override;
|
||||
};
|
95
lib/spells/CreatureSpellMechanics.cpp
Normal file
95
lib/spells/CreatureSpellMechanics.cpp
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* CreatureSpellMechanics.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "CreatureSpellMechanics.h"
|
||||
|
||||
#include "../NetPacks.h"
|
||||
#include "../BattleState.h"
|
||||
|
||||
///AcidBreathDamageMechanics
|
||||
void AcidBreathDamageMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
//calculating dmg to display
|
||||
ctx.sc.dmgToDisplay = parameters.usedSpellPower;
|
||||
|
||||
for(auto & attackedCre : ctx.attackedCres) //no immunities
|
||||
{
|
||||
BattleStackAttacked bsa;
|
||||
bsa.flags |= BattleStackAttacked::SPELL_EFFECT;
|
||||
bsa.spellID = owner->id;
|
||||
bsa.damageAmount = parameters.usedSpellPower; //damage times the number of attackers
|
||||
bsa.stackAttacked = (attackedCre)->ID;
|
||||
bsa.attackerID = -1;
|
||||
(attackedCre)->prepareAttacked(bsa, env->getRandomGenerator());
|
||||
ctx.si.stacks.push_back(bsa);
|
||||
}
|
||||
}
|
||||
|
||||
///DeathStareMechanics
|
||||
void DeathStareMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||
{
|
||||
//calculating dmg to display
|
||||
ctx.sc.dmgToDisplay = parameters.usedSpellPower;
|
||||
if(!ctx.attackedCres.empty())
|
||||
vstd::amin(ctx.sc.dmgToDisplay, (*ctx.attackedCres.begin())->count); //stack is already reduced after attack
|
||||
|
||||
for(auto & attackedCre : ctx.attackedCres)
|
||||
{
|
||||
BattleStackAttacked bsa;
|
||||
bsa.flags |= BattleStackAttacked::SPELL_EFFECT;
|
||||
bsa.spellID = owner->id;
|
||||
bsa.damageAmount = parameters.usedSpellPower * (attackedCre)->valOfBonuses(Bonus::STACK_HEALTH);
|
||||
bsa.stackAttacked = (attackedCre)->ID;
|
||||
bsa.attackerID = -1;
|
||||
(attackedCre)->prepareAttacked(bsa, env->getRandomGenerator());
|
||||
ctx.si.stacks.push_back(bsa);
|
||||
}
|
||||
}
|
||||
|
||||
///DispellHelpfulMechanics
|
||||
void DispellHelpfulMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
|
||||
{
|
||||
DefaultSpellMechanics::applyBattle(battle, packet);
|
||||
|
||||
for(auto stackID : packet->affectedCres)
|
||||
{
|
||||
if(vstd::contains(packet->resisted, stackID))
|
||||
continue;
|
||||
|
||||
CStack *s = battle->getStack(stackID);
|
||||
s->popBonuses([&](const Bonus *b) -> bool
|
||||
{
|
||||
return Selector::positiveSpellEffects(b);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem DispellHelpfulMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
||||
{
|
||||
TBonusListPtr spellBon = obj->getSpellBonuses();
|
||||
bool hasPositiveSpell = false;
|
||||
for(const Bonus * b : *spellBon)
|
||||
{
|
||||
if(SpellID(b->sid).toSpell()->isPositive())
|
||||
{
|
||||
hasPositiveSpell = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!hasPositiveSpell)
|
||||
{
|
||||
return ESpellCastProblem::NO_SPELLS_TO_DISPEL;
|
||||
}
|
||||
|
||||
//use default algorithm only if there is no mechanics-related problem
|
||||
return DefaultSpellMechanics::isImmuneByStack(caster,obj);
|
||||
}
|
40
lib/spells/CreatureSpellMechanics.h
Normal file
40
lib/spells/CreatureSpellMechanics.h
Normal file
@ -0,0 +1,40 @@
|
||||
/*
|
||||
* CreatureSpellMechanics.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ISpellMechanics.h"
|
||||
#include "CDefaultSpellMechanics.h"
|
||||
|
||||
class DLL_LINKAGE AcidBreathDamageMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
AcidBreathDamageMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE DeathStareMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
DeathStareMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
protected:
|
||||
void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const override;
|
||||
};
|
||||
|
||||
class DLL_LINKAGE DispellHelpfulMechanics : public DefaultSpellMechanics
|
||||
{
|
||||
public:
|
||||
DispellHelpfulMechanics(CSpell * s): DefaultSpellMechanics(s){};
|
||||
|
||||
void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const override;
|
||||
|
||||
ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const override;
|
||||
};
|
87
lib/spells/ISpellMechanics.cpp
Normal file
87
lib/spells/ISpellMechanics.cpp
Normal file
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* ISpellMechanics.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "ISpellMechanics.h"
|
||||
|
||||
#include "CDefaultSpellMechanics.h"
|
||||
|
||||
#include "AdventureSpellMechanics.h"
|
||||
#include "BattleSpellMechanics.h"
|
||||
#include "CreatureSpellMechanics.h"
|
||||
|
||||
///ISpellMechanics
|
||||
ISpellMechanics::ISpellMechanics(CSpell * s):
|
||||
owner(s)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ISpellMechanics * ISpellMechanics::createMechanics(CSpell * s)
|
||||
{
|
||||
switch (s->id)
|
||||
{
|
||||
case SpellID::ACID_BREATH_DAMAGE:
|
||||
return new AcidBreathDamageMechanics(s);
|
||||
case SpellID::CHAIN_LIGHTNING:
|
||||
return new ChainLightningMechanics(s);
|
||||
case SpellID::CLONE:
|
||||
return new CloneMechanics(s);
|
||||
case SpellID::CURE:
|
||||
return new CureMechanics(s);
|
||||
case SpellID::DEATH_STARE:
|
||||
return new DeathStareMechanics(s);
|
||||
case SpellID::DISPEL:
|
||||
return new DispellMechanics(s);
|
||||
case SpellID::DISPEL_HELPFUL_SPELLS:
|
||||
return new DispellHelpfulMechanics(s);
|
||||
case SpellID::FIRE_WALL:
|
||||
case SpellID::FORCE_FIELD:
|
||||
return new WallMechanics(s);
|
||||
case SpellID::HYPNOTIZE:
|
||||
return new HypnotizeMechanics(s);
|
||||
case SpellID::LAND_MINE:
|
||||
case SpellID::QUICKSAND:
|
||||
return new ObstacleMechanics(s);
|
||||
case SpellID::REMOVE_OBSTACLE:
|
||||
return new RemoveObstacleMechanics(s);
|
||||
case SpellID::SACRIFICE:
|
||||
return new SacrificeMechanics(s);
|
||||
case SpellID::SUMMON_FIRE_ELEMENTAL:
|
||||
case SpellID::SUMMON_EARTH_ELEMENTAL:
|
||||
case SpellID::SUMMON_WATER_ELEMENTAL:
|
||||
case SpellID::SUMMON_AIR_ELEMENTAL:
|
||||
return new SummonMechanics(s);
|
||||
case SpellID::TELEPORT:
|
||||
return new TeleportMechanics(s);
|
||||
case SpellID::SUMMON_BOAT:
|
||||
return new SummonBoatMechanics(s);
|
||||
case SpellID::SCUTTLE_BOAT:
|
||||
return new ScuttleBoatMechanics(s);
|
||||
case SpellID::DIMENSION_DOOR:
|
||||
return new DimensionDoorMechanics(s);
|
||||
case SpellID::FLY:
|
||||
case SpellID::WATER_WALK:
|
||||
case SpellID::VISIONS:
|
||||
case SpellID::DISGUISE:
|
||||
return new DefaultSpellMechanics(s); //implemented using bonus system
|
||||
case SpellID::TOWN_PORTAL:
|
||||
return new TownPortalMechanics(s);
|
||||
case SpellID::VIEW_EARTH:
|
||||
return new ViewEarthMechanics(s);
|
||||
case SpellID::VIEW_AIR:
|
||||
return new ViewAirMechanics(s);
|
||||
default:
|
||||
if(s->isRisingSpell())
|
||||
return new SpecialRisingSpellMechanics(s);
|
||||
else
|
||||
return new DefaultSpellMechanics(s);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SpellMechanics.h, part of VCMI engine
|
||||
* ISpellMechanics.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
@ -7,48 +7,43 @@
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CSpellHandler.h"
|
||||
#include "BattleHex.h"
|
||||
#include "../BattleHex.h"
|
||||
|
||||
class DLL_LINKAGE ISpellMechanics
|
||||
{
|
||||
public:
|
||||
|
||||
struct DLL_LINKAGE SpellTargetingContext
|
||||
{
|
||||
const CBattleInfoCallback * cb;
|
||||
const CBattleInfoCallback * cb;
|
||||
CSpell::TargetInfo ti;
|
||||
ECastingMode::ECastingMode mode;
|
||||
BattleHex destination;
|
||||
PlayerColor casterColor;
|
||||
int schoolLvl;
|
||||
|
||||
|
||||
SpellTargetingContext(const CSpell * s, const CBattleInfoCallback * c, ECastingMode::ECastingMode m, PlayerColor cc, int lvl, BattleHex dest)
|
||||
: cb(c), ti(s,lvl, m), mode(m), destination(dest), casterColor(cc), schoolLvl(lvl)
|
||||
{};
|
||||
|
||||
|
||||
};
|
||||
|
||||
public:
|
||||
ISpellMechanics(CSpell * s);
|
||||
virtual ~ISpellMechanics(){};
|
||||
|
||||
virtual std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool *outDroppedHexes = nullptr) const = 0;
|
||||
virtual std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const = 0;
|
||||
|
||||
virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0;
|
||||
|
||||
//virtual bool adventureCast(const SpellCastContext & context) const = 0;
|
||||
virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0;
|
||||
|
||||
static ISpellMechanics * createMechanics(CSpell * s);
|
||||
|
||||
virtual void afterCast(BattleInfo * battle, const BattleSpellCast * packet) const = 0;
|
||||
|
||||
protected:
|
||||
CSpell * owner;
|
||||
};
|
||||
virtual ~ISpellMechanics(){};
|
||||
|
||||
virtual std::vector<BattleHex> rangeInHexes(BattleHex centralHex, ui8 schoolLvl, ui8 side, bool * outDroppedHexes = nullptr) const = 0;
|
||||
virtual std::set<const CStack *> getAffectedStacks(SpellTargetingContext & ctx) const = 0;
|
||||
|
||||
virtual ESpellCastProblem::ESpellCastProblem isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const = 0;
|
||||
|
||||
virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0;
|
||||
virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0;
|
||||
virtual void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const = 0;
|
||||
|
||||
static ISpellMechanics * createMechanics(CSpell * s);
|
||||
protected:
|
||||
CSpell * owner;
|
||||
};
|
27
lib/spells/ViewSpellInt.cpp
Normal file
27
lib/spells/ViewSpellInt.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* ViewSpellInt.cpp, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#include "StdInc.h"
|
||||
|
||||
#include "ViewSpellInt.h"
|
||||
|
||||
#include "../mapObjects/CObjectHandler.h"
|
||||
|
||||
ObjectPosInfo::ObjectPosInfo():
|
||||
pos(),id(Obj::NO_OBJ), subId(-1), owner(PlayerColor::CANNOT_DETERMINE)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
ObjectPosInfo::ObjectPosInfo(const CGObjectInstance * obj):
|
||||
pos(obj->pos),id(obj->ID), subId(obj->subID), owner(obj->tempOwner)
|
||||
{
|
||||
|
||||
}
|
32
lib/spells/ViewSpellInt.h
Normal file
32
lib/spells/ViewSpellInt.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* ViewSpellInt.h, part of VCMI engine
|
||||
*
|
||||
* Authors: listed in file AUTHORS in main folder
|
||||
*
|
||||
* License: GNU General Public License v2.0 or later
|
||||
* Full text of license available in license.txt file, in main folder
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../int3.h"
|
||||
#include "../GameConstants.h"
|
||||
|
||||
class CGObjectInstance;
|
||||
|
||||
struct DLL_LINKAGE ObjectPosInfo
|
||||
{
|
||||
int3 pos;
|
||||
Obj id;
|
||||
si32 subId;
|
||||
PlayerColor owner;
|
||||
ObjectPosInfo();
|
||||
ObjectPosInfo(const CGObjectInstance * obj);
|
||||
|
||||
template <typename Handler> void serialize(Handler & h, const int version)
|
||||
{
|
||||
h & pos & id & subId & owner;
|
||||
}
|
||||
};
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "../lib/CArtHandler.h"
|
||||
#include "../lib/CBuildingHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CSpellHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CGeneralTextHandler.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/CCreatureHandler.h"
|
||||
@ -67,8 +67,11 @@ public:
|
||||
void sendAndApply(CPackForClient * info) const override;
|
||||
CRandomGenerator & getRandomGenerator() const override;
|
||||
void complain(const std::string & problem) const override;
|
||||
const CMap * getMap() const override;
|
||||
const CGameInfoCallback * getCb() const override;
|
||||
bool moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker = PlayerColor::NEUTRAL) const override;
|
||||
private:
|
||||
CGameHandler * gh;
|
||||
mutable CGameHandler * gh;
|
||||
};
|
||||
|
||||
CondSh<bool> battleMadeAction;
|
||||
@ -4970,222 +4973,12 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
|
||||
bool CGameHandler::castSpell(const CGHeroInstance *h, SpellID spellID, const int3 &pos)
|
||||
{
|
||||
const CSpell *s = spellID.toSpell();
|
||||
int cost = h->getSpellCost(s);
|
||||
int schoolLevel = h->getSpellSchoolLevel(s);
|
||||
|
||||
if(!h->canCastThisSpell(s))
|
||||
COMPLAIN_RET("Hero cannot cast this spell!");
|
||||
if(h->mana < cost)
|
||||
COMPLAIN_RET("Hero doesn't have enough spell points to cast this spell!");
|
||||
if(s->combatSpell)
|
||||
COMPLAIN_RET("This function can be used only for adventure map spells!");
|
||||
|
||||
AdvmapSpellCast asc;
|
||||
asc.caster = h;
|
||||
asc.spellID = spellID;
|
||||
sendAndApply(&asc);
|
||||
|
||||
switch(spellID)
|
||||
{
|
||||
case SpellID::SUMMON_BOAT:
|
||||
{
|
||||
//check if spell works at all
|
||||
if(gs->getRandomGenerator().nextInt(99) >= s->getPower(schoolLevel)) //power is % chance of success
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 336); //%s tried to summon a boat, but failed.
|
||||
iw.text.addReplacement(h->name);
|
||||
sendAndApply(&iw);
|
||||
break;
|
||||
}
|
||||
|
||||
//try to find unoccupied boat to summon
|
||||
const CGBoat *nearest = nullptr;
|
||||
double dist = 0;
|
||||
int3 summonPos = h->bestLocation();
|
||||
if(summonPos.x < 0)
|
||||
COMPLAIN_RET("There is no water tile available!");
|
||||
|
||||
for(const CGObjectInstance *obj : gs->map->objects)
|
||||
{
|
||||
if(obj && obj->ID == Obj::BOAT)
|
||||
{
|
||||
const CGBoat *b = static_cast<const CGBoat*>(obj);
|
||||
if(b->hero) continue; //we're looking for unoccupied boat
|
||||
|
||||
double nDist = distance(b->pos, h->getPosition());
|
||||
if(!nearest || nDist < dist) //it's first boat or closer than previous
|
||||
{
|
||||
nearest = b;
|
||||
dist = nDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(nearest) //we found boat to summon
|
||||
{
|
||||
ChangeObjPos cop;
|
||||
cop.objid = nearest->id;
|
||||
cop.nPos = summonPos + int3(1,0,0);;
|
||||
cop.flags = 1;
|
||||
sendAndApply(&cop);
|
||||
}
|
||||
else if(schoolLevel < 2) //none or basic level -> cannot create boat :(
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 335); //There are no boats to summon.
|
||||
sendAndApply(&iw);
|
||||
}
|
||||
else //create boat
|
||||
{
|
||||
NewObject no;
|
||||
no.ID = Obj::BOAT;
|
||||
no.subID = h->getBoatType();
|
||||
no.pos = summonPos + int3(1,0,0);;
|
||||
sendAndApply(&no);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SpellID::SCUTTLE_BOAT:
|
||||
{
|
||||
//check if spell works at all
|
||||
if(gs->getRandomGenerator().nextInt(99) >= s->getPower(schoolLevel)) //power is % chance of success
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 337); //%s tried to scuttle the boat, but failed
|
||||
iw.text.addReplacement(h->name);
|
||||
sendAndApply(&iw);
|
||||
break;
|
||||
}
|
||||
if(!gs->map->isInTheMap(pos))
|
||||
COMPLAIN_RET("Invalid dst tile for scuttle!");
|
||||
|
||||
//TODO: test range, visibility
|
||||
const TerrainTile *t = &gs->map->getTile(pos);
|
||||
if(!t->visitableObjects.size() || t->visitableObjects.back()->ID != Obj::BOAT)
|
||||
COMPLAIN_RET("There is no boat to scuttle!");
|
||||
|
||||
RemoveObject ro;
|
||||
ro.id = t->visitableObjects.back()->id;
|
||||
sendAndApply(&ro);
|
||||
break;
|
||||
}
|
||||
case SpellID::DIMENSION_DOOR:
|
||||
{
|
||||
const TerrainTile *dest = getTile(pos);
|
||||
const TerrainTile *curr = getTile(h->getSightCenter());
|
||||
|
||||
if(!dest)
|
||||
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->getPower(schoolLevel)) //limit casts per turn
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 338); //%s is not skilled enough to cast this spell again today.
|
||||
iw.text.addReplacement(h->name);
|
||||
sendAndApply(&iw);
|
||||
break;
|
||||
}
|
||||
|
||||
GiveBonus gb;
|
||||
gb.id = h->id.getNum();
|
||||
gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::NONE, Bonus::SPELL_EFFECT, 0, SpellID::DIMENSION_DOOR);
|
||||
sendAndApply(&gb);
|
||||
|
||||
if(!dest->isClear(curr)) //wrong dest tile
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->tempOwner;
|
||||
iw.text.addTxt(MetaString::GENERAL_TXT, 70); //Dimension Door failed!
|
||||
sendAndApply(&iw);
|
||||
break;
|
||||
}
|
||||
|
||||
if (moveHero(h->id, pos + h->getVisitableOffset(), true))
|
||||
{
|
||||
SetMovePoints smp;
|
||||
smp.hid = h->id;
|
||||
smp.val = std::max<ui32>(0, h->movement - 300);
|
||||
sendAndApply(&smp);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SpellID::FLY:
|
||||
{
|
||||
int subtype = schoolLevel >= 2 ? 1 : 2; //adv or expert
|
||||
|
||||
GiveBonus gb;
|
||||
gb.id = h->id.getNum();
|
||||
gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::FLYING_MOVEMENT, Bonus::SPELL_EFFECT, 0, SpellID::FLY, subtype);
|
||||
sendAndApply(&gb);
|
||||
}
|
||||
break;
|
||||
case SpellID::WATER_WALK:
|
||||
{
|
||||
int subtype = schoolLevel >= 2 ? 1 : 2; //adv or expert
|
||||
|
||||
GiveBonus gb;
|
||||
gb.id = h->id.getNum();
|
||||
gb.bonus = Bonus(Bonus::ONE_DAY, Bonus::WATER_WALKING, Bonus::SPELL_EFFECT, 0, SpellID::WATER_WALK, subtype);
|
||||
sendAndApply(&gb);
|
||||
}
|
||||
break;
|
||||
|
||||
case SpellID::TOWN_PORTAL:
|
||||
{
|
||||
if (!gs->map->isInTheMap(pos))
|
||||
COMPLAIN_RET("Destination tile not present!")
|
||||
TerrainTile tile = gs->map->getTile(pos);
|
||||
if (tile.visitableObjects.empty() || tile.visitableObjects.back()->ID != Obj::TOWN )
|
||||
COMPLAIN_RET("Town not found for Town Portal!");
|
||||
|
||||
CGTownInstance * town = static_cast<CGTownInstance*>(tile.visitableObjects.back());
|
||||
if (town->tempOwner != h->tempOwner)
|
||||
COMPLAIN_RET("Can't teleport to another player!");
|
||||
if (town->visitingHero)
|
||||
COMPLAIN_RET("Can't teleport to occupied town!");
|
||||
|
||||
if (h->getSpellSchoolLevel(s) < 2)
|
||||
{
|
||||
si32 dist = town->pos.dist2dSQ(h->pos);
|
||||
ObjectInstanceID nearest = town->id; //nearest town's ID
|
||||
for(const CGTownInstance * currTown : gs->getPlayer(h->tempOwner)->towns)
|
||||
{
|
||||
si32 currDist = currTown->pos.dist2dSQ(h->pos);
|
||||
if (currDist < dist)
|
||||
{
|
||||
nearest = currTown->id;
|
||||
dist = currDist;
|
||||
}
|
||||
}
|
||||
if (town->id != nearest)
|
||||
COMPLAIN_RET("This hero can only teleport to nearest town!")
|
||||
}
|
||||
moveHero(h->id, town->visitablePos() + h->getVisitableOffset() ,1);
|
||||
}
|
||||
break;
|
||||
|
||||
case SpellID::VISIONS:
|
||||
case SpellID::VIEW_EARTH:
|
||||
case SpellID::DISGUISE:
|
||||
case SpellID::VIEW_AIR:
|
||||
default:
|
||||
COMPLAIN_RET("This spell is not implemented yet!");
|
||||
}
|
||||
|
||||
SetMana sm;
|
||||
sm.hid = h->id;
|
||||
sm.absolute = false;
|
||||
sm.val = -cost;
|
||||
sendAndApply(&sm);
|
||||
|
||||
return true;
|
||||
|
||||
AdventureSpellCastParameters p;
|
||||
p.caster = h;
|
||||
p.pos = pos;
|
||||
|
||||
return s->adventureCast(spellEnv, p);
|
||||
}
|
||||
|
||||
void CGameHandler::visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h)
|
||||
@ -6010,7 +5803,7 @@ CGameHandler::FinishingBattleHelper::FinishingBattleHelper()
|
||||
winnerHero = loserHero = nullptr;
|
||||
}
|
||||
|
||||
|
||||
///ServerSpellCastEnvironment
|
||||
ServerSpellCastEnvironment::ServerSpellCastEnvironment(CGameHandler * gh): gh(gh)
|
||||
{
|
||||
|
||||
@ -6030,3 +5823,20 @@ void ServerSpellCastEnvironment::complain(const std::string& problem) const
|
||||
{
|
||||
gh->complain(problem);
|
||||
}
|
||||
|
||||
const CGameInfoCallback * ServerSpellCastEnvironment::getCb() const
|
||||
{
|
||||
return gh;
|
||||
}
|
||||
|
||||
|
||||
const CMap * ServerSpellCastEnvironment::getMap() const
|
||||
{
|
||||
return gh->gameState()->map;
|
||||
}
|
||||
|
||||
bool ServerSpellCastEnvironment::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, PlayerColor asker) const
|
||||
{
|
||||
return gh->moveHero(hid, dst, teleporting, asker);
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,7 @@
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CTownHandler.h"
|
||||
#include "../lib/CBuildingHandler.h"
|
||||
#include "../lib/CSpellHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CCreatureHandler.h"
|
||||
#include "zlib.h"
|
||||
#include "CVCMIServer.h"
|
||||
|
Loading…
Reference in New Issue
Block a user