mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-03 00:46:55 +02:00
DETERMINISTIC_BATTLES switch.
It disables following effects: * random dmg range. Now dmg dealt = (min_dmg + max_dmg) / 2 * REBIRTH - no random premy for tha last stack (only "fully reborn" stacks will be resurrected) * obstacles placement * MAGIC_RESISTANCE * luck & morale (both positive and negative) * DOUBLE_DAMAGE_CHANCE * BALLISTA_DOUBLE_DMG chance * catapult % chance for hitting wall (it will always hit) * catapult and first aid tent random default targets (when hero has no appropriate sec skill) * MAGIC_MIRROR * SPELL_BEFORE_ATTACK * SPELL_AFTER_ATTACK * DEATH_STARE * ACID_BREATH
This commit is contained in:
2
global.h
2
global.h
@ -143,6 +143,8 @@ const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT;
|
|||||||
|
|
||||||
const int SPELLBOOK_GOLD_COST = 500;
|
const int SPELLBOOK_GOLD_COST = 500;
|
||||||
|
|
||||||
|
|
||||||
|
const bool DETERMINISTIC_BATTLES = true;
|
||||||
//for battle stacks' positions
|
//for battle stacks' positions
|
||||||
struct THex
|
struct THex
|
||||||
{
|
{
|
||||||
|
@ -673,6 +673,12 @@ ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, c
|
|||||||
TDmgRange range = calculateDmgRange(attacker, defender, attackerHero, defendingHero, shooting, charge, lucky, deathBlow, ballistaDoubleDmg);
|
TDmgRange range = calculateDmgRange(attacker, defender, attackerHero, defendingHero, shooting, charge, lucky, deathBlow, ballistaDoubleDmg);
|
||||||
|
|
||||||
if(range.first != range.second)
|
if(range.first != range.second)
|
||||||
|
{
|
||||||
|
if(DETERMINISTIC_BATTLES)
|
||||||
|
{
|
||||||
|
return (range.first + range.second) / 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
int valuesToAverage[10];
|
int valuesToAverage[10];
|
||||||
int howManyToAv = std::min<ui32>(10, attacker->count);
|
int howManyToAv = std::min<ui32>(10, attacker->count);
|
||||||
@ -683,6 +689,7 @@ ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, c
|
|||||||
|
|
||||||
return std::accumulate(valuesToAverage, valuesToAverage + howManyToAv, 0) / howManyToAv;
|
return std::accumulate(valuesToAverage, valuesToAverage + howManyToAv, 0) / howManyToAv;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return range.first;
|
return range.first;
|
||||||
}
|
}
|
||||||
@ -1591,7 +1598,7 @@ BattleInfo * BattleInfo::setupBattle( int3 tile, int terrain, int terType, const
|
|||||||
}
|
}
|
||||||
|
|
||||||
//randomize obstacles
|
//randomize obstacles
|
||||||
if(town == NULL && !creatureBank) //do it only when it's not siege and not creature bank
|
if(town == NULL && !creatureBank && !DETERMINISTIC_BATTLES) //do it only when it's not siege and not creature bank
|
||||||
{
|
{
|
||||||
bool obAv[BFIELD_SIZE]; //availability of hexes for obstacles;
|
bool obAv[BFIELD_SIZE]; //availability of hexes for obstacles;
|
||||||
std::vector<int> possibleObstacles;
|
std::vector<int> possibleObstacles;
|
||||||
@ -2049,8 +2056,11 @@ std::vector<ui32> BattleInfo::calculateResistedStacks( const CSpell * sp, const
|
|||||||
bonusHero = hero2;
|
bonusHero = hero2;
|
||||||
|
|
||||||
int prob = (*it)->magicResistance(); //probability of resistance in %
|
int prob = (*it)->magicResistance(); //probability of resistance in %
|
||||||
|
amin(prob, 100);
|
||||||
|
|
||||||
if(prob > 100) prob = 100;
|
//disable spell resistance
|
||||||
|
if(DETERMINISTIC_BATTLES)
|
||||||
|
prob = 0;
|
||||||
|
|
||||||
if(rand()%100 < prob) //immunity from resistance
|
if(rand()%100 < prob) //immunity from resistance
|
||||||
ret.push_back((*it)->ID);
|
ret.push_back((*it)->ID);
|
||||||
@ -2551,10 +2561,13 @@ void CStack::prepareAttacked(BattleStackAttacked &bsa) const
|
|||||||
if (resurrectFactor > 0 && casts) //there must be casts left
|
if (resurrectFactor > 0 && casts) //there must be casts left
|
||||||
{
|
{
|
||||||
int resurrectedCount = base->count * resurrectFactor / 100;
|
int resurrectedCount = base->count * resurrectFactor / 100;
|
||||||
|
if(!DETERMINISTIC_BATTLES)
|
||||||
|
{
|
||||||
if (resurrectedCount)
|
if (resurrectedCount)
|
||||||
resurrectedCount += ((base->count * resurrectFactor / 100.0f - resurrectedCount) > ran()%100 / 100.0f) ? 1 : 0; //last stack has proportional chance to rebirth
|
resurrectedCount += ((base->count * resurrectFactor / 100.0f - resurrectedCount) > rand()%100 / 100.0f) ? 1 : 0; //last stack has proportional chance to rebirth
|
||||||
else //only one unit
|
else //only one unit
|
||||||
resurrectedCount += ((base->count * resurrectFactor / 100.0f) > ran()%100 / 100.0f) ? 1 : 0;
|
resurrectedCount += ((base->count * resurrectFactor / 100.0f) > rand()%100 / 100.0f) ? 1 : 0;
|
||||||
|
}
|
||||||
if (hasBonusOfType(Bonus::REBIRTH, 1))
|
if (hasBonusOfType(Bonus::REBIRTH, 1))
|
||||||
amax (resurrectedCount, 1); //resurrect at least one Sacred Phoenix
|
amax (resurrectedCount, 1); //resurrect at least one Sacred Phoenix
|
||||||
if (resurrectedCount)
|
if (resurrectedCount)
|
||||||
|
@ -531,11 +531,11 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
|
|||||||
noLuck = true;
|
noLuck = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!noLuck && attackerLuck > 0 && rand()%24 < attackerLuck) //TODO?: negative luck option?
|
if(!DETERMINISTIC_BATTLES && !noLuck && attackerLuck > 0 && rand()%24 < attackerLuck) //TODO?: negative luck option?
|
||||||
{
|
{
|
||||||
bat.flags |= BattleAttack::LUCKY;
|
bat.flags |= BattleAttack::LUCKY;
|
||||||
}
|
}
|
||||||
if (rand()%100 < att->valOfBonuses(Bonus::DOUBLE_DAMAGE_CHANCE))
|
if(!DETERMINISTIC_BATTLES && rand()%100 < att->valOfBonuses(Bonus::DOUBLE_DAMAGE_CHANCE))
|
||||||
{
|
{
|
||||||
bat.flags |= BattleAttack::DEATH_BLOW;
|
bat.flags |= BattleAttack::DEATH_BLOW;
|
||||||
}
|
}
|
||||||
@ -545,7 +545,7 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
|
|||||||
static const int artilleryLvlToChance[] = {0, 50, 75, 100};
|
static const int artilleryLvlToChance[] = {0, 50, 75, 100};
|
||||||
const CGHeroInstance * owner = gs->curB->getHero(att->owner);
|
const CGHeroInstance * owner = gs->curB->getHero(att->owner);
|
||||||
int chance = artilleryLvlToChance[owner->getSecSkillLevel(CGHeroInstance::ARTILLERY)];
|
int chance = artilleryLvlToChance[owner->getSecSkillLevel(CGHeroInstance::ARTILLERY)];
|
||||||
if(chance > rand() % 100)
|
if(!DETERMINISTIC_BATTLES && chance > rand() % 100)
|
||||||
{
|
{
|
||||||
bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG;
|
bat.flags |= BattleAttack::BALLISTA_DOUBLE_DMG;
|
||||||
}
|
}
|
||||||
@ -3189,9 +3189,11 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(rand()%100 <= chanceForHit) //hit is successful
|
if(DETERMINISTIC_BATTLES || rand()%100 <= chanceForHit) //hit is successful
|
||||||
{
|
{
|
||||||
int dmgRand = rand()%100;
|
int dmgRand = rand()%100;
|
||||||
|
if(DETERMINISTIC_BATTLES)
|
||||||
|
dmgRand = 50;
|
||||||
//accumulating dmgChance
|
//accumulating dmgChance
|
||||||
dmgChance[1] += dmgChance[0];
|
dmgChance[1] += dmgChance[0];
|
||||||
dmgChance[2] += dmgChance[1];
|
dmgChance[2] += dmgChance[1];
|
||||||
@ -3734,7 +3736,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, THex destinati
|
|||||||
if(!si.stacks.empty()) //after spellcast info shows
|
if(!si.stacks.empty()) //after spellcast info shows
|
||||||
sendAndApply(&si);
|
sendAndApply(&si);
|
||||||
//Magic Mirror effect
|
//Magic Mirror effect
|
||||||
if (spell->positiveness < 0 && mode != SpellCasting::MAGIC_MIRROR && spell->level && spell->range[0] == "0") //it is actual spell and can be reflected to single target, no recurrence
|
if(!DETERMINISTIC_BATTLES && spell->positiveness < 0 && mode != SpellCasting::MAGIC_MIRROR && spell->level && spell->range[0] == "0") //it is actual spell and can be reflected to single target, no recurrence
|
||||||
{
|
{
|
||||||
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
|
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
|
||||||
{
|
{
|
||||||
@ -4369,7 +4371,7 @@ bool CGameHandler::dig( const CGHeroInstance *h )
|
|||||||
|
|
||||||
void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType attackMode, const CStack * attacker)
|
void CGameHandler::attackCasting(const BattleAttack & bat, Bonus::BonusType attackMode, const CStack * attacker)
|
||||||
{
|
{
|
||||||
if(attacker->hasBonusOfType(attackMode))
|
if(!DETERMINISTIC_BATTLES && attacker->hasBonusOfType(attackMode))
|
||||||
{
|
{
|
||||||
std::set<ui32> spellsToCast;
|
std::set<ui32> spellsToCast;
|
||||||
TBonusListPtr spells = attacker->getBonuses(Selector::type(attackMode));
|
TBonusListPtr spells = attacker->getBonuses(Selector::type(attackMode));
|
||||||
@ -4431,7 +4433,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
|
|||||||
const CStack * attacker = gs->curB->getStack(bat.stackAttacking);
|
const CStack * attacker = gs->curB->getStack(bat.stackAttacking);
|
||||||
attackCasting(bat, Bonus::SPELL_AFTER_ATTACK, attacker);
|
attackCasting(bat, Bonus::SPELL_AFTER_ATTACK, attacker);
|
||||||
|
|
||||||
if (attacker->hasBonusOfType(Bonus::DEATH_STARE)) // spell id 79
|
if (!DETERMINISTIC_BATTLES && attacker->hasBonusOfType(Bonus::DEATH_STARE)) // spell id 79
|
||||||
{
|
{
|
||||||
int staredCreatures = 0;
|
int staredCreatures = 0;
|
||||||
double mean = attacker->count * attacker->valOfBonuses(Bonus::DEATH_STARE, 0) / 100;
|
double mean = attacker->count * attacker->valOfBonuses(Bonus::DEATH_STARE, 0) / 100;
|
||||||
@ -4453,6 +4455,9 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
|
|||||||
!attacker->attackerOwned, attacker->owner, NULL, NULL, staredCreatures, SpellCasting::AFTER_ATTACK_CASTING, attacker);
|
!attacker->attackerOwned, attacker->owner, NULL, NULL, staredCreatures, SpellCasting::AFTER_ATTACK_CASTING, attacker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!DETERMINISTIC_BATTLES)
|
||||||
|
{
|
||||||
int acidDamage = 0;
|
int acidDamage = 0;
|
||||||
TBonusListPtr acidBreath = attacker->getBonuses(Selector::type(Bonus::ACID_BREATH));
|
TBonusListPtr acidBreath = attacker->getBonuses(Selector::type(Bonus::ACID_BREATH));
|
||||||
BOOST_FOREACH(const Bonus *b, *acidBreath)
|
BOOST_FOREACH(const Bonus *b, *acidBreath)
|
||||||
@ -4466,6 +4471,7 @@ void CGameHandler::handleAfterAttackCasting( const BattleAttack & bat )
|
|||||||
!attacker->attackerOwned, attacker->owner, NULL, NULL,
|
!attacker->attackerOwned, attacker->owner, NULL, NULL,
|
||||||
acidDamage * attacker->count, SpellCasting::AFTER_ATTACK_CASTING, attacker);
|
acidDamage * attacker->count, SpellCasting::AFTER_ATTACK_CASTING, attacker);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &pos)
|
bool CGameHandler::castSpell(const CGHeroInstance *h, int spellID, const int3 &pos)
|
||||||
@ -4960,6 +4966,7 @@ void CGameHandler::runBattle()
|
|||||||
//check for bad morale => freeze
|
//check for bad morale => freeze
|
||||||
int nextStackMorale = next->MoraleVal();
|
int nextStackMorale = next->MoraleVal();
|
||||||
if( nextStackMorale < 0 &&
|
if( nextStackMorale < 0 &&
|
||||||
|
!DETERMINISTIC_BATTLES &&
|
||||||
!(NBonus::hasOfType(gs->curB->heroes[0], Bonus::BLOCK_MORALE) || NBonus::hasOfType(gs->curB->heroes[1], Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses)
|
!(NBonus::hasOfType(gs->curB->heroes[0], Bonus::BLOCK_MORALE) || NBonus::hasOfType(gs->curB->heroes[1], Bonus::BLOCK_MORALE)) //checking if gs->curB->heroes have (or don't have) morale blocking bonuses)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
@ -5034,7 +5041,7 @@ void CGameHandler::runBattle()
|
|||||||
BattleAction attack;
|
BattleAction attack;
|
||||||
static const int wallHexes[] = {50, 183, 182, 130, 62, 29, 12, 95};
|
static const int wallHexes[] = {50, 183, 182, 130, 62, 29, 12, 95};
|
||||||
|
|
||||||
attack.destinationTile = wallHexes[ rand()%ARRAY_COUNT(wallHexes) ];
|
attack.destinationTile = wallHexes[DETERMINISTIC_BATTLES ? 0 : rand()%ARRAY_COUNT(wallHexes)];
|
||||||
attack.actionType = BattleAction::CATAPULT;
|
attack.actionType = BattleAction::CATAPULT;
|
||||||
attack.additionalInfo = 0;
|
attack.additionalInfo = 0;
|
||||||
attack.side = !next->attackerOwned;
|
attack.side = !next->attackerOwned;
|
||||||
@ -5073,9 +5080,9 @@ void CGameHandler::runBattle()
|
|||||||
//heal random creature
|
//heal random creature
|
||||||
const CStack * toBeHealed = NULL;
|
const CStack * toBeHealed = NULL;
|
||||||
if (possibleStacks.size() > 0)
|
if (possibleStacks.size() > 0)
|
||||||
toBeHealed = possibleStacks[ rand()%possibleStacks.size() ];
|
toBeHealed = possibleStacks[DETERMINISTIC_BATTLES ? 0 : rand()%possibleStacks.size() ];
|
||||||
else
|
else
|
||||||
toBeHealed = secondPriority[ rand()%secondPriority.size() ];
|
toBeHealed = secondPriority[DETERMINISTIC_BATTLES ? 0 : rand()%secondPriority.size() ];
|
||||||
|
|
||||||
heal.actionType = BattleAction::STACK_HEAL;
|
heal.actionType = BattleAction::STACK_HEAL;
|
||||||
heal.additionalInfo = 0;
|
heal.additionalInfo = 0;
|
||||||
@ -5115,7 +5122,8 @@ void CGameHandler::runBattle()
|
|||||||
|
|
||||||
//check for good morale
|
//check for good morale
|
||||||
nextStackMorale = next->MoraleVal();
|
nextStackMorale = next->MoraleVal();
|
||||||
if(!vstd::contains(next->state,HAD_MORALE) //only one extra move per turn possible
|
if(!DETERMINISTIC_BATTLES
|
||||||
|
&& !vstd::contains(next->state,HAD_MORALE) //only one extra move per turn possible
|
||||||
&& !vstd::contains(next->state,DEFENDING)
|
&& !vstd::contains(next->state,DEFENDING)
|
||||||
&& !vstd::contains(next->state,WAITING)
|
&& !vstd::contains(next->state,WAITING)
|
||||||
&& next->alive()
|
&& next->alive()
|
||||||
|
Reference in New Issue
Block a user