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

- Drain Life now has % effect depending on bonus value

- Stack can use more than 2 attacks. Additional attacks can now be seperated as "ONLY_MELEE_FIGHT and "ONLY_DISTANCE_FIGHT".
This commit is contained in:
DjWarmonger 2013-02-08 09:22:10 +00:00
parent 043c7c52a3
commit 140786a04b
8 changed files with 63 additions and 49 deletions

View File

@ -15,7 +15,7 @@
{ "id": "DEFENSIVE_STANCE", "name": "Defense Bonus", "description": "+%d Defense when defending" }, { "id": "DEFENSIVE_STANCE", "name": "Defense Bonus", "description": "+%d Defense when defending" },
{ "id": "ADDITIONAL_ATTACK", "name": "Double Strike", "description": "Attacks twice" }, { "id": "ADDITIONAL_ATTACK", "name": "Double Strike", "description": "Attacks twice" },
{ "id": "DRAGON_NATURE", "name": "Dragon", "description": "Creature has a Dragon Nature" }, { "id": "DRAGON_NATURE", "name": "Dragon", "description": "Creature has a Dragon Nature" },
{ "id": "LIFE_DRAIN", "name": "Drain life", "description": "Drains life equal to damage dealt" }, { "id": "LIFE_DRAIN", "name": "Drain life (%d%)", "description": "Drains life equal to damage dealt" },
{ "id": "FEAR", "name": "Fear", "description": "Causes Fear on an enemy stack" }, { "id": "FEAR", "name": "Fear", "description": "Causes Fear on an enemy stack" },
{ "id": "FEARLESS", "name": "Fearless", "description": "Immune to Fear ability" }, { "id": "FEARLESS", "name": "Fearless", "description": "Immune to Fear ability" },
{ "id": "FLYING", "name": "Fly", "description": "Can Fly (ignores obstacles)" }, { "id": "FLYING", "name": "Fly", "description": "Can Fly (ignores obstacles)" },

View File

