mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +02:00
Merge branch 'develop' of https://github.com/vcmi/vcmi into mutexRelax
This commit is contained in:
commit
e14faea181
@ -643,7 +643,7 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo &AttackInfo
|
|||||||
auto attacker = AttackInfo.attacker;
|
auto attacker = AttackInfo.attacker;
|
||||||
auto enemy = AttackInfo.defender;
|
auto enemy = AttackInfo.defender;
|
||||||
|
|
||||||
const int remainingCounterAttacks = getValOr(state.counterAttacksLeft, enemy, enemy->counterAttacks);
|
const int remainingCounterAttacks = getValOr(state.counterAttacksLeft, enemy, enemy->counterAttacksRemaining());
|
||||||
const bool counterAttacksBlocked = attacker->hasBonusOfType(Bonus::BLOCKS_RETALIATION) || enemy->hasBonusOfType(Bonus::NO_RETALIATION);
|
const bool counterAttacksBlocked = attacker->hasBonusOfType(Bonus::BLOCKS_RETALIATION) || enemy->hasBonusOfType(Bonus::NO_RETALIATION);
|
||||||
const int totalAttacks = 1 + AttackInfo.attackerBonuses->getBonuses(Selector::type(Bonus::ADDITIONAL_ATTACK), (Selector::effectRange (Bonus::NO_LIMIT).Or(Selector::effectRange(Bonus::ONLY_MELEE_FIGHT))))->totalValue();
|
const int totalAttacks = 1 + AttackInfo.attackerBonuses->getBonuses(Selector::type(Bonus::ADDITIONAL_ATTACK), (Selector::effectRange (Bonus::NO_LIMIT).Or(Selector::effectRange(Bonus::ONLY_MELEE_FIGHT))))->totalValue();
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ SDL_Surface * BitmapHandler::loadH3PCX(ui8 * pcx, size_t size)
|
|||||||
tp.r = pcx[it++];
|
tp.r = pcx[it++];
|
||||||
tp.g = pcx[it++];
|
tp.g = pcx[it++];
|
||||||
tp.b = pcx[it++];
|
tp.b = pcx[it++];
|
||||||
CSDL_Ext::colorSetAlpha(tp,SDL_ALPHA_OPAQUE);
|
tp.a = SDL_ALPHA_OPAQUE;
|
||||||
ret->format->palette->colors[i] = tp;
|
ret->format->palette->colors[i] = tp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,7 +142,7 @@ SDL_Surface * BitmapHandler::loadBitmapFromDir(std::string path, std::string fna
|
|||||||
{
|
{
|
||||||
//set correct value for alpha\unused channel
|
//set correct value for alpha\unused channel
|
||||||
for (int i=0; i < ret->format->palette->ncolors; i++)
|
for (int i=0; i < ret->format->palette->ncolors; i++)
|
||||||
CSDL_Ext::colorSetAlpha(ret->format->palette->colors[i],SDL_ALPHA_OPAQUE);
|
ret->format->palette->colors[i].a = SDL_ALPHA_OPAQUE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -67,7 +67,7 @@ void CDefHandler::openFromMemory(ui8 *table, const std::string & name)
|
|||||||
palette[it].r = de.palette[it].R;
|
palette[it].r = de.palette[it].R;
|
||||||
palette[it].g = de.palette[it].G;
|
palette[it].g = de.palette[it].G;
|
||||||
palette[it].b = de.palette[it].B;
|
palette[it].b = de.palette[it].B;
|
||||||
CSDL_Ext::colorSetAlpha(palette[it],SDL_ALPHA_OPAQUE);
|
palette[it].a = SDL_ALPHA_OPAQUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The SDefEntryBlock starts just after the SDefEntry
|
// The SDefEntryBlock starts just after the SDefEntry
|
||||||
@ -122,12 +122,6 @@ void CDefHandler::openFromMemory(ui8 *table, const std::string & name)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDefHandler::expand(ui8 N,ui8 & BL, ui8 & BR)
|
|
||||||
{
|
|
||||||
BL = (N & 0xE0) >> 5;
|
|
||||||
BR = N & 0x1F;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Surface * CDefHandler::getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const
|
SDL_Surface * CDefHandler::getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const
|
||||||
{
|
{
|
||||||
SDL_Surface * ret=nullptr;
|
SDL_Surface * ret=nullptr;
|
||||||
@ -180,13 +174,12 @@ SDL_Surface * CDefHandler::getSprite (int SIndex, const ui8 * FDef, const SDL_Co
|
|||||||
|
|
||||||
BaseOffset += sizeof(SSpriteDef);
|
BaseOffset += sizeof(SSpriteDef);
|
||||||
int BaseOffsetor = BaseOffset;
|
int BaseOffsetor = BaseOffset;
|
||||||
|
|
||||||
if(SDL_SetPaletteColors(ret->format->palette,palette,0,256) != 0)
|
|
||||||
{
|
|
||||||
logGlobal->errorStream() << __FUNCTION__ <<": Unable to set palette";
|
|
||||||
logGlobal->errorStream() << SDL_GetError();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
SDL_Palette * p = SDL_AllocPalette(256);
|
||||||
|
SDL_SetPaletteColors(p, palette, 0, 256);
|
||||||
|
SDL_SetSurfacePalette(ret, p);
|
||||||
|
SDL_FreePalette(p);
|
||||||
|
|
||||||
int ftcp=0;
|
int ftcp=0;
|
||||||
|
|
||||||
// If there's a margin anywhere, just blank out the whole surface.
|
// If there's a margin anywhere, just blank out the whole surface.
|
||||||
|
@ -85,7 +85,9 @@ private:
|
|||||||
int group;
|
int group;
|
||||||
} ;
|
} ;
|
||||||
std::vector<SEntry> SEntries ;
|
std::vector<SEntry> SEntries ;
|
||||||
|
|
||||||
|
void openFromMemory(ui8 * table, const std::string & name);
|
||||||
|
SDL_Surface * getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const;
|
||||||
public:
|
public:
|
||||||
int width, height; //width and height
|
int width, height; //width and height
|
||||||
std::string defName;
|
std::string defName;
|
||||||
@ -94,9 +96,7 @@ public:
|
|||||||
|
|
||||||
CDefHandler(); //c-tor
|
CDefHandler(); //c-tor
|
||||||
~CDefHandler(); //d-tor
|
~CDefHandler(); //d-tor
|
||||||
SDL_Surface * getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const; //saves picture with given number to "testtt.bmp"
|
|
||||||
static void expand(ui8 N,ui8 & BL, ui8 & BR);
|
|
||||||
void openFromMemory(ui8 * table, const std::string & name);
|
|
||||||
CDefEssential * essentialize();
|
CDefEssential * essentialize();
|
||||||
|
|
||||||
static CDefHandler * giveDef(const std::string & defName);
|
static CDefHandler * giveDef(const std::string & defName);
|
||||||
|
@ -186,7 +186,7 @@ public:
|
|||||||
|
|
||||||
void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) override {};
|
void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) override {};
|
||||||
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) override {};
|
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) override {};
|
||||||
bool changeStackType(const StackLocation &sl, CCreature *c) override {return false;};
|
bool changeStackType(const StackLocation &sl, const CCreature *c) override {return false;};
|
||||||
bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) override {return false;};
|
bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) override {return false;};
|
||||||
bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count) override {return false;};
|
bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count) override {return false;};
|
||||||
bool eraseStack(const StackLocation &sl, bool forceRemoval = false){return false;};
|
bool eraseStack(const StackLocation &sl, bool forceRemoval = false){return false;};
|
||||||
|
@ -58,7 +58,7 @@ void Graphics::loadPaletteAndColors()
|
|||||||
col.r = pals[startPoint++];
|
col.r = pals[startPoint++];
|
||||||
col.g = pals[startPoint++];
|
col.g = pals[startPoint++];
|
||||||
col.b = pals[startPoint++];
|
col.b = pals[startPoint++];
|
||||||
CSDL_Ext::colorSetAlpha(col,SDL_ALPHA_OPAQUE);
|
col.a = SDL_ALPHA_OPAQUE;
|
||||||
startPoint++;
|
startPoint++;
|
||||||
playerColorPalette[i] = col;
|
playerColorPalette[i] = col;
|
||||||
}
|
}
|
||||||
@ -74,7 +74,7 @@ void Graphics::loadPaletteAndColors()
|
|||||||
neutralColorPalette[i].g = reader.readUInt8();
|
neutralColorPalette[i].g = reader.readUInt8();
|
||||||
neutralColorPalette[i].b = reader.readUInt8();
|
neutralColorPalette[i].b = reader.readUInt8();
|
||||||
reader.readUInt8(); // this is "flags" entry, not alpha
|
reader.readUInt8(); // this is "flags" entry, not alpha
|
||||||
CSDL_Ext::colorSetAlpha(neutralColorPalette[i], SDL_ALPHA_OPAQUE);
|
neutralColorPalette[i].a = SDL_ALPHA_OPAQUE;
|
||||||
}
|
}
|
||||||
//colors initialization
|
//colors initialization
|
||||||
SDL_Color colors[] = {
|
SDL_Color colors[] = {
|
||||||
@ -92,8 +92,11 @@ void Graphics::loadPaletteAndColors()
|
|||||||
{
|
{
|
||||||
playerColors[i] = colors[i];
|
playerColors[i] = colors[i];
|
||||||
}
|
}
|
||||||
neutralColor->r = 0x84; neutralColor->g = 0x84; neutralColor->b = 0x84; //gray
|
//gray
|
||||||
CSDL_Ext::colorSetAlpha(*neutralColor,SDL_ALPHA_OPAQUE);
|
neutralColor->r = 0x84;
|
||||||
|
neutralColor->g = 0x84;
|
||||||
|
neutralColor->b = 0x84;
|
||||||
|
neutralColor->a = SDL_ALPHA_OPAQUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::initializeBattleGraphics()
|
void Graphics::initializeBattleGraphics()
|
||||||
|
@ -283,7 +283,9 @@ void CDefenceAnimation::endAnim()
|
|||||||
|
|
||||||
CDummyAnimation::CDummyAnimation(CBattleInterface * _owner, int howManyFrames)
|
CDummyAnimation::CDummyAnimation(CBattleInterface * _owner, int howManyFrames)
|
||||||
: CBattleAnimation(_owner), counter(0), howMany(howManyFrames)
|
: CBattleAnimation(_owner), counter(0), howMany(howManyFrames)
|
||||||
{}
|
{
|
||||||
|
logAnim->debugStream() << "Created dummy animation for " << howManyFrames <<" frames";
|
||||||
|
}
|
||||||
|
|
||||||
bool CDummyAnimation::init()
|
bool CDummyAnimation::init()
|
||||||
{
|
{
|
||||||
|
@ -360,7 +360,7 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
|
|||||||
{
|
{
|
||||||
idToObstacle[ID] = CDefHandler::giveDef(elem->getInfo().defName);
|
idToObstacle[ID] = CDefHandler::giveDef(elem->getInfo().defName);
|
||||||
for(auto & _n : idToObstacle[ID]->ourImages)
|
for(auto & _n : idToObstacle[ID]->ourImages)
|
||||||
{
|
{
|
||||||
CSDL_Ext::setDefaultColorKey(_n.bitmap);
|
CSDL_Ext::setDefaultColorKey(_n.bitmap);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1240,15 +1240,9 @@ void CBattleInterface::displayBattleFinished()
|
|||||||
void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
||||||
{
|
{
|
||||||
const SpellID spellID(sc->id);
|
const SpellID spellID(sc->id);
|
||||||
const CSpell &spell = * spellID.toSpell();
|
const CSpell & spell = * spellID.toSpell();
|
||||||
const std::string & spellName = spell.name;
|
|
||||||
|
|
||||||
const std::string& castSoundPath = spell.getCastSound();
|
const std::string & castSoundPath = spell.getCastSound();
|
||||||
|
|
||||||
std::string casterName("Something");
|
|
||||||
|
|
||||||
if(sc->castedByHero)
|
|
||||||
casterName = curInt->cb->battleGetHeroInfo(sc->side).name;
|
|
||||||
|
|
||||||
if(!castSoundPath.empty())
|
if(!castSoundPath.empty())
|
||||||
CCS->soundh->playSound(castSoundPath);
|
CCS->soundh->playSound(castSoundPath);
|
||||||
@ -1262,19 +1256,16 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
|||||||
const CStack * casterStack = curInt->cb->battleGetStackByID(casterStackID);
|
const CStack * casterStack = curInt->cb->battleGetStackByID(casterStackID);
|
||||||
if(casterStack != nullptr)
|
if(casterStack != nullptr)
|
||||||
{
|
{
|
||||||
casterName = casterStack->type->namePl;
|
srccoord = CClickableHex::getXYUnitAnim(casterStack->position, casterStack, this);
|
||||||
srccoord = CClickableHex::getXYUnitAnim(casterStack->position, casterStack, this);
|
|
||||||
srccoord.x += 250;
|
srccoord.x += 250;
|
||||||
srccoord.y += 240;
|
srccoord.y += 240;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: play custom cast animation
|
//todo: play custom cast animation
|
||||||
{
|
displaySpellCast(spellID, BattleHex::INVALID, false);
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//playing projectile animation
|
//playing projectile animation
|
||||||
if(sc->tile.isValid())
|
if(sc->tile.isValid())
|
||||||
{
|
{
|
||||||
@ -1304,163 +1295,33 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
|
|||||||
delete animDef;
|
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();
|
waitForAnims();
|
||||||
|
|
||||||
displaySpellHit(spellID, sc->tile);
|
displaySpellHit(spellID, sc->tile);
|
||||||
|
|
||||||
//queuing affect /resist animation
|
//queuing affect animation
|
||||||
for (auto & elem : sc->affectedCres)
|
for(auto & elem : sc->affectedCres)
|
||||||
{
|
{
|
||||||
BattleHex position = curInt->cb->battleGetStackByID(elem, false)->position;
|
BattleHex position = curInt->cb->battleGetStackByID(elem, false)->position;
|
||||||
|
displaySpellEffect(spellID, position);
|
||||||
if(vstd::contains(sc->resisted,elem))
|
|
||||||
displayEffect(78, position);
|
|
||||||
else
|
|
||||||
displaySpellEffect(spellID, position);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(sc->id)
|
//queuing additional animation
|
||||||
|
for(auto & elem : sc->customEffects)
|
||||||
{
|
{
|
||||||
case SpellID::SUMMON_FIRE_ELEMENTAL:
|
BattleHex position = curInt->cb->battleGetStackByID(elem.stack, false)->position;
|
||||||
case SpellID::SUMMON_EARTH_ELEMENTAL:
|
displayEffect(elem.effect, position);
|
||||||
case SpellID::SUMMON_WATER_ELEMENTAL:
|
}
|
||||||
case SpellID::SUMMON_AIR_ELEMENTAL:
|
|
||||||
case SpellID::CLONE:
|
|
||||||
case SpellID::REMOVE_OBSTACLE:
|
|
||||||
addNewAnim(new CDummyAnimation(this, 2)); //interface won't return until animation is played. TODO: make it smarter?
|
|
||||||
break;
|
|
||||||
} //switch(sc->id)
|
|
||||||
|
|
||||||
//displaying message in console
|
//displaying message in console
|
||||||
bool customSpell = false;
|
std::vector<std::string> logLines;
|
||||||
if(sc->affectedCres.size() == 1)
|
|
||||||
{
|
spell.prepareBattleLog(curInt->cb.get(), sc, logLines);
|
||||||
const CStack * attackedStack = curInt->cb->battleGetStackByID(*sc->affectedCres.begin(), false);
|
|
||||||
|
for(auto line : logLines)
|
||||||
|
console->addText(line);
|
||||||
|
|
||||||
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", 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)
|
|
||||||
{
|
|
||||||
case SpellID::STONE_GAZE:
|
|
||||||
customSpell = true;
|
|
||||||
plural = true;
|
|
||||||
textID = 558;
|
|
||||||
break;
|
|
||||||
case SpellID::POISON:
|
|
||||||
customSpell = true;
|
|
||||||
plural = true;
|
|
||||||
textID = 561;
|
|
||||||
break;
|
|
||||||
case SpellID::BIND:
|
|
||||||
customSpell = true;
|
|
||||||
text = CGI->generaltexth->allTexts[560];
|
|
||||||
boost::algorithm::replace_first(text, "%s", attackedNamePl);
|
|
||||||
break;//Roots and vines bind the %s to the ground!
|
|
||||||
case SpellID::DISEASE:
|
|
||||||
customSpell = true;
|
|
||||||
plural = true;
|
|
||||||
textID = 553;
|
|
||||||
break;
|
|
||||||
case SpellID::PARALYZE:
|
|
||||||
customSpell = true;
|
|
||||||
plural = true;
|
|
||||||
textID = 563;
|
|
||||||
break;
|
|
||||||
case SpellID::AGE:
|
|
||||||
{
|
|
||||||
customSpell = true;
|
|
||||||
text = getPluralText(551);
|
|
||||||
boost::algorithm::replace_first(text, "%s", attackedName);
|
|
||||||
//The %s shrivel with age, and lose %d hit points."
|
|
||||||
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", 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
|
|
||||||
console->addText(text);
|
|
||||||
customSpell = true;
|
|
||||||
text = ""; //yeah, it's a terrible mess
|
|
||||||
break;
|
|
||||||
case SpellID::DISPEL_HELPFUL_SPELLS:
|
|
||||||
text = CGI->generaltexth->allTexts[555];
|
|
||||||
boost::algorithm::replace_first(text, "%s", attackedNamePl);
|
|
||||||
customSpell = true;
|
|
||||||
break;
|
|
||||||
case SpellID::DEATH_STARE:
|
|
||||||
customSpell = true;
|
|
||||||
if (sc->dmgToDisplay)
|
|
||||||
{
|
|
||||||
if (sc->dmgToDisplay > 1)
|
|
||||||
{
|
|
||||||
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", attackedNamePl);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
text = CGI->generaltexth->allTexts[118]; //One %s dies under the terrible gaze of the %s.
|
|
||||||
boost::algorithm::replace_first(text, "%s", attackedNameSing);
|
|
||||||
}
|
|
||||||
boost::algorithm::replace_first(text, "%s", casterName); //casting stack
|
|
||||||
}
|
|
||||||
else
|
|
||||||
text = "";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
text = CGI->generaltexth->allTexts[565]; //The %s casts %s
|
|
||||||
boost::algorithm::replace_first(text, "%s", casterName); //casting stack
|
|
||||||
|
|
||||||
}
|
|
||||||
if (plural)
|
|
||||||
{
|
|
||||||
text = getPluralText(textID);
|
|
||||||
boost::algorithm::replace_first(text, "%s", attackedName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!customSpell && !sc->dmgToDisplay)
|
|
||||||
boost::algorithm::replace_first(text, "%s", spellName); //simple spell name
|
|
||||||
if (text.size())
|
|
||||||
console->addText(text);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::string text = CGI->generaltexth->allTexts[196];
|
|
||||||
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", spellName); //simple spell name
|
|
||||||
boost::algorithm::replace_first(dmgInfo, "%d", boost::lexical_cast<std::string>(sc->dmgToDisplay));
|
|
||||||
console->addText(dmgInfo); //todo: casualties (?)
|
|
||||||
}
|
|
||||||
waitForAnims();
|
waitForAnims();
|
||||||
//mana absorption
|
//mana absorption
|
||||||
if(sc->manaGained > 0)
|
if(sc->manaGained > 0)
|
||||||
@ -1517,14 +1378,14 @@ void CBattleInterface::castThisSpell(SpellID spellID)
|
|||||||
assert(castingHero); // code below assumes non-null hero
|
assert(castingHero); // code below assumes non-null hero
|
||||||
sp = spellID.toSpell();
|
sp = spellID.toSpell();
|
||||||
spellSelMode = ANY_LOCATION;
|
spellSelMode = ANY_LOCATION;
|
||||||
|
|
||||||
const CSpell::TargetInfo ti = sp->getTargetInfo(castingHero->getSpellSchoolLevel(sp));
|
const CSpell::TargetInfo ti = sp->getTargetInfo(castingHero->getSpellSchoolLevel(sp));
|
||||||
|
|
||||||
if(ti.massive || ti.type == CSpell::NO_TARGET)
|
if(ti.massive || ti.type == CSpell::NO_TARGET)
|
||||||
spellSelMode = NO_LOCATION;
|
spellSelMode = NO_LOCATION;
|
||||||
else if(ti.type == CSpell::LOCATION && ti.clearAffected)
|
else if(ti.type == CSpell::LOCATION && ti.clearAffected)
|
||||||
{
|
{
|
||||||
spellSelMode = FREE_LOCATION;
|
spellSelMode = FREE_LOCATION;
|
||||||
}
|
}
|
||||||
else if(ti.type == CSpell::CREATURE)
|
else if(ti.type == CSpell::CREATURE)
|
||||||
{
|
{
|
||||||
@ -1532,11 +1393,11 @@ void CBattleInterface::castThisSpell(SpellID spellID)
|
|||||||
spellSelMode = selectionTypeByPositiveness(*sp);
|
spellSelMode = selectionTypeByPositiveness(*sp);
|
||||||
else
|
else
|
||||||
spellSelMode = ANY_CREATURE;
|
spellSelMode = ANY_CREATURE;
|
||||||
}
|
}
|
||||||
else if(ti.type == CSpell::OBSTACLE)
|
else if(ti.type == CSpell::OBSTACLE)
|
||||||
{
|
{
|
||||||
spellSelMode = OBSTACLE;
|
spellSelMode = OBSTACLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spellSelMode == NO_LOCATION) //user does not have to select location
|
if (spellSelMode == NO_LOCATION) //user does not have to select location
|
||||||
{
|
{
|
||||||
@ -1558,33 +1419,57 @@ void CBattleInterface::displayEffect(ui32 effect, int destTile, bool areaEffect)
|
|||||||
addNewAnim(new CSpellEffectAnimation(this, effect, destTile, 0, 0, false));
|
addNewAnim(new CSpellEffectAnimation(this, effect, destTile, 0, 0, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CBattleInterface::displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile, bool areaEffect)
|
||||||
|
{
|
||||||
|
if(animation.pause > 0)
|
||||||
|
{
|
||||||
|
addNewAnim(new CDummyAnimation(this, animation.pause));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CBattleInterface::displaySpellCast(SpellID spellID, BattleHex destinationTile, bool areaEffect)
|
||||||
|
{
|
||||||
|
const CSpell * spell = spellID.toSpell();
|
||||||
|
|
||||||
|
if(spell == nullptr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
for(const CSpell::TAnimation & animation : spell->animationInfo.cast)
|
||||||
|
{
|
||||||
|
displaySpellAnimation(animation, destinationTile, areaEffect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CBattleInterface::displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect)
|
void CBattleInterface::displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect)
|
||||||
{
|
{
|
||||||
const CSpell * spell = spellID.toSpell();
|
const CSpell * spell = spellID.toSpell();
|
||||||
|
|
||||||
if(spell == nullptr)
|
if(spell == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for(const CSpell::TAnimation & animation : spell->animationInfo.affect)
|
for(const CSpell::TAnimation & animation : spell->animationInfo.affect)
|
||||||
{
|
{
|
||||||
addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM));
|
displaySpellAnimation(animation, destinationTile, areaEffect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBattleInterface::displaySpellHit(SpellID spellID, BattleHex destinationTile, bool areaEffect)
|
void CBattleInterface::displaySpellHit(SpellID spellID, BattleHex destinationTile, bool areaEffect)
|
||||||
{
|
{
|
||||||
const CSpell * spell = spellID.toSpell();
|
const CSpell * spell = spellID.toSpell();
|
||||||
|
|
||||||
if(spell == nullptr)
|
if(spell == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for(const CSpell::TAnimation & animation : spell->animationInfo.hit)
|
for(const CSpell::TAnimation & animation : spell->animationInfo.hit)
|
||||||
{
|
{
|
||||||
addNewAnim(new CSpellEffectAnimation(this, animation.resourceName, destinationTile, false, animation.verticalPosition == VerticalPosition::BOTTOM));
|
displaySpellAnimation(animation, destinationTile, areaEffect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CBattleInterface::battleTriggerEffect(const BattleTriggerEffect & bte)
|
void CBattleInterface::battleTriggerEffect(const BattleTriggerEffect & bte)
|
||||||
{
|
{
|
||||||
const CStack * stack = curInt->cb->battleGetStackByID(bte.stackID);
|
const CStack * stack = curInt->cb->battleGetStackByID(bte.stackID);
|
||||||
@ -1712,8 +1597,8 @@ void CBattleInterface::endCastingSpell()
|
|||||||
{
|
{
|
||||||
assert(spellDestSelectMode);
|
assert(spellDestSelectMode);
|
||||||
|
|
||||||
delete spellToCast;
|
vstd::clear_pointer(spellToCast);
|
||||||
spellToCast = nullptr;
|
|
||||||
sp = nullptr;
|
sp = nullptr;
|
||||||
spellDestSelectMode = false;
|
spellDestSelectMode = false;
|
||||||
CCS->curh->changeGraphic(ECursor::COMBAT, ECursor::COMBAT_POINTER);
|
CCS->curh->changeGraphic(ECursor::COMBAT, ECursor::COMBAT_POINTER);
|
||||||
@ -1797,7 +1682,7 @@ void CBattleInterface::printConsoleAttacked( const CStack * defender, int dmg, i
|
|||||||
{
|
{
|
||||||
if (attacker)
|
if (attacker)
|
||||||
formattedText.append(" ");
|
formattedText.append(" ");
|
||||||
|
|
||||||
boost::format txt;
|
boost::format txt;
|
||||||
if(killed > 1)
|
if(killed > 1)
|
||||||
{
|
{
|
||||||
@ -2218,7 +2103,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
|
|||||||
{
|
{
|
||||||
ui8 skill = 0;
|
ui8 skill = 0;
|
||||||
if (creatureCasting)
|
if (creatureCasting)
|
||||||
skill = sactive->valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, SpellID::TELEPORT));
|
skill = sactive->getSpellSchoolLevel(SpellID(SpellID::TELEPORT).toSpell());
|
||||||
else
|
else
|
||||||
skill = getActiveHero()->getSpellSchoolLevel (CGI->spellh->objects[spellToCast->additionalInfo]);
|
skill = getActiveHero()->getSpellSchoolLevel (CGI->spellh->objects[spellToCast->additionalInfo]);
|
||||||
//TODO: explicitely save power, skill
|
//TODO: explicitely save power, skill
|
||||||
@ -2283,7 +2168,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
|
|||||||
|
|
||||||
if (vstd::contains(localActions, selectedAction)) //try to use last selected action by default
|
if (vstd::contains(localActions, selectedAction)) //try to use last selected action by default
|
||||||
currentAction = selectedAction;
|
currentAction = selectedAction;
|
||||||
else if (localActions.size()) //if not possible, select first available action 9they are sorted by suggested priority)
|
else if (localActions.size()) //if not possible, select first available action (they are sorted by suggested priority)
|
||||||
currentAction = localActions.front();
|
currentAction = localActions.front();
|
||||||
else //no legal action possible
|
else //no legal action possible
|
||||||
{
|
{
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
//#include "../../lib/CCreatureSet.h"
|
|
||||||
#include "../../lib/ConstTransitivePtr.h" //may be reundant
|
#include "../../lib/ConstTransitivePtr.h" //may be reundant
|
||||||
#include "../../lib/GameConstants.h"
|
#include "../../lib/GameConstants.h"
|
||||||
|
|
||||||
#include "CBattleAnimations.h"
|
#include "CBattleAnimations.h"
|
||||||
|
|
||||||
|
#include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CBattleInterface.h, part of VCMI engine
|
* CBattleInterface.h, part of VCMI engine
|
||||||
*
|
*
|
||||||
@ -155,7 +155,7 @@ private:
|
|||||||
|
|
||||||
shared_ptr<CPlayerInterface> tacticianInterface; //used during tactics mode, points to the interface of player with higher tactics (can be either attacker or defender in hot-seat), valid onloy for human players
|
shared_ptr<CPlayerInterface> tacticianInterface; //used during tactics mode, points to the interface of player with higher tactics (can be either attacker or defender in hot-seat), valid onloy for human players
|
||||||
bool tacticsMode;
|
bool tacticsMode;
|
||||||
bool stackCanCastSpell; //if true, active stack could possibly cats some target spell
|
bool stackCanCastSpell; //if true, active stack could possibly cast some target spell
|
||||||
bool creatureCasting; //if true, stack currently aims to cats a spell
|
bool creatureCasting; //if true, stack currently aims to cats a spell
|
||||||
bool spellDestSelectMode; //if true, player is choosing destination for his spell - only for GUI / console
|
bool spellDestSelectMode; //if true, player is choosing destination for his spell - only for GUI / console
|
||||||
PossibleActions spellSelMode;
|
PossibleActions spellSelMode;
|
||||||
@ -319,8 +319,12 @@ public:
|
|||||||
void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
|
void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
|
||||||
void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook
|
void 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 displayEffect(ui32 effect, int destTile, bool areaEffect = true); //displays custom effect on the battlefield
|
||||||
|
|
||||||
|
void displaySpellCast(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s cast animation
|
||||||
void displaySpellEffect(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s affected animation
|
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
|
void displaySpellHit(SpellID spellID, BattleHex destinationTile, bool areaEffect = true); //displays spell`s affected animation
|
||||||
|
|
||||||
|
void displaySpellAnimation(const CSpell::TAnimation & animation, BattleHex destinationTile, bool areaEffect = true);
|
||||||
|
|
||||||
void battleTriggerEffect(const BattleTriggerEffect & bte);
|
void battleTriggerEffect(const BattleTriggerEffect & bte);
|
||||||
void setBattleCursor(const int myNumber); //really complex and messy, sets attackingHex
|
void setBattleCursor(const int myNumber); //really complex and messy, sets attackingHex
|
||||||
|
@ -69,6 +69,7 @@ void CBattleConsole::showAll(SDL_Surface * to)
|
|||||||
|
|
||||||
bool CBattleConsole::addText(const std::string & text)
|
bool CBattleConsole::addText(const std::string & text)
|
||||||
{
|
{
|
||||||
|
logGlobal->traceStream() <<"CBattleConsole message: "<<text;
|
||||||
if(text.size()>70)
|
if(text.size()>70)
|
||||||
return false; //text too long!
|
return false; //text too long!
|
||||||
int firstInToken = 0;
|
int firstInToken = 0;
|
||||||
|
@ -178,7 +178,7 @@ CCreatureAnimation::CCreatureAnimation(std::string name, TSpeedController contro
|
|||||||
elem.r = reader.readUInt8();
|
elem.r = reader.readUInt8();
|
||||||
elem.g = reader.readUInt8();
|
elem.g = reader.readUInt8();
|
||||||
elem.b = reader.readUInt8();
|
elem.b = reader.readUInt8();
|
||||||
CSDL_Ext::colorSetAlpha(elem,0);
|
elem.a = SDL_ALPHA_OPAQUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i=0; i<totalBlocks; i++)
|
for (int i=0; i<totalBlocks; i++)
|
||||||
|
@ -158,7 +158,7 @@ CDefFile::CDefFile(std::string Name):
|
|||||||
palette[i].r = data[it++];
|
palette[i].r = data[it++];
|
||||||
palette[i].g = data[it++];
|
palette[i].g = data[it++];
|
||||||
palette[i].b = data[it++];
|
palette[i].b = data[it++];
|
||||||
CSDL_Ext::colorSetAlpha(palette[i],255);
|
palette[i].a = SDL_ALPHA_OPAQUE;
|
||||||
}
|
}
|
||||||
if (type == 71 || type == 64)//Buttons/buildings don't have shadows\semi-transparency
|
if (type == 71 || type == 64)//Buttons/buildings don't have shadows\semi-transparency
|
||||||
memset(palette, 0, sizeof(SDL_Color)*2);
|
memset(palette, 0, sizeof(SDL_Color)*2);
|
||||||
@ -355,7 +355,11 @@ void SDLImageLoader::init(Point SpriteSize, Point Margins, Point FullSize, SDL_C
|
|||||||
image->fullSize = FullSize;
|
image->fullSize = FullSize;
|
||||||
|
|
||||||
//Prepare surface
|
//Prepare surface
|
||||||
SDL_SetColors(image->surf, pal, 0, 256);
|
SDL_Palette * p = SDL_AllocPalette(256);
|
||||||
|
SDL_SetPaletteColors(p, pal, 0, 256);
|
||||||
|
SDL_SetSurfacePalette(image->surf, p);
|
||||||
|
SDL_FreePalette(p);
|
||||||
|
|
||||||
SDL_LockSurface(image->surf);
|
SDL_LockSurface(image->surf);
|
||||||
lineStart = position = (ui8*)image->surf->pixels;
|
lineStart = position = (ui8*)image->surf->pixels;
|
||||||
}
|
}
|
||||||
|
@ -59,11 +59,6 @@ inline bool isShiftKeyDown()
|
|||||||
}
|
}
|
||||||
namespace CSDL_Ext
|
namespace CSDL_Ext
|
||||||
{
|
{
|
||||||
//todo: remove
|
|
||||||
STRONG_INLINE void colorSetAlpha(SDL_Color & color, Uint8 alpha)
|
|
||||||
{
|
|
||||||
color.a = alpha;
|
|
||||||
}
|
|
||||||
//todo: should this better be assignment operator?
|
//todo: should this better be assignment operator?
|
||||||
STRONG_INLINE void colorAssign(SDL_Color & dest, const SDL_Color & source)
|
STRONG_INLINE void colorAssign(SDL_Color & dest, const SDL_Color & source)
|
||||||
{
|
{
|
||||||
|
@ -302,7 +302,7 @@ void CGarrisonSlot::clickLeft(tribool down, bool previousState)
|
|||||||
{
|
{
|
||||||
const CArmedInstance * selectedObj = owner->armedObjs[selection->upg];
|
const CArmedInstance * selectedObj = owner->armedObjs[selection->upg];
|
||||||
if (!creature && selectedObj->stacksCount() == 1)
|
if (!creature && selectedObj->stacksCount() == 1)
|
||||||
LOCPLINT->cb->splitStack(selectedObj, owner->armedObjs[upg], selection->ID, ID, myStack->count - 1);
|
LOCPLINT->cb->splitStack(selectedObj, owner->armedObjs[upg], selection->ID, ID, selection->myStack->count - 1);
|
||||||
else
|
else
|
||||||
LOCPLINT->cb->swapCreatures(owner->armedObjs[upg], owner->armedObjs[selection->upg], ID, selection->ID);
|
LOCPLINT->cb->swapCreatures(owner->armedObjs[upg], owner->armedObjs[selection->upg], ID, selection->ID);
|
||||||
}
|
}
|
||||||
|
@ -361,40 +361,35 @@ void MoraleLuckBox::set(const IBonusBearer *node)
|
|||||||
const int hoverTextBase[] = {7, 4};
|
const int hoverTextBase[] = {7, 4};
|
||||||
const Bonus::BonusType bonusType[] = {Bonus::LUCK, Bonus::MORALE};
|
const Bonus::BonusType bonusType[] = {Bonus::LUCK, Bonus::MORALE};
|
||||||
int (IBonusBearer::*getValue[])() const = {&IBonusBearer::LuckVal, &IBonusBearer::MoraleVal};
|
int (IBonusBearer::*getValue[])() const = {&IBonusBearer::LuckVal, &IBonusBearer::MoraleVal};
|
||||||
|
TBonusListPtr modifierList(new BonusList());
|
||||||
int mrlt = -9;
|
|
||||||
TModDescr mrl;
|
|
||||||
|
|
||||||
if (node)
|
if (node)
|
||||||
{
|
{
|
||||||
node->getModifiersWDescr(mrl, bonusType[morale]);
|
modifierList = node->getBonuses(Selector::type(bonusType[morale]));
|
||||||
bonusValue = (node->*getValue[morale])();
|
bonusValue = (node->*getValue[morale])();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
bonusValue = 0;
|
bonusValue = 0;
|
||||||
|
|
||||||
mrlt = (bonusValue>0)-(bonusValue<0); //signum: -1 - bad luck / morale, 0 - neutral, 1 - good
|
int mrlt = (bonusValue>0)-(bonusValue<0); //signum: -1 - bad luck / morale, 0 - neutral, 1 - good
|
||||||
hoverText = CGI->generaltexth->heroscrn[hoverTextBase[morale] - mrlt];
|
hoverText = CGI->generaltexth->heroscrn[hoverTextBase[morale] - mrlt];
|
||||||
baseType = componentType[morale];
|
baseType = componentType[morale];
|
||||||
text = CGI->generaltexth->arraytxt[textId[morale]];
|
text = CGI->generaltexth->arraytxt[textId[morale]];
|
||||||
boost::algorithm::replace_first(text,"%s",CGI->generaltexth->arraytxt[neutralDescr[morale]-mrlt]);
|
boost::algorithm::replace_first(text,"%s",CGI->generaltexth->arraytxt[neutralDescr[morale]-mrlt]);
|
||||||
if (!mrl.size())
|
|
||||||
text += CGI->generaltexth->arraytxt[noneTxtId];
|
if (morale && node && (node->hasBonusOfType(Bonus::UNDEAD)
|
||||||
|
|| node->hasBonusOfType(Bonus::BLOCK_MORALE)
|
||||||
|
|| node->hasBonusOfType(Bonus::NON_LIVING)))
|
||||||
|
text += CGI->generaltexth->arraytxt[113]; //unaffected by morale
|
||||||
|
else if(modifierList->empty())
|
||||||
|
text += CGI->generaltexth->arraytxt[noneTxtId];//no modifiers
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//it's a creature window
|
for(const Bonus * elem : *modifierList)
|
||||||
if ((morale && node && node->hasBonusOfType(Bonus::UNDEAD)) ||
|
|
||||||
node->hasBonusOfType(Bonus::BLOCK_MORALE) || node->hasBonusOfType(Bonus::NON_LIVING))
|
|
||||||
{
|
{
|
||||||
text += CGI->generaltexth->arraytxt[113]; //unaffected by morale
|
if(elem->val != 0)
|
||||||
}
|
//no bonuses with value 0
|
||||||
else
|
text += "\n" + elem->Description();
|
||||||
{
|
|
||||||
for(auto & elem : mrl)
|
|
||||||
{
|
|
||||||
if (elem.first) //no bonuses with value 0
|
|
||||||
text += "\n" + elem.second;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,10 +498,10 @@ void CTextInput::setText( const std::string &nText, bool callCb )
|
|||||||
|
|
||||||
bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key)
|
bool CTextInput::captureThisEvent(const SDL_KeyboardEvent & key)
|
||||||
{
|
{
|
||||||
if(key.keysym.sym == SDLK_RETURN || key.keysym.sym == SDLK_KP_ENTER)
|
if(key.keysym.sym == SDLK_RETURN || key.keysym.sym == SDLK_KP_ENTER || key.keysym.sym == SDLK_ESCAPE)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CTextInput::textInputed(const SDL_TextInputEvent & event)
|
void CTextInput::textInputed(const SDL_TextInputEvent & event)
|
||||||
|
@ -135,7 +135,7 @@ void CQuestLog::init()
|
|||||||
minimap = new CQuestMinimap (Rect (12, 12, 169, 169));
|
minimap = new CQuestMinimap (Rect (12, 12, 169, 169));
|
||||||
// TextBox have it's own 4 pixel padding from top at least for English. To achieve 10px from both left and top only add 6px margin
|
// TextBox have it's own 4 pixel padding from top at least for English. To achieve 10px from both left and top only add 6px margin
|
||||||
description = new CTextBox ("", Rect(205, 18, 385, DESCRIPTION_HEIGHT_MAX), CSlider::BROWN, FONT_MEDIUM, TOPLEFT, Colors::WHITE);
|
description = new CTextBox ("", Rect(205, 18, 385, DESCRIPTION_HEIGHT_MAX), CSlider::BROWN, FONT_MEDIUM, TOPLEFT, Colors::WHITE);
|
||||||
ok = new CButton(Point(539, 398), "IOKAY.DEF", CGI->generaltexth->zelp[445], boost::bind(&CQuestLog::close,this), SDLK_RETURN);
|
ok = new CButton(Point(539, 398), "IOKAY.DEF", CGI->generaltexth->zelp[445], std::bind(&CQuestLog::close,this), SDLK_RETURN);
|
||||||
// Both button and lable are shifted to -2px by x and y to not make them actually look like they're on same line with quests list and ok button
|
// Both button and lable are shifted to -2px by x and y to not make them actually look like they're on same line with quests list and ok button
|
||||||
hideCompleteButton = new CToggleButton(Point(10, 396), "sysopchk.def", CButton::tooltip(texts["hideComplete"]), std::bind(&CQuestLog::toggleComplete, this, _1));
|
hideCompleteButton = new CToggleButton(Point(10, 396), "sysopchk.def", CButton::tooltip(texts["hideComplete"]), std::bind(&CQuestLog::toggleComplete, this, _1));
|
||||||
hideCompleteLabel = new CLabel(46, 398, FONT_MEDIUM, TOPLEFT, Colors::WHITE, texts["hideComplete"]["label"].String());
|
hideCompleteLabel = new CLabel(46, 398, FONT_MEDIUM, TOPLEFT, Colors::WHITE, texts["hideComplete"]["label"].String());
|
||||||
@ -182,7 +182,7 @@ void CQuestLog::recreateLabelList()
|
|||||||
auto label = make_shared<CQuestLabel>(Rect(13, 195, 149,31), FONT_SMALL, TOPLEFT, Colors::WHITE, text.toString());
|
auto label = make_shared<CQuestLabel>(Rect(13, 195, 149,31), FONT_SMALL, TOPLEFT, Colors::WHITE, text.toString());
|
||||||
label->disable();
|
label->disable();
|
||||||
|
|
||||||
label->callback = boost::bind(&CQuestLog::selectQuest, this, i, currentLabel);
|
label->callback = std::bind(&CQuestLog::selectQuest, this, i, currentLabel);
|
||||||
labels.push_back(label);
|
labels.push_back(label);
|
||||||
|
|
||||||
// Select latest active quest
|
// Select latest active quest
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
],
|
],
|
||||||
"specialties":
|
"specialties":
|
||||||
[
|
[
|
||||||
{ "type":10, "val": 1, "subtype": 5, "info": 0 }
|
{ "type":1, "val": 0, "subtype": 0, "info": 106 }
|
||||||
],
|
],
|
||||||
"army" :
|
"army" :
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
@ -41,7 +41,7 @@
|
|||||||
],
|
],
|
||||||
"specialties":
|
"specialties":
|
||||||
[
|
[
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 106 }
|
{ "type":1, "val": 0, "subtype": 0, "info": 98 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"wystan":
|
"wystan":
|
||||||
@ -56,7 +56,7 @@
|
|||||||
],
|
],
|
||||||
"specialties":
|
"specialties":
|
||||||
[
|
[
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 98 }
|
{ "type":1, "val": 0, "subtype": 0, "info": 100 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tazar":
|
"tazar":
|
||||||
@ -70,7 +70,7 @@
|
|||||||
],
|
],
|
||||||
"specialties":
|
"specialties":
|
||||||
[
|
[
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 100 }
|
{ "type":2, "val": 5, "subtype": 23, "info": 0 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"alkin":
|
"alkin":
|
||||||
@ -85,7 +85,7 @@
|
|||||||
],
|
],
|
||||||
"specialties":
|
"specialties":
|
||||||
[
|
[
|
||||||
{ "type":2, "val": 5, "subtype": 23, "info": 0 }
|
{ "type":1, "val": 0, "subtype": 0, "info": 102 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"korbac":
|
"korbac":
|
||||||
@ -100,7 +100,7 @@
|
|||||||
],
|
],
|
||||||
"specialties":
|
"specialties":
|
||||||
[
|
[
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 102 }
|
{ "type":1, "val": 0, "subtype": 0, "info": 104 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"gerwulf":
|
"gerwulf":
|
||||||
@ -115,7 +115,7 @@
|
|||||||
],
|
],
|
||||||
"specialties":
|
"specialties":
|
||||||
[
|
[
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 104 }
|
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"broghild":
|
"broghild":
|
||||||
@ -130,7 +130,7 @@
|
|||||||
],
|
],
|
||||||
"specialties":
|
"specialties":
|
||||||
[
|
[
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 146 }
|
{ "type":1, "val": 0, "subtype": 0, "info": 108 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mirlanda":
|
"mirlanda":
|
||||||
@ -145,7 +145,7 @@
|
|||||||
],
|
],
|
||||||
"specialties":
|
"specialties":
|
||||||
[
|
[
|
||||||
{ "type":1, "val": 0, "subtype": 0, "info": 108 }
|
{ "type":8, "val": 0, "subtype": 45, "info": 0 }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"rosic":
|
"rosic":
|
||||||
|
@ -12,6 +12,10 @@
|
|||||||
"type": "array",
|
"type": "array",
|
||||||
"items":{
|
"items":{
|
||||||
"anyOf":[
|
"anyOf":[
|
||||||
|
{
|
||||||
|
//dummy animation, pause, Value - frame count
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
//assumed verticalPosition: top
|
//assumed verticalPosition: top
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
@ -242,7 +242,9 @@
|
|||||||
"removeObstacle" : {
|
"removeObstacle" : {
|
||||||
"index" : 64,
|
"index" : 64,
|
||||||
"targetType" : "OBSTACLE",
|
"targetType" : "OBSTACLE",
|
||||||
|
"animation":{
|
||||||
|
"cast":[2]
|
||||||
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "REMOVEOB"
|
"cast": "REMOVEOB"
|
||||||
},
|
},
|
||||||
@ -258,7 +260,9 @@
|
|||||||
"clone" : {
|
"clone" : {
|
||||||
"index" : 65,
|
"index" : 65,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
"animation":{
|
||||||
|
"cast":[2]
|
||||||
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "CLONE"
|
"cast": "CLONE"
|
||||||
},
|
},
|
||||||
@ -278,7 +282,9 @@
|
|||||||
"fireElemental" : {
|
"fireElemental" : {
|
||||||
"index" : 66,
|
"index" : 66,
|
||||||
"targetType" : "NO_TARGET",
|
"targetType" : "NO_TARGET",
|
||||||
|
"animation":{
|
||||||
|
"cast":[2]
|
||||||
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "SUMNELM"
|
"cast": "SUMNELM"
|
||||||
},
|
},
|
||||||
@ -294,7 +300,9 @@
|
|||||||
"earthElemental" : {
|
"earthElemental" : {
|
||||||
"index" : 67,
|
"index" : 67,
|
||||||
"targetType" : "NO_TARGET",
|
"targetType" : "NO_TARGET",
|
||||||
|
"animation":{
|
||||||
|
"cast":[2]
|
||||||
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "SUMNELM"
|
"cast": "SUMNELM"
|
||||||
},
|
},
|
||||||
@ -310,7 +318,9 @@
|
|||||||
"waterElemental" : {
|
"waterElemental" : {
|
||||||
"index" : 68,
|
"index" : 68,
|
||||||
"targetType" : "NO_TARGET",
|
"targetType" : "NO_TARGET",
|
||||||
|
"animation":{
|
||||||
|
"cast":[2]
|
||||||
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "SUMNELM"
|
"cast": "SUMNELM"
|
||||||
},
|
},
|
||||||
@ -326,7 +336,9 @@
|
|||||||
"airElemental" : {
|
"airElemental" : {
|
||||||
"index" : 69,
|
"index" : 69,
|
||||||
"targetType" : "NO_TARGET",
|
"targetType" : "NO_TARGET",
|
||||||
|
"animation":{
|
||||||
|
"cast":[2]
|
||||||
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "SUMNELM"
|
"cast": "SUMNELM"
|
||||||
},
|
},
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
{
|
{
|
||||||
"shield" : {
|
"shield" : {
|
||||||
"index" : 27,
|
"index" : 27,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C13SPE0"]
|
"affect":["C13SPE0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "SHIELD"
|
"cast": "SHIELD"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"generalDamageReduction" : {
|
"generalDamageReduction" : {
|
||||||
@ -32,16 +32,16 @@
|
|||||||
"airShield" : {
|
"airShield" : {
|
||||||
"index" : 28,
|
"index" : 28,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C01SPA0"]
|
"affect":["C01SPA0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "AIRSHELD"
|
"cast": "AIRSHELD"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"generalDamageReduction" : {
|
"generalDamageReduction" : {
|
||||||
@ -62,19 +62,19 @@
|
|||||||
"fireShield" : {
|
"fireShield" : {
|
||||||
"index" : 29,
|
"index" : 29,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C05SPF0"]
|
"affect":["C05SPF0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "FIRESHIE"
|
"cast": "FIRESHIE"
|
||||||
},
|
},
|
||||||
// It looks that fireshield has two separate sounds
|
// It looks that fireshield has two separate sounds
|
||||||
// "soundfile":"FIRESHLD.wav"
|
// "soundfile":"FIRESHLD.wav"
|
||||||
//
|
//
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"fireShield" : {
|
"fireShield" : {
|
||||||
@ -82,9 +82,6 @@
|
|||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"expert":{
|
|
||||||
"range" : "X"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
@ -94,10 +91,10 @@
|
|||||||
"protectAir" : {
|
"protectAir" : {
|
||||||
"index" : 30,
|
"index" : 30,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C11SPE0"]
|
"affect":["C11SPE0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "PROTECTA"
|
"cast": "PROTECTA"
|
||||||
},
|
},
|
||||||
@ -124,10 +121,10 @@
|
|||||||
"protectFire" : {
|
"protectFire" : {
|
||||||
"index" : 31,
|
"index" : 31,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C11SPW0"]
|
"affect":["C11SPW0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "PROTECTF"
|
"cast": "PROTECTF"
|
||||||
},
|
},
|
||||||
@ -154,16 +151,16 @@
|
|||||||
"protectWater" : {
|
"protectWater" : {
|
||||||
"index" : 32,
|
"index" : 32,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C11SPF0"]
|
"affect":["C11SPF0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "PROTECTW"
|
"cast": "PROTECTW"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"spellDamageReduction" : {
|
"spellDamageReduction" : {
|
||||||
@ -184,10 +181,10 @@
|
|||||||
"protectEarth" : {
|
"protectEarth" : {
|
||||||
"index" : 33,
|
"index" : 33,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C13SPA0"]
|
"affect":["C13SPA0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "PROTECTE"
|
"cast": "PROTECTE"
|
||||||
},
|
},
|
||||||
@ -214,10 +211,10 @@
|
|||||||
"antiMagic" : {
|
"antiMagic" : {
|
||||||
"index" : 34,
|
"index" : 34,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C02SPE0"]
|
"affect":["C02SPE0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "ANTIMAGK"
|
"cast": "ANTIMAGK"
|
||||||
},
|
},
|
||||||
@ -227,7 +224,7 @@
|
|||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"levelSpellImmunity" : {
|
"levelSpellImmunity" : {
|
||||||
"val" : 3,
|
"val" : 3,
|
||||||
"type" : "LEVEL_SPELL_IMMUNITY",
|
"type" : "LEVEL_SPELL_IMMUNITY",
|
||||||
"valueType" : "INDEPENDENT_MAX",
|
"valueType" : "INDEPENDENT_MAX",
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
@ -257,10 +254,10 @@
|
|||||||
"magicMirror" : {
|
"magicMirror" : {
|
||||||
"index" : 36,
|
"index" : 36,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C02SPA0"]
|
"affect":["C02SPA0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "BACKLASH"
|
"cast": "BACKLASH"
|
||||||
},
|
},
|
||||||
@ -284,16 +281,16 @@
|
|||||||
"bless" : {
|
"bless" : {
|
||||||
"index" : 41,
|
"index" : 41,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C01SPW"] //C01SPW0
|
"affect":["C01SPW"] //C01SPW0
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "BLESS"
|
"cast": "BLESS"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"alwaysMaximumDamage" : {
|
"alwaysMaximumDamage" : {
|
||||||
@ -322,16 +319,16 @@
|
|||||||
"curse" : {
|
"curse" : {
|
||||||
"index" : 42,
|
"index" : 42,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C04SPW"]//C04SPW0
|
"affect":["C04SPW"]//C04SPW0
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "CURSE"
|
"cast": "CURSE"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"alwaysMinimumDamage" : {
|
"alwaysMinimumDamage" : {
|
||||||
@ -362,20 +359,20 @@
|
|||||||
"bloodlust" : {
|
"bloodlust" : {
|
||||||
"index" : 43,
|
"index" : 43,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["SP12_"] //???
|
"affect":["SP12_"] //???
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "BLOODLUS"
|
"cast": "BLOODLUS"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"primarySkill" : {
|
"primarySkill" : {
|
||||||
"val" : 3,
|
"val" : 3,
|
||||||
"type" : "PRIMARY_SKILL",
|
"type" : "PRIMARY_SKILL",
|
||||||
"subtype" : "primSkill.attack",
|
"subtype" : "primSkill.attack",
|
||||||
"effectRange" : "ONLY_MELEE_FIGHT",
|
"effectRange" : "ONLY_MELEE_FIGHT",
|
||||||
@ -412,10 +409,10 @@
|
|||||||
"precision" : {
|
"precision" : {
|
||||||
"index" : 44,
|
"index" : 44,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C12SPA0"]
|
"affect":["C12SPA0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "PRECISON"
|
"cast": "PRECISON"
|
||||||
},
|
},
|
||||||
@ -459,22 +456,22 @@
|
|||||||
"weakness" : {
|
"weakness" : {
|
||||||
"index" : 45,
|
"index" : 45,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C0ACID"]
|
"affect":["C0ACID"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "WEAKNESS"
|
"cast": "WEAKNESS"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"primarySkill" : {
|
"primarySkill" : {
|
||||||
"type" : "PRIMARY_SKILL",
|
"type" : "PRIMARY_SKILL",
|
||||||
"subtype" : "primSkill.attack",
|
"subtype" : "primSkill.attack",
|
||||||
"val" : -3,
|
"val" : -3,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -506,22 +503,22 @@
|
|||||||
"stoneSkin" : {
|
"stoneSkin" : {
|
||||||
"index" : 46,
|
"index" : 46,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C16SPE"] //C16SPE0
|
"affect":["C16SPE"] //C16SPE0
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "TUFFSKIN"
|
"cast": "TUFFSKIN"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"primarySkill" : {
|
"primarySkill" : {
|
||||||
"type" : "PRIMARY_SKILL",
|
"type" : "PRIMARY_SKILL",
|
||||||
"subtype" : "primSkill.defence",
|
"subtype" : "primSkill.defence",
|
||||||
"val" : 3,
|
"val" : 3,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -549,11 +546,11 @@
|
|||||||
"disruptingRay" : {
|
"disruptingRay" : {
|
||||||
"index" : 47,
|
"index" : 47,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C07SPA1"],
|
"affect":["C07SPA1"],
|
||||||
"projectile":[{"defName":"C07SPA0"}]//???
|
"projectile":[{"defName":"C07SPA0"}]//???
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "DISRUPTR"
|
"cast": "DISRUPTR"
|
||||||
},
|
},
|
||||||
@ -565,7 +562,7 @@
|
|||||||
"primarySkill" : {
|
"primarySkill" : {
|
||||||
"type" : "PRIMARY_SKILL",
|
"type" : "PRIMARY_SKILL",
|
||||||
"subtype" : "primSkill.defence",
|
"subtype" : "primSkill.defence",
|
||||||
"val" : -3,
|
"val" : -3,
|
||||||
"valueType" : "ADDITIVE_VALUE",
|
"valueType" : "ADDITIVE_VALUE",
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
}
|
}
|
||||||
@ -593,34 +590,34 @@
|
|||||||
"prayer" : {
|
"prayer" : {
|
||||||
"index" : 48,
|
"index" : 48,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":[{"defName":"C10SPW", "verticalPosition":"bottom"}]
|
"affect":[{"defName":"C10SPW", "verticalPosition":"bottom"}]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "PRAYER"
|
"cast": "PRAYER"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"attack" : {
|
"attack" : {
|
||||||
"type" : "PRIMARY_SKILL",
|
"type" : "PRIMARY_SKILL",
|
||||||
"subtype" : "primSkill.attack",
|
"subtype" : "primSkill.attack",
|
||||||
"val" : 2,
|
"val" : 2,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
},
|
},
|
||||||
"defence" : {
|
"defence" : {
|
||||||
"type" : "PRIMARY_SKILL",
|
"type" : "PRIMARY_SKILL",
|
||||||
"subtype" : "primSkill.defence",
|
"subtype" : "primSkill.defence",
|
||||||
"val" : 2,
|
"val" : 2,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
},
|
},
|
||||||
"stacksSpeed" : {
|
"stacksSpeed" : {
|
||||||
"addInfo" : 0,
|
"addInfo" : 0,
|
||||||
"type" : "STACKS_SPEED",
|
"type" : "STACKS_SPEED",
|
||||||
"val" : 2,
|
"val" : 2,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -660,21 +657,21 @@
|
|||||||
"mirth" : {
|
"mirth" : {
|
||||||
"index" : 49,
|
"index" : 49,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C09SPW0"]
|
"affect":["C09SPW0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "MIRTH"
|
"cast": "MIRTH"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"morale" : {
|
"morale" : {
|
||||||
"type" : "MORALE",
|
"type" : "MORALE",
|
||||||
"val" : 1,
|
"val" : 1,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -700,7 +697,7 @@
|
|||||||
},
|
},
|
||||||
"absoluteImmunity":{
|
"absoluteImmunity":{
|
||||||
"SIEGE_WEAPON": true,
|
"SIEGE_WEAPON": true,
|
||||||
"UNDEAD": true,
|
"UNDEAD": true,
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"immunity" : {
|
||||||
"MIND_IMMUNITY": true,
|
"MIND_IMMUNITY": true,
|
||||||
@ -713,21 +710,21 @@
|
|||||||
"sorrow" : {
|
"sorrow" : {
|
||||||
"index" : 50,
|
"index" : 50,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C14SPE0"]
|
"affect":["C14SPE0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "SORROW"
|
"cast": "SORROW"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"morale" : {
|
"morale" : {
|
||||||
"type" : "MORALE",
|
"type" : "MORALE",
|
||||||
"val" : -1,
|
"val" : -1,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -753,7 +750,7 @@
|
|||||||
},
|
},
|
||||||
"absoluteImmunity":{
|
"absoluteImmunity":{
|
||||||
"SIEGE_WEAPON": true,
|
"SIEGE_WEAPON": true,
|
||||||
"UNDEAD": true,
|
"UNDEAD": true,
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"immunity" : {
|
||||||
"MIND_IMMUNITY": true,
|
"MIND_IMMUNITY": true,
|
||||||
@ -766,21 +763,21 @@
|
|||||||
"fortune" : {
|
"fortune" : {
|
||||||
"index" : 51,
|
"index" : 51,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C09SPA0"]
|
"affect":["C09SPA0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "FORTUNE"
|
"cast": "FORTUNE"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"luck" : {
|
"luck" : {
|
||||||
"type" : "LUCK",
|
"type" : "LUCK",
|
||||||
"val" : 1,
|
"val" : 1,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -811,21 +808,21 @@
|
|||||||
"misfortune" : {
|
"misfortune" : {
|
||||||
"index" : 52,
|
"index" : 52,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C10SPF0"]
|
"affect":["C10SPF0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "MISFORT"
|
"cast": "MISFORT"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"luck" : {
|
"luck" : {
|
||||||
"type" : "LUCK",
|
"type" : "LUCK",
|
||||||
"val" : -1,
|
"val" : -1,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -856,22 +853,22 @@
|
|||||||
"haste" : {
|
"haste" : {
|
||||||
"index" : 53,
|
"index" : 53,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C15SPA0"]
|
"affect":["C15SPA0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "HASTE"
|
"cast": "HASTE"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"stacksSpeed" : {
|
"stacksSpeed" : {
|
||||||
"addInfo" : 0,
|
"addInfo" : 0,
|
||||||
"type" : "STACKS_SPEED",
|
"type" : "STACKS_SPEED",
|
||||||
"val" : 3,
|
"val" : 3,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -905,22 +902,22 @@
|
|||||||
"slow" : {
|
"slow" : {
|
||||||
"index" : 54,
|
"index" : 54,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":[{"defName":"C09SPE0", "verticalPosition":"bottom"}]
|
"affect":[{"defName":"C09SPE0", "verticalPosition":"bottom"}]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "MUCKMIRE"
|
"cast": "MUCKMIRE"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"stacksSpeed" : {
|
"stacksSpeed" : {
|
||||||
"addInfo" : 0,
|
"addInfo" : 0,
|
||||||
"type" : "STACKS_SPEED",
|
"type" : "STACKS_SPEED",
|
||||||
"val" : -25,
|
"val" : -25,
|
||||||
"valueType" : "PERCENT_TO_ALL",
|
"valueType" : "PERCENT_TO_ALL",
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
}
|
}
|
||||||
@ -956,10 +953,10 @@
|
|||||||
"slayer" : {
|
"slayer" : {
|
||||||
"index" : 55,
|
"index" : 55,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C13SPW0"]
|
"affect":["C13SPW0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "SLAYER"
|
"cast": "SLAYER"
|
||||||
},
|
},
|
||||||
@ -1010,10 +1007,10 @@
|
|||||||
"frenzy" : {
|
"frenzy" : {
|
||||||
"index" : 56,
|
"index" : 56,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C08SPF0"]
|
"affect":["C08SPF0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "FRENZY"
|
"cast": "FRENZY"
|
||||||
},
|
},
|
||||||
@ -1024,8 +1021,9 @@
|
|||||||
"effects" : {
|
"effects" : {
|
||||||
"inFrenzy" : {
|
"inFrenzy" : {
|
||||||
"type" : "IN_FRENZY",
|
"type" : "IN_FRENZY",
|
||||||
"val" : 100,
|
"val" : 100,
|
||||||
"duration" : "STACK_GETS_TURN"
|
"duration" : "N_TURNS",
|
||||||
|
"turns" : 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1055,21 +1053,21 @@
|
|||||||
"counterstrike" : {
|
"counterstrike" : {
|
||||||
"index" : 58,
|
"index" : 58,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C04SPA0"]
|
"affect":["C04SPA0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "CNTRSTRK"
|
"cast": "CNTRSTRK"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"additionalRetaliation" : {
|
"additionalRetaliation" : {
|
||||||
"type" : "ADDITIONAL_RETALIATION",
|
"type" : "ADDITIONAL_RETALIATION",
|
||||||
"val" : 1,
|
"val" : 1,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1099,11 +1097,11 @@
|
|||||||
},
|
},
|
||||||
"berserk" : {
|
"berserk" : {
|
||||||
"index" : 59,
|
"index" : 59,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "LOCATION",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C01SPF"] //C01SPF0
|
"affect":["C01SPF"] //C01SPF0
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "BERSERK"
|
"cast": "BERSERK"
|
||||||
},
|
},
|
||||||
@ -1114,7 +1112,8 @@
|
|||||||
"effects" : {
|
"effects" : {
|
||||||
"attacksNearestCreature" : {
|
"attacksNearestCreature" : {
|
||||||
"type" : "ATTACKS_NEAREST_CREATURE",
|
"type" : "ATTACKS_NEAREST_CREATURE",
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS",
|
||||||
|
"turns" : 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -1164,10 +1163,10 @@
|
|||||||
"hypnotize" : {
|
"hypnotize" : {
|
||||||
"index" : 60,
|
"index" : 60,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C10SPA0"]
|
"affect":["C10SPA0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "HYPNOTIZ"
|
"cast": "HYPNOTIZ"
|
||||||
},
|
},
|
||||||
@ -1225,17 +1224,17 @@
|
|||||||
},
|
},
|
||||||
"forgetfulness" : {
|
"forgetfulness" : {
|
||||||
"index" : 61,
|
"index" : 61,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C06SPW"]//C06SPW0
|
"affect":["C06SPW"]//C06SPW0
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "FORGET"
|
"cast": "FORGET"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"effects" : {
|
||||||
"forgetful" : {
|
"forgetful" : {
|
||||||
@ -1292,10 +1291,10 @@
|
|||||||
"blind" : {
|
"blind" : {
|
||||||
"index" : 62,
|
"index" : 62,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C02SPF0"]
|
"affect":["C02SPF0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "BLIND"
|
"cast": "BLIND"
|
||||||
},
|
},
|
||||||
|
@ -763,7 +763,7 @@ CGHeroInstance * BattleInfo::battleGetFightingHero(ui8 side) const
|
|||||||
|
|
||||||
CStack::CStack(const CStackInstance *Base, PlayerColor O, int I, bool AO, SlotID S)
|
CStack::CStack(const CStackInstance *Base, PlayerColor O, int I, bool AO, SlotID S)
|
||||||
: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),
|
: base(Base), ID(I), owner(O), slot(S), attackerOwned(AO),
|
||||||
counterAttacks(1)
|
counterAttacksPerformed(0),counterAttacksTotalCache(0), cloneID(-1)
|
||||||
{
|
{
|
||||||
assert(base);
|
assert(base);
|
||||||
type = base->type;
|
type = base->type;
|
||||||
@ -776,7 +776,8 @@ CStack::CStack()
|
|||||||
setNodeType(STACK_BATTLE);
|
setNodeType(STACK_BATTLE);
|
||||||
}
|
}
|
||||||
CStack::CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S)
|
CStack::CStack(const CStackBasicDescriptor *stack, PlayerColor O, int I, bool AO, SlotID S)
|
||||||
: base(nullptr), ID(I), owner(O), slot(S), attackerOwned(AO), counterAttacks(1)
|
: base(nullptr), ID(I), owner(O), slot(S), attackerOwned(AO), counterAttacksPerformed(0),
|
||||||
|
cloneID(-1)
|
||||||
{
|
{
|
||||||
type = stack->type;
|
type = stack->type;
|
||||||
count = baseAmount = stack->count;
|
count = baseAmount = stack->count;
|
||||||
@ -794,7 +795,9 @@ void CStack::init()
|
|||||||
slot = SlotID(255);
|
slot = SlotID(255);
|
||||||
attackerOwned = false;
|
attackerOwned = false;
|
||||||
position = BattleHex();
|
position = BattleHex();
|
||||||
counterAttacks = -1;
|
counterAttacksPerformed = 0;
|
||||||
|
counterAttacksTotalCache = 0;
|
||||||
|
cloneID = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStack::postInit()
|
void CStack::postInit()
|
||||||
@ -804,9 +807,11 @@ void CStack::postInit()
|
|||||||
|
|
||||||
firstHPleft = MaxHealth();
|
firstHPleft = MaxHealth();
|
||||||
shots = getCreature()->valOfBonuses(Bonus::SHOTS);
|
shots = getCreature()->valOfBonuses(Bonus::SHOTS);
|
||||||
counterAttacks = 1 + valOfBonuses(Bonus::ADDITIONAL_RETALIATION);
|
counterAttacksPerformed = 0;
|
||||||
|
counterAttacksTotalCache = 0;
|
||||||
casts = valOfBonuses(Bonus::CASTS);
|
casts = valOfBonuses(Bonus::CASTS);
|
||||||
resurrected = 0;
|
resurrected = 0;
|
||||||
|
cloneID = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ui32 CStack::level() const
|
ui32 CStack::level() const
|
||||||
@ -848,10 +853,10 @@ void CStack::stackEffectToFeature(std::vector<Bonus> & sf, const Bonus & sse)
|
|||||||
|
|
||||||
for(Bonus& b : tmp)
|
for(Bonus& b : tmp)
|
||||||
{
|
{
|
||||||
b.turnsRemain = sse.turnsRemain;
|
if(b.turnsRemain == 0)
|
||||||
|
b.turnsRemain = sse.turnsRemain;
|
||||||
sf.push_back(b);
|
sf.push_back(b);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CStack::willMove(int turn /*= 0*/) const
|
bool CStack::willMove(int turn /*= 0*/) const
|
||||||
@ -1129,12 +1134,25 @@ bool CStack::isMeleeAttackPossible(const CStack * attacker, const CStack * defen
|
|||||||
bool CStack::ableToRetaliate() const //FIXME: crash after clone is killed
|
bool CStack::ableToRetaliate() const //FIXME: crash after clone is killed
|
||||||
{
|
{
|
||||||
return alive()
|
return alive()
|
||||||
&& (counterAttacks > 0 || hasBonusOfType(Bonus::UNLIMITED_RETALIATIONS))
|
&& (counterAttacksPerformed < counterAttacksTotal() || hasBonusOfType(Bonus::UNLIMITED_RETALIATIONS))
|
||||||
&& !hasBonusOfType(Bonus::SIEGE_WEAPON)
|
&& !hasBonusOfType(Bonus::SIEGE_WEAPON)
|
||||||
&& !hasBonusOfType(Bonus::HYPNOTIZED)
|
&& !hasBonusOfType(Bonus::HYPNOTIZED)
|
||||||
&& !hasBonusOfType(Bonus::NO_RETALIATION);
|
&& !hasBonusOfType(Bonus::NO_RETALIATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui8 CStack::counterAttacksTotal() const
|
||||||
|
{
|
||||||
|
//after dispell bonus should remain during current round
|
||||||
|
ui8 val = 1 + valOfBonuses(Bonus::ADDITIONAL_RETALIATION);
|
||||||
|
vstd::amax(counterAttacksTotalCache, val);
|
||||||
|
return counterAttacksTotalCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
si8 CStack::counterAttacksRemaining() const
|
||||||
|
{
|
||||||
|
return counterAttacksTotal() - counterAttacksPerformed;
|
||||||
|
}
|
||||||
|
|
||||||
std::string CStack::getName() const
|
std::string CStack::getName() const
|
||||||
{
|
{
|
||||||
return (count > 1) ? type->namePl : type->nameSing; //War machines can't use base
|
return (count > 1) ? type->namePl : type->nameSing; //War machines can't use base
|
||||||
@ -1152,6 +1170,21 @@ bool CStack::canBeHealed() const
|
|||||||
&& !hasBonusOfType(Bonus::SIEGE_WEAPON);
|
&& !hasBonusOfType(Bonus::SIEGE_WEAPON);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui8 CStack::getSpellSchoolLevel(const CSpell * spell, int * outSelectedSchool) const
|
||||||
|
{
|
||||||
|
int skill = valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, spell->id));
|
||||||
|
|
||||||
|
vstd::abetween(skill, 0, 3);
|
||||||
|
|
||||||
|
return skill;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui32 CStack::getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const
|
||||||
|
{
|
||||||
|
//stacks does not have spellpower etc. (yet?)
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
bool CMP_stack::operator()( const CStack* a, const CStack* b )
|
bool CMP_stack::operator()( const CStack* a, const CStack* b )
|
||||||
{
|
{
|
||||||
switch(phase)
|
switch(phase)
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
#pragma once
|
/*
|
||||||
|
* BattleState.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 "BattleHex.h"
|
#include "BattleHex.h"
|
||||||
#include "HeroBonus.h"
|
#include "HeroBonus.h"
|
||||||
@ -12,16 +21,7 @@
|
|||||||
#include "GameConstants.h"
|
#include "GameConstants.h"
|
||||||
#include "CBattleCallback.h"
|
#include "CBattleCallback.h"
|
||||||
#include "int3.h"
|
#include "int3.h"
|
||||||
|
#include "spells/Magic.h"
|
||||||
/*
|
|
||||||
* BattleState.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 CGHeroInstance;
|
class CGHeroInstance;
|
||||||
class CStack;
|
class CStack;
|
||||||
@ -159,7 +159,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode, public CBattleInfoCallb
|
|||||||
static int battlefieldTypeToTerrain(int bfieldType); //converts above to ERM BI format
|
static int battlefieldTypeToTerrain(int bfieldType); //converts above to ERM BI format
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor
|
class DLL_LINKAGE CStack : public CBonusSystemNode, public CStackBasicDescriptor, public ISpellCaster
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const CStackInstance *base; //garrison slot from which stack originates (nullptr for war machines, summoned cres, etc)
|
const CStackInstance *base; //garrison slot from which stack originates (nullptr for war machines, summoned cres, etc)
|
||||||
@ -171,11 +171,15 @@ public:
|
|||||||
SlotID slot; //slot - position in garrison (may be 255 for neutrals/called creatures)
|
SlotID slot; //slot - position in garrison (may be 255 for neutrals/called creatures)
|
||||||
bool attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle)
|
bool attackerOwned; //if true, this stack is owned by attakcer (this one from left hand side of battle)
|
||||||
BattleHex position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower
|
BattleHex position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower
|
||||||
ui8 counterAttacks; //how many counter attacks can be performed more in this turn (by default set at the beginning of the round to 1)
|
///how many times this stack has been counterattacked this round
|
||||||
|
ui8 counterAttacksPerformed;
|
||||||
|
///cached total count of counterattacks; should be cleared each round;do not serialize
|
||||||
|
mutable ui8 counterAttacksTotalCache;
|
||||||
si16 shots; //how many shots left
|
si16 shots; //how many shots left
|
||||||
ui8 casts; //how many casts left
|
ui8 casts; //how many casts left
|
||||||
TQuantity resurrected; // these units will be taken back after battle is over
|
TQuantity resurrected; // these units will be taken back after battle is over
|
||||||
|
///id of alive clone of this stack clone if any
|
||||||
|
si32 cloneID;
|
||||||
std::set<EBattleStackState::EBattleStackState> state;
|
std::set<EBattleStackState::EBattleStackState> state;
|
||||||
//overrides
|
//overrides
|
||||||
const CCreature* getCreature() const {return type;}
|
const CCreature* getCreature() const {return type;}
|
||||||
@ -191,6 +195,10 @@ public:
|
|||||||
std::string getName() const; //plural or singular
|
std::string getName() const; //plural or singular
|
||||||
bool willMove(int turn = 0) const; //if stack has remaining move this turn
|
bool willMove(int turn = 0) const; //if stack has remaining move this turn
|
||||||
bool ableToRetaliate() const; //if stack can retaliate after attacked
|
bool ableToRetaliate() const; //if stack can retaliate after attacked
|
||||||
|
///how many times this stack can counterattack in one round
|
||||||
|
ui8 counterAttacksTotal() const;
|
||||||
|
///how many times this stack can counterattack in one round more
|
||||||
|
si8 counterAttacksRemaining() const;
|
||||||
bool moved(int turn = 0) const; //if stack was already moved this turn
|
bool moved(int turn = 0) const; //if stack was already moved this turn
|
||||||
bool waited(int turn = 0) const;
|
bool waited(int turn = 0) const;
|
||||||
bool canMove(int turn = 0) const; //if stack can move
|
bool canMove(int turn = 0) const; //if stack can move
|
||||||
@ -222,12 +230,16 @@ public:
|
|||||||
std::pair<int,int> countKilledByAttack(int damageReceived) const; //returns pair<killed count, new left HP>
|
std::pair<int,int> countKilledByAttack(int damageReceived) const; //returns pair<killed count, new left HP>
|
||||||
void prepareAttacked(BattleStackAttacked &bsa, CRandomGenerator & rand, boost::optional<int> customCount = boost::none) const; //requires bsa.damageAmout filled
|
void prepareAttacked(BattleStackAttacked &bsa, CRandomGenerator & rand, boost::optional<int> customCount = boost::none) const; //requires bsa.damageAmout filled
|
||||||
|
|
||||||
|
///ISpellCaster
|
||||||
|
ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const override;
|
||||||
|
ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override;
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
assert(isIndependentNode());
|
assert(isIndependentNode());
|
||||||
h & static_cast<CBonusSystemNode&>(*this);
|
h & static_cast<CBonusSystemNode&>(*this);
|
||||||
h & static_cast<CStackBasicDescriptor&>(*this);
|
h & static_cast<CStackBasicDescriptor&>(*this);
|
||||||
h & ID & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacks
|
h & ID & baseAmount & firstHPleft & owner & slot & attackerOwned & position & state & counterAttacksPerformed
|
||||||
& shots & casts & count & resurrected;
|
& shots & casts & count & resurrected;
|
||||||
|
|
||||||
const CArmedInstance *army = (base ? base->armyObj : nullptr);
|
const CArmedInstance *army = (base ? base->armyObj : nullptr);
|
||||||
|
@ -28,8 +28,8 @@ DLL_LINKAGE CConsoleHandler * console = nullptr;
|
|||||||
#define CONSOLE_TEAL "\x1b[1;36m"
|
#define CONSOLE_TEAL "\x1b[1;36m"
|
||||||
#else
|
#else
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
#include <dbghelp.h>
|
||||||
#ifndef __MINGW32__
|
#ifndef __MINGW32__
|
||||||
#include <dbghelp.h>
|
|
||||||
#pragma comment(lib, "dbghelp.lib")
|
#pragma comment(lib, "dbghelp.lib")
|
||||||
#endif
|
#endif
|
||||||
typedef WORD TColor;
|
typedef WORD TColor;
|
||||||
@ -121,7 +121,6 @@ LONG WINAPI onUnhandledException(EXCEPTION_POINTERS* exception)
|
|||||||
const DWORD threadId = ::GetCurrentThreadId();
|
const DWORD threadId = ::GetCurrentThreadId();
|
||||||
logGlobal->errorStream() << "Thread ID: " << threadId << " [" << std::dec << std::setw(0) << threadId << "]";
|
logGlobal->errorStream() << "Thread ID: " << threadId << " [" << std::dec << std::setw(0) << threadId << "]";
|
||||||
|
|
||||||
#ifndef __MINGW32__
|
|
||||||
//exception info to be placed in the dump
|
//exception info to be placed in the dump
|
||||||
MINIDUMP_EXCEPTION_INFORMATION meinfo = {threadId, exception, TRUE};
|
MINIDUMP_EXCEPTION_INFORMATION meinfo = {threadId, exception, TRUE};
|
||||||
|
|
||||||
@ -140,7 +139,6 @@ LONG WINAPI onUnhandledException(EXCEPTION_POINTERS* exception)
|
|||||||
HANDLE dfile = CreateFileA(mname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
|
HANDLE dfile = CreateFileA(mname, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
|
||||||
logGlobal->errorStream() << "Crash info will be put in " << mname;
|
logGlobal->errorStream() << "Crash info will be put in " << mname;
|
||||||
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), dfile, MiniDumpWithDataSegs, &meinfo, 0, 0);
|
MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), dfile, MiniDumpWithDataSegs, &meinfo, 0, 0);
|
||||||
#endif
|
|
||||||
MessageBoxA(0, "VCMI has crashed. We are sorry. File with information about encountered problem has been created.", "VCMI Crashhandler", MB_OK | MB_ICONERROR);
|
MessageBoxA(0, "VCMI has crashed. We are sorry. File with information about encountered problem has been created.", "VCMI Crashhandler", MB_OK | MB_ICONERROR);
|
||||||
return EXCEPTION_EXECUTE_HANDLER;
|
return EXCEPTION_EXECUTE_HANDLER;
|
||||||
}
|
}
|
||||||
|
@ -206,12 +206,24 @@ std::string CLegacyConfigParser::extractQuotedString()
|
|||||||
{
|
{
|
||||||
ret += extractQuotedPart();
|
ret += extractQuotedPart();
|
||||||
|
|
||||||
// double quote - add it to string and continue unless
|
// double quote - add it to string and continue quoted part
|
||||||
// line terminated using tabulation
|
if (curr < end && *curr == '\"')
|
||||||
if (curr < end && *curr == '\"' && *curr != '\t')
|
|
||||||
{
|
{
|
||||||
ret += '\"';
|
ret += '\"';
|
||||||
}
|
}
|
||||||
|
//extract normal part
|
||||||
|
else if(curr < end && *curr != '\t' && *curr != '\r')
|
||||||
|
{
|
||||||
|
char * begin = curr;
|
||||||
|
|
||||||
|
while (curr < end && *curr != '\t' && *curr != '\r' && *curr != '\"')//find end of string or next quoted part start
|
||||||
|
curr++;
|
||||||
|
|
||||||
|
ret += std::string(begin, curr);
|
||||||
|
|
||||||
|
if(curr>=end || *curr != '\"')
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
else // end of string
|
else // end of string
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@
|
|||||||
#include "mapping/CCampaignHandler.h" //for CCampaignState
|
#include "mapping/CCampaignHandler.h" //for CCampaignState
|
||||||
#include "rmg/CMapGenerator.h" // for CMapGenOptions
|
#include "rmg/CMapGenerator.h" // for CMapGenOptions
|
||||||
|
|
||||||
const ui32 version = 753;
|
const ui32 version = 754;
|
||||||
const ui32 minSupportedVersion = version;
|
const ui32 minSupportedVersion = 753;
|
||||||
|
|
||||||
class CISer;
|
class CISer;
|
||||||
class COSer;
|
class COSer;
|
||||||
|
@ -864,7 +864,10 @@ public:
|
|||||||
WALKING_DEAD = 58,
|
WALKING_DEAD = 58,
|
||||||
WIGHTS = 60,
|
WIGHTS = 60,
|
||||||
LICHES = 64,
|
LICHES = 64,
|
||||||
|
BONE_DRAGON = 68,
|
||||||
TROGLODYTES = 70,
|
TROGLODYTES = 70,
|
||||||
|
HYDRA = 110,
|
||||||
|
CHAOS_HYDRA = 111,
|
||||||
AIR_ELEMENTAL = 112,
|
AIR_ELEMENTAL = 112,
|
||||||
EARTH_ELEMENTAL = 113,
|
EARTH_ELEMENTAL = 113,
|
||||||
FIRE_ELEMENTAL = 114,
|
FIRE_ELEMENTAL = 114,
|
||||||
|
@ -211,21 +211,8 @@ Bonus * BonusList::getFirst(const CSelector &select)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BonusList::getModifiersWDescr(TModDescr &out) const
|
|
||||||
{
|
|
||||||
for (auto & elem : bonuses)
|
|
||||||
{
|
|
||||||
Bonus *b = elem;
|
|
||||||
out.push_back(std::make_pair(b->val, b->Description()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void BonusList::getBonuses(BonusList & out, const CSelector &selector) const
|
void BonusList::getBonuses(BonusList & out, const CSelector &selector) const
|
||||||
{
|
{
|
||||||
// for(Bonus *i : *this)
|
|
||||||
// if(selector(i) && i->effectRange == Bonus::NO_LIMIT)
|
|
||||||
// out.push_back(i);
|
|
||||||
|
|
||||||
getBonuses(out, selector, nullptr);
|
getBonuses(out, selector, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,17 +346,6 @@ bool IBonusBearer::hasBonusOfType(Bonus::BonusType type, int subtype /*= -1*/) c
|
|||||||
return hasBonus(s, cachingStr.str());
|
return hasBonus(s, cachingStr.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void IBonusBearer::getModifiersWDescr(TModDescr &out, Bonus::BonusType type, int subtype /*= -1 */) const
|
|
||||||
{
|
|
||||||
std::stringstream cachingStr;
|
|
||||||
cachingStr << "type_" << type << "s_" << subtype;
|
|
||||||
getModifiersWDescr(out, subtype != -1 ? Selector::typeSubtype(type, subtype) : Selector::type(type), cachingStr.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
void IBonusBearer::getModifiersWDescr(TModDescr &out, const CSelector &selector, const std::string &cachingStr /* =""*/) const
|
|
||||||
{
|
|
||||||
getBonuses(selector, cachingStr)->getModifiersWDescr(out);
|
|
||||||
}
|
|
||||||
int IBonusBearer::getBonusesCount(Bonus::BonusSource from, int id) const
|
int IBonusBearer::getBonusesCount(Bonus::BonusSource from, int id) const
|
||||||
{
|
{
|
||||||
std::stringstream cachingStr;
|
std::stringstream cachingStr;
|
||||||
@ -524,8 +500,13 @@ bool IBonusBearer::isLiving() const //TODO: theoreticaly there exists "LIVING" b
|
|||||||
const TBonusListPtr IBonusBearer::getSpellBonuses() const
|
const TBonusListPtr IBonusBearer::getSpellBonuses() const
|
||||||
{
|
{
|
||||||
std::stringstream cachingStr;
|
std::stringstream cachingStr;
|
||||||
cachingStr << "source_" << Bonus::SPELL_EFFECT;
|
cachingStr << "!type_" << Bonus::NONE << "source_" << Bonus::SPELL_EFFECT;
|
||||||
return getBonuses(Selector::sourceType(Bonus::SPELL_EFFECT), Selector::anyRange(), cachingStr.str());
|
CSelector selector = Selector::sourceType(Bonus::SPELL_EFFECT)
|
||||||
|
.And(CSelector([](const Bonus * b)->bool
|
||||||
|
{
|
||||||
|
return !b->type == Bonus::NONE;
|
||||||
|
}));
|
||||||
|
return getBonuses(selector, Selector::anyRange(), cachingStr.str());
|
||||||
}
|
}
|
||||||
|
|
||||||
const Bonus * IBonusBearer::getEffect(ui16 id, int turn /*= 0*/) const
|
const Bonus * IBonusBearer::getEffect(ui16 id, int turn /*= 0*/) const
|
||||||
@ -1105,12 +1086,6 @@ bool NBonus::hasOfType(const CBonusSystemNode *obj, Bonus::BonusType type, int s
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NBonus::getModifiersWDescr(const CBonusSystemNode *obj, TModDescr &out, Bonus::BonusType type, int subtype /*= -1 */)
|
|
||||||
{
|
|
||||||
if(obj)
|
|
||||||
return obj->getModifiersWDescr(out, type, subtype);
|
|
||||||
}
|
|
||||||
|
|
||||||
int NBonus::getCount(const CBonusSystemNode *obj, Bonus::BonusSource from, int id)
|
int NBonus::getCount(const CBonusSystemNode *obj, Bonus::BonusSource from, int id)
|
||||||
{
|
{
|
||||||
if(obj)
|
if(obj)
|
||||||
@ -1127,28 +1102,34 @@ const CSpell * Bonus::sourceSpell() const
|
|||||||
|
|
||||||
std::string Bonus::Description() const
|
std::string Bonus::Description() const
|
||||||
{
|
{
|
||||||
if(description.size())
|
|
||||||
return description;
|
|
||||||
|
|
||||||
std::ostringstream str;
|
std::ostringstream str;
|
||||||
str << std::showpos << val << " ";
|
|
||||||
|
if(description.empty())
|
||||||
switch(source)
|
switch(source)
|
||||||
{
|
{
|
||||||
case ARTIFACT:
|
case ARTIFACT:
|
||||||
str << VLC->arth->artifacts[sid]->Name();
|
str << VLC->arth->artifacts[sid]->Name();
|
||||||
break;;
|
break;;
|
||||||
case SPELL_EFFECT:
|
case SPELL_EFFECT:
|
||||||
str << SpellID(sid).toSpell()->name;
|
str << SpellID(sid).toSpell()->name;
|
||||||
break;
|
break;
|
||||||
case CREATURE_ABILITY:
|
case CREATURE_ABILITY:
|
||||||
str << VLC->creh->creatures[sid]->namePl;
|
str << VLC->creh->creatures[sid]->namePl;
|
||||||
break;
|
break;
|
||||||
case SECONDARY_SKILL:
|
case SECONDARY_SKILL:
|
||||||
str << VLC->generaltexth->skillName[sid]/* << " secondary skill"*/;
|
str << VLC->generaltexth->skillName[sid]/* << " secondary skill"*/;
|
||||||
break;
|
break;
|
||||||
}
|
default:
|
||||||
|
//todo: handle all possible sources
|
||||||
|
str << "Unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
str << description;
|
||||||
|
|
||||||
|
if(val != 0)
|
||||||
|
str << " " << std::showpos << val;
|
||||||
|
|
||||||
return str.str();
|
return str.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,6 @@ class BonusList;
|
|||||||
typedef shared_ptr<BonusList> TBonusListPtr;
|
typedef shared_ptr<BonusList> TBonusListPtr;
|
||||||
typedef shared_ptr<ILimiter> TLimiterPtr;
|
typedef shared_ptr<ILimiter> TLimiterPtr;
|
||||||
typedef shared_ptr<IPropagator> TPropagatorPtr;
|
typedef shared_ptr<IPropagator> TPropagatorPtr;
|
||||||
typedef std::vector<std::pair<int,std::string> > TModDescr; //modifiers values and their descriptions
|
|
||||||
typedef std::set<CBonusSystemNode*> TNodes;
|
typedef std::set<CBonusSystemNode*> TNodes;
|
||||||
typedef std::set<const CBonusSystemNode*> TCNodes;
|
typedef std::set<const CBonusSystemNode*> TCNodes;
|
||||||
typedef std::vector<CBonusSystemNode *> TNodesVector;
|
typedef std::vector<CBonusSystemNode *> TNodesVector;
|
||||||
@ -448,7 +447,6 @@ public:
|
|||||||
int totalValue() const; //subtype -> subtype of bonus, if -1 then any
|
int totalValue() const; //subtype -> subtype of bonus, if -1 then any
|
||||||
void getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit) const;
|
void getBonuses(BonusList &out, const CSelector &selector, const CSelector &limit) const;
|
||||||
void getAllBonuses(BonusList &out) const;
|
void getAllBonuses(BonusList &out) const;
|
||||||
void getModifiersWDescr(TModDescr &out) const;
|
|
||||||
|
|
||||||
void getBonuses(BonusList & out, const CSelector &selector) const;
|
void getBonuses(BonusList & out, const CSelector &selector) const;
|
||||||
|
|
||||||
@ -579,7 +577,6 @@ public:
|
|||||||
// * root is node on which call was made (nullptr will be replaced with this)
|
// * root is node on which call was made (nullptr will be replaced with this)
|
||||||
//interface
|
//interface
|
||||||
virtual const TBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const = 0;
|
virtual const TBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const = 0;
|
||||||
void getModifiersWDescr(TModDescr &out, const CSelector &selector, const std::string &cachingStr = "") const; //out: pairs<modifier value, modifier description>
|
|
||||||
int getBonusesCount(const CSelector &selector, const std::string &cachingStr = "") const;
|
int getBonusesCount(const CSelector &selector, const std::string &cachingStr = "") const;
|
||||||
int valOfBonuses(const CSelector &selector, const std::string &cachingStr = "") const;
|
int valOfBonuses(const CSelector &selector, const std::string &cachingStr = "") const;
|
||||||
bool hasBonus(const CSelector &selector, const std::string &cachingStr = "") const;
|
bool hasBonus(const CSelector &selector, const std::string &cachingStr = "") const;
|
||||||
@ -594,7 +591,6 @@ public:
|
|||||||
int valOfBonuses(Bonus::BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then anyt;
|
int valOfBonuses(Bonus::BonusType type, int subtype = -1) const; //subtype -> subtype of bonus, if -1 then anyt;
|
||||||
bool hasBonusOfType(Bonus::BonusType type, int subtype = -1) const;//determines if hero has a bonus of given type (and optionally subtype)
|
bool hasBonusOfType(Bonus::BonusType type, int subtype = -1) const;//determines if hero has a bonus of given type (and optionally subtype)
|
||||||
bool hasBonusFrom(Bonus::BonusSource source, ui32 sourceID) const;
|
bool hasBonusFrom(Bonus::BonusSource source, ui32 sourceID) const;
|
||||||
void getModifiersWDescr( TModDescr &out, Bonus::BonusType type, int subtype = -1 ) const; //out: pairs<modifier value, modifier description>
|
|
||||||
int getBonusesCount(Bonus::BonusSource from, int id) const;
|
int getBonusesCount(Bonus::BonusSource from, int id) const;
|
||||||
|
|
||||||
//various hlp functions for non-trivial values
|
//various hlp functions for non-trivial values
|
||||||
@ -722,8 +718,6 @@ namespace NBonus
|
|||||||
//set of methods that may be safely called with nullptr objs
|
//set of methods that may be safely called with nullptr objs
|
||||||
DLL_LINKAGE int valOf(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype = -1); //subtype -> subtype of bonus, if -1 then any
|
DLL_LINKAGE int valOf(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype = -1); //subtype -> subtype of bonus, if -1 then any
|
||||||
DLL_LINKAGE bool hasOfType(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype = -1);//determines if hero has a bonus of given type (and optionally subtype)
|
DLL_LINKAGE bool hasOfType(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype = -1);//determines if hero has a bonus of given type (and optionally subtype)
|
||||||
//DLL_LINKAGE const HeroBonus * get(const CBonusSystemNode *obj, int from, int id );
|
|
||||||
DLL_LINKAGE void getModifiersWDescr(const CBonusSystemNode *obj, TModDescr &out, Bonus::BonusType type, int subtype = -1 ); //out: pairs<modifier value, modifier description>
|
|
||||||
DLL_LINKAGE int getCount(const CBonusSystemNode *obj, Bonus::BonusSource from, int id);
|
DLL_LINKAGE int getCount(const CBonusSystemNode *obj, Bonus::BonusSource from, int id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ public:
|
|||||||
virtual void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) =0;
|
virtual void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) =0;
|
||||||
virtual void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) =0;
|
virtual void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) =0;
|
||||||
virtual bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) =0;
|
virtual bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) =0;
|
||||||
virtual bool changeStackType(const StackLocation &sl, CCreature *c) =0;
|
virtual bool changeStackType(const StackLocation &sl, const CCreature *c) =0;
|
||||||
virtual bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count = -1) =0; //count -1 => moves whole stack
|
virtual bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count = -1) =0; //count -1 => moves whole stack
|
||||||
virtual bool eraseStack(const StackLocation &sl, bool forceRemoval = false) =0;
|
virtual bool eraseStack(const StackLocation &sl, bool forceRemoval = false) =0;
|
||||||
virtual bool swapStacks(const StackLocation &sl1, const StackLocation &sl2) =0;
|
virtual bool swapStacks(const StackLocation &sl1, const StackLocation &sl2) =0;
|
||||||
|
@ -795,7 +795,7 @@ struct ChangeStackCount : CGarrisonOperationPack //521
|
|||||||
struct SetStackType : CGarrisonOperationPack //522
|
struct SetStackType : CGarrisonOperationPack //522
|
||||||
{
|
{
|
||||||
StackLocation sl;
|
StackLocation sl;
|
||||||
CCreature *type;
|
const CCreature *type;
|
||||||
|
|
||||||
void applyCl(CClient *cl);
|
void applyCl(CClient *cl);
|
||||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||||
@ -1478,6 +1478,18 @@ struct EndAction : public CPackForClient//3008
|
|||||||
|
|
||||||
struct BattleSpellCast : public CPackForClient//3009
|
struct BattleSpellCast : public CPackForClient//3009
|
||||||
{
|
{
|
||||||
|
///custom effect (resistance, reflection, etc)
|
||||||
|
struct CustomEffect
|
||||||
|
{
|
||||||
|
/// WoG AC format
|
||||||
|
ui32 effect;
|
||||||
|
ui32 stack;
|
||||||
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
|
{
|
||||||
|
h & effect & stack;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
BattleSpellCast(){type = 3009; casterStack = -1;};
|
BattleSpellCast(){type = 3009; casterStack = -1;};
|
||||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||||
void applyCl(CClient *cl);
|
void applyCl(CClient *cl);
|
||||||
@ -1488,13 +1500,13 @@ struct BattleSpellCast : public CPackForClient//3009
|
|||||||
ui8 skill; //caster's skill level
|
ui8 skill; //caster's skill level
|
||||||
ui8 manaGained; //mana channeling ability
|
ui8 manaGained; //mana channeling ability
|
||||||
BattleHex tile; //destination tile (may not be set in some global/mass spells
|
BattleHex tile; //destination tile (may not be set in some global/mass spells
|
||||||
std::vector<ui32> resisted; //ids of creatures that resisted this spell
|
std::vector<CustomEffect> customEffects;
|
||||||
std::set<ui32> affectedCres; //ids of creatures affected by this spell, generally used if spell does not set any effect (like dispel or cure)
|
std::set<ui32> affectedCres; //ids of creatures affected by this spell, generally used if spell does not set any effect (like dispel or cure)
|
||||||
si32 casterStack;// -1 if not cated by creature, >=0 caster stack ID
|
si32 casterStack;// -1 if not cated by creature, >=0 caster stack ID
|
||||||
bool castedByHero; //if true - spell has been casted by hero, otherwise by a creature
|
bool castByHero; //if true - spell has been casted by hero, otherwise by a creature
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & dmgToDisplay & side & id & skill & manaGained & tile & resisted & affectedCres & casterStack & castedByHero;
|
h & dmgToDisplay & side & id & skill & manaGained & tile & customEffects & affectedCres & casterStack & castByHero;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1629,10 +1641,9 @@ struct BattleSetStackProperty : public CPackForClient //3018
|
|||||||
{
|
{
|
||||||
BattleSetStackProperty(){type = 3018;};
|
BattleSetStackProperty(){type = 3018;};
|
||||||
|
|
||||||
enum BattleStackProperty {CASTS, ENCHANTER_COUNTER, UNBIND, CLONED};
|
enum BattleStackProperty {CASTS, ENCHANTER_COUNTER, UNBIND, CLONED, HAS_CLONE};
|
||||||
|
|
||||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||||
//void applyCl(CClient *cl){};
|
|
||||||
|
|
||||||
int stackID;
|
int stackID;
|
||||||
BattleStackProperty which;
|
BattleStackProperty which;
|
||||||
|
@ -1095,7 +1095,8 @@ DLL_LINKAGE void BattleNextRound::applyGs( CGameState *gs )
|
|||||||
s->state -= EBattleStackState::HAD_MORALE;
|
s->state -= EBattleStackState::HAD_MORALE;
|
||||||
s->state -= EBattleStackState::FEAR;
|
s->state -= EBattleStackState::FEAR;
|
||||||
s->state -= EBattleStackState::DRAINED_MANA;
|
s->state -= EBattleStackState::DRAINED_MANA;
|
||||||
s->counterAttacks = 1 + s->valOfBonuses(Bonus::ADDITIONAL_RETALIATION);
|
s->counterAttacksPerformed = 0;
|
||||||
|
s->counterAttacksTotalCache = 0;
|
||||||
// new turn effects
|
// new turn effects
|
||||||
s->battleTurnPassed();
|
s->battleTurnPassed();
|
||||||
}
|
}
|
||||||
@ -1247,7 +1248,12 @@ DLL_LINKAGE void BattleStackAttacked::applyGs( CGameState *gs )
|
|||||||
{
|
{
|
||||||
//"hide" killed creatures instead so we keep info about it
|
//"hide" killed creatures instead so we keep info about it
|
||||||
at->state.insert(EBattleStackState::DEAD_CLONE);
|
at->state.insert(EBattleStackState::DEAD_CLONE);
|
||||||
|
|
||||||
|
for(CStack * s : gs->curB->stacks)
|
||||||
|
{
|
||||||
|
if(s->cloneID == at->ID)
|
||||||
|
s->cloneID = -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1255,7 +1261,7 @@ DLL_LINKAGE void BattleAttack::applyGs( CGameState *gs )
|
|||||||
{
|
{
|
||||||
CStack *attacker = gs->curB->getStack(stackAttacking);
|
CStack *attacker = gs->curB->getStack(stackAttacking);
|
||||||
if(counter())
|
if(counter())
|
||||||
attacker->counterAttacks--;
|
attacker->counterAttacksPerformed++;
|
||||||
|
|
||||||
if(shot())
|
if(shot())
|
||||||
{
|
{
|
||||||
@ -1603,6 +1609,11 @@ DLL_LINKAGE void BattleSetStackProperty::applyGs(CGameState *gs)
|
|||||||
stack->state.insert(EBattleStackState::CLONED);
|
stack->state.insert(EBattleStackState::CLONED);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case HAS_CLONE:
|
||||||
|
{
|
||||||
|
stack->cloneID = val;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@
|
|||||||
<Add option="-lboost_chrono$(#boost.libsuffix)" />
|
<Add option="-lboost_chrono$(#boost.libsuffix)" />
|
||||||
<Add option="-lboost_locale$(#boost.libsuffix)" />
|
<Add option="-lboost_locale$(#boost.libsuffix)" />
|
||||||
<Add option="-liconv" />
|
<Add option="-liconv" />
|
||||||
|
<Add option="-ldbghelp" />
|
||||||
<Add directory="$(#sdl2.lib)" />
|
<Add directory="$(#sdl2.lib)" />
|
||||||
<Add directory="$(#boost.lib32)" />
|
<Add directory="$(#boost.lib32)" />
|
||||||
<Add directory="$(#zlib.lib)" />
|
<Add directory="$(#zlib.lib)" />
|
||||||
@ -59,6 +60,7 @@
|
|||||||
<Add option="-lboost_chrono$(#boost.libsuffix)" />
|
<Add option="-lboost_chrono$(#boost.libsuffix)" />
|
||||||
<Add option="-lboost_locale$(#boost.libsuffix)" />
|
<Add option="-lboost_locale$(#boost.libsuffix)" />
|
||||||
<Add option="-liconv" />
|
<Add option="-liconv" />
|
||||||
|
<Add option="-ldbghelp" />
|
||||||
<Add directory="$(#sdl2.lib)" />
|
<Add directory="$(#sdl2.lib)" />
|
||||||
<Add directory="$(#boost.lib32)" />
|
<Add directory="$(#boost.lib32)" />
|
||||||
<Add directory="$(#zlib.lib)" />
|
<Add directory="$(#zlib.lib)" />
|
||||||
@ -88,6 +90,7 @@
|
|||||||
<Add option="-lboost_chrono$(#boost.libsuffix)" />
|
<Add option="-lboost_chrono$(#boost.libsuffix)" />
|
||||||
<Add option="-lboost_locale$(#boost.libsuffix)" />
|
<Add option="-lboost_locale$(#boost.libsuffix)" />
|
||||||
<Add option="-liconv" />
|
<Add option="-liconv" />
|
||||||
|
<Add option="-ldbghelp" />
|
||||||
<Add directory="$(#sdl2.lib64)" />
|
<Add directory="$(#sdl2.lib64)" />
|
||||||
<Add directory="$(#boost.lib64)" />
|
<Add directory="$(#boost.lib64)" />
|
||||||
<Add directory="$(#zlib64.lib)" />
|
<Add directory="$(#zlib64.lib)" />
|
||||||
@ -311,6 +314,7 @@
|
|||||||
<Unit filename="spells/CreatureSpellMechanics.h" />
|
<Unit filename="spells/CreatureSpellMechanics.h" />
|
||||||
<Unit filename="spells/ISpellMechanics.cpp" />
|
<Unit filename="spells/ISpellMechanics.cpp" />
|
||||||
<Unit filename="spells/ISpellMechanics.h" />
|
<Unit filename="spells/ISpellMechanics.h" />
|
||||||
|
<Unit filename="spells/Magic.h" />
|
||||||
<Unit filename="spells/ViewSpellInt.cpp" />
|
<Unit filename="spells/ViewSpellInt.cpp" />
|
||||||
<Unit filename="spells/ViewSpellInt.h" />
|
<Unit filename="spells/ViewSpellInt.h" />
|
||||||
<Unit filename="vcmi_endian.h" />
|
<Unit filename="vcmi_endian.h" />
|
||||||
|
@ -86,11 +86,13 @@ void CArmedInstance::updateMoraleBonusFromArmy()
|
|||||||
{
|
{
|
||||||
b->val = +1;
|
b->val = +1;
|
||||||
b->description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1
|
b->description = VLC->generaltexth->arraytxt[115]; //All troops of one alignment +1
|
||||||
|
b->description = b->description.substr(0, b->description.size()-3);//trim "+1"
|
||||||
}
|
}
|
||||||
else if (!factions.empty()) // no bonus from empty garrison
|
else if (!factions.empty()) // no bonus from empty garrison
|
||||||
{
|
{
|
||||||
b->val = 2 - factionsInArmy;
|
b->val = 2 - factionsInArmy;
|
||||||
b->description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factionsInArmy % b->val); //Troops of %d alignments %d
|
b->description = boost::str(boost::format(VLC->generaltexth->arraytxt[114]) % factionsInArmy % b->val); //Troops of %d alignments %d
|
||||||
|
b->description = b->description.substr(0, b->description.size()-2);//trim value
|
||||||
}
|
}
|
||||||
boost::algorithm::trim(b->description);
|
boost::algorithm::trim(b->description);
|
||||||
|
|
||||||
@ -100,7 +102,11 @@ void CArmedInstance::updateMoraleBonusFromArmy()
|
|||||||
if(hasUndead)
|
if(hasUndead)
|
||||||
{
|
{
|
||||||
if(!undeadModifier)
|
if(!undeadModifier)
|
||||||
addNewBonus(new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, UNDEAD_MODIFIER_ID, VLC->generaltexth->arraytxt[116]));
|
{
|
||||||
|
undeadModifier = new Bonus(Bonus::PERMANENT, Bonus::MORALE, Bonus::ARMY, -1, UNDEAD_MODIFIER_ID, VLC->generaltexth->arraytxt[116]);
|
||||||
|
undeadModifier->description = undeadModifier->description.substr(0, undeadModifier->description.size()-2);//trim value
|
||||||
|
addNewBonus(undeadModifier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if(undeadModifier)
|
else if(undeadModifier)
|
||||||
removeBonus(undeadModifier);
|
removeBonus(undeadModifier);
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "../IGameCallback.h"
|
#include "../IGameCallback.h"
|
||||||
#include "../CGameState.h"
|
#include "../CGameState.h"
|
||||||
#include "../CCreatureHandler.h"
|
#include "../CCreatureHandler.h"
|
||||||
|
#include "../BattleState.h"
|
||||||
|
|
||||||
///helpers
|
///helpers
|
||||||
static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID)
|
static void showInfoDialog(const PlayerColor playerID, const ui32 txtID, const ui16 soundID)
|
||||||
@ -216,9 +217,10 @@ CGHeroInstance::CGHeroInstance()
|
|||||||
setNodeType(HERO);
|
setNodeType(HERO);
|
||||||
ID = Obj::HERO;
|
ID = Obj::HERO;
|
||||||
tacticFormationEnabled = inTownGarrison = false;
|
tacticFormationEnabled = inTownGarrison = false;
|
||||||
mana = movement = portrait = level = -1;
|
mana = movement = portrait = -1;
|
||||||
isStanding = true;
|
isStanding = true;
|
||||||
moveDir = 4;
|
moveDir = 4;
|
||||||
|
level = 1;
|
||||||
exp = 0xffffffff;
|
exp = 0xffffffff;
|
||||||
visitedTown = nullptr;
|
visitedTown = nullptr;
|
||||||
type = nullptr;
|
type = nullptr;
|
||||||
@ -290,7 +292,6 @@ void CGHeroInstance::initHero()
|
|||||||
}
|
}
|
||||||
assert(validTypes());
|
assert(validTypes());
|
||||||
|
|
||||||
level = 1;
|
|
||||||
if(exp == 0xffffffff)
|
if(exp == 0xffffffff)
|
||||||
{
|
{
|
||||||
initExp();
|
initExp();
|
||||||
@ -877,15 +878,73 @@ ui8 CGHeroInstance::getSpellSchoolLevel(const CSpell * spell, int *outSelectedSc
|
|||||||
|
|
||||||
vstd::amax(skill, valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 0)); //any school bonus
|
vstd::amax(skill, valOfBonuses(Bonus::MAGIC_SCHOOL_SKILL, 0)); //any school bonus
|
||||||
vstd::amax(skill, valOfBonuses(Bonus::SPELL, spell->id.toEnum())); //given by artifact or other effect
|
vstd::amax(skill, valOfBonuses(Bonus::SPELL, spell->id.toEnum())); //given by artifact or other effect
|
||||||
if (hasBonusOfType(Bonus::MAXED_SPELL, spell->id))//hero specialty (Daremyth, Melodia)
|
|
||||||
skill = 3;
|
|
||||||
assert(skill >= 0 && skill <= 3);
|
assert(skill >= 0 && skill <= 3);
|
||||||
return skill;
|
return skill;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui32 CGHeroInstance::getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const
|
||||||
|
{
|
||||||
|
//applying sorcery secondary skill
|
||||||
|
|
||||||
|
base *= (100.0 + valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::SORCERY)) / 100.0;
|
||||||
|
base *= (100.0 + valOfBonuses(Bonus::SPELL_DAMAGE) + valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, spell->id.toEnum())) / 100.0;
|
||||||
|
|
||||||
|
spell->forEachSchool([&base, this](const SpellSchoolInfo & cnf, bool & stop)
|
||||||
|
{
|
||||||
|
base *= (100.0 + valOfBonuses(cnf.damagePremyBonus)) / 100.0;
|
||||||
|
stop = true; //only bonus from one school is used
|
||||||
|
});
|
||||||
|
|
||||||
|
if (affectedStack && affectedStack->getCreature()->level) //Hero specials like Solmyr, Deemer
|
||||||
|
base *= (100. + ((valOfBonuses(Bonus::SPECIAL_SPELL_LEV, spell->id.toEnum()) * level) / affectedStack->getCreature()->level)) / 100.0;
|
||||||
|
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const
|
bool CGHeroInstance::canCastThisSpell(const CSpell * spell) const
|
||||||
{
|
{
|
||||||
return spell->isCastableBy(this, nullptr !=getArt(ArtifactPosition::SPELLBOOK), spells);
|
if(nullptr == getArt(ArtifactPosition::SPELLBOOK))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const bool isAllowed = IObjectInterface::cb->isAllowed(0, spell->id);
|
||||||
|
|
||||||
|
const bool inSpellBook = vstd::contains(spells, spell->id);
|
||||||
|
const bool specificBonus = hasBonusOfType(Bonus::SPELL, spell->id);
|
||||||
|
|
||||||
|
bool schoolBonus = false;
|
||||||
|
|
||||||
|
spell->forEachSchool([this, &schoolBonus](const SpellSchoolInfo & cnf, bool & stop)
|
||||||
|
{
|
||||||
|
if(hasBonusOfType(cnf.knoledgeBonus))
|
||||||
|
{
|
||||||
|
schoolBonus = stop = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const bool levelBonus = hasBonusOfType(Bonus::SPELLS_OF_LEVEL, spell->level);
|
||||||
|
|
||||||
|
if (spell->isSpecialSpell())
|
||||||
|
{
|
||||||
|
if (inSpellBook)
|
||||||
|
{//hero has this spell in spellbook
|
||||||
|
logGlobal->errorStream() << "Special spell " << spell->name << "in spellbook.";
|
||||||
|
}
|
||||||
|
return specificBonus;
|
||||||
|
}
|
||||||
|
else if(!isAllowed)
|
||||||
|
{
|
||||||
|
if (inSpellBook)
|
||||||
|
{//hero has this spell in spellbook
|
||||||
|
logGlobal->errorStream() << "Banned spell " << spell->name << " in spellbook.";
|
||||||
|
}
|
||||||
|
return specificBonus;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return inSpellBook || schoolBonus || specificBonus || levelBonus;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include "CObjectHandler.h"
|
#include "CObjectHandler.h"
|
||||||
#include "CArmedInstance.h"
|
#include "CArmedInstance.h"
|
||||||
|
#include "../spells/Magic.h"
|
||||||
|
|
||||||
#include "../CArtHandler.h" // For CArtifactSet
|
#include "../CArtHandler.h" // For CArtifactSet
|
||||||
#include "../CRandomGenerator.h"
|
#include "../CRandomGenerator.h"
|
||||||
@ -35,7 +36,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet
|
class DLL_LINKAGE CGHeroInstance : public CArmedInstance, public IBoatGenerator, public CArtifactSet, public ISpellCaster
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum ECanDig
|
enum ECanDig
|
||||||
@ -162,14 +163,13 @@ public:
|
|||||||
int maxMovePoints(bool onLand) const;
|
int maxMovePoints(bool onLand) const;
|
||||||
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const;
|
int movementPointsAfterEmbark(int MPsBefore, int basicCost, bool disembark = false) const;
|
||||||
|
|
||||||
//int getSpellSecLevel(int spell) const; //returns level of secondary ability (fire, water, earth, air magic) known to this hero and applicable to given spell; -1 if error
|
|
||||||
static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
|
static int3 convertPosition(int3 src, bool toh3m); //toh3m=true: manifest->h3m; toh3m=false: h3m->manifest
|
||||||
double getFightingStrength() const; // takes attack / defense skill into account
|
double getFightingStrength() const; // takes attack / defense skill into account
|
||||||
double getMagicStrength() const; // takes knowledge / spell power skill into account
|
double getMagicStrength() const; // takes knowledge / spell power skill into account
|
||||||
double getHeroStrength() const; // includes fighting and magic strength
|
double getHeroStrength() const; // includes fighting and magic strength
|
||||||
ui64 getTotalStrength() const; // includes fighting strength and army strength
|
ui64 getTotalStrength() const; // includes fighting strength and army strength
|
||||||
TExpType calculateXp(TExpType exp) const; //apply learning skill
|
TExpType calculateXp(TExpType exp) const; //apply learning skill
|
||||||
ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const; //returns level on which given spell would be cast by this hero (0 - none, 1 - basic etc); optionally returns number of selected school by arg - 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic,
|
|
||||||
bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses
|
bool canCastThisSpell(const CSpell * spell) const; //determines if this hero can cast given spell; takes into account existing spell in spellbook, existing spellbook and artifact bonuses
|
||||||
CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
|
CStackBasicDescriptor calculateNecromancy (const BattleResult &battleResult) const;
|
||||||
void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
|
void showNecromancyDialog(const CStackBasicDescriptor &raisedStack) const;
|
||||||
@ -198,13 +198,18 @@ public:
|
|||||||
|
|
||||||
CGHeroInstance();
|
CGHeroInstance();
|
||||||
virtual ~CGHeroInstance();
|
virtual ~CGHeroInstance();
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
///ArtBearer
|
||||||
ArtBearer::ArtBearer bearerType() const override;
|
ArtBearer::ArtBearer bearerType() const override;
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
|
///IBonusBearer
|
||||||
CBonusSystemNode *whereShouldBeAttached(CGameState *gs) override;
|
CBonusSystemNode *whereShouldBeAttached(CGameState *gs) override;
|
||||||
std::string nodeName() const override;
|
std::string nodeName() const override;
|
||||||
|
|
||||||
|
///ISpellCaster
|
||||||
|
ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const override;
|
||||||
|
ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override;
|
||||||
|
|
||||||
void deserializationFix();
|
void deserializationFix();
|
||||||
|
|
||||||
void initObj() override;
|
void initObj() override;
|
||||||
|
@ -436,12 +436,12 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const
|
|||||||
//other *-of-legion-like bonuses (%d to growth cumulative with grail)
|
//other *-of-legion-like bonuses (%d to growth cumulative with grail)
|
||||||
TBonusListPtr bonuses = getBonuses(Selector::type(Bonus::CREATURE_GROWTH).And(Selector::subtype(level)));
|
TBonusListPtr bonuses = getBonuses(Selector::type(Bonus::CREATURE_GROWTH).And(Selector::subtype(level)));
|
||||||
for(const Bonus *b : *bonuses)
|
for(const Bonus *b : *bonuses)
|
||||||
ret.entries.push_back(GrowthInfo::Entry(b->Description() + " %+d", b->val));
|
ret.entries.push_back(GrowthInfo::Entry(b->val, b->Description()));
|
||||||
|
|
||||||
//statue-of-legion-like bonus: % to base+castle
|
//statue-of-legion-like bonus: % to base+castle
|
||||||
TBonusListPtr bonuses2 = getBonuses(Selector::type(Bonus::CREATURE_GROWTH_PERCENT));
|
TBonusListPtr bonuses2 = getBonuses(Selector::type(Bonus::CREATURE_GROWTH_PERCENT));
|
||||||
for(const Bonus *b : *bonuses2)
|
for(const Bonus *b : *bonuses2)
|
||||||
ret.entries.push_back(GrowthInfo::Entry(b->Description() + " %+d", b->val * (base + castleBonus) / 100));
|
ret.entries.push_back(GrowthInfo::Entry(b->val * (base + castleBonus) / 100, b->Description()));
|
||||||
|
|
||||||
if(hasBuilt(BuildingID::GRAIL)) //grail - +50% to ALL (so far added) growth
|
if(hasBuilt(BuildingID::GRAIL)) //grail - +50% to ALL (so far added) growth
|
||||||
ret.entries.push_back(GrowthInfo::Entry(subID, BuildingID::GRAIL, ret.totalGrowth() / 2));
|
ret.entries.push_back(GrowthInfo::Entry(subID, BuildingID::GRAIL, ret.totalGrowth() / 2));
|
||||||
@ -1244,6 +1244,12 @@ GrowthInfo::Entry::Entry(int subID, BuildingID building, int _count)
|
|||||||
description = boost::str(boost::format("%s %+d") % VLC->townh->factions[subID]->town->buildings.at(building)->Name() % count);
|
description = boost::str(boost::format("%s %+d") % VLC->townh->factions[subID]->town->buildings.at(building)->Name() % count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GrowthInfo::Entry::Entry(int _count, const std::string &fullDescription)
|
||||||
|
: count(_count)
|
||||||
|
{
|
||||||
|
description = fullDescription;
|
||||||
|
}
|
||||||
|
|
||||||
CTownAndVisitingHero::CTownAndVisitingHero()
|
CTownAndVisitingHero::CTownAndVisitingHero()
|
||||||
{
|
{
|
||||||
setNodeType(TOWN_AND_VISITOR);
|
setNodeType(TOWN_AND_VISITOR);
|
||||||
|
@ -130,6 +130,7 @@ struct DLL_LINKAGE GrowthInfo
|
|||||||
std::string description;
|
std::string description;
|
||||||
Entry(const std::string &format, int _count);
|
Entry(const std::string &format, int _count);
|
||||||
Entry(int subID, BuildingID building, int _count);
|
Entry(int subID, BuildingID building, int _count);
|
||||||
|
Entry(int _count, const std::string &fullDescription);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Entry> entries;
|
std::vector<Entry> entries;
|
||||||
|
@ -93,6 +93,12 @@ void CloneMechanics::applyBattleEffects(const SpellCastEnvironment * env, Battle
|
|||||||
ssp.val = 0;
|
ssp.val = 0;
|
||||||
ssp.absolute = 1;
|
ssp.absolute = 1;
|
||||||
env->sendAndApply(&ssp);
|
env->sendAndApply(&ssp);
|
||||||
|
|
||||||
|
ssp.stackID = clonedStack->ID;
|
||||||
|
ssp.which = BattleSetStackProperty::HAS_CLONE;
|
||||||
|
ssp.val = bsa.newStackID;
|
||||||
|
ssp.absolute = 1;
|
||||||
|
env->sendAndApply(&ssp);
|
||||||
}
|
}
|
||||||
|
|
||||||
ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const CGHeroInstance * caster, const CStack * obj) const
|
||||||
@ -100,6 +106,8 @@ ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const CGHer
|
|||||||
//can't clone already cloned creature
|
//can't clone already cloned creature
|
||||||
if(vstd::contains(obj->state, EBattleStackState::CLONED))
|
if(vstd::contains(obj->state, EBattleStackState::CLONED))
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||||
|
if(obj->cloneID != -1)
|
||||||
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||||
//TODO: how about stacks casting Clone?
|
//TODO: how about stacks casting Clone?
|
||||||
//currently Clone casted by stack is assumed Expert level
|
//currently Clone casted by stack is assumed Expert level
|
||||||
ui8 schoolLevel;
|
ui8 schoolLevel;
|
||||||
@ -127,26 +135,15 @@ ESpellCastProblem::ESpellCastProblem CloneMechanics::isImmuneByStack(const CGHer
|
|||||||
void CureMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
|
void CureMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
|
||||||
{
|
{
|
||||||
DefaultSpellMechanics::applyBattle(battle, packet);
|
DefaultSpellMechanics::applyBattle(battle, packet);
|
||||||
|
doDispell(battle, packet, [](const Bonus * b) -> bool
|
||||||
for(auto stackID : packet->affectedCres)
|
|
||||||
{
|
{
|
||||||
if(vstd::contains(packet->resisted, stackID))
|
if(b->source == Bonus::SPELL_EFFECT)
|
||||||
{
|
{
|
||||||
logGlobal->errorStream() << "Resistance to positive spell CURE";
|
CSpell * sp = SpellID(b->sid).toSpell();
|
||||||
continue;
|
return sp->isNegative();
|
||||||
}
|
}
|
||||||
|
return false; //not a spell effect
|
||||||
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
|
///DispellMechanics
|
||||||
@ -310,8 +307,8 @@ ESpellCastProblem::ESpellCastProblem HypnotizeMechanics::isImmuneByStack(const C
|
|||||||
//TODO: what with other creatures casting hypnotize, Faerie Dragons style?
|
//TODO: what with other creatures casting hypnotize, Faerie Dragons style?
|
||||||
ui64 subjectHealth = (obj->count - 1) * obj->MaxHealth() + obj->firstHPleft;
|
ui64 subjectHealth = (obj->count - 1) * obj->MaxHealth() + obj->firstHPleft;
|
||||||
//apply 'damage' bonus for hypnotize, including hero specialty
|
//apply 'damage' bonus for hypnotize, including hero specialty
|
||||||
ui64 maxHealth = owner->calculateBonus(caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
|
ui64 maxHealth = caster->getSpellBonus(owner, caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER)
|
||||||
* owner->power + owner->getPower(caster->getSpellSchoolLevel(owner)), caster, obj);
|
* owner->power + owner->getPower(caster->getSpellSchoolLevel(owner)), obj);
|
||||||
if (subjectHealth > maxHealth)
|
if (subjectHealth > maxHealth)
|
||||||
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
return ESpellCastProblem::STACK_IMMUNE_TO_SPELL;
|
||||||
}
|
}
|
||||||
@ -470,7 +467,8 @@ ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCasted(const CBatt
|
|||||||
//using isImmuneBy directly as this mechanics does not have overridden immunity check
|
//using isImmuneBy directly as this mechanics does not have overridden immunity check
|
||||||
//therefore we do not need to check caster and casting mode
|
//therefore we do not need to check caster and casting mode
|
||||||
//TODO: check that we really should check immunity for both stacks
|
//TODO: check that we really should check immunity for both stacks
|
||||||
const bool immune = ESpellCastProblem::OK != owner->isImmuneBy(stack);
|
ESpellCastProblem::ESpellCastProblem res = owner->isImmuneBy(stack);
|
||||||
|
const bool immune = ESpellCastProblem::OK != res && ESpellCastProblem::NOT_DECIDED != res;
|
||||||
const bool casterStack = stack->owner == player;
|
const bool casterStack = stack->owner == player;
|
||||||
|
|
||||||
if(!immune && casterStack)
|
if(!immune && casterStack)
|
||||||
@ -482,7 +480,6 @@ ESpellCastProblem::ESpellCastProblem SacrificeMechanics::canBeCasted(const CBatt
|
|||||||
if(targetExists && targetToSacrificeExists)
|
if(targetExists && targetToSacrificeExists)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(targetExists && targetToSacrificeExists)
|
if(targetExists && targetToSacrificeExists)
|
||||||
@ -571,7 +568,7 @@ void SummonMechanics::applyBattleEffects(const SpellCastEnvironment * env, Battl
|
|||||||
bsa.pos = parameters.cb->getAvaliableHex(creatureToSummon, !(bool)parameters.casterSide); //TODO: unify it
|
bsa.pos = parameters.cb->getAvaliableHex(creatureToSummon, !(bool)parameters.casterSide); //TODO: unify it
|
||||||
|
|
||||||
//TODO stack casting -> probably power will be zero; set the proper number of creatures manually
|
//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;
|
int percentBonus = parameters.casterHero ? parameters.casterHero->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, owner->id.toEnum()) : 0;
|
||||||
|
|
||||||
bsa.amount = parameters.usedSpellPower
|
bsa.amount = parameters.usedSpellPower
|
||||||
* owner->getPower(parameters.spellLvl)
|
* owner->getPower(parameters.spellLvl)
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
#include "../NetPacks.h"
|
#include "../NetPacks.h"
|
||||||
#include "../BattleState.h"
|
#include "../BattleState.h"
|
||||||
|
|
||||||
|
#include "../CGeneralTextHandler.h"
|
||||||
|
|
||||||
namespace SRSLPraserHelpers
|
namespace SRSLPraserHelpers
|
||||||
{
|
{
|
||||||
static int XYToHex(int x, int y)
|
static int XYToHex(int x, int y)
|
||||||
@ -120,7 +122,7 @@ namespace SRSLPraserHelpers
|
|||||||
///DefaultSpellMechanics
|
///DefaultSpellMechanics
|
||||||
void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
|
void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const
|
||||||
{
|
{
|
||||||
if (packet->castedByHero)
|
if (packet->castByHero)
|
||||||
{
|
{
|
||||||
if (packet->side < 2)
|
if (packet->side < 2)
|
||||||
{
|
{
|
||||||
@ -131,9 +133,6 @@ void DefaultSpellMechanics::applyBattle(BattleInfo * battle, const BattleSpellCa
|
|||||||
//handle countering spells
|
//handle countering spells
|
||||||
for(auto stackID : packet->affectedCres)
|
for(auto stackID : packet->affectedCres)
|
||||||
{
|
{
|
||||||
if(vstd::contains(packet->resisted, stackID))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
CStack * s = battle->getStack(stackID);
|
CStack * s = battle->getStack(stackID);
|
||||||
s->popBonuses([&](const Bonus * b) -> bool
|
s->popBonuses([&](const Bonus * b) -> bool
|
||||||
{
|
{
|
||||||
@ -230,16 +229,16 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
|
|||||||
sc.skill = parameters.spellLvl;
|
sc.skill = parameters.spellLvl;
|
||||||
sc.tile = parameters.destination;
|
sc.tile = parameters.destination;
|
||||||
sc.dmgToDisplay = 0;
|
sc.dmgToDisplay = 0;
|
||||||
sc.castedByHero = nullptr != parameters.caster;
|
sc.castByHero = nullptr != parameters.casterHero;
|
||||||
sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1);
|
sc.casterStack = (parameters.casterStack ? parameters.casterStack->ID : -1);
|
||||||
sc.manaGained = 0;
|
sc.manaGained = 0;
|
||||||
|
|
||||||
int spellCost = 0;
|
int spellCost = 0;
|
||||||
|
|
||||||
//calculate spell cost
|
//calculate spell cost
|
||||||
if(parameters.caster)
|
if(parameters.casterHero)
|
||||||
{
|
{
|
||||||
spellCost = parameters.cb->battleGetSpellCost(owner, parameters.caster);
|
spellCost = parameters.cb->battleGetSpellCost(owner, parameters.casterHero);
|
||||||
|
|
||||||
if(parameters.secHero && parameters.mode == ECastingMode::HERO_CASTING) //handle mana channel
|
if(parameters.secHero && parameters.mode == ECastingMode::HERO_CASTING) //handle mana channel
|
||||||
{
|
{
|
||||||
@ -259,27 +258,60 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
|
|||||||
//must be vector, as in Chain Lightning order matters
|
//must be vector, as in Chain Lightning order matters
|
||||||
std::vector<const CStack*> attackedCres; //CStack vector is somewhat more suitable than ID vector
|
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);
|
auto creatures = owner->getAffectedStacks(parameters.cb, parameters.mode, parameters.casterColor, parameters.spellLvl, parameters.destination, parameters.casterHero);
|
||||||
std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres));
|
std::copy(creatures.begin(), creatures.end(), std::back_inserter(attackedCres));
|
||||||
|
|
||||||
for (auto cre : attackedCres)
|
std::vector <const CStack*> reflected;//for magic mirror
|
||||||
{
|
|
||||||
sc.affectedCres.insert(cre->ID);
|
|
||||||
}
|
|
||||||
|
|
||||||
//checking if creatures resist
|
//checking if creatures resist
|
||||||
//resistance is applied only to negative spells
|
//resistance/reflection is applied only to negative spells
|
||||||
if(owner->isNegative())
|
if(owner->isNegative())
|
||||||
{
|
{
|
||||||
|
//it is actual spell and can be reflected to single target, no recurrence
|
||||||
|
const bool tryMagicMirror = parameters.mode != ECastingMode::MAGIC_MIRROR && owner->level && owner->getLevelInfo(0).range == "0";
|
||||||
|
std::vector <const CStack*> resisted;
|
||||||
for(auto s : attackedCres)
|
for(auto s : attackedCres)
|
||||||
{
|
{
|
||||||
|
//magic resistance
|
||||||
const int prob = std::min((s)->magicResistance(), 100); //probability of resistance in %
|
const int prob = std::min((s)->magicResistance(), 100); //probability of resistance in %
|
||||||
|
|
||||||
if(env->getRandomGenerator().nextInt(99) < prob)
|
if(env->getRandomGenerator().nextInt(99) < prob)
|
||||||
{
|
{
|
||||||
sc.resisted.push_back(s->ID);
|
resisted.push_back(s);
|
||||||
|
}
|
||||||
|
//magic mirror
|
||||||
|
if(tryMagicMirror)
|
||||||
|
{
|
||||||
|
const int mirrorChance = (s)->valOfBonuses(Bonus::MAGIC_MIRROR);
|
||||||
|
if(env->getRandomGenerator().nextInt(99) < mirrorChance)
|
||||||
|
reflected.push_back(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vstd::erase_if(attackedCres, [&resisted, reflected](const CStack * s)
|
||||||
|
{
|
||||||
|
return vstd::contains(resisted, s) || vstd::contains(reflected, s);
|
||||||
|
});
|
||||||
|
|
||||||
|
for(auto s : resisted)
|
||||||
|
{
|
||||||
|
BattleSpellCast::CustomEffect effect;
|
||||||
|
effect.effect = 78;
|
||||||
|
effect.stack = s->ID;
|
||||||
|
sc.customEffects.push_back(effect);
|
||||||
|
}
|
||||||
|
for(auto s : reflected)
|
||||||
|
{
|
||||||
|
BattleSpellCast::CustomEffect effect;
|
||||||
|
effect.effect = 3;
|
||||||
|
effect.stack = s->ID;
|
||||||
|
sc.customEffects.push_back(effect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto cre : attackedCres)
|
||||||
|
{
|
||||||
|
sc.affectedCres.insert(cre->ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
StacksInjured si;
|
StacksInjured si;
|
||||||
@ -290,12 +322,12 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
|
|||||||
env->sendAndApply(&sc);
|
env->sendAndApply(&sc);
|
||||||
|
|
||||||
//spend mana
|
//spend mana
|
||||||
if(parameters.caster)
|
if(parameters.casterHero)
|
||||||
{
|
{
|
||||||
SetMana sm;
|
SetMana sm;
|
||||||
sm.absolute = false;
|
sm.absolute = false;
|
||||||
|
|
||||||
sm.hid = parameters.caster->id;
|
sm.hid = parameters.casterHero->id;
|
||||||
sm.val = -spellCost;
|
sm.val = -spellCost;
|
||||||
|
|
||||||
env->sendAndApply(&sm);
|
env->sendAndApply(&sm);
|
||||||
@ -328,61 +360,143 @@ void DefaultSpellMechanics::battleCast(const SpellCastEnvironment * env, BattleS
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Magic Mirror effect
|
//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 : reflected)
|
||||||
{
|
{
|
||||||
for(auto & attackedCre : attackedCres)
|
TStacks mirrorTargets = parameters.cb->battleGetStacksIf([this, parameters](const CStack * battleStack)
|
||||||
{
|
{
|
||||||
int mirrorChance = (attackedCre)->valOfBonuses(Bonus::MAGIC_MIRROR);
|
//Get all enemy stacks. Magic mirror can reflect to immune creature (with no effect)
|
||||||
if(mirrorChance > env->getRandomGenerator().nextInt(99))
|
return battleStack->owner == parameters.casterColor;
|
||||||
{
|
},
|
||||||
std::vector<const CStack *> mirrorTargets;
|
true);//turrets included
|
||||||
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;
|
if(!mirrorTargets.empty())
|
||||||
mirrorParameters.spellLvl = 0;
|
{
|
||||||
mirrorParameters.casterSide = 1-parameters.casterSide;
|
int targetHex = (*RandomGeneratorUtil::nextItem(mirrorTargets, env->getRandomGenerator()))->position;
|
||||||
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);
|
BattleSpellCastParameters mirrorParameters = parameters;
|
||||||
}
|
mirrorParameters.spellLvl = 0;
|
||||||
}
|
mirrorParameters.casterSide = 1-parameters.casterSide;
|
||||||
|
mirrorParameters.casterColor = (attackedCre)->owner;
|
||||||
|
mirrorParameters.casterHero = nullptr;
|
||||||
|
mirrorParameters.destination = targetHex;
|
||||||
|
mirrorParameters.secHero = parameters.casterHero;
|
||||||
|
mirrorParameters.mode = ECastingMode::MAGIC_MIRROR;
|
||||||
|
mirrorParameters.casterStack = (attackedCre);
|
||||||
|
mirrorParameters.selectedStack = nullptr;
|
||||||
|
|
||||||
|
battleCast(env, mirrorParameters);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DefaultSpellMechanics::battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
|
||||||
|
const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const
|
||||||
|
{
|
||||||
|
const std::string attackedName = attackedStack->getName();
|
||||||
|
const std::string attackedNameSing = attackedStack->getCreature()->nameSing;
|
||||||
|
const std::string attackedNamePl = attackedStack->getCreature()->namePl;
|
||||||
|
|
||||||
|
auto getPluralFormat = [attackedStack](const int baseTextID) -> boost::format
|
||||||
|
{
|
||||||
|
return boost::format(VLC->generaltexth->allTexts[(attackedStack->count > 1 ? baseTextID + 1 : baseTextID)]);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto logSimple = [&logLines, getPluralFormat, attackedName](const int baseTextID)
|
||||||
|
{
|
||||||
|
boost::format fmt = getPluralFormat(baseTextID);
|
||||||
|
fmt % attackedName;
|
||||||
|
logLines.push_back(fmt.str());
|
||||||
|
};
|
||||||
|
|
||||||
|
auto logPlural = [&logLines, attackedNamePl](const int baseTextID)
|
||||||
|
{
|
||||||
|
boost::format fmt(VLC->generaltexth->allTexts[baseTextID]);
|
||||||
|
fmt % attackedNamePl;
|
||||||
|
logLines.push_back(fmt.str());
|
||||||
|
};
|
||||||
|
|
||||||
|
displayDamage = false; //in most following cases damage info text is custom
|
||||||
|
switch(owner->id)
|
||||||
|
{
|
||||||
|
case SpellID::STONE_GAZE:
|
||||||
|
logSimple(558);
|
||||||
|
break;
|
||||||
|
case SpellID::POISON:
|
||||||
|
logSimple(561);
|
||||||
|
break;
|
||||||
|
case SpellID::BIND:
|
||||||
|
logPlural(560);//Roots and vines bind the %s to the ground!
|
||||||
|
break;
|
||||||
|
case SpellID::DISEASE:
|
||||||
|
logSimple(553);
|
||||||
|
break;
|
||||||
|
case SpellID::PARALYZE:
|
||||||
|
logSimple(563);
|
||||||
|
break;
|
||||||
|
case SpellID::AGE:
|
||||||
|
{
|
||||||
|
boost::format text = getPluralFormat(551);
|
||||||
|
text % attackedName;
|
||||||
|
//The %s shrivel with age, and lose %d hit points."
|
||||||
|
TBonusListPtr bl = attackedStack->getBonuses(Selector::type(Bonus::STACK_HEALTH));
|
||||||
|
const int fullHP = bl->totalValue();
|
||||||
|
bl->remove_if(Selector::source(Bonus::SPELL_EFFECT, SpellID::AGE));
|
||||||
|
text % (fullHP - bl->totalValue());
|
||||||
|
logLines.push_back(text.str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SpellID::THUNDERBOLT:
|
||||||
|
{
|
||||||
|
logPlural(367);
|
||||||
|
std::string text = VLC->generaltexth->allTexts[343].substr(1, VLC->generaltexth->allTexts[343].size() - 1); //Does %d points of damage.
|
||||||
|
boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(packet->dmgToDisplay)); //no more text afterwards
|
||||||
|
logLines.push_back(text);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case SpellID::DISPEL_HELPFUL_SPELLS:
|
||||||
|
logPlural(555);
|
||||||
|
break;
|
||||||
|
case SpellID::DEATH_STARE:
|
||||||
|
if (packet->dmgToDisplay > 0)
|
||||||
|
{
|
||||||
|
std::string text;
|
||||||
|
if (packet->dmgToDisplay > 1)
|
||||||
|
{
|
||||||
|
text = VLC->generaltexth->allTexts[119]; //%d %s die under the terrible gaze of the %s.
|
||||||
|
boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(packet->dmgToDisplay));
|
||||||
|
boost::algorithm::replace_first(text, "%s", attackedNamePl);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text = VLC->generaltexth->allTexts[118]; //One %s dies under the terrible gaze of the %s.
|
||||||
|
boost::algorithm::replace_first(text, "%s", attackedNameSing);
|
||||||
|
}
|
||||||
|
boost::algorithm::replace_first(text, "%s", casterName); //casting stack
|
||||||
|
logLines.push_back(text);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
boost::format text(VLC->generaltexth->allTexts[565]); //The %s casts %s
|
||||||
|
text % casterName % owner->name;
|
||||||
|
displayDamage = true;
|
||||||
|
logLines.push_back(text.str());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int DefaultSpellMechanics::calculateDuration(const CGHeroInstance * caster, int usedSpellPower) const
|
int DefaultSpellMechanics::calculateDuration(const CGHeroInstance * caster, int usedSpellPower) const
|
||||||
{
|
{
|
||||||
if(!caster)
|
if(caster == nullptr)
|
||||||
{
|
{
|
||||||
if (!usedSpellPower)
|
if (!usedSpellPower)
|
||||||
return 3; //default duration of all creature spells
|
return 3; //default duration of all creature spells
|
||||||
else
|
else
|
||||||
return usedSpellPower; //use creature spell power
|
return usedSpellPower; //use creature spell power
|
||||||
}
|
}
|
||||||
switch(owner->id)
|
else
|
||||||
{
|
|
||||||
case SpellID::FRENZY:
|
|
||||||
return 1;
|
|
||||||
default: //other spells
|
|
||||||
return caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + caster->valOfBonuses(Bonus::SPELL_DURATION);
|
return caster->getPrimSkillLevel(PrimarySkill::SPELL_POWER) + caster->valOfBonuses(Bonus::SPELL_DURATION);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ui32 DefaultSpellMechanics::calculateHealedHP(const CGHeroInstance* caster, const CStack* stack, const CStack* sacrificedStack) const
|
ui32 DefaultSpellMechanics::calculateHealedHP(const CGHeroInstance* caster, const CStack* stack, const CStack* sacrificedStack) const
|
||||||
@ -402,13 +516,21 @@ ui32 DefaultSpellMechanics::calculateHealedHP(const CGHeroInstance* caster, cons
|
|||||||
healedHealth = (spellPowerSkill + sacrificedStack->MaxHealth() + levelPower) * sacrificedStack->count;
|
healedHealth = (spellPowerSkill + sacrificedStack->MaxHealth() + levelPower) * sacrificedStack->count;
|
||||||
else
|
else
|
||||||
healedHealth = spellPowerSkill * owner->power + levelPower; //???
|
healedHealth = spellPowerSkill * owner->power + levelPower; //???
|
||||||
healedHealth = owner->calculateBonus(healedHealth, caster, stack);
|
healedHealth = caster->getSpellBonus(owner, healedHealth, stack);
|
||||||
return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (owner->isRisingSpell() ? stack->baseAmount * stack->MaxHealth() : 0));
|
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
|
void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const
|
||||||
{
|
{
|
||||||
|
int effectLevel = parameters.spellLvl;
|
||||||
|
{
|
||||||
|
//MAXED_SPELL bonus.
|
||||||
|
if(parameters.casterHero != nullptr)
|
||||||
|
if(parameters.casterHero->hasBonusOfType(Bonus::MAXED_SPELL, owner->id))
|
||||||
|
effectLevel = 3;
|
||||||
|
}
|
||||||
|
|
||||||
//applying effects
|
//applying effects
|
||||||
if(owner->isOffensiveSpell())
|
if(owner->isOffensiveSpell())
|
||||||
{
|
{
|
||||||
@ -427,14 +549,11 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
|
|||||||
int chainLightningModifier = 0;
|
int chainLightningModifier = 0;
|
||||||
for(auto & attackedCre : ctx.attackedCres)
|
for(auto & attackedCre : ctx.attackedCres)
|
||||||
{
|
{
|
||||||
if(vstd::contains(ctx.sc.resisted, (attackedCre)->ID)) //this creature resisted the spell
|
|
||||||
continue;
|
|
||||||
|
|
||||||
BattleStackAttacked bsa;
|
BattleStackAttacked bsa;
|
||||||
if(spellDamage)
|
if(spellDamage)
|
||||||
bsa.damageAmount = spellDamage >> chainLightningModifier;
|
bsa.damageAmount = spellDamage >> chainLightningModifier;
|
||||||
else
|
else
|
||||||
bsa.damageAmount = owner->calculateDamage(parameters.caster, attackedCre, parameters.spellLvl, parameters.usedSpellPower) >> chainLightningModifier;
|
bsa.damageAmount = owner->calculateDamage(parameters.casterHero, attackedCre, effectLevel, parameters.usedSpellPower) >> chainLightningModifier;
|
||||||
|
|
||||||
ctx.sc.dmgToDisplay += bsa.damageAmount;
|
ctx.sc.dmgToDisplay += bsa.damageAmount;
|
||||||
|
|
||||||
@ -459,29 +578,42 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
|
|||||||
stackSpellPower = parameters.casterStack->valOfBonuses(Bonus::CREATURE_ENCHANT_POWER);
|
stackSpellPower = parameters.casterStack->valOfBonuses(Bonus::CREATURE_ENCHANT_POWER);
|
||||||
}
|
}
|
||||||
SetStackEffect sse;
|
SetStackEffect sse;
|
||||||
Bonus pseudoBonus;
|
//get default spell duration (spell power with bonuses for heroes)
|
||||||
pseudoBonus.sid = owner->id;
|
int duration = calculateDuration(parameters.casterHero, stackSpellPower ? stackSpellPower : parameters.usedSpellPower);
|
||||||
pseudoBonus.val = parameters.spellLvl;
|
//generate actual stack bonuses
|
||||||
pseudoBonus.turnsRemain = calculateDuration(parameters.caster, stackSpellPower ? stackSpellPower : parameters.usedSpellPower);
|
{
|
||||||
CStack::stackEffectToFeature(sse.effect, pseudoBonus);
|
int maxDuration = 0;
|
||||||
|
std::vector<Bonus> tmp;
|
||||||
|
owner->getEffects(tmp, effectLevel);
|
||||||
|
for(Bonus& b : tmp)
|
||||||
|
{
|
||||||
|
//use configured duration if present
|
||||||
|
if(b.turnsRemain == 0)
|
||||||
|
b.turnsRemain = duration;
|
||||||
|
vstd::amax(maxDuration, b.turnsRemain);
|
||||||
|
sse.effect.push_back(b);
|
||||||
|
}
|
||||||
|
//if all spell effects have special duration, use it
|
||||||
|
duration = maxDuration;
|
||||||
|
}
|
||||||
|
//fix to original config: shield should display damage reduction
|
||||||
if(owner->id == SpellID::SHIELD || owner->id == SpellID::AIR_SHIELD)
|
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
|
sse.effect.back().val = (100 - sse.effect.back().val);
|
||||||
}
|
}
|
||||||
if(owner->id == SpellID::BIND && parameters.casterStack)//bind
|
//we need to know who cast Bind
|
||||||
|
if(owner->id == SpellID::BIND && parameters.casterStack)
|
||||||
{
|
{
|
||||||
sse.effect.back().additionalInfo = parameters.casterStack->ID; //we need to know who casted Bind
|
sse.effect.back().additionalInfo = parameters.casterStack->ID;
|
||||||
}
|
}
|
||||||
const Bonus * bonus = nullptr;
|
const Bonus * bonus = nullptr;
|
||||||
if(parameters.caster)
|
if(parameters.casterHero)
|
||||||
bonus = parameters.caster->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, owner->id));
|
bonus = parameters.casterHero->getBonusLocalFirst(Selector::typeSubtype(Bonus::SPECIAL_PECULIAR_ENCHANT, owner->id));
|
||||||
//TODO does hero specialty should affects his stack casting spells?
|
//TODO does hero specialty should affects his stack casting spells?
|
||||||
|
|
||||||
si32 power = 0;
|
si32 power = 0;
|
||||||
for(const CStack * affected : ctx.attackedCres)
|
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);
|
sse.stacks.push_back(affected->ID);
|
||||||
|
|
||||||
//Apply hero specials - peculiar enchants
|
//Apply hero specials - peculiar enchants
|
||||||
@ -512,17 +644,17 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
|
|||||||
case 1: //only Coronius as yet
|
case 1: //only Coronius as yet
|
||||||
{
|
{
|
||||||
power = std::max(5 - tier, 0);
|
power = std::max(5 - tier, 0);
|
||||||
Bonus specialBonus = CStack::featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, pseudoBonus.turnsRemain);
|
Bonus specialBonus = CStack::featureGenerator(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK, power, duration);
|
||||||
specialBonus.sid = owner->id;
|
specialBonus.sid = owner->id;
|
||||||
sse.uniqueBonuses.push_back(std::pair<ui32,Bonus> (affected->ID, specialBonus)); //additional attack to Slayer effect
|
sse.uniqueBonuses.push_back(std::pair<ui32,Bonus> (affected->ID, specialBonus)); //additional attack to Slayer effect
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (parameters.caster && parameters.caster->hasBonusOfType(Bonus::SPECIAL_BLESS_DAMAGE, owner->id)) //TODO: better handling of bonus percentages
|
if (parameters.casterHero && parameters.casterHero->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;
|
int damagePercent = parameters.casterHero->level * parameters.casterHero->valOfBonuses(Bonus::SPECIAL_BLESS_DAMAGE, owner->id.toEnum()) / tier;
|
||||||
Bonus specialBonus = CStack::featureGenerator(Bonus::CREATURE_DAMAGE, 0, damagePercent, pseudoBonus.turnsRemain);
|
Bonus specialBonus = CStack::featureGenerator(Bonus::CREATURE_DAMAGE, 0, damagePercent, duration);
|
||||||
specialBonus.valType = Bonus::PERCENT_TO_ALL;
|
specialBonus.valType = Bonus::PERCENT_TO_ALL;
|
||||||
specialBonus.sid = owner->id;
|
specialBonus.sid = owner->id;
|
||||||
sse.uniqueBonuses.push_back (std::pair<ui32,Bonus> (affected->ID, specialBonus));
|
sse.uniqueBonuses.push_back (std::pair<ui32,Bonus> (affected->ID, specialBonus));
|
||||||
@ -563,13 +695,13 @@ void DefaultSpellMechanics::applyBattleEffects(const SpellCastEnvironment * env,
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
//any typical spell (commander's cure or animate dead)
|
//any typical spell (commander's cure or animate dead)
|
||||||
int healedHealth = parameters.usedSpellPower * owner->power + owner->getPower(parameters.spellLvl);
|
int healedHealth = parameters.usedSpellPower * owner->power + owner->getPower(effectLevel);
|
||||||
hi.healedHP = std::min<ui32>(healedHealth, attackedCre->MaxHealth() - attackedCre->firstHPleft + (resurrect ? attackedCre->baseAmount * attackedCre->MaxHealth() : 0));
|
hi.healedHP = std::min<ui32>(healedHealth, attackedCre->MaxHealth() - attackedCre->firstHPleft + (resurrect ? attackedCre->baseAmount * attackedCre->MaxHealth() : 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
hi.healedHP = calculateHealedHP(parameters.caster, attackedCre, parameters.selectedStack); //Casted by hero
|
hi.healedHP = calculateHealedHP(parameters.casterHero, attackedCre, parameters.selectedStack); //Casted by hero
|
||||||
hi.lowLevelResurrection = (parameters.spellLvl <= 1) && (owner->id != SpellID::ANIMATE_DEAD);
|
hi.lowLevelResurrection = (effectLevel <= 1) && (owner->id != SpellID::ANIMATE_DEAD);
|
||||||
shr.healedStacks.push_back(hi);
|
shr.healedStacks.push_back(hi);
|
||||||
}
|
}
|
||||||
if(!shr.healedStacks.empty())
|
if(!shr.healedStacks.empty())
|
||||||
@ -734,9 +866,6 @@ void DefaultSpellMechanics::doDispell(BattleInfo * battle, const BattleSpellCast
|
|||||||
{
|
{
|
||||||
for(auto stackID : packet->affectedCres)
|
for(auto stackID : packet->affectedCres)
|
||||||
{
|
{
|
||||||
if(vstd::contains(packet->resisted, stackID))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
CStack *s = battle->getStack(stackID);
|
CStack *s = battle->getStack(stackID);
|
||||||
s->popBonuses(selector);
|
s->popBonuses(selector);
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,8 @@ public:
|
|||||||
bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override final;
|
bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const override final;
|
||||||
void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const override;
|
void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const override;
|
||||||
|
|
||||||
|
void battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
|
||||||
|
const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const override;
|
||||||
protected:
|
protected:
|
||||||
virtual void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const;
|
virtual void applyBattleEffects(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters, SpellCastContext & ctx) const;
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "../mapObjects/CGHeroInstance.h"
|
#include "../mapObjects/CGHeroInstance.h"
|
||||||
#include "../BattleState.h"
|
#include "../BattleState.h"
|
||||||
#include "../CBattleCallback.h"
|
#include "../CBattleCallback.h"
|
||||||
|
#include "../CGameState.h"
|
||||||
|
|
||||||
#include "ISpellMechanics.h"
|
#include "ISpellMechanics.h"
|
||||||
|
|
||||||
@ -71,7 +72,7 @@ namespace SpellConfig
|
|||||||
}
|
}
|
||||||
|
|
||||||
BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo* cb)
|
BattleSpellCastParameters::BattleSpellCastParameters(const BattleInfo* cb)
|
||||||
: spellLvl(0), destination(BattleHex::INVALID), casterSide(0),casterColor(PlayerColor::CANNOT_DETERMINE),caster(nullptr), secHero(nullptr),
|
: spellLvl(0), destination(BattleHex::INVALID), casterSide(0),casterColor(PlayerColor::CANNOT_DETERMINE),casterHero(nullptr), secHero(nullptr),
|
||||||
usedSpellPower(0),mode(ECastingMode::HERO_CASTING), casterStack(nullptr), selectedStack(nullptr), cb(cb)
|
usedSpellPower(0),mode(ECastingMode::HERO_CASTING), casterStack(nullptr), selectedStack(nullptr), cb(cb)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -126,38 +127,6 @@ void CSpell::battleCast(const SpellCastEnvironment * env, BattleSpellCastParamet
|
|||||||
mechanics->battleCast(env, parameters);
|
mechanics->battleCast(env, parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CSpell::isCastableBy(const IBonusBearer * caster, bool hasSpellBook, const std::set<SpellID> & spellBook) const
|
|
||||||
{
|
|
||||||
if(!hasSpellBook)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const bool inSpellBook = vstd::contains(spellBook, id);
|
|
||||||
const bool isBonus = caster->hasBonusOfType(Bonus::SPELL, id);
|
|
||||||
|
|
||||||
bool inTome = false;
|
|
||||||
|
|
||||||
forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
|
|
||||||
{
|
|
||||||
if(caster->hasBonusOfType(cnf.knoledgeBonus))
|
|
||||||
{
|
|
||||||
inTome = stop = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (isSpecialSpell())
|
|
||||||
{
|
|
||||||
if (inSpellBook)
|
|
||||||
{//hero has this spell in spellbook
|
|
||||||
logGlobal->errorStream() << "Special spell in spellbook "<<name;
|
|
||||||
}
|
|
||||||
return isBonus;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return inSpellBook || inTome || isBonus || caster->hasBonusOfType(Bonus::SPELLS_OF_LEVEL, level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
|
const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
|
||||||
{
|
{
|
||||||
if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
|
if(level < 0 || level >= GameConstants::SPELL_SCHOOL_LEVELS)
|
||||||
@ -169,29 +138,7 @@ const CSpell::LevelInfo & CSpell::getLevelInfo(const int level) const
|
|||||||
return levels.at(level);
|
return levels.at(level);
|
||||||
}
|
}
|
||||||
|
|
||||||
ui32 CSpell::calculateBonus(ui32 baseDamage, const CGHeroInstance * caster, const CStack * affectedCreature) const
|
ui32 CSpell::calculateDamage(const ISpellCaster * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const
|
||||||
{
|
|
||||||
ui32 ret = baseDamage;
|
|
||||||
|
|
||||||
//applying sorcery secondary skill
|
|
||||||
if(caster)
|
|
||||||
{
|
|
||||||
ret *= (100.0 + caster->valOfBonuses(Bonus::SECONDARY_SKILL_PREMY, SecondarySkill::SORCERY)) / 100.0;
|
|
||||||
ret *= (100.0 + caster->valOfBonuses(Bonus::SPELL_DAMAGE) + caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, id.toEnum())) / 100.0;
|
|
||||||
|
|
||||||
forEachSchool([&](const SpellSchoolInfo & cnf, bool & stop)
|
|
||||||
{
|
|
||||||
ret *= (100.0 + caster->valOfBonuses(cnf.damagePremyBonus)) / 100.0;
|
|
||||||
stop = true; //only bonus from one school is used
|
|
||||||
});
|
|
||||||
|
|
||||||
if (affectedCreature && affectedCreature->getCreature()->level) //Hero specials like Solmyr, Deemer
|
|
||||||
ret *= (100. + ((caster->valOfBonuses(Bonus::SPECIAL_SPELL_LEV, id.toEnum()) * caster->level) / affectedCreature->getCreature()->level)) / 100.0;
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui32 CSpell::calculateDamage(const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const
|
|
||||||
{
|
{
|
||||||
ui32 ret = 0; //value to return
|
ui32 ret = 0; //value to return
|
||||||
|
|
||||||
@ -230,7 +177,9 @@ ui32 CSpell::calculateDamage(const CGHeroInstance * caster, const CStack * affec
|
|||||||
ret /= 100;
|
ret /= 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ret = calculateBonus(ret, caster, affectedCreature);
|
|
||||||
|
if(nullptr != caster) //todo: make sure that caster always present
|
||||||
|
ret = caster->getSpellBonus(this, ret, affectedCreature);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -551,6 +500,59 @@ ESpellCastProblem::ESpellCastProblem CSpell::isImmuneByStack(const CGHeroInstanc
|
|||||||
return ESpellCastProblem::OK;
|
return ESpellCastProblem::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CSpell::prepareBattleLog(const CBattleInfoCallback * cb, const BattleSpellCast * packet, std::vector<std::string> & logLines) const
|
||||||
|
{
|
||||||
|
bool displayDamage = true;
|
||||||
|
|
||||||
|
std::string casterName("Something"); //todo: localize
|
||||||
|
|
||||||
|
if(packet->castByHero)
|
||||||
|
casterName = cb->battleGetHeroInfo(packet->side).name;
|
||||||
|
|
||||||
|
{
|
||||||
|
const auto casterStackID = packet->casterStack;
|
||||||
|
|
||||||
|
if(casterStackID > 0)
|
||||||
|
{
|
||||||
|
const CStack * casterStack = cb->battleGetStackByID(casterStackID);
|
||||||
|
if(casterStack != nullptr)
|
||||||
|
casterName = casterStack->type->namePl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(packet->affectedCres.size() == 1)
|
||||||
|
{
|
||||||
|
const CStack * attackedStack = cb->battleGetStackByID(*packet->affectedCres.begin(), false);
|
||||||
|
|
||||||
|
const std::string attackedNamePl = attackedStack->getCreature()->namePl;
|
||||||
|
|
||||||
|
if(packet->castByHero)
|
||||||
|
{
|
||||||
|
const std::string fmt = VLC->generaltexth->allTexts[195];
|
||||||
|
logLines.push_back(boost::to_string(boost::format(fmt) % casterName % this->name % attackedNamePl));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mechanics->battleLogSingleTarget(logLines, packet, casterName, attackedStack, displayDamage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
boost::format text(VLC->generaltexth->allTexts[196]);
|
||||||
|
text % casterName % this->name;
|
||||||
|
logLines.push_back(text.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(packet->dmgToDisplay > 0 && displayDamage)
|
||||||
|
{
|
||||||
|
boost::format dmgInfo(VLC->generaltexth->allTexts[376]);
|
||||||
|
dmgInfo % this->name % packet->dmgToDisplay;
|
||||||
|
logLines.push_back(dmgInfo.str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void CSpell::setIsOffensive(const bool val)
|
void CSpell::setIsOffensive(const bool val)
|
||||||
{
|
{
|
||||||
isOffensive = val;
|
isOffensive = val;
|
||||||
@ -588,6 +590,14 @@ void CSpell::setupMechanics()
|
|||||||
mechanics = ISpellMechanics::createMechanics(this);
|
mechanics = ISpellMechanics::createMechanics(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///CSpell::AnimationInfo
|
||||||
|
CSpell::AnimationItem::AnimationItem()
|
||||||
|
:resourceName(""),verticalPosition(VerticalPosition::TOP),pause(0)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///CSpell::AnimationInfo
|
///CSpell::AnimationInfo
|
||||||
CSpell::AnimationInfo::AnimationInfo()
|
CSpell::AnimationInfo::AnimationInfo()
|
||||||
{
|
{
|
||||||
@ -924,7 +934,6 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json)
|
|||||||
for(const JsonNode & item : queueNode)
|
for(const JsonNode & item : queueNode)
|
||||||
{
|
{
|
||||||
CSpell::TAnimation newItem;
|
CSpell::TAnimation newItem;
|
||||||
newItem.verticalPosition = VerticalPosition::TOP;
|
|
||||||
|
|
||||||
if(item.getType() == JsonNode::DATA_STRING)
|
if(item.getType() == JsonNode::DATA_STRING)
|
||||||
newItem.resourceName = item.String();
|
newItem.resourceName = item.String();
|
||||||
@ -936,6 +945,11 @@ CSpell * CSpellHandler::loadFromJson(const JsonNode & json)
|
|||||||
if("bottom" == vPosStr)
|
if("bottom" == vPosStr)
|
||||||
newItem.verticalPosition = VerticalPosition::BOTTOM;
|
newItem.verticalPosition = VerticalPosition::BOTTOM;
|
||||||
}
|
}
|
||||||
|
else if(item.getType() == JsonNode::DATA_FLOAT)
|
||||||
|
{
|
||||||
|
newItem.pause = item.Float();
|
||||||
|
}
|
||||||
|
|
||||||
q.push_back(newItem);
|
q.push_back(newItem);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "Magic.h"
|
||||||
#include "../IHandlerBase.h"
|
#include "../IHandlerBase.h"
|
||||||
#include "../ConstTransitivePtr.h"
|
#include "../ConstTransitivePtr.h"
|
||||||
#include "../int3.h"
|
#include "../int3.h"
|
||||||
@ -66,7 +66,7 @@ public:
|
|||||||
BattleHex destination;
|
BattleHex destination;
|
||||||
ui8 casterSide;
|
ui8 casterSide;
|
||||||
PlayerColor casterColor;
|
PlayerColor casterColor;
|
||||||
const CGHeroInstance * caster;
|
const CGHeroInstance * casterHero; //deprecated
|
||||||
const CGHeroInstance * secHero;
|
const CGHeroInstance * secHero;
|
||||||
int usedSpellPower;
|
int usedSpellPower;
|
||||||
ECastingMode::ECastingMode mode;
|
ECastingMode::ECastingMode mode;
|
||||||
@ -104,10 +104,21 @@ public:
|
|||||||
{
|
{
|
||||||
std::string resourceName;
|
std::string resourceName;
|
||||||
VerticalPosition verticalPosition;
|
VerticalPosition verticalPosition;
|
||||||
|
int pause;
|
||||||
|
|
||||||
|
AnimationItem();
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler & h, const int version)
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
{
|
{
|
||||||
h & resourceName & verticalPosition;
|
h & resourceName & verticalPosition;
|
||||||
|
if(version >= 754)
|
||||||
|
{
|
||||||
|
h & pause;
|
||||||
|
}
|
||||||
|
else if(!h.saving)
|
||||||
|
{
|
||||||
|
pause = 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -217,8 +228,6 @@ public:
|
|||||||
CSpell();
|
CSpell();
|
||||||
~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)
|
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
|
ETargetType getTargetType() const; //deprecated
|
||||||
|
|
||||||
@ -248,11 +257,8 @@ public:
|
|||||||
//internal, for use only by Mechanics classes
|
//internal, for use only by Mechanics classes
|
||||||
ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *obj) const;
|
ESpellCastProblem::ESpellCastProblem isImmuneBy(const IBonusBearer *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
|
///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;
|
ui32 calculateDamage(const ISpellCaster * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const;
|
||||||
|
|
||||||
///selects from allStacks actually affected stacks
|
///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;
|
std::set<const CStack *> getAffectedStacks(const CBattleInfoCallback * cb, ECastingMode::ECastingMode mode, PlayerColor casterColor, int spellLvl, BattleHex destination, const CGHeroInstance * caster = nullptr) const;
|
||||||
@ -322,6 +328,11 @@ public:
|
|||||||
///implementation of BattleSpellCast applying
|
///implementation of BattleSpellCast applying
|
||||||
void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const;
|
void applyBattle(BattleInfo * battle, const BattleSpellCast * packet) const;
|
||||||
|
|
||||||
|
public:
|
||||||
|
///Client logic.
|
||||||
|
|
||||||
|
void prepareBattleLog(const CBattleInfoCallback * cb, const BattleSpellCast * packet, std::vector<std::string> & logLines) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setIsOffensive(const bool val);
|
void setIsOffensive(const bool val);
|
||||||
void setIsRising(const bool val);
|
void setIsRising(const bool val);
|
||||||
|
@ -47,6 +47,9 @@ public:
|
|||||||
virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0;
|
virtual bool adventureCast(const SpellCastEnvironment * env, AdventureSpellCastParameters & parameters) const = 0;
|
||||||
virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0;
|
virtual void battleCast(const SpellCastEnvironment * env, BattleSpellCastParameters & parameters) const = 0;
|
||||||
|
|
||||||
|
virtual void battleLogSingleTarget(std::vector<std::string> & logLines, const BattleSpellCast * packet,
|
||||||
|
const std::string & casterName, const CStack * attackedStack, bool & displayDamage) const = 0;
|
||||||
|
|
||||||
static ISpellMechanics * createMechanics(CSpell * s);
|
static ISpellMechanics * createMechanics(CSpell * s);
|
||||||
protected:
|
protected:
|
||||||
CSpell * owner;
|
CSpell * owner;
|
||||||
|
32
lib/spells/Magic.h
Normal file
32
lib/spells/Magic.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* Magic.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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* High-level interface for spells subsystem
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
class CSpell;
|
||||||
|
class CStack;
|
||||||
|
|
||||||
|
class DLL_LINKAGE ISpellCaster
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~ISpellCaster(){};
|
||||||
|
|
||||||
|
/// returns level on which given spell would be cast by this(0 - none, 1 - basic etc);
|
||||||
|
/// caster may not know this spell at all
|
||||||
|
/// optionally returns number of selected school by arg - 0 - air magic, 1 - fire magic, 2 - water magic, 3 - earth magic
|
||||||
|
virtual ui8 getSpellSchoolLevel(const CSpell * spell, int *outSelectedSchool = nullptr) const = 0;
|
||||||
|
|
||||||
|
virtual ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const = 0;
|
||||||
|
};
|
@ -2789,7 +2789,7 @@ bool CGameHandler::upgradeCreature( ObjectInstanceID objid, SlotID pos, Creature
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGameHandler::changeStackType(const StackLocation &sl, CCreature *c)
|
bool CGameHandler::changeStackType(const StackLocation &sl, const CCreature *c)
|
||||||
{
|
{
|
||||||
if(!sl.army->hasStackAtSlot(sl.slot))
|
if(!sl.army->hasStackAtSlot(sl.slot))
|
||||||
COMPLAIN_RET("Cannot find a stack to change type");
|
COMPLAIN_RET("Cannot find a stack to change type");
|
||||||
@ -3208,14 +3208,15 @@ bool CGameHandler::transformInUndead(const IMarket *market, const CGHeroInstance
|
|||||||
|
|
||||||
|
|
||||||
const CStackInstance &s = army->getStack(slot);
|
const CStackInstance &s = army->getStack(slot);
|
||||||
int resCreature;//resulting creature - bone dragons or skeletons
|
|
||||||
|
|
||||||
if (s.hasBonusOfType(Bonus::DRAGON_NATURE))
|
//resulting creature - bone dragons or skeletons
|
||||||
resCreature = 68;
|
CreatureID resCreature = CreatureID::SKELETON;
|
||||||
else
|
|
||||||
resCreature = 56;
|
|
||||||
|
|
||||||
changeStackType(StackLocation(army, slot), VLC->creh->creatures.at(resCreature));
|
if(s.hasBonusOfType(Bonus::DRAGON_NATURE)
|
||||||
|
|| (s.getCreatureID() == CreatureID::HYDRA)
|
||||||
|
|| (s.getCreatureID() == CreatureID::CHAOS_HYDRA))
|
||||||
|
resCreature = CreatureID::BONE_DRAGON;
|
||||||
|
changeStackType(StackLocation(army, slot), resCreature.toCreature());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3431,7 +3432,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
|||||||
//defensive stance //TODO: remove this bonus when stack becomes active
|
//defensive stance //TODO: remove this bonus when stack becomes active
|
||||||
SetStackEffect sse;
|
SetStackEffect sse;
|
||||||
sse.effect.push_back( Bonus(Bonus::STACK_GETS_TURN, Bonus::PRIMARY_SKILL, Bonus::OTHER, 20, -1, PrimarySkill::DEFENSE, Bonus::PERCENT_TO_ALL) );
|
sse.effect.push_back( Bonus(Bonus::STACK_GETS_TURN, Bonus::PRIMARY_SKILL, Bonus::OTHER, 20, -1, PrimarySkill::DEFENSE, Bonus::PERCENT_TO_ALL) );
|
||||||
sse.effect.push_back( Bonus(Bonus::STACK_GETS_TURN, Bonus::PRIMARY_SKILL, Bonus::OTHER, gs->curB->stacks.at(ba.stackNumber)->valOfBonuses(Bonus::DEFENSIVE_STANCE),
|
sse.effect.push_back( Bonus(Bonus::STACK_GETS_TURN, Bonus::PRIMARY_SKILL, Bonus::OTHER, gs->curB->battleGetStackByID(ba.stackNumber)->valOfBonuses(Bonus::DEFENSIVE_STANCE),
|
||||||
-1, PrimarySkill::DEFENSE, Bonus::ADDITIVE_VALUE));
|
-1, PrimarySkill::DEFENSE, Bonus::ADDITIVE_VALUE));
|
||||||
sse.stacks.push_back(ba.stackNumber);
|
sse.stacks.push_back(ba.stackNumber);
|
||||||
sendAndApply(&sse);
|
sendAndApply(&sse);
|
||||||
@ -3880,7 +3881,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
|||||||
p.mode = ECastingMode::CREATURE_ACTIVE_CASTING;
|
p.mode = ECastingMode::CREATURE_ACTIVE_CASTING;
|
||||||
p.destination = destination;
|
p.destination = destination;
|
||||||
p.casterColor = stack->owner;
|
p.casterColor = stack->owner;
|
||||||
p.caster = nullptr;
|
p.casterHero = nullptr;
|
||||||
p.usedSpellPower = 0;
|
p.usedSpellPower = 0;
|
||||||
p.casterStack = stack;
|
p.casterStack = stack;
|
||||||
p.selectedStack = nullptr;
|
p.selectedStack = nullptr;
|
||||||
@ -4080,7 +4081,7 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
|
|||||||
parameters.destination = ba.destinationTile;
|
parameters.destination = ba.destinationTile;
|
||||||
parameters.casterSide = ba.side;
|
parameters.casterSide = ba.side;
|
||||||
parameters.casterColor = h->tempOwner;
|
parameters.casterColor = h->tempOwner;
|
||||||
parameters.caster = h;
|
parameters.casterHero = h;
|
||||||
parameters.secHero = secondHero;
|
parameters.secHero = secondHero;
|
||||||
|
|
||||||
parameters.usedSpellPower = h->getPrimSkillLevel(PrimarySkill::SPELL_POWER);
|
parameters.usedSpellPower = h->getPrimSkillLevel(PrimarySkill::SPELL_POWER);
|
||||||
@ -4239,7 +4240,7 @@ void CGameHandler::stackTurnTrigger(const CStack * st)
|
|||||||
parameters.destination = BattleHex::INVALID;
|
parameters.destination = BattleHex::INVALID;
|
||||||
parameters.casterSide = side;
|
parameters.casterSide = side;
|
||||||
parameters.casterColor = st->owner;
|
parameters.casterColor = st->owner;
|
||||||
parameters.caster = nullptr;
|
parameters.casterHero = nullptr;
|
||||||
parameters.secHero = gs->curB->getHero(gs->curB->theOtherPlayer(st->owner));
|
parameters.secHero = gs->curB->getHero(gs->curB->theOtherPlayer(st->owner));
|
||||||
parameters.usedSpellPower = 0;
|
parameters.usedSpellPower = 0;
|
||||||
parameters.mode = ECastingMode::ENCHANTER_CASTING;
|
parameters.mode = ECastingMode::ENCHANTER_CASTING;
|
||||||
@ -4952,7 +4953,7 @@ void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType atta
|
|||||||
parameters.destination = destination;
|
parameters.destination = destination;
|
||||||
parameters.casterSide = !attacker->attackerOwned;
|
parameters.casterSide = !attacker->attackerOwned;
|
||||||
parameters.casterColor = attacker->owner;
|
parameters.casterColor = attacker->owner;
|
||||||
parameters.caster = nullptr;
|
parameters.casterHero = nullptr;
|
||||||
parameters.secHero = nullptr;
|
parameters.secHero = nullptr;
|
||||||
|
|
||||||
parameters.usedSpellPower = 0;
|
parameters.usedSpellPower = 0;
|
||||||
@ -4987,7 +4988,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
|
|||||||
parameters.destination = gs->curB->battleGetStackByID(bat.bsa.at(0).stackAttacked)->position;
|
parameters.destination = gs->curB->battleGetStackByID(bat.bsa.at(0).stackAttacked)->position;
|
||||||
parameters.casterSide = !attacker->attackerOwned;
|
parameters.casterSide = !attacker->attackerOwned;
|
||||||
parameters.casterColor = attacker->owner;
|
parameters.casterColor = attacker->owner;
|
||||||
parameters.caster = nullptr;
|
parameters.casterHero = nullptr;
|
||||||
parameters.secHero = nullptr;
|
parameters.secHero = nullptr;
|
||||||
|
|
||||||
parameters.usedSpellPower = power;
|
parameters.usedSpellPower = power;
|
||||||
@ -5297,7 +5298,7 @@ void CGameHandler::runBattle()
|
|||||||
parameters.destination = BattleHex::INVALID;
|
parameters.destination = BattleHex::INVALID;
|
||||||
parameters.casterSide = i;
|
parameters.casterSide = i;
|
||||||
parameters.casterColor = h->tempOwner;
|
parameters.casterColor = h->tempOwner;
|
||||||
parameters.caster = nullptr;
|
parameters.casterHero = nullptr;
|
||||||
parameters.secHero = gs->curB->battleGetFightingHero(1-i);
|
parameters.secHero = gs->curB->battleGetFightingHero(1-i);
|
||||||
|
|
||||||
|
|
||||||
|
@ -142,7 +142,7 @@ public:
|
|||||||
|
|
||||||
void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) override;
|
void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) override;
|
||||||
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) override;
|
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) override;
|
||||||
bool changeStackType(const StackLocation &sl, CCreature *c) override;
|
bool changeStackType(const StackLocation &sl, const CCreature *c) override;
|
||||||
bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) override;
|
bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) override;
|
||||||
bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count) override;
|
bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count) override;
|
||||||
bool eraseStack(const StackLocation &sl, bool forceRemoval = false) override;
|
bool eraseStack(const StackLocation &sl, bool forceRemoval = false) override;
|
||||||
|
@ -297,7 +297,11 @@ bool CGarrisonDialogQuery::blocksPack(const CPack *pack) const
|
|||||||
{
|
{
|
||||||
return !vstd::contains(ourIds, dismiss->heroID);
|
return !vstd::contains(ourIds, dismiss->heroID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(auto upgrade = dynamic_cast<const UpgradeCreature*>(pack))
|
||||||
|
{
|
||||||
|
return !vstd::contains(ourIds, upgrade->id);
|
||||||
|
}
|
||||||
return CDialogQuery::blocksPack(pack);
|
return CDialogQuery::blocksPack(pack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user