mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-26 22:57:00 +02:00
Support for WoG "cast before attack" bonus. Minor fixes.
This commit is contained in:
parent
92006b53f1
commit
79a453f442
@ -5,6 +5,7 @@ RETURN_AFTER_STRIKE Attack and Return Returns after melee attack
|
|||||||
SPELL_RESISTANCE_AURA Aura of Resistance Nearby stacks get %d% resistance
|
SPELL_RESISTANCE_AURA Aura of Resistance Nearby stacks get %d% resistance
|
||||||
TWO_HEX_ATTACK_BREATH Breath Breath Attack (2-hex range)
|
TWO_HEX_ATTACK_BREATH Breath Breath Attack (2-hex range)
|
||||||
SPELL_AFTER_ATTACK Caster - %s %d% chance to cast after attack
|
SPELL_AFTER_ATTACK Caster - %s %d% chance to cast after attack
|
||||||
|
SPELL_BEFORE_ATTACK Caster - %s %d% chance to cast before attack
|
||||||
CATAPULT Catapult Attacks siege walls
|
CATAPULT Catapult Attacks siege walls
|
||||||
JOUSTING Champion Charge +5% damage per hex travelled
|
JOUSTING Champion Charge +5% damage per hex travelled
|
||||||
DOUBLE_DAMAGE_CHANCE Death Blow %d% chance for double damage
|
DOUBLE_DAMAGE_CHANCE Death Blow %d% chance for double damage
|
||||||
|
2
global.h
2
global.h
@ -325,7 +325,7 @@ namespace SpellCasting
|
|||||||
NO_APPROPRIATE_TARGET, STACK_IMMUNE_TO_SPELL, WRONG_SPELL_TARGET
|
NO_APPROPRIATE_TARGET, STACK_IMMUNE_TO_SPELL, WRONG_SPELL_TARGET
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ECastingMode {HERO_CASTING, AFTER_ATTACK_CASTING};
|
enum ECastingMode {HERO_CASTING, AFTER_ATTACK_CASTING}; //also includes cast before attack
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Buildings
|
namespace Buildings
|
||||||
|
@ -1029,6 +1029,11 @@ void CCreatureHandler::loadStackExp(Bonus & b, BonusList & bl, std::string & src
|
|||||||
b.type= Bonus::HATE;
|
b.type= Bonus::HATE;
|
||||||
b.subtype = stringToNumber(mod);
|
b.subtype = stringToNumber(mod);
|
||||||
break;
|
break;
|
||||||
|
case 'p':
|
||||||
|
b.type = Bonus::SPELL_BEFORE_ATTACK;
|
||||||
|
b.subtype = stringToNumber(mod);
|
||||||
|
b.additionalInfo = 3; //always expert?
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
tlog3 << "Not parsed bonus " << buf << mod << "\n";
|
tlog3 << "Not parsed bonus " << buf << mod << "\n";
|
||||||
return;
|
return;
|
||||||
|
@ -620,6 +620,7 @@ std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
|
|||||||
boost::algorithm::replace_first(text, "%s", VLC->creh->creatures[bonus->subtype]->namePl);
|
boost::algorithm::replace_first(text, "%s", VLC->creh->creatures[bonus->subtype]->namePl);
|
||||||
break;
|
break;
|
||||||
case Bonus::SPELL_AFTER_ATTACK:
|
case Bonus::SPELL_AFTER_ATTACK:
|
||||||
|
case Bonus::SPELL_BEFORE_ATTACK:
|
||||||
{
|
{
|
||||||
boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype))));
|
boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype))));
|
||||||
boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype]->name);
|
boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype]->name);
|
||||||
@ -650,6 +651,7 @@ std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
|
|||||||
boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(bonus->val));
|
boost::algorithm::replace_first(text, "%d", boost::lexical_cast<std::string>(bonus->val));
|
||||||
break;
|
break;
|
||||||
case Bonus::SPELL_AFTER_ATTACK:
|
case Bonus::SPELL_AFTER_ATTACK:
|
||||||
|
case Bonus::SPELL_BEFORE_ATTACK:
|
||||||
boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype]->name);
|
boost::algorithm::replace_first(text, "%s", VLC->spellh->spells[bonus->subtype]->name);
|
||||||
break;
|
break;
|
||||||
case Bonus::SPELL_IMMUNITY:
|
case Bonus::SPELL_IMMUNITY:
|
||||||
@ -683,7 +685,8 @@ std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
|
|||||||
case Bonus::SPELL_AFTER_ATTACK:
|
case Bonus::SPELL_AFTER_ATTACK:
|
||||||
fileName = "E_CAST.bmp"; break;
|
fileName = "E_CAST.bmp"; break;
|
||||||
//"E_CAST1.bmp"
|
//"E_CAST1.bmp"
|
||||||
//"E_CAST2.bmp"
|
case Bonus::SPELL_BEFORE_ATTACK:
|
||||||
|
fileName ="E_CAST2.bmp"; break;
|
||||||
//"E_CASTER.bmp"
|
//"E_CASTER.bmp"
|
||||||
case Bonus::JOUSTING:
|
case Bonus::JOUSTING:
|
||||||
fileName = "E_CHAMP.bmp"; break;
|
fileName = "E_CHAMP.bmp"; break;
|
||||||
|
@ -93,6 +93,7 @@ namespace PrimarySkill
|
|||||||
BONUS_NAME(CHANGES_SPELL_COST_FOR_ALLY) /*in mana points (value) , eg. mage*/ \
|
BONUS_NAME(CHANGES_SPELL_COST_FOR_ALLY) /*in mana points (value) , eg. mage*/ \
|
||||||
BONUS_NAME(CHANGES_SPELL_COST_FOR_ENEMY) /*in mana points (value) , eg. pegasus */ \
|
BONUS_NAME(CHANGES_SPELL_COST_FOR_ENEMY) /*in mana points (value) , eg. pegasus */ \
|
||||||
BONUS_NAME(SPELL_AFTER_ATTACK) /* subtype - spell id, value - chance %, additional info % 1000 - level, (additional info)/1000 -> [0 - all attacks, 1 - shot only, 2 - melee only*/ \
|
BONUS_NAME(SPELL_AFTER_ATTACK) /* subtype - spell id, value - chance %, additional info % 1000 - level, (additional info)/1000 -> [0 - all attacks, 1 - shot only, 2 - melee only*/ \
|
||||||
|
BONUS_NAME(SPELL_BEFORE_ATTACK) /* subtype - spell id, value - chance %, additional info % 1000 - level, (additional info)/1000 -> [0 - all attacks, 1 - shot only, 2 - melee only*/ \
|
||||||
BONUS_NAME(SPELL_RESISTANCE_AURA) /*eg. unicorns, value - resistance bonus in % for adjacent creatures*/ \
|
BONUS_NAME(SPELL_RESISTANCE_AURA) /*eg. unicorns, value - resistance bonus in % for adjacent creatures*/ \
|
||||||
BONUS_NAME(LEVEL_SPELL_IMMUNITY) /*creature is immune to all spell with level below or equal to value of this bonus*/ \
|
BONUS_NAME(LEVEL_SPELL_IMMUNITY) /*creature is immune to all spell with level below or equal to value of this bonus*/ \
|
||||||
BONUS_NAME(TWO_HEX_ATTACK_BREATH) /*eg. dragons*/ \
|
BONUS_NAME(TWO_HEX_ATTACK_BREATH) /*eg. dragons*/ \
|
||||||
|
@ -3046,6 +3046,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
|||||||
//attack
|
//attack
|
||||||
BattleAttack bat;
|
BattleAttack bat;
|
||||||
prepareAttack(bat, curStack, stackAtEnd, distance, ba.additionalInfo);
|
prepareAttack(bat, curStack, stackAtEnd, distance, ba.additionalInfo);
|
||||||
|
handleAttackBeforeCasting(bat); //only before first attack
|
||||||
sendAndApply(&bat);
|
sendAndApply(&bat);
|
||||||
handleAfterAttackCasting(bat);
|
handleAfterAttackCasting(bat);
|
||||||
|
|
||||||
@ -3096,6 +3097,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
|||||||
BattleAttack bat;
|
BattleAttack bat;
|
||||||
bat.flags |= BattleAttack::SHOT;
|
bat.flags |= BattleAttack::SHOT;
|
||||||
prepareAttack(bat, curStack, destStack, 0, ba.destinationTile);
|
prepareAttack(bat, curStack, destStack, 0, ba.destinationTile);
|
||||||
|
handleAttackBeforeCasting(bat);
|
||||||
sendAndApply(&bat);
|
sendAndApply(&bat);
|
||||||
handleAfterAttackCasting(bat);
|
handleAfterAttackCasting(bat);
|
||||||
|
|
||||||
@ -3107,7 +3109,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
|||||||
prepareAttack(bat2, curStack, destStack, 0, ba.destinationTile);
|
prepareAttack(bat2, curStack, destStack, 0, ba.destinationTile);
|
||||||
sendAndApply(&bat2);
|
sendAndApply(&bat2);
|
||||||
}
|
}
|
||||||
|
//TODO: allow more than one additional attack
|
||||||
if(curStack->valOfBonuses(Bonus::ADDITIONAL_ATTACK) > 0 //if unit shots twice let's make another shot
|
if(curStack->valOfBonuses(Bonus::ADDITIONAL_ATTACK) > 0 //if unit shots twice let's make another shot
|
||||||
&& curStack->alive()
|
&& curStack->alive()
|
||||||
&& destStack->alive()
|
&& destStack->alive()
|
||||||
@ -3473,7 +3475,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, THex destinati
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
BattleStackAttacked bsa;
|
BattleStackAttacked bsa;
|
||||||
if (destination > -1 && (*it)->coversPos(destination)) //display effect only upon primary target of area spell
|
if (destination > -1 && (*it)->coversPos(destination) || spell->range[spellLvl] == "X") //display effect only upon primary target of area spell
|
||||||
{
|
{
|
||||||
bsa.flags |= BattleStackAttacked::EFFECT;
|
bsa.flags |= BattleStackAttacked::EFFECT;
|
||||||
bsa.effect = spell->mainEffectAnim;
|
bsa.effect = spell->mainEffectAnim;
|
||||||
@ -4317,13 +4319,12 @@ bool CGameHandler::dig( const CGHeroInstance *h )
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
|
void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType attackMode, const CStack * attacker)
|
||||||
{
|
{
|
||||||
const CStack * attacker = gs->curB->getStack(bat.stackAttacking);
|
if(attacker->hasBonusOfType(attackMode))
|
||||||
if( attacker->hasBonusOfType(Bonus::SPELL_AFTER_ATTACK) )
|
|
||||||
{
|
{
|
||||||
std::set<ui32> spellsToCast;
|
std::set<ui32> spellsToCast;
|
||||||
boost::shared_ptr<BonusList> spells = attacker->getBonuses(Selector::type(Bonus::SPELL_AFTER_ATTACK));
|
boost::shared_ptr<BonusList> spells = attacker->getBonuses(Selector::type(attackMode));
|
||||||
BOOST_FOREACH(const Bonus *sf, *spells)
|
BOOST_FOREACH(const Bonus *sf, *spells)
|
||||||
{
|
{
|
||||||
spellsToCast.insert (sf->subtype);
|
spellsToCast.insert (sf->subtype);
|
||||||
@ -4344,7 +4345,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
|
|||||||
if(oneOfAttacked == NULL) //all attacked creatures have been killed
|
if(oneOfAttacked == NULL) //all attacked creatures have been killed
|
||||||
return;
|
return;
|
||||||
int spellLevel = 0;
|
int spellLevel = 0;
|
||||||
boost::shared_ptr<BonusList> spellsByType = attacker->getBonuses(Selector::typeSubtype(Bonus::SPELL_AFTER_ATTACK, spellID));
|
boost::shared_ptr<BonusList> spellsByType = attacker->getBonuses(Selector::typeSubtype(attackMode, spellID));
|
||||||
BOOST_FOREACH(const Bonus *sf, *spellsByType)
|
BOOST_FOREACH(const Bonus *sf, *spellsByType)
|
||||||
{
|
{
|
||||||
amax(spellLevel, sf->additionalInfo % 1000); //pick highest level
|
amax(spellLevel, sf->additionalInfo % 1000); //pick highest level
|
||||||
@ -4352,24 +4353,36 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
|
|||||||
if (meleeRanged == 0 || (meleeRanged == 1 && bat.shot()) || (meleeRanged == 2 && !bat.shot()))
|
if (meleeRanged == 0 || (meleeRanged == 1 && bat.shot()) || (meleeRanged == 2 && !bat.shot()))
|
||||||
castMe = true;
|
castMe = true;
|
||||||
}
|
}
|
||||||
int chance = attacker->valOfBonuses((Selector::typeSubtype(Bonus::SPELL_AFTER_ATTACK, spellID)));
|
int chance = attacker->valOfBonuses((Selector::typeSubtype(attackMode, spellID)));
|
||||||
amin (chance, 100);
|
amin (chance, 100);
|
||||||
int destination = oneOfAttacked->position;
|
int destination = oneOfAttacked->position;
|
||||||
|
|
||||||
const CSpell * spell = VLC->spellh->spells[spellID];
|
const CSpell * spell = VLC->spellh->spells[spellID];
|
||||||
if(gs->curB->battleCanCastThisSpellHere(attacker->owner, spell, SpellCasting::AFTER_ATTACK_CASTING, oneOfAttacked->position)
|
if(gs->curB->battleCanCastThisSpellHere(attacker->owner, spell, SpellCasting::AFTER_ATTACK_CASTING, oneOfAttacked->position) != SpellCasting::OK)
|
||||||
!= SpellCasting::OK)
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
//check if spell should be casted (probability handling)
|
//check if spell should be casted (probability handling)
|
||||||
if(rand()%100 >= chance)
|
if(rand()%100 >= chance)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
//casting
|
//casting //TODO: check if spell can be blocked or target is immune
|
||||||
if (castMe) //stacks use 0 spell power. If needed, default = 3 or custom value is used
|
if (castMe) //stacks use 0 spell power. If needed, default = 3 or custom value is used
|
||||||
handleSpellCasting(spellID, spellLevel, destination, !attacker->attackerOwned, attacker->owner, NULL, NULL, 0, SpellCasting::AFTER_ATTACK_CASTING, attacker);
|
handleSpellCasting(spellID, spellLevel, destination, !attacker->attackerOwned, attacker->owner, NULL, NULL, 0, SpellCasting::AFTER_ATTACK_CASTING, attacker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGameHandler::handleAttackBeforeCasting (const BattleAttack & bat)
|
||||||
|
{
|
||||||
|
const CStack * attacker = gs->curB->getStack(bat.stackAttacking);
|
||||||
|
attackCasting(bat, Bonus::SPELL_BEFORE_ATTACK, attacker); //no detah stare / acid bretah needed?
|
||||||
|
}
|
||||||
|
|
||||||
|
void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
|
||||||
|
{
|
||||||
|
const CStack * attacker = gs->curB->getStack(bat.stackAttacking);
|
||||||
|
attackCasting(bat, Bonus::SPELL_AFTER_ATTACK, attacker);
|
||||||
|
|
||||||
if (attacker->hasBonusOfType(Bonus::DEATH_STARE)) // spell id 79
|
if (attacker->hasBonusOfType(Bonus::DEATH_STARE)) // spell id 79
|
||||||
{
|
{
|
||||||
int staredCreatures = 0;
|
int staredCreatures = 0;
|
||||||
|
@ -249,7 +249,9 @@ public:
|
|||||||
|
|
||||||
void run(bool resume);
|
void run(bool resume);
|
||||||
void newTurn();
|
void newTurn();
|
||||||
|
void handleAttackBeforeCasting (const BattleAttack & bat);
|
||||||
void handleAfterAttackCasting (const BattleAttack & bat);
|
void handleAfterAttackCasting (const BattleAttack & bat);
|
||||||
|
void attackCasting(const BattleAttack & bat, Bonus::BonusType attackMode, const CStack * attacker);
|
||||||
bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, int slot);
|
bool sacrificeArtifact(const IMarket * m, const CGHeroInstance * hero, int slot);
|
||||||
friend class CVCMIServer;
|
friend class CVCMIServer;
|
||||||
friend class CScriptCallback;
|
friend class CScriptCallback;
|
||||||
|
Loading…
Reference in New Issue
Block a user