@ -152,6 +152,7 @@
"id": 7, "id": 7,
"level": 4, "level": 4,
"faction": "castle", "faction": "castle",
"abilities": [ [ "ADDITIONAL_ATTACK", 1, 0, 0 ] ],
"graphics" : "graphics" :
{ {
"animation": "CCRUSD.DEF" "animation": "CCRUSD.DEF"

View File

@ -142,7 +142,7 @@
"id": 63, "id": 63,
"level": 4, "level": 4,
"faction": "necropolis", "faction": "necropolis",
"abilities": [ [ "LIFE_DRAIN", 0, 0, 0 ], "abilities": [ [ "LIFE_DRAIN", 100, 0, 0 ], //drain 100% of damage dealt
[ "BLOCKS_RETALIATION", 0, 0, 0 ] ], //vampire lords [ "BLOCKS_RETALIATION", 0, 0, 0 ] ], //vampire lords
"graphics" : "graphics" :
{ {

View File

@ -107,6 +107,13 @@
"id": 19, "id": 19,
"level": 3, "level": 3,
"faction": "rampart", "faction": "rampart",
"abilities": [
{
"type": "ADDITIONAL_ATTACK",
"val" : 1,
"effectRange": "ONLY_DISTANCE_FIGHT"
}
],
"graphics" : "graphics" :
{ {
"animation": "CGRELF.DEF", "animation": "CGRELF.DEF",

View File

@ -50,7 +50,8 @@
"id": 154, "id": 154,
"level": 8, "level": 8,
"faction": "necropolis", "faction": "necropolis",
"abilities": [ [ "DRAGON_NATURE", 0, 0, 0 ] ], //blood dragon is a dragon "abilities": [ [ "LIFE_DRAIN", 40, 0, 0 ], //40%
[ "DRAGON_NATURE", 0, 0, 0 ] ], //blood dragon is a dragon
"graphics" : "graphics" :
{ {
"animation": "ZM154Z.DEF" "animation": "ZM154Z.DEF"

View File

@ -263,8 +263,6 @@ void CCreatureHandler::loadCreatures()
ncre.addBonus(0, Bonus::SHOOTER); ncre.addBonus(0, Bonus::SHOOTER);
if(boost::algorithm::find_first(ncre.abilityRefs, "SIEGE_WEAPON")) if(boost::algorithm::find_first(ncre.abilityRefs, "SIEGE_WEAPON"))
ncre.addBonus(0, Bonus::SIEGE_WEAPON); ncre.addBonus(0, Bonus::SIEGE_WEAPON);
if(boost::algorithm::find_first(ncre.abilityRefs, "const_two_attacks"))
ncre.addBonus(1, Bonus::ADDITIONAL_ATTACK);
if(boost::algorithm::find_first(ncre.abilityRefs, "const_free_attack")) if(boost::algorithm::find_first(ncre.abilityRefs, "const_free_attack"))
ncre.addBonus(0, Bonus::BLOCKS_RETALIATION); ncre.addBonus(0, Bonus::BLOCKS_RETALIATION);
if(boost::algorithm::find_first(ncre.abilityRefs, "IS_UNDEAD")) if(boost::algorithm::find_first(ncre.abilityRefs, "IS_UNDEAD"))
@ -324,7 +322,10 @@ void CCreatureHandler::loadCreatures()
} }
BOOST_FOREACH(const JsonNode &ability, node.second["abilities"].Vector()) BOOST_FOREACH(const JsonNode &ability, node.second["abilities"].Vector())
{ {
AddAbility(c, ability.Vector()); if (ability.getType() == JsonNode::DATA_VECTOR)
AddAbility(c, ability.Vector());
else
c->addNewBonus(JsonUtils::parseBonus(ability));
} }
loadCreatureJson(c, node.second); loadCreatureJson(c, node.second);

View File

@ -593,8 +593,9 @@ std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
case Bonus::ATTACKS_ALL_ADJACENT: case Bonus::ATTACKS_ALL_ADJACENT:
case Bonus::ADDITIONAL_ATTACK: //TODO: what with more than one attack? Axe of Ferocity for example case Bonus::ADDITIONAL_ATTACK: //TODO: what with more than one attack? Axe of Ferocity for example
case Bonus::FULL_HP_REGENERATION: case Bonus::FULL_HP_REGENERATION:
case Bonus::MANA_DRAIN:
case Bonus::LIFE_DRAIN:
case Bonus::REBIRTH: case Bonus::REBIRTH:
case Bonus::LIFE_DRAIN: //TODO: chance, hp percentage?
case Bonus::SELF_MORALE: case Bonus::SELF_MORALE:
case Bonus::SELF_LUCK: case Bonus::SELF_LUCK:
case Bonus::FEAR: case Bonus::FEAR:
@ -616,7 +617,6 @@ std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
case Bonus::SPELL_RESISTANCE_AURA: case Bonus::SPELL_RESISTANCE_AURA:
case Bonus::SPELL_DAMAGE_REDUCTION: case Bonus::SPELL_DAMAGE_REDUCTION:
case Bonus::LEVEL_SPELL_IMMUNITY: case Bonus::LEVEL_SPELL_IMMUNITY:
case Bonus::MANA_DRAIN:
case Bonus::HP_REGENERATION: case Bonus::HP_REGENERATION:
case Bonus::ADDITIONAL_RETALIATION: case Bonus::ADDITIONAL_RETALIATION:
case Bonus::DEFENSIVE_STANCE: case Bonus::DEFENSIVE_STANCE:
@ -667,6 +667,7 @@ std::string CStackInstance::bonusToString(Bonus *bonus, bool description) const
case Bonus::ENEMY_DEFENCE_REDUCTION: case Bonus::ENEMY_DEFENCE_REDUCTION:
case Bonus::REBIRTH: case Bonus::REBIRTH:
case Bonus::DEATH_STARE: case Bonus::DEATH_STARE:
case Bonus::LIFE_DRAIN:
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))));
break; break;
case Bonus::HATE: case Bonus::HATE:
@ -905,6 +906,8 @@ std::string CStackInstance::bonusToGraphics(Bonus *bonus) const
fileName = "ManaChannel.bmp"; break; fileName = "ManaChannel.bmp"; break;
case Bonus::MANA_DRAIN: case Bonus::MANA_DRAIN:
fileName = "ManaDrain.bmp"; break; fileName = "ManaDrain.bmp"; break;
case Bonus::LIFE_DRAIN:
fileName = "DrainLife.bmp"; break;
} }
if(!fileName.empty()) if(!fileName.empty())
fileName = "zvs/Lib1.res/" + fileName; fileName = "zvs/Lib1.res/" + fileName;

View File

