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

* redesign of stack's abilities and bonuses - not 100% working, but close ;]

This commit is contained in:
mateuszb 2009-05-08 16:55:04 +00:00
parent 7964f44850
commit ae2909667d
9 changed files with 182 additions and 52 deletions

View File

@ -550,7 +550,7 @@ void CBattleInterface::show(SDL_Surface * to)
&& (LOCPLINT->curAction->destinationTile != curStack.position) //nor if it's on destination tile for current action && (LOCPLINT->curAction->destinationTile != curStack.position) //nor if it's on destination tile for current action
) )
) )
&& !vstd::contains(curStack.abilities,SIEGE_WEAPON) //and not a war machine... && !curStack.hasFeatureOfType(StackFeature::SIEGE_WEAPON) //and not a war machine...
) )
{ {
int xAdd = curStack.attackerOwned ? 220 : 202; int xAdd = curStack.attackerOwned ? 220 : 202;

View File

@ -533,7 +533,7 @@ bool CCallback::battleCanShoot(int ID, int dest)
return false; return false;
} }
if(vstd::contains(our->abilities,SHOOTER)//it's shooter if(our->hasFeatureOfType(StackFeature::SHOOTER)//it's shooter
&& our->owner != dst->owner && our->owner != dst->owner
&& dst->alive() && dst->alive()
&& !gs->curB->isStackBlocked(ID) && !gs->curB->isStackBlocked(ID)

View File

@ -443,9 +443,44 @@ std::pair< std::vector<int>, int > BattleInfo::getPath(int start, int dest, bool
return std::make_pair(path, dist[dest]); return std::make_pair(path, dist[dest]);
} }
int CStack::valOfFeatures(StackFeature::ECombatFeatures type, int subtype) const
{
int ret = 0;
if(subtype == -1)
{
for(std::vector<StackFeature>::const_iterator i=features.begin(); i != features.end(); i++)
if(i->type == type)
ret += i->value;
}
else
{
for(std::vector<StackFeature>::const_iterator i=features.begin(); i != features.end(); i++)
if(i->type == type && i->subtype == subtype)
ret += i->value;
}
return ret;
}
bool CStack::hasFeatureOfType(StackFeature::ECombatFeatures type, int subtype) const
{
if(subtype == -1) //any subtype
{
for(std::vector<StackFeature>::const_iterator i=features.begin(); i != features.end(); i++)
if(i->type == type)
return true;
}
else //given subtype
{
for(std::vector<StackFeature>::const_iterator i=features.begin(); i != features.end(); i++)
if(i->type == type && i->subtype == subtype)
return true;
}
return false;
}
CStack::CStack(CCreature * C, int A, int O, int I, bool AO, int S) CStack::CStack(CCreature * C, int A, int O, int I, bool AO, int S)
:ID(I), creature(C), amount(A), baseAmount(A), firstHPleft(C->hitPoints), owner(O), slot(S), attackerOwned(AO), position(-1), :ID(I), creature(C), amount(A), baseAmount(A), firstHPleft(C->hitPoints), owner(O), slot(S), attackerOwned(AO), position(-1),
counterAttacks(1), shots(C->shots), state(), effects(), speed(creature->speed), abilities(C->abilities), attack(C->attack), defense(C->defence) counterAttacks(1), shots(C->shots), state(), effects(), speed(creature->speed), features(C->abilities), attack(C->attack), defense(C->defence)
{ {
state.insert(ALIVE); state.insert(ALIVE);
} }
@ -577,10 +612,10 @@ si32 CStack::Defense() const
bool CStack::willMove() bool CStack::willMove()
{ {
return !vstd::contains(state,DEFENDING) return !vstd::contains(state, DEFENDING)
&& !vstd::contains(state,MOVED) && !vstd::contains(state, MOVED)
&& alive() && alive()
&& !vstd::contains(abilities,NOT_ACTIVE); //eg. Ammo Cart && ! hasFeatureOfType(StackFeature::NOT_ACTIVE); //eg. Ammo Cart
} }
CGHeroInstance* CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, int notThatOne) CGHeroInstance* CGameState::HeroesPool::pickHeroFor(bool native, int player, const CTown *town, int notThatOne)
@ -2039,12 +2074,12 @@ std::vector<CStack> BattleInfo::getStackQueue()
int id = -1, speed = -1; int id = -1, speed = -1;
for(unsigned int i=0; i<stacks.size(); ++i) //find not waited stacks only for(unsigned int i=0; i<stacks.size(); ++i) //find not waited stacks only
{ {
if((moved == 1 ||!vstd::contains(stacks[i]->state,DEFENDING)) if((moved == 1 ||!vstd::contains(stacks[i]->state, DEFENDING))
&& stacks[i]->alive() && stacks[i]->alive()
&& (moved == 1 || !vstd::contains(stacks[i]->state,MOVED)) && (moved == 1 || !vstd::contains(stacks[i]->state, MOVED))
&& !vstd::contains(stacks[i]->state,WAITING) && !vstd::contains(stacks[i]->state,WAITING)
&& taken[i]==0 && taken[i]==0
&& !vstd::contains(stacks[i]->abilities,NOT_ACTIVE)) //eg. Ammo Cart && !stacks[i]->hasFeatureOfType(StackFeature::NOT_ACTIVE)) //eg. Ammo Cart
{ {
if(speed == -1 || stacks[i]->Speed() > speed) if(speed == -1 || stacks[i]->Speed() > speed)
{ {
@ -2063,12 +2098,12 @@ std::vector<CStack> BattleInfo::getStackQueue()
int id = -1, speed = 10000; //infinite speed int id = -1, speed = 10000; //infinite speed
for(unsigned int i=0; i<stacks.size(); ++i) //find waited stacks only for(unsigned int i=0; i<stacks.size(); ++i) //find waited stacks only
{ {
if((moved == 1 ||!vstd::contains(stacks[i]->state,DEFENDING)) if((moved == 1 ||!vstd::contains(stacks[i]->state, DEFENDING))
&& stacks[i]->alive() && stacks[i]->alive()
&& (moved == 1 || !vstd::contains(stacks[i]->state,MOVED)) && (moved == 1 || !vstd::contains(stacks[i]->state, MOVED))
&& vstd::contains(stacks[i]->state,WAITING) && vstd::contains(stacks[i]->state,WAITING)
&& taken[i]==0 && taken[i]==0
&& !vstd::contains(stacks[i]->abilities,NOT_ACTIVE)) //eg. Ammo Cart && !stacks[i]->hasFeatureOfType(StackFeature::NOT_ACTIVE)) //eg. Ammo Cart
{ {
if(stacks[i]->Speed() < speed) //slowest one if(stacks[i]->Speed() < speed) //slowest one
{ {

View File

@ -158,7 +158,7 @@ public:
si32 defense; //defense of stack with hero bonus si32 defense; //defense of stack with hero bonus
si8 morale, luck; //base stack luck/morale si8 morale, luck; //base stack luck/morale
std::set<EAbilities> abilities; std::vector<StackFeature> features;
std::set<ECombatInfo> state; std::set<ECombatInfo> state;
struct StackEffect struct StackEffect
{ {
@ -172,8 +172,11 @@ public:
}; };
std::vector<StackEffect> effects; std::vector<StackEffect> effects;
int valOfFeatures(StackFeature::ECombatFeatures type, int subtype = -1) const;//subtype -> subtype of bonus, if -1 then any
bool hasFeatureOfType(StackFeature::ECombatFeatures type, int subtype = -1) const; //determines if stack has a bonus of given type (and optionally subtype)
CStack(CCreature * C, int A, int O, int I, bool AO, int S); //c-tor CStack(CCreature * C, int A, int O, int I, bool AO, int S); //c-tor
CStack() : ID(-1), creature(NULL), amount(-1), baseAmount(-1), firstHPleft(-1), owner(255), slot(255), attackerOwned(true), position(-1), counterAttacks(1), abilities(), state(), effects() {} //c-tor CStack() : ID(-1), creature(NULL), amount(-1), baseAmount(-1), firstHPleft(-1), owner(255), slot(255), attackerOwned(true), position(-1), counterAttacks(1) {} //c-tor
const StackEffect * getEffect(ui16 id) const; //effect id (SP) const StackEffect * getEffect(ui16 id) const; //effect id (SP)
ui8 howManyEffectsSet(ui16 id) const; //returns amount of effects with given id set for this stack ui8 howManyEffectsSet(ui16 id) const; //returns amount of effects with given id set for this stack
bool willMove(); //if stack has remaining move this turn bool willMove(); //if stack has remaining move this turn
@ -191,7 +194,7 @@ public:
ui32 id; ui32 id;
h & id; h & id;
creature = &VLC->creh->creatures[id]; creature = &VLC->creh->creatures[id];
abilities = creature->abilities; features = creature->abilities;
} }
template <typename Handler> void serialize(Handler &h, const int version) template <typename Handler> void serialize(Handler &h, const int version)
{ {

101
global.h
View File

@ -47,12 +47,101 @@ enum Ecolor {RED, BLUE, TAN, GREEN, ORANGE, PURPLE, TEAL, PINK}; //player's colo
enum EvictoryConditions {artifact, gatherTroop, gatherResource, buildCity, buildGrail, beatHero, enum EvictoryConditions {artifact, gatherTroop, gatherResource, buildCity, buildGrail, beatHero,
captureCity, beatMonster, takeDwellings, takeMines, transportItem, winStandard=255}; captureCity, beatMonster, takeDwellings, takeMines, transportItem, winStandard=255};
enum ElossCon {lossCastle, lossHero, timeExpires, lossStandard=255}; enum ElossCon {lossCastle, lossHero, timeExpires, lossStandard=255};
enum EAbilities {DOUBLE_WIDE, FLYING, SHOOTER, TWO_HEX_ATTACK, SIEGE_ABILITY, SIEGE_WEAPON, //enum EAbilities {DOUBLE_WIDE, FLYING, SHOOTER, TWO_HEX_ATTACK, SIEGE_ABILITY, SIEGE_WEAPON,
KING1, KING2, KING3, MIND_IMMUNITY, NO_OBSTACLE_PENALTY, NO_CLOSE_COMBAT_PENALTY, // KING1, KING2, KING3, MIND_IMMUNITY, NO_OBSTACLE_PENALTY, NO_CLOSE_COMBAT_PENALTY,
JOUSTING, FIRE_IMMUNITY, TWICE_ATTACK, NO_ENEMY_RETALIATION, NO_MORAL_PENALTY, // JOUSTING, FIRE_IMMUNITY, TWICE_ATTACK, NO_ENEMY_RETALIATION, NO_MORAL_PENALTY,
UNDEAD, MULTI_HEAD_ATTACK, EXTENDED_RADIOUS_SHOOTER, GHOST, RAISES_MORALE, // UNDEAD, MULTI_HEAD_ATTACK, EXTENDED_RADIOUS_SHOOTER, GHOST, RAISES_MORALE,
LOWERS_MORALE, DRAGON, STRIKE_AND_RETURN, FEARLESS, REBIRTH, NOT_ACTIVE}; //some flags are used only for battles // LOWERS_MORALE, DRAGON, STRIKE_AND_RETURN, FEARLESS, REBIRTH, NOT_ACTIVE}; //some flags are used only for battles
enum ECombatInfo{ALIVE = NOT_ACTIVE+1, SUMMONED, CLONED, HAD_MORALE, WAITING, MOVED, DEFENDING}; enum ECombatInfo{ALIVE = 180, SUMMONED, CLONED, HAD_MORALE, WAITING, MOVED, DEFENDING};
struct StackFeature
{
//general list of stack abilities and effects
enum ECombatFeatures
{
NO_TYPE,
DOUBLE_WIDE, FLYING, SHOOTER, CHARGE_IMMUNITY, ADDITIONAL_ATTACK, UNLIMITED_RETAILATIONS,
NO_MELEE_PENALTY, JOUSTING /*for champions*/,
RAISING_MORALE, HATE /*eg. angels hate devils*/,
KING1,
KING2, KING3, MAGIC_RESISTANCE /*in %*/,
CHANGES_SPELL_COST /*in mana points, eg. pegasus or mage*/,
SPELL_AFTER_ATTACK /* subtype - spell id, value - spell level, aditional info - chance in %; eg. dendroids*/,
SPELL_RESISTANCE_AURA /*eg. unicorns, value - resistance bonus in % for adjacent creatures*/,
LEVEL_SPELL_IMMUNITY /*creature is immune to all spell with level below value of this bonus*/,
TWO_HEX_ATTACK_BREATH /*eg. dragons*/,
SPELL_DAMAGE_REDUCTION /*eg. golems; value - reduction in %, subtype - spell school; -1 - all, 0 - air, 1 - fire, 2 - water, 3 - earth*/,
NO_WALL_PENALTY, NON_LIVING /*eg. gargoyle*/,
RANDOM_SPELLCASTER, BLOCKS_RETAILATION /*eg. naga*/,
SPELL_IMMUNITY /*value - spell id*/,
MANA_CHANNELING /*in %, eg. familiar*/,
SPELL_LIKE_ATTACK /*value - spell id; range is taken from spell, but damage from creature; eg. magog*/,
THREE_HEADED_ATTACK /*eg. cerberus*/,
DEAMON_SUMMONING /*pit lord*/,
FIRE_IMMUNITY, FIRE_SHIELD, ENEMY_MORALE_DECREASING, ENEMY_LUCK_DECREASING, UNDEAD, REGENERATION, MANA_DRAIN, LIFE_DRAIN,
DOUBLE_DAMAGE_CHANCE /*in %, eg. dread knight*/,
RETURN_AFTER_STRIKE, SELF_MORALE /*eg. minotaur*/,
SPELLCASTER /*value - spell id*/, CATAPULT,
ENEMY_DEFENCE_REDUCTION /*in %, eg. behemots*/,
GENERAL_DAMAGE_REDUCTION /*eg. while stoned or blinded - in %, subtype: -1 - any damage, 0 - melee damage, 1 - ranged damage*/,
ATTACKS_ALL_ADAJCENT /*eg. hydra*/,
MORE_DAMEGE_FROM_SPELL /*value - damage increase in %, subtype - spell id*/,
CASTS_SPELL_WHEN_KILLED /*similar to spell after attack*/,
FEAR, FEARLESS, NO_DISTANCE_PENALTY, NO_OBSTACLES_PENALTY,
SELF_LUCK /*halfling*/,
ATTACK_BONUS, DEFENCE_BONUS, SPEED_BONUS, HP_BONUS, ENCHANTER, HEALER, SIEGE_WEAPON, LUCK_BONUS, MORALE_BONUS, HYPNOTIZED, ADDITIONAL_RETAILATION,
MAGIC_MIRROR /* value - chance of redirecting in %*/,
SUMMONED, ALWAYS_MINUMUM_DAMAGE /*unit does its minimum damage from range; -1 - any attack, 0 - melee, 1 - ranged*/,
ALWAYS_MAXIMUM_DAMAGE /*eg. bless effect, -1 - any attack, 0 - melee, 1 - ranged*/,
ATTACKS_NEAREST_CREATURE /*while in berserk*/, IN_FRENZY,
SLAYER /*value - level*/,
FORGETFULL /*forgetfullnes spell effect*/,
CLONED, NOT_ACTIVE
};
enum EDuration
{
WHOLE_BATTLE,
N_TURNS,
UNITL_BEING_ATTACKED,/*removed after attack and counterattacks are performed*/
UNTIL_ATTACK /*removed after attack and counterattacks are performed*/
};
ECombatFeatures type;
EDuration duration;
ui16 turnsRemain; //if duration is N_TURNS it describes how long the effect will last
si16 subtype; //subtype of bonus/feature
si32 value;
si32 additionalInfo;
inline bool operator == (const ECombatFeatures & cf) const
{
return type == cf;
}
StackFeature() : type(NO_TYPE)
{}
StackFeature(const ECombatFeatures & cf) : type(cf), duration(WHOLE_BATTLE)
{}
template <typename Handler> void serialize(Handler &h, const int version)
{
h & type & duration & turnsRemain & subtype & value & additionalInfo;
}
};
//generates StackFeature from given data
inline StackFeature makeFeature(StackFeature::ECombatFeatures type, StackFeature::EDuration duration, si16 subtype, si32 value, ui16 turnsRemain = 0, si32 additionalInfo = 0)
{
StackFeature sf;
sf.type = type;
sf.duration = duration;
sf.turnsRemain = turnsRemain;
sf.subtype = subtype;
sf.value = value;
sf.additionalInfo = additionalInfo;
return sf;
}
class CGameInfo; class CGameInfo;
extern CGameInfo* CGI; extern CGameInfo* CGI;

View File

@ -52,21 +52,24 @@ int CCreature::getQuantityID(const int & quantity)
bool CCreature::isDoubleWide() const bool CCreature::isDoubleWide() const
{ {
return vstd::contains(abilities,DOUBLE_WIDE); return vstd::contains(abilities, StackFeature::DOUBLE_WIDE);
} }
bool CCreature::isFlying() const bool CCreature::isFlying() const
{ {
return vstd::contains(abilities,FLYING); return vstd::contains(abilities, StackFeature::FLYING);
} }
bool CCreature::isShooting() const bool CCreature::isShooting() const
{ {
return vstd::contains(abilities,SHOOTER); return vstd::contains(abilities, StackFeature::SHOOTER);
} }
bool CCreature::isUndead() const bool CCreature::isUndead() const
{ {
return vstd::contains(abilities,UNDEAD); return vstd::contains(abilities, StackFeature::UNDEAD);
} }
si32 CCreature::maxAmount(const std::vector<si32> &res) const //how many creatures can be bought si32 CCreature::maxAmount(const std::vector<si32> &res) const //how many creatures can be bought
{ {
int ret = 2147483645; int ret = 2147483645;
@ -335,19 +338,19 @@ void CCreatureHandler::loadCreatures()
ncre.abilityRefs = buf.substr(befi, i-befi); ncre.abilityRefs = buf.substr(befi, i-befi);
i+=2; i+=2;
if(boost::algorithm::find_first(ncre.abilityRefs, "DOUBLE_WIDE")) if(boost::algorithm::find_first(ncre.abilityRefs, "DOUBLE_WIDE"))
ncre.abilities.insert(DOUBLE_WIDE); ncre.abilities.push_back(StackFeature::DOUBLE_WIDE);
if(boost::algorithm::find_first(ncre.abilityRefs, "FLYING_ARMY")) if(boost::algorithm::find_first(ncre.abilityRefs, "FLYING_ARMY"))
ncre.abilities.insert(FLYING); ncre.abilities.push_back(StackFeature::FLYING);
if(boost::algorithm::find_first(ncre.abilityRefs, "SHOOTING_ARMY")) if(boost::algorithm::find_first(ncre.abilityRefs, "SHOOTING_ARMY"))
ncre.abilities.insert(SHOOTER); ncre.abilities.push_back(StackFeature::SHOOTER);
if(boost::algorithm::find_first(ncre.abilityRefs, "SIEGE_WEAPON")) if(boost::algorithm::find_first(ncre.abilityRefs, "SIEGE_WEAPON"))
ncre.abilities.insert(SIEGE_WEAPON); ncre.abilities.push_back(StackFeature::SIEGE_WEAPON);
if(boost::algorithm::find_first(ncre.abilityRefs, "const_two_attacks")) if(boost::algorithm::find_first(ncre.abilityRefs, "const_two_attacks"))
ncre.abilities.insert(TWICE_ATTACK); ncre.abilities.push_back(makeFeature(StackFeature::ADDITIONAL_ATTACK, StackFeature::WHOLE_BATTLE, 0, 1));
if(boost::algorithm::find_first(ncre.abilityRefs, "const_free_attack")) if(boost::algorithm::find_first(ncre.abilityRefs, "const_free_attack"))
ncre.abilities.insert(NO_ENEMY_RETALIATION); ncre.abilities.push_back(StackFeature::BLOCKS_RETAILATION);
if(boost::algorithm::find_first(ncre.abilityRefs, "IS_UNDEAD")) if(boost::algorithm::find_first(ncre.abilityRefs, "IS_UNDEAD"))
ncre.abilities.insert(UNDEAD); ncre.abilities.push_back(StackFeature::UNDEAD);
if(ncre.nameSing!=std::string("") && ncre.namePl!=std::string("")) if(ncre.nameSing!=std::string("") && ncre.namePl!=std::string(""))
{ {
ncre.idNumber = creatures.size(); ncre.idNumber = creatures.size();
@ -482,22 +485,22 @@ void CCreatureHandler::loadCreatures()
inp2.close(); inp2.close();
//TODO: create a tidy configuration file to control fixing unit abilities //TODO: create a tidy configuration file to control fixing unit abilities
creatures[115].abilities.insert(DOUBLE_WIDE);//water elemental should be treated as double-wide creatures[115].abilities.push_back(StackFeature::DOUBLE_WIDE);//water elemental should be treated as double-wide
creatures[123].abilities.insert(DOUBLE_WIDE);//ice elemental should be treated as double-wide creatures[123].abilities.push_back(StackFeature::DOUBLE_WIDE);//ice elemental should be treated as double-wide
creatures[140].abilities.insert(DOUBLE_WIDE);//boar should be treated as double-wide creatures[140].abilities.push_back(StackFeature::DOUBLE_WIDE);//boar should be treated as double-wide
creatures[142].abilities.insert(DOUBLE_WIDE);//nomads should be treated as double-wide creatures[142].abilities.push_back(StackFeature::DOUBLE_WIDE);//nomads should be treated as double-wide
creatures[46].abilities -= FLYING; //hell hound creatures[46].abilities -= StackFeature::FLYING; //hell hound
creatures[47].abilities -= FLYING; //cerberus creatures[47].abilities -= StackFeature::FLYING; //cerberus
creatures[52].abilities += FLYING; //Efreeti creatures[52].abilities += StackFeature::FLYING; //Efreeti
creatures[53].abilities += FLYING; //Efreet Sultan creatures[53].abilities += StackFeature::FLYING; //Efreet Sultan
creatures[47].abilities += MULTI_HEAD_ATTACK; //cerberus creatures[47].abilities += StackFeature::THREE_HEADED_ATTACK; //cerberus
creatures[87].abilities += TWICE_ATTACK; //wolf raider creatures[87].abilities += makeFeature(StackFeature::ADDITIONAL_ATTACK, StackFeature::WHOLE_BATTLE, 0, 1); //wolf raider
creatures[147].abilities += NOT_ACTIVE; //First Aid Tent //TODO: remove when support is added creatures[147].abilities += StackFeature::NOT_ACTIVE; //First Aid Tent //TODO: remove when support is added
creatures[148].abilities += NOT_ACTIVE; //Ammo Cart creatures[148].abilities += StackFeature::NOT_ACTIVE; //Ammo Cart
} }
void CCreatureHandler::loadAnimationInfo() void CCreatureHandler::loadAnimationInfo()

View File

@ -34,7 +34,7 @@ public:
std::string abilityRefs; //references to abilities, in textformat std::string abilityRefs; //references to abilities, in textformat
std::string animDefName; std::string animDefName;
ui32 idNumber; ui32 idNumber;
std::set<EAbilities> abilities; std::vector<StackFeature> abilities;
si8 faction; //-1 = neutral si8 faction; //-1 = neutral
///animation info ///animation info

View File

@ -879,7 +879,7 @@ bool CGHeroInstance::hasBonusOfType(HeroBonus::BonusType type, int subtype /*= -
if(i->type == type && i->subtype == subtype) if(i->type == type && i->subtype == subtype)
return true; return true;
} }
throw "CGHeroInstance::hasBonusOfType - we shouln't be here!"; return false;
} }
int CGTownInstance::getSightRadious() const //returns sight distance int CGTownInstance::getSightRadious() const //returns sight distance

View File

@ -1053,7 +1053,7 @@ void CGameHandler::checkForBattleEnd( std::vector<CStack*> &stacks )
hasStack[0] = hasStack[1] = false; hasStack[0] = hasStack[1] = false;
for(int b = 0; b<stacks.size(); ++b) for(int b = 0; b<stacks.size(); ++b)
{ {
if(stacks[b]->alive() && !vstd::contains(stacks[b]->abilities,SIEGE_WEAPON)) if(stacks[b]->alive() && !stacks[b]->hasFeatureOfType(StackFeature::SIEGE_WEAPON))
{ {
hasStack[1-stacks[b]->attackerOwned] = true; hasStack[1-stacks[b]->attackerOwned] = true;
} }
@ -2093,10 +2093,10 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
sendAndApply(&bat); sendAndApply(&bat);
//counterattack //counterattack
if(!vstd::contains(curStack->abilities,NO_ENEMY_RETALIATION) if(!curStack->hasFeatureOfType(StackFeature::BLOCKS_RETAILATION)
&& stackAtEnd->alive() && stackAtEnd->alive()
&& stackAtEnd->counterAttacks && stackAtEnd->counterAttacks
&& !vstd::contains(stackAtEnd->abilities, SIEGE_WEAPON)) //TODO: support for multiple retaliatons per turn && !stackAtEnd->hasFeatureOfType(StackFeature::SIEGE_WEAPON)) //TODO: support for multiple retaliatons per turn
{ {
prepareAttack(bat,stackAtEnd,curStack); prepareAttack(bat,stackAtEnd,curStack);
bat.flags |= 2; bat.flags |= 2;
@ -2104,7 +2104,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
} }
//second attack //second attack
if(vstd::contains(curStack->abilities,TWICE_ATTACK) if(curStack->valOfFeatures(StackFeature::ADDITIONAL_ATTACK) > 0
&& curStack->alive() && curStack->alive()
&& stackAtEnd->alive() ) && stackAtEnd->alive() )
{ {
@ -2123,7 +2123,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|| !destStack //there is a stack at destination tile || !destStack //there is a stack at destination tile
|| !curStack->shots //stack has shots || !curStack->shots //stack has shots
|| gs->curB->isStackBlocked(curStack->ID) //we are not blocked || gs->curB->isStackBlocked(curStack->ID) //we are not blocked
|| !vstd::contains(curStack->abilities,SHOOTER) //our stack is shooting unit || !curStack->hasFeatureOfType(StackFeature::SHOOTER) //our stack is shooting unit
) )
break; break;
for(int g=0; g<curStack->effects.size(); ++g) for(int g=0; g<curStack->effects.size(); ++g)
@ -2139,7 +2139,7 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
bat.flags |= 1; bat.flags |= 1;
sendAndApply(&bat); sendAndApply(&bat);
if(vstd::contains(curStack->abilities,TWICE_ATTACK) //if unit shots twice let's make another shot if(curStack->valOfFeatures(StackFeature::ADDITIONAL_ATTACK) > 0 //if unit shots twice let's make another shot
&& curStack->alive() && curStack->alive()
&& destStack->alive() && destStack->alive()
&& curStack->shots && curStack->shots