From 5d3d9689e8ded4004764c2dfd6ed32448cba1719 Mon Sep 17 00:00:00 2001 From: mateuszb Date: Tue, 6 Apr 2010 13:19:54 +0000 Subject: [PATCH] * luck support * support for distance/wall penalties & no * penalty abilities * reworked damage calculation to fit OH3 formula better --- CCallback.cpp | 12 ++- CCallback.h | 8 +- client/CAdvmapInterface.cpp | 2 +- client/CBattleInterface.cpp | 10 ++- client/CPlayerInterface.cpp | 2 +- client/Client.cpp | 2 +- lib/CGameState.cpp | 164 +++++++++++++++++++++++++++--------- lib/CGameState.h | 9 +- server/CGameHandler.cpp | 6 +- 9 files changed, 162 insertions(+), 53 deletions(-) diff --git a/CCallback.cpp b/CCallback.cpp index be73bb6f4..09ab7bb7a 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -669,7 +669,7 @@ std::pair 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; diff --git a/CCallback.h b/CCallback.h index 6d7285e53..204bcf3ce 100644 --- a/CCallback.h +++ b/CCallback.h @@ -167,7 +167,7 @@ public: virtual void getStackQueue( std::vector &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 battleGetAvailableHexes(int ID, bool addOccupiable)=0; //reutrns numbers of hexes reachable by creature with id ID + virtual std::vector 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 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 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 diff --git a/client/CAdvmapInterface.cpp b/client/CAdvmapInterface.cpp index beb412111..4bc718bbf 100644 --- a/client/CAdvmapInterface.cpp +++ b/client/CAdvmapInterface.cpp @@ -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 #include diff --git a/client/CBattleInterface.cpp b/client/CBattleInterface.cpp index 1bf867561..e2fb2abc2 100644 --- a/client/CBattleInterface.cpp +++ b/client/CBattleInterface.cpp @@ -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 diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index f5c693d63..4163c3a4b 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -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 #include diff --git a/client/Client.cpp b/client/Client.cpp index 9b8798e29..691611ba0 100644 --- a/client/Client.cpp +++ b/client/Client.cpp @@ -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" diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp index b82d77e1a..35a02496a 100644 --- a/lib/CGameState.cpp +++ b/lib/CGameState.cpp @@ -24,6 +24,8 @@ #include #include #include "RegisterTypes.cpp" +#include +#include boost::rand48 ran; @@ -2373,9 +2375,9 @@ bool CGameState::checkForVisitableDir( const int3 & src, const TerrainTile *pom, } return true; } -std::pair BattleInfo::calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge) +std::pair 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 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 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 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 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; geffects.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 returnedVal; @@ -2593,12 +2629,21 @@ std::pair 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 range = calculateDmgRange(attacker, defender, attackerHero, defendingHero, shooting, charge); + std::pair 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(10, attacker->amount); + for (int g=0; g 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; diff --git a/lib/CGameState.h b/lib/CGameState.h index 83861de9b..3fa585938 100644 --- a/lib/CGameState.h +++ b/lib/CGameState.h @@ -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 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 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 + 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 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 void calculateCasualties(std::map *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount) std::set 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 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 diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 2bb839a8f..842565f9d 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -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);