@ -863,7 +863,8 @@ void CGameHandler::applyBattleEffects(BattleAttack &bat, const CStack *att, cons
StacksHealedOrResurrected::HealInfo hi; StacksHealedOrResurrected::HealInfo hi;
hi.stackID = att->ID; hi.stackID = att->ID;
hi.healedHP = std::min<int>(bsa.damageAmount, att->MaxHealth() - att->firstHPleft + att->MaxHealth() * (att->baseAmount - att->count) ); hi.healedHP = std::min<int> (bsa.damageAmount * att->valOfBonuses (Bonus::LIFE_DRAIN) / 100,
att->MaxHealth() - att->firstHPleft + att->MaxHealth() * (att->baseAmount - att->count) );
hi.lowLevelResurrection = false; hi.lowLevelResurrection = false;
shi.healedStacks.push_back(hi); shi.healedStacks.push_back(hi);
@ -3457,38 +3458,32 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
} }
//attack //attack
if(stack->alive()) //move can cause death, eg. by walking into the moat
{
BattleAttack bat;
prepareAttack(bat, stack, stackAtEnd, distance, ba.additionalInfo);
handleAttackBeforeCasting(bat); //only before first attack
sendAndApply(&bat);
handleAfterAttackCasting(bat);
}
//counterattack int totalAttacks = 1 + stack->getBonuses(Selector::type (Bonus::ADDITIONAL_ATTACK),
if(!stack->hasBonusOfType(Bonus::BLOCKS_RETALIATION) (Selector::effectRange (Bonus::NO_LIMIT) || Selector::effectRange(Bonus::ONLY_MELEE_FIGHT)))->totalValue(); //all unspicified attacks + melee attacks
&& stackAtEnd->ableToRetaliate()
&& stack->alive()) //attacker may have died (fire shield)
{
BattleAttack bat;
prepareAttack(bat, stackAtEnd, stack, 0, stack->position);
bat.flags |= BattleAttack::COUNTER;
sendAndApply(&bat);
handleAfterAttackCasting(bat);
}
//second attack for (int i = 0; i < totalAttacks; ++i)
if(stack //FIXME: clones tend to disapear during actions
&& stack->valOfBonuses(Bonus::ADDITIONAL_ATTACK) > 0
&& !stack->hasBonusOfType(Bonus::SHOOTER)
&& stack->alive()
&& stackAtEnd->alive() )
{ {
BattleAttack bat; if( stack &&stack->alive()) //move can cause death, eg. by walking into the moat
prepareAttack(bat, stack, stackAtEnd, 0, ba.additionalInfo); {
sendAndApply(&bat); BattleAttack bat;
handleAfterAttackCasting(bat); prepareAttack(bat, stack, stackAtEnd, distance, ba.additionalInfo);
handleAttackBeforeCasting(bat); //only before first attack
sendAndApply(&bat);
handleAfterAttackCasting(bat);
}
//counterattack
if(!stack->hasBonusOfType(Bonus::BLOCKS_RETALIATION)
&& stackAtEnd->ableToRetaliate()
&& stack->alive()) //attacker may have died (fire shield)
{
BattleAttack bat;
prepareAttack(bat, stackAtEnd, stack, 0, stack->position);
bat.flags |= BattleAttack::COUNTER;
sendAndApply(&bat);
handleAfterAttackCasting(bat);
}
} }
//return //return
@ -3529,18 +3524,24 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
prepareAttack(bat2, stack, destStack, 0, ba.destinationTile); prepareAttack(bat2, stack, destStack, 0, ba.destinationTile);
sendAndApply(&bat2); sendAndApply(&bat2);
} }
//TODO: allow more than one additional attack //allow more than one additional attack
if(stack->valOfBonuses(Bonus::ADDITIONAL_ATTACK) > 0 //if unit shots twice let's make another shot
&& stack->alive() int additionalAttacks = stack->getBonuses(Selector::type (Bonus::ADDITIONAL_ATTACK),
&& destStack->alive() (Selector::effectRange (Bonus::NO_LIMIT) || Selector::effectRange(Bonus::ONLY_DISTANCE_FIGHT)))->totalValue();
&& stack->shots for (int i = 0; i < additionalAttacks; ++i)
)
{ {
BattleAttack bat; if(
bat.flags |= BattleAttack::SHOT; stack->alive()
prepareAttack(bat, stack, destStack, 0, ba.destinationTile); && destStack->alive()
sendAndApply(&bat); && stack->shots
handleAfterAttackCasting(bat); )
{
BattleAttack bat;
bat.flags |= BattleAttack::SHOT;
prepareAttack(bat, stack, destStack, 0, ba.destinationTile);
sendAndApply(&bat);
handleAfterAttackCasting(bat);
}
} }
sendAndApply(&end_action); sendAndApply(&end_action);