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

* luck support

* support for distance/wall penalties & no * penalty abilities
* reworked damage calculation to fit OH3 formula better
This commit is contained in:
mateuszb 2010-04-06 13:19:54 +00:00
parent 943c433d3f
commit 5d3d9689e8
9 changed files with 162 additions and 53 deletions

View File

@ -669,7 +669,7 @@ std::pair<ui32, ui32> CCallback::battleEstimateDamage(int attackerID, int defend
const CStack * attacker = gs->curB->getStack(attackerID, false),
* defender = gs->curB->getStack(defenderID);
return BattleInfo::calculateDmgRange(attacker, defender, attackerHero, defenderHero, battleCanShoot(attacker->ID, defender->position), 0);
return gs->curB->calculateDmgRange(attacker, defender, attackerHero, defenderHero, battleCanShoot(attacker->ID, defender->position), 0, false);
}
ui8 CCallback::battleGetSiegeLevel()
@ -961,6 +961,16 @@ bool CCallback::hasAccess(int playerId) const
return playerId == player || player < 0;
}
si8 CCallback::battleHasDistancePenalty( int stackID, int destHex )
{
return gs->curB->hasDistancePenalty(stackID, destHex);
}
si8 CCallback::battleHasWallPenalty( int stackID, int destHex )
{
return gs->curB->hasWallPenalty(stackID, destHex);
}
InfoAboutTown::InfoAboutTown()
{
tType = NULL;

View File

@ -167,7 +167,7 @@ public:
virtual void getStackQueue( std::vector<const CStack *> &out, int howMany )=0; //returns vector of stack in order of their move sequence
virtual CCreature battleGetCreature(int number)=0; //returns type of creature by given number of stack
//virtual bool battleMoveCreature(int ID, int dest)=0; //moves creature with id ID to dest if possible
virtual std::vector<int> battleGetAvailableHexes(int ID, bool addOccupiable)=0; //reutrns numbers of hexes reachable by creature with id ID
virtual std::vector<int> battleGetAvailableHexes(int ID, bool addOccupiable)=0; //returns numbers of hexes reachable by creature with id ID
virtual bool battleIsStackMine(int ID)=0; //returns true if stack with id ID belongs to caller
virtual bool battleCanShoot(int ID, int dest)=0; //returns true if unit with id ID can shoot to dest
virtual bool battleCanCastSpell()=0; //returns true, if caller can cast a spell
@ -177,9 +177,11 @@ public:
virtual int battleGetWallUnderHex(int hex)=0; //returns part of destructible wall / gate / keep under given hex or -1 if not found
virtual std::pair<ui32, ui32> battleEstimateDamage(int attackerID, int defenderID)=0; //estimates damage dealt by attacker to defender; it may be not precise especially when stack has randomly working bonuses; returns pair <min dmg, max dmg>
virtual ui8 battleGetSiegeLevel()=0; //returns 0 when there is no siege, 1 if fort, 2 is citadel, 3 is castle
virtual const CGHeroInstance * battleGetFightingHero(ui8 side) const =0; //returns hero corresponding ot given side (0 - attacker, 1 - defender)
virtual const CGHeroInstance * battleGetFightingHero(ui8 side) const =0; //returns hero corresponding to given side (0 - attacker, 1 - defender)
virtual si8 battleGetStackMorale(int stackID) =0; //returns morale of given stack
virtual si8 battleGetStackLuck(int stackID) =0; //returns luck of given stack
virtual si8 battleHasDistancePenalty(int stackID, int destHex) =0; //checks if given stack has distance penalty
virtual si8 battleHasWallPenalty(int stackID, int destHex) =0; //checks if given stack has wall penalty
};
struct HeroMoveDetails
@ -302,6 +304,8 @@ public:
const CGHeroInstance * battleGetFightingHero(ui8 side) const; //returns hero corresponding ot given side (0 - attacker, 1 - defender)
si8 battleGetStackMorale(int stackID); //returns morale of given stack
si8 battleGetStackLuck(int stackID); //returns luck of given stack
si8 battleHasDistancePenalty(int stackID, int destHex); //checks if given stack has distance penalty
si8 battleHasWallPenalty(int stackID, int destHex); //checks if given stack has wall penalty
//XXX hmmm _tmain on _GNUC_ wtf?
//friends

View File

@ -18,7 +18,7 @@
#include "../hch/CObjectHandler.h"
#include "../hch/CTownHandler.h"
#include "../lib/map.h"
#include "../mapHandler.h"
#include "mapHandler.h"
#include "../stdafx.h"
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>

View File

@ -1757,7 +1757,15 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
}
else if(curInt->cb->battleCanShoot(activeStack,myNumber)) //we can shoot enemy
{
CGI->curh->changeGraphic(1,3);
if(curInt->cb->battleHasDistancePenalty(activeStack, myNumber) ||
curInt->cb->battleHasWallPenalty(activeStack, myNumber))
{
CGI->curh->changeGraphic(1,15);
}
else
{
CGI->curh->changeGraphic(1,3);
}
//setting console text
char buf[500];
//calculating estimated dmg

View File

@ -29,7 +29,7 @@
#include "../lib/NetPacks.h"
#include "../lib/map.h"
#include "../lib/VCMIDirs.h"
#include "../mapHandler.h"
#include "mapHandler.h"
#include "../timeHandler.h"
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>

View File

@ -17,7 +17,7 @@
#include "../lib/NetPacks.h"
#include "../lib/VCMI_Lib.h"
#include "../lib/map.h"
#include "../mapHandler.h"
#include "mapHandler.h"
#include "CConfigHandler.h"
#include "Client.h"
#include "GUIBase.h"

View File

@ -24,6 +24,8 @@
#include <boost/thread/shared_mutex.hpp>
#include <boost/assign/list_of.hpp>
#include "RegisterTypes.cpp"
#include <algorithm>
#include <numeric>
boost::rand48 ran;
@ -2373,9 +2375,9 @@ bool CGameState::checkForVisitableDir( const int3 & src, const TerrainTile *pom,
}
return true;
}
std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge)
std::pair<ui32, ui32> BattleInfo::calculateDmgRange( const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky )
{
float attackDefenseBonus,
float additiveBonus=1.0f, multBonus=1.0f,
minDmg = attacker->creature->damageMin * attacker->amount,
maxDmg = attacker->creature->damageMax * attacker->amount;
@ -2400,36 +2402,37 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, cons
maxDmg *= attackerHero->getPrimSkillLevel(0) + 1;
}
int attackDefenceDifference = 0;
if(attacker->hasFeatureOfType(StackFeature::GENERAL_ATTACK_REDUCTION))
{
float multAttackReduction = attacker->valOfFeatures(StackFeature::GENERAL_ATTACK_REDUCTION, -1024) / 100.0f;
attackDefenseBonus = attacker->Attack() * multAttackReduction;
attackDefenceDifference = attacker->Attack() * multAttackReduction;
}
else
{
attackDefenseBonus = attacker->Attack();
attackDefenceDifference = attacker->Attack();
}
if(attacker->hasFeatureOfType(StackFeature::ENEMY_DEFENCE_REDUCTION))
{
float multDefenceReduction = (100.0f - attacker->valOfFeatures(StackFeature::ENEMY_DEFENCE_REDUCTION, -1024)) / 100.0f;
attackDefenseBonus -= defender->Defense() * multDefenceReduction;
attackDefenceDifference -= defender->Defense() * multDefenceReduction;
}
else
{
attackDefenseBonus -= defender->Defense();
attackDefenceDifference -= defender->Defense();
}
//calculating total attack/defense skills modifier
if(!shooting && attacker->hasFeatureOfType(StackFeature::ATTACK_BONUS, 0)) //bloodlust handling (etc.)
{
attackDefenseBonus += attacker->valOfFeatures(StackFeature::ATTACK_BONUS, 0);
attackDefenceDifference += attacker->valOfFeatures(StackFeature::ATTACK_BONUS, 0);
}
if(shooting && attacker->hasFeatureOfType(StackFeature::ATTACK_BONUS, 1)) //precision handling (etc.)
{
attackDefenseBonus += attacker->valOfFeatures(StackFeature::ATTACK_BONUS, 1);
attackDefenceDifference += attacker->valOfFeatures(StackFeature::ATTACK_BONUS, 1);
}
if(attacker->getEffect(55)) //slayer handling
@ -2455,42 +2458,44 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, cons
{
if(defender->creature->idNumber == affectedIds[g])
{
attackDefenseBonus += VLC->spellh->spells[55].powers[attacker->getEffect(55)->level];
attackDefenceDifference += VLC->spellh->spells[55].powers[attacker->getEffect(55)->level];
break;
}
}
}
float dmgBonusMultiplier = 1.0f;
//applying jousting bonus
if( attacker->hasFeatureOfType(StackFeature::JOUSTING) && !defender->hasFeatureOfType(StackFeature::CHARGE_IMMUNITY) )
dmgBonusMultiplier += charge * 0.05f;
//bonus from attack/defense skills
if(attackDefenseBonus < 0) //decreasing dmg
if(attackDefenceDifference < 0) //decreasing dmg
{
if(0.02f * (-attackDefenseBonus) > 0.3f)
float dec = 0.025f * (-attackDefenceDifference);
if(dec > 0.7f)
{
dmgBonusMultiplier += -0.3f;
multBonus *= 1.0f - dec;
}
else
{
dmgBonusMultiplier += 0.02f * attackDefenseBonus;
multBonus *= dec;
}
}
else //increasing dmg
{
if(0.05f * attackDefenseBonus > 4.0f)
float inc = 0.05f * attackDefenceDifference;
if(inc > 4.0f)
{
dmgBonusMultiplier += 4.0f;
additiveBonus += 4.0f;
}
else
{
dmgBonusMultiplier += 0.05f * attackDefenseBonus;
additiveBonus += inc;
}
}
//applying jousting bonus
if( attacker->hasFeatureOfType(StackFeature::JOUSTING) && !defender->hasFeatureOfType(StackFeature::CHARGE_IMMUNITY) )
additiveBonus += charge * 0.05f;
//handling secondary abilities and artifacts giving premies to them
if(attackerHero)
{
@ -2499,20 +2504,20 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, cons
switch(attackerHero->getSecSkillLevel(1)) //archery
{
case 1: //basic
dmgBonusMultiplier += 0.1f;
additiveBonus += 0.1f;
break;
case 2: //advanced
dmgBonusMultiplier += 0.25f;
additiveBonus += 0.25f;
break;
case 3: //expert
dmgBonusMultiplier += 0.5f;
additiveBonus += 0.5f;
break;
}
if(attackerHero->getSecSkillLevel(1) > 0) //non-none level
{
//apply artifact premy to archery
dmgBonusMultiplier += attackerHero->valOfBonuses(HeroBonus::SECONDARY_SKILL_PREMY, 1) / 100.0f;
additiveBonus += attackerHero->valOfBonuses(HeroBonus::SECONDARY_SKILL_PREMY, 1) / 100.0f;
}
}
else
@ -2520,54 +2525,85 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, cons
switch(attackerHero->getSecSkillLevel(22)) //offense
{
case 1: //basic
dmgBonusMultiplier += 0.1f;
additiveBonus += 0.1f;
break;
case 2: //advanced
dmgBonusMultiplier += 0.2f;
additiveBonus += 0.2f;
break;
case 3: //expert
dmgBonusMultiplier += 0.3f;
additiveBonus += 0.3f;
break;
}
}
}
if(defendingHero)
{
switch(defendingHero->getSecSkillLevel(23)) //armorer
{
case 1: //basic
dmgBonusMultiplier *= 0.95f;
multBonus *= 0.95f;
break;
case 2: //advanced
dmgBonusMultiplier *= 0.9f;
multBonus *= 0.9f;
break;
case 3: //expert
dmgBonusMultiplier *= 0.85f;
multBonus *= 0.85f;
break;
}
}
//handling hate effect
if( attacker->hasFeatureOfType(StackFeature::HATE, defender->creature->idNumber) )
dmgBonusMultiplier += 0.5f;
additiveBonus += 0.5f;
//luck bonus
if (lucky)
{
additiveBonus += 1.0f;
}
//handling spell effects
if(!shooting && defender->hasFeatureOfType(StackFeature::GENERAL_DAMAGE_REDUCTION, 0)) //eg. shield
{
dmgBonusMultiplier *= float(defender->valOfFeatures(StackFeature::GENERAL_DAMAGE_REDUCTION, 0)) / 100.0f;
multBonus *= float(defender->valOfFeatures(StackFeature::GENERAL_DAMAGE_REDUCTION, 0)) / 100.0f;
}
else if(shooting && defender->hasFeatureOfType(StackFeature::GENERAL_DAMAGE_REDUCTION, 1)) //eg. air shield
{
dmgBonusMultiplier *= float(defender->valOfFeatures(StackFeature::GENERAL_DAMAGE_REDUCTION, 1)) / 100.0f;
multBonus *= float(defender->valOfFeatures(StackFeature::GENERAL_DAMAGE_REDUCTION, 1)) / 100.0f;
}
if(attacker->getEffect(42)) //curse handling (partial, the rest is below)
{
dmgBonusMultiplier *= 0.8f * float(VLC->spellh->spells[42].powers[attacker->getEffect(42)->level]); //the second factor is 1 or 0
multBonus *= 0.8f * float(VLC->spellh->spells[42].powers[attacker->getEffect(42)->level]); //the second factor is 1 or 0
}
class HLP
{
public:
static bool hasAdvancedAirShield(const CStack * stack)
{
for(int g=0; g<stack->effects.size(); ++g)
{
if (stack->effects[g].id == 28 && stack->effects[g].level >= 2)
{
return true;
}
}
return false;
}
};
minDmg *= dmgBonusMultiplier;
maxDmg *= dmgBonusMultiplier;
//wall / distance penalty + advanced air shield
if (shooting && (
hasDistancePenalty(attacker->ID, defender->position) || hasWallPenalty(attacker->ID, defender->position) ||
HLP::hasAdvancedAirShield(defender) )
)
{
multBonus *= 0.5;
}
minDmg *= additiveBonus * multBonus;
maxDmg *= additiveBonus * multBonus;
std::pair<ui32, ui32> returnedVal;
@ -2593,12 +2629,21 @@ std::pair<ui32, ui32> BattleInfo::calculateDmgRange(const CStack* attacker, cons
return returnedVal;
}
ui32 BattleInfo::calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge)
ui32 BattleInfo::calculateDmg( const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky )
{
std::pair<ui32, ui32> range = calculateDmgRange(attacker, defender, attackerHero, defendingHero, shooting, charge);
std::pair<ui32, ui32> range = calculateDmgRange(attacker, defender, attackerHero, defendingHero, shooting, charge, lucky);
if(range.first != range.second)
return range.first + rand() % (range.second - range.first + 1);
{
int valuesToAverage[10];
int howManyToAv = std::min<ui32>(10, attacker->amount);
for (int g=0; g<howManyToAv; ++g)
{
valuesToAverage[g] = range.first + rand() % (range.second - range.first + 1);
}
return std::accumulate(valuesToAverage, valuesToAverage + howManyToAv, 0) / howManyToAv;
}
else
return range.first;
}
@ -2809,6 +2854,13 @@ int BattleInfo::hexToWallPart(int hex) const
return -1; //not found!
}
int BattleInfo::lineToWallHex( int line ) const
{
static const int lineToHex[] = {12, 29, 45, 62, 78, 95, 112, 130, 147, 165, 182};
return lineToHex[line];
}
std::pair<const CStack *, int> BattleInfo::getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const
{
bool ac[BFIELD_SIZE];
@ -3585,6 +3637,36 @@ si8 BattleInfo::Luck( const CStack * st ) const
return ret;
}
si8 BattleInfo::hasDistancePenalty( int stackID, int destHex )
{
const CStack * stack = getStack(stackID);
int distance = std::abs(destHex % BFIELD_WIDTH - stack->position % BFIELD_WIDTH);
//I hope it's approximately correct
return distance > 8 && !stack->hasFeatureOfType(StackFeature::NO_DISTANCE_PENALTY);
}
si8 BattleInfo::hasWallPenalty( int stackID, int destHex )
{
if (siege == 0)
{
return false;
}
const CStack * stack = getStack(stackID);
if (stack->hasFeatureOfType(StackFeature::NO_WALL_PENALTY));
{
return false;
}
int wallInStackLine = lineToWallHex(stack->position/BFIELD_WIDTH);
int wallInDestLine = lineToWallHex(destHex/BFIELD_WIDTH);
bool stackLeft = stack->position < wallInStackLine;
bool destLeft = destHex < wallInDestLine;
return stackLeft != destLeft;
}
int3 CPath::startPos() const
{
return nodes[nodes.size()-1].coord;

View File

@ -188,19 +188,22 @@ struct DLL_EXPORT BattleInfo
si8 Morale(const CStack * st) const; //get morale of stack with all modificators
si8 Luck(const CStack * st) const; //get luck of stack with all modificators
bool isStackBlocked(int ID); //returns true if there is neighbouring enemy stack
bool isStackBlocked(int ID); //returns true if there is neighboring enemy stack
static signed char mutualPosition(int hex1, int hex2); //returns info about mutual position of given hexes (-1 - they're distant, 0 - left top, 1 - right top, 2 - right, 3 - right bottom, 4 - left bottom, 5 - left)
static std::vector<int> neighbouringTiles(int hex);
static ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge); //charge - number of hexes travelled before attack (for champion's jousting)
static std::pair<ui32, ui32> calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge); //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
ui32 calculateDmg(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky); //charge - number of hexes travelled before attack (for champion's jousting)
std::pair<ui32, ui32> calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky); //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
std::set<CStack*> getAttackedCreatures(const CSpell * s, int skillLevel, ui8 attackerOwner, int destinationTile); //calculates stack affected by given spell
static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster);
CStack * generateNewStack(const CGHeroInstance * owner, int creatureID, int amount, int stackID, bool attackerOwned, int slot, int /*TerrainTile::EterrainType*/ terrain, int position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
ui32 getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //returns cost of given spell
int hexToWallPart(int hex) const; //returns part of destructible wall / gate / keep under given hex or -1 if not found
int lineToWallHex(int line) const; //returns hex with wall in given line
std::pair<const CStack *, int> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; //if attackerOwned is indetermnate, returened stack is of any owner; hex is the number of hex we should be looking from; returns (nerarest creature, predecessorHex)
ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; //calculates damage inflicted by spell
si8 hasDistancePenalty(int stackID, int destHex); //determines if given stack has distance penalty shooting given pos
si8 hasWallPenalty(int stackID, int destHex); //determines if given stack has wall penalty shooting given pos
};
class DLL_EXPORT CStack

View File

@ -637,13 +637,15 @@ void CGameHandler::prepareAttack(BattleAttack &bat, const CStack *att, const CSt
BattleStackAttacked *bsa = &bat.bsa.back();
bsa->stackAttacked = def->ID;
bsa->attackerID = att->ID;
bsa->damageAmount = BattleInfo::calculateDmg(att, def, gs->battleGetOwner(att->ID), gs->battleGetOwner(def->ID), bat.shot(), distance);//counting dealt damage
if(gs->curB->Luck(att) > 0 && rand()%24 < gs->curB->Luck(att))
{
bsa->damageAmount *= 2;
bat.flags |= 4;
}
bsa->damageAmount = gs->curB->calculateDmg(att, def, gs->battleGetOwner(att->ID), gs->battleGetOwner(def->ID), bat.shot(), distance, bat.lucky());//counting dealt damage
int dmg = bsa->damageAmount;
prepareAttacked(*bsa, def);