mirror of
https://github.com/vcmi/vcmi.git
synced 2026-05-16 09:28:24 +02:00
Spells configuration version 2 (effect-based)
* Indirect spell effects loading * Json serializer improvements * spell->canBeCastAt do not allow useless cast for any spell * Added proxy caster class for spell-created obstacles * Handle damage from spell-created obstacles inside mechanics * Experimental GameState integration/regression tests * Ignore mod settings and load only "vcmi" mod when running tests * fixed https://bugs.vcmi.eu/view.php?id=2765 (with tests) * Huge improvements of BattleAI regarding spell casts * AI can cast almost any combat spell except TELEPORT, SACRIFICE and obstacle placement spells. * Possible fix for https://bugs.vcmi.eu/view.php?id=1811 * CStack factored out to several classes * [Battle] Allowed RETURN_AFTER_STRIKE effect on server side to be optional * [Battle] Allowed BattleAction have multiple destinations * [Spells] Converted limit|immunity to target condition * [Spells] Use partial configuration reload for backward compatibility handling * [Tests] Started tests for CUnitState * Partial fixes of fire shield effect * [Battle] Do HP calculations in 64 bits * [BattleAI] Use threading for spell cast evaluation * [BattleAI] Made AI be able to evaluate modified turn order (on hypothetical battle state) * Implemented https://bugs.vcmi.eu/view.php?id=2811 * plug rare freeze when hypnotized unit shots vertically * Correctly apply ONLY_MELEE_FIGHT / ONLY_DISTANCE_FIGHT for unit damage, attack & defense * [BattleAI] Try to not waste a cast if battle is actually won already * Extended JsonSerializeFormat API * fixed https://bugs.vcmi.eu/view.php?id=2847 * Any unit effect can be now chained (not only damage like Chain Lightning) ** only damage effect for now actually uses "chainFactor" * Possible quick fix for https://bugs.vcmi.eu/view.php?id=2860
This commit is contained in:
@@ -10,53 +10,88 @@
|
|||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "AttackPossibility.h"
|
#include "AttackPossibility.h"
|
||||||
|
|
||||||
int AttackPossibility::damageDiff() const
|
AttackPossibility::AttackPossibility(BattleHex tile_, const BattleAttackInfo & attack_)
|
||||||
|
: tile(tile_),
|
||||||
|
attack(attack_)
|
||||||
{
|
{
|
||||||
if (!priorities)
|
|
||||||
priorities = new Priorities();
|
|
||||||
const auto dealtDmgValue = priorities->stackEvaluator(enemy) * damageDealt;
|
|
||||||
const auto receivedDmgValue = priorities->stackEvaluator(attack.attacker) * damageReceived;
|
|
||||||
return dealtDmgValue - receivedDmgValue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int AttackPossibility::attackValue() const
|
|
||||||
|
int64_t AttackPossibility::damageDiff() const
|
||||||
|
{
|
||||||
|
//TODO: use target priority from HypotheticBattle
|
||||||
|
const auto dealtDmgValue = damageDealt;
|
||||||
|
const auto receivedDmgValue = damageReceived;
|
||||||
|
|
||||||
|
int64_t diff = 0;
|
||||||
|
|
||||||
|
//friendly fire or not
|
||||||
|
if(attack.attacker->unitSide() == attack.defender->unitSide())
|
||||||
|
diff = -dealtDmgValue - receivedDmgValue;
|
||||||
|
else
|
||||||
|
diff = dealtDmgValue - receivedDmgValue;
|
||||||
|
|
||||||
|
//mind control
|
||||||
|
auto actualSide = getCbc()->playerToSide(getCbc()->battleGetOwner(attack.attacker));
|
||||||
|
if(actualSide && actualSide.get() != attack.attacker->unitSide())
|
||||||
|
diff = -diff;
|
||||||
|
return diff;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t AttackPossibility::attackValue() const
|
||||||
{
|
{
|
||||||
return damageDiff() + tacticImpact;
|
return damageDiff() + tacticImpact;
|
||||||
}
|
}
|
||||||
|
|
||||||
AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo &AttackInfo, const HypotheticChangesToBattleState &state, BattleHex hex)
|
AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo & attackInfo, BattleHex hex)
|
||||||
{
|
{
|
||||||
auto attacker = AttackInfo.attacker;
|
const std::string cachingStringBlocksRetaliation = "type_BLOCKS_RETALIATION";
|
||||||
auto enemy = AttackInfo.defender;
|
static const auto selectorBlocksRetaliation = Selector::type(Bonus::BLOCKS_RETALIATION);
|
||||||
|
|
||||||
const int remainingCounterAttacks = getValOr(state.counterAttacksLeft, enemy, enemy->counterAttacks.available());
|
const bool counterAttacksBlocked = attackInfo.attacker->hasBonus(selectorBlocksRetaliation, cachingStringBlocksRetaliation);
|
||||||
const bool counterAttacksBlocked = attacker->hasBonusOfType(Bonus::BLOCKS_RETALIATION) || enemy->hasBonusOfType(Bonus::NO_RETALIATION);
|
|
||||||
const int totalAttacks = 1 + AttackInfo.attackerBonuses->getBonuses(Selector::type(Bonus::ADDITIONAL_ATTACK), (Selector::effectRange (Bonus::NO_LIMIT).Or(Selector::effectRange(Bonus::ONLY_MELEE_FIGHT))))->totalValue();
|
|
||||||
|
|
||||||
AttackPossibility ap = {enemy, hex, AttackInfo, 0, 0, 0};
|
AttackPossibility ap(hex, attackInfo);
|
||||||
|
|
||||||
auto curBai = AttackInfo; //we'll modify here the stack counts
|
ap.attackerState = attackInfo.attacker->acquireState();
|
||||||
for(int i = 0; i < totalAttacks; i++)
|
|
||||||
|
const int totalAttacks = ap.attackerState->getTotalAttacks(attackInfo.shooting);
|
||||||
|
|
||||||
|
if(!attackInfo.shooting)
|
||||||
|
ap.attackerState->setPosition(hex);
|
||||||
|
|
||||||
|
auto defenderState = attackInfo.defender->acquireState();
|
||||||
|
ap.affectedUnits.push_back(defenderState);
|
||||||
|
|
||||||
|
for(int i = 0; i < totalAttacks; i++)
|
||||||
{
|
{
|
||||||
std::pair<ui32, ui32> retaliation(0,0);
|
TDmgRange retaliation(0,0);
|
||||||
auto attackDmg = getCbc()->battleEstimateDamage(CRandomGenerator::getDefault(), curBai, &retaliation);
|
auto attackDmg = getCbc()->battleEstimateDamage(ap.attack, &retaliation);
|
||||||
ap.damageDealt = (attackDmg.first + attackDmg.second) / 2;
|
|
||||||
ap.damageReceived = (retaliation.first + retaliation.second) / 2;
|
|
||||||
|
|
||||||
if(remainingCounterAttacks <= i || counterAttacksBlocked)
|
vstd::amin(attackDmg.first, defenderState->getAvailableHealth());
|
||||||
ap.damageReceived = 0;
|
vstd::amin(attackDmg.second, defenderState->getAvailableHealth());
|
||||||
|
|
||||||
curBai.attackerHealth = attacker->healthAfterAttacked(ap.damageReceived);
|
vstd::amin(retaliation.first, ap.attackerState->getAvailableHealth());
|
||||||
curBai.defenderHealth = enemy->healthAfterAttacked(ap.damageDealt);
|
vstd::amin(retaliation.second, ap.attackerState->getAvailableHealth());
|
||||||
if(curBai.attackerHealth.getCount() <= 0)
|
|
||||||
|
ap.damageDealt += (attackDmg.first + attackDmg.second) / 2;
|
||||||
|
|
||||||
|
ap.attackerState->afterAttack(attackInfo.shooting, false);
|
||||||
|
|
||||||
|
//FIXME: use ranged retaliation
|
||||||
|
if(!attackInfo.shooting && defenderState->ableToRetaliate() && !counterAttacksBlocked)
|
||||||
|
{
|
||||||
|
ap.damageReceived += (retaliation.first + retaliation.second) / 2;
|
||||||
|
defenderState->afterAttack(attackInfo.shooting, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ap.attackerState->damage(ap.damageReceived);
|
||||||
|
defenderState->damage(ap.damageDealt);
|
||||||
|
|
||||||
|
if(!ap.attackerState->alive() || !defenderState->alive())
|
||||||
break;
|
break;
|
||||||
//TODO what about defender? should we break? but in pessimistic scenario defender might be alive
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO other damage related to attack (eg. fire shield and other abilities)
|
//TODO other damage related to attack (eg. fire shield and other abilities)
|
||||||
|
|
||||||
return ap;
|
return ap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Priorities* AttackPossibility::priorities = nullptr;
|
|
||||||
|
|||||||
@@ -8,43 +8,29 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "../../lib/CStack.h"
|
#include "../../lib/battle/CUnitState.h"
|
||||||
#include "../../CCallback.h"
|
#include "../../CCallback.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
#include "StackWithBonuses.h"
|
||||||
|
|
||||||
struct HypotheticChangesToBattleState
|
|
||||||
{
|
|
||||||
std::map<const CStack *, const IBonusBearer *> bonusesOfStacks;
|
|
||||||
std::map<const CStack *, int> counterAttacksLeft;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Priorities
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::vector<double> resourceTypeBaseValues;
|
|
||||||
std::function<double(const CStack *)> stackEvaluator;
|
|
||||||
Priorities()
|
|
||||||
{
|
|
||||||
// range::copy(VLC->objh->resVals, std::back_inserter(resourceTypeBaseValues));
|
|
||||||
stackEvaluator = [](const CStack*){ return 1.0; };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class AttackPossibility
|
class AttackPossibility
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const CStack *enemy; //redundant (to attack.defender) but looks nice
|
|
||||||
BattleHex tile; //tile from which we attack
|
BattleHex tile; //tile from which we attack
|
||||||
BattleAttackInfo attack;
|
BattleAttackInfo attack;
|
||||||
|
|
||||||
int damageDealt;
|
std::shared_ptr<battle::CUnitState> attackerState;
|
||||||
int damageReceived; //usually by counter-attack
|
|
||||||
int tacticImpact;
|
|
||||||
|
|
||||||
int damageDiff() const;
|
std::vector<std::shared_ptr<battle::CUnitState>> affectedUnits;
|
||||||
int attackValue() const;
|
|
||||||
|
|
||||||
static AttackPossibility evaluate(const BattleAttackInfo &AttackInfo, const HypotheticChangesToBattleState &state, BattleHex hex);
|
int64_t damageDealt = 0;
|
||||||
static Priorities * priorities;
|
int64_t damageReceived = 0; //usually by counter-attack
|
||||||
|
int64_t tacticImpact = 0;
|
||||||
|
|
||||||
|
AttackPossibility(BattleHex tile_, const BattleAttackInfo & attack_);
|
||||||
|
|
||||||
|
int64_t damageDiff() const;
|
||||||
|
int64_t attackValue() const;
|
||||||
|
|
||||||
|
static AttackPossibility evaluate(const BattleAttackInfo & attackInfo, BattleHex hex);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
<Add option="-Wno-sign-compare" />
|
<Add option="-Wno-sign-compare" />
|
||||||
<Add option="-Wno-unused-parameter" />
|
<Add option="-Wno-unused-parameter" />
|
||||||
<Add option="-Wno-overloaded-virtual" />
|
<Add option="-Wno-overloaded-virtual" />
|
||||||
|
<Add option="-DBOOST_THREAD_USE_LIB" />
|
||||||
<Add option="-DBOOST_SYSTEM_NO_DEPRECATED" />
|
<Add option="-DBOOST_SYSTEM_NO_DEPRECATED" />
|
||||||
<Add option="-D_WIN32_WINNT=0x0501" />
|
<Add option="-D_WIN32_WINNT=0x0501" />
|
||||||
<Add option="-D_WIN32" />
|
<Add option="-D_WIN32" />
|
||||||
@@ -65,6 +66,7 @@
|
|||||||
<Add directory="../../include" />
|
<Add directory="../../include" />
|
||||||
</Compiler>
|
</Compiler>
|
||||||
<Linker>
|
<Linker>
|
||||||
|
<Add option="-lboost_thread$(#boost.libsuffix)" />
|
||||||
<Add option="-lboost_system$(#boost.libsuffix)" />
|
<Add option="-lboost_system$(#boost.libsuffix)" />
|
||||||
<Add option="-lVCMI_lib" />
|
<Add option="-lVCMI_lib" />
|
||||||
<Add directory="../.." />
|
<Add directory="../.." />
|
||||||
@@ -73,8 +75,11 @@
|
|||||||
<Unit filename="AttackPossibility.h" />
|
<Unit filename="AttackPossibility.h" />
|
||||||
<Unit filename="BattleAI.cpp" />
|
<Unit filename="BattleAI.cpp" />
|
||||||
<Unit filename="BattleAI.h" />
|
<Unit filename="BattleAI.h" />
|
||||||
|
<Unit filename="CMakeLists.txt" />
|
||||||
<Unit filename="EnemyInfo.cpp" />
|
<Unit filename="EnemyInfo.cpp" />
|
||||||
<Unit filename="EnemyInfo.h" />
|
<Unit filename="EnemyInfo.h" />
|
||||||
|
<Unit filename="PossibleSpellcast.cpp" />
|
||||||
|
<Unit filename="PossibleSpellcast.h" />
|
||||||
<Unit filename="PotentialTargets.cpp" />
|
<Unit filename="PotentialTargets.cpp" />
|
||||||
<Unit filename="PotentialTargets.h" />
|
<Unit filename="PotentialTargets.h" />
|
||||||
<Unit filename="StackWithBonuses.cpp" />
|
<Unit filename="StackWithBonuses.cpp" />
|
||||||
|
|||||||
+286
-144
@@ -9,13 +9,57 @@
|
|||||||
*/
|
*/
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "BattleAI.h"
|
#include "BattleAI.h"
|
||||||
|
|
||||||
|
#include <vstd/RNG.h>
|
||||||
|
|
||||||
#include "StackWithBonuses.h"
|
#include "StackWithBonuses.h"
|
||||||
#include "EnemyInfo.h"
|
#include "EnemyInfo.h"
|
||||||
|
#include "PossibleSpellcast.h"
|
||||||
|
#include "../../lib/CStopWatch.h"
|
||||||
|
#include "../../lib/CThreadHelper.h"
|
||||||
#include "../../lib/spells/CSpellHandler.h"
|
#include "../../lib/spells/CSpellHandler.h"
|
||||||
|
#include "../../lib/spells/ISpellMechanics.h"
|
||||||
|
#include "../../lib/CStack.h"//todo: remove
|
||||||
|
|
||||||
#define LOGL(text) print(text)
|
#define LOGL(text) print(text)
|
||||||
#define LOGFL(text, formattingEl) print(boost::str(boost::format(text) % formattingEl))
|
#define LOGFL(text, formattingEl) print(boost::str(boost::format(text) % formattingEl))
|
||||||
|
|
||||||
|
class RNGStub : public vstd::RNG
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
vstd::TRandI64 getInt64Range(int64_t lower, int64_t upper) override
|
||||||
|
{
|
||||||
|
return [=]()->int64_t
|
||||||
|
{
|
||||||
|
return (lower + upper)/2;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
vstd::TRand getDoubleRange(double lower, double upper) override
|
||||||
|
{
|
||||||
|
return [=]()->double
|
||||||
|
{
|
||||||
|
return (lower + upper)/2;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SpellTypes
|
||||||
|
{
|
||||||
|
ADVENTURE, BATTLE, OTHER
|
||||||
|
};
|
||||||
|
|
||||||
|
SpellTypes spellType(const CSpell * spell)
|
||||||
|
{
|
||||||
|
if(!spell->isCombatSpell() || spell->isCreatureAbility())
|
||||||
|
return SpellTypes::OTHER;
|
||||||
|
|
||||||
|
if(spell->isOffensiveSpell() || spell->hasEffects() || spell->hasBattleEffects())
|
||||||
|
return SpellTypes::BATTLE;
|
||||||
|
|
||||||
|
return SpellTypes::OTHER;
|
||||||
|
}
|
||||||
|
|
||||||
CBattleAI::CBattleAI()
|
CBattleAI::CBattleAI()
|
||||||
: side(-1), wasWaitingForRealize(false), wasUnlockingGs(false)
|
: side(-1), wasWaitingForRealize(false), wasUnlockingGs(false)
|
||||||
{
|
{
|
||||||
@@ -70,31 +114,38 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
|||||||
//spellcast may finish battle
|
//spellcast may finish battle
|
||||||
//send special preudo-action
|
//send special preudo-action
|
||||||
BattleAction cancel;
|
BattleAction cancel;
|
||||||
cancel.actionType = Battle::CANCEL;
|
cancel.actionType = EActionType::CANCEL;
|
||||||
return cancel;
|
return cancel;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(auto action = considerFleeingOrSurrendering())
|
if(auto action = considerFleeingOrSurrendering())
|
||||||
return *action;
|
return *action;
|
||||||
PotentialTargets targets(stack);
|
//best action is from effective owner point if view, we are effective owner as we received "activeStack"
|
||||||
|
|
||||||
|
HypotheticBattle hb(getCbc());
|
||||||
|
|
||||||
|
PotentialTargets targets(stack, &hb);
|
||||||
if(targets.possibleAttacks.size())
|
if(targets.possibleAttacks.size())
|
||||||
{
|
{
|
||||||
auto hlp = targets.bestAction();
|
auto hlp = targets.bestAction();
|
||||||
if(hlp.attack.shooting)
|
if(hlp.attack.shooting)
|
||||||
return BattleAction::makeShotAttack(stack, hlp.enemy);
|
return BattleAction::makeShotAttack(stack, hlp.attack.defender);
|
||||||
else
|
else
|
||||||
return BattleAction::makeMeleeAttack(stack, hlp.enemy, hlp.tile);
|
return BattleAction::makeMeleeAttack(stack, hlp.attack.defender->getPosition(), hlp.tile);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(stack->waited())
|
if(stack->waited())
|
||||||
{
|
{
|
||||||
//ThreatMap threatsToUs(stack); // These lines may be usefull but they are't used in the code.
|
//ThreatMap threatsToUs(stack); // These lines may be usefull but they are't used in the code.
|
||||||
auto dists = getCbc()->battleGetDistances(stack);
|
auto dists = getCbc()->battleGetDistances(stack, stack->getPosition());
|
||||||
const EnemyInfo &ei= *range::min_element(targets.unreachableEnemies, std::bind(isCloser, _1, _2, std::ref(dists)));
|
if(!targets.unreachableEnemies.empty())
|
||||||
if(distToNearestNeighbour(ei.s->position, dists) < GameConstants::BFIELD_SIZE)
|
|
||||||
{
|
{
|
||||||
return goTowards(stack, ei.s->position);
|
const EnemyInfo &ei= *range::min_element(targets.unreachableEnemies, std::bind(isCloser, _1, _2, std::ref(dists)));
|
||||||
|
if(distToNearestNeighbour(ei.s->getPosition(), dists) < GameConstants::BFIELD_SIZE)
|
||||||
|
{
|
||||||
|
return goTowards(stack, ei.s->getPosition());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -116,9 +167,15 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
|
|||||||
|
|
||||||
BattleAction CBattleAI::goTowards(const CStack * stack, BattleHex destination)
|
BattleAction CBattleAI::goTowards(const CStack * stack, BattleHex destination)
|
||||||
{
|
{
|
||||||
assert(destination.isValid());
|
if(!destination.isValid())
|
||||||
auto avHexes = cb->battleGetAvailableHexes(stack, false);
|
{
|
||||||
|
logAi->error("CBattleAI::goTowards: invalid destination");
|
||||||
|
return BattleAction::makeDefend(stack);
|
||||||
|
}
|
||||||
|
|
||||||
auto reachability = cb->getReachability(stack);
|
auto reachability = cb->getReachability(stack);
|
||||||
|
auto avHexes = cb->battleGetAvailableHexes(reachability, stack);
|
||||||
|
|
||||||
if(vstd::contains(avHexes, destination))
|
if(vstd::contains(avHexes, destination))
|
||||||
return BattleAction::makeMove(stack, destination);
|
return BattleAction::makeMove(stack, destination);
|
||||||
auto destNeighbours = destination.neighbouringTiles();
|
auto destNeighbours = destination.neighbouringTiles();
|
||||||
@@ -156,7 +213,12 @@ BattleAction CBattleAI::goTowards(const CStack * stack, BattleHex destination)
|
|||||||
BattleHex currentDest = bestNeighbor;
|
BattleHex currentDest = bestNeighbor;
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
assert(currentDest.isValid());
|
if(!currentDest.isValid())
|
||||||
|
{
|
||||||
|
logAi->error("CBattleAI::goTowards: internal error");
|
||||||
|
return BattleAction::makeDefend(stack);
|
||||||
|
}
|
||||||
|
|
||||||
if(vstd::contains(avHexes, currentDest))
|
if(vstd::contains(avHexes, currentDest))
|
||||||
return BattleAction::makeMove(stack, currentDest);
|
return BattleAction::makeMove(stack, currentDest);
|
||||||
currentDest = reachability.predecessors[currentDest];
|
currentDest = reachability.predecessors[currentDest];
|
||||||
@@ -166,22 +228,7 @@ BattleAction CBattleAI::goTowards(const CStack * stack, BattleHex destination)
|
|||||||
|
|
||||||
BattleAction CBattleAI::useCatapult(const CStack * stack)
|
BattleAction CBattleAI::useCatapult(const CStack * stack)
|
||||||
{
|
{
|
||||||
throw std::runtime_error("The method or operation is not implemented.");
|
throw std::runtime_error("CBattleAI::useCatapult is not implemented.");
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
enum SpellTypes
|
|
||||||
{
|
|
||||||
OFFENSIVE_SPELL, TIMED_EFFECT, OTHER
|
|
||||||
};
|
|
||||||
|
|
||||||
SpellTypes spellType(const CSpell *spell)
|
|
||||||
{
|
|
||||||
if (spell->isOffensiveSpell())
|
|
||||||
return OFFENSIVE_SPELL;
|
|
||||||
if (spell->hasEffects())
|
|
||||||
return TIMED_EFFECT;
|
|
||||||
return OTHER;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBattleAI::attemptCastingSpell()
|
void CBattleAI::attemptCastingSpell()
|
||||||
@@ -190,7 +237,7 @@ void CBattleAI::attemptCastingSpell()
|
|||||||
if(!hero)
|
if(!hero)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(cb->battleCanCastSpell(hero, ECastingMode::HERO_CASTING) != ESpellCastProblem::OK)
|
if(cb->battleCanCastSpell(hero, spells::Mode::HERO) != ESpellCastProblem::OK)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LOGL("Casting spells sounds like fun. Let's see...");
|
LOGL("Casting spells sounds like fun. Let's see...");
|
||||||
@@ -198,21 +245,28 @@ void CBattleAI::attemptCastingSpell()
|
|||||||
std::vector<const CSpell*> possibleSpells;
|
std::vector<const CSpell*> possibleSpells;
|
||||||
vstd::copy_if(VLC->spellh->objects, std::back_inserter(possibleSpells), [this, hero] (const CSpell *s) -> bool
|
vstd::copy_if(VLC->spellh->objects, std::back_inserter(possibleSpells), [this, hero] (const CSpell *s) -> bool
|
||||||
{
|
{
|
||||||
return s->canBeCast(getCbc().get(), ECastingMode::HERO_CASTING, hero) == ESpellCastProblem::OK;
|
return s->canBeCast(getCbc().get(), spells::Mode::HERO, hero);
|
||||||
});
|
});
|
||||||
LOGFL("I can cast %d spells.", possibleSpells.size());
|
LOGFL("I can cast %d spells.", possibleSpells.size());
|
||||||
|
|
||||||
vstd::erase_if(possibleSpells, [](const CSpell *s)
|
vstd::erase_if(possibleSpells, [](const CSpell *s)
|
||||||
{return spellType(s) == OTHER; });
|
{
|
||||||
LOGFL("I know about workings of %d of them.", possibleSpells.size());
|
return spellType(s) != SpellTypes::BATTLE;
|
||||||
|
});
|
||||||
|
|
||||||
|
LOGFL("I know how %d of them works.", possibleSpells.size());
|
||||||
|
|
||||||
//Get possible spell-target pairs
|
//Get possible spell-target pairs
|
||||||
std::vector<PossibleSpellcast> possibleCasts;
|
std::vector<PossibleSpellcast> possibleCasts;
|
||||||
for(auto spell : possibleSpells)
|
for(auto spell : possibleSpells)
|
||||||
{
|
{
|
||||||
for(auto hex : getTargetsToConsider(spell, hero))
|
spells::BattleCast temp(getCbc().get(), hero, spells::Mode::HERO, spell);
|
||||||
|
|
||||||
|
for(auto & target : temp.findPotentialTargets())
|
||||||
{
|
{
|
||||||
PossibleSpellcast ps = {spell, hex, 0};
|
PossibleSpellcast ps;
|
||||||
|
ps.dest = target;
|
||||||
|
ps.spell = spell;
|
||||||
possibleCasts.push_back(ps);
|
possibleCasts.push_back(ps);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -220,141 +274,229 @@ void CBattleAI::attemptCastingSpell()
|
|||||||
if(possibleCasts.empty())
|
if(possibleCasts.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::map<const CStack*, int> valueOfStack;
|
using ValueMap = PossibleSpellcast::ValueMap;
|
||||||
for(auto stack : cb->battleGetStacks())
|
|
||||||
|
auto evaluateQueue = [&](ValueMap & values, const std::vector<battle::Units> & queue, HypotheticBattle * state, size_t minTurnSpan, bool * enemyHadTurnOut) -> bool
|
||||||
{
|
{
|
||||||
PotentialTargets pt(stack);
|
bool firstRound = true;
|
||||||
valueOfStack[stack] = pt.bestActionValue();
|
bool enemyHadTurn = false;
|
||||||
|
size_t ourTurnSpan = 0;
|
||||||
|
|
||||||
|
bool stop = false;
|
||||||
|
|
||||||
|
for(auto & round : queue)
|
||||||
|
{
|
||||||
|
if(!firstRound)
|
||||||
|
state->nextRound(0);//todo: set actual value?
|
||||||
|
for(auto unit : round)
|
||||||
|
{
|
||||||
|
if(!vstd::contains(values, unit->unitId()))
|
||||||
|
values[unit->unitId()] = 0;
|
||||||
|
|
||||||
|
if(!unit->alive())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if(state->battleGetOwner(unit) != playerID)
|
||||||
|
{
|
||||||
|
enemyHadTurn = true;
|
||||||
|
|
||||||
|
if(!firstRound || state->battleCastSpells(unit->unitSide()) == 0)
|
||||||
|
{
|
||||||
|
//enemy could counter our spell at this point
|
||||||
|
//anyway, we do not know what enemy will do
|
||||||
|
//just stop evaluation
|
||||||
|
stop = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(!enemyHadTurn)
|
||||||
|
{
|
||||||
|
ourTurnSpan++;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->nextTurn(unit->unitId());
|
||||||
|
|
||||||
|
PotentialTargets pt(unit, state);
|
||||||
|
|
||||||
|
if(!pt.possibleAttacks.empty())
|
||||||
|
{
|
||||||
|
AttackPossibility ap = pt.bestAction();
|
||||||
|
|
||||||
|
auto swb = state->getForUpdate(unit->unitId());
|
||||||
|
*swb = *ap.attackerState;
|
||||||
|
|
||||||
|
if(ap.damageDealt > 0)
|
||||||
|
swb->removeUnitBonus(Bonus::UntilAttack);
|
||||||
|
if(ap.damageReceived > 0)
|
||||||
|
swb->removeUnitBonus(Bonus::UntilBeingAttacked);
|
||||||
|
|
||||||
|
for(auto affected : ap.affectedUnits)
|
||||||
|
{
|
||||||
|
swb = state->getForUpdate(affected->unitId());
|
||||||
|
*swb = *affected;
|
||||||
|
|
||||||
|
if(ap.damageDealt > 0)
|
||||||
|
swb->removeUnitBonus(Bonus::UntilBeingAttacked);
|
||||||
|
if(ap.damageReceived > 0 && ap.attack.defender->unitId() == affected->unitId())
|
||||||
|
swb->removeUnitBonus(Bonus::UntilAttack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto bav = pt.bestActionValue();
|
||||||
|
|
||||||
|
//best action is from effective owner`s point if view, we need to convert to our point if view
|
||||||
|
if(state->battleGetOwner(unit) != playerID)
|
||||||
|
bav = -bav;
|
||||||
|
values[unit->unitId()] += bav;
|
||||||
|
}
|
||||||
|
|
||||||
|
firstRound = false;
|
||||||
|
|
||||||
|
if(stop)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(enemyHadTurnOut)
|
||||||
|
*enemyHadTurnOut = enemyHadTurn;
|
||||||
|
|
||||||
|
return ourTurnSpan > minTurnSpan;
|
||||||
|
};
|
||||||
|
|
||||||
|
RNGStub rngStub;
|
||||||
|
|
||||||
|
ValueMap valueOfStack;
|
||||||
|
ValueMap healthOfStack;
|
||||||
|
|
||||||
|
TStacks all = cb->battleGetAllStacks(false);
|
||||||
|
|
||||||
|
for(auto unit : all)
|
||||||
|
{
|
||||||
|
healthOfStack[unit->unitId()] = unit->getAvailableHealth();
|
||||||
|
valueOfStack[unit->unitId()] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto evaluateSpellcast = [&] (const PossibleSpellcast &ps) -> int
|
auto amount = all.size();
|
||||||
|
|
||||||
|
std::vector<battle::Units> turnOrder;
|
||||||
|
|
||||||
|
cb->battleGetTurnOrder(turnOrder, amount, 2); //no more than 1 turn after current, each unit at least once
|
||||||
|
|
||||||
{
|
{
|
||||||
const int skillLevel = hero->getSpellSchoolLevel(ps.spell);
|
bool enemyHadTurn = false;
|
||||||
const int spellPower = hero->getPrimSkillLevel(PrimarySkill::SPELL_POWER);
|
|
||||||
switch(spellType(ps.spell))
|
HypotheticBattle state(cb);
|
||||||
|
evaluateQueue(valueOfStack, turnOrder, &state, 0, &enemyHadTurn);
|
||||||
|
|
||||||
|
if(!enemyHadTurn)
|
||||||
{
|
{
|
||||||
case OFFENSIVE_SPELL:
|
auto battleIsFinishedOpt = state.battleIsFinished();
|
||||||
{
|
|
||||||
int damageDealt = 0, damageReceived = 0;
|
if(battleIsFinishedOpt)
|
||||||
auto stacksSuffering = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, hero, skillLevel, ps.dest);
|
|
||||||
if(stacksSuffering.empty())
|
|
||||||
return -1;
|
|
||||||
for(auto stack : stacksSuffering)
|
|
||||||
{
|
{
|
||||||
const int dmg = ps.spell->calculateDamage(hero, stack, skillLevel, spellPower);
|
print("No need to cast a spell. Battle will finish soon.");
|
||||||
if(stack->owner == playerID)
|
return;
|
||||||
damageReceived += dmg;
|
|
||||||
else
|
|
||||||
damageDealt += dmg;
|
|
||||||
}
|
}
|
||||||
const int damageDiff = damageDealt - damageReceived * 10;
|
|
||||||
LOGFL("Casting %s on hex %d would deal { %d %d } damage points among %d stacks.",
|
|
||||||
ps.spell->name % ps.dest % damageDealt % damageReceived % stacksSuffering.size());
|
|
||||||
//TODO tactic effect too
|
|
||||||
return damageDiff;
|
|
||||||
}
|
}
|
||||||
case TIMED_EFFECT:
|
}
|
||||||
|
|
||||||
|
auto evaluateSpellcast = [&] (PossibleSpellcast * ps)
|
||||||
|
{
|
||||||
|
int64_t totalGain = 0;
|
||||||
|
|
||||||
|
HypotheticBattle state(cb);
|
||||||
|
|
||||||
|
spells::BattleCast cast(&state, hero, spells::Mode::HERO, ps->spell);
|
||||||
|
cast.target = ps->dest;
|
||||||
|
cast.cast(&state, rngStub);
|
||||||
|
ValueMap newHealthOfStack;
|
||||||
|
ValueMap newValueOfStack;
|
||||||
|
|
||||||
|
size_t ourUnits = 0;
|
||||||
|
|
||||||
|
for(auto unit : all)
|
||||||
{
|
{
|
||||||
auto stacksAffected = ps.spell->getAffectedStacks(cb.get(), ECastingMode::HERO_CASTING, hero, skillLevel, ps.dest);
|
newHealthOfStack[unit->unitId()] = unit->getAvailableHealth();
|
||||||
if(stacksAffected.empty())
|
newValueOfStack[unit->unitId()] = 0;
|
||||||
return -1;
|
|
||||||
int totalGain = 0;
|
if(state.battleGetOwner(unit) == playerID && unit->alive() && unit->willMove())
|
||||||
for(const CStack * sta : stacksAffected)
|
ourUnits++;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t minTurnSpan = ourUnits/3; //todo: tweak this
|
||||||
|
|
||||||
|
std::vector<battle::Units> newTurnOrder;
|
||||||
|
state.battleGetTurnOrder(newTurnOrder, amount, 2);
|
||||||
|
|
||||||
|
if(evaluateQueue(newValueOfStack, newTurnOrder, &state, minTurnSpan, nullptr))
|
||||||
|
{
|
||||||
|
for(auto unit : all)
|
||||||
{
|
{
|
||||||
StackWithBonuses swb;
|
auto newValue = getValOr(newValueOfStack, unit->unitId(), 0);
|
||||||
swb.stack = sta;
|
auto oldValue = getValOr(valueOfStack, unit->unitId(), 0);
|
||||||
//todo: handle effect actualization in HypotheticChangesToBattleState
|
|
||||||
ps.spell->getEffects(swb.bonusesToAdd, skillLevel, false, hero->getEnchantPower(ps.spell));
|
auto healthDiff = newHealthOfStack[unit->unitId()] - healthOfStack[unit->unitId()];
|
||||||
ps.spell->getEffects(swb.bonusesToAdd, skillLevel, true, hero->getEnchantPower(ps.spell));
|
|
||||||
HypotheticChangesToBattleState state;
|
if(unit->unitOwner() != playerID)
|
||||||
state.bonusesOfStacks[swb.stack] = &swb;
|
healthDiff = -healthDiff;
|
||||||
PotentialTargets pt(swb.stack, state);
|
|
||||||
auto newValue = pt.bestActionValue();
|
auto gain = newValue - oldValue + healthDiff;
|
||||||
auto oldValue = valueOfStack[swb.stack];
|
|
||||||
auto gain = newValue - oldValue;
|
if(gain != 0)
|
||||||
if(swb.stack->owner != playerID) //enemy
|
totalGain += gain;
|
||||||
gain = -gain;
|
|
||||||
LOGFL("Casting %s on %s would improve the stack by %d points (from %d to %d)",
|
|
||||||
ps.spell->name % sta->nodeName() % (gain) % (oldValue) % (newValue));
|
|
||||||
totalGain += gain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGFL("Total gain of cast %s at hex %d is %d", ps.spell->name % (ps.dest.hex) % (totalGain));
|
ps->value = totalGain;
|
||||||
return totalGain;
|
|
||||||
}
|
}
|
||||||
default:
|
else
|
||||||
assert(0);
|
{
|
||||||
return 0;
|
ps->value = -1;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::vector<std::function<void()>> tasks;
|
||||||
|
|
||||||
for(PossibleSpellcast & psc : possibleCasts)
|
for(PossibleSpellcast & psc : possibleCasts)
|
||||||
psc.value = evaluateSpellcast(psc);
|
{
|
||||||
auto pscValue = [] (const PossibleSpellcast &ps) -> int
|
tasks.push_back(std::bind(evaluateSpellcast, &psc));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t threadCount = boost::thread::hardware_concurrency();
|
||||||
|
|
||||||
|
if(threadCount == 0)
|
||||||
|
{
|
||||||
|
logGlobal->warn("No information of CPU cores available");
|
||||||
|
threadCount = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
CStopWatch timer;
|
||||||
|
|
||||||
|
CThreadHelper threadHelper(&tasks, threadCount);
|
||||||
|
threadHelper.run();
|
||||||
|
|
||||||
|
LOGFL("Evaluation took %d ms", timer.getDiff());
|
||||||
|
|
||||||
|
auto pscValue = [] (const PossibleSpellcast &ps) -> int64_t
|
||||||
{
|
{
|
||||||
return ps.value;
|
return ps.value;
|
||||||
};
|
};
|
||||||
auto castToPerform = *vstd::maxElementByFun(possibleCasts, pscValue);
|
auto castToPerform = *vstd::maxElementByFun(possibleCasts, pscValue);
|
||||||
LOGFL("Best spell is %s. Will cast.", castToPerform.spell->name);
|
|
||||||
BattleAction spellcast;
|
|
||||||
spellcast.actionType = Battle::HERO_SPELL;
|
|
||||||
spellcast.additionalInfo = castToPerform.spell->id;
|
|
||||||
spellcast.destinationTile = castToPerform.dest;
|
|
||||||
spellcast.side = side;
|
|
||||||
spellcast.stackNumber = (!side) ? -1 : -2;
|
|
||||||
cb->battleMakeAction(&spellcast);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<BattleHex> CBattleAI::getTargetsToConsider(const CSpell * spell, const ISpellCaster * caster) const
|
if(castToPerform.value > 0)
|
||||||
{
|
|
||||||
const CSpell::TargetInfo targetInfo(spell, caster->getSpellSchoolLevel(spell));
|
|
||||||
std::vector<BattleHex> ret;
|
|
||||||
if(targetInfo.massive || targetInfo.type == CSpell::NO_TARGET)
|
|
||||||
{
|
{
|
||||||
ret.push_back(BattleHex());
|
LOGFL("Best spell is %s (value %d). Will cast.", castToPerform.spell->name % castToPerform.value);
|
||||||
|
BattleAction spellcast;
|
||||||
|
spellcast.actionType = EActionType::HERO_SPELL;
|
||||||
|
spellcast.actionSubtype = castToPerform.spell->id;
|
||||||
|
spellcast.setTarget(castToPerform.dest);
|
||||||
|
spellcast.side = side;
|
||||||
|
spellcast.stackNumber = (!side) ? -1 : -2;
|
||||||
|
cb->battleMakeAction(&spellcast);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch(targetInfo.type)
|
LOGFL("Best spell is %s. But it is actually useless (value %d).", castToPerform.spell->name % castToPerform.value);
|
||||||
{
|
|
||||||
case CSpell::CREATURE:
|
|
||||||
{
|
|
||||||
for(const CStack * stack : getCbc()->battleAliveStacks())
|
|
||||||
{
|
|
||||||
bool immune = ESpellCastProblem::OK != spell->isImmuneByStack(caster, stack);
|
|
||||||
bool casterStack = stack->owner == caster->getOwner();
|
|
||||||
|
|
||||||
if(!immune)
|
|
||||||
switch (spell->positiveness)
|
|
||||||
{
|
|
||||||
case CSpell::POSITIVE:
|
|
||||||
if(casterStack || targetInfo.smart)
|
|
||||||
ret.push_back(stack->position);
|
|
||||||
break;
|
|
||||||
case CSpell::NEUTRAL:
|
|
||||||
ret.push_back(stack->position);
|
|
||||||
break;
|
|
||||||
case CSpell::NEGATIVE:
|
|
||||||
if(!casterStack || targetInfo.smart)
|
|
||||||
ret.push_back(stack->position);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CSpell::LOCATION:
|
|
||||||
{
|
|
||||||
for(int i = 0; i < GameConstants::BFIELD_SIZE; i++)
|
|
||||||
if(BattleHex(i).isAvailable())
|
|
||||||
ret.push_back(i);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int CBattleAI::distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances &dists, BattleHex *chosenHex)
|
int CBattleAI::distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances &dists, BattleHex *chosenHex)
|
||||||
@@ -374,18 +516,18 @@ int CBattleAI::distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDi
|
|||||||
|
|
||||||
void CBattleAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side)
|
void CBattleAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool Side)
|
||||||
{
|
{
|
||||||
print("battleStart called");
|
LOG_TRACE(logAi);
|
||||||
side = Side;
|
side = Side;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CBattleAI::isCloser(const EnemyInfo &ei1, const EnemyInfo &ei2, const ReachabilityInfo::TDistances &dists)
|
bool CBattleAI::isCloser(const EnemyInfo &ei1, const EnemyInfo &ei2, const ReachabilityInfo::TDistances &dists)
|
||||||
{
|
{
|
||||||
return distToNearestNeighbour(ei1.s->position, dists) < distToNearestNeighbour(ei2.s->position, dists);
|
return distToNearestNeighbour(ei1.s->getPosition(), dists) < distToNearestNeighbour(ei2.s->getPosition(), dists);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CBattleAI::print(const std::string &text) const
|
void CBattleAI::print(const std::string &text) const
|
||||||
{
|
{
|
||||||
logAi->trace("CBattleAI [%p]: %s", this, text);
|
logAi->trace("%s Battle AI[%p]: %s", playerID.getStr(), this, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering()
|
boost::optional<BattleAction> CBattleAI::considerFleeingOrSurrendering()
|
||||||
|
|||||||
+1
-13
@@ -45,13 +45,6 @@ struct CurrentOffensivePotential
|
|||||||
};
|
};
|
||||||
*/ // These lines may be usefull but they are't used in the code.
|
*/ // These lines may be usefull but they are't used in the code.
|
||||||
|
|
||||||
struct PossibleSpellcast
|
|
||||||
{
|
|
||||||
const CSpell *spell;
|
|
||||||
BattleHex dest;
|
|
||||||
si32 value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CBattleAI : public CBattleGameInterface
|
class CBattleAI : public CBattleGameInterface
|
||||||
{
|
{
|
||||||
int side;
|
int side;
|
||||||
@@ -72,7 +65,6 @@ public:
|
|||||||
|
|
||||||
boost::optional<BattleAction> considerFleeingOrSurrendering();
|
boost::optional<BattleAction> considerFleeingOrSurrendering();
|
||||||
|
|
||||||
std::vector<BattleHex> getTargetsToConsider(const CSpell *spell, const ISpellCaster * caster) const;
|
|
||||||
static int distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances& dists, BattleHex *chosenHex = nullptr);
|
static int distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances& dists, BattleHex *chosenHex = nullptr);
|
||||||
static bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const ReachabilityInfo::TDistances & dists);
|
static bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const ReachabilityInfo::TDistances & dists);
|
||||||
|
|
||||||
@@ -82,7 +74,7 @@ public:
|
|||||||
//void actionFinished(const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero
|
//void actionFinished(const BattleAction &action) override;//occurs AFTER every action taken by any stack or by the hero
|
||||||
//void actionStarted(const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero
|
//void actionStarted(const BattleAction &action) override;//occurs BEFORE every action taken by any stack or by the hero
|
||||||
//void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack
|
//void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack
|
||||||
//void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) override; //called when stack receives damage (after battleAttack())
|
//void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, const std::vector<MetaString> & battleLog) override; //called when stack receives damage (after battleAttack())
|
||||||
//void battleEnd(const BattleResult *br) override;
|
//void battleEnd(const BattleResult *br) override;
|
||||||
//void battleResultsApplied() override; //called when all effects of last battle are applied
|
//void battleResultsApplied() override; //called when all effects of last battle are applied
|
||||||
//void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied;
|
//void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied;
|
||||||
@@ -92,9 +84,5 @@ public:
|
|||||||
//void battleStacksEffectsSet(const SetStackEffect & sse) override;//called when a specific effect is set to stacks
|
//void battleStacksEffectsSet(const SetStackEffect & sse) override;//called when a specific effect is set to stacks
|
||||||
//void battleTriggerEffect(const BattleTriggerEffect & bte) override;
|
//void battleTriggerEffect(const BattleTriggerEffect & bte) override;
|
||||||
//void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right
|
//void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right
|
||||||
//void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom) override; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp
|
|
||||||
//void battleNewStackAppeared(const CStack * stack) override; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned
|
|
||||||
//void battleObstaclesRemoved(const std::set<si32> & removedObstacles) override; //called when a certain set of obstacles is removed from batlefield; IDs of them are given
|
|
||||||
//void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
|
//void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
|
||||||
//void battleStacksRemoved(const BattleStacksRemoved & bsr) override; //called when certain stack is completely removed from battlefield
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ set(battleAI_SRCS
|
|||||||
common.cpp
|
common.cpp
|
||||||
EnemyInfo.cpp
|
EnemyInfo.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
|
PossibleSpellcast.cpp
|
||||||
PotentialTargets.cpp
|
PotentialTargets.cpp
|
||||||
StackWithBonuses.cpp
|
StackWithBonuses.cpp
|
||||||
ThreatMap.cpp
|
ThreatMap.cpp
|
||||||
@@ -21,6 +22,7 @@ set(battleAI_HEADERS
|
|||||||
common.h
|
common.h
|
||||||
EnemyInfo.h
|
EnemyInfo.h
|
||||||
PotentialTargets.h
|
PotentialTargets.h
|
||||||
|
PossibleSpellcast.h
|
||||||
StackWithBonuses.h
|
StackWithBonuses.h
|
||||||
ThreatMap.h
|
ThreatMap.h
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -9,13 +9,11 @@
|
|||||||
*/
|
*/
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "EnemyInfo.h"
|
#include "EnemyInfo.h"
|
||||||
#include "../../lib/CRandomGenerator.h"
|
|
||||||
#include "../../CCallback.h"
|
|
||||||
#include "common.h"
|
|
||||||
|
|
||||||
void EnemyInfo::calcDmg(const CStack * ourStack)
|
#include "../../lib/battle/Unit.h"
|
||||||
|
|
||||||
|
bool EnemyInfo::operator==(const EnemyInfo & ei) const
|
||||||
{
|
{
|
||||||
TDmgRange retal, dmg = getCbc()->battleEstimateDamage(CRandomGenerator::getDefault(), ourStack, s, &retal);
|
return s->unitId() == ei.s->unitId();
|
||||||
adi = (dmg.first + dmg.second) / 2;
|
|
||||||
adr = (retal.first + retal.second) / 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-12
@@ -9,21 +9,16 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../../lib/battle/BattleHex.h"
|
namespace battle
|
||||||
|
{
|
||||||
class CStack;
|
class Unit;
|
||||||
|
}
|
||||||
|
|
||||||
class EnemyInfo
|
class EnemyInfo
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const CStack * s;
|
const battle::Unit * s;
|
||||||
int adi, adr;
|
EnemyInfo(const battle::Unit * _s) : s(_s)
|
||||||
std::vector<BattleHex> attackFrom; //for melee fight
|
|
||||||
EnemyInfo(const CStack * _s) : s(_s)
|
|
||||||
{}
|
{}
|
||||||
void calcDmg(const CStack * ourStack);
|
bool operator==(const EnemyInfo & ei) const;
|
||||||
bool operator==(const EnemyInfo& ei) const
|
|
||||||
{
|
|
||||||
return s == ei.s;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* PossibleSpellcast.cpp, part of VCMI engine
|
||||||
|
*
|
||||||
|
* Authors: listed in file AUTHORS in main folder
|
||||||
|
*
|
||||||
|
* License: GNU General Public License v2.0 or later
|
||||||
|
* Full text of license available in license.txt file, in main folder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "StdInc.h"
|
||||||
|
|
||||||
|
#include "PossibleSpellcast.h"
|
||||||
|
|
||||||
|
PossibleSpellcast::PossibleSpellcast()
|
||||||
|
: spell(nullptr),
|
||||||
|
dest(),
|
||||||
|
value(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
PossibleSpellcast::~PossibleSpellcast() = default;
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* PossibleSpellcast.h, part of VCMI engine
|
||||||
|
*
|
||||||
|
* Authors: listed in file AUTHORS in main folder
|
||||||
|
*
|
||||||
|
* License: GNU General Public License v2.0 or later
|
||||||
|
* Full text of license available in license.txt file, in main folder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../../lib/battle/Destination.h"
|
||||||
|
#include "../../lib/spells/Magic.h"
|
||||||
|
|
||||||
|
class CSpell;
|
||||||
|
|
||||||
|
class PossibleSpellcast
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using ValueMap = std::map<uint32_t, int64_t>;
|
||||||
|
|
||||||
|
const CSpell * spell;
|
||||||
|
spells::Target dest;
|
||||||
|
int64_t value;
|
||||||
|
|
||||||
|
PossibleSpellcast();
|
||||||
|
virtual ~PossibleSpellcast();
|
||||||
|
};
|
||||||
@@ -9,51 +9,76 @@
|
|||||||
*/
|
*/
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "PotentialTargets.h"
|
#include "PotentialTargets.h"
|
||||||
|
#include "../../lib/CStack.h"//todo: remove
|
||||||
|
|
||||||
PotentialTargets::PotentialTargets(const CStack * attacker, const HypotheticChangesToBattleState & state)
|
PotentialTargets::PotentialTargets(const battle::Unit * attacker, const HypotheticBattle * state)
|
||||||
{
|
{
|
||||||
auto dists = getCbc()->battleGetDistances(attacker);
|
auto attIter = state->stackStates.find(attacker->unitId());
|
||||||
auto avHexes = getCbc()->battleGetAvailableHexes(attacker, false);
|
const battle::Unit * attackerInfo = (attIter == state->stackStates.end()) ? attacker : attIter->second.get();
|
||||||
|
|
||||||
for(const CStack *enemy : getCbc()->battleGetStacks())
|
auto reachability = state->getReachability(attackerInfo);
|
||||||
|
auto avHexes = state->battleGetAvailableHexes(reachability, attackerInfo);
|
||||||
|
|
||||||
|
//FIXME: this should part of battleGetAvailableHexes
|
||||||
|
bool forceTarget = false;
|
||||||
|
const battle::Unit * forcedTarget = nullptr;
|
||||||
|
BattleHex forcedHex;
|
||||||
|
|
||||||
|
if(attackerInfo->hasBonusOfType(Bonus::ATTACKS_NEAREST_CREATURE))
|
||||||
{
|
{
|
||||||
//Consider only stacks of different owner
|
forceTarget = true;
|
||||||
if(enemy->side == attacker->side)
|
auto nearest = state->getNearestStack(attackerInfo);
|
||||||
|
|
||||||
|
if(nearest.first != nullptr)
|
||||||
|
{
|
||||||
|
forcedTarget = nearest.first;
|
||||||
|
forcedHex = nearest.second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto aliveUnits = state->battleGetUnitsIf([=](const battle::Unit * unit)
|
||||||
|
{
|
||||||
|
return unit->isValidTarget() && unit->unitId() != attackerInfo->unitId();
|
||||||
|
});
|
||||||
|
|
||||||
|
for(auto defender : aliveUnits)
|
||||||
|
{
|
||||||
|
if(!forceTarget && !state->battleMatchOwner(attackerInfo, defender))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto GenerateAttackInfo = [&](bool shooting, BattleHex hex) -> AttackPossibility
|
auto GenerateAttackInfo = [&](bool shooting, BattleHex hex) -> AttackPossibility
|
||||||
{
|
{
|
||||||
auto bai = BattleAttackInfo(attacker, enemy, shooting);
|
auto bai = BattleAttackInfo(attackerInfo, defender, shooting);
|
||||||
bai.attackerBonuses = getValOr(state.bonusesOfStacks, bai.attacker, bai.attacker);
|
|
||||||
bai.defenderBonuses = getValOr(state.bonusesOfStacks, bai.defender, bai.defender);
|
|
||||||
|
|
||||||
if(hex.isValid())
|
if(hex.isValid() && !shooting)
|
||||||
{
|
bai.chargedFields = reachability.distances[hex];
|
||||||
assert(dists[hex] <= attacker->Speed());
|
|
||||||
bai.chargedFields = dists[hex];
|
|
||||||
}
|
|
||||||
|
|
||||||
return AttackPossibility::evaluate(bai, state, hex);
|
return AttackPossibility::evaluate(bai, hex);
|
||||||
};
|
};
|
||||||
|
|
||||||
if(getCbc()->battleCanShoot(attacker, enemy->position))
|
if(forceTarget)
|
||||||
|
{
|
||||||
|
if(forcedTarget && defender->unitId() == forcedTarget->unitId())
|
||||||
|
possibleAttacks.push_back(GenerateAttackInfo(false, forcedHex));
|
||||||
|
else
|
||||||
|
unreachableEnemies.push_back(defender);
|
||||||
|
}
|
||||||
|
else if(state->battleCanShoot(attackerInfo, defender->getPosition()))
|
||||||
{
|
{
|
||||||
possibleAttacks.push_back(GenerateAttackInfo(true, BattleHex::INVALID));
|
possibleAttacks.push_back(GenerateAttackInfo(true, BattleHex::INVALID));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for(BattleHex hex : avHexes)
|
for(BattleHex hex : avHexes)
|
||||||
if(CStack::isMeleeAttackPossible(attacker, enemy, hex))
|
if(CStack::isMeleeAttackPossible(attackerInfo, defender, hex))
|
||||||
possibleAttacks.push_back(GenerateAttackInfo(false, hex));
|
possibleAttacks.push_back(GenerateAttackInfo(false, hex));
|
||||||
|
|
||||||
if(!vstd::contains_if(possibleAttacks, [=](const AttackPossibility &pa) { return pa.enemy == enemy; }))
|
if(!vstd::contains_if(possibleAttacks, [=](const AttackPossibility & pa) { return pa.attack.defender->unitId() == defender->unitId(); }))
|
||||||
unreachableEnemies.push_back(enemy);
|
unreachableEnemies.push_back(defender);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int PotentialTargets::bestActionValue() const
|
int PotentialTargets::bestActionValue() const
|
||||||
{
|
{
|
||||||
if(possibleAttacks.empty())
|
if(possibleAttacks.empty())
|
||||||
|
|||||||
@@ -14,12 +14,10 @@ class PotentialTargets
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::vector<AttackPossibility> possibleAttacks;
|
std::vector<AttackPossibility> possibleAttacks;
|
||||||
std::vector<const CStack *> unreachableEnemies;
|
std::vector<const battle::Unit *> unreachableEnemies;
|
||||||
|
|
||||||
//std::function<AttackPossibility(bool,BattleHex)> GenerateAttackInfo; //args: shooting, destHex
|
|
||||||
|
|
||||||
PotentialTargets(){};
|
PotentialTargets(){};
|
||||||
PotentialTargets(const CStack *attacker, const HypotheticChangesToBattleState &state = HypotheticChangesToBattleState());
|
PotentialTargets(const battle::Unit * attacker, const HypotheticBattle * state);
|
||||||
|
|
||||||
AttackPossibility bestAction() const;
|
AttackPossibility bestAction() const;
|
||||||
int bestActionValue() const;
|
int bestActionValue() const;
|
||||||
|
|||||||
@@ -9,21 +9,383 @@
|
|||||||
*/
|
*/
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "StackWithBonuses.h"
|
#include "StackWithBonuses.h"
|
||||||
|
#include "../../lib/NetPacksBase.h"
|
||||||
#include "../../lib/CStack.h"
|
#include "../../lib/CStack.h"
|
||||||
|
|
||||||
|
void actualizeEffect(TBonusListPtr target, const Bonus & ef)
|
||||||
|
{
|
||||||
|
for(auto bonus : *target) //TODO: optimize
|
||||||
|
{
|
||||||
|
if(bonus->source == Bonus::SPELL_EFFECT && bonus->type == ef.type && bonus->subtype == ef.subtype)
|
||||||
|
{
|
||||||
|
bonus->turnsRemain = std::max(bonus->turnsRemain, ef.turnsRemain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const TBonusListPtr StackWithBonuses::getAllBonuses(const CSelector &selector, const CSelector &limit,
|
StackWithBonuses::StackWithBonuses(const HypotheticBattle * Owner, const CStack * Stack)
|
||||||
const CBonusSystemNode * root, const std::string & cachingStr) const
|
: battle::CUnitState(),
|
||||||
|
origBearer(Stack),
|
||||||
|
owner(Owner),
|
||||||
|
type(Stack->unitType()),
|
||||||
|
baseAmount(Stack->unitBaseAmount()),
|
||||||
|
id(Stack->unitId()),
|
||||||
|
side(Stack->unitSide()),
|
||||||
|
player(Stack->unitOwner()),
|
||||||
|
slot(Stack->unitSlot())
|
||||||
|
{
|
||||||
|
localInit(Owner);
|
||||||
|
|
||||||
|
battle::CUnitState::operator=(*Stack);
|
||||||
|
}
|
||||||
|
|
||||||
|
StackWithBonuses::StackWithBonuses(const HypotheticBattle * Owner, const battle::UnitInfo & info)
|
||||||
|
: battle::CUnitState(),
|
||||||
|
origBearer(nullptr),
|
||||||
|
owner(Owner),
|
||||||
|
baseAmount(info.count),
|
||||||
|
id(info.id),
|
||||||
|
side(info.side),
|
||||||
|
slot(SlotID::SUMMONED_SLOT_PLACEHOLDER)
|
||||||
|
{
|
||||||
|
type = info.type.toCreature();
|
||||||
|
origBearer = type;
|
||||||
|
|
||||||
|
player = Owner->getSidePlayer(side);
|
||||||
|
|
||||||
|
position = info.position;
|
||||||
|
summoned = info.summoned;
|
||||||
|
localInit(Owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
StackWithBonuses::~StackWithBonuses() = default;
|
||||||
|
|
||||||
|
StackWithBonuses & StackWithBonuses::operator=(const battle::CUnitState & other)
|
||||||
|
{
|
||||||
|
battle::CUnitState::operator=(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CCreature * StackWithBonuses::unitType() const
|
||||||
|
{
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t StackWithBonuses::unitBaseAmount() const
|
||||||
|
{
|
||||||
|
return baseAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t StackWithBonuses::unitId() const
|
||||||
|
{
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui8 StackWithBonuses::unitSide() const
|
||||||
|
{
|
||||||
|
return side;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerColor StackWithBonuses::unitOwner() const
|
||||||
|
{
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotID StackWithBonuses::unitSlot() const
|
||||||
|
{
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TBonusListPtr StackWithBonuses::getAllBonuses(const CSelector & selector, const CSelector & limit,
|
||||||
|
const CBonusSystemNode * root, const std::string & cachingStr) const
|
||||||
{
|
{
|
||||||
TBonusListPtr ret = std::make_shared<BonusList>();
|
TBonusListPtr ret = std::make_shared<BonusList>();
|
||||||
const TBonusListPtr originalList = stack->getAllBonuses(selector, limit, root, cachingStr);
|
const TBonusListPtr originalList = origBearer->getAllBonuses(selector, limit, root, cachingStr);
|
||||||
range::copy(*originalList, std::back_inserter(*ret));
|
|
||||||
for(auto &bonus : bonusesToAdd)
|
vstd::copy_if(*originalList, std::back_inserter(*ret), [this](const std::shared_ptr<Bonus> & b)
|
||||||
|
{
|
||||||
|
return !vstd::contains(bonusesToRemove, b);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
for(const Bonus & bonus : bonusesToUpdate)
|
||||||
|
{
|
||||||
|
if(selector(&bonus) && (!limit || !limit(&bonus)))
|
||||||
|
{
|
||||||
|
if(ret->getFirst(Selector::source(Bonus::SPELL_EFFECT, bonus.sid).And(Selector::typeSubtype(bonus.type, bonus.subtype))))
|
||||||
|
{
|
||||||
|
actualizeEffect(ret, bonus);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto b = std::make_shared<Bonus>(bonus);
|
||||||
|
ret->push_back(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto & bonus : bonusesToAdd)
|
||||||
{
|
{
|
||||||
auto b = std::make_shared<Bonus>(bonus);
|
auto b = std::make_shared<Bonus>(bonus);
|
||||||
if(selector(b.get()) && (!limit || !limit(b.get())))
|
if(selector(b.get()) && (!limit || !limit(b.get())))
|
||||||
ret->push_back(b);
|
ret->push_back(b);
|
||||||
}
|
}
|
||||||
//TODO limiters?
|
//TODO limiters?
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t StackWithBonuses::getTreeVersion() const
|
||||||
|
{
|
||||||
|
return owner->getTreeVersion();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackWithBonuses::addUnitBonus(const std::vector<Bonus> & bonus)
|
||||||
|
{
|
||||||
|
vstd::concatenate(bonusesToAdd, bonus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackWithBonuses::updateUnitBonus(const std::vector<Bonus> & bonus)
|
||||||
|
{
|
||||||
|
//TODO: optimize, actualize to last value
|
||||||
|
|
||||||
|
vstd::concatenate(bonusesToUpdate, bonus);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackWithBonuses::removeUnitBonus(const std::vector<Bonus> & bonus)
|
||||||
|
{
|
||||||
|
for(auto & one : bonus)
|
||||||
|
{
|
||||||
|
CSelector selector([&one](const Bonus * b) -> bool
|
||||||
|
{
|
||||||
|
//compare everything but turnsRemain, limiter and propagator
|
||||||
|
return one.duration == b->duration
|
||||||
|
&& one.type == b->type
|
||||||
|
&& one.subtype == b->subtype
|
||||||
|
&& one.source == b->source
|
||||||
|
&& one.val == b->val
|
||||||
|
&& one.sid == b->sid
|
||||||
|
&& one.valType == b->valType
|
||||||
|
&& one.additionalInfo == b->additionalInfo
|
||||||
|
&& one.effectRange == b->effectRange
|
||||||
|
&& one.description == b->description;
|
||||||
|
});
|
||||||
|
|
||||||
|
removeUnitBonus(selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackWithBonuses::removeUnitBonus(const CSelector & selector)
|
||||||
|
{
|
||||||
|
TBonusListPtr toRemove = origBearer->getBonuses(selector);
|
||||||
|
|
||||||
|
for(auto b : *toRemove)
|
||||||
|
bonusesToRemove.insert(b);
|
||||||
|
|
||||||
|
vstd::erase_if(bonusesToAdd, [&](const Bonus & b){return selector(&b);});
|
||||||
|
vstd::erase_if(bonusesToUpdate, [&](const Bonus & b){return selector(&b);});
|
||||||
|
}
|
||||||
|
|
||||||
|
void StackWithBonuses::spendMana(const spells::PacketSender * server, const int spellCost) const
|
||||||
|
{
|
||||||
|
//TODO: evaluate cast use
|
||||||
|
}
|
||||||
|
|
||||||
|
HypotheticBattle::HypotheticBattle(Subject realBattle)
|
||||||
|
: BattleProxy(realBattle),
|
||||||
|
bonusTreeVersion(1)
|
||||||
|
{
|
||||||
|
auto activeUnit = realBattle->battleActiveUnit();
|
||||||
|
activeUnitId = activeUnit ? activeUnit->unitId() : -1;
|
||||||
|
|
||||||
|
nextId = 0xF0000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HypotheticBattle::unitHasAmmoCart(const battle::Unit * unit) const
|
||||||
|
{
|
||||||
|
//FIXME: check ammocart alive state here
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerColor HypotheticBattle::unitEffectiveOwner(const battle::Unit * unit) const
|
||||||
|
{
|
||||||
|
return battleGetOwner(unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<StackWithBonuses> HypotheticBattle::getForUpdate(uint32_t id)
|
||||||
|
{
|
||||||
|
auto iter = stackStates.find(id);
|
||||||
|
|
||||||
|
if(iter == stackStates.end())
|
||||||
|
{
|
||||||
|
const CStack * s = subject->battleGetStackByID(id, false);
|
||||||
|
|
||||||
|
auto ret = std::make_shared<StackWithBonuses>(this, s);
|
||||||
|
stackStates[id] = ret;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
battle::Units HypotheticBattle::getUnitsIf(battle::UnitFilter predicate) const
|
||||||
|
{
|
||||||
|
battle::Units proxyed = BattleProxy::getUnitsIf(predicate);
|
||||||
|
|
||||||
|
battle::Units ret;
|
||||||
|
ret.reserve(proxyed.size());
|
||||||
|
|
||||||
|
for(auto unit : proxyed)
|
||||||
|
{
|
||||||
|
//unit was not changed, trust proxyed data
|
||||||
|
if(stackStates.find(unit->unitId()) == stackStates.end())
|
||||||
|
ret.push_back(unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto id_unit : stackStates)
|
||||||
|
{
|
||||||
|
if(predicate(id_unit.second.get()))
|
||||||
|
ret.push_back(id_unit.second.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t HypotheticBattle::getActiveStackID() const
|
||||||
|
{
|
||||||
|
return activeUnitId;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HypotheticBattle::nextRound(int32_t roundNr)
|
||||||
|
{
|
||||||
|
//TODO:HypotheticBattle::nextRound
|
||||||
|
|
||||||
|
for(auto unit : battleAliveUnits())
|
||||||
|
{
|
||||||
|
auto forUpdate = getForUpdate(unit->unitId());
|
||||||
|
//TODO: update Bonus::NTurns effects
|
||||||
|
forUpdate->afterNewRound();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HypotheticBattle::nextTurn(uint32_t unitId)
|
||||||
|
{
|
||||||
|
activeUnitId = unitId;
|
||||||
|
auto unit = getForUpdate(unitId);
|
||||||
|
|
||||||
|
unit->removeUnitBonus(Bonus::UntilGetsTurn);
|
||||||
|
|
||||||
|
unit->afterGetsTurn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void HypotheticBattle::addUnit(uint32_t id, const JsonNode & data)
|
||||||
|
{
|
||||||
|
battle::UnitInfo info;
|
||||||
|
info.load(id, data);
|
||||||
|
std::shared_ptr<StackWithBonuses> newUnit = std::make_shared<StackWithBonuses>(this, info);
|
||||||
|
stackStates[newUnit->unitId()] = newUnit;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HypotheticBattle::moveUnit(uint32_t id, BattleHex destination)
|
||||||
|
{
|
||||||
|
std::shared_ptr<StackWithBonuses> changed = getForUpdate(id);
|
||||||
|
changed->position = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HypotheticBattle::setUnitState(uint32_t id, const JsonNode & data, int64_t healthDelta)
|
||||||
|
{
|
||||||
|
std::shared_ptr<StackWithBonuses> changed = getForUpdate(id);
|
||||||
|
|
||||||
|
changed->load(data);
|
||||||
|
|
||||||
|
if(healthDelta < 0)
|
||||||
|
{
|
||||||
|
changed->removeUnitBonus(Bonus::UntilBeingAttacked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HypotheticBattle::removeUnit(uint32_t id)
|
||||||
|
{
|
||||||
|
std::set<uint32_t> ids;
|
||||||
|
ids.insert(id);
|
||||||
|
|
||||||
|
while(!ids.empty())
|
||||||
|
{
|
||||||
|
auto toRemoveId = *ids.begin();
|
||||||
|
|
||||||
|
auto toRemove = getForUpdate(toRemoveId);
|
||||||
|
|
||||||
|
if(!toRemove->ghost)
|
||||||
|
{
|
||||||
|
toRemove->onRemoved();
|
||||||
|
|
||||||
|
//TODO: emulate detachFromAll() somehow
|
||||||
|
|
||||||
|
//stack may be removed instantly (not being killed first)
|
||||||
|
//handle clone remove also here
|
||||||
|
if(toRemove->cloneID >= 0)
|
||||||
|
{
|
||||||
|
ids.insert(toRemove->cloneID);
|
||||||
|
toRemove->cloneID = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: cleanup remaining clone links if any
|
||||||
|
// for(auto s : stacks)
|
||||||
|
// {
|
||||||
|
// if(s->cloneID == toRemoveId)
|
||||||
|
// s->cloneID = -1;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
ids.erase(toRemoveId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HypotheticBattle::addUnitBonus(uint32_t id, const std::vector<Bonus> & bonus)
|
||||||
|
{
|
||||||
|
getForUpdate(id)->addUnitBonus(bonus);
|
||||||
|
bonusTreeVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HypotheticBattle::updateUnitBonus(uint32_t id, const std::vector<Bonus> & bonus)
|
||||||
|
{
|
||||||
|
getForUpdate(id)->updateUnitBonus(bonus);
|
||||||
|
bonusTreeVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HypotheticBattle::removeUnitBonus(uint32_t id, const std::vector<Bonus> & bonus)
|
||||||
|
{
|
||||||
|
getForUpdate(id)->removeUnitBonus(bonus);
|
||||||
|
bonusTreeVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HypotheticBattle::setWallState(int partOfWall, si8 state)
|
||||||
|
{
|
||||||
|
//TODO:HypotheticBattle::setWallState
|
||||||
|
}
|
||||||
|
|
||||||
|
void HypotheticBattle::addObstacle(const ObstacleChanges & changes)
|
||||||
|
{
|
||||||
|
//TODO:HypotheticBattle::addObstacle
|
||||||
|
}
|
||||||
|
|
||||||
|
void HypotheticBattle::removeObstacle(uint32_t id)
|
||||||
|
{
|
||||||
|
//TODO:HypotheticBattle::removeObstacle
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t HypotheticBattle::nextUnitId() const
|
||||||
|
{
|
||||||
|
return nextId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t HypotheticBattle::getActualDamage(const TDmgRange & damage, int32_t attackerCount, vstd::RNG & rng) const
|
||||||
|
{
|
||||||
|
return (damage.first + damage.second) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t HypotheticBattle::getTreeVersion() const
|
||||||
|
{
|
||||||
|
return getBattleNode()->getTreeVersion() + bonusTreeVersion;
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,15 +9,105 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "../../lib/HeroBonus.h"
|
#include "../../lib/HeroBonus.h"
|
||||||
|
#include "../../lib/battle/BattleProxy.h"
|
||||||
|
#include "../../lib/battle/CUnitState.h"
|
||||||
|
|
||||||
|
class HypotheticBattle;
|
||||||
class CStack;
|
class CStack;
|
||||||
|
|
||||||
class StackWithBonuses : public IBonusBearer
|
class StackWithBonuses : public battle::CUnitState, public virtual IBonusBearer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const CStack *stack;
|
|
||||||
mutable std::vector<Bonus> bonusesToAdd;
|
|
||||||
|
|
||||||
virtual const TBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit,
|
std::vector<Bonus> bonusesToAdd;
|
||||||
const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const override;
|
std::vector<Bonus> bonusesToUpdate;
|
||||||
|
std::set<std::shared_ptr<Bonus>> bonusesToRemove;
|
||||||
|
|
||||||
|
StackWithBonuses(const HypotheticBattle * Owner, const CStack * Stack);
|
||||||
|
|
||||||
|
StackWithBonuses(const HypotheticBattle * Owner, const battle::UnitInfo & info);
|
||||||
|
|
||||||
|
virtual ~StackWithBonuses();
|
||||||
|
|
||||||
|
StackWithBonuses & operator= (const battle::CUnitState & other);
|
||||||
|
|
||||||
|
///IUnitInfo
|
||||||
|
const CCreature * unitType() const override;
|
||||||
|
|
||||||
|
int32_t unitBaseAmount() const override;
|
||||||
|
|
||||||
|
uint32_t unitId() const override;
|
||||||
|
ui8 unitSide() const override;
|
||||||
|
PlayerColor unitOwner() const override;
|
||||||
|
SlotID unitSlot() const override;
|
||||||
|
|
||||||
|
///IBonusBearer
|
||||||
|
const TBonusListPtr getAllBonuses(const CSelector & selector, const CSelector & limit,
|
||||||
|
const CBonusSystemNode * root = nullptr, const std::string & cachingStr = "") const override;
|
||||||
|
|
||||||
|
int64_t getTreeVersion() const override;
|
||||||
|
|
||||||
|
void addUnitBonus(const std::vector<Bonus> & bonus);
|
||||||
|
void updateUnitBonus(const std::vector<Bonus> & bonus);
|
||||||
|
void removeUnitBonus(const std::vector<Bonus> & bonus);
|
||||||
|
|
||||||
|
void removeUnitBonus(const CSelector & selector);
|
||||||
|
|
||||||
|
void spendMana(const spells::PacketSender * server, const int spellCost) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const IBonusBearer * origBearer;
|
||||||
|
const HypotheticBattle * owner;
|
||||||
|
|
||||||
|
const CCreature * type;
|
||||||
|
ui32 baseAmount;
|
||||||
|
uint32_t id;
|
||||||
|
ui8 side;
|
||||||
|
PlayerColor player;
|
||||||
|
SlotID slot;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HypotheticBattle : public BattleProxy, public battle::IUnitEnvironment
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::map<uint32_t, std::shared_ptr<StackWithBonuses>> stackStates;
|
||||||
|
|
||||||
|
HypotheticBattle(Subject realBattle);
|
||||||
|
|
||||||
|
bool unitHasAmmoCart(const battle::Unit * unit) const override;
|
||||||
|
PlayerColor unitEffectiveOwner(const battle::Unit * unit) const override;
|
||||||
|
|
||||||
|
std::shared_ptr<StackWithBonuses> getForUpdate(uint32_t id);
|
||||||
|
|
||||||
|
int32_t getActiveStackID() const override;
|
||||||
|
|
||||||
|
battle::Units getUnitsIf(battle::UnitFilter predicate) const override;
|
||||||
|
|
||||||
|
void nextRound(int32_t roundNr) override;
|
||||||
|
void nextTurn(uint32_t unitId) override;
|
||||||
|
|
||||||
|
void addUnit(uint32_t id, const JsonNode & data) override;
|
||||||
|
void setUnitState(uint32_t id, const JsonNode & data, int64_t healthDelta) override;
|
||||||
|
void moveUnit(uint32_t id, BattleHex destination) override;
|
||||||
|
void removeUnit(uint32_t id) override;
|
||||||
|
|
||||||
|
void addUnitBonus(uint32_t id, const std::vector<Bonus> & bonus) override;
|
||||||
|
void updateUnitBonus(uint32_t id, const std::vector<Bonus> & bonus) override;
|
||||||
|
void removeUnitBonus(uint32_t id, const std::vector<Bonus> & bonus) override;
|
||||||
|
|
||||||
|
void setWallState(int partOfWall, si8 state) override;
|
||||||
|
|
||||||
|
void addObstacle(const ObstacleChanges & changes) override;
|
||||||
|
void removeObstacle(uint32_t id) override;
|
||||||
|
|
||||||
|
uint32_t nextUnitId() const override;
|
||||||
|
|
||||||
|
int64_t getActualDamage(const TDmgRange & damage, int32_t attackerCount, vstd::RNG & rng) const override;
|
||||||
|
|
||||||
|
int64_t getTreeVersion() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int32_t bonusTreeVersion;
|
||||||
|
int32_t activeUnitId;
|
||||||
|
mutable uint32_t nextId;
|
||||||
};
|
};
|
||||||
|
|||||||
+14
-35
@@ -53,7 +53,7 @@ struct EnemyInfo
|
|||||||
{}
|
{}
|
||||||
void calcDmg(const CStack * ourStack)
|
void calcDmg(const CStack * ourStack)
|
||||||
{
|
{
|
||||||
TDmgRange retal, dmg = cbc->battleEstimateDamage(CRandomGenerator::getDefault(), ourStack, s, &retal);
|
TDmgRange retal, dmg = cbc->battleEstimateDamage(ourStack, s, &retal);
|
||||||
adi = (dmg.first + dmg.second) / 2;
|
adi = (dmg.first + dmg.second) / 2;
|
||||||
adr = (retal.first + retal.second) / 2;
|
adr = (retal.first + retal.second) / 2;
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ int distToNearestNeighbour(BattleHex hex, const ReachabilityInfo::TDistances& di
|
|||||||
|
|
||||||
bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const ReachabilityInfo::TDistances & dists)
|
bool isCloser(const EnemyInfo & ei1, const EnemyInfo & ei2, const ReachabilityInfo::TDistances & dists)
|
||||||
{
|
{
|
||||||
return distToNearestNeighbour(ei1.s->position, dists) < distToNearestNeighbour(ei2.s->position, dists);
|
return distToNearestNeighbour(ei1.s->getPosition(), dists) < distToNearestNeighbour(ei2.s->getPosition(), dists);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -111,17 +111,16 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
|
|||||||
{
|
{
|
||||||
//boost::this_thread::sleep(boost::posix_time::seconds(2));
|
//boost::this_thread::sleep(boost::posix_time::seconds(2));
|
||||||
print("activeStack called for " + stack->nodeName());
|
print("activeStack called for " + stack->nodeName());
|
||||||
auto dists = cb->battleGetDistances(stack);
|
auto dists = cb->battleGetDistances(stack, stack->getPosition());
|
||||||
std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable;
|
std::vector<EnemyInfo> enemiesShootable, enemiesReachable, enemiesUnreachable;
|
||||||
|
|
||||||
if(stack->type->idNumber == CreatureID::CATAPULT)
|
if(stack->type->idNumber == CreatureID::CATAPULT)
|
||||||
{
|
{
|
||||||
BattleAction attack;
|
BattleAction attack;
|
||||||
static const std::vector<int> wallHexes = {50, 183, 182, 130, 78, 29, 12, 95};
|
static const std::vector<int> wallHexes = {50, 183, 182, 130, 78, 29, 12, 95};
|
||||||
|
auto seletectedHex = *RandomGeneratorUtil::nextItem(wallHexes, CRandomGenerator::getDefault());
|
||||||
attack.destinationTile = *RandomGeneratorUtil::nextItem(wallHexes, CRandomGenerator::getDefault());
|
attack.aimToHex(seletectedHex);
|
||||||
attack.actionType = Battle::CATAPULT;
|
attack.actionType = EActionType::CATAPULT;
|
||||||
attack.additionalInfo = 0;
|
|
||||||
attack.side = side;
|
attack.side = side;
|
||||||
attack.stackNumber = stack->ID;
|
attack.stackNumber = stack->ID;
|
||||||
|
|
||||||
@@ -134,13 +133,13 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
|
|||||||
|
|
||||||
for (const CStack *s : cb->battleGetStacks(CBattleCallback::ONLY_ENEMY))
|
for (const CStack *s : cb->battleGetStacks(CBattleCallback::ONLY_ENEMY))
|
||||||
{
|
{
|
||||||
if(cb->battleCanShoot(stack, s->position))
|
if(cb->battleCanShoot(stack, s->getPosition()))
|
||||||
{
|
{
|
||||||
enemiesShootable.push_back(s);
|
enemiesShootable.push_back(s);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::vector<BattleHex> avHexes = cb->battleGetAvailableHexes(stack, false);
|
std::vector<BattleHex> avHexes = cb->battleGetAvailableHexes(stack);
|
||||||
|
|
||||||
for (BattleHex hex : avHexes)
|
for (BattleHex hex : avHexes)
|
||||||
{
|
{
|
||||||
@@ -157,7 +156,7 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!vstd::contains(enemiesReachable, s) && s->position.isValid())
|
if(!vstd::contains(enemiesReachable, s) && s->getPosition().isValid())
|
||||||
enemiesUnreachable.push_back(s);
|
enemiesUnreachable.push_back(s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -176,16 +175,16 @@ BattleAction CStupidAI::activeStack( const CStack * stack )
|
|||||||
else if(enemiesReachable.size())
|
else if(enemiesReachable.size())
|
||||||
{
|
{
|
||||||
const EnemyInfo &ei= *std::max_element(enemiesReachable.begin(), enemiesReachable.end(), &isMoreProfitable);
|
const EnemyInfo &ei= *std::max_element(enemiesReachable.begin(), enemiesReachable.end(), &isMoreProfitable);
|
||||||
return BattleAction::makeMeleeAttack(stack, ei.s, *std::max_element(ei.attackFrom.begin(), ei.attackFrom.end(), &willSecondHexBlockMoreEnemyShooters));
|
return BattleAction::makeMeleeAttack(stack, ei.s->getPosition(), *std::max_element(ei.attackFrom.begin(), ei.attackFrom.end(), &willSecondHexBlockMoreEnemyShooters));
|
||||||
}
|
}
|
||||||
else if(enemiesUnreachable.size()) //due to #955 - a buggy battle may occur when there are no enemies
|
else if(enemiesUnreachable.size()) //due to #955 - a buggy battle may occur when there are no enemies
|
||||||
{
|
{
|
||||||
assert(enemiesUnreachable.size());
|
assert(enemiesUnreachable.size());
|
||||||
const EnemyInfo &ei= *std::min_element(enemiesUnreachable.begin(), enemiesUnreachable.end(), std::bind(isCloser, _1, _2, std::ref(dists)));
|
const EnemyInfo &ei= *std::min_element(enemiesUnreachable.begin(), enemiesUnreachable.end(), std::bind(isCloser, _1, _2, std::ref(dists)));
|
||||||
assert(ei.s);
|
assert(ei.s);
|
||||||
if(distToNearestNeighbour(ei.s->position, dists) < GameConstants::BFIELD_SIZE)
|
if(distToNearestNeighbour(ei.s->getPosition(), dists) < GameConstants::BFIELD_SIZE)
|
||||||
{
|
{
|
||||||
return goTowards(stack, ei.s->position);
|
return goTowards(stack, ei.s->getPosition());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +196,7 @@ void CStupidAI::battleAttack(const BattleAttack *ba)
|
|||||||
print("battleAttack called");
|
print("battleAttack called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStupidAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa)
|
void CStupidAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, const std::vector<MetaString> & battleLog)
|
||||||
{
|
{
|
||||||
print("battleStacksAttacked called");
|
print("battleStacksAttacked called");
|
||||||
}
|
}
|
||||||
@@ -243,31 +242,11 @@ void CStupidAI::battleStart(const CCreatureSet *army1, const CCreatureSet *army2
|
|||||||
side = Side;
|
side = Side;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStupidAI::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom)
|
|
||||||
{
|
|
||||||
print("battleStacksHealedRes called");
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStupidAI::battleNewStackAppeared(const CStack * stack)
|
|
||||||
{
|
|
||||||
print("battleNewStackAppeared called");
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStupidAI::battleObstaclesRemoved(const std::set<si32> & removedObstacles)
|
|
||||||
{
|
|
||||||
print("battleObstaclesRemoved called");
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStupidAI::battleCatapultAttacked(const CatapultAttack & ca)
|
void CStupidAI::battleCatapultAttacked(const CatapultAttack & ca)
|
||||||
{
|
{
|
||||||
print("battleCatapultAttacked called");
|
print("battleCatapultAttacked called");
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStupidAI::battleStacksRemoved(const BattleStacksRemoved & bsr)
|
|
||||||
{
|
|
||||||
print("battleStacksRemoved called");
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStupidAI::print(const std::string &text) const
|
void CStupidAI::print(const std::string &text) const
|
||||||
{
|
{
|
||||||
logAi->trace("CStupidAI [%p]: %s", this, text);
|
logAi->trace("CStupidAI [%p]: %s", this, text);
|
||||||
@@ -276,8 +255,8 @@ void CStupidAI::print(const std::string &text) const
|
|||||||
BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
|
BattleAction CStupidAI::goTowards(const CStack * stack, BattleHex destination)
|
||||||
{
|
{
|
||||||
assert(destination.isValid());
|
assert(destination.isValid());
|
||||||
auto avHexes = cb->battleGetAvailableHexes(stack, false);
|
|
||||||
auto reachability = cb->getReachability(stack);
|
auto reachability = cb->getReachability(stack);
|
||||||
|
auto avHexes = cb->battleGetAvailableHexes(reachability, stack);
|
||||||
|
|
||||||
if(vstd::contains(avHexes, destination))
|
if(vstd::contains(avHexes, destination))
|
||||||
return BattleAction::makeMove(stack, destination);
|
return BattleAction::makeMove(stack, destination);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public:
|
|||||||
BattleAction activeStack(const CStack * stack) override; //called when it's turn of that stack
|
BattleAction activeStack(const CStack * stack) override; //called when it's turn of that stack
|
||||||
|
|
||||||
void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack
|
void battleAttack(const BattleAttack *ba) override; //called when stack is performing attack
|
||||||
void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) override; //called when stack receives damage (after battleAttack())
|
void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, const std::vector<MetaString> & battleLog) override; //called when stack receives damage (after battleAttack())
|
||||||
void battleEnd(const BattleResult *br) override;
|
void battleEnd(const BattleResult *br) override;
|
||||||
//void battleResultsApplied() override; //called when all effects of last battle are applied
|
//void battleResultsApplied() override; //called when all effects of last battle are applied
|
||||||
void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied;
|
void battleNewRoundFirst(int round) override; //called at the beginning of each turn before changes are applied;
|
||||||
@@ -37,11 +37,7 @@ public:
|
|||||||
void battleStacksEffectsSet(const SetStackEffect & sse) override;//called when a specific effect is set to stacks
|
void battleStacksEffectsSet(const SetStackEffect & sse) override;//called when a specific effect is set to stacks
|
||||||
//void battleTriggerEffect(const BattleTriggerEffect & bte) override;
|
//void battleTriggerEffect(const BattleTriggerEffect & bte) override;
|
||||||
void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right
|
void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right
|
||||||
void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom) override; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp
|
|
||||||
void battleNewStackAppeared(const CStack * stack) override; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned
|
|
||||||
void battleObstaclesRemoved(const std::set<si32> & removedObstacles) override; //called when a certain set of obstacles is removed from batlefield; IDs of them are given
|
|
||||||
void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
|
void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
|
||||||
void battleStacksRemoved(const BattleStacksRemoved & bsr) override; //called when certain stack is completely removed from battlefield
|
|
||||||
|
|
||||||
BattleAction goTowards(const CStack * stack, BattleHex hex );
|
BattleAction goTowards(const CStack * stack, BattleHex hex );
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -1359,7 +1359,7 @@ static const BuildingID unitsUpgrade[] = { BuildingID::DWELL_LVL_1_UP, BuildingI
|
|||||||
BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP};
|
BuildingID::DWELL_LVL_4_UP, BuildingID::DWELL_LVL_5_UP, BuildingID::DWELL_LVL_6_UP, BuildingID::DWELL_LVL_7_UP};
|
||||||
static const BuildingID unitGrowth[] = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::HORDE_1,
|
static const BuildingID unitGrowth[] = { BuildingID::FORT, BuildingID::CITADEL, BuildingID::CASTLE, BuildingID::HORDE_1,
|
||||||
BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR};
|
BuildingID::HORDE_1_UPGR, BuildingID::HORDE_2, BuildingID::HORDE_2_UPGR};
|
||||||
static const BuildingID spells[] = {BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3,
|
static const BuildingID _spells[] = {BuildingID::MAGES_GUILD_1, BuildingID::MAGES_GUILD_2, BuildingID::MAGES_GUILD_3,
|
||||||
BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5};
|
BuildingID::MAGES_GUILD_4, BuildingID::MAGES_GUILD_5};
|
||||||
static const BuildingID extra[] = {BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3,
|
static const BuildingID extra[] = {BuildingID::RESOURCE_SILO, BuildingID::SPECIAL_1, BuildingID::SPECIAL_2, BuildingID::SPECIAL_3,
|
||||||
BuildingID::SPECIAL_4, BuildingID::SHIPYARD}; // all remaining buildings
|
BuildingID::SPECIAL_4, BuildingID::SHIPYARD}; // all remaining buildings
|
||||||
@@ -1416,7 +1416,7 @@ void VCAI::buildStructure(const CGTownInstance * t)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//remaining tasks
|
//remaining tasks
|
||||||
if(tryBuildNextStructure(t, std::vector<BuildingID>(spells, spells + ARRAY_COUNT(spells))))
|
if(tryBuildNextStructure(t, std::vector<BuildingID>(_spells, _spells + ARRAY_COUNT(_spells))))
|
||||||
return;
|
return;
|
||||||
if(tryBuildAnyStructure(t, std::vector<BuildingID>(extra, extra + ARRAY_COUNT(extra))))
|
if(tryBuildAnyStructure(t, std::vector<BuildingID>(extra, extra + ARRAY_COUNT(extra))))
|
||||||
return;
|
return;
|
||||||
|
|||||||
+6
-5
@@ -174,7 +174,7 @@ bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID)
|
|||||||
|
|
||||||
int CBattleCallback::battleMakeAction(BattleAction* action)
|
int CBattleCallback::battleMakeAction(BattleAction* action)
|
||||||
{
|
{
|
||||||
assert(action->actionType == Battle::HERO_SPELL);
|
assert(action->actionType == EActionType::HERO_SPELL);
|
||||||
MakeCustomAction mca(*action);
|
MakeCustomAction mca(*action);
|
||||||
sendRequest(&mca);
|
sendRequest(&mca);
|
||||||
return 0;
|
return 0;
|
||||||
@@ -279,9 +279,11 @@ void CCallback::buildBoat( const IShipyard *obj )
|
|||||||
sendRequest(&bb);
|
sendRequest(&bb);
|
||||||
}
|
}
|
||||||
|
|
||||||
CCallback::CCallback( CGameState * GS, boost::optional<PlayerColor> Player, CClient *C )
|
CCallback::CCallback(CGameState * GS, boost::optional<PlayerColor> Player, CClient * C)
|
||||||
:CBattleCallback(GS, Player, C)
|
: CBattleCallback(Player, C)
|
||||||
{
|
{
|
||||||
|
gs = GS;
|
||||||
|
|
||||||
waitTillRealize = false;
|
waitTillRealize = false;
|
||||||
unlockGsWhenWaiting = false;
|
unlockGsWhenWaiting = false;
|
||||||
}
|
}
|
||||||
@@ -367,9 +369,8 @@ void CCallback::unregisterBattleInterface(std::shared_ptr<IBattleEventsReceiver>
|
|||||||
cl->additionalBattleInts[*player] -= battleEvents;
|
cl->additionalBattleInts[*player] -= battleEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
CBattleCallback::CBattleCallback(CGameState *GS, boost::optional<PlayerColor> Player, CClient *C )
|
CBattleCallback::CBattleCallback(boost::optional<PlayerColor> Player, CClient *C )
|
||||||
{
|
{
|
||||||
gs = GS;
|
|
||||||
player = Player;
|
player = Player;
|
||||||
cl = C;
|
cl = C;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-3
@@ -17,7 +17,7 @@ class CGameState;
|
|||||||
struct CPath;
|
struct CPath;
|
||||||
class CGObjectInstance;
|
class CGObjectInstance;
|
||||||
class CArmedInstance;
|
class CArmedInstance;
|
||||||
struct BattleAction;
|
class BattleAction;
|
||||||
class CGTownInstance;
|
class CGTownInstance;
|
||||||
struct lua_State;
|
struct lua_State;
|
||||||
class CClient;
|
class CClient;
|
||||||
@@ -85,10 +85,9 @@ class CBattleCallback : public IBattleCallback, public CPlayerBattleCallback
|
|||||||
protected:
|
protected:
|
||||||
int sendRequest(const CPack *request); //returns requestID (that'll be matched to requestID in PackageApplied)
|
int sendRequest(const CPack *request); //returns requestID (that'll be matched to requestID in PackageApplied)
|
||||||
CClient *cl;
|
CClient *cl;
|
||||||
//virtual bool hasAccess(int playerId) const;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CBattleCallback(CGameState *GS, boost::optional<PlayerColor> Player, CClient *C);
|
CBattleCallback(boost::optional<PlayerColor> Player, CClient *C);
|
||||||
int battleMakeAction(BattleAction* action) override;//for casting spells by hero - DO NOT use it for moving active stack
|
int battleMakeAction(BattleAction* action) override;//for casting spells by hero - DO NOT use it for moving active stack
|
||||||
bool battleMakeTacticAction(BattleAction * action) override; // performs tactic phase actions
|
bool battleMakeTacticAction(BattleAction * action) override; // performs tactic phase actions
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 733 B |
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"basepath": "vcmi/battleQueue/",
|
||||||
|
"images" :
|
||||||
|
[
|
||||||
|
{ "frame" : 0, "file" : "defendBig"},
|
||||||
|
{ "frame" : 1, "file" : "waitBig"}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"basepath": "vcmi/battleQueue/",
|
||||||
|
"images" :
|
||||||
|
[
|
||||||
|
{ "frame" : 0, "file" : "defendSmall"},
|
||||||
|
{ "frame" : 1, "file" : "waitSmall"}
|
||||||
|
]
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 759 B |
@@ -1,356 +0,0 @@
|
|||||||
/*
|
|
||||||
* CDefHandler.cpp, part of VCMI engine
|
|
||||||
*
|
|
||||||
* Authors: listed in file AUTHORS in main folder
|
|
||||||
*
|
|
||||||
* License: GNU General Public License v2.0 or later
|
|
||||||
* Full text of license available in license.txt file, in main folder
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#include "StdInc.h"
|
|
||||||
#include "SDL.h"
|
|
||||||
#include "CDefHandler.h"
|
|
||||||
|
|
||||||
#include "../lib/filesystem/Filesystem.h"
|
|
||||||
#include "../lib/VCMI_Lib.h"
|
|
||||||
#include "CBitmapHandler.h"
|
|
||||||
#include "gui/SDL_Extensions.h"
|
|
||||||
|
|
||||||
#ifdef unused
|
|
||||||
static long long pow(long long a, int b)
|
|
||||||
{
|
|
||||||
if (!b) return 1;
|
|
||||||
long c = a;
|
|
||||||
while (--b)
|
|
||||||
a*=c;
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
CDefHandler::CDefHandler()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
CDefHandler::~CDefHandler()
|
|
||||||
{
|
|
||||||
for (auto & elem : ourImages)
|
|
||||||
{
|
|
||||||
if (elem.bitmap)
|
|
||||||
{
|
|
||||||
SDL_FreeSurface(elem.bitmap);
|
|
||||||
elem.bitmap=nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CDefHandler::openFromMemory(ui8 *table, const std::string & name)
|
|
||||||
{
|
|
||||||
SDL_Color palette[256];
|
|
||||||
SDefEntry &de = * reinterpret_cast<SDefEntry *>(table);
|
|
||||||
ui8 *p;
|
|
||||||
|
|
||||||
defName = name;
|
|
||||||
DEFType = read_le_u32(&de.DEFType);
|
|
||||||
width = read_le_u32(&de.width);
|
|
||||||
height = read_le_u32(&de.height);
|
|
||||||
ui32 totalBlocks = read_le_u32(&de.totalBlocks);
|
|
||||||
|
|
||||||
for (ui32 it=0;it<256;it++)
|
|
||||||
{
|
|
||||||
palette[it].r = de.palette[it].R;
|
|
||||||
palette[it].g = de.palette[it].G;
|
|
||||||
palette[it].b = de.palette[it].B;
|
|
||||||
palette[it].a = SDL_ALPHA_OPAQUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The SDefEntryBlock starts just after the SDefEntry
|
|
||||||
p = reinterpret_cast<ui8 *>(&de);
|
|
||||||
p += sizeof(de);
|
|
||||||
|
|
||||||
int totalEntries=0;
|
|
||||||
for (ui32 z=0; z<totalBlocks; z++)
|
|
||||||
{
|
|
||||||
SDefEntryBlock &block = * reinterpret_cast<SDefEntryBlock *>(p);
|
|
||||||
ui32 totalInBlock;
|
|
||||||
|
|
||||||
totalInBlock = read_le_u32(&block.totalInBlock);
|
|
||||||
|
|
||||||
for (ui32 j=SEntries.size(); j<totalEntries+totalInBlock; j++)
|
|
||||||
SEntries.push_back(SEntry());
|
|
||||||
|
|
||||||
p = block.data;
|
|
||||||
for (ui32 j=0; j<totalInBlock; j++)
|
|
||||||
{
|
|
||||||
char Buffer[13];
|
|
||||||
memcpy(Buffer, p, 12);
|
|
||||||
Buffer[12] = 0;
|
|
||||||
SEntries[totalEntries+j].name=Buffer;
|
|
||||||
p += 13;
|
|
||||||
}
|
|
||||||
for (ui32 j=0; j<totalInBlock; j++)
|
|
||||||
{
|
|
||||||
SEntries[totalEntries+j].offset = read_le_u32(p);
|
|
||||||
p += 4;
|
|
||||||
}
|
|
||||||
//totalEntries+=totalInBlock;
|
|
||||||
for(ui32 hh=0; hh<totalInBlock; ++hh)
|
|
||||||
{
|
|
||||||
SEntries[totalEntries].group = z;
|
|
||||||
++totalEntries;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto & elem : SEntries)
|
|
||||||
{
|
|
||||||
elem.name = elem.name.substr(0, elem.name.find('.')+4);
|
|
||||||
}
|
|
||||||
//RWEntries = new ui32[height];
|
|
||||||
for(ui32 i=0; i < SEntries.size(); ++i)
|
|
||||||
{
|
|
||||||
Cimage nimg;
|
|
||||||
nimg.bitmap = getSprite(i, table, palette);
|
|
||||||
nimg.imName = SEntries[i].name;
|
|
||||||
nimg.groupNumber = SEntries[i].group;
|
|
||||||
ourImages.push_back(nimg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Surface * CDefHandler::getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const
|
|
||||||
{
|
|
||||||
SDL_Surface * ret=nullptr;
|
|
||||||
|
|
||||||
ui32 BaseOffset,
|
|
||||||
SpriteWidth, SpriteHeight, //format of sprite
|
|
||||||
TotalRowLength, // length of read segment
|
|
||||||
add, FullHeight,FullWidth,
|
|
||||||
RowAdd,
|
|
||||||
defType2;
|
|
||||||
int LeftMargin, RightMargin, TopMargin, BottomMargin;
|
|
||||||
|
|
||||||
|
|
||||||
ui8 SegmentType;
|
|
||||||
|
|
||||||
BaseOffset = SEntries[SIndex].offset;
|
|
||||||
SSpriteDef sd = * reinterpret_cast<const SSpriteDef *>(FDef + BaseOffset);
|
|
||||||
|
|
||||||
defType2 = read_le_u32(&sd.defType2);
|
|
||||||
FullWidth = read_le_u32(&sd.FullWidth);
|
|
||||||
FullHeight = read_le_u32(&sd.FullHeight);
|
|
||||||
SpriteWidth = read_le_u32(&sd.SpriteWidth);
|
|
||||||
SpriteHeight = read_le_u32(&sd.SpriteHeight);
|
|
||||||
LeftMargin = read_le_u32(&sd.LeftMargin);
|
|
||||||
TopMargin = read_le_u32(&sd.TopMargin);
|
|
||||||
RightMargin = FullWidth - SpriteWidth - LeftMargin;
|
|
||||||
BottomMargin = FullHeight - SpriteHeight - TopMargin;
|
|
||||||
|
|
||||||
//if(LeftMargin + RightMargin < 0)
|
|
||||||
// SpriteWidth += LeftMargin + RightMargin; //ugly construction... TODO: check how to do it nicer
|
|
||||||
if(LeftMargin<0)
|
|
||||||
SpriteWidth+=LeftMargin;
|
|
||||||
if(RightMargin<0)
|
|
||||||
SpriteWidth+=RightMargin;
|
|
||||||
|
|
||||||
// Note: this looks bogus because we allocate only FullWidth, not FullWidth+add
|
|
||||||
add = 4 - FullWidth%4;
|
|
||||||
if (add==4)
|
|
||||||
add=0;
|
|
||||||
|
|
||||||
ret = SDL_CreateRGBSurface(SDL_SWSURFACE, FullWidth, FullHeight, 8, 0, 0, 0, 0);
|
|
||||||
|
|
||||||
if(nullptr == ret)
|
|
||||||
{
|
|
||||||
logGlobal->error("%s: Unable to create surface", __FUNCTION__);
|
|
||||||
logGlobal->error("%dX%d", FullWidth, FullHeight);
|
|
||||||
logGlobal->error(SDL_GetError());
|
|
||||||
throw std::runtime_error("Unable to create surface");
|
|
||||||
}
|
|
||||||
|
|
||||||
BaseOffset += sizeof(SSpriteDef);
|
|
||||||
int BaseOffsetor = BaseOffset;
|
|
||||||
|
|
||||||
SDL_Palette * p = SDL_AllocPalette(256);
|
|
||||||
SDL_SetPaletteColors(p, palette, 0, 256);
|
|
||||||
SDL_SetSurfacePalette(ret, p);
|
|
||||||
SDL_FreePalette(p);
|
|
||||||
|
|
||||||
int ftcp=0;
|
|
||||||
|
|
||||||
// If there's a margin anywhere, just blank out the whole surface.
|
|
||||||
if (TopMargin > 0 || BottomMargin > 0 || LeftMargin > 0 || RightMargin > 0) {
|
|
||||||
memset( reinterpret_cast<char*>(ret->pixels), 0, FullHeight*FullWidth);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip top margin
|
|
||||||
if (TopMargin > 0)
|
|
||||||
ftcp += TopMargin*(FullWidth+add);
|
|
||||||
|
|
||||||
switch(defType2)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
{
|
|
||||||
for (ui32 i=0;i<SpriteHeight;i++)
|
|
||||||
{
|
|
||||||
if (LeftMargin>0)
|
|
||||||
ftcp += LeftMargin;
|
|
||||||
|
|
||||||
memcpy(reinterpret_cast<char*>(ret->pixels)+ftcp, &FDef[BaseOffset], SpriteWidth);
|
|
||||||
ftcp += SpriteWidth;
|
|
||||||
BaseOffset += SpriteWidth;
|
|
||||||
|
|
||||||
if (RightMargin>0)
|
|
||||||
ftcp += RightMargin;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
{
|
|
||||||
const ui32 * RWEntriesLoc = reinterpret_cast<const ui32 *>(FDef+BaseOffset);
|
|
||||||
BaseOffset += sizeof(int) * SpriteHeight;
|
|
||||||
for (ui32 i=0;i<SpriteHeight;i++)
|
|
||||||
{
|
|
||||||
BaseOffset=BaseOffsetor + read_le_u32(RWEntriesLoc + i);
|
|
||||||
if (LeftMargin>0)
|
|
||||||
ftcp += LeftMargin;
|
|
||||||
|
|
||||||
TotalRowLength=0;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
ui32 SegmentLength;
|
|
||||||
|
|
||||||
SegmentType=FDef[BaseOffset++];
|
|
||||||
SegmentLength=FDef[BaseOffset++] + 1;
|
|
||||||
|
|
||||||
if (SegmentType==0xFF)
|
|
||||||
{
|
|
||||||
memcpy(reinterpret_cast<char*>(ret->pixels)+ftcp, FDef + BaseOffset, SegmentLength);
|
|
||||||
BaseOffset+=SegmentLength;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memset(reinterpret_cast<char*>(ret->pixels)+ftcp, SegmentType, SegmentLength);
|
|
||||||
}
|
|
||||||
ftcp += SegmentLength;
|
|
||||||
TotalRowLength += SegmentLength;
|
|
||||||
}while(TotalRowLength<SpriteWidth);
|
|
||||||
|
|
||||||
RowAdd=SpriteWidth-TotalRowLength;
|
|
||||||
|
|
||||||
if (RightMargin>0)
|
|
||||||
ftcp += RightMargin;
|
|
||||||
|
|
||||||
if (add>0)
|
|
||||||
ftcp += add+RowAdd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
{
|
|
||||||
BaseOffset = BaseOffsetor + read_le_u16(FDef + BaseOffsetor);
|
|
||||||
|
|
||||||
for (ui32 i=0;i<SpriteHeight;i++)
|
|
||||||
{
|
|
||||||
//BaseOffset = BaseOffsetor+RWEntries[i];
|
|
||||||
if (LeftMargin>0)
|
|
||||||
ftcp += LeftMargin;
|
|
||||||
|
|
||||||
TotalRowLength=0;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
SegmentType=FDef[BaseOffset++];
|
|
||||||
ui8 code = SegmentType / 32;
|
|
||||||
ui8 value = (SegmentType & 31) + 1;
|
|
||||||
if(code==7)
|
|
||||||
{
|
|
||||||
memcpy(reinterpret_cast<char*>(ret->pixels)+ftcp, &FDef[BaseOffset], value);
|
|
||||||
ftcp += value;
|
|
||||||
BaseOffset += value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memset(reinterpret_cast<char*>(ret->pixels)+ftcp, code, value);
|
|
||||||
ftcp += value;
|
|
||||||
}
|
|
||||||
TotalRowLength+=value;
|
|
||||||
} while(TotalRowLength<SpriteWidth);
|
|
||||||
|
|
||||||
if (RightMargin>0)
|
|
||||||
ftcp += RightMargin;
|
|
||||||
|
|
||||||
RowAdd=SpriteWidth-TotalRowLength;
|
|
||||||
|
|
||||||
if (add>0)
|
|
||||||
ftcp += add+RowAdd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
{
|
|
||||||
for (ui32 i=0;i<SpriteHeight;i++)
|
|
||||||
{
|
|
||||||
BaseOffset = BaseOffsetor + read_le_u16(FDef + BaseOffsetor+i*2*(SpriteWidth/32));
|
|
||||||
if (LeftMargin>0)
|
|
||||||
ftcp += LeftMargin;
|
|
||||||
|
|
||||||
TotalRowLength=0;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
SegmentType=FDef[BaseOffset++];
|
|
||||||
ui8 code = SegmentType / 32;
|
|
||||||
ui8 value = (SegmentType & 31) + 1;
|
|
||||||
|
|
||||||
int len = std::min<ui32>(value, SpriteWidth - TotalRowLength) - std::max(0, -LeftMargin);
|
|
||||||
vstd::amax(len, 0);
|
|
||||||
|
|
||||||
if(code==7)
|
|
||||||
{
|
|
||||||
memcpy((ui8*)ret->pixels + ftcp, FDef + BaseOffset, len);
|
|
||||||
ftcp += len;
|
|
||||||
BaseOffset += len;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
memset((ui8*)ret->pixels + ftcp, code, len);
|
|
||||||
ftcp += len;
|
|
||||||
}
|
|
||||||
TotalRowLength+=( LeftMargin>=0 ? value : value+LeftMargin );
|
|
||||||
}while(TotalRowLength<SpriteWidth);
|
|
||||||
|
|
||||||
if (RightMargin>0)
|
|
||||||
ftcp += RightMargin;
|
|
||||||
|
|
||||||
RowAdd=SpriteWidth-TotalRowLength;
|
|
||||||
|
|
||||||
if (add>0)
|
|
||||||
ftcp += add+RowAdd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("Unknown sprite format.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Color ttcol = ret->format->palette->colors[0];
|
|
||||||
Uint32 keycol = SDL_MapRGBA(ret->format, ttcol.r, ttcol.b, ttcol.g, ttcol.a);
|
|
||||||
SDL_SetColorKey(ret, SDL_TRUE, keycol);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
CDefHandler * CDefHandler::giveDef(const std::string & defName)
|
|
||||||
{
|
|
||||||
ResourceID resID(std::string("SPRITES/") + defName, EResType::ANIMATION);
|
|
||||||
|
|
||||||
auto data = CResourceHandler::get()->load(resID)->readAll().first;
|
|
||||||
if(!data)
|
|
||||||
throw std::runtime_error("bad def name!");
|
|
||||||
auto nh = new CDefHandler();
|
|
||||||
nh->openFromMemory(data.get(), defName);
|
|
||||||
return nh;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
* CDefHandler.h, part of VCMI engine
|
|
||||||
*
|
|
||||||
* Authors: listed in file AUTHORS in main folder
|
|
||||||
*
|
|
||||||
* License: GNU General Public License v2.0 or later
|
|
||||||
* Full text of license available in license.txt file, in main folder
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../lib/vcmi_endian.h"
|
|
||||||
|
|
||||||
struct SDL_Surface;
|
|
||||||
struct SDL_Color;
|
|
||||||
|
|
||||||
struct Cimage
|
|
||||||
{
|
|
||||||
int groupNumber;
|
|
||||||
std::string imName; //name without extension
|
|
||||||
SDL_Surface * bitmap;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Def entry in file. Integer fields are all little endian and will
|
|
||||||
// need to be converted.
|
|
||||||
struct SDefEntryBlock
|
|
||||||
{
|
|
||||||
ui32 unknown1;
|
|
||||||
ui32 totalInBlock;
|
|
||||||
ui32 unknown2;
|
|
||||||
ui32 unknown3;
|
|
||||||
ui8 data[0];
|
|
||||||
} PACKED_STRUCT;
|
|
||||||
|
|
||||||
// Def entry in file. Integer fields are all little endian and will
|
|
||||||
// need to be converted.
|
|
||||||
struct SDefEntry
|
|
||||||
{
|
|
||||||
ui32 DEFType;
|
|
||||||
ui32 width;
|
|
||||||
ui32 height;
|
|
||||||
ui32 totalBlocks;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
ui8 R;
|
|
||||||
ui8 G;
|
|
||||||
ui8 B;
|
|
||||||
} palette[256];
|
|
||||||
|
|
||||||
// SDefEntry is followed by a series of SDefEntryBlock
|
|
||||||
// This is commented out because VC++ doesn't accept C99 syntax.
|
|
||||||
//struct SDefEntryBlock blocks[];
|
|
||||||
} PACKED_STRUCT;
|
|
||||||
|
|
||||||
// Def entry in file. Integer fields are all little endian and will
|
|
||||||
// need to be converted.
|
|
||||||
struct SSpriteDef
|
|
||||||
{
|
|
||||||
ui32 prSize;
|
|
||||||
ui32 defType2;
|
|
||||||
ui32 FullWidth;
|
|
||||||
ui32 FullHeight;
|
|
||||||
ui32 SpriteWidth;
|
|
||||||
ui32 SpriteHeight;
|
|
||||||
ui32 LeftMargin;
|
|
||||||
ui32 TopMargin;
|
|
||||||
} PACKED_STRUCT;
|
|
||||||
|
|
||||||
|
|
||||||
class CDefHandler
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
ui32 DEFType;
|
|
||||||
struct SEntry
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
int offset;
|
|
||||||
int group;
|
|
||||||
} ;
|
|
||||||
std::vector<SEntry> SEntries ;
|
|
||||||
|
|
||||||
void openFromMemory(ui8 * table, const std::string & name);
|
|
||||||
SDL_Surface * getSprite (int SIndex, const ui8 * FDef, const SDL_Color * palette) const;
|
|
||||||
public:
|
|
||||||
int width, height; //width and height
|
|
||||||
std::string defName;
|
|
||||||
std::vector<Cimage> ourImages;
|
|
||||||
|
|
||||||
CDefHandler();
|
|
||||||
~CDefHandler();
|
|
||||||
|
|
||||||
|
|
||||||
static CDefHandler * giveDef(const std::string & defName);
|
|
||||||
};
|
|
||||||
@@ -714,6 +714,46 @@ void processCommand(const std::string &message)
|
|||||||
std::cout << "\rExtracting done :)\n";
|
std::cout << "\rExtracting done :)\n";
|
||||||
std::cout << " Extracted files can be found in " << outPath << " directory\n";
|
std::cout << " Extracted files can be found in " << outPath << " directory\n";
|
||||||
}
|
}
|
||||||
|
else if(message=="get config")
|
||||||
|
{
|
||||||
|
std::cout << "Command accepted.\t";
|
||||||
|
|
||||||
|
const bfs::path outPath =
|
||||||
|
VCMIDirs::get().userCachePath() / "extracted" / "configuration";
|
||||||
|
|
||||||
|
bfs::create_directories(outPath);
|
||||||
|
|
||||||
|
const std::vector<std::string> contentNames = {"heroClasses", "artifacts", "creatures", "factions", "objects", "heroes", "spells", "skills"};
|
||||||
|
|
||||||
|
for(auto contentName : contentNames)
|
||||||
|
{
|
||||||
|
auto & content = VLC->modh->content[contentName];
|
||||||
|
|
||||||
|
auto contentOutPath = outPath / contentName;
|
||||||
|
bfs::create_directories(contentOutPath);
|
||||||
|
|
||||||
|
for(auto & iter : content.modData)
|
||||||
|
{
|
||||||
|
const JsonNode & modData = iter.second.modData;
|
||||||
|
|
||||||
|
for(auto & nameAndObject : modData.Struct())
|
||||||
|
{
|
||||||
|
const JsonNode & object = nameAndObject.second;
|
||||||
|
|
||||||
|
std::string name = CModHandler::normalizeIdentifier(object.meta, "core", nameAndObject.first);
|
||||||
|
|
||||||
|
boost::algorithm::replace_all(name,":","_");
|
||||||
|
|
||||||
|
const bfs::path filePath = contentOutPath / (name + ".json");
|
||||||
|
bfs::ofstream file(filePath);
|
||||||
|
file << object.toJson();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << "\rExtracting done :)\n";
|
||||||
|
std::cout << " Extracted files can be found in " << outPath << " directory\n";
|
||||||
|
}
|
||||||
else if(message=="get txt")
|
else if(message=="get txt")
|
||||||
{
|
{
|
||||||
std::cout << "Command accepted.\t";
|
std::cout << "Command accepted.\t";
|
||||||
|
|||||||
@@ -45,7 +45,6 @@ set(client_SRCS
|
|||||||
|
|
||||||
CBitmapHandler.cpp
|
CBitmapHandler.cpp
|
||||||
CreatureCostBox.cpp
|
CreatureCostBox.cpp
|
||||||
CDefHandler.cpp
|
|
||||||
CGameInfo.cpp
|
CGameInfo.cpp
|
||||||
Client.cpp
|
Client.cpp
|
||||||
CMessage.cpp
|
CMessage.cpp
|
||||||
@@ -103,7 +102,6 @@ set(client_HEADERS
|
|||||||
|
|
||||||
CBitmapHandler.h
|
CBitmapHandler.h
|
||||||
CreatureCostBox.h
|
CreatureCostBox.h
|
||||||
CDefHandler.h
|
|
||||||
CGameInfo.h
|
CGameInfo.h
|
||||||
Client.h
|
Client.h
|
||||||
CMessage.h
|
CMessage.h
|
||||||
|
|||||||
+113
-101
@@ -42,7 +42,8 @@
|
|||||||
#include "../lib/JsonNode.h"
|
#include "../lib/JsonNode.h"
|
||||||
#include "CMusicHandler.h"
|
#include "CMusicHandler.h"
|
||||||
#include "../lib/CondSh.h"
|
#include "../lib/CondSh.h"
|
||||||
#include "../lib/NetPacks.h"
|
#include "../lib/NetPacksBase.h"
|
||||||
|
#include "../lib/NetPacks.h"//todo: remove
|
||||||
#include "../lib/mapping/CMap.h"
|
#include "../lib/mapping/CMap.h"
|
||||||
#include "../lib/VCMIDirs.h"
|
#include "../lib/VCMIDirs.h"
|
||||||
#include "mapHandler.h"
|
#include "mapHandler.h"
|
||||||
@@ -695,80 +696,91 @@ void CPlayerInterface::battleStart(const CCreatureSet *army1, const CCreatureSet
|
|||||||
BATTLE_EVENT_POSSIBLE_RETURN;
|
BATTLE_EVENT_POSSIBLE_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CPlayerInterface::battleUnitsChanged(const std::vector<UnitChanges> & units, const std::vector<CustomEffectInfo> & customEffects, const std::vector<MetaString> & battleLog)
|
||||||
void CPlayerInterface::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom)
|
|
||||||
{
|
{
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
BATTLE_EVENT_POSSIBLE_RETURN;
|
BATTLE_EVENT_POSSIBLE_RETURN;
|
||||||
|
|
||||||
for (auto & healedStack : healedStacks)
|
for(auto & info : units)
|
||||||
{
|
{
|
||||||
const CStack * healed = cb->battleGetStackByID(healedStack.first);
|
switch(info.operation)
|
||||||
if (battleInt->creAnims[healed->ID]->isDead())
|
|
||||||
{
|
{
|
||||||
//stack has been resurrected
|
case UnitChanges::EOperation::RESET_STATE:
|
||||||
battleInt->creAnims[healed->ID]->setType(CCreatureAnim::HOLDING);
|
{
|
||||||
|
const battle::Unit * unit = cb->battleGetUnitByID(info.id);
|
||||||
|
|
||||||
|
if(!unit)
|
||||||
|
{
|
||||||
|
logGlobal->error("Invalid unit ID %d", info.id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto iter = battleInt->creAnims.find(info.id);
|
||||||
|
|
||||||
|
if(iter == battleInt->creAnims.end())
|
||||||
|
{
|
||||||
|
logGlobal->error("Unit %d have no animation", info.id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CCreatureAnimation * animation = iter->second;
|
||||||
|
|
||||||
|
if(unit->alive() && animation->isDead())
|
||||||
|
animation->setType(CCreatureAnim::HOLDING);
|
||||||
|
|
||||||
|
//TODO: handle more cases
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case UnitChanges::EOperation::REMOVE:
|
||||||
|
battleInt->stackRemoved(info.id);
|
||||||
|
break;
|
||||||
|
case UnitChanges::EOperation::ADD:
|
||||||
|
{
|
||||||
|
const CStack * unit = cb->battleGetStackByID(info.id);
|
||||||
|
if(!unit)
|
||||||
|
{
|
||||||
|
logGlobal->error("Invalid unit ID %d", info.id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
battleInt->unitAdded(unit);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logGlobal->error("Unknown unit operation %d", (int)info.operation);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(lifeDrain)
|
battleInt->displayCustomEffects(customEffects);
|
||||||
|
battleInt->displayBattleLog(battleLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPlayerInterface::battleObstaclesChanged(const std::vector<ObstacleChanges> & obstacles)
|
||||||
|
{
|
||||||
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
|
BATTLE_EVENT_POSSIBLE_RETURN;
|
||||||
|
|
||||||
|
bool needUpdate = false;
|
||||||
|
|
||||||
|
for(auto & change : obstacles)
|
||||||
{
|
{
|
||||||
const CStack * attacker = cb->battleGetStackByID(healedStacks[0].first, false);
|
if(change.operation == BattleChanges::EOperation::ADD)
|
||||||
const CStack * defender = cb->battleGetStackByID(lifeDrainFrom, false);
|
|
||||||
|
|
||||||
if(attacker && defender)
|
|
||||||
{
|
{
|
||||||
battleInt->displayEffect(52, attacker->position); //TODO: transparency
|
auto instance = cb->battleGetObstacleByID(change.id);
|
||||||
CCS->soundh->playSound(soundBase::DRAINLIF);
|
if(instance)
|
||||||
|
battleInt->obstaclePlaced(*instance);
|
||||||
MetaString text;
|
else
|
||||||
attacker->addText(text, MetaString::GENERAL_TXT, 361);
|
logNetwork->error("Invalid obstacle instance %d", change.id);
|
||||||
attacker->addNameReplacement(text, false);
|
|
||||||
text.addReplacement(healedStacks[0].second);
|
|
||||||
defender->addNameReplacement(text, true);
|
|
||||||
battleInt->console->addText(text.toString());
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logGlobal->error("Unable to display life drain info");
|
needUpdate = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(tentHeal)
|
|
||||||
{
|
|
||||||
const CStack * healer = cb->battleGetStackByID(lifeDrainFrom, false);
|
|
||||||
const CStack * target = cb->battleGetStackByID(healedStacks[0].first, false);
|
|
||||||
|
|
||||||
if(healer && target)
|
if(needUpdate)
|
||||||
{
|
//update accessible hexes
|
||||||
MetaString text;
|
battleInt->redrawBackgroundWithHexes(battleInt->activeStack);
|
||||||
text.addTxt(MetaString::GENERAL_TXT, 414);
|
|
||||||
healer->addNameReplacement(text, false);
|
|
||||||
target->addNameReplacement(text, false);
|
|
||||||
text.addReplacement(healedStacks[0].second);
|
|
||||||
battleInt->console->addText(text.toString());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logGlobal->error("Unable to display tent heal info");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPlayerInterface::battleNewStackAppeared(const CStack * stack)
|
|
||||||
{
|
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
|
||||||
BATTLE_EVENT_POSSIBLE_RETURN;
|
|
||||||
|
|
||||||
battleInt->newStack(stack);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPlayerInterface::battleObstaclesRemoved(const std::set<si32> & removedObstacles)
|
|
||||||
{
|
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
|
||||||
BATTLE_EVENT_POSSIBLE_RETURN;
|
|
||||||
|
|
||||||
//update accessible hexes
|
|
||||||
battleInt->redrawBackgroundWithHexes(battleInt->activeStack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPlayerInterface::battleCatapultAttacked(const CatapultAttack & ca)
|
void CPlayerInterface::battleCatapultAttacked(const CatapultAttack & ca)
|
||||||
@@ -779,17 +791,6 @@ void CPlayerInterface::battleCatapultAttacked(const CatapultAttack & ca)
|
|||||||
battleInt->stackIsCatapulting(ca);
|
battleInt->stackIsCatapulting(ca);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPlayerInterface::battleStacksRemoved(const BattleStacksRemoved & bsr)
|
|
||||||
{
|
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
|
||||||
BATTLE_EVENT_POSSIBLE_RETURN;
|
|
||||||
|
|
||||||
for (auto & elem : bsr.stackIDs) //for each removed stack
|
|
||||||
{
|
|
||||||
battleInt->stackRemoved(elem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPlayerInterface::battleNewRound(int round) //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
void CPlayerInterface::battleNewRound(int round) //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
||||||
{
|
{
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
@@ -864,9 +865,9 @@ BattleAction CPlayerInterface::activeStack(const CStack * stack) //called when i
|
|||||||
BattleAction ret = *(CBattleInterface::givenCommand.data);
|
BattleAction ret = *(CBattleInterface::givenCommand.data);
|
||||||
vstd::clear_pointer(CBattleInterface::givenCommand.data);
|
vstd::clear_pointer(CBattleInterface::givenCommand.data);
|
||||||
|
|
||||||
if (ret.actionType == Battle::CANCEL)
|
if(ret.actionType == EActionType::CANCEL)
|
||||||
{
|
{
|
||||||
if (stackId != ret.stackNumber)
|
if(stackId != ret.stackNumber)
|
||||||
logGlobal->error("Not current active stack action canceled");
|
logGlobal->error("Not current active stack action canceled");
|
||||||
logGlobal->trace("Canceled command for %s", stackName);
|
logGlobal->trace("Canceled command for %s", stackName);
|
||||||
}
|
}
|
||||||
@@ -932,37 +933,36 @@ void CPlayerInterface::battleTriggerEffect (const BattleTriggerEffect & bte)
|
|||||||
RETURN_IF_QUICK_COMBAT;
|
RETURN_IF_QUICK_COMBAT;
|
||||||
battleInt->battleTriggerEffect(bte);
|
battleInt->battleTriggerEffect(bte);
|
||||||
}
|
}
|
||||||
void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa)
|
void CPlayerInterface::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, const std::vector<MetaString> & battleLog)
|
||||||
{
|
{
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
EVENT_HANDLER_CALLED_BY_CLIENT;
|
||||||
BATTLE_EVENT_POSSIBLE_RETURN;
|
BATTLE_EVENT_POSSIBLE_RETURN;
|
||||||
|
|
||||||
std::vector<StackAttackedInfo> arg;
|
std::vector<StackAttackedInfo> arg;
|
||||||
for (auto & elem : bsa)
|
for(auto & elem : bsa)
|
||||||
{
|
{
|
||||||
const CStack *defender = cb->battleGetStackByID(elem.stackAttacked, false);
|
const CStack * defender = cb->battleGetStackByID(elem.stackAttacked, false);
|
||||||
const CStack *attacker = cb->battleGetStackByID(elem.attackerID, false);
|
const CStack * attacker = cb->battleGetStackByID(elem.attackerID, false);
|
||||||
if (elem.isEffect())
|
if(elem.isEffect())
|
||||||
{
|
{
|
||||||
if (defender && !elem.isSecondary())
|
if(defender && !elem.isSecondary())
|
||||||
battleInt->displayEffect(elem.effect, defender->position);
|
battleInt->displayEffect(elem.effect, defender->getPosition());
|
||||||
}
|
}
|
||||||
if (elem.isSpell())
|
if(elem.isSpell())
|
||||||
{
|
{
|
||||||
if (defender)
|
if(defender)
|
||||||
battleInt->displaySpellEffect(elem.spellID, defender->position);
|
battleInt->displaySpellEffect(elem.spellID, defender->getPosition());
|
||||||
}
|
}
|
||||||
//FIXME: why action is deleted during enchanter cast?
|
//FIXME: why action is deleted during enchanter cast?
|
||||||
bool remoteAttack = false;
|
bool remoteAttack = false;
|
||||||
|
|
||||||
if (LOCPLINT->curAction)
|
if(LOCPLINT->curAction)
|
||||||
remoteAttack |= LOCPLINT->curAction->actionType != Battle::WALK_AND_ATTACK;
|
remoteAttack |= LOCPLINT->curAction->actionType != EActionType::WALK_AND_ATTACK;
|
||||||
|
|
||||||
StackAttackedInfo to_put = {defender, elem.damageAmount, elem.killedAmount, attacker, remoteAttack, elem.killed(), elem.willRebirth(), elem.cloneKilled()};
|
StackAttackedInfo to_put = {defender, elem.damageAmount, elem.killedAmount, attacker, remoteAttack, elem.killed(), elem.willRebirth(), elem.cloneKilled()};
|
||||||
arg.push_back(to_put);
|
arg.push_back(to_put);
|
||||||
}
|
}
|
||||||
|
battleInt->stacksAreAttacked(arg, battleLog);
|
||||||
battleInt->stacksAreAttacked(arg);
|
|
||||||
}
|
}
|
||||||
void CPlayerInterface::battleAttack(const BattleAttack * ba)
|
void CPlayerInterface::battleAttack(const BattleAttack * ba)
|
||||||
{
|
{
|
||||||
@@ -982,13 +982,13 @@ void CPlayerInterface::battleAttack(const BattleAttack * ba)
|
|||||||
if(ba->lucky()) //lucky hit
|
if(ba->lucky()) //lucky hit
|
||||||
{
|
{
|
||||||
battleInt->console->addText(attacker->formatGeneralMessage(-45));
|
battleInt->console->addText(attacker->formatGeneralMessage(-45));
|
||||||
battleInt->displayEffect(18, attacker->position);
|
battleInt->displayEffect(18, attacker->getPosition());
|
||||||
CCS->soundh->playSound(soundBase::GOODLUCK);
|
CCS->soundh->playSound(soundBase::GOODLUCK);
|
||||||
}
|
}
|
||||||
if(ba->unlucky()) //unlucky hit
|
if(ba->unlucky()) //unlucky hit
|
||||||
{
|
{
|
||||||
battleInt->console->addText(attacker->formatGeneralMessage(-44));
|
battleInt->console->addText(attacker->formatGeneralMessage(-44));
|
||||||
battleInt->displayEffect(48, attacker->position);
|
battleInt->displayEffect(48, attacker->getPosition());
|
||||||
CCS->soundh->playSound(soundBase::BADLUCK);
|
CCS->soundh->playSound(soundBase::BADLUCK);
|
||||||
}
|
}
|
||||||
if(ba->deathBlow())
|
if(ba->deathBlow())
|
||||||
@@ -997,12 +997,23 @@ void CPlayerInterface::battleAttack(const BattleAttack * ba)
|
|||||||
for(auto & elem : ba->bsa)
|
for(auto & elem : ba->bsa)
|
||||||
{
|
{
|
||||||
const CStack * attacked = cb->battleGetStackByID(elem.stackAttacked);
|
const CStack * attacked = cb->battleGetStackByID(elem.stackAttacked);
|
||||||
battleInt->displayEffect(73, attacked->position);
|
battleInt->displayEffect(73, attacked->getPosition());
|
||||||
}
|
}
|
||||||
CCS->soundh->playSound(soundBase::deathBlow);
|
CCS->soundh->playSound(soundBase::deathBlow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
battleInt->displayCustomEffects(ba->customEffects);
|
||||||
|
|
||||||
battleInt->waitForAnims();
|
battleInt->waitForAnims();
|
||||||
|
|
||||||
|
auto actionTarget = curAction->getTarget(cb.get());
|
||||||
|
|
||||||
|
if(actionTarget.empty() || (actionTarget.size() < 2 && !ba->shot()))
|
||||||
|
{
|
||||||
|
logNetwork->error("Invalid current action: no destination.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(ba->shot())
|
if(ba->shot())
|
||||||
{
|
{
|
||||||
for(auto & elem : ba->bsa)
|
for(auto & elem : ba->bsa)
|
||||||
@@ -1010,17 +1021,22 @@ void CPlayerInterface::battleAttack(const BattleAttack * ba)
|
|||||||
if(!elem.isSecondary()) //display projectile only for primary target
|
if(!elem.isSecondary()) //display projectile only for primary target
|
||||||
{
|
{
|
||||||
const CStack * attacked = cb->battleGetStackByID(elem.stackAttacked);
|
const CStack * attacked = cb->battleGetStackByID(elem.stackAttacked);
|
||||||
battleInt->stackAttacking(attacker, attacked->position, attacked, true);
|
battleInt->stackAttacking(attacker, attacked->getPosition(), attacked, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
auto attackFrom = actionTarget.at(0).hexValue;
|
||||||
|
auto attackTarget = actionTarget.at(1).hexValue;
|
||||||
|
|
||||||
|
//TODO: use information from BattleAttack but not curAction
|
||||||
|
|
||||||
int shift = 0;
|
int shift = 0;
|
||||||
if(ba->counter() && BattleHex::mutualPosition(curAction->destinationTile, attacker->position) < 0)
|
if(ba->counter() && BattleHex::mutualPosition(attackTarget, attacker->getPosition()) < 0)
|
||||||
{
|
{
|
||||||
int distp = BattleHex::getDistance(curAction->destinationTile + 1, attacker->position);
|
int distp = BattleHex::getDistance(attackTarget + 1, attacker->getPosition());
|
||||||
int distm = BattleHex::getDistance(curAction->destinationTile - 1, attacker->position);
|
int distm = BattleHex::getDistance(attackTarget - 1, attacker->getPosition());
|
||||||
|
|
||||||
if(distp < distm)
|
if(distp < distm)
|
||||||
shift = 1;
|
shift = 1;
|
||||||
@@ -1028,25 +1044,21 @@ void CPlayerInterface::battleAttack(const BattleAttack * ba)
|
|||||||
shift = -1;
|
shift = -1;
|
||||||
}
|
}
|
||||||
const CStack * attacked = cb->battleGetStackByID(ba->bsa.begin()->stackAttacked);
|
const CStack * attacked = cb->battleGetStackByID(ba->bsa.begin()->stackAttacked);
|
||||||
battleInt->stackAttacking(attacker, ba->counter() ? curAction->destinationTile + shift : curAction->additionalInfo, attacked, false);
|
battleInt->stackAttacking(attacker, ba->counter() ? BattleHex(attackTarget + shift) : attackTarget, attacked, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
//battleInt->waitForAnims(); //FIXME: freeze
|
//battleInt->waitForAnims(); //FIXME: freeze
|
||||||
|
|
||||||
if(ba->spellLike())
|
if(ba->spellLike())
|
||||||
{
|
{
|
||||||
|
//TODO: use information from BattleAttack but not curAction
|
||||||
|
|
||||||
|
auto destination = actionTarget.at(0).hexValue;
|
||||||
//display hit animation
|
//display hit animation
|
||||||
SpellID spellID = ba->spellID;
|
SpellID spellID = ba->spellID;
|
||||||
battleInt->displaySpellHit(spellID, curAction->destinationTile);
|
battleInt->displaySpellHit(spellID, destination);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void CPlayerInterface::battleObstaclePlaced(const CObstacleInstance &obstacle)
|
|
||||||
{
|
|
||||||
EVENT_HANDLER_CALLED_BY_CLIENT;
|
|
||||||
BATTLE_EVENT_POSSIBLE_RETURN;
|
|
||||||
|
|
||||||
battleInt->obstaclePlaced(obstacle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPlayerInterface::battleGateStateChanged(const EGateState state)
|
void CPlayerInterface::battleGateStateChanged(const EGateState state)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -212,15 +212,12 @@ public:
|
|||||||
void battleSpellCast(const BattleSpellCast *sc) override;
|
void battleSpellCast(const BattleSpellCast *sc) override;
|
||||||
void battleStacksEffectsSet(const SetStackEffect & sse) override; //called when a specific effect is set to stacks
|
void battleStacksEffectsSet(const SetStackEffect & sse) override; //called when a specific effect is set to stacks
|
||||||
void battleTriggerEffect(const BattleTriggerEffect & bte) override; //various one-shot effect
|
void battleTriggerEffect(const BattleTriggerEffect & bte) override; //various one-shot effect
|
||||||
void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) override;
|
void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, const std::vector<MetaString> & battleLog) override;
|
||||||
void battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) override; //called by engine just before battle starts; side=0 - left, side=1 - right
|
void battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) override; //called by engine just before battle starts; side=0 - left, side=1 - right
|
||||||
void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right
|
void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override; //called by engine when battle starts; side=0 - left, side=1 - right
|
||||||
void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom) override; //called when stacks are healed / resurrected
|
void battleUnitsChanged(const std::vector<UnitChanges> & units, const std::vector<CustomEffectInfo> & customEffects, const std::vector<MetaString> & battleLog) override;
|
||||||
void battleNewStackAppeared(const CStack * stack) override; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned
|
void battleObstaclesChanged(const std::vector<ObstacleChanges> & obstacles) override;
|
||||||
void battleObstaclesRemoved(const std::set<si32> & removedObstacles) override; //called when a certain set of obstacles is removed from batlefield; IDs of them are given
|
|
||||||
void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
|
void battleCatapultAttacked(const CatapultAttack & ca) override; //called when catapult makes an attack
|
||||||
void battleStacksRemoved(const BattleStacksRemoved & bsr) override; //called when certain stack is completely removed from battlefield
|
|
||||||
void battleObstaclePlaced(const CObstacleInstance &obstacle) override;
|
|
||||||
void battleGateStateChanged(const EGateState state) override;
|
void battleGateStateChanged(const EGateState state) override;
|
||||||
void yourTacticPhase(int distance) override;
|
void yourTacticPhase(int distance) override;
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -3455,7 +3455,8 @@ void CBonusSelection::selectMap(int mapNr, bool initialSelect)
|
|||||||
//get header
|
//get header
|
||||||
std::string & headerStr = ourCampaign->camp->mapPieces.find(mapNr)->second;
|
std::string & headerStr = ourCampaign->camp->mapPieces.find(mapNr)->second;
|
||||||
auto buffer = reinterpret_cast<const ui8 *>(headerStr.data());
|
auto buffer = reinterpret_cast<const ui8 *>(headerStr.data());
|
||||||
ourHeader = CMapService::loadMapHeader(buffer, headerStr.size(), scenarioName);
|
CMapService mapService;
|
||||||
|
ourHeader = mapService.loadMapHeader(buffer, headerStr.size(), scenarioName);
|
||||||
|
|
||||||
std::map<ui8, std::string> names;
|
std::map<ui8, std::string> names;
|
||||||
names[1] = settings["general"]["playerName"].String();
|
names[1] = settings["general"]["playerName"].String();
|
||||||
|
|||||||
+5
-3
@@ -41,6 +41,7 @@
|
|||||||
#include "../lib/VCMI_Lib.h"
|
#include "../lib/VCMI_Lib.h"
|
||||||
#include "../lib/VCMIDirs.h"
|
#include "../lib/VCMIDirs.h"
|
||||||
#include "../lib/mapping/CMap.h"
|
#include "../lib/mapping/CMap.h"
|
||||||
|
#include "../lib/mapping/CMapService.h"
|
||||||
#include "../lib/JsonNode.h"
|
#include "../lib/JsonNode.h"
|
||||||
#include "mapHandler.h"
|
#include "mapHandler.h"
|
||||||
#include "../lib/CConfigHandler.h"
|
#include "../lib/CConfigHandler.h"
|
||||||
@@ -153,7 +154,7 @@ void CClient::waitForMoveAndSend(PlayerColor color)
|
|||||||
setThreadName("CClient::waitForMoveAndSend");
|
setThreadName("CClient::waitForMoveAndSend");
|
||||||
assert(vstd::contains(battleints, color));
|
assert(vstd::contains(battleints, color));
|
||||||
BattleAction ba = battleints[color]->activeStack(gs->curB->battleGetStackByID(gs->curB->activeStack, false));
|
BattleAction ba = battleints[color]->activeStack(gs->curB->battleGetStackByID(gs->curB->activeStack, false));
|
||||||
if(ba.actionType != Battle::CANCEL)
|
if(ba.actionType != EActionType::CANCEL)
|
||||||
{
|
{
|
||||||
logNetwork->trace("Send battle action to server: %s", ba.toString());
|
logNetwork->trace("Send battle action to server: %s", ba.toString());
|
||||||
MakeAction temp_action(ba);
|
MakeAction temp_action(ba);
|
||||||
@@ -413,10 +414,11 @@ void CClient::newGame( CConnection *con, StartInfo *si )
|
|||||||
c.disableSmartPointerSerialization();
|
c.disableSmartPointerSerialization();
|
||||||
|
|
||||||
// Initialize game state
|
// Initialize game state
|
||||||
|
CMapService mapService;
|
||||||
gs = new CGameState();
|
gs = new CGameState();
|
||||||
logNetwork->info("\tCreating gamestate: %i",tmh.getDiff());
|
logNetwork->info("\tCreating gamestate: %i",tmh.getDiff());
|
||||||
|
|
||||||
gs->init(si, settings["general"]["saveRandomMaps"].Bool());
|
gs->init(&mapService, si, settings["general"]["saveRandomMaps"].Bool());
|
||||||
logNetwork->info("Initializing GameState (together): %d ms", tmh.getDiff());
|
logNetwork->info("Initializing GameState (together): %d ms", tmh.getDiff());
|
||||||
|
|
||||||
// Now after possible random map gen, we know exact player count.
|
// Now after possible random map gen, we know exact player count.
|
||||||
@@ -951,7 +953,7 @@ void CClient::installNewBattleInterface(std::shared_ptr<CBattleGameInterface> ba
|
|||||||
if(needCallback)
|
if(needCallback)
|
||||||
{
|
{
|
||||||
logGlobal->trace("\tInitializing the battle interface for player %s", *color);
|
logGlobal->trace("\tInitializing the battle interface for player %s", *color);
|
||||||
auto cbc = std::make_shared<CBattleCallback>(gs, color, this);
|
auto cbc = std::make_shared<CBattleCallback>(color, this);
|
||||||
battleCallbacks[colorUsed] = cbc;
|
battleCallbacks[colorUsed] = cbc;
|
||||||
battleInterface->init(cbc);
|
battleInterface->init(cbc);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ class CGameState;
|
|||||||
class CGameInterface;
|
class CGameInterface;
|
||||||
class CConnection;
|
class CConnection;
|
||||||
class CCallback;
|
class CCallback;
|
||||||
struct BattleAction;
|
class BattleAction;
|
||||||
struct SharedMemory;
|
struct SharedMemory;
|
||||||
class CClient;
|
class CClient;
|
||||||
class CScriptingModule;
|
class CScriptingModule;
|
||||||
|
|||||||
@@ -645,11 +645,6 @@ void BattleTriggerEffect::applyCl(CClient * cl)
|
|||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleTriggerEffect, *this);
|
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleTriggerEffect, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleObstaclePlaced::applyCl(CClient * cl)
|
|
||||||
{
|
|
||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleObstaclePlaced, *obstacle);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BattleUpdateGateState::applyFirstCl(CClient * cl)
|
void BattleUpdateGateState::applyFirstCl(CClient * cl)
|
||||||
{
|
{
|
||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleGateStateChanged, state);
|
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleGateStateChanged, state);
|
||||||
@@ -667,30 +662,14 @@ void BattleStackMoved::applyFirstCl(CClient *cl)
|
|||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStackMoved, movedStack, tilesToMove, distance);
|
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStackMoved, movedStack, tilesToMove, distance);
|
||||||
}
|
}
|
||||||
|
|
||||||
//void BattleStackAttacked::(CClient *cl)
|
|
||||||
void BattleStackAttacked::applyFirstCl(CClient *cl)
|
|
||||||
{
|
|
||||||
std::vector<BattleStackAttacked> bsa;
|
|
||||||
bsa.push_back(*this);
|
|
||||||
|
|
||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, bsa);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BattleAttack::applyFirstCl(CClient *cl)
|
void BattleAttack::applyFirstCl(CClient *cl)
|
||||||
{
|
{
|
||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleAttack, this);
|
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleAttack, this);
|
||||||
for (auto & elem : bsa)
|
|
||||||
{
|
|
||||||
for (int z=0; z<elem.healedStacks.size(); ++z)
|
|
||||||
{
|
|
||||||
elem.healedStacks[z].applyCl(cl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleAttack::applyCl(CClient *cl)
|
void BattleAttack::applyCl(CClient *cl)
|
||||||
{
|
{
|
||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, bsa);
|
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, bsa, battleLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StartAction::applyFirstCl(CClient *cl)
|
void StartAction::applyFirstCl(CClient *cl)
|
||||||
@@ -712,7 +691,7 @@ void SetStackEffect::applyCl(CClient *cl)
|
|||||||
|
|
||||||
void StacksInjured::applyCl(CClient *cl)
|
void StacksInjured::applyCl(CClient *cl)
|
||||||
{
|
{
|
||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, stacks);
|
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksAttacked, stacks, battleLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleResultsApplied::applyCl(CClient *cl)
|
void BattleResultsApplied::applyCl(CClient *cl)
|
||||||
@@ -722,20 +701,15 @@ void BattleResultsApplied::applyCl(CClient *cl)
|
|||||||
callInterfaceIfPresent(cl, PlayerColor::SPECTATOR, &IGameEventsReceiver::battleResultsApplied);
|
callInterfaceIfPresent(cl, PlayerColor::SPECTATOR, &IGameEventsReceiver::battleResultsApplied);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StacksHealedOrResurrected::applyCl(CClient * cl)
|
void BattleUnitsChanged::applyCl(CClient * cl)
|
||||||
{
|
{
|
||||||
std::vector<std::pair<ui32, ui32> > shiftedHealed;
|
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleUnitsChanged, changedStacks, customEffects, battleLog);
|
||||||
for(auto & elem : healedStacks)
|
|
||||||
{
|
|
||||||
shiftedHealed.push_back(std::make_pair(elem.stackId, (ui32)elem.delta));
|
|
||||||
}
|
|
||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksHealedRes, shiftedHealed, lifeDrain, tentHealing, drainedFrom);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObstaclesRemoved::applyCl(CClient *cl)
|
void BattleObstaclesChanged::applyCl(CClient *cl)
|
||||||
{
|
{
|
||||||
//inform interfaces about removed obstacles
|
//inform interfaces about removed obstacles
|
||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleObstaclesRemoved, obstacles);
|
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleObstaclesChanged, changes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CatapultAttack::applyCl(CClient *cl)
|
void CatapultAttack::applyCl(CClient *cl)
|
||||||
@@ -744,17 +718,6 @@ void CatapultAttack::applyCl(CClient *cl)
|
|||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleCatapultAttacked, *this);
|
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleCatapultAttacked, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleStacksRemoved::applyFirstCl(CClient * cl)
|
|
||||||
{
|
|
||||||
//inform interfaces about removed stacks
|
|
||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleStacksRemoved, *this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void BattleStackAdded::applyCl(CClient *cl)
|
|
||||||
{
|
|
||||||
callBattleInterfaceIfPresentForBothSides(cl, &IBattleEventsReceiver::battleNewStackAppeared, GS(cl)->curB->stacks.back());
|
|
||||||
}
|
|
||||||
|
|
||||||
CGameState* CPackForClient::GS(CClient *cl)
|
CGameState* CPackForClient::GS(CClient *cl)
|
||||||
{
|
{
|
||||||
return cl->gs;
|
return cl->gs;
|
||||||
|
|||||||
@@ -104,8 +104,6 @@
|
|||||||
<Unit filename="../CCallback.h" />
|
<Unit filename="../CCallback.h" />
|
||||||
<Unit filename="CBitmapHandler.cpp" />
|
<Unit filename="CBitmapHandler.cpp" />
|
||||||
<Unit filename="CBitmapHandler.h" />
|
<Unit filename="CBitmapHandler.h" />
|
||||||
<Unit filename="CDefHandler.cpp" />
|
|
||||||
<Unit filename="CDefHandler.h" />
|
|
||||||
<Unit filename="CGameInfo.cpp" />
|
<Unit filename="CGameInfo.cpp" />
|
||||||
<Unit filename="CGameInfo.h" />
|
<Unit filename="CGameInfo.h" />
|
||||||
<Unit filename="CMT.cpp" />
|
<Unit filename="CMT.cpp" />
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ CAttackAnimation::CAttackAnimation(CBattleInterface *_owner, const CStack *attac
|
|||||||
dest(_dest), attackedStack(defender), attackingStack(attacker)
|
dest(_dest), attackedStack(defender), attackingStack(attacker)
|
||||||
{
|
{
|
||||||
assert(attackingStack && "attackingStack is nullptr in CBattleAttack::CBattleAttack !\n");
|
assert(attackingStack && "attackingStack is nullptr in CBattleAttack::CBattleAttack !\n");
|
||||||
attackingStackPosBeforeReturn = attackingStack->position;
|
attackingStackPosBeforeReturn = attackingStack->getPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
CDefenceAnimation::CDefenceAnimation(StackAttackedInfo _attackedInfo, CBattleInterface * _owner)
|
CDefenceAnimation::CDefenceAnimation(StackAttackedInfo _attackedInfo, CBattleInterface * _owner)
|
||||||
@@ -184,9 +184,9 @@ bool CDefenceAnimation::init()
|
|||||||
|
|
||||||
|
|
||||||
//reverse unit if necessary
|
//reverse unit if necessary
|
||||||
if (attacker && owner->getCurrentPlayerInterface()->cb->isToReverse(stack->position, attacker->position, owner->creDir[stack->ID], attacker->doubleWide(), owner->creDir[attacker->ID]))
|
if(attacker && owner->getCurrentPlayerInterface()->cb->isToReverse(stack->getPosition(), attacker->getPosition(), owner->creDir[stack->ID], attacker->doubleWide(), owner->creDir[attacker->ID]))
|
||||||
{
|
{
|
||||||
owner->addNewAnim(new CReverseAnimation(owner, stack, stack->position, true));
|
owner->addNewAnim(new CReverseAnimation(owner, stack, stack->getPosition(), true));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//unit reversed
|
//unit reversed
|
||||||
@@ -226,11 +226,10 @@ std::string CDefenceAnimation::getMySound()
|
|||||||
{
|
{
|
||||||
if(killed)
|
if(killed)
|
||||||
return battle_sound(stack->getCreature(), killed);
|
return battle_sound(stack->getCreature(), killed);
|
||||||
|
else if(stack->defendingAnim)
|
||||||
if (vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM))
|
|
||||||
return battle_sound(stack->getCreature(), defend);
|
return battle_sound(stack->getCreature(), defend);
|
||||||
|
else
|
||||||
return battle_sound(stack->getCreature(), wince);
|
return battle_sound(stack->getCreature(), wince);
|
||||||
}
|
}
|
||||||
|
|
||||||
CCreatureAnim::EAnimType CDefenceAnimation::getMyAnimType()
|
CCreatureAnim::EAnimType CDefenceAnimation::getMyAnimType()
|
||||||
@@ -243,10 +242,10 @@ CCreatureAnim::EAnimType CDefenceAnimation::getMyAnimType()
|
|||||||
return CCreatureAnim::DEATH;
|
return CCreatureAnim::DEATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM))
|
if(stack->defendingAnim)
|
||||||
return CCreatureAnim::DEFENCE;
|
return CCreatureAnim::DEFENCE;
|
||||||
|
else
|
||||||
return CCreatureAnim::HITTED;
|
return CCreatureAnim::HITTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CDefenceAnimation::startAnimation()
|
void CDefenceAnimation::startAnimation()
|
||||||
@@ -324,7 +323,7 @@ bool CMeleeAttackAnimation::init()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool toReverse = owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStackPosBeforeReturn, attackedStack->position, owner->creDir[stack->ID], attackedStack->doubleWide(), owner->creDir[attackedStack->ID]);
|
bool toReverse = owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStackPosBeforeReturn, attackedStack->getPosition(), owner->creDir[stack->ID], attackedStack->doubleWide(), owner->creDir[attackedStack->ID]);
|
||||||
|
|
||||||
if(toReverse)
|
if(toReverse)
|
||||||
{
|
{
|
||||||
@@ -366,7 +365,7 @@ bool CMeleeAttackAnimation::init()
|
|||||||
int mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn, dest);
|
int mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn, dest);
|
||||||
if(mutPos == -1 && attackingStack->doubleWide())
|
if(mutPos == -1 && attackingStack->doubleWide())
|
||||||
{
|
{
|
||||||
mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn + revShiftattacker, attackedStack->position);
|
mutPos = BattleHex::mutualPosition(attackingStackPosBeforeReturn + revShiftattacker, attackedStack->getPosition());
|
||||||
}
|
}
|
||||||
if (mutPos == -1 && attackedStack->doubleWide())
|
if (mutPos == -1 && attackedStack->doubleWide())
|
||||||
{
|
{
|
||||||
@@ -558,7 +557,7 @@ CMovementAnimation::CMovementAnimation(CBattleInterface *_owner, const CStack *_
|
|||||||
: CBattleStackAnimation(_owner, _stack),
|
: CBattleStackAnimation(_owner, _stack),
|
||||||
destTiles(_destTiles),
|
destTiles(_destTiles),
|
||||||
curentMoveIndex(0),
|
curentMoveIndex(0),
|
||||||
oldPos(stack->position),
|
oldPos(stack->getPosition()),
|
||||||
begX(0), begY(0),
|
begX(0), begY(0),
|
||||||
distanceX(0), distanceY(0),
|
distanceX(0), distanceY(0),
|
||||||
timeToMove(0.0),
|
timeToMove(0.0),
|
||||||
@@ -731,16 +730,18 @@ bool CShootingAnimation::init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//reverse unit if necessary
|
//reverse unit if necessary
|
||||||
if (attackingStack && attackedStack && owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->position, attackedStack->position, owner->creDir[attackingStack->ID], attackingStack->doubleWide(), owner->creDir[attackedStack->ID]))
|
if (attackingStack && attackedStack && owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->getPosition(), attackedStack->getPosition(), owner->creDir[attackingStack->ID], attackingStack->doubleWide(), owner->creDir[attackedStack->ID]))
|
||||||
{
|
{
|
||||||
owner->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->position, true));
|
owner->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->getPosition(), true));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// opponent must face attacker ( = different directions) before he can be attacked
|
// opponent must face attacker ( = different directions) before he can be attacked
|
||||||
if (attackingStack && attackedStack &&
|
//FIXME: this cause freeze
|
||||||
owner->creDir[attackingStack->ID] == owner->creDir[attackedStack->ID])
|
|
||||||
return false;
|
// if (attackingStack && attackedStack &&
|
||||||
|
// owner->creDir[attackingStack->ID] == owner->creDir[attackedStack->ID])
|
||||||
|
// return false;
|
||||||
|
|
||||||
// Create the projectile animation
|
// Create the projectile animation
|
||||||
|
|
||||||
@@ -780,7 +781,7 @@ bool CShootingAnimation::init()
|
|||||||
int multiplier = spi.reverse ? -1 : 1;
|
int multiplier = spi.reverse ? -1 : 1;
|
||||||
|
|
||||||
double projectileAngle = atan2(fabs((double)destPos.y - fromPos.y), fabs((double)destPos.x - fromPos.x));
|
double projectileAngle = atan2(fabs((double)destPos.y - fromPos.y), fabs((double)destPos.x - fromPos.x));
|
||||||
if(shooter->position < dest)
|
if(shooter->getPosition() < dest)
|
||||||
projectileAngle = -projectileAngle;
|
projectileAngle = -projectileAngle;
|
||||||
|
|
||||||
// Calculate projectile start position. Offsets are read out of the CRANIM.TXT.
|
// Calculate projectile start position. Offsets are read out of the CRANIM.TXT.
|
||||||
@@ -920,7 +921,7 @@ CCastAnimation::CCastAnimation(CBattleInterface * owner_, const CStack * attacke
|
|||||||
: CRangedAttackAnimation(owner_, attacker, dest_, defender)
|
: CRangedAttackAnimation(owner_, attacker, dest_, defender)
|
||||||
{
|
{
|
||||||
if(!dest_.isValid() && defender)
|
if(!dest_.isValid() && defender)
|
||||||
dest = defender->position;
|
dest = defender->getPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CCastAnimation::init()
|
bool CCastAnimation::init()
|
||||||
@@ -937,17 +938,17 @@ bool CCastAnimation::init()
|
|||||||
//reverse unit if necessary
|
//reverse unit if necessary
|
||||||
if(attackedStack)
|
if(attackedStack)
|
||||||
{
|
{
|
||||||
if(owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->position, attackedStack->position, owner->creDir[attackingStack->ID], attackingStack->doubleWide(), owner->creDir[attackedStack->ID]))
|
if(owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->getPosition(), attackedStack->getPosition(), owner->creDir[attackingStack->ID], attackingStack->doubleWide(), owner->creDir[attackedStack->ID]))
|
||||||
{
|
{
|
||||||
owner->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->position, true));
|
owner->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->getPosition(), true));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(dest.isValid() && owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->position, dest, owner->creDir[attackingStack->ID], false, false))
|
if(dest.isValid() && owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->getPosition(), dest, owner->creDir[attackingStack->ID], false, false))
|
||||||
{
|
{
|
||||||
owner->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->position, true));
|
owner->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->getPosition(), true));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -962,13 +963,13 @@ bool CCastAnimation::init()
|
|||||||
|
|
||||||
// NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise
|
// NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise
|
||||||
fromPos = owner->creAnims[attackingStack->ID]->pos.topLeft();
|
fromPos = owner->creAnims[attackingStack->ID]->pos.topLeft();
|
||||||
//xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner);
|
//xycoord = CClickableHex::getXYUnitAnim(shooter->getPosition(), true, shooter, owner);
|
||||||
|
|
||||||
destPos = CClickableHex::getXYUnitAnim(dest, attackedStack, owner);
|
destPos = CClickableHex::getXYUnitAnim(dest, attackedStack, owner);
|
||||||
|
|
||||||
|
|
||||||
double projectileAngle = atan2(fabs((double)destPos.y - fromPos.y), fabs((double)destPos.x - fromPos.x));
|
double projectileAngle = atan2(fabs((double)destPos.y - fromPos.y), fabs((double)destPos.x - fromPos.x));
|
||||||
if(attackingStack->position < dest)
|
if(attackingStack->getPosition() < dest)
|
||||||
projectileAngle = -projectileAngle;
|
projectileAngle = -projectileAngle;
|
||||||
|
|
||||||
|
|
||||||
@@ -1045,7 +1046,6 @@ void CCastAnimation::endAnim()
|
|||||||
CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx, int _dy, bool _Vflip, bool _alignToBottom)
|
CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx, int _dy, bool _Vflip, bool _alignToBottom)
|
||||||
: CBattleAnimation(_owner),
|
: CBattleAnimation(_owner),
|
||||||
destTile(BattleHex::INVALID),
|
destTile(BattleHex::INVALID),
|
||||||
customAnim(_customAnim),
|
|
||||||
x(_x),
|
x(_x),
|
||||||
y(_y),
|
y(_y),
|
||||||
dx(_dx),
|
dx(_dx),
|
||||||
@@ -1053,13 +1053,29 @@ CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _custo
|
|||||||
Vflip(_Vflip),
|
Vflip(_Vflip),
|
||||||
alignToBottom(_alignToBottom)
|
alignToBottom(_alignToBottom)
|
||||||
{
|
{
|
||||||
logAnim->debug("Created effect animation %s", customAnim);
|
logAnim->debug("Created effect animation %s", _customAnim);
|
||||||
|
|
||||||
|
customAnim = std::make_shared<CAnimation>(_customAnim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::shared_ptr<CAnimation> _customAnim, int _x, int _y, int _dx, int _dy)
|
||||||
|
: CBattleAnimation(_owner),
|
||||||
|
destTile(BattleHex::INVALID),
|
||||||
|
customAnim(_customAnim),
|
||||||
|
x(_x),
|
||||||
|
y(_y),
|
||||||
|
dx(_dx),
|
||||||
|
dy(_dy),
|
||||||
|
Vflip(false),
|
||||||
|
alignToBottom(false)
|
||||||
|
{
|
||||||
|
logAnim->debug("Created custom effect animation");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip, bool _alignToBottom)
|
CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip, bool _alignToBottom)
|
||||||
: CBattleAnimation(_owner),
|
: CBattleAnimation(_owner),
|
||||||
destTile(_destTile),
|
destTile(_destTile),
|
||||||
customAnim(_customAnim),
|
|
||||||
x(-1),
|
x(-1),
|
||||||
y(-1),
|
y(-1),
|
||||||
dx(0),
|
dx(0),
|
||||||
@@ -1067,24 +1083,18 @@ CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _custo
|
|||||||
Vflip(_Vflip),
|
Vflip(_Vflip),
|
||||||
alignToBottom(_alignToBottom)
|
alignToBottom(_alignToBottom)
|
||||||
{
|
{
|
||||||
logAnim->debug("Created effect animation %s", customAnim);
|
logAnim->debug("Created effect animation %s", _customAnim);
|
||||||
|
customAnim = std::make_shared<CAnimation>(_customAnim);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool CEffectAnimation::init()
|
bool CEffectAnimation::init()
|
||||||
{
|
{
|
||||||
if(!isEarliest(true))
|
if(!isEarliest(true))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(customAnim.empty())
|
|
||||||
{
|
|
||||||
endAnim();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool areaEffect = (!destTile.isValid() && x == -1 && y == -1);
|
const bool areaEffect = (!destTile.isValid() && x == -1 && y == -1);
|
||||||
|
|
||||||
std::shared_ptr<CAnimation> animation = std::make_shared<CAnimation>(customAnim);
|
std::shared_ptr<CAnimation> animation = customAnim;
|
||||||
|
|
||||||
animation->preload();
|
animation->preload();
|
||||||
if(Vflip)
|
if(Vflip)
|
||||||
|
|||||||
@@ -238,7 +238,7 @@ class CEffectAnimation : public CBattleAnimation
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
BattleHex destTile;
|
BattleHex destTile;
|
||||||
std::string customAnim;
|
std::shared_ptr<CAnimation> customAnim;
|
||||||
int x, y, dx, dy;
|
int x, y, dx, dy;
|
||||||
bool Vflip;
|
bool Vflip;
|
||||||
bool alignToBottom;
|
bool alignToBottom;
|
||||||
@@ -248,6 +248,9 @@ public:
|
|||||||
void endAnim() override;
|
void endAnim() override;
|
||||||
|
|
||||||
CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx = 0, int _dy = 0, bool _Vflip = false, bool _alignToBottom = false);
|
CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx = 0, int _dy = 0, bool _Vflip = false, bool _alignToBottom = false);
|
||||||
|
|
||||||
|
CEffectAnimation(CBattleInterface * _owner, std::shared_ptr<CAnimation> _customAnim, int _x, int _y, int _dx = 0, int _dy = 0);
|
||||||
|
|
||||||
CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip = false, bool _alignToBottom = false);
|
CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, BattleHex _destTile, bool _Vflip = false, bool _alignToBottom = false);
|
||||||
virtual ~CEffectAnimation(){};
|
virtual ~CEffectAnimation(){};
|
||||||
};
|
};
|
||||||
|
|||||||
+355
-328
File diff suppressed because it is too large
Load Diff
@@ -19,7 +19,6 @@
|
|||||||
class CLabel;
|
class CLabel;
|
||||||
class CCreatureSet;
|
class CCreatureSet;
|
||||||
class CGHeroInstance;
|
class CGHeroInstance;
|
||||||
class CDefHandler;
|
|
||||||
class CStack;
|
class CStack;
|
||||||
class CCallback;
|
class CCallback;
|
||||||
class CButton;
|
class CButton;
|
||||||
@@ -30,7 +29,7 @@ struct BattleSpellCast;
|
|||||||
struct CObstacleInstance;
|
struct CObstacleInstance;
|
||||||
template <typename T> struct CondSh;
|
template <typename T> struct CondSh;
|
||||||
struct SetStackEffect;
|
struct SetStackEffect;
|
||||||
struct BattleAction;
|
class BattleAction;
|
||||||
class CGTownInstance;
|
class CGTownInstance;
|
||||||
struct CatapultAttack;
|
struct CatapultAttack;
|
||||||
struct CatapultProjectileInfo;
|
struct CatapultProjectileInfo;
|
||||||
@@ -46,15 +45,16 @@ struct ProjectileInfo;
|
|||||||
class CClickableHex;
|
class CClickableHex;
|
||||||
struct BattleHex;
|
struct BattleHex;
|
||||||
struct InfoAboutHero;
|
struct InfoAboutHero;
|
||||||
struct BattleAction;
|
|
||||||
class CBattleGameInterface;
|
class CBattleGameInterface;
|
||||||
|
struct CustomEffectInfo;
|
||||||
class CAnimation;
|
class CAnimation;
|
||||||
|
class IImage;
|
||||||
|
|
||||||
/// Small struct which contains information about the id of the attacked stack, the damage dealt,...
|
/// Small struct which contains information about the id of the attacked stack, the damage dealt,...
|
||||||
struct StackAttackedInfo
|
struct StackAttackedInfo
|
||||||
{
|
{
|
||||||
const CStack *defender; //attacked stack
|
const CStack *defender; //attacked stack
|
||||||
int32_t dmg; //damage dealt
|
int64_t dmg; //damage dealt
|
||||||
unsigned int amountKilled; //how many creatures in stack has been killed
|
unsigned int amountKilled; //how many creatures in stack has been killed
|
||||||
const CStack *attacker; //attacking stack
|
const CStack *attacker; //attacking stack
|
||||||
bool indirectAttack; //if true, stack was attacked indirectly - spell or ranged attack
|
bool indirectAttack; //if true, stack was attacked indirectly - spell or ranged attack
|
||||||
@@ -132,15 +132,8 @@ private:
|
|||||||
|
|
||||||
std::map<int, std::shared_ptr<CAnimation>> idToProjectile;
|
std::map<int, std::shared_ptr<CAnimation>> idToProjectile;
|
||||||
|
|
||||||
std::map<int, CDefHandler *> idToObstacle; //obstacles located on the battlefield
|
std::map<std::string, std::shared_ptr<CAnimation>> animationsCache;
|
||||||
std::map<int, SDL_Surface *> idToAbsoluteObstacle; //obstacles located on the battlefield
|
std::map<si32, std::shared_ptr<CAnimation>> obstacleAnimations;
|
||||||
|
|
||||||
//TODO these should be loaded only when needed (and freed then) but I believe it's rather work for resource manager,
|
|
||||||
//so I didn't implement that (having ongoing RM development)
|
|
||||||
CDefHandler *landMine;
|
|
||||||
CDefHandler *quicksand;
|
|
||||||
CDefHandler *fireWall;
|
|
||||||
CDefHandler *smallForceField[2], *bigForceField[2]; // [side]
|
|
||||||
|
|
||||||
std::map<int, bool> creDir; // <creatureID, if false reverse creature's animation> //TODO: move it to battle callback
|
std::map<int, bool> creDir; // <creatureID, if false reverse creature's animation> //TODO: move it to battle callback
|
||||||
ui8 animCount;
|
ui8 animCount;
|
||||||
@@ -185,7 +178,9 @@ private:
|
|||||||
void printConsoleAttacked(const CStack *defender, int dmg, int killed, const CStack *attacker, bool Multiple);
|
void printConsoleAttacked(const CStack *defender, int dmg, int killed, const CStack *attacker, bool Multiple);
|
||||||
|
|
||||||
std::list<ProjectileInfo> projectiles; //projectiles flying on battlefield
|
std::list<ProjectileInfo> projectiles; //projectiles flying on battlefield
|
||||||
void giveCommand(Battle::ActionType action, BattleHex tile, ui32 stackID, si32 additional=-1, si32 selectedStack = -1);
|
void giveCommand(EActionType action, BattleHex tile = BattleHex(), si32 additional = -1);
|
||||||
|
void sendCommand(BattleAction *& command, const CStack * actor = nullptr);
|
||||||
|
|
||||||
bool isTileAttackable(const BattleHex & number) const; //returns true if tile 'number' is neighboring any tile from active stack's range or is one of these tiles
|
bool isTileAttackable(const BattleHex & number) const; //returns true if tile 'number' is neighboring any tile from active stack's range or is one of these tiles
|
||||||
bool isCatapultAttackable(BattleHex hex) const; //returns true if given tile can be attacked by catapult
|
bool isCatapultAttackable(BattleHex hex) const; //returns true if given tile can be attacked by catapult
|
||||||
|
|
||||||
@@ -259,12 +254,14 @@ private:
|
|||||||
BattleObjectsByHex sortObjectsByHex();
|
BattleObjectsByHex sortObjectsByHex();
|
||||||
void updateBattleAnimations();
|
void updateBattleAnimations();
|
||||||
|
|
||||||
SDL_Surface *getObstacleImage(const CObstacleInstance &oi);
|
IImage * getObstacleImage(const CObstacleInstance & oi);
|
||||||
Point getObstaclePosition(SDL_Surface *image, const CObstacleInstance &obstacle);
|
|
||||||
|
Point getObstaclePosition(IImage * image, const CObstacleInstance & obstacle);
|
||||||
|
|
||||||
void redrawBackgroundWithHexes(const CStack *activeStack);
|
void redrawBackgroundWithHexes(const CStack *activeStack);
|
||||||
/** End of battle screen blitting methods */
|
/** End of battle screen blitting methods */
|
||||||
|
|
||||||
PossibleActions getCasterAction(const CSpell *spell, const ISpellCaster *caster, ECastingMode::ECastingMode mode) const;
|
PossibleActions getCasterAction(const CSpell *spell, const spells::Caster *caster, spells::Mode mode) const;
|
||||||
|
|
||||||
void setHeroAnimation(ui8 side, int phase);
|
void setHeroAnimation(ui8 side, int phase);
|
||||||
public:
|
public:
|
||||||
@@ -329,12 +326,12 @@ public:
|
|||||||
|
|
||||||
//call-ins
|
//call-ins
|
||||||
void startAction(const BattleAction* action);
|
void startAction(const BattleAction* action);
|
||||||
void newStack(const CStack *stack); //new stack appeared on battlefield
|
void unitAdded(const CStack * stack); //new stack appeared on battlefield
|
||||||
void stackRemoved(int stackID); //stack disappeared from batlefiled
|
void stackRemoved(uint32_t stackID); //stack disappeared from batlefiled
|
||||||
void stackActivated(const CStack *stack); //active stack has been changed
|
void stackActivated(const CStack *stack); //active stack has been changed
|
||||||
void stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance); //stack with id number moved to destHex
|
void stackMoved(const CStack *stack, std::vector<BattleHex> destHex, int distance); //stack with id number moved to destHex
|
||||||
void waitForAnims();
|
void waitForAnims();
|
||||||
void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos); //called when a certain amount of stacks has been attacked
|
void stacksAreAttacked(std::vector<StackAttackedInfo> attackedInfos, const std::vector<MetaString> & battleLog); //called when a certain amount of stacks has been attacked
|
||||||
void stackAttacking(const CStack *attacker, BattleHex dest, const CStack *attacked, bool shooting); //called when stack with id ID is attacking something on hex dest
|
void stackAttacking(const CStack *attacker, BattleHex dest, const CStack *attacked, bool shooting); //called when stack with id ID is attacking something on hex dest
|
||||||
void newRoundFirst( int round );
|
void newRoundFirst( int round );
|
||||||
void newRound(int number); //caled when round is ended; number is the number of round
|
void newRound(int number); //caled when round is ended; number is the number of round
|
||||||
@@ -345,6 +342,10 @@ public:
|
|||||||
void spellCast(const BattleSpellCast *sc); //called when a hero casts a spell
|
void spellCast(const BattleSpellCast *sc); //called when a hero casts a spell
|
||||||
void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
|
void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
|
||||||
void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook
|
void castThisSpell(SpellID spellID); //called when player has chosen a spell from spellbook
|
||||||
|
|
||||||
|
void displayBattleLog(const std::vector<MetaString> & battleLog);
|
||||||
|
void displayCustomEffects(const std::vector<CustomEffectInfo> & customEffects);
|
||||||
|
|
||||||
void displayEffect(ui32 effect, BattleHex destTile); //displays custom effect on the battlefield
|
void displayEffect(ui32 effect, BattleHex destTile); //displays custom effect on the battlefield
|
||||||
|
|
||||||
void displaySpellCast(SpellID spellID, BattleHex destinationTile); //displays spell`s cast animation
|
void displaySpellCast(SpellID spellID, BattleHex destinationTile); //displays spell`s cast animation
|
||||||
@@ -370,7 +371,7 @@ public:
|
|||||||
|
|
||||||
void gateStateChanged(const EGateState state);
|
void gateStateChanged(const EGateState state);
|
||||||
|
|
||||||
void initStackProjectile(const CStack *stack);
|
void initStackProjectile(const CStack * stack);
|
||||||
|
|
||||||
const CGHeroInstance *currentHero() const;
|
const CGHeroInstance *currentHero() const;
|
||||||
InfoAboutHero enemyHero() const;
|
InfoAboutHero enemyHero() const;
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ void CBattleHero::clickLeft(tribool down, bool previousState)
|
|||||||
if(!myHero || down || !myOwner->myTurn)
|
if(!myHero || down || !myOwner->myTurn)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(myOwner->getCurrentPlayerInterface()->cb->battleCanCastSpell(myHero, ECastingMode::HERO_CASTING) == ESpellCastProblem::OK) //check conditions
|
if(myOwner->getCurrentPlayerInterface()->cb->battleCanCastSpell(myHero, spells::Mode::HERO) == ESpellCastProblem::OK) //check conditions
|
||||||
{
|
{
|
||||||
for(int it=0; it<GameConstants::BFIELD_SIZE; ++it) //do nothing when any hex is hovered - hero's animation overlaps battlefield
|
for(int it=0; it<GameConstants::BFIELD_SIZE; ++it) //do nothing when any hex is hovered - hero's animation overlaps battlefield
|
||||||
{
|
{
|
||||||
@@ -505,9 +505,9 @@ Point CClickableHex::getXYUnitAnim(BattleHex hexNum, const CStack * stack, CBatt
|
|||||||
assert(cbi);
|
assert(cbi);
|
||||||
|
|
||||||
Point ret(-500, -500); //returned value
|
Point ret(-500, -500); //returned value
|
||||||
if(stack && stack->position < 0) //creatures in turrets
|
if(stack && stack->initialPosition < 0) //creatures in turrets
|
||||||
{
|
{
|
||||||
switch(stack->position)
|
switch(stack->initialPosition)
|
||||||
{
|
{
|
||||||
case -2: //keep
|
case -2: //keep
|
||||||
ret = cbi->siegeH->town->town->clientInfo.siegePositions[18];
|
ret = cbi->siegeH->town->town->clientInfo.siegePositions[18];
|
||||||
@@ -669,105 +669,130 @@ CHeroInfoWindow::CHeroInfoWindow(const InfoAboutHero &hero, Point *position) : C
|
|||||||
new CLabel(39, 186, EFonts::FONT_TINY, EAlignment::CENTER, Colors::WHITE, std::to_string(currentSpellPoints) + "/" + std::to_string(maxSpellPoints));
|
new CLabel(39, 186, EFonts::FONT_TINY, EAlignment::CENTER, Colors::WHITE, std::to_string(currentSpellPoints) + "/" + std::to_string(maxSpellPoints));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStackQueue::update()
|
|
||||||
{
|
|
||||||
stacksSorted.clear();
|
|
||||||
owner->getCurrentPlayerInterface()->cb->battleGetStackQueue(stacksSorted, stackBoxes.size());
|
|
||||||
if(stacksSorted.size())
|
|
||||||
{
|
|
||||||
for (int i = 0; i < stackBoxes.size() ; i++)
|
|
||||||
{
|
|
||||||
stackBoxes[i]->setStack(stacksSorted[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//no stacks on battlefield... what to do with queue?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CStackQueue::CStackQueue(bool Embedded, CBattleInterface * _owner)
|
CStackQueue::CStackQueue(bool Embedded, CBattleInterface * _owner)
|
||||||
:embedded(Embedded), owner(_owner)
|
: embedded(Embedded),
|
||||||
|
owner(_owner)
|
||||||
{
|
{
|
||||||
OBJ_CONSTRUCTION_CAPTURING_ALL;
|
OBJ_CONSTRUCTION_CAPTURING_ALL;
|
||||||
if(embedded)
|
if(embedded)
|
||||||
{
|
{
|
||||||
bg = nullptr;
|
|
||||||
pos.w = QUEUE_SIZE * 37;
|
pos.w = QUEUE_SIZE * 37;
|
||||||
pos.h = 46;
|
pos.h = 46;
|
||||||
pos.x = screen->w/2 - pos.w/2;
|
pos.x = screen->w/2 - pos.w/2;
|
||||||
pos.y = (screen->h - 600)/2 + 10;
|
pos.y = (screen->h - 600)/2 + 10;
|
||||||
|
|
||||||
|
icons = std::make_shared<CAnimation>("CPRSMALL");
|
||||||
|
stateIcons = std::make_shared<CAnimation>("VCMI/BATTLEQUEUE/STATESSMALL");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
bg = BitmapHandler::loadBitmap("DIBOXBCK");
|
|
||||||
pos.w = 800;
|
pos.w = 800;
|
||||||
pos.h = 85;
|
pos.h = 85;
|
||||||
|
|
||||||
|
new CFilledTexture("DIBOXBCK", Rect(0,0, pos.w, pos.h));
|
||||||
|
|
||||||
|
icons = std::make_shared<CAnimation>("TWCRPORT");
|
||||||
|
stateIcons = std::make_shared<CAnimation>("VCMI/BATTLEQUEUE/STATESSMALL");
|
||||||
|
//TODO: where use big icons?
|
||||||
|
//stateIcons = std::make_shared<CAnimation>("VCMI/BATTLEQUEUE/STATESBIG");
|
||||||
}
|
}
|
||||||
|
stateIcons->preload();
|
||||||
|
|
||||||
stackBoxes.resize(QUEUE_SIZE);
|
stackBoxes.resize(QUEUE_SIZE);
|
||||||
for (int i = 0; i < stackBoxes.size(); i++)
|
for (int i = 0; i < stackBoxes.size(); i++)
|
||||||
{
|
{
|
||||||
stackBoxes[i] = new StackBox(embedded);
|
stackBoxes[i] = new StackBox(this);
|
||||||
stackBoxes[i]->moveBy(Point(1 + (embedded ? 36 : 80)*i, 0));
|
stackBoxes[i]->moveBy(Point(1 + (embedded ? 36 : 80)*i, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CStackQueue::~CStackQueue()
|
CStackQueue::~CStackQueue()
|
||||||
{
|
{
|
||||||
SDL_FreeSurface(bg);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStackQueue::showAll(SDL_Surface * to)
|
void CStackQueue::update()
|
||||||
{
|
{
|
||||||
blitBg(to);
|
std::vector<battle::Units> queueData;
|
||||||
|
|
||||||
CIntObject::showAll(to);
|
owner->getCurrentPlayerInterface()->cb->battleGetTurnOrder(queueData, stackBoxes.size(), 0);
|
||||||
}
|
|
||||||
|
|
||||||
void CStackQueue::blitBg( SDL_Surface * to )
|
size_t boxIndex = 0;
|
||||||
{
|
|
||||||
if(bg)
|
for(size_t turn = 0; turn < queueData.size() && boxIndex < stackBoxes.size(); turn++)
|
||||||
{
|
{
|
||||||
SDL_SetClipRect(to, &pos);
|
for(size_t unitIndex = 0; unitIndex < queueData[turn].size() && boxIndex < stackBoxes.size(); boxIndex++, unitIndex++)
|
||||||
CSDL_Ext::fillTexture(to, bg);
|
stackBoxes[boxIndex]->setStack(queueData[turn][unitIndex], turn);
|
||||||
SDL_SetClipRect(to, nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for(; boxIndex < stackBoxes.size(); boxIndex++)
|
||||||
|
stackBoxes[boxIndex]->setStack(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStackQueue::StackBox::showAll(SDL_Surface * to)
|
CStackQueue::StackBox::StackBox(CStackQueue * owner)
|
||||||
{
|
: bg(nullptr),
|
||||||
assert(stack);
|
icon(nullptr),
|
||||||
bg->colorize(stack->owner);
|
amount(nullptr),
|
||||||
CIntObject::showAll(to);
|
stateIcon(nullptr)
|
||||||
|
|
||||||
if(small)
|
|
||||||
printAtMiddleLoc(makeNumberShort(stack->getCount()), pos.w/2, pos.h - 7, FONT_SMALL, Colors::WHITE, to);
|
|
||||||
else
|
|
||||||
printAtMiddleLoc(makeNumberShort(stack->getCount()), pos.w/2, pos.h - 8, FONT_MEDIUM, Colors::WHITE, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStackQueue::StackBox::setStack( const CStack *stack )
|
|
||||||
{
|
|
||||||
this->stack = stack;
|
|
||||||
assert(stack);
|
|
||||||
icon->setFrame(stack->getCreature()->iconIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
CStackQueue::StackBox::StackBox(bool small):
|
|
||||||
stack(nullptr),
|
|
||||||
small(small)
|
|
||||||
{
|
{
|
||||||
OBJ_CONSTRUCTION_CAPTURING_ALL;
|
OBJ_CONSTRUCTION_CAPTURING_ALL;
|
||||||
bg = new CPicture(small ? "StackQueueSmall" : "StackQueueLarge" );
|
bg = new CPicture(owner->embedded ? "StackQueueSmall" : "StackQueueLarge" );
|
||||||
|
|
||||||
if (small)
|
|
||||||
{
|
|
||||||
icon = new CAnimImage("CPRSMALL", 0, 0, 5, 2);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
icon = new CAnimImage("TWCRPORT", 0, 0, 9, 1);
|
|
||||||
|
|
||||||
pos.w = bg->pos.w;
|
pos.w = bg->pos.w;
|
||||||
pos.h = bg->pos.h;
|
pos.h = bg->pos.h;
|
||||||
|
|
||||||
|
if(owner->embedded)
|
||||||
|
{
|
||||||
|
icon = new CAnimImage(owner->icons, 0, 0, 5, 2);
|
||||||
|
amount = new CLabel(pos.w/2, pos.h - 7, FONT_SMALL, CENTER, Colors::WHITE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
icon = new CAnimImage(owner->icons, 0, 0, 9, 1);
|
||||||
|
amount = new CLabel(pos.w/2, pos.h - 8, FONT_MEDIUM, CENTER, Colors::WHITE);
|
||||||
|
|
||||||
|
int icon_x = pos.w - 17;
|
||||||
|
int icon_y = pos.h - 18;
|
||||||
|
|
||||||
|
stateIcon = new CAnimImage(owner->stateIcons, 0, 0, icon_x, icon_y);
|
||||||
|
stateIcon->visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStackQueue::StackBox::setStack(const battle::Unit * nStack, size_t turn)
|
||||||
|
{
|
||||||
|
if(nStack)
|
||||||
|
{
|
||||||
|
bg->colorize(nStack->unitOwner());
|
||||||
|
icon->visible = true;
|
||||||
|
icon->setFrame(nStack->creatureIconIndex());
|
||||||
|
amount->setText(makeNumberShort(nStack->getCount()));
|
||||||
|
|
||||||
|
if(stateIcon)
|
||||||
|
{
|
||||||
|
if(nStack->defended(turn) || (turn > 0 && nStack->defended(turn - 1)))
|
||||||
|
{
|
||||||
|
stateIcon->setFrame(0, 0);
|
||||||
|
stateIcon->visible = true;
|
||||||
|
}
|
||||||
|
else if(nStack->waited(turn))
|
||||||
|
{
|
||||||
|
stateIcon->setFrame(1, 0);
|
||||||
|
stateIcon->visible = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stateIcon->visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
bg->colorize(PlayerColor::NEUTRAL);
|
||||||
|
icon->visible = false;
|
||||||
|
icon->setFrame(0);
|
||||||
|
amount->setText("");
|
||||||
|
|
||||||
|
if(stateIcon)
|
||||||
|
stateIcon->visible = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,10 @@ class CToggleGroup;
|
|||||||
class CLabel;
|
class CLabel;
|
||||||
struct BattleResult;
|
struct BattleResult;
|
||||||
class CStack;
|
class CStack;
|
||||||
|
namespace battle
|
||||||
|
{
|
||||||
|
class Unit;
|
||||||
|
}
|
||||||
class CAnimImage;
|
class CAnimImage;
|
||||||
class CPlayerInterface;
|
class CPlayerInterface;
|
||||||
|
|
||||||
@@ -141,27 +145,23 @@ class CStackQueue : public CIntObject
|
|||||||
public:
|
public:
|
||||||
CPicture * bg;
|
CPicture * bg;
|
||||||
CAnimImage * icon;
|
CAnimImage * icon;
|
||||||
const CStack *stack;
|
CLabel * amount;
|
||||||
bool small;
|
CAnimImage * stateIcon;
|
||||||
|
|
||||||
void showAll(SDL_Surface * to) override;
|
void setStack(const battle::Unit * nStack, size_t turn = 0);
|
||||||
void setStack(const CStack *nStack);
|
StackBox(CStackQueue * owner);
|
||||||
StackBox(bool small);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static const int QUEUE_SIZE = 10;
|
static const int QUEUE_SIZE = 10;
|
||||||
const bool embedded;
|
const bool embedded;
|
||||||
std::vector<const CStack *> stacksSorted;
|
|
||||||
std::vector<StackBox *> stackBoxes;
|
std::vector<StackBox *> stackBoxes;
|
||||||
|
|
||||||
SDL_Surface * bg;
|
|
||||||
|
|
||||||
CBattleInterface * owner;
|
CBattleInterface * owner;
|
||||||
|
|
||||||
|
std::shared_ptr<CAnimation> icons;
|
||||||
|
std::shared_ptr<CAnimation> stateIcons;
|
||||||
|
|
||||||
CStackQueue(bool Embedded, CBattleInterface * _owner);
|
CStackQueue(bool Embedded, CBattleInterface * _owner);
|
||||||
~CStackQueue();
|
~CStackQueue();
|
||||||
void update();
|
void update();
|
||||||
void showAll(SDL_Surface *to) override;
|
|
||||||
void blitBg(SDL_Surface * to);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1641,11 +1641,11 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *
|
|||||||
new CLabel(78, 11, FONT_SMALL, CENTER, Colors::WHITE, getMyCreature()->namePl);
|
new CLabel(78, 11, FONT_SMALL, CENTER, Colors::WHITE, getMyCreature()->namePl);
|
||||||
|
|
||||||
Rect sizes(287, 4, 96, 18);
|
Rect sizes(287, 4, 96, 18);
|
||||||
values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[190], CGI->generaltexth->fcommands[0], getMyCreature()->Attack()));
|
values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[190], CGI->generaltexth->fcommands[0], getMyCreature()->getAttack(false)));
|
||||||
sizes.y+=20;
|
sizes.y+=20;
|
||||||
values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[191], CGI->generaltexth->fcommands[1], getMyCreature()->Defense()));
|
values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[191], CGI->generaltexth->fcommands[1], getMyCreature()->getDefence(false)));
|
||||||
sizes.y+=21;
|
sizes.y+=21;
|
||||||
values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[199], CGI->generaltexth->fcommands[2], getMyCreature()->getMinDamage(), getMyCreature()->getMaxDamage()));
|
values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[199], CGI->generaltexth->fcommands[2], getMyCreature()->getMinDamage(false), getMyCreature()->getMaxDamage(false)));
|
||||||
sizes.y+=20;
|
sizes.y+=20;
|
||||||
values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[388], CGI->generaltexth->fcommands[3], getMyCreature()->MaxHealth()));
|
values.push_back(new LabeledValue(sizes, CGI->generaltexth->allTexts[388], CGI->generaltexth->fcommands[3], getMyCreature()->MaxHealth()));
|
||||||
sizes.y+=21;
|
sizes.y+=21;
|
||||||
|
|||||||
@@ -219,7 +219,7 @@ void CStackWindow::CWindowSection::createStackInfo(bool showExp, bool showArt)
|
|||||||
|
|
||||||
int dmgMultiply = 1;
|
int dmgMultiply = 1;
|
||||||
if(parent->info->owner && parent->info->stackNode->hasBonusOfType(Bonus::SIEGE_WEAPON))
|
if(parent->info->owner && parent->info->stackNode->hasBonusOfType(Bonus::SIEGE_WEAPON))
|
||||||
dmgMultiply += parent->info->owner->Attack();
|
dmgMultiply += parent->info->owner->getPrimSkillLevel(PrimarySkill::ATTACK);
|
||||||
|
|
||||||
new CPicture("stackWindow/icons", 117, 32);
|
new CPicture("stackWindow/icons", 117, 32);
|
||||||
|
|
||||||
@@ -230,9 +230,9 @@ void CStackWindow::CWindowSection::createStackInfo(bool showExp, bool showArt)
|
|||||||
|
|
||||||
if(battleStack != nullptr) // in battle
|
if(battleStack != nullptr) // in battle
|
||||||
{
|
{
|
||||||
printStatBase(EStat::ATTACK, CGI->generaltexth->primarySkillNames[0], parent->info->creature->Attack(), battleStack->Attack());
|
printStatBase(EStat::ATTACK, CGI->generaltexth->primarySkillNames[0], parent->info->creature->getAttack(battleStack->isShooter()), battleStack->getAttack(battleStack->isShooter()));
|
||||||
printStatBase(EStat::DEFENCE, CGI->generaltexth->primarySkillNames[1], parent->info->creature->Defense(false), battleStack->Defense());
|
printStatBase(EStat::DEFENCE, CGI->generaltexth->primarySkillNames[1], parent->info->creature->getDefence(battleStack->isShooter()), battleStack->getDefence(battleStack->isShooter()));
|
||||||
printStatRange(EStat::DAMAGE, CGI->generaltexth->allTexts[199], parent->info->stackNode->getMinDamage() * dmgMultiply, battleStack->getMaxDamage() * dmgMultiply);
|
printStatRange(EStat::DAMAGE, CGI->generaltexth->allTexts[199], parent->info->stackNode->getMinDamage(battleStack->isShooter()) * dmgMultiply, battleStack->getMaxDamage(battleStack->isShooter()) * dmgMultiply);
|
||||||
printStatBase(EStat::HEALTH, CGI->generaltexth->allTexts[388], parent->info->creature->MaxHealth(), battleStack->MaxHealth());
|
printStatBase(EStat::HEALTH, CGI->generaltexth->allTexts[388], parent->info->creature->MaxHealth(), battleStack->MaxHealth());
|
||||||
printStatBase(EStat::SPEED, CGI->generaltexth->zelp[441].first, parent->info->creature->Speed(), battleStack->Speed());
|
printStatBase(EStat::SPEED, CGI->generaltexth->zelp[441].first, parent->info->creature->Speed(), battleStack->Speed());
|
||||||
|
|
||||||
@@ -250,9 +250,9 @@ void CStackWindow::CWindowSection::createStackInfo(bool showExp, bool showArt)
|
|||||||
const bool shooter = parent->info->stackNode->hasBonusOfType(Bonus::SHOOTER) && parent->info->stackNode->valOfBonuses(Bonus::SHOTS);
|
const bool shooter = parent->info->stackNode->hasBonusOfType(Bonus::SHOOTER) && parent->info->stackNode->valOfBonuses(Bonus::SHOTS);
|
||||||
const bool caster = parent->info->stackNode->valOfBonuses(Bonus::CASTS);
|
const bool caster = parent->info->stackNode->valOfBonuses(Bonus::CASTS);
|
||||||
|
|
||||||
printStatBase(EStat::ATTACK, CGI->generaltexth->primarySkillNames[0], parent->info->creature->Attack(), parent->info->stackNode->Attack());
|
printStatBase(EStat::ATTACK, CGI->generaltexth->primarySkillNames[0], parent->info->creature->getAttack(shooter), parent->info->stackNode->getAttack(shooter));
|
||||||
printStatBase(EStat::DEFENCE, CGI->generaltexth->primarySkillNames[1], parent->info->creature->Defense(false), parent->info->stackNode->Defense());
|
printStatBase(EStat::DEFENCE, CGI->generaltexth->primarySkillNames[1], parent->info->creature->getDefence(shooter), parent->info->stackNode->getDefence(shooter));
|
||||||
printStatRange(EStat::DAMAGE, CGI->generaltexth->allTexts[199], parent->info->stackNode->getMinDamage() * dmgMultiply, parent->info->stackNode->getMaxDamage() * dmgMultiply);
|
printStatRange(EStat::DAMAGE, CGI->generaltexth->allTexts[199], parent->info->stackNode->getMinDamage(shooter) * dmgMultiply, parent->info->stackNode->getMaxDamage(shooter) * dmgMultiply);
|
||||||
printStatBase(EStat::HEALTH, CGI->generaltexth->allTexts[388], parent->info->creature->MaxHealth(), parent->info->stackNode->MaxHealth());
|
printStatBase(EStat::HEALTH, CGI->generaltexth->allTexts[388], parent->info->creature->MaxHealth(), parent->info->stackNode->MaxHealth());
|
||||||
printStatBase(EStat::SPEED, CGI->generaltexth->zelp[441].first, parent->info->creature->Speed(), parent->info->stackNode->Speed());
|
printStatBase(EStat::SPEED, CGI->generaltexth->zelp[441].first, parent->info->creature->Speed(), parent->info->stackNode->Speed());
|
||||||
|
|
||||||
|
|||||||
@@ -61,6 +61,11 @@ const TBonusListPtr CHeroWithMaybePickedArtifact::getAllBonuses(const CSelector
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t CHeroWithMaybePickedArtifact::getTreeVersion() const
|
||||||
|
{
|
||||||
|
return hero->getTreeVersion(); //this assumes that hero and artifact belongs to main bonus tree
|
||||||
|
}
|
||||||
|
|
||||||
CHeroWithMaybePickedArtifact::CHeroWithMaybePickedArtifact(CWindowWithArtifacts *Cww, const CGHeroInstance *Hero)
|
CHeroWithMaybePickedArtifact::CHeroWithMaybePickedArtifact(CWindowWithArtifacts *Cww, const CGHeroInstance *Hero)
|
||||||
: hero(Hero), cww(Cww)
|
: hero(Hero), cww(Cww)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
//helper class for calculating values of hero bonuses without bonuses from picked up artifact
|
//helper class for calculating values of hero bonuses without bonuses from picked up artifact
|
||||||
class CHeroWithMaybePickedArtifact : public IBonusBearer
|
class CHeroWithMaybePickedArtifact : public virtual IBonusBearer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const CGHeroInstance *hero;
|
const CGHeroInstance *hero;
|
||||||
@@ -47,6 +47,8 @@ public:
|
|||||||
|
|
||||||
CHeroWithMaybePickedArtifact(CWindowWithArtifacts *Cww, const CGHeroInstance *Hero);
|
CHeroWithMaybePickedArtifact(CWindowWithArtifacts *Cww, const CGHeroInstance *Hero);
|
||||||
const TBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const override;
|
const TBonusListPtr getAllBonuses(const CSelector &selector, const CSelector &limit, const CBonusSystemNode *root = nullptr, const std::string &cachingStr = "") const override;
|
||||||
|
|
||||||
|
int64_t getTreeVersion() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
class CHeroWindow: public CWindowObject, public CWindowWithGarrison, public CWindowWithArtifacts
|
class CHeroWindow: public CWindowObject, public CWindowWithGarrison, public CWindowWithArtifacts
|
||||||
|
|||||||
@@ -34,14 +34,13 @@
|
|||||||
|
|
||||||
#include "../../CCallback.h"
|
#include "../../CCallback.h"
|
||||||
|
|
||||||
#include "../../lib/CStack.h"
|
|
||||||
#include "../../lib/CConfigHandler.h"
|
#include "../../lib/CConfigHandler.h"
|
||||||
#include "../../lib/CGeneralTextHandler.h"
|
#include "../../lib/CGeneralTextHandler.h"
|
||||||
#include "../../lib/CHeroHandler.h"
|
|
||||||
#include "../../lib/spells/CSpellHandler.h"
|
#include "../../lib/spells/CSpellHandler.h"
|
||||||
|
#include "../../lib/spells/Problem.h"
|
||||||
#include "../../lib/GameConstants.h"
|
#include "../../lib/GameConstants.h"
|
||||||
#include "../../lib/CGameState.h"
|
|
||||||
#include "../../lib/mapObjects/CGTownInstance.h"
|
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||||
|
|
||||||
CSpellWindow::InteractiveArea::InteractiveArea(const SDL_Rect & myRect, std::function<void()> funcL, int helpTextId, CSpellWindow * _owner)
|
CSpellWindow::InteractiveArea::InteractiveArea(const SDL_Rect & myRect, std::function<void()> funcL, int helpTextId, CSpellWindow * _owner)
|
||||||
{
|
{
|
||||||
@@ -125,7 +124,7 @@ CSpellWindow::CSpellWindow(const CGHeroInstance * _myHero, CPlayerInterface * _m
|
|||||||
|
|
||||||
++sitesPerOurTab[4];
|
++sitesPerOurTab[4];
|
||||||
|
|
||||||
spell->forEachSchool([&sitesPerOurTab](const SpellSchoolInfo & school, bool & stop)
|
spell->forEachSchool([&sitesPerOurTab](const spells::SchoolInfo & school, bool & stop)
|
||||||
{
|
{
|
||||||
++sitesPerOurTab[(ui8)school.id];
|
++sitesPerOurTab[(ui8)school.id];
|
||||||
});
|
});
|
||||||
@@ -536,101 +535,46 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
|||||||
owner->myInt->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[206]) % spellCost % owner->myHero->mana));
|
owner->myInt->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[206]) % spellCost % owner->myHero->mana));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
//battle spell on adv map or adventure map spell during combat => display infowindow, not cast
|
|
||||||
if((mySpell->isCombatSpell() && !owner->myInt->battleInt)
|
//anything that is not combat spell is adventure spell
|
||||||
|| (mySpell->isAdventureSpell() && (owner->myInt->battleInt || owner->myInt->castleInt)))
|
//this not an error in general to cast even creature ability with hero
|
||||||
|
const bool combatSpell = mySpell->isCombatSpell();
|
||||||
|
if(mySpell->isCombatSpell() != !mySpell->isAdventureSpell())
|
||||||
{
|
{
|
||||||
std::vector<CComponent*> hlp(1, new CComponent(CComponent::spell, mySpell->id, 0));
|
logGlobal->error("Spell have invalid flags");
|
||||||
owner->myInt->showInfoDialog(mySpell->getLevelInfo(schoolLevel).description, hlp);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//we will cast a spell
|
const bool inCombat = owner->myInt->battleInt != nullptr;
|
||||||
if(mySpell->isCombatSpell() && owner->myInt->battleInt) //if battle window is open
|
const bool inCastle = owner->myInt->castleInt != nullptr;
|
||||||
|
|
||||||
|
//battle spell on adv map or adventure map spell during combat => display infowindow, not cast
|
||||||
|
if((combatSpell ^ inCombat) || inCastle)
|
||||||
{
|
{
|
||||||
ESpellCastProblem::ESpellCastProblem problem = mySpell->canBeCast(owner->myInt->cb.get(), ECastingMode::HERO_CASTING, owner->myHero);
|
std::vector<CComponent*> hlp(1, new CComponent(CComponent::spell, mySpell->id, 0));
|
||||||
switch (problem)
|
owner->myInt->showInfoDialog(mySpell->getLevelInfo(schoolLevel).description, hlp);
|
||||||
|
}
|
||||||
|
else if(combatSpell)
|
||||||
|
{
|
||||||
|
spells::detail::ProblemImpl problem;
|
||||||
|
if(mySpell->canBeCast(problem, owner->myInt->cb.get(), spells::Mode::HERO, owner->myHero))
|
||||||
{
|
{
|
||||||
case ESpellCastProblem::OK:
|
owner->myInt->battleInt->castThisSpell(mySpell->id);
|
||||||
{
|
owner->fexitb();
|
||||||
owner->myInt->battleInt->castThisSpell(mySpell->id);
|
}
|
||||||
owner->fexitb();
|
else
|
||||||
return;
|
{
|
||||||
}
|
std::vector<std::string> texts;
|
||||||
break;
|
problem.getAll(texts);
|
||||||
case ESpellCastProblem::ANOTHER_ELEMENTAL_SUMMONED:
|
if(!texts.empty())
|
||||||
{
|
owner->myInt->showInfoDialog(texts.front());
|
||||||
std::string text = CGI->generaltexth->allTexts[538], elemental, caster;
|
else
|
||||||
const PlayerColor player = owner->myInt->playerID;
|
owner->myInt->showInfoDialog("Unknown problem with this spell, no more information available.");
|
||||||
|
|
||||||
const TStacks stacks = owner->myInt->cb->battleGetStacksIf([player](const CStack * s)
|
|
||||||
{
|
|
||||||
return s->owner == player
|
|
||||||
&& vstd::contains(s->state, EBattleStackState::SUMMONED)
|
|
||||||
&& !s->isClone();
|
|
||||||
});
|
|
||||||
for(const CStack * s : stacks)
|
|
||||||
{
|
|
||||||
elemental = s->getCreature()->namePl;
|
|
||||||
}
|
|
||||||
if (owner->myHero->type->sex)
|
|
||||||
{ //female
|
|
||||||
caster = CGI->generaltexth->allTexts[540];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ //male
|
|
||||||
caster = CGI->generaltexth->allTexts[539];
|
|
||||||
}
|
|
||||||
std::string summoner = owner->myHero->name;
|
|
||||||
|
|
||||||
text = boost::str(boost::format(text) % summoner % elemental % caster);
|
|
||||||
|
|
||||||
owner->myInt->showInfoDialog(text);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESpellCastProblem::SPELL_LEVEL_LIMIT_EXCEEDED:
|
|
||||||
{
|
|
||||||
//Recanter's Cloak or similar effect. Try to retrieve bonus
|
|
||||||
const auto b = owner->myHero->getBonusLocalFirst(Selector::type(Bonus::BLOCK_MAGIC_ABOVE));
|
|
||||||
//TODO what about other values and non-artifact sources?
|
|
||||||
if(b && b->val == 2 && b->source == Bonus::ARTIFACT)
|
|
||||||
{
|
|
||||||
std::string artName = CGI->arth->artifacts[b->sid]->Name();
|
|
||||||
//The %s prevents %s from casting 3rd level or higher spells.
|
|
||||||
owner->myInt->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[536])
|
|
||||||
% artName % owner->myHero->name));
|
|
||||||
}
|
|
||||||
else if(b && b->source == Bonus::TERRAIN_OVERLAY && b->sid == BFieldType::CURSED_GROUND)
|
|
||||||
{
|
|
||||||
owner->myInt->showInfoDialog(CGI->generaltexth->allTexts[537]);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// General message:
|
|
||||||
// %s recites the incantations but they seem to have no effect.
|
|
||||||
std::string text = CGI->generaltexth->allTexts[541], caster = owner->myHero->name;
|
|
||||||
text = boost::str(boost::format(text) % caster);
|
|
||||||
owner->myInt->showInfoDialog(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ESpellCastProblem::NO_APPROPRIATE_TARGET:
|
|
||||||
{
|
|
||||||
owner->myInt->showInfoDialog(CGI->generaltexth->allTexts[185]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
// General message:
|
|
||||||
std::string text = CGI->generaltexth->allTexts[541], caster = owner->myHero->name;
|
|
||||||
text = boost::str(boost::format(text) % caster);
|
|
||||||
owner->myInt->showInfoDialog(text);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(mySpell->isAdventureSpell() && !owner->myInt->battleInt) //adventure spell and not in battle
|
else //adventure spell
|
||||||
{
|
{
|
||||||
const CGHeroInstance *h = owner->myHero;
|
const CGHeroInstance * h = owner->myHero;
|
||||||
GH.popInt(owner);
|
GH.popInt(owner);
|
||||||
|
|
||||||
auto guard = vstd::makeScopeGuard([this]()
|
auto guard = vstd::makeScopeGuard([this]()
|
||||||
@@ -639,9 +583,9 @@ void CSpellWindow::SpellArea::clickLeft(tribool down, bool previousState)
|
|||||||
owner->myInt->spellbookSettings.spellbokLastPageAdvmap = owner->currentPage;
|
owner->myInt->spellbookSettings.spellbokLastPageAdvmap = owner->currentPage;
|
||||||
});
|
});
|
||||||
|
|
||||||
if(mySpell->getTargetType() == CSpell::LOCATION)
|
if(mySpell->getTargetType() == spells::AimType::LOCATION)
|
||||||
adventureInt->enterCastingMode(mySpell);
|
adventureInt->enterCastingMode(mySpell);
|
||||||
else if(mySpell->getTargetType() == CSpell::NO_TARGET)
|
else if(mySpell->getTargetType() == spells::AimType::NO_TARGET)
|
||||||
owner->myInt->cb->castSpell(h, mySpell->id);
|
owner->myInt->cb->castSpell(h, mySpell->id);
|
||||||
else
|
else
|
||||||
logGlobal->error("Invalid spell target type");
|
logGlobal->error("Invalid spell target type");
|
||||||
@@ -654,7 +598,7 @@ void CSpellWindow::SpellArea::clickRight(tribool down, bool previousState)
|
|||||||
if(mySpell && down)
|
if(mySpell && down)
|
||||||
{
|
{
|
||||||
std::string dmgInfo;
|
std::string dmgInfo;
|
||||||
int causedDmg = owner->myInt->cb->estimateSpellDamage(mySpell, owner->myHero);
|
auto causedDmg = owner->myInt->cb->estimateSpellDamage(mySpell, owner->myHero);
|
||||||
if(causedDmg == 0 || mySpell->id == SpellID::TITANS_LIGHTNING_BOLT) //Titan's Lightning Bolt already has damage info included
|
if(causedDmg == 0 || mySpell->id == SpellID::TITANS_LIGHTNING_BOLT) //Titan's Lightning Bolt already has damage info included
|
||||||
dmgInfo = "";
|
dmgInfo = "";
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "../CreatureCostBox.h"
|
#include "../CreatureCostBox.h"
|
||||||
#include "QuickRecruitmentWindow.h"
|
#include "QuickRecruitmentWindow.h"
|
||||||
#include "../gui/CGuiHandler.h"
|
#include "../gui/CGuiHandler.h"
|
||||||
|
#include "../../lib/CCreatureHandler.h"
|
||||||
|
|
||||||
void CreaturePurchaseCard::initButtons()
|
void CreaturePurchaseCard::initButtons()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
#include "../gui/CGuiHandler.h"
|
#include "../gui/CGuiHandler.h"
|
||||||
#include "../../CCallback.h"
|
#include "../../CCallback.h"
|
||||||
#include "../CreatureCostBox.h"
|
#include "../CreatureCostBox.h"
|
||||||
#include "../lib/ResourceSet.h"
|
#include "../../lib/ResourceSet.h"
|
||||||
|
#include "../../lib/CCreatureHandler.h"
|
||||||
#include "CreaturePurchaseCard.h"
|
#include "CreaturePurchaseCard.h"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -196,7 +196,7 @@
|
|||||||
"type" : "object",
|
"type" : "object",
|
||||||
"additionalProperties" : false,
|
"additionalProperties" : false,
|
||||||
"default": {},
|
"default": {},
|
||||||
"required" : [ "animationSpeed", "mouseShadow", "cellBorders", "stackRange", "showQueue" ],
|
"required" : [ "animationSpeed", "mouseShadow", "cellBorders", "stackRange", "showQueue", "queueSize" ],
|
||||||
"properties" : {
|
"properties" : {
|
||||||
"animationSpeed" : {
|
"animationSpeed" : {
|
||||||
"type" : "number",
|
"type" : "number",
|
||||||
@@ -217,6 +217,11 @@
|
|||||||
"showQueue" : {
|
"showQueue" : {
|
||||||
"type" : "boolean",
|
"type" : "boolean",
|
||||||
"default" : true
|
"default" : true
|
||||||
|
},
|
||||||
|
"queueSize" : {
|
||||||
|
"type" : "string",
|
||||||
|
"default" : "auto",
|
||||||
|
"enum" : [ "auto", "small", "big" ]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -97,6 +97,12 @@
|
|||||||
"$ref" : "vcmi:bonus"
|
"$ref" : "vcmi:bonus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"battleEffects":{
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties" : {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
},
|
||||||
"targetModifier":{
|
"targetModifier":{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
@@ -246,7 +252,10 @@
|
|||||||
"$ref" : "#/definitions/flags",
|
"$ref" : "#/definitions/flags",
|
||||||
"description": "flags structure of bonus names, presence of all bonuses required to be affected by, can't be negated."
|
"description": "flags structure of bonus names, presence of all bonuses required to be affected by, can't be negated."
|
||||||
},
|
},
|
||||||
|
"targetCondition":{
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties" : true
|
||||||
|
},
|
||||||
"animation":{"$ref": "#/definitions/animation"},
|
"animation":{"$ref": "#/definitions/animation"},
|
||||||
|
|
||||||
"graphics":{
|
"graphics":{
|
||||||
|
|||||||
+101
-62
@@ -16,7 +16,7 @@
|
|||||||
"notActive" : {
|
"notActive" : {
|
||||||
"val" : 0,
|
"val" : 0,
|
||||||
"type" : "NOT_ACTIVE",
|
"type" : "NOT_ACTIVE",
|
||||||
"subtype" : 62,
|
"subtype" : "spell.stoneGaze",
|
||||||
"duration" : [
|
"duration" : [
|
||||||
"UNTIL_BEING_ATTACKED",
|
"UNTIL_BEING_ATTACKED",
|
||||||
"N_TURNS"
|
"N_TURNS"
|
||||||
@@ -43,11 +43,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteImmunity":{
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"indifferent": true
|
"indifferent": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"poison" : {
|
"poison" : {
|
||||||
@@ -80,15 +82,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteImmunity":{
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"immunity" : {
|
|
||||||
"UNDEAD": true,
|
|
||||||
"NON_LIVING": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"negative": true
|
"negative": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.NON_LIVING" : "normal",
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bind" : {
|
"bind" : {
|
||||||
@@ -149,15 +151,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteImmunity":{
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"immunity" : {
|
|
||||||
"UNDEAD": true,
|
|
||||||
"NON_LIVING": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"negative": true
|
"negative": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.NON_LIVING" : "normal",
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"paralyze" : {
|
"paralyze" : {
|
||||||
@@ -178,7 +180,7 @@
|
|||||||
"notActive" : {
|
"notActive" : {
|
||||||
"val" : 0,
|
"val" : 0,
|
||||||
"type" : "NOT_ACTIVE",
|
"type" : "NOT_ACTIVE",
|
||||||
"subtype" : 74,
|
"subtype" : "spell.paralyze",
|
||||||
"duration" : [
|
"duration" : [
|
||||||
"UNTIL_BEING_ATTACKED",
|
"UNTIL_BEING_ATTACKED",
|
||||||
"N_TURNS"
|
"N_TURNS"
|
||||||
@@ -195,11 +197,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteImmunity":{
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"negative": true
|
"negative": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"age" : {
|
"age" : {
|
||||||
@@ -226,15 +230,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteImmunity":{
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"immunity" : {
|
|
||||||
"UNDEAD": true,
|
|
||||||
"NON_LIVING": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"negative": true
|
"negative": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.NON_LIVING" : "normal",
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"deathCloud" : {
|
"deathCloud" : {
|
||||||
@@ -249,18 +253,23 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0-1"
|
"range" : "0-1",
|
||||||
|
"battleEffects":{
|
||||||
|
"damage":{
|
||||||
|
"type":"core:damage"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteImmunity":{
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"immunity" : {
|
|
||||||
"UNDEAD": true,
|
|
||||||
"NON_LIVING": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"indifferent": true
|
"indifferent": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.NON_LIVING" : "normal",
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"thunderbolt" : {
|
"thunderbolt" : {
|
||||||
@@ -275,12 +284,16 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0"
|
"range" : "0",
|
||||||
|
"battleEffects":{
|
||||||
|
"damage":{
|
||||||
|
"type":"core:damage"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -296,6 +309,15 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
|
"battleEffects":{
|
||||||
|
"dispel":{
|
||||||
|
"type":"core:dispel",
|
||||||
|
"ignoreImmunity" : true,
|
||||||
|
"dispelNegative":false,
|
||||||
|
"dispelNeutral":false,
|
||||||
|
"dispelPositive":true
|
||||||
|
}
|
||||||
|
},
|
||||||
"range" : "0"
|
"range" : "0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -316,41 +338,50 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
|
"battleEffects":{
|
||||||
|
"destruction":{
|
||||||
|
"type":"core:damage",
|
||||||
|
"killByCount": true
|
||||||
|
}
|
||||||
|
},
|
||||||
"range" : "0"
|
"range" : "0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
|
||||||
"UNDEAD": true,
|
|
||||||
"SIEGE_WEAPON": true,
|
|
||||||
"NON_LIVING": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"indifferent": true
|
"indifferent": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.NON_LIVING" : "absolute",
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"acidBreath" : {
|
"acidBreath" : {
|
||||||
"index" : 80,
|
"index" : 80,
|
||||||
"targetType": "NO_TARGET",
|
"targetType": "NO_TARGET",
|
||||||
|
|
||||||
"animation":{
|
|
||||||
//???
|
|
||||||
},
|
|
||||||
"sounds": {
|
|
||||||
"cast": "ACID"
|
|
||||||
},
|
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"battleEffects":{
|
||||||
"targetModifier":{"smart":true},
|
"timed":{
|
||||||
"cumulativeEffects" : {
|
"type":"core:timed",
|
||||||
"primarySkill" : {
|
"cumulative":true,
|
||||||
"val" : -3,
|
"ignoreImmunity" : true,
|
||||||
"type" : "PRIMARY_SKILL",
|
"bonus" :{
|
||||||
"subtype" : "primSkill.defence",
|
"primarySkill" : {
|
||||||
"duration" : "PERMANENT",
|
"val" : -3,
|
||||||
"valueType" : "ADDITIVE_VALUE"
|
"type" : "PRIMARY_SKILL",
|
||||||
|
"subtype" : "primSkill.defence",
|
||||||
|
"duration" : "PERMANENT",
|
||||||
|
"valueType" : "ADDITIVE_VALUE"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"range" : "0",
|
||||||
|
"targetModifier":{"smart":true}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
@@ -369,6 +400,12 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
|
"battleEffects":{
|
||||||
|
"damage":{
|
||||||
|
"type":"core:damage",
|
||||||
|
"ignoreImmunity" : true
|
||||||
|
}
|
||||||
|
},
|
||||||
"range" : "0"
|
"range" : "0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -376,8 +413,10 @@
|
|||||||
"damage": true,
|
"damage": true,
|
||||||
"indifferent": true
|
"indifferent": true
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"targetCondition" : {
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
"noneOf" : {
|
||||||
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+131
-70
@@ -2,7 +2,7 @@
|
|||||||
"magicArrow" : {
|
"magicArrow" : {
|
||||||
"index" : 15,
|
"index" : 15,
|
||||||
"targetType": "CREATURE",
|
"targetType": "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"projectile": [
|
"projectile": [
|
||||||
{"minimumAngle": 0 ,"defName":"C20SPX4"},
|
{"minimumAngle": 0 ,"defName":"C20SPX4"},
|
||||||
@@ -19,22 +19,26 @@
|
|||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {"type":"core:damage"}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":true}
|
"targetModifier":{"smart":true}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"targetCondition" : {
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
"noneOf" : {
|
||||||
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"iceBolt" : {
|
"iceBolt" : {
|
||||||
"index" : 16,
|
"index" : 16,
|
||||||
"targetType": "CREATURE",
|
"targetType": "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"projectile": [
|
"projectile": [
|
||||||
{"minimumAngle": 0 ,"defName":"C08SPW4"},
|
{"minimumAngle": 0 ,"defName":"C08SPW4"},
|
||||||
@@ -44,54 +48,62 @@
|
|||||||
{"minimumAngle": 1.50 ,"defName":"C08SPW0"}
|
{"minimumAngle": 1.50 ,"defName":"C08SPW0"}
|
||||||
],
|
],
|
||||||
"hit":["C08SPW5"]
|
"hit":["C08SPW5"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "ICERAY"
|
"cast": "ICERAY"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {"type":"core:damage"}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":true}
|
"targetModifier":{"smart":true}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"targetCondition" : {
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
"noneOf" : {
|
||||||
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lightningBolt" : {
|
"lightningBolt" : {
|
||||||
"index" : 17,
|
"index" : 17,
|
||||||
"targetType": "CREATURE",
|
"targetType": "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":[{"defName":"C03SPA0", "verticalPosition":"bottom"}, "C11SPA1"]
|
"affect":[{"defName":"C03SPA0", "verticalPosition":"bottom"}, "C11SPA1"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "LIGHTBLT"
|
"cast": "LIGHTBLT"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {"type":"core:damage"}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":true}
|
"targetModifier":{"smart":true}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"targetCondition" : {
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
"noneOf" : {
|
||||||
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"implosion" : {
|
"implosion" : {
|
||||||
"index" : 18,
|
"index" : 18,
|
||||||
"targetType": "CREATURE",
|
"targetType": "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C05SPE0"]
|
"affect":["C05SPE0"]
|
||||||
},
|
},
|
||||||
@@ -101,245 +113,294 @@
|
|||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {"type":"core:damage"}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":true}
|
"targetModifier":{"smart":true}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
"targetCondition" : {
|
||||||
"SIEGE_WEAPON": true
|
"noneOf" : {
|
||||||
},
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal",
|
||||||
"immunity" : {
|
"bonus.SIEGE_WEAPON" : "absolute"
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"chainLightning" : {
|
"chainLightning" : {
|
||||||
"index" : 19,
|
"index" : 19,
|
||||||
"targetType": "CREATURE",
|
"targetType": "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":[{"defName":"C03SPA0", "verticalPosition":"bottom"}, "C11SPA1"]
|
"affect":[{"defName":"C03SPA0", "verticalPosition":"bottom"}, "C11SPA1"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "CHAINLTE"
|
"cast": "CHAINLTE"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {
|
||||||
|
"type" : "core:damage",
|
||||||
|
"chainFactor" : 0.5,
|
||||||
|
"chainLength" : 4
|
||||||
|
}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":true}
|
"targetModifier":{"smart":true}
|
||||||
|
},
|
||||||
|
"advanced" : {
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {
|
||||||
|
"chainLength" : 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expert" : {
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {
|
||||||
|
"chainLength" : 5
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"frostRing" : {
|
"frostRing" : {
|
||||||
"index" : 20,
|
"index" : 20,
|
||||||
"targetType": "LOCATION",
|
"targetType": "LOCATION",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"hit":["C07SPW"] //C07SPW0 ???
|
"hit":["C07SPW"] //C07SPW0 ???
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "FROSTING"
|
"cast": "FROSTING"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "1",
|
"range" : "1",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {"type":"core:damage"}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":false}
|
"targetModifier":{"smart":false}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"targetCondition" : {
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
"noneOf" : {
|
||||||
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fireball" : {
|
"fireball" : {
|
||||||
"index" : 21,
|
"index" : 21,
|
||||||
"targetType": "LOCATION",
|
"targetType": "LOCATION",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"hit":["C13SPF"] //C13SPF0 ???
|
"hit":["C13SPF"] //C13SPF0 ???
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "SPONTCOMB"
|
"cast": "SPONTCOMB"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0,1",
|
"range" : "0,1",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {"type":"core:damage"}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":false}
|
"targetModifier":{"smart":false}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"targetCondition" : {
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
"noneOf" : {
|
||||||
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"inferno" : {
|
"inferno" : {
|
||||||
"index" : 22,
|
"index" : 22,
|
||||||
"targetType": "LOCATION",
|
"targetType": "LOCATION",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"hit":["C04SPF0"]
|
"hit":["C04SPF0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "FIREBLST"
|
"cast": "FIREBLST"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0-2",
|
"range" : "0-2",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {"type":"core:damage"}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":false}
|
"targetModifier":{"smart":false}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"targetCondition" : {
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
"noneOf" : {
|
||||||
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"meteorShower" : {
|
"meteorShower" : {
|
||||||
"index" : 23,
|
"index" : 23,
|
||||||
"targetType": "LOCATION",
|
"targetType": "LOCATION",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"hit":["C08SPE0"]
|
"hit":["C08SPE0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "METEOR"
|
"cast": "METEOR"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0,1",
|
"range" : "0,1",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {"type":"core:damage"}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":false}
|
"targetModifier":{"smart":false}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"targetCondition" : {
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
"noneOf" : {
|
||||||
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"deathRipple" : {
|
"deathRipple" : {
|
||||||
"index" : 24,
|
"index" : 24,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C04SPE0"]
|
"affect":["C04SPE0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "DEATHRIP"
|
"cast": "DEATHRIP"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "X",
|
"range" : "X",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {"type":"core:damage"}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":false}
|
"targetModifier":{"smart":false}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"absoluteImmunity":{
|
"targetCondition" : {
|
||||||
"SIEGE_WEAPON": true,
|
"noneOf" : {
|
||||||
"UNDEAD": true,
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal",
|
||||||
},
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
"immunity" : {
|
"bonus.UNDEAD" : "absolute"
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"destroyUndead" : {
|
"destroyUndead" : {
|
||||||
"index" : 25,
|
"index" : 25,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"affect":["C14SPA0"]
|
"affect":["C14SPA0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "SACBRETH"
|
"cast": "SACBRETH"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "X",
|
"range" : "X",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {"type":"core:damage"}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":false}
|
"targetModifier":{"smart":false}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"absoluteLimit" : {
|
"targetCondition" : {
|
||||||
"UNDEAD": true
|
"allOf" : {
|
||||||
},
|
"bonus.UNDEAD" : "absolute"
|
||||||
"immunity" : {
|
},
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
"noneOf" : {
|
||||||
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"armageddon" : {
|
"armageddon" : {
|
||||||
"index" : 26,
|
"index" : 26,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"hit":["C06SPF0"]
|
"hit":["C06SPF0"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "ARMGEDN"
|
"cast": "ARMGEDN"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "X",
|
"range" : "X",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {"type":"core:damage"}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":false}
|
"targetModifier":{"smart":false}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true
|
"negative": true
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"targetCondition" : {
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
"noneOf" : {
|
||||||
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"titanBolt" : {
|
"titanBolt" : {
|
||||||
"index" : 57,
|
"index" : 57,
|
||||||
"targetType" : "CREATURE",
|
"targetType" : "CREATURE",
|
||||||
|
|
||||||
"animation":{
|
"animation":{
|
||||||
"hit":[{"defName":"C03SPA0", "verticalPosition":"bottom"}, "C11SPA1"]
|
"hit":[{"defName":"C03SPA0", "verticalPosition":"bottom"}, "C11SPA1"]
|
||||||
},
|
},
|
||||||
"sounds": {
|
"sounds": {
|
||||||
"cast": "LIGHTBLT"
|
"cast": "LIGHTBLT"
|
||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
|
"battleEffects" : {
|
||||||
|
"directDamage" : {"type":"core:damage"}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":true}
|
"targetModifier":{"smart":true}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"offensive": true,
|
|
||||||
"negative": true,
|
"negative": true,
|
||||||
"special": true
|
"special": true
|
||||||
}
|
}
|
||||||
|
|||||||
+405
-28
@@ -8,7 +8,42 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "X"
|
"range" : "X",
|
||||||
|
"battleEffects" : {
|
||||||
|
"obstacle" : {
|
||||||
|
"type":"core:obstacle",
|
||||||
|
"hidden" : true,
|
||||||
|
"passable" : true,
|
||||||
|
"trap" : true,
|
||||||
|
"trigger" : false,
|
||||||
|
"patchCount" : 4,
|
||||||
|
"turnsRemaining" : -1,
|
||||||
|
"attacker" :{
|
||||||
|
"animation" : "C17SPE1",
|
||||||
|
"appearAnimation" : "C17SPE0",
|
||||||
|
"offsetY" : -42
|
||||||
|
},
|
||||||
|
"defender" :{
|
||||||
|
"animation" : "C17SPE1",
|
||||||
|
"appearAnimation" : "C17SPE0",
|
||||||
|
"offsetY" : -42
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advanced":{
|
||||||
|
"battleEffects":{
|
||||||
|
"obstacle":{
|
||||||
|
"patchCount" : 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expert":{
|
||||||
|
"battleEffects":{
|
||||||
|
"obstacle":{
|
||||||
|
"patchCount" : 8
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
@@ -24,15 +59,57 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "X"
|
"range" : "X",
|
||||||
|
"battleEffects" : {
|
||||||
|
"obstacle" : {
|
||||||
|
"type":"core:obstacle",
|
||||||
|
"hidden" : true,
|
||||||
|
"passable" : true,
|
||||||
|
"trap" : false,
|
||||||
|
"trigger" : true,
|
||||||
|
"removeOnTrigger" : true,
|
||||||
|
"patchCount" : 4,
|
||||||
|
"turnsRemaining" : -1,
|
||||||
|
"attacker" :{
|
||||||
|
"animation" : "C09SPF1",
|
||||||
|
"appearAnimation" : "C09SPF0"
|
||||||
|
},
|
||||||
|
"defender" :{
|
||||||
|
"animation" : "C09SPF1",
|
||||||
|
"appearAnimation" : "C09SPF0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"damage":{
|
||||||
|
"type":"core:damage",
|
||||||
|
"optional":false,
|
||||||
|
"indirect":true,
|
||||||
|
"customEffectId" : 82
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advanced":{
|
||||||
|
"battleEffects":{
|
||||||
|
"obstacle":{
|
||||||
|
"patchCount" : 6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expert":{
|
||||||
|
"battleEffects":{
|
||||||
|
"obstacle":{
|
||||||
|
"patchCount" : 8
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"damage": true,
|
"damage": true,
|
||||||
"indifferent": true
|
"indifferent": true
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"targetCondition" : {
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
"noneOf" : {
|
||||||
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"forceField" : {
|
"forceField" : {
|
||||||
@@ -47,6 +124,61 @@
|
|||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{
|
"targetModifier":{
|
||||||
"clearAffected": true
|
"clearAffected": true
|
||||||
|
},
|
||||||
|
"battleEffects":{
|
||||||
|
"obstacle":{
|
||||||
|
"type":"core:obstacle",
|
||||||
|
"hidden" : false,
|
||||||
|
"passable" : false,
|
||||||
|
"trap" : false,
|
||||||
|
"trigger" : false,
|
||||||
|
"patchCount" : 1,
|
||||||
|
"turnsRemaining" : 2,
|
||||||
|
"attacker" :{
|
||||||
|
"range" : [[""]],
|
||||||
|
"shape" : [[""], ["TR"]],
|
||||||
|
"animation" : "C15SPE1",
|
||||||
|
"appearAnimation" : "C15SPE0"
|
||||||
|
},
|
||||||
|
"defender" :{
|
||||||
|
"range" : [[""]],
|
||||||
|
"shape" : [[""], ["TL"]],
|
||||||
|
"animation" : "C15SPE4",
|
||||||
|
"appearAnimation" : "C15SPE0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advanced":{
|
||||||
|
"battleEffects":{
|
||||||
|
"obstacle":{
|
||||||
|
"attacker" :{
|
||||||
|
"shape" : [[""], ["TR"], ["TR", "TL"]],
|
||||||
|
"animation" : "C15SPE10",
|
||||||
|
"appearAnimation" : "C15SPE9"
|
||||||
|
},
|
||||||
|
"defender" :{
|
||||||
|
"shape" : [[""], ["TL"], ["TL", "TR"]],
|
||||||
|
"animation" : "C15SPE7",
|
||||||
|
"appearAnimation" : "C15SPE6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expert":{
|
||||||
|
"battleEffects":{
|
||||||
|
"obstacle":{
|
||||||
|
"attacker" :{
|
||||||
|
"shape" : [[""], ["TR"], ["TR", "TL"]],
|
||||||
|
"animation" : "C15SPE10",
|
||||||
|
"appearAnimation" : "C15SPE9"
|
||||||
|
},
|
||||||
|
"defender" :{
|
||||||
|
"shape" : [[""], ["TL"], ["TL", "TR"]],
|
||||||
|
"animation" : "C15SPE7",
|
||||||
|
"appearAnimation" : "C15SPE6"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -66,6 +198,58 @@
|
|||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{
|
"targetModifier":{
|
||||||
"clearAffected": true
|
"clearAffected": true
|
||||||
|
},
|
||||||
|
"battleEffects":{
|
||||||
|
"obstacle":{
|
||||||
|
"type":"core:obstacle",
|
||||||
|
"hidden" : false,
|
||||||
|
"passable" : true,
|
||||||
|
"trap" : false,
|
||||||
|
"trigger" : true,
|
||||||
|
"patchCount" : 1,
|
||||||
|
"turnsRemaining" : 2,
|
||||||
|
"attacker" :{
|
||||||
|
"shape" : [[""]],
|
||||||
|
"range" : [[""], ["TR"]],
|
||||||
|
"animation" : "C07SPF61",
|
||||||
|
"appearAnimation" : "C07SPF60"
|
||||||
|
},
|
||||||
|
"defender" :{
|
||||||
|
"shape" : [[""]],
|
||||||
|
"range" : [[""], ["TL"]],
|
||||||
|
"animation" : "C07SPF61",
|
||||||
|
"appearAnimation" : "C07SPF60"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"damage":{
|
||||||
|
"type":"core:damage",
|
||||||
|
"optional":false,
|
||||||
|
"indirect":true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advanced":{
|
||||||
|
"battleEffects":{
|
||||||
|
"obstacle":{
|
||||||
|
"attacker" :{
|
||||||
|
"range" : [[""], ["TR"], ["TR", "TL"]]
|
||||||
|
},
|
||||||
|
"defender" :{
|
||||||
|
"range" : [[""], ["TL"], ["TL", "TR"]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expert":{
|
||||||
|
"battleEffects":{
|
||||||
|
"obstacle":{
|
||||||
|
"attacker" :{
|
||||||
|
"range" : [[""], ["TR"], ["TR", "TL"]]
|
||||||
|
},
|
||||||
|
"defender" :{
|
||||||
|
"range" : [[""], ["TL"], ["TL", "TR"]]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -73,8 +257,10 @@
|
|||||||
"damage": true,
|
"damage": true,
|
||||||
"indifferent": true
|
"indifferent": true
|
||||||
},
|
},
|
||||||
"immunity" : {
|
"targetCondition" : {
|
||||||
"DIRECT_DAMAGE_IMMUNITY": true
|
"noneOf" : {
|
||||||
|
"bonus.DIRECT_DAMAGE_IMMUNITY" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"earthquake" : {
|
"earthquake" : {
|
||||||
@@ -87,7 +273,27 @@
|
|||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
|
"battleEffects":{
|
||||||
|
"catapult":{
|
||||||
|
"type":"core:catapult",
|
||||||
|
"targetsToAttack": 2
|
||||||
|
}
|
||||||
|
},
|
||||||
"range" : "X"
|
"range" : "X"
|
||||||
|
},
|
||||||
|
"advanced":{
|
||||||
|
"battleEffects":{
|
||||||
|
"catapult":{
|
||||||
|
"targetsToAttack": 3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expert":{
|
||||||
|
"battleEffects":{
|
||||||
|
"catapult":{
|
||||||
|
"targetsToAttack": 4
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
@@ -107,7 +313,19 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{
|
||||||
|
"smart":true
|
||||||
|
},
|
||||||
|
"battleEffects":{
|
||||||
|
"dispel":{
|
||||||
|
"type":"core:dispel",
|
||||||
|
"optional":false,
|
||||||
|
"ignoreImmunity" : true,
|
||||||
|
"dispelNegative":true,
|
||||||
|
"dispelNeutral":true,
|
||||||
|
"dispelPositive":true
|
||||||
|
}
|
||||||
|
},
|
||||||
"range" : "0"
|
"range" : "0"
|
||||||
},
|
},
|
||||||
"advanced":{
|
"advanced":{
|
||||||
@@ -115,6 +333,16 @@
|
|||||||
},
|
},
|
||||||
"expert":{
|
"expert":{
|
||||||
"targetModifier":{"smart":false},
|
"targetModifier":{"smart":false},
|
||||||
|
"battleEffects":{
|
||||||
|
"dispel":{
|
||||||
|
"optional":true
|
||||||
|
},
|
||||||
|
"removeObstacle":{
|
||||||
|
"optional":true,
|
||||||
|
"type":"core:removeObstacle",
|
||||||
|
"removeAllSpells" : true
|
||||||
|
}
|
||||||
|
},
|
||||||
"range" : "X"
|
"range" : "X"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -135,6 +363,21 @@
|
|||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
|
"battleEffects":{
|
||||||
|
"heal":{
|
||||||
|
"type":"core:heal",
|
||||||
|
"healLevel":"heal",
|
||||||
|
"healPower":"permanent",
|
||||||
|
"optional":true
|
||||||
|
},
|
||||||
|
"cure":{
|
||||||
|
"type":"core:dispel",
|
||||||
|
"optional":true,
|
||||||
|
"dispelNegative":true,
|
||||||
|
"dispelNeutral":false,
|
||||||
|
"dispelPositive":false
|
||||||
|
}
|
||||||
|
},
|
||||||
"range" : "0"
|
"range" : "0"
|
||||||
},
|
},
|
||||||
"expert":{
|
"expert":{
|
||||||
@@ -158,17 +401,49 @@
|
|||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
|
"battleEffects":{
|
||||||
|
"heal":{
|
||||||
|
"type":"core:heal",
|
||||||
|
"healLevel":"resurrect",
|
||||||
|
"healPower":"oneBattle",
|
||||||
|
"minFullUnits" : 1
|
||||||
|
},
|
||||||
|
"cure":{
|
||||||
|
"type":"core:dispel",
|
||||||
|
"indirect": true,
|
||||||
|
"optional":true,
|
||||||
|
"dispelNegative":true,
|
||||||
|
"dispelNeutral":false,
|
||||||
|
"dispelPositive":false
|
||||||
|
}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":true}
|
"targetModifier":{"smart":true}
|
||||||
|
},
|
||||||
|
"advanced":{
|
||||||
|
"battleEffects":{
|
||||||
|
"heal":{
|
||||||
|
"healPower":"permanent"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expert":{
|
||||||
|
"battleEffects":{
|
||||||
|
"heal":{
|
||||||
|
"healPower":"permanent"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"rising": true,
|
"rising": true,
|
||||||
"positive": true
|
"positive": true
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
"targetCondition" : {
|
||||||
"UNDEAD": true,
|
"noneOf" : {
|
||||||
"SIEGE_WEAPON": true,
|
"bonus.NON_LIVING" : "absolute",
|
||||||
"NON_LIVING": true
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"animateDead" : {
|
"animateDead" : {
|
||||||
@@ -184,6 +459,14 @@
|
|||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
|
"battleEffects":{
|
||||||
|
"heal":{
|
||||||
|
"type":"core:heal",
|
||||||
|
"healLevel":"resurrect",
|
||||||
|
"healPower":"permanent",
|
||||||
|
"minFullUnits" : 1
|
||||||
|
}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":true}
|
"targetModifier":{"smart":true}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -191,8 +474,10 @@
|
|||||||
"rising": true,
|
"rising": true,
|
||||||
"positive": true
|
"positive": true
|
||||||
},
|
},
|
||||||
"absoluteLimit" : {
|
"targetCondition" : {
|
||||||
"UNDEAD": true
|
"allOf" : {
|
||||||
|
"bonus.UNDEAD" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sacrifice" : {
|
"sacrifice" : {
|
||||||
@@ -208,6 +493,14 @@
|
|||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
|
"battleEffects":{
|
||||||
|
"sacrifice":{
|
||||||
|
"type":"core:sacrifice",
|
||||||
|
"healLevel":"resurrect",
|
||||||
|
"healPower":"permanent",
|
||||||
|
"minFullUnits" : 0
|
||||||
|
}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":true}
|
"targetModifier":{"smart":true}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -215,10 +508,12 @@
|
|||||||
"rising": true,
|
"rising": true,
|
||||||
"positive": true
|
"positive": true
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
"targetCondition" : {
|
||||||
"SIEGE_WEAPON": true,
|
"noneOf" : {
|
||||||
"UNDEAD": true,
|
"bonus.NON_LIVING" : "absolute",
|
||||||
"NON_LIVING": true
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"teleport" : {
|
"teleport" : {
|
||||||
@@ -231,14 +526,21 @@
|
|||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
|
"battleEffects":{
|
||||||
|
"teleport":{
|
||||||
|
"type":"core:teleport"
|
||||||
|
}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":true}
|
"targetModifier":{"smart":true}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"positive": true
|
"positive": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"removeObstacle" : {
|
"removeObstacle" : {
|
||||||
@@ -252,7 +554,28 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0"
|
"range" : "0",
|
||||||
|
"battleEffects": {
|
||||||
|
"removeObstacle" : {
|
||||||
|
"optional":false,
|
||||||
|
"type":"core:removeObstacle",
|
||||||
|
"removeUsual" : true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advanced" : {
|
||||||
|
"battleEffects": {
|
||||||
|
"removeObstacle" : {
|
||||||
|
"removeSpells" : ["fireWall"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expert" : {
|
||||||
|
"battleEffects": {
|
||||||
|
"removeObstacle" : {
|
||||||
|
"removeAllSpells" : true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
@@ -271,14 +594,36 @@
|
|||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
|
"battleEffects":{
|
||||||
|
"clone":{
|
||||||
|
"maxTier":5,
|
||||||
|
"type":"core:clone"
|
||||||
|
}
|
||||||
|
},
|
||||||
"targetModifier":{"smart":true}
|
"targetModifier":{"smart":true}
|
||||||
|
},
|
||||||
|
"advanced":{
|
||||||
|
"battleEffects":{
|
||||||
|
"clone":{
|
||||||
|
"maxTier":6
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"expert":{
|
||||||
|
"battleEffects":{
|
||||||
|
"clone":{
|
||||||
|
"maxTier":1000
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"positive": true
|
"positive": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fireElemental" : {
|
"fireElemental" : {
|
||||||
@@ -292,7 +637,15 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "X"
|
"range" : "X",
|
||||||
|
"battleEffects":{
|
||||||
|
"summon":{
|
||||||
|
"exclusive":true,
|
||||||
|
"id":"fireElemental",
|
||||||
|
"permanent":false,
|
||||||
|
"type":"core:summon"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
@@ -310,7 +663,15 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "X"
|
"range" : "X",
|
||||||
|
"battleEffects":{
|
||||||
|
"summon":{
|
||||||
|
"exclusive":true,
|
||||||
|
"id":"earthElemental",
|
||||||
|
"permanent":false,
|
||||||
|
"type":"core:summon"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
@@ -328,7 +689,15 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "X"
|
"range" : "X",
|
||||||
|
"battleEffects":{
|
||||||
|
"summon":{
|
||||||
|
"exclusive":true,
|
||||||
|
"id":"waterElemental",
|
||||||
|
"permanent":false,
|
||||||
|
"type":"core:summon"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
@@ -346,7 +715,15 @@
|
|||||||
},
|
},
|
||||||
"levels" : {
|
"levels" : {
|
||||||
"base":{
|
"base":{
|
||||||
"range" : "X"
|
"range" : "X",
|
||||||
|
"battleEffects":{
|
||||||
|
"summon":{
|
||||||
|
"exclusive":true,
|
||||||
|
"id":"airElemental",
|
||||||
|
"permanent":false,
|
||||||
|
"type":"core:summon"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
|
|||||||
+186
-95
@@ -108,11 +108,24 @@
|
|||||||
"spellDamageReduction" : {
|
"spellDamageReduction" : {
|
||||||
"type" : "SPELL_DAMAGE_REDUCTION",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype" : 0,
|
"subtype" : 0,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS",
|
||||||
|
"val" : 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advanced" : {
|
||||||
|
"effects" : {
|
||||||
|
"spellDamageReduction" : {
|
||||||
|
"val" : 50
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"expert":{
|
"expert":{
|
||||||
|
"effects" : {
|
||||||
|
"spellDamageReduction" : {
|
||||||
|
"val" : 50
|
||||||
|
}
|
||||||
|
},
|
||||||
"range" : "X"
|
"range" : "X"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -138,11 +151,24 @@
|
|||||||
"spellDamageReduction" : {
|
"spellDamageReduction" : {
|
||||||
"type" : "SPELL_DAMAGE_REDUCTION",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype" : 1,
|
"subtype" : 1,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS",
|
||||||
|
"val" : 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advanced" : {
|
||||||
|
"effects" : {
|
||||||
|
"spellDamageReduction" : {
|
||||||
|
"val" : 50
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"expert":{
|
"expert":{
|
||||||
|
"effects" : {
|
||||||
|
"spellDamageReduction" : {
|
||||||
|
"val" : 50
|
||||||
|
}
|
||||||
|
},
|
||||||
"range" : "X"
|
"range" : "X"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -168,11 +194,24 @@
|
|||||||
"spellDamageReduction" : {
|
"spellDamageReduction" : {
|
||||||
"type" : "SPELL_DAMAGE_REDUCTION",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype" : 2,
|
"subtype" : 2,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS",
|
||||||
|
"val" : 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advanced" : {
|
||||||
|
"effects" : {
|
||||||
|
"spellDamageReduction" : {
|
||||||
|
"val" : 50
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"expert":{
|
"expert":{
|
||||||
|
"effects" : {
|
||||||
|
"spellDamageReduction" : {
|
||||||
|
"val" : 50
|
||||||
|
}
|
||||||
|
},
|
||||||
"range" : "X"
|
"range" : "X"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -198,11 +237,24 @@
|
|||||||
"spellDamageReduction" : {
|
"spellDamageReduction" : {
|
||||||
"type" : "SPELL_DAMAGE_REDUCTION",
|
"type" : "SPELL_DAMAGE_REDUCTION",
|
||||||
"subtype" : 3,
|
"subtype" : 3,
|
||||||
"duration" : "N_TURNS"
|
"duration" : "N_TURNS",
|
||||||
|
"val" : 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"advanced" : {
|
||||||
|
"effects" : {
|
||||||
|
"spellDamageReduction" : {
|
||||||
|
"val" : 50
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"expert":{
|
"expert":{
|
||||||
|
"effects" : {
|
||||||
|
"spellDamageReduction" : {
|
||||||
|
"val" : 50
|
||||||
|
}
|
||||||
|
},
|
||||||
"range" : "X"
|
"range" : "X"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -224,26 +276,47 @@
|
|||||||
"base":{
|
"base":{
|
||||||
"range" : "0",
|
"range" : "0",
|
||||||
"targetModifier":{"smart":true},
|
"targetModifier":{"smart":true},
|
||||||
"effects" : {
|
"battleEffects":{
|
||||||
"levelSpellImmunity" : {
|
"spellImmunity":{
|
||||||
"val" : 3,
|
"type":"core:timed",
|
||||||
"type" : "LEVEL_SPELL_IMMUNITY",
|
"bonus":{
|
||||||
"valueType" : "INDEPENDENT_MAX",
|
"levelSpellImmunity":{
|
||||||
"duration" : "N_TURNS"
|
"addInfo" : 1, //absolute
|
||||||
|
"val" : 3,
|
||||||
|
"type" : "LEVEL_SPELL_IMMUNITY",
|
||||||
|
"valueType" : "INDEPENDENT_MAX",
|
||||||
|
"duration" : "N_TURNS"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dispel":{
|
||||||
|
"type":"core:dispel",
|
||||||
|
"optional":true,
|
||||||
|
"dispelNegative":true,
|
||||||
|
"dispelNeutral":true,
|
||||||
|
"dispelPositive":false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"advanced":{
|
"advanced":{
|
||||||
"effects" : {
|
"battleEffects":{
|
||||||
"levelSpellImmunity" : {
|
"spellImmunity":{
|
||||||
"val" : 4
|
"bonus" :{
|
||||||
|
"levelSpellImmunity":{
|
||||||
|
"val" : 4
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"expert":{
|
"expert":{
|
||||||
"effects" : {
|
"battleEffects":{
|
||||||
"levelSpellImmunity" : {
|
"spellImmunity":{
|
||||||
"val" : 5
|
"bonus":{
|
||||||
|
"levelSpellImmunity":{
|
||||||
|
"val" : 5
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -310,12 +383,14 @@
|
|||||||
"counters" : {
|
"counters" : {
|
||||||
"spell.curse": true
|
"spell.curse": true
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
|
||||||
"SIEGE_WEAPON": true,
|
|
||||||
"UNDEAD": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"positive": true
|
"positive": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"curse" : {
|
"curse" : {
|
||||||
@@ -350,12 +425,14 @@
|
|||||||
"counters" : {
|
"counters" : {
|
||||||
"spell.bless": true
|
"spell.bless": true
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
|
||||||
"SIEGE_WEAPON": true,
|
|
||||||
"UNDEAD": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"negative": true
|
"negative": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bloodlust" : {
|
"bloodlust" : {
|
||||||
@@ -401,11 +478,13 @@
|
|||||||
"counters" : {
|
"counters" : {
|
||||||
"spell.weakness": true
|
"spell.weakness": true
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"positive": true
|
"positive": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"precision" : {
|
"precision" : {
|
||||||
@@ -448,11 +527,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteLimit" : {
|
|
||||||
"SHOOTER": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"positive": true
|
"positive": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"allOf" : {
|
||||||
|
"bonus.SHOOTER" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"weakness" : {
|
"weakness" : {
|
||||||
@@ -697,16 +778,16 @@
|
|||||||
"counters" : {
|
"counters" : {
|
||||||
"spell.sorrow":true
|
"spell.sorrow":true
|
||||||
},
|
},
|
||||||
"absoluteImmunity":{
|
|
||||||
"SIEGE_WEAPON": true,
|
|
||||||
"UNDEAD": true,
|
|
||||||
},
|
|
||||||
"immunity" : {
|
|
||||||
"MIND_IMMUNITY": true,
|
|
||||||
"NON_LIVING": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"positive": true
|
"positive": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.MIND_IMMUNITY" : "normal",
|
||||||
|
"bonus.NON_LIVING" : "normal",
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sorrow" : {
|
"sorrow" : {
|
||||||
@@ -750,16 +831,16 @@
|
|||||||
"counters" : {
|
"counters" : {
|
||||||
"spell.mirth":true
|
"spell.mirth":true
|
||||||
},
|
},
|
||||||
"absoluteImmunity":{
|
|
||||||
"SIEGE_WEAPON": true,
|
|
||||||
"UNDEAD": true,
|
|
||||||
},
|
|
||||||
"immunity" : {
|
|
||||||
"MIND_IMMUNITY": true,
|
|
||||||
"NON_LIVING": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"negative": true
|
"negative": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.MIND_IMMUNITY" : "normal",
|
||||||
|
"bonus.NON_LIVING" : "normal",
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fortune" : {
|
"fortune" : {
|
||||||
@@ -894,11 +975,13 @@
|
|||||||
"counters" : {
|
"counters" : {
|
||||||
"spell.slow": true
|
"spell.slow": true
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"positive": true
|
"positive": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"slow" : {
|
"slow" : {
|
||||||
@@ -941,15 +1024,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
"counters" : {
|
"counters" : {
|
||||||
"spell.haste":true
|
"spell.haste":true
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"negative": true
|
"negative": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"slayer" : {
|
"slayer" : {
|
||||||
@@ -1024,8 +1108,7 @@
|
|||||||
"inFrenzy" : {
|
"inFrenzy" : {
|
||||||
"type" : "IN_FRENZY",
|
"type" : "IN_FRENZY",
|
||||||
"val" : 100,
|
"val" : 100,
|
||||||
"duration" : "N_TURNS",
|
"duration" : "UNTIL_ATTACK"
|
||||||
"turns" : 1
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -1044,11 +1127,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"positive": true
|
"positive": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"counterstrike" : {
|
"counterstrike" : {
|
||||||
@@ -1089,11 +1174,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"positive": true
|
"positive": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"berserk" : {
|
"berserk" : {
|
||||||
@@ -1127,16 +1214,16 @@
|
|||||||
"counters" : {
|
"counters" : {
|
||||||
"spell.hypnotize": true
|
"spell.hypnotize": true
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"immunity" : {
|
|
||||||
"MIND_IMMUNITY": true,
|
|
||||||
"UNDEAD": true,
|
|
||||||
"NON_LIVING": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"negative": true
|
"negative": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.MIND_IMMUNITY" : "normal",
|
||||||
|
"bonus.NON_LIVING" : "normal",
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"hypnotize" : {
|
"hypnotize" : {
|
||||||
@@ -1192,13 +1279,16 @@
|
|||||||
"counters" : {
|
"counters" : {
|
||||||
"spell.berserk": true
|
"spell.berserk": true
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
"targetCondition" : {
|
||||||
"SIEGE_WEAPON": true
|
"allOf" : {
|
||||||
},
|
"healthValueSpecial" : "absolute"
|
||||||
"immunity" : {
|
},
|
||||||
"MIND_IMMUNITY": true,
|
"noneOf" : {
|
||||||
"UNDEAD": true,
|
"bonus.SIEGE_WEAPON":"absolute",
|
||||||
"NON_LIVING": true
|
"bonus.MIND_IMMUNITY":"normal",
|
||||||
|
"bonus.UNDEAD":"normal",
|
||||||
|
"bonus.NON_LIVING":"normal"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"negative": true
|
"negative": true
|
||||||
@@ -1255,19 +1345,19 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteLimit" : {
|
|
||||||
"SHOOTER": true
|
|
||||||
},
|
|
||||||
"absoluteImmunity" : {
|
|
||||||
"SIEGE_WEAPON": true
|
|
||||||
},
|
|
||||||
"immunity" : {
|
|
||||||
"MIND_IMMUNITY": true,
|
|
||||||
"UNDEAD": true,
|
|
||||||
"NON_LIVING": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"negative": true
|
"negative": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"allOf" : {
|
||||||
|
"bonus.SHOOTER" : "absolute"
|
||||||
|
},
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.MIND_IMMUNITY" : "normal",
|
||||||
|
"bonus.NON_LIVING" : "normal",
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "normal"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"blind" : {
|
"blind" : {
|
||||||
@@ -1316,16 +1406,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"absoluteImmunity" : {
|
|
||||||
"SIEGE_WEAPON": true,
|
|
||||||
"UNDEAD": true
|
|
||||||
},
|
|
||||||
"immunity" : {
|
|
||||||
"MIND_IMMUNITY": true,
|
|
||||||
"NON_LIVING": true
|
|
||||||
},
|
|
||||||
"flags" : {
|
"flags" : {
|
||||||
"negative": true
|
"negative": true
|
||||||
|
},
|
||||||
|
"targetCondition" : {
|
||||||
|
"noneOf" : {
|
||||||
|
"bonus.MIND_IMMUNITY" : "normal",
|
||||||
|
"bonus.NON_LIVING" : "normal",
|
||||||
|
"bonus.SIEGE_WEAPON" : "absolute",
|
||||||
|
"bonus.UNDEAD" : "absolute"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* RNG.h, part of VCMI engine
|
||||||
|
*
|
||||||
|
* Authors: listed in file AUTHORS in main folder
|
||||||
|
*
|
||||||
|
* License: GNU General Public License v2.0 or later
|
||||||
|
* Full text of license available in license.txt file, in main folder
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace vstd
|
||||||
|
{
|
||||||
|
|
||||||
|
typedef std::function<int64_t()> TRandI64;
|
||||||
|
typedef std::function<double()> TRand;
|
||||||
|
|
||||||
|
class DLL_LINKAGE RNG
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
virtual ~RNG() = default;
|
||||||
|
|
||||||
|
virtual TRandI64 getInt64Range(int64_t lower, int64_t upper) = 0;
|
||||||
|
|
||||||
|
virtual TRand getDoubleRange(double lower, double upper) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace RandomGeneratorUtil
|
||||||
|
{
|
||||||
|
template<typename Container>
|
||||||
|
auto nextItem(const Container & container, vstd::RNG & rand) -> decltype(std::begin(container))
|
||||||
|
{
|
||||||
|
assert(!container.empty());
|
||||||
|
return std::next(container.begin(), rand.getInt64Range(0, container.size() - 1)());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Container>
|
||||||
|
auto nextItem(Container & container, vstd::RNG & rand) -> decltype(std::begin(container))
|
||||||
|
{
|
||||||
|
assert(!container.empty());
|
||||||
|
return std::next(container.begin(), rand.getInt64Range(0, container.size() - 1)());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void randomShuffle(std::vector<T> & container, vstd::RNG & rand)
|
||||||
|
{
|
||||||
|
int64_t n = (container.end() - container.begin());
|
||||||
|
|
||||||
|
for(int64_t i = n-1; i>0; --i)
|
||||||
|
{
|
||||||
|
std::swap(container.begin()[i],container.begin()[rand.getInt64Range(0, i)()]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-17
@@ -728,20 +728,6 @@ void CArtHandler::afterLoadFinalization()
|
|||||||
CBonusSystemNode::treeHasChanged();
|
CBonusSystemNode::treeHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
si32 CArtHandler::decodeArfifact(const std::string& identifier)
|
|
||||||
{
|
|
||||||
auto rawId = VLC->modh->identifiers.getIdentifier("core", "artifact", identifier);
|
|
||||||
if(rawId)
|
|
||||||
return rawId.get();
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CArtHandler::encodeArtifact(const si32 index)
|
|
||||||
{
|
|
||||||
return VLC->arth->artifacts[index]->identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
CArtifactInstance::CArtifactInstance()
|
CArtifactInstance::CArtifactInstance()
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
@@ -1407,7 +1393,7 @@ void CArtifactSet::serializeJsonHero(JsonSerializeFormat & handler, CMap * map)
|
|||||||
for(const ArtSlotInfo & info : artifactsInBackpack)
|
for(const ArtSlotInfo & info : artifactsInBackpack)
|
||||||
backpackTemp.push_back(info.artifact->artType->id);
|
backpackTemp.push_back(info.artifact->artType->id);
|
||||||
}
|
}
|
||||||
handler.serializeIdArray(NArtifactPosition::backpack, backpackTemp, &CArtHandler::decodeArfifact, &CArtHandler::encodeArtifact);
|
handler.serializeIdArray(NArtifactPosition::backpack, backpackTemp);
|
||||||
if(!handler.saving)
|
if(!handler.saving)
|
||||||
{
|
{
|
||||||
for(const ArtifactID & artifactID : backpackTemp)
|
for(const ArtifactID & artifactID : backpackTemp)
|
||||||
@@ -1441,12 +1427,12 @@ void CArtifactSet::serializeJsonSlot(JsonSerializeFormat & handler, const Artifa
|
|||||||
if(info != nullptr && !info->locked)
|
if(info != nullptr && !info->locked)
|
||||||
{
|
{
|
||||||
artifactID = info->artifact->artType->id;
|
artifactID = info->artifact->artType->id;
|
||||||
handler.serializeId(NArtifactPosition::namesHero[slot.num], artifactID, ArtifactID::NONE, &CArtHandler::decodeArfifact, &CArtHandler::encodeArtifact);
|
handler.serializeId(NArtifactPosition::namesHero[slot.num], artifactID, ArtifactID::NONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
handler.serializeId(NArtifactPosition::namesHero[slot.num], artifactID, ArtifactID::NONE, &CArtHandler::decodeArfifact, &CArtHandler::encodeArtifact);
|
handler.serializeId(NArtifactPosition::namesHero[slot.num], artifactID, ArtifactID::NONE);
|
||||||
|
|
||||||
if(artifactID != ArtifactID::NONE)
|
if(artifactID != ArtifactID::NONE)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -271,12 +271,6 @@ public:
|
|||||||
|
|
||||||
std::vector<bool> getDefaultAllowed() const override;
|
std::vector<bool> getDefaultAllowed() const override;
|
||||||
|
|
||||||
///json serialization helper
|
|
||||||
static si32 decodeArfifact(const std::string & identifier);
|
|
||||||
|
|
||||||
///json serialization helper
|
|
||||||
static std::string encodeArtifact(const si32 index);
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & artifacts;
|
h & artifacts;
|
||||||
|
|||||||
@@ -479,20 +479,6 @@ std::vector<bool> CCreatureHandler::getDefaultAllowed() const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
si32 CCreatureHandler::decodeCreature(const std::string& identifier)
|
|
||||||
{
|
|
||||||
auto rawId = VLC->modh->identifiers.getIdentifier("core", "creature", identifier);
|
|
||||||
if(rawId)
|
|
||||||
return rawId.get();
|
|
||||||
else
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CCreatureHandler::encodeCreature(const si32 index)
|
|
||||||
{
|
|
||||||
return VLC->creh->creatures[index]->identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CCreatureHandler::loadCrExpBon()
|
void CCreatureHandler::loadCrExpBon()
|
||||||
{
|
{
|
||||||
if (VLC->modh->modules.STACK_EXP) //reading default stack experience bonuses
|
if (VLC->modh->modules.STACK_EXP) //reading default stack experience bonuses
|
||||||
|
|||||||
@@ -259,12 +259,6 @@ public:
|
|||||||
|
|
||||||
std::vector<bool> getDefaultAllowed() const override;
|
std::vector<bool> getDefaultAllowed() const override;
|
||||||
|
|
||||||
///json serialization helper
|
|
||||||
static si32 decodeCreature(const std::string & identifier);
|
|
||||||
|
|
||||||
///json serialization helper
|
|
||||||
static std::string encodeCreature(const si32 index);
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
//TODO: should be optimized, not all these informations needs to be serialized (same for ccreature)
|
//TODO: should be optimized, not all these informations needs to be serialized (same for ccreature)
|
||||||
|
|||||||
@@ -189,14 +189,14 @@ int CGameInfoCallback::getSpellCost(const CSpell * sp, const CGHeroInstance * ca
|
|||||||
return caster->getSpellCost(sp);
|
return caster->getSpellCost(sp);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CGameInfoCallback::estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const
|
int64_t CGameInfoCallback::estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const
|
||||||
{
|
{
|
||||||
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
|
//boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
|
||||||
|
|
||||||
ERROR_RET_VAL_IF(hero && !canGetFullInfo(hero), "Cannot get info about caster!", -1);
|
ERROR_RET_VAL_IF(hero && !canGetFullInfo(hero), "Cannot get info about caster!", -1);
|
||||||
|
|
||||||
if (hero) //we see hero's spellbook
|
if(hero) //we see hero's spellbook
|
||||||
return sp->calculateDamage(hero, nullptr, hero->getEffectLevel(sp), hero->getEffectPower(sp));
|
return sp->calculateDamage(hero);
|
||||||
else
|
else
|
||||||
return 0; //mage guild
|
return 0; //mage guild
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "int3.h"
|
||||||
#include "ResourceSet.h" // for Res::ERes
|
#include "ResourceSet.h" // for Res::ERes
|
||||||
#include "battle/CPlayerBattleCallback.h"
|
#include "battle/CPlayerBattleCallback.h"
|
||||||
|
|
||||||
@@ -28,13 +29,15 @@ class CGTeleport;
|
|||||||
class CMapHeader;
|
class CMapHeader;
|
||||||
struct TeamState;
|
struct TeamState;
|
||||||
struct QuestInfo;
|
struct QuestInfo;
|
||||||
class int3;
|
|
||||||
struct ShashInt3;
|
struct ShashInt3;
|
||||||
|
class CGameState;
|
||||||
|
|
||||||
|
|
||||||
class DLL_LINKAGE CGameInfoCallback : public virtual CCallbackBase
|
class DLL_LINKAGE CGameInfoCallback : public virtual CCallbackBase
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
|
CGameState * gs;
|
||||||
|
|
||||||
CGameInfoCallback();
|
CGameInfoCallback();
|
||||||
CGameInfoCallback(CGameState *GS, boost::optional<PlayerColor> Player);
|
CGameInfoCallback(CGameState *GS, boost::optional<PlayerColor> Player);
|
||||||
bool hasAccess(boost::optional<PlayerColor> playerId) const;
|
bool hasAccess(boost::optional<PlayerColor> playerId) const;
|
||||||
@@ -72,7 +75,7 @@ public:
|
|||||||
int getHeroCount(PlayerColor player, bool includeGarrisoned) const;
|
int getHeroCount(PlayerColor player, bool includeGarrisoned) const;
|
||||||
bool getHeroInfo(const CGObjectInstance * hero, InfoAboutHero & dest, const CGObjectInstance * selectedObject = nullptr) const;
|
bool getHeroInfo(const CGObjectInstance * hero, InfoAboutHero & dest, const CGObjectInstance * selectedObject = nullptr) const;
|
||||||
int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction
|
int getSpellCost(const CSpell * sp, const CGHeroInstance * caster) const; //when called during battle, takes into account creatures' spell cost reduction
|
||||||
int estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const; //estimates damage of given spell; returns 0 if spell causes no dmg
|
int64_t estimateSpellDamage(const CSpell * sp, const CGHeroInstance * hero) const; //estimates damage of given spell; returns 0 if spell causes no dmg
|
||||||
const CArtifactInstance * getArtInstance(ArtifactInstanceID aid) const;
|
const CArtifactInstance * getArtInstance(ArtifactInstanceID aid) const;
|
||||||
const CGObjectInstance * getObjInstance(ObjectInstanceID oid) const;
|
const CGObjectInstance * getObjInstance(ObjectInstanceID oid) const;
|
||||||
|
|
||||||
|
|||||||
+7
-18
@@ -134,7 +134,7 @@ std::shared_ptr<CScriptingModule> CDynLibHandler::getNewScriptingModule(std::str
|
|||||||
BattleAction CGlobalAI::activeStack(const CStack * stack)
|
BattleAction CGlobalAI::activeStack(const CStack * stack)
|
||||||
{
|
{
|
||||||
BattleAction ba;
|
BattleAction ba;
|
||||||
ba.actionType = Battle::DEFEND;
|
ba.actionType = EActionType::DEFEND;
|
||||||
ba.stackNumber = stack->ID;
|
ba.stackNumber = stack->ID;
|
||||||
return ba;
|
return ba;
|
||||||
}
|
}
|
||||||
@@ -164,9 +164,9 @@ void CAdventureAI::battleStart(const CCreatureSet * army1, const CCreatureSet *
|
|||||||
battleAI->battleStart(army1, army2, tile, hero1, hero2, side);
|
battleAI->battleStart(army1, army2, tile, hero1, hero2, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAdventureAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa)
|
void CAdventureAI::battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, const std::vector<MetaString> & battleLog)
|
||||||
{
|
{
|
||||||
battleAI->battleStacksAttacked(bsa);
|
battleAI->battleStacksAttacked(bsa, battleLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAdventureAI::actionStarted(const BattleAction & action)
|
void CAdventureAI::actionStarted(const BattleAction & action)
|
||||||
@@ -189,19 +189,9 @@ void CAdventureAI::battleStacksEffectsSet(const SetStackEffect & sse)
|
|||||||
battleAI->battleStacksEffectsSet(sse);
|
battleAI->battleStacksEffectsSet(sse);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAdventureAI::battleStacksRemoved(const BattleStacksRemoved & bsr)
|
void CAdventureAI::battleObstaclesChanged(const std::vector<ObstacleChanges> & obstacles)
|
||||||
{
|
{
|
||||||
battleAI->battleStacksRemoved(bsr);
|
battleAI->battleObstaclesChanged(obstacles);
|
||||||
}
|
|
||||||
|
|
||||||
void CAdventureAI::battleObstaclesRemoved(const std::set<si32> & removedObstacles)
|
|
||||||
{
|
|
||||||
battleAI->battleObstaclesRemoved(removedObstacles);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAdventureAI::battleNewStackAppeared(const CStack * stack)
|
|
||||||
{
|
|
||||||
battleAI->battleNewStackAppeared(stack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAdventureAI::battleStackMoved(const CStack * stack, std::vector<BattleHex> dest, int distance)
|
void CAdventureAI::battleStackMoved(const CStack * stack, std::vector<BattleHex> dest, int distance)
|
||||||
@@ -225,10 +215,9 @@ void CAdventureAI::battleEnd(const BattleResult * br)
|
|||||||
battleAI.reset();
|
battleAI.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CAdventureAI::battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain,
|
void CAdventureAI::battleUnitsChanged(const std::vector<UnitChanges> & units, const std::vector<CustomEffectInfo> & customEffects, const std::vector<MetaString> & battleLog)
|
||||||
bool tentHeal, si32 lifeDrainFrom)
|
|
||||||
{
|
{
|
||||||
battleAI->battleStacksHealedRes(healedStacks, lifeDrain, tentHeal, lifeDrainFrom);
|
battleAI->battleUnitsChanged(units, customEffects, battleLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
BattleAction CAdventureAI::activeStack(const CStack * stack)
|
BattleAction CAdventureAI::activeStack(const CStack * stack)
|
||||||
|
|||||||
@@ -43,7 +43,6 @@ struct Bonus;
|
|||||||
struct PackageApplied;
|
struct PackageApplied;
|
||||||
struct SetObjectProperty;
|
struct SetObjectProperty;
|
||||||
struct CatapultAttack;
|
struct CatapultAttack;
|
||||||
struct BattleStacksRemoved;
|
|
||||||
struct StackLocation;
|
struct StackLocation;
|
||||||
class CStackInstance;
|
class CStackInstance;
|
||||||
class CCommanderInstance;
|
class CCommanderInstance;
|
||||||
@@ -134,20 +133,17 @@ public:
|
|||||||
virtual void battleNewRound(int round) override;
|
virtual void battleNewRound(int round) override;
|
||||||
virtual void battleCatapultAttacked(const CatapultAttack & ca) override;
|
virtual void battleCatapultAttacked(const CatapultAttack & ca) override;
|
||||||
virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override;
|
virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side) override;
|
||||||
virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa) override;
|
virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, const std::vector<MetaString> & battleLog) override;
|
||||||
virtual void actionStarted(const BattleAction &action) override;
|
virtual void actionStarted(const BattleAction &action) override;
|
||||||
virtual void battleNewRoundFirst(int round) override;
|
virtual void battleNewRoundFirst(int round) override;
|
||||||
virtual void actionFinished(const BattleAction &action) override;
|
virtual void actionFinished(const BattleAction &action) override;
|
||||||
virtual void battleStacksEffectsSet(const SetStackEffect & sse) override;
|
virtual void battleStacksEffectsSet(const SetStackEffect & sse) override;
|
||||||
//virtual void battleTriggerEffect(const BattleTriggerEffect & bte);
|
virtual void battleObstaclesChanged(const std::vector<ObstacleChanges> & obstacles) override;
|
||||||
virtual void battleStacksRemoved(const BattleStacksRemoved & bsr) override;
|
|
||||||
virtual void battleObstaclesRemoved(const std::set<si32> & removedObstacles) override;
|
|
||||||
virtual void battleNewStackAppeared(const CStack * stack) override;
|
|
||||||
virtual void battleStackMoved(const CStack * stack, std::vector<BattleHex> dest, int distance) override;
|
virtual void battleStackMoved(const CStack * stack, std::vector<BattleHex> dest, int distance) override;
|
||||||
virtual void battleAttack(const BattleAttack *ba) override;
|
virtual void battleAttack(const BattleAttack *ba) override;
|
||||||
virtual void battleSpellCast(const BattleSpellCast *sc) override;
|
virtual void battleSpellCast(const BattleSpellCast *sc) override;
|
||||||
virtual void battleEnd(const BattleResult *br) override;
|
virtual void battleEnd(const BattleResult *br) override;
|
||||||
virtual void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom) override;
|
virtual void battleUnitsChanged(const std::vector<UnitChanges> & units, const std::vector<CustomEffectInfo> & customEffects, const std::vector<MetaString> & battleLog) override;
|
||||||
|
|
||||||
virtual void saveGame(BinarySerializer & h, const int version) override; //saving
|
virtual void saveGame(BinarySerializer & h, const int version) override; //saving
|
||||||
virtual void loadGame(BinaryDeserializer & h, const int version) override; //loading
|
virtual void loadGame(BinaryDeserializer & h, const int version) override; //loading
|
||||||
|
|||||||
+8
-9
@@ -33,7 +33,6 @@
|
|||||||
#include "rmg/CMapGenerator.h"
|
#include "rmg/CMapGenerator.h"
|
||||||
#include "CStopWatch.h"
|
#include "CStopWatch.h"
|
||||||
#include "mapping/CMapEditManager.h"
|
#include "mapping/CMapEditManager.h"
|
||||||
#include "mapping/CMapService.h"
|
|
||||||
#include "serializer/CTypeList.h"
|
#include "serializer/CTypeList.h"
|
||||||
#include "serializer/CMemorySerializer.h"
|
#include "serializer/CMemorySerializer.h"
|
||||||
#include "VCMIDirs.h"
|
#include "VCMIDirs.h"
|
||||||
@@ -703,7 +702,7 @@ CGameState::~CGameState()
|
|||||||
ptr.second.dellNull();
|
ptr.second.dellNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameState::init(StartInfo * si, bool allowSavingRandomMap)
|
void CGameState::init(const IMapService * mapService, StartInfo * si, bool allowSavingRandomMap)
|
||||||
{
|
{
|
||||||
logGlobal->info("\tUsing random seed: %d", si->seedToBeUsed);
|
logGlobal->info("\tUsing random seed: %d", si->seedToBeUsed);
|
||||||
getRandomGenerator().setSeed(si->seedToBeUsed);
|
getRandomGenerator().setSeed(si->seedToBeUsed);
|
||||||
@@ -714,10 +713,10 @@ void CGameState::init(StartInfo * si, bool allowSavingRandomMap)
|
|||||||
switch(scenarioOps->mode)
|
switch(scenarioOps->mode)
|
||||||
{
|
{
|
||||||
case StartInfo::NEW_GAME:
|
case StartInfo::NEW_GAME:
|
||||||
initNewGame(allowSavingRandomMap);
|
initNewGame(mapService, allowSavingRandomMap);
|
||||||
break;
|
break;
|
||||||
case StartInfo::CAMPAIGN:
|
case StartInfo::CAMPAIGN:
|
||||||
initCampaign();
|
initCampaign(mapService);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logGlobal->error("Wrong mode: %d", static_cast<int>(scenarioOps->mode));
|
logGlobal->error("Wrong mode: %d", static_cast<int>(scenarioOps->mode));
|
||||||
@@ -773,7 +772,7 @@ void CGameState::init(StartInfo * si, bool allowSavingRandomMap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameState::initNewGame(bool allowSavingRandomMap)
|
void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRandomMap)
|
||||||
{
|
{
|
||||||
if(scenarioOps->createRandomMap())
|
if(scenarioOps->createRandomMap())
|
||||||
{
|
{
|
||||||
@@ -800,7 +799,7 @@ void CGameState::initNewGame(bool allowSavingRandomMap)
|
|||||||
const std::string fileName = boost::str(boost::format("%s_%d.vmap") % templateName % seed );
|
const std::string fileName = boost::str(boost::format("%s_%d.vmap") % templateName % seed );
|
||||||
const auto fullPath = path / fileName;
|
const auto fullPath = path / fileName;
|
||||||
|
|
||||||
CMapService::saveMap(randomMap, fullPath);
|
mapService->saveMap(randomMap, fullPath);
|
||||||
|
|
||||||
logGlobal->info("Random map has been saved to:");
|
logGlobal->info("Random map has been saved to:");
|
||||||
logGlobal->info(fullPath.string());
|
logGlobal->info(fullPath.string());
|
||||||
@@ -841,11 +840,11 @@ void CGameState::initNewGame(bool allowSavingRandomMap)
|
|||||||
{
|
{
|
||||||
logGlobal->info("Open map file: %s", scenarioOps->mapname);
|
logGlobal->info("Open map file: %s", scenarioOps->mapname);
|
||||||
const ResourceID mapURI(scenarioOps->mapname, EResType::MAP);
|
const ResourceID mapURI(scenarioOps->mapname, EResType::MAP);
|
||||||
map = CMapService::loadMap(mapURI).release();
|
map = mapService->loadMap(mapURI).release();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameState::initCampaign()
|
void CGameState::initCampaign(const IMapService * mapService)
|
||||||
{
|
{
|
||||||
logGlobal->info("Open campaign map file: %d", scenarioOps->campState->currentMap.get());
|
logGlobal->info("Open campaign map file: %d", scenarioOps->campState->currentMap.get());
|
||||||
auto campaign = scenarioOps->campState;
|
auto campaign = scenarioOps->campState;
|
||||||
@@ -857,7 +856,7 @@ void CGameState::initCampaign()
|
|||||||
|
|
||||||
std::string & mapContent = campaign->camp->mapPieces[*campaign->currentMap];
|
std::string & mapContent = campaign->camp->mapPieces[*campaign->currentMap];
|
||||||
auto buffer = reinterpret_cast<const ui8 *>(mapContent.data());
|
auto buffer = reinterpret_cast<const ui8 *>(mapContent.data());
|
||||||
map = CMapService::loadMap(buffer, mapContent.size(), scenarioName).release();
|
map = mapService->loadMap(buffer, mapContent.size(), scenarioName).release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CGameState::checkMapChecksum()
|
void CGameState::checkMapChecksum()
|
||||||
|
|||||||
+5
-5
@@ -26,7 +26,6 @@ class CTown;
|
|||||||
class CCallback;
|
class CCallback;
|
||||||
class IGameCallback;
|
class IGameCallback;
|
||||||
class CCreatureSet;
|
class CCreatureSet;
|
||||||
class CStack;
|
|
||||||
class CQuest;
|
class CQuest;
|
||||||
class CGHeroInstance;
|
class CGHeroInstance;
|
||||||
class CGTownInstance;
|
class CGTownInstance;
|
||||||
@@ -54,6 +53,7 @@ class CQuest;
|
|||||||
class CCampaignScenario;
|
class CCampaignScenario;
|
||||||
struct EventCondition;
|
struct EventCondition;
|
||||||
class CScenarioTravel;
|
class CScenarioTravel;
|
||||||
|
class IMapService;
|
||||||
|
|
||||||
namespace boost
|
namespace boost
|
||||||
{
|
{
|
||||||
@@ -127,7 +127,7 @@ struct UpgradeInfo
|
|||||||
UpgradeInfo(){oldID = CreatureID::NONE;};
|
UpgradeInfo(){oldID = CreatureID::NONE;};
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BattleInfo;
|
class BattleInfo;
|
||||||
|
|
||||||
DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult);
|
DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EVictoryLossCheckResult & victoryLossCheckResult);
|
||||||
|
|
||||||
@@ -152,7 +152,7 @@ public:
|
|||||||
CGameState();
|
CGameState();
|
||||||
virtual ~CGameState();
|
virtual ~CGameState();
|
||||||
|
|
||||||
void init(StartInfo * si, bool allowSavingRandomMap = false);
|
void init(const IMapService * mapService, StartInfo * si, bool allowSavingRandomMap = false);
|
||||||
|
|
||||||
ConstTransitivePtr<StartInfo> scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized)
|
ConstTransitivePtr<StartInfo> scenarioOps, initialOpts; //second one is a copy of settings received from pregame (not randomized)
|
||||||
PlayerColor currentPlayer; //ID of player currently having turn
|
PlayerColor currentPlayer; //ID of player currently having turn
|
||||||
@@ -245,8 +245,8 @@ private:
|
|||||||
|
|
||||||
// ----- initialization -----
|
// ----- initialization -----
|
||||||
|
|
||||||
void initNewGame(bool allowSavingRandomMap);
|
void initNewGame(const IMapService * mapService, bool allowSavingRandomMap);
|
||||||
void initCampaign();
|
void initCampaign(const IMapService * mapService);
|
||||||
void checkMapChecksum();
|
void checkMapChecksum();
|
||||||
void initGrailPosition();
|
void initGrailPosition();
|
||||||
void initRandomFactionsForPlayers();
|
void initRandomFactionsForPlayers();
|
||||||
|
|||||||
+49
-4
@@ -10,14 +10,19 @@ set(lib_SRCS
|
|||||||
battle/BattleAttackInfo.cpp
|
battle/BattleAttackInfo.cpp
|
||||||
battle/BattleHex.cpp
|
battle/BattleHex.cpp
|
||||||
battle/BattleInfo.cpp
|
battle/BattleInfo.cpp
|
||||||
|
battle/BattleProxy.cpp
|
||||||
battle/CBattleInfoCallback.cpp
|
battle/CBattleInfoCallback.cpp
|
||||||
battle/CBattleInfoEssentials.cpp
|
battle/CBattleInfoEssentials.cpp
|
||||||
battle/CCallbackBase.cpp
|
battle/CCallbackBase.cpp
|
||||||
battle/CObstacleInstance.cpp
|
battle/CObstacleInstance.cpp
|
||||||
battle/CPlayerBattleCallback.cpp
|
battle/CPlayerBattleCallback.cpp
|
||||||
|
battle/CUnitState.cpp
|
||||||
|
battle/Destination.cpp
|
||||||
|
battle/IBattleState.cpp
|
||||||
battle/ReachabilityInfo.cpp
|
battle/ReachabilityInfo.cpp
|
||||||
battle/SideInBattle.cpp
|
battle/SideInBattle.cpp
|
||||||
battle/SiegeInfo.cpp
|
battle/SiegeInfo.cpp
|
||||||
|
battle/Unit.cpp
|
||||||
|
|
||||||
filesystem/AdapterLoaders.cpp
|
filesystem/AdapterLoaders.cpp
|
||||||
filesystem/CArchiveLoader.cpp
|
filesystem/CArchiveLoader.cpp
|
||||||
@@ -93,12 +98,29 @@ set(lib_SRCS
|
|||||||
|
|
||||||
spells/AdventureSpellMechanics.cpp
|
spells/AdventureSpellMechanics.cpp
|
||||||
spells/BattleSpellMechanics.cpp
|
spells/BattleSpellMechanics.cpp
|
||||||
spells/CDefaultSpellMechanics.cpp
|
|
||||||
spells/CreatureSpellMechanics.cpp
|
|
||||||
spells/CSpellHandler.cpp
|
spells/CSpellHandler.cpp
|
||||||
spells/ISpellMechanics.cpp
|
spells/ISpellMechanics.cpp
|
||||||
|
spells/Problem.cpp
|
||||||
|
spells/TargetCondition.cpp
|
||||||
spells/ViewSpellInt.cpp
|
spells/ViewSpellInt.cpp
|
||||||
|
|
||||||
|
spells/effects/Catapult.cpp
|
||||||
|
spells/effects/Clone.cpp
|
||||||
|
spells/effects/Damage.cpp
|
||||||
|
spells/effects/Dispel.cpp
|
||||||
|
spells/effects/Effect.cpp
|
||||||
|
spells/effects/Effects.cpp
|
||||||
|
spells/effects/Heal.cpp
|
||||||
|
spells/effects/LocationEffect.cpp
|
||||||
|
spells/effects/Obstacle.cpp
|
||||||
|
spells/effects/Registry.cpp
|
||||||
|
spells/effects/UnitEffect.cpp
|
||||||
|
spells/effects/Summon.cpp
|
||||||
|
spells/effects/Teleport.cpp
|
||||||
|
spells/effects/Timed.cpp
|
||||||
|
spells/effects/RemoveObstacle.cpp
|
||||||
|
spells/effects/Sacrifice.cpp
|
||||||
|
|
||||||
CAndroidVMHelper.cpp
|
CAndroidVMHelper.cpp
|
||||||
CArtHandler.cpp
|
CArtHandler.cpp
|
||||||
CBonusTypeHandler.cpp
|
CBonusTypeHandler.cpp
|
||||||
@@ -143,14 +165,20 @@ set(lib_HEADERS
|
|||||||
battle/BattleAttackInfo.h
|
battle/BattleAttackInfo.h
|
||||||
battle/BattleHex.h
|
battle/BattleHex.h
|
||||||
battle/BattleInfo.h
|
battle/BattleInfo.h
|
||||||
|
battle/BattleProxy.h
|
||||||
battle/CBattleInfoCallback.h
|
battle/CBattleInfoCallback.h
|
||||||
battle/CBattleInfoEssentials.h
|
battle/CBattleInfoEssentials.h
|
||||||
battle/CCallbackBase.h
|
battle/CCallbackBase.h
|
||||||
battle/CObstacleInstance.h
|
battle/CObstacleInstance.h
|
||||||
battle/CPlayerBattleCallback.h
|
battle/CPlayerBattleCallback.h
|
||||||
|
battle/CUnitState.h
|
||||||
|
battle/Destination.h
|
||||||
|
battle/IBattleState.h
|
||||||
|
battle/IUnitInfo.h
|
||||||
battle/ReachabilityInfo.h
|
battle/ReachabilityInfo.h
|
||||||
battle/SideInBattle.h
|
battle/SideInBattle.h
|
||||||
battle/SiegeInfo.h
|
battle/SiegeInfo.h
|
||||||
|
battle/Unit.h
|
||||||
|
|
||||||
filesystem/AdapterLoaders.h
|
filesystem/AdapterLoaders.h
|
||||||
filesystem/CArchiveLoader.h
|
filesystem/CArchiveLoader.h
|
||||||
@@ -227,14 +255,31 @@ set(lib_HEADERS
|
|||||||
|
|
||||||
spells/AdventureSpellMechanics.h
|
spells/AdventureSpellMechanics.h
|
||||||
spells/BattleSpellMechanics.h
|
spells/BattleSpellMechanics.h
|
||||||
spells/CDefaultSpellMechanics.h
|
|
||||||
spells/CreatureSpellMechanics.h
|
|
||||||
spells/CSpellHandler.h
|
spells/CSpellHandler.h
|
||||||
spells/ISpellMechanics.h
|
spells/ISpellMechanics.h
|
||||||
spells/Magic.h
|
spells/Magic.h
|
||||||
spells/SpellMechanics.h
|
spells/SpellMechanics.h
|
||||||
|
spells/Problem.h
|
||||||
|
spells/TargetCondition.h
|
||||||
spells/ViewSpellInt.h
|
spells/ViewSpellInt.h
|
||||||
|
|
||||||
|
spells/effects/Catapult.h
|
||||||
|
spells/effects/Clone.h
|
||||||
|
spells/effects/Damage.h
|
||||||
|
spells/effects/Dispel.h
|
||||||
|
spells/effects/Effect.h
|
||||||
|
spells/effects/Effects.h
|
||||||
|
spells/effects/EffectsFwd.h
|
||||||
|
spells/effects/Heal.h
|
||||||
|
spells/effects/LocationEffect.h
|
||||||
|
spells/effects/Obstacle.h
|
||||||
|
spells/effects/Registry.h
|
||||||
|
spells/effects/UnitEffect.h
|
||||||
|
spells/effects/Summon.h
|
||||||
|
spells/effects/Teleport.h
|
||||||
|
spells/effects/Timed.h
|
||||||
|
spells/effects/RemoveObstacle.h
|
||||||
|
spells/effects/Sacrifice.h
|
||||||
AI_Base.h
|
AI_Base.h
|
||||||
CAndroidVMHelper.h
|
CAndroidVMHelper.h
|
||||||
CArtHandler.h
|
CArtHandler.h
|
||||||
|
|||||||
+84
-41
@@ -316,7 +316,7 @@ void CIdentifierStorage::finalize()
|
|||||||
state = FINISHED;
|
state = FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
CContentHandler::ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, std::string objectName):
|
ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler, std::string objectName):
|
||||||
handler(handler),
|
handler(handler),
|
||||||
objectName(objectName),
|
objectName(objectName),
|
||||||
originalData(handler->loadLegacyData(VLC->modh->settings.data["textData"][objectName].Float()))
|
originalData(handler->loadLegacyData(VLC->modh->settings.data["textData"][objectName].Float()))
|
||||||
@@ -327,7 +327,7 @@ CContentHandler::ContentTypeHandler::ContentTypeHandler(IHandlerBase * handler,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CContentHandler::ContentTypeHandler::preloadModData(std::string modName, std::vector<std::string> fileList, bool validate)
|
bool ContentTypeHandler::preloadModData(std::string modName, std::vector<std::string> fileList, bool validate)
|
||||||
{
|
{
|
||||||
bool result;
|
bool result;
|
||||||
JsonNode data = JsonUtils::assembleFromFiles(fileList, result);
|
JsonNode data = JsonUtils::assembleFromFiles(fileList, result);
|
||||||
@@ -362,7 +362,7 @@ bool CContentHandler::ContentTypeHandler::preloadModData(std::string modName, st
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool validate)
|
bool ContentTypeHandler::loadMod(std::string modName, bool validate)
|
||||||
{
|
{
|
||||||
ModInfo & modInfo = modData[modName];
|
ModInfo & modInfo = modData[modName];
|
||||||
bool result = true;
|
bool result = true;
|
||||||
@@ -387,42 +387,47 @@ bool CContentHandler::ContentTypeHandler::loadMod(std::string modName, bool vali
|
|||||||
// try to add H3 object data
|
// try to add H3 object data
|
||||||
size_t index = data["index"].Float();
|
size_t index = data["index"].Float();
|
||||||
|
|
||||||
if (originalData.size() > index)
|
if(originalData.size() > index)
|
||||||
{
|
{
|
||||||
logMod->trace("found original data in loadMod(%s) at index %d", name, index);
|
logMod->trace("found original data in loadMod(%s) at index %d", name, index);
|
||||||
JsonUtils::merge(originalData[index], data);
|
JsonUtils::merge(originalData[index], data);
|
||||||
performValidate(originalData[index],name);
|
std::swap(originalData[index], data);
|
||||||
handler->loadObject(modName, name, originalData[index], index);
|
|
||||||
originalData[index].clear(); // do not use same data twice (same ID)
|
originalData[index].clear(); // do not use same data twice (same ID)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
logMod->debug("no original data in loadMod(%s) at index %d", name, index);
|
logMod->warn("no original data in loadMod(%s) at index %d", name, index);
|
||||||
performValidate(data, name);
|
|
||||||
handler->loadObject(modName, name, data, index);
|
|
||||||
}
|
}
|
||||||
continue;
|
performValidate(data, name);
|
||||||
|
handler->loadObject(modName, name, data, index);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// normal new object
|
||||||
|
logMod->trace("no index in loadMod(%s)", name);
|
||||||
|
performValidate(data,name);
|
||||||
|
handler->loadObject(modName, name, data);
|
||||||
}
|
}
|
||||||
// normal new object
|
|
||||||
logMod->trace("no index in loadMod(%s)", name);
|
|
||||||
performValidate(data,name);
|
|
||||||
handler->loadObject(modName, name, data);
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void CContentHandler::ContentTypeHandler::loadCustom()
|
void ContentTypeHandler::loadCustom()
|
||||||
{
|
{
|
||||||
handler->loadCustom();
|
handler->loadCustom();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CContentHandler::ContentTypeHandler::afterLoadFinalization()
|
void ContentTypeHandler::afterLoadFinalization()
|
||||||
{
|
{
|
||||||
handler->afterLoadFinalization();
|
handler->afterLoadFinalization();
|
||||||
}
|
}
|
||||||
|
|
||||||
CContentHandler::CContentHandler()
|
CContentHandler::CContentHandler()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void CContentHandler::init()
|
||||||
{
|
{
|
||||||
handlers.insert(std::make_pair("heroClasses", ContentTypeHandler(&VLC->heroh->classes, "heroClass")));
|
handlers.insert(std::make_pair("heroClasses", ContentTypeHandler(&VLC->heroh->classes, "heroClass")));
|
||||||
handlers.insert(std::make_pair("artifacts", ContentTypeHandler(VLC->arth, "artifact")));
|
handlers.insert(std::make_pair("artifacts", ContentTypeHandler(VLC->arth, "artifact")));
|
||||||
@@ -507,6 +512,11 @@ void CContentHandler::load(CModInfo & mod)
|
|||||||
logMod->info("\t\t[SKIP] %s", mod.name);
|
logMod->info("\t\t[SKIP] %s", mod.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ContentTypeHandler & CContentHandler::operator[](const std::string & name) const
|
||||||
|
{
|
||||||
|
return handlers.at(name);
|
||||||
|
}
|
||||||
|
|
||||||
static JsonNode loadModSettings(std::string path)
|
static JsonNode loadModSettings(std::string path)
|
||||||
{
|
{
|
||||||
if (CResourceHandler::get("local")->existsResource(ResourceID(path)))
|
if (CResourceHandler::get("local")->existsResource(ResourceID(path)))
|
||||||
@@ -810,31 +820,42 @@ std::vector<std::string> CModHandler::getModList(std::string path)
|
|||||||
|
|
||||||
void CModHandler::loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods)
|
void CModHandler::loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods)
|
||||||
{
|
{
|
||||||
for (std::string modName : getModList(path))
|
for(std::string modName : getModList(path))
|
||||||
|
loadOneMod(modName, parent, modSettings, enableMods);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CModHandler::loadOneMod(std::string modName, std::string parent, const JsonNode & modSettings, bool enableMods)
|
||||||
|
{
|
||||||
|
boost::to_lower(modName);
|
||||||
|
std::string modFullName = parent.empty() ? modName : parent + '.' + modName;
|
||||||
|
|
||||||
|
if(CResourceHandler::get("initial")->existsResource(ResourceID(CModInfo::getModFile(modFullName))))
|
||||||
{
|
{
|
||||||
boost::to_lower(modName);
|
CModInfo mod(modFullName, modSettings[modName], JsonNode(ResourceID(CModInfo::getModFile(modFullName))));
|
||||||
std::string modFullName = parent.empty() ? modName : parent + '.' + modName;
|
if (!parent.empty()) // this is submod, add parent to dependencies
|
||||||
|
mod.dependencies.insert(parent);
|
||||||
|
|
||||||
if (CResourceHandler::get("initial")->existsResource(ResourceID(CModInfo::getModFile(modFullName))))
|
allMods[modFullName] = mod;
|
||||||
{
|
if (mod.enabled && enableMods)
|
||||||
CModInfo mod(modFullName, modSettings[modName], JsonNode(ResourceID(CModInfo::getModFile(modFullName))));
|
activeMods.push_back(modFullName);
|
||||||
if (!parent.empty()) // this is submod, add parent to dependecies
|
|
||||||
mod.dependencies.insert(parent);
|
|
||||||
|
|
||||||
allMods[modFullName] = mod;
|
loadMods(CModInfo::getModDir(modFullName) + '/', modFullName, modSettings[modName]["mods"], enableMods && mod.enabled);
|
||||||
if (mod.enabled && enableMods)
|
|
||||||
activeMods.push_back(modFullName);
|
|
||||||
|
|
||||||
loadMods(CModInfo::getModDir(modFullName) + '/', modFullName, modSettings[modName]["mods"], enableMods && mod.enabled);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CModHandler::loadMods()
|
void CModHandler::loadMods(bool onlyEssential)
|
||||||
{
|
{
|
||||||
const JsonNode modConfig = loadModSettings("config/modSettings.json");
|
JsonNode modConfig;
|
||||||
|
|
||||||
loadMods("", "", modConfig["activeMods"], true);
|
if(onlyEssential)
|
||||||
|
{
|
||||||
|
loadOneMod("vcmi", "", modConfig, true);//only vcmi and submods
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
modConfig = loadModSettings("config/modSettings.json");
|
||||||
|
loadMods("", "", modConfig["activeMods"], true);
|
||||||
|
}
|
||||||
|
|
||||||
coreMod = CModInfo("core", modConfig["core"], JsonNode(ResourceID("config/gameConfig.json")));
|
coreMod = CModInfo("core", modConfig["core"], JsonNode(ResourceID("config/gameConfig.json")));
|
||||||
coreMod.name = "Original game files";
|
coreMod.name = "Original game files";
|
||||||
@@ -941,9 +962,10 @@ void CModHandler::load()
|
|||||||
{
|
{
|
||||||
CStopWatch totalTime, timer;
|
CStopWatch totalTime, timer;
|
||||||
|
|
||||||
CContentHandler content;
|
|
||||||
logMod->info("\tInitializing content handler: %d ms", timer.getDiff());
|
logMod->info("\tInitializing content handler: %d ms", timer.getDiff());
|
||||||
|
|
||||||
|
content.init();
|
||||||
|
|
||||||
for(const TModID & modName : activeMods)
|
for(const TModID & modName : activeMods)
|
||||||
{
|
{
|
||||||
logMod->trace("Generating checksum for %s", modName);
|
logMod->trace("Generating checksum for %s", modName);
|
||||||
@@ -976,7 +998,7 @@ void CModHandler::load()
|
|||||||
logMod->info("\tAll game content loaded in %d ms", totalTime.getDiff());
|
logMod->info("\tAll game content loaded in %d ms", totalTime.getDiff());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CModHandler::afterLoad()
|
void CModHandler::afterLoad(bool onlyEssential)
|
||||||
{
|
{
|
||||||
JsonNode modSettings;
|
JsonNode modSettings;
|
||||||
for (auto & modEntry : allMods)
|
for (auto & modEntry : allMods)
|
||||||
@@ -987,8 +1009,12 @@ void CModHandler::afterLoad()
|
|||||||
}
|
}
|
||||||
modSettings["core"] = coreMod.saveLocalData();
|
modSettings["core"] = coreMod.saveLocalData();
|
||||||
|
|
||||||
FileStream file(*CResourceHandler::get()->getResourceName(ResourceID("config/modSettings.json")), std::ofstream::out | std::ofstream::trunc);
|
if(!onlyEssential)
|
||||||
file << modSettings.toJson();
|
{
|
||||||
|
FileStream file(*CResourceHandler::get()->getResourceName(ResourceID("config/modSettings.json")), std::ofstream::out | std::ofstream::trunc);
|
||||||
|
file << modSettings.toJson();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CModHandler::normalizeIdentifier(const std::string & scope, const std::string & remoteScope, const std::string & identifier)
|
std::string CModHandler::normalizeIdentifier(const std::string & scope, const std::string & remoteScope, const std::string & identifier)
|
||||||
@@ -1026,10 +1052,27 @@ void CModHandler::parseIdentifier(const std::string & fullIdentifier, std::strin
|
|||||||
|
|
||||||
std::string CModHandler::makeFullIdentifier(const std::string & scope, const std::string & type, const std::string & identifier)
|
std::string CModHandler::makeFullIdentifier(const std::string & scope, const std::string & type, const std::string & identifier)
|
||||||
{
|
{
|
||||||
auto p = splitString(identifier, ':');
|
if(type == "")
|
||||||
|
logGlobal->error("Full identifier (%s %s) requires type name", scope, identifier);
|
||||||
|
|
||||||
if(p.first != "")
|
std::string actualScope = scope;
|
||||||
return p.first + ":" + type + "." + p.second;//ignore type if identifier is scoped
|
std::string actualName = identifier;
|
||||||
|
|
||||||
|
//ignore scope if identifier is scoped
|
||||||
|
auto scopeAndName = splitString(identifier, ':');
|
||||||
|
|
||||||
|
if(scopeAndName.first != "")
|
||||||
|
{
|
||||||
|
actualScope = scopeAndName.first;
|
||||||
|
actualName = scopeAndName.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(actualScope == "")
|
||||||
|
{
|
||||||
|
return actualName == "" ? type : type + "." + actualName;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return scope == "" ? (identifier == "" ? type : type + "." + identifier) : scope + ":" + type + "." + identifier;
|
{
|
||||||
|
return actualName == "" ? actualScope+ ":" + type : actualScope + ":" + type + "." + actualName;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-34
@@ -108,40 +108,38 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// class used to load all game data into handlers. Used only during loading
|
/// internal type to handle loading of one data type (e.g. artifacts, creatures)
|
||||||
class CContentHandler
|
class DLL_LINKAGE ContentTypeHandler
|
||||||
{
|
{
|
||||||
/// internal type to handle loading of one data type (e.g. artifacts, creatures)
|
public:
|
||||||
class ContentTypeHandler
|
struct ModInfo
|
||||||
{
|
{
|
||||||
struct ModInfo
|
/// mod data from this mod and for this mod
|
||||||
{
|
JsonNode modData;
|
||||||
/// mod data from this mod and for this mod
|
/// mod data for this mod from other mods (patches)
|
||||||
JsonNode modData;
|
JsonNode patches;
|
||||||
/// mod data for this mod from other mods (patches)
|
|
||||||
JsonNode patches;
|
|
||||||
};
|
|
||||||
|
|
||||||
/// handler to which all data will be loaded
|
|
||||||
IHandlerBase * handler;
|
|
||||||
|
|
||||||
std::string objectName;
|
|
||||||
|
|
||||||
/// contains all loaded H3 data
|
|
||||||
std::vector<JsonNode> originalData;
|
|
||||||
std::map<std::string, ModInfo> modData;
|
|
||||||
|
|
||||||
public:
|
|
||||||
ContentTypeHandler(IHandlerBase * handler, std::string objectName);
|
|
||||||
|
|
||||||
/// local version of methods in ContentHandler
|
|
||||||
/// returns true if loading was successful
|
|
||||||
bool preloadModData(std::string modName, std::vector<std::string> fileList, bool validate);
|
|
||||||
bool loadMod(std::string modName, bool validate);
|
|
||||||
void loadCustom();
|
|
||||||
void afterLoadFinalization();
|
|
||||||
};
|
};
|
||||||
|
/// handler to which all data will be loaded
|
||||||
|
IHandlerBase * handler;
|
||||||
|
std::string objectName;
|
||||||
|
|
||||||
|
/// contains all loaded H3 data
|
||||||
|
std::vector<JsonNode> originalData;
|
||||||
|
std::map<std::string, ModInfo> modData;
|
||||||
|
|
||||||
|
ContentTypeHandler(IHandlerBase * handler, std::string objectName);
|
||||||
|
|
||||||
|
/// local version of methods in ContentHandler
|
||||||
|
/// returns true if loading was successful
|
||||||
|
bool preloadModData(std::string modName, std::vector<std::string> fileList, bool validate);
|
||||||
|
bool loadMod(std::string modName, bool validate);
|
||||||
|
void loadCustom();
|
||||||
|
void afterLoadFinalization();
|
||||||
|
};
|
||||||
|
|
||||||
|
/// class used to load all game data into handlers. Used only during loading
|
||||||
|
class DLL_LINKAGE CContentHandler
|
||||||
|
{
|
||||||
/// preloads all data from fileList as data from modName.
|
/// preloads all data from fileList as data from modName.
|
||||||
bool preloadModData(std::string modName, JsonNode modConfig, bool validate);
|
bool preloadModData(std::string modName, JsonNode modConfig, bool validate);
|
||||||
|
|
||||||
@@ -150,9 +148,10 @@ class CContentHandler
|
|||||||
|
|
||||||
std::map<std::string, ContentTypeHandler> handlers;
|
std::map<std::string, ContentTypeHandler> handlers;
|
||||||
public:
|
public:
|
||||||
/// fully initialize object. Will cause reading of H3 config files
|
|
||||||
CContentHandler();
|
CContentHandler();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
/// preloads all data from fileList as data from modName.
|
/// preloads all data from fileList as data from modName.
|
||||||
void preloadData(CModInfo & mod);
|
void preloadData(CModInfo & mod);
|
||||||
|
|
||||||
@@ -163,6 +162,8 @@ public:
|
|||||||
|
|
||||||
/// all data was loaded, time for final validation / integration
|
/// all data was loaded, time for final validation / integration
|
||||||
void afterLoadFinalization();
|
void afterLoadFinalization();
|
||||||
|
|
||||||
|
const ContentTypeHandler & operator[] (const std::string & name) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef std::string TModID;
|
typedef std::string TModID;
|
||||||
@@ -246,14 +247,17 @@ class DLL_LINKAGE CModHandler
|
|||||||
std::vector <TModID> resolveDependencies(std::vector<TModID> input) const;
|
std::vector <TModID> resolveDependencies(std::vector<TModID> input) const;
|
||||||
|
|
||||||
std::vector<std::string> getModList(std::string path);
|
std::vector<std::string> getModList(std::string path);
|
||||||
void loadMods(std::string path, std::string namePrefix, const JsonNode & modSettings, bool enableMods);
|
void loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods);
|
||||||
|
void loadOneMod(std::string modName, std::string parent, const JsonNode & modSettings, bool enableMods);
|
||||||
public:
|
public:
|
||||||
|
|
||||||
CIdentifierStorage identifiers;
|
CIdentifierStorage identifiers;
|
||||||
|
|
||||||
|
CContentHandler content; //(!)Do not serialize
|
||||||
|
|
||||||
/// receives list of available mods and trying to load mod.json from all of them
|
/// receives list of available mods and trying to load mod.json from all of them
|
||||||
void initializeConfig();
|
void initializeConfig();
|
||||||
void loadMods();
|
void loadMods(bool onlyEssential = false);
|
||||||
void loadModFilesystems();
|
void loadModFilesystems();
|
||||||
|
|
||||||
CModInfo & getModData(TModID modId);
|
CModInfo & getModData(TModID modId);
|
||||||
@@ -264,7 +268,7 @@ public:
|
|||||||
|
|
||||||
/// load content from all available mods
|
/// load content from all available mods
|
||||||
void load();
|
void load();
|
||||||
void afterLoad();
|
void afterLoad(bool onlyEssential);
|
||||||
|
|
||||||
struct DLL_LINKAGE hardcodedFeatures
|
struct DLL_LINKAGE hardcodedFeatures
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,7 +32,12 @@ void CRandomGenerator::resetSeed()
|
|||||||
|
|
||||||
TRandI CRandomGenerator::getIntRange(int lower, int upper)
|
TRandI CRandomGenerator::getIntRange(int lower, int upper)
|
||||||
{
|
{
|
||||||
return std::bind(TIntDist(lower, upper), std::ref(rand));
|
return std::bind(TIntDist(lower, upper), std::ref(rand));
|
||||||
|
}
|
||||||
|
|
||||||
|
vstd::TRandI64 CRandomGenerator::getInt64Range(int64_t lower, int64_t upper)
|
||||||
|
{
|
||||||
|
return std::bind(TInt64Dist(lower, upper), std::ref(rand));
|
||||||
}
|
}
|
||||||
|
|
||||||
int CRandomGenerator::nextInt(int upper)
|
int CRandomGenerator::nextInt(int upper)
|
||||||
@@ -50,7 +55,7 @@ int CRandomGenerator::nextInt()
|
|||||||
return TIntDist()(rand);
|
return TIntDist()(rand);
|
||||||
}
|
}
|
||||||
|
|
||||||
TRand CRandomGenerator::getDoubleRange(double lower, double upper)
|
vstd::TRand CRandomGenerator::getDoubleRange(double lower, double upper)
|
||||||
{
|
{
|
||||||
return std::bind(TRealDist(lower, upper), std::ref(rand));
|
return std::bind(TRealDist(lower, upper), std::ref(rand));
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-32
@@ -10,16 +10,18 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vstd/RNG.h>
|
||||||
|
|
||||||
typedef std::mt19937 TGenerator;
|
typedef std::mt19937 TGenerator;
|
||||||
typedef std::uniform_int_distribution<int> TIntDist;
|
typedef std::uniform_int_distribution<int> TIntDist;
|
||||||
|
typedef std::uniform_int_distribution<int64_t> TInt64Dist;
|
||||||
typedef std::uniform_real_distribution<double> TRealDist;
|
typedef std::uniform_real_distribution<double> TRealDist;
|
||||||
typedef std::function<int()> TRandI;
|
typedef std::function<int()> TRandI;
|
||||||
typedef std::function<double()> TRand;
|
|
||||||
|
|
||||||
/// The random generator randomly generates integers and real numbers("doubles") between
|
/// The random generator randomly generates integers and real numbers("doubles") between
|
||||||
/// a given range. This is a header only class and mainly a wrapper for
|
/// a given range. This is a header only class and mainly a wrapper for
|
||||||
/// convenient usage of the standard random API. An instance of this RNG is not thread safe.
|
/// convenient usage of the standard random API. An instance of this RNG is not thread safe.
|
||||||
class DLL_LINKAGE CRandomGenerator : boost::noncopyable
|
class DLL_LINKAGE CRandomGenerator : public vstd::RNG, boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
/// Seeds the generator by default with the product of the current time in milliseconds and the
|
/// Seeds the generator by default with the product of the current time in milliseconds and the
|
||||||
@@ -36,22 +38,24 @@ public:
|
|||||||
/// e.g.: auto a = gen.getIntRange(0,10); a(); a(); a();
|
/// e.g.: auto a = gen.getIntRange(0,10); a(); a(); a();
|
||||||
/// requires: lower <= upper
|
/// requires: lower <= upper
|
||||||
TRandI getIntRange(int lower, int upper);
|
TRandI getIntRange(int lower, int upper);
|
||||||
|
|
||||||
|
vstd::TRandI64 getInt64Range(int64_t lower, int64_t upper) override;
|
||||||
|
|
||||||
/// Generates an integer between 0 and upper.
|
/// Generates an integer between 0 and upper.
|
||||||
/// requires: 0 <= upper
|
/// requires: 0 <= upper
|
||||||
int nextInt(int upper);
|
int nextInt(int upper);
|
||||||
|
|
||||||
/// requires: lower <= upper
|
/// requires: lower <= upper
|
||||||
int nextInt(int lower, int upper);
|
int nextInt(int lower, int upper);
|
||||||
|
|
||||||
/// Generates an integer between 0 and the maximum value it can hold.
|
/// Generates an integer between 0 and the maximum value it can hold.
|
||||||
int nextInt();
|
int nextInt();
|
||||||
|
|
||||||
/// Generate several double/real numbers within the same range.
|
/// Generate several double/real numbers within the same range.
|
||||||
/// e.g.: auto a = gen.getDoubleRange(4.5,10.2); a(); a(); a();
|
/// e.g.: auto a = gen.getDoubleRange(4.5,10.2); a(); a(); a();
|
||||||
/// requires: lower <= upper
|
/// requires: lower <= upper
|
||||||
TRand getDoubleRange(double lower, double upper);
|
vstd::TRand getDoubleRange(double lower, double upper) override;
|
||||||
|
|
||||||
/// Generates a double between 0 and upper.
|
/// Generates a double between 0 and upper.
|
||||||
/// requires: 0 <= upper
|
/// requires: 0 <= upper
|
||||||
double nextDouble(double upper);
|
double nextDouble(double upper);
|
||||||
@@ -94,29 +98,3 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace RandomGeneratorUtil
|
|
||||||
{
|
|
||||||
template<typename Container>
|
|
||||||
auto nextItem(const Container & container, CRandomGenerator & rand) -> decltype(std::begin(container))
|
|
||||||
{
|
|
||||||
assert(!container.empty());
|
|
||||||
return std::next(container.begin(), rand.nextInt(container.size() - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Container>
|
|
||||||
auto nextItem(Container & container, CRandomGenerator & rand) -> decltype(std::begin(container))
|
|
||||||
{
|
|
||||||
assert(!container.empty());
|
|
||||||
return std::next(container.begin(), rand.nextInt(container.size() - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void randomShuffle(std::vector<T>& container, CRandomGenerator & rand)
|
|
||||||
{
|
|
||||||
int n = (container.end() - container.begin());
|
|
||||||
for (int i = n-1; i>0; --i)
|
|
||||||
{
|
|
||||||
std::swap (container.begin()[i],container.begin()[rand.nextInt(i)]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -22,10 +22,6 @@
|
|||||||
#include "CModHandler.h"
|
#include "CModHandler.h"
|
||||||
#include "StringConstants.h"
|
#include "StringConstants.h"
|
||||||
|
|
||||||
#include "CStack.h"
|
|
||||||
#include "battle/BattleInfo.h"
|
|
||||||
#include "battle/CBattleInfoCallback.h"
|
|
||||||
|
|
||||||
///CSkill
|
///CSkill
|
||||||
CSkill::LevelInfo::LevelInfo()
|
CSkill::LevelInfo::LevelInfo()
|
||||||
{
|
{
|
||||||
@@ -158,7 +154,7 @@ const std::string & CSkillHandler::skillName(int skill) const
|
|||||||
return objects[skill]->name;
|
return objects[skill]->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
CSkill * CSkillHandler::loadFromJson(const JsonNode & json, const std::string & identifier)
|
CSkill * CSkillHandler::loadFromJson(const JsonNode & json, const std::string & identifier, size_t index)
|
||||||
{
|
{
|
||||||
CSkill * skill = nullptr;
|
CSkill * skill = nullptr;
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -83,5 +83,5 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CSkill * loadFromJson(const JsonNode & json, const std::string & identifier) override;
|
CSkill * loadFromJson(const JsonNode & json, const std::string & identifier, size_t index) override;
|
||||||
};
|
};
|
||||||
|
|||||||
+144
-680
@@ -9,326 +9,34 @@
|
|||||||
*/
|
*/
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "CStack.h"
|
#include "CStack.h"
|
||||||
|
|
||||||
|
#include <vstd/RNG.h>
|
||||||
|
|
||||||
#include "CGeneralTextHandler.h"
|
#include "CGeneralTextHandler.h"
|
||||||
#include "battle/BattleInfo.h"
|
#include "battle/BattleInfo.h"
|
||||||
#include "spells/CSpellHandler.h"
|
#include "spells/CSpellHandler.h"
|
||||||
#include "CRandomGenerator.h"
|
|
||||||
#include "NetPacks.h"
|
#include "NetPacks.h"
|
||||||
|
|
||||||
|
|
||||||
///CAmmo
|
|
||||||
CAmmo::CAmmo(const CStack * Owner, CSelector totalSelector):
|
|
||||||
CStackResource(Owner), totalProxy(Owner, totalSelector)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t CAmmo::available() const
|
|
||||||
{
|
|
||||||
return total() - used;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CAmmo::canUse(int32_t amount) const
|
|
||||||
{
|
|
||||||
return available() - amount >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAmmo::reset()
|
|
||||||
{
|
|
||||||
used = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t CAmmo::total() const
|
|
||||||
{
|
|
||||||
return totalProxy->totalValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CAmmo::use(int32_t amount)
|
|
||||||
{
|
|
||||||
if(available() - amount < 0)
|
|
||||||
{
|
|
||||||
logGlobal->error("Stack ammo overuse");
|
|
||||||
used += available();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
used += amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
///CShots
|
|
||||||
CShots::CShots(const CStack * Owner):
|
|
||||||
CAmmo(Owner, Selector::type(Bonus::SHOTS))
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void CShots::use(int32_t amount)
|
|
||||||
{
|
|
||||||
//don't remove ammo if we control a working ammo cart
|
|
||||||
bool hasAmmoCart = false;
|
|
||||||
|
|
||||||
for(const CStack * st : owner->battle->stacks)
|
|
||||||
{
|
|
||||||
if(owner->battle->battleMatchOwner(st, owner, true) && st->getCreature()->idNumber == CreatureID::AMMO_CART && st->alive())
|
|
||||||
{
|
|
||||||
hasAmmoCart = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!hasAmmoCart)
|
|
||||||
CAmmo::use(amount);
|
|
||||||
}
|
|
||||||
|
|
||||||
///CCasts
|
|
||||||
CCasts::CCasts(const CStack * Owner):
|
|
||||||
CAmmo(Owner, Selector::type(Bonus::CASTS))
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
///CRetaliations
|
|
||||||
CRetaliations::CRetaliations(const CStack * Owner):
|
|
||||||
CAmmo(Owner, Selector::type(Bonus::ADDITIONAL_RETALIATION)), totalCache(0)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t CRetaliations::total() const
|
|
||||||
{
|
|
||||||
//after dispell bonus should remain during current round
|
|
||||||
int32_t val = 1 + totalProxy->totalValue();
|
|
||||||
vstd::amax(totalCache, val);
|
|
||||||
return totalCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CRetaliations::reset()
|
|
||||||
{
|
|
||||||
CAmmo::reset();
|
|
||||||
totalCache = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
///CHealth
|
|
||||||
CHealth::CHealth(const IUnitHealthInfo * Owner):
|
|
||||||
owner(Owner)
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
CHealth::CHealth(const CHealth & other):
|
|
||||||
owner(other.owner),
|
|
||||||
firstHPleft(other.firstHPleft),
|
|
||||||
fullUnits(other.fullUnits),
|
|
||||||
resurrected(other.resurrected)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHealth::init()
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
fullUnits = owner->unitBaseAmount() > 1 ? owner->unitBaseAmount() - 1 : 0;
|
|
||||||
firstHPleft = owner->unitBaseAmount() > 0 ? owner->unitMaxHealth() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHealth::addResurrected(int32_t amount)
|
|
||||||
{
|
|
||||||
resurrected += amount;
|
|
||||||
vstd::amax(resurrected, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t CHealth::available() const
|
|
||||||
{
|
|
||||||
return static_cast<int64_t>(firstHPleft) + owner->unitMaxHealth() * fullUnits;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t CHealth::total() const
|
|
||||||
{
|
|
||||||
return static_cast<int64_t>(owner->unitMaxHealth()) * owner->unitBaseAmount();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHealth::damage(int32_t & amount)
|
|
||||||
{
|
|
||||||
const int32_t oldCount = getCount();
|
|
||||||
|
|
||||||
const bool withKills = amount >= firstHPleft;
|
|
||||||
|
|
||||||
if(withKills)
|
|
||||||
{
|
|
||||||
int64_t totalHealth = available();
|
|
||||||
if(amount > totalHealth)
|
|
||||||
amount = totalHealth;
|
|
||||||
totalHealth -= amount;
|
|
||||||
if(totalHealth <= 0)
|
|
||||||
{
|
|
||||||
fullUnits = 0;
|
|
||||||
firstHPleft = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
setFromTotal(totalHealth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
firstHPleft -= amount;
|
|
||||||
}
|
|
||||||
|
|
||||||
addResurrected(getCount() - oldCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHealth::heal(int32_t & amount, EHealLevel level, EHealPower power)
|
|
||||||
{
|
|
||||||
const int32_t unitHealth = owner->unitMaxHealth();
|
|
||||||
const int32_t oldCount = getCount();
|
|
||||||
|
|
||||||
int32_t maxHeal = std::numeric_limits<int32_t>::max();
|
|
||||||
|
|
||||||
switch(level)
|
|
||||||
{
|
|
||||||
case EHealLevel::HEAL:
|
|
||||||
maxHeal = std::max(0, unitHealth - firstHPleft);
|
|
||||||
break;
|
|
||||||
case EHealLevel::RESURRECT:
|
|
||||||
maxHeal = total() - available();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
assert(level == EHealLevel::OVERHEAL);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
vstd::amax(maxHeal, 0);
|
|
||||||
vstd::abetween(amount, 0, maxHeal);
|
|
||||||
|
|
||||||
if(amount == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
int64_t availableHealth = available();
|
|
||||||
|
|
||||||
availableHealth += amount;
|
|
||||||
setFromTotal(availableHealth);
|
|
||||||
|
|
||||||
if(power == EHealPower::ONE_BATTLE)
|
|
||||||
addResurrected(getCount() - oldCount);
|
|
||||||
else
|
|
||||||
assert(power == EHealPower::PERMANENT);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHealth::setFromTotal(const int64_t totalHealth)
|
|
||||||
{
|
|
||||||
const int32_t unitHealth = owner->unitMaxHealth();
|
|
||||||
firstHPleft = totalHealth % unitHealth;
|
|
||||||
fullUnits = totalHealth / unitHealth;
|
|
||||||
|
|
||||||
if(firstHPleft == 0 && fullUnits >= 1)
|
|
||||||
{
|
|
||||||
firstHPleft = unitHealth;
|
|
||||||
fullUnits -= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHealth::reset()
|
|
||||||
{
|
|
||||||
fullUnits = 0;
|
|
||||||
firstHPleft = 0;
|
|
||||||
resurrected = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t CHealth::getCount() const
|
|
||||||
{
|
|
||||||
return fullUnits + (firstHPleft > 0 ? 1 : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t CHealth::getFirstHPleft() const
|
|
||||||
{
|
|
||||||
return firstHPleft;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t CHealth::getResurrected() const
|
|
||||||
{
|
|
||||||
return resurrected;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHealth::fromInfo(const CHealthInfo & info)
|
|
||||||
{
|
|
||||||
firstHPleft = info.firstHPleft;
|
|
||||||
fullUnits = info.fullUnits;
|
|
||||||
resurrected = info.resurrected;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHealth::toInfo(CHealthInfo & info) const
|
|
||||||
{
|
|
||||||
info.firstHPleft = firstHPleft;
|
|
||||||
info.fullUnits = fullUnits;
|
|
||||||
info.resurrected = resurrected;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CHealth::takeResurrected()
|
|
||||||
{
|
|
||||||
if(resurrected != 0)
|
|
||||||
{
|
|
||||||
int64_t totalHealth = available();
|
|
||||||
|
|
||||||
totalHealth -= resurrected * owner->unitMaxHealth();
|
|
||||||
vstd::amax(totalHealth, 0);
|
|
||||||
setFromTotal(totalHealth);
|
|
||||||
resurrected = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
///CStack
|
///CStack
|
||||||
CStack::CStack(const CStackInstance * Base, PlayerColor O, int I, ui8 Side, SlotID S):
|
CStack::CStack(const CStackInstance * Base, PlayerColor O, int I, ui8 Side, SlotID S)
|
||||||
base(Base), ID(I), owner(O), slot(S), side(Side),
|
: CBonusSystemNode(STACK_BATTLE),
|
||||||
counterAttacks(this), shots(this), casts(this), health(this), cloneID(-1),
|
CUnitState(),
|
||||||
position()
|
base(Base),
|
||||||
|
ID(I),
|
||||||
|
type(Base->type),
|
||||||
|
baseAmount(base->count),
|
||||||
|
owner(O),
|
||||||
|
slot(S),
|
||||||
|
side(Side),
|
||||||
|
initialPosition()
|
||||||
{
|
{
|
||||||
assert(base);
|
|
||||||
type = base->type;
|
|
||||||
baseAmount = base->count;
|
|
||||||
health.init(); //???
|
health.init(); //???
|
||||||
setNodeType(STACK_BATTLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CStack::CStack():
|
CStack::CStack()
|
||||||
counterAttacks(this), shots(this), casts(this), health(this)
|
: CBonusSystemNode(STACK_BATTLE),
|
||||||
{
|
CUnitState()
|
||||||
init();
|
|
||||||
setNodeType(STACK_BATTLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
CStack::CStack(const CStackBasicDescriptor * stack, PlayerColor O, int I, ui8 Side, SlotID S):
|
|
||||||
base(nullptr), ID(I), owner(O), slot(S), side(Side),
|
|
||||||
counterAttacks(this), shots(this), casts(this), health(this), cloneID(-1),
|
|
||||||
position()
|
|
||||||
{
|
|
||||||
type = stack->type;
|
|
||||||
baseAmount = stack->count;
|
|
||||||
health.init(); //???
|
|
||||||
setNodeType(STACK_BATTLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t CStack::getKilled() const
|
|
||||||
{
|
|
||||||
int32_t res = baseAmount - health.getCount() + health.getResurrected();
|
|
||||||
vstd::amax(res, 0);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t CStack::getCount() const
|
|
||||||
{
|
|
||||||
return health.getCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t CStack::getFirstHPleft() const
|
|
||||||
{
|
|
||||||
return health.getFirstHPleft();
|
|
||||||
}
|
|
||||||
|
|
||||||
const CCreature * CStack::getCreature() const
|
|
||||||
{
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStack::init()
|
|
||||||
{
|
{
|
||||||
base = nullptr;
|
base = nullptr;
|
||||||
type = nullptr;
|
type = nullptr;
|
||||||
@@ -337,14 +45,32 @@ void CStack::init()
|
|||||||
owner = PlayerColor::NEUTRAL;
|
owner = PlayerColor::NEUTRAL;
|
||||||
slot = SlotID(255);
|
slot = SlotID(255);
|
||||||
side = 1;
|
side = 1;
|
||||||
position = BattleHex();
|
initialPosition = BattleHex();
|
||||||
cloneID = -1;
|
}
|
||||||
|
|
||||||
|
CStack::CStack(const CStackBasicDescriptor * stack, PlayerColor O, int I, ui8 Side, SlotID S)
|
||||||
|
: CBonusSystemNode(STACK_BATTLE),
|
||||||
|
CUnitState(),
|
||||||
|
base(nullptr),
|
||||||
|
ID(I),
|
||||||
|
type(stack->type),
|
||||||
|
baseAmount(stack->count),
|
||||||
|
owner(O),
|
||||||
|
slot(S),
|
||||||
|
side(Side),
|
||||||
|
initialPosition()
|
||||||
|
{
|
||||||
|
health.init(); //???
|
||||||
|
}
|
||||||
|
|
||||||
|
const CCreature * CStack::getCreature() const
|
||||||
|
{
|
||||||
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStack::localInit(BattleInfo * battleInfo)
|
void CStack::localInit(BattleInfo * battleInfo)
|
||||||
{
|
{
|
||||||
battle = battleInfo;
|
battle = battleInfo;
|
||||||
cloneID = -1;
|
|
||||||
assert(type);
|
assert(type);
|
||||||
|
|
||||||
exportBonuses();
|
exportBonuses();
|
||||||
@@ -359,185 +85,35 @@ void CStack::localInit(BattleInfo * battleInfo)
|
|||||||
attachTo(const_cast<CCreature *>(type));
|
attachTo(const_cast<CCreature *>(type));
|
||||||
}
|
}
|
||||||
|
|
||||||
shots.reset();
|
CUnitState::localInit(this);
|
||||||
counterAttacks.reset();
|
position = initialPosition;
|
||||||
casts.reset();
|
|
||||||
health.init();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ui32 CStack::level() const
|
ui32 CStack::level() const
|
||||||
{
|
{
|
||||||
if(base)
|
if(base)
|
||||||
return base->getLevel(); //creatture or commander
|
return base->getLevel(); //creature or commander
|
||||||
else
|
else
|
||||||
return std::max(1, (int)getCreature()->level); //war machine, clone etc
|
return std::max(1, (int)getCreature()->level); //war machine, clone etc
|
||||||
}
|
}
|
||||||
|
|
||||||
si32 CStack::magicResistance() const
|
si32 CStack::magicResistance() const
|
||||||
{
|
{
|
||||||
si32 magicResistance;
|
si32 magicResistance = IBonusBearer::magicResistance();
|
||||||
if(base) //TODO: make war machines receive aura of magic resistance
|
|
||||||
|
si32 auraBonus = 0;
|
||||||
|
|
||||||
|
for(auto one : battle->battleAdjacentUnits(this))
|
||||||
{
|
{
|
||||||
magicResistance = base->magicResistance();
|
if(one->unitOwner() == owner)
|
||||||
int auraBonus = 0;
|
vstd::amax(auraBonus, one->valOfBonuses(Bonus::SPELL_RESISTANCE_AURA)); //max value
|
||||||
for(const CStack * stack : base->armyObj->battle-> batteAdjacentCreatures(this))
|
|
||||||
{
|
|
||||||
if(stack->owner == owner)
|
|
||||||
{
|
|
||||||
vstd::amax(auraBonus, stack->valOfBonuses(Bonus::SPELL_RESISTANCE_AURA)); //max value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
magicResistance += auraBonus;
|
|
||||||
vstd::amin(magicResistance, 100);
|
|
||||||
}
|
}
|
||||||
else
|
magicResistance += auraBonus;
|
||||||
magicResistance = type->magicResistance();
|
vstd::amin(magicResistance, 100);
|
||||||
|
|
||||||
return magicResistance;
|
return magicResistance;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CStack::willMove(int turn) const
|
|
||||||
{
|
|
||||||
return (turn ? true : !vstd::contains(state, EBattleStackState::DEFENDING))
|
|
||||||
&& !moved(turn)
|
|
||||||
&& canMove(turn);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::canMove(int turn) const
|
|
||||||
{
|
|
||||||
return alive()
|
|
||||||
&& !hasBonus(Selector::type(Bonus::NOT_ACTIVE).And(Selector::turns(turn))); //eg. Ammo Cart or blinded creature
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::canCast() const
|
|
||||||
{
|
|
||||||
return casts.canUse(1);//do not check specific cast abilities here
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::isCaster() const
|
|
||||||
{
|
|
||||||
return casts.total() > 0;//do not check specific cast abilities here
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::canShoot() const
|
|
||||||
{
|
|
||||||
return shots.canUse(1) && hasBonusOfType(Bonus::SHOOTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::isShooter() const
|
|
||||||
{
|
|
||||||
return shots.total() > 0 && hasBonusOfType(Bonus::SHOOTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::moved(int turn) const
|
|
||||||
{
|
|
||||||
if(!turn)
|
|
||||||
return vstd::contains(state, EBattleStackState::MOVED);
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::waited(int turn) const
|
|
||||||
{
|
|
||||||
if(!turn)
|
|
||||||
return vstd::contains(state, EBattleStackState::WAITING);
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::doubleWide() const
|
|
||||||
{
|
|
||||||
return getCreature()->doubleWide;
|
|
||||||
}
|
|
||||||
|
|
||||||
BattleHex CStack::occupiedHex() const
|
|
||||||
{
|
|
||||||
return occupiedHex(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
BattleHex CStack::occupiedHex(BattleHex assumedPos) const
|
|
||||||
{
|
|
||||||
if(doubleWide())
|
|
||||||
{
|
|
||||||
if(side == BattleSide::ATTACKER)
|
|
||||||
return assumedPos - 1;
|
|
||||||
else
|
|
||||||
return assumedPos + 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return BattleHex::INVALID;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<BattleHex> CStack::getHexes() const
|
|
||||||
{
|
|
||||||
return getHexes(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<BattleHex> CStack::getHexes(BattleHex assumedPos) const
|
|
||||||
{
|
|
||||||
return getHexes(assumedPos, doubleWide(), side);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<BattleHex> CStack::getHexes(BattleHex assumedPos, bool twoHex, ui8 side)
|
|
||||||
{
|
|
||||||
std::vector<BattleHex> hexes;
|
|
||||||
hexes.push_back(assumedPos);
|
|
||||||
|
|
||||||
if(twoHex)
|
|
||||||
{
|
|
||||||
if(side == BattleSide::ATTACKER)
|
|
||||||
hexes.push_back(assumedPos - 1);
|
|
||||||
else
|
|
||||||
hexes.push_back(assumedPos + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hexes;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::coversPos(BattleHex pos) const
|
|
||||||
{
|
|
||||||
return vstd::contains(getHexes(), pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<BattleHex> CStack::getSurroundingHexes(BattleHex attackerPos) const
|
|
||||||
{
|
|
||||||
BattleHex hex = (attackerPos != BattleHex::INVALID) ? attackerPos : position; //use hypothetical position
|
|
||||||
std::vector<BattleHex> hexes;
|
|
||||||
if(doubleWide())
|
|
||||||
{
|
|
||||||
const int WN = GameConstants::BFIELD_WIDTH;
|
|
||||||
if(side == BattleSide::ATTACKER)
|
|
||||||
{
|
|
||||||
//position is equal to front hex
|
|
||||||
BattleHex::checkAndPush(hex - ((hex / WN) % 2 ? WN + 2 : WN + 1), hexes);
|
|
||||||
BattleHex::checkAndPush(hex - ((hex / WN) % 2 ? WN + 1 : WN), hexes);
|
|
||||||
BattleHex::checkAndPush(hex - ((hex / WN) % 2 ? WN : WN - 1), hexes);
|
|
||||||
BattleHex::checkAndPush(hex - 2, hexes);
|
|
||||||
BattleHex::checkAndPush(hex + 1, hexes);
|
|
||||||
BattleHex::checkAndPush(hex + ((hex / WN) % 2 ? WN - 2 : WN - 1), hexes);
|
|
||||||
BattleHex::checkAndPush(hex + ((hex / WN) % 2 ? WN - 1 : WN), hexes);
|
|
||||||
BattleHex::checkAndPush(hex + ((hex / WN) % 2 ? WN : WN + 1), hexes);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
BattleHex::checkAndPush(hex - ((hex / WN) % 2 ? WN + 1 : WN), hexes);
|
|
||||||
BattleHex::checkAndPush(hex - ((hex / WN) % 2 ? WN : WN - 1), hexes);
|
|
||||||
BattleHex::checkAndPush(hex - ((hex / WN) % 2 ? WN - 1 : WN - 2), hexes);
|
|
||||||
BattleHex::checkAndPush(hex + 2, hexes);
|
|
||||||
BattleHex::checkAndPush(hex - 1, hexes);
|
|
||||||
BattleHex::checkAndPush(hex + ((hex / WN) % 2 ? WN - 1 : WN), hexes);
|
|
||||||
BattleHex::checkAndPush(hex + ((hex / WN) % 2 ? WN : WN + 1), hexes);
|
|
||||||
BattleHex::checkAndPush(hex + ((hex / WN) % 2 ? WN + 1 : WN + 2), hexes);
|
|
||||||
}
|
|
||||||
return hexes;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return hex.neighbouringTiles();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BattleHex::EDir CStack::destShiftDir() const
|
BattleHex::EDir CStack::destShiftDir() const
|
||||||
{
|
{
|
||||||
if(doubleWide())
|
if(doubleWide())
|
||||||
@@ -595,7 +171,8 @@ const CGHeroInstance * CStack::getMyHero() const
|
|||||||
std::string CStack::nodeName() const
|
std::string CStack::nodeName() const
|
||||||
{
|
{
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "Battle stack [" << ID << "]: " << health.getCount() << " creatures of ";
|
oss << owner.getStr();
|
||||||
|
oss << " battle stack [" << ID << "]: " << getCount() << " of ";
|
||||||
if(type)
|
if(type)
|
||||||
oss << type->namePl;
|
oss << type->namePl;
|
||||||
else
|
else
|
||||||
@@ -607,156 +184,95 @@ std::string CStack::nodeName() const
|
|||||||
return oss.str();
|
return oss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
CHealth CStack::healthAfterAttacked(int32_t & damage) const
|
void CStack::prepareAttacked(BattleStackAttacked & bsa, vstd::RNG & rand) const
|
||||||
{
|
{
|
||||||
return healthAfterAttacked(damage, health);
|
auto newState = acquireState();
|
||||||
|
prepareAttacked(bsa, rand, newState);
|
||||||
}
|
}
|
||||||
|
|
||||||
CHealth CStack::healthAfterAttacked(int32_t & damage, const CHealth & customHealth) const
|
void CStack::prepareAttacked(BattleStackAttacked & bsa, vstd::RNG & rand, std::shared_ptr<battle::CUnitState> customState)
|
||||||
{
|
{
|
||||||
CHealth res = customHealth;
|
auto initialCount = customState->getCount();
|
||||||
|
|
||||||
if(isClone())
|
customState->damage(bsa.damageAmount);
|
||||||
{
|
|
||||||
// block ability should not kill clone (0 damage)
|
|
||||||
if(damage > 0)
|
|
||||||
{
|
|
||||||
damage = 1;//??? what should be actual damage against clone?
|
|
||||||
res.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
res.damage(damage);
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
bsa.killedAmount = initialCount - customState->getCount();
|
||||||
}
|
|
||||||
|
|
||||||
CHealth CStack::healthAfterHealed(int32_t & toHeal, EHealLevel level, EHealPower power) const
|
if(!customState->alive() && customState->isClone())
|
||||||
{
|
|
||||||
CHealth res = health;
|
|
||||||
|
|
||||||
if(level == EHealLevel::HEAL && power == EHealPower::ONE_BATTLE)
|
|
||||||
logGlobal->error("Heal for one battle does not make sense", nodeName(), toHeal);
|
|
||||||
else if(isClone())
|
|
||||||
logGlobal->error("Attempt to heal clone: %s for %d HP", nodeName(), toHeal);
|
|
||||||
else
|
|
||||||
res.heal(toHeal, level, power);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStack::prepareAttacked(BattleStackAttacked & bsa, CRandomGenerator & rand) const
|
|
||||||
{
|
|
||||||
prepareAttacked(bsa, rand, health);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStack::prepareAttacked(BattleStackAttacked & bsa, CRandomGenerator & rand, const CHealth & customHealth) const
|
|
||||||
{
|
|
||||||
CHealth afterAttack = healthAfterAttacked(bsa.damageAmount, customHealth);
|
|
||||||
|
|
||||||
bsa.killedAmount = customHealth.getCount() - afterAttack.getCount();
|
|
||||||
afterAttack.toInfo(bsa.newHealth);
|
|
||||||
bsa.newHealth.stackId = ID;
|
|
||||||
bsa.newHealth.delta = -bsa.damageAmount;
|
|
||||||
|
|
||||||
if(afterAttack.available() <= 0 && isClone())
|
|
||||||
{
|
{
|
||||||
bsa.flags |= BattleStackAttacked::CLONE_KILLED;
|
bsa.flags |= BattleStackAttacked::CLONE_KILLED;
|
||||||
return; // no rebirth I believe
|
|
||||||
}
|
}
|
||||||
|
else if(!customState->alive()) //stack killed
|
||||||
if(afterAttack.available() <= 0) //stack killed
|
|
||||||
{
|
{
|
||||||
bsa.flags |= BattleStackAttacked::KILLED;
|
bsa.flags |= BattleStackAttacked::KILLED;
|
||||||
|
|
||||||
int resurrectFactor = valOfBonuses(Bonus::REBIRTH);
|
auto resurrectValue = customState->valOfBonuses(Bonus::REBIRTH);
|
||||||
if(resurrectFactor > 0 && canCast()) //there must be casts left
|
|
||||||
{
|
|
||||||
int resurrectedStackCount = baseAmount * resurrectFactor / 100;
|
|
||||||
|
|
||||||
// last stack has proportional chance to rebirth
|
if(resurrectValue > 0 && customState->canCast()) //there must be casts left
|
||||||
//FIXME: diff is always 0
|
{
|
||||||
auto diff = baseAmount * resurrectFactor / 100.0 - resurrectedStackCount;
|
double resurrectFactor = resurrectValue / 100;
|
||||||
if(diff > rand.nextDouble(0, 0.99))
|
|
||||||
|
auto baseAmount = customState->unitBaseAmount();
|
||||||
|
|
||||||
|
double resurrectedRaw = baseAmount * resurrectFactor;
|
||||||
|
|
||||||
|
int32_t resurrectedCount = static_cast<int32_t>(floor(resurrectedRaw));
|
||||||
|
|
||||||
|
int32_t resurrectedAdd = static_cast<int32_t>(baseAmount - (resurrectedCount/resurrectFactor));
|
||||||
|
|
||||||
|
auto rangeGen = rand.getInt64Range(0, 99);
|
||||||
|
|
||||||
|
for(int32_t i = 0; i < resurrectedAdd; i++)
|
||||||
{
|
{
|
||||||
resurrectedStackCount += 1;
|
if(resurrectValue > rangeGen())
|
||||||
|
resurrectedCount += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(hasBonusOfType(Bonus::REBIRTH, 1))
|
if(customState->hasBonusOfType(Bonus::REBIRTH, 1))
|
||||||
{
|
{
|
||||||
// resurrect at least one Sacred Phoenix
|
// resurrect at least one Sacred Phoenix
|
||||||
vstd::amax(resurrectedStackCount, 1);
|
vstd::amax(resurrectedCount, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(resurrectedStackCount > 0)
|
if(resurrectedCount > 0)
|
||||||
{
|
{
|
||||||
|
customState->casts.use();
|
||||||
bsa.flags |= BattleStackAttacked::REBIRTH;
|
bsa.flags |= BattleStackAttacked::REBIRTH;
|
||||||
//TODO: use StackHealedOrResurrected
|
int64_t toHeal = customState->MaxHealth() * resurrectedCount;
|
||||||
bsa.newHealth.firstHPleft = MaxHealth();
|
//TODO: add one-battle rebirth?
|
||||||
bsa.newHealth.fullUnits = resurrectedStackCount - 1;
|
customState->heal(toHeal, EHealLevel::RESURRECT, EHealPower::PERMANENT);
|
||||||
bsa.newHealth.resurrected = 0; //TODO: add one-battle rebirth?
|
customState->counterAttacks.use(customState->counterAttacks.available());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
customState->save(bsa.newState.data);
|
||||||
|
bsa.newState.healthDelta = -bsa.damageAmount;
|
||||||
|
bsa.newState.id = customState->unitId();
|
||||||
|
bsa.newState.operation = UnitChanges::EOperation::RESET_STATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CStack::isMeleeAttackPossible(const CStack * attacker, const CStack * defender, BattleHex attackerPos, BattleHex defenderPos)
|
bool CStack::isMeleeAttackPossible(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos, BattleHex defenderPos)
|
||||||
{
|
{
|
||||||
if(!attackerPos.isValid())
|
if(!attackerPos.isValid())
|
||||||
attackerPos = attacker->position;
|
attackerPos = attacker->getPosition();
|
||||||
if(!defenderPos.isValid())
|
if(!defenderPos.isValid())
|
||||||
defenderPos = defender->position;
|
defenderPos = defender->getPosition();
|
||||||
|
|
||||||
return
|
return
|
||||||
(BattleHex::mutualPosition(attackerPos, defenderPos) >= 0)//front <=> front
|
(BattleHex::mutualPosition(attackerPos, defenderPos) >= 0)//front <=> front
|
||||||
|| (attacker->doubleWide()//back <=> front
|
|| (attacker->doubleWide()//back <=> front
|
||||||
&& BattleHex::mutualPosition(attackerPos + (attacker->side == BattleSide::ATTACKER ? -1 : 1), defenderPos) >= 0)
|
&& BattleHex::mutualPosition(attackerPos + (attacker->unitSide() == BattleSide::ATTACKER ? -1 : 1), defenderPos) >= 0)
|
||||||
|| (defender->doubleWide()//front <=> back
|
|| (defender->doubleWide()//front <=> back
|
||||||
&& BattleHex::mutualPosition(attackerPos, defenderPos + (defender->side == BattleSide::ATTACKER ? -1 : 1)) >= 0)
|
&& BattleHex::mutualPosition(attackerPos, defenderPos + (defender->unitSide() == BattleSide::ATTACKER ? -1 : 1)) >= 0)
|
||||||
|| (defender->doubleWide() && attacker->doubleWide()//back <=> back
|
|| (defender->doubleWide() && attacker->doubleWide()//back <=> back
|
||||||
&& BattleHex::mutualPosition(attackerPos + (attacker->side == BattleSide::ATTACKER ? -1 : 1), defenderPos + (defender->side == BattleSide::ATTACKER ? -1 : 1)) >= 0);
|
&& BattleHex::mutualPosition(attackerPos + (attacker->unitSide() == BattleSide::ATTACKER ? -1 : 1), defenderPos + (defender->unitSide() == BattleSide::ATTACKER ? -1 : 1)) >= 0);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CStack::ableToRetaliate() const
|
|
||||||
{
|
|
||||||
return alive()
|
|
||||||
&& (counterAttacks.canUse() || hasBonusOfType(Bonus::UNLIMITED_RETALIATIONS))
|
|
||||||
&& !hasBonusOfType(Bonus::SIEGE_WEAPON)
|
|
||||||
&& !hasBonusOfType(Bonus::HYPNOTIZED)
|
|
||||||
&& !hasBonusOfType(Bonus::NO_RETALIATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string CStack::getName() const
|
std::string CStack::getName() const
|
||||||
{
|
{
|
||||||
return (health.getCount() == 1) ? type->nameSing : type->namePl; //War machines can't use base
|
return (getCount() == 1) ? type->nameSing : type->namePl; //War machines can't use base
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::isValidTarget(bool allowDead) const
|
|
||||||
{
|
|
||||||
return (alive() || (allowDead && isDead())) && position.isValid() && !isTurret();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::isDead() const
|
|
||||||
{
|
|
||||||
return !alive() && !isGhost();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::isClone() const
|
|
||||||
{
|
|
||||||
return vstd::contains(state, EBattleStackState::CLONED);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::isGhost() const
|
|
||||||
{
|
|
||||||
return vstd::contains(state, EBattleStackState::GHOST);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::isTurret() const
|
|
||||||
{
|
|
||||||
return type->idNumber == CreatureID::ARROW_TOWERS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CStack::canBeHealed() const
|
bool CStack::canBeHealed() const
|
||||||
@@ -766,75 +282,9 @@ bool CStack::canBeHealed() const
|
|||||||
&& !hasBonusOfType(Bonus::SIEGE_WEAPON);
|
&& !hasBonusOfType(Bonus::SIEGE_WEAPON);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStack::makeGhost()
|
const CCreature * CStack::unitType() const
|
||||||
{
|
{
|
||||||
state.erase(EBattleStackState::ALIVE);
|
return type;
|
||||||
state.insert(EBattleStackState::GHOST_PENDING);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CStack::alive() const //determines if stack is alive
|
|
||||||
{
|
|
||||||
return vstd::contains(state, EBattleStackState::ALIVE);
|
|
||||||
}
|
|
||||||
|
|
||||||
ui8 CStack::getSpellSchoolLevel(const CSpell * spell, int * outSelectedSchool) const
|
|
||||||
{
|
|
||||||
int skill = valOfBonuses(Selector::typeSubtype(Bonus::SPELLCASTER, spell->id));
|
|
||||||
vstd::abetween(skill, 0, 3);
|
|
||||||
return skill;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui32 CStack::getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const
|
|
||||||
{
|
|
||||||
//stacks does not have sorcery-like bonuses (yet?)
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CStack::getEffectLevel(const CSpell * spell) const
|
|
||||||
{
|
|
||||||
return getSpellSchoolLevel(spell);
|
|
||||||
}
|
|
||||||
|
|
||||||
int CStack::getEffectPower(const CSpell * spell) const
|
|
||||||
{
|
|
||||||
return valOfBonuses(Bonus::CREATURE_SPELL_POWER) * health.getCount() / 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CStack::getEnchantPower(const CSpell * spell) const
|
|
||||||
{
|
|
||||||
int res = valOfBonuses(Bonus::CREATURE_ENCHANT_POWER);
|
|
||||||
if(res <= 0)
|
|
||||||
res = 3;//default for creatures
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CStack::getEffectValue(const CSpell * spell) const
|
|
||||||
{
|
|
||||||
return valOfBonuses(Bonus::SPECIFIC_SPELL_POWER, spell->id.toEnum()) * health.getCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
const PlayerColor CStack::getOwner() const
|
|
||||||
{
|
|
||||||
return battle->battleGetOwner(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStack::getCasterName(MetaString & text) const
|
|
||||||
{
|
|
||||||
//always plural name in case of spell cast.
|
|
||||||
addNameReplacement(text, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CStack::getCastDescription(const CSpell * spell, const std::vector<const CStack *> & attacked, MetaString & text) const
|
|
||||||
{
|
|
||||||
text.addTxt(MetaString::GENERAL_TXT, 565);//The %s casts %s
|
|
||||||
//todo: use text 566 for single creature
|
|
||||||
getCasterName(text);
|
|
||||||
text.addReplacement(MetaString::SPELL_NAME, spell->id.toEnum());
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t CStack::unitMaxHealth() const
|
|
||||||
{
|
|
||||||
return MaxHealth();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t CStack::unitBaseAmount() const
|
int32_t CStack::unitBaseAmount() const
|
||||||
@@ -842,46 +292,60 @@ int32_t CStack::unitBaseAmount() const
|
|||||||
return baseAmount;
|
return baseAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStack::addText(MetaString & text, ui8 type, int32_t serial, const boost::logic::tribool & plural) const
|
bool CStack::unitHasAmmoCart(const battle::Unit * unit) const
|
||||||
{
|
{
|
||||||
if(boost::logic::indeterminate(plural))
|
bool hasAmmoCart = false;
|
||||||
serial = VLC->generaltexth->pluralText(serial, health.getCount());
|
|
||||||
else if(plural)
|
|
||||||
serial = VLC->generaltexth->pluralText(serial, 2);
|
|
||||||
else
|
|
||||||
serial = VLC->generaltexth->pluralText(serial, 1);
|
|
||||||
|
|
||||||
text.addTxt(type, serial);
|
for(const CStack * st : battle->stacks)
|
||||||
|
{
|
||||||
|
if(battle->battleMatchOwner(st, unit, true) && st->getCreature()->idNumber == CreatureID::AMMO_CART && st->alive())
|
||||||
|
{
|
||||||
|
hasAmmoCart = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasAmmoCart;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStack::addNameReplacement(MetaString & text, const boost::logic::tribool & plural) const
|
PlayerColor CStack::unitEffectiveOwner(const battle::Unit * unit) const
|
||||||
{
|
{
|
||||||
if(boost::logic::indeterminate(plural))
|
return battle->battleGetOwner(unit);
|
||||||
text.addCreReplacement(type->idNumber, health.getCount());
|
|
||||||
else if(plural)
|
|
||||||
text.addReplacement(MetaString::CRE_PL_NAMES, type->idNumber.num);
|
|
||||||
else
|
|
||||||
text.addReplacement(MetaString::CRE_SING_NAMES, type->idNumber.num);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CStack::formatGeneralMessage(const int32_t baseTextId) const
|
uint32_t CStack::unitId() const
|
||||||
{
|
{
|
||||||
const int32_t textId = VLC->generaltexth->pluralText(baseTextId, health.getCount());
|
return ID;
|
||||||
|
|
||||||
MetaString text;
|
|
||||||
text.addTxt(MetaString::GENERAL_TXT, textId);
|
|
||||||
text.addCreReplacement(type->idNumber, health.getCount());
|
|
||||||
|
|
||||||
return text.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStack::setHealth(const CHealthInfo & value)
|
ui8 CStack::unitSide() const
|
||||||
{
|
{
|
||||||
health.reset();
|
return side;
|
||||||
health.fromInfo(value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CStack::setHealth(const CHealth & value)
|
PlayerColor CStack::unitOwner() const
|
||||||
{
|
{
|
||||||
health = value;
|
return owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
SlotID CStack::unitSlot() const
|
||||||
|
{
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CStack::getDescription() const
|
||||||
|
{
|
||||||
|
return nodeName();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CStack::spendMana(const spells::PacketSender * server, const int spellCost) const
|
||||||
|
{
|
||||||
|
if(spellCost != 1)
|
||||||
|
logGlobal->warn("Unexpected spell cost %d for creature", spellCost);
|
||||||
|
|
||||||
|
BattleSetStackProperty ssp;
|
||||||
|
ssp.stackID = unitId();
|
||||||
|
ssp.which = BattleSetStackProperty::CASTS;
|
||||||
|
ssp.val = -spellCost;
|
||||||
|
ssp.absolute = false;
|
||||||
|
server->sendAndApply(&ssp);
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-212
@@ -9,179 +9,42 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "JsonNode.h"
|
||||||
|
#include "HeroBonus.h"
|
||||||
|
#include "CCreatureHandler.h" //todo: remove
|
||||||
#include "battle/BattleHex.h"
|
#include "battle/BattleHex.h"
|
||||||
#include "CCreatureHandler.h"
|
|
||||||
#include "mapObjects/CGHeroInstance.h" // for commander serialization
|
#include "mapObjects/CGHeroInstance.h" // for commander serialization
|
||||||
|
|
||||||
|
#include "battle/CUnitState.h"
|
||||||
|
|
||||||
struct BattleStackAttacked;
|
struct BattleStackAttacked;
|
||||||
struct BattleInfo;
|
class BattleInfo;
|
||||||
class CStack;
|
|
||||||
class CHealthInfo;
|
|
||||||
|
|
||||||
template <typename Quantity>
|
class DLL_LINKAGE CStack : public CBonusSystemNode, public battle::CUnitState, public battle::IUnitEnvironment
|
||||||
class DLL_LINKAGE CStackResource
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CStackResource(const CStack * Owner):
|
|
||||||
owner(Owner)
|
|
||||||
{
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual void reset()
|
|
||||||
{
|
|
||||||
used = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
|
||||||
const CStack * owner;
|
|
||||||
Quantity used;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE CAmmo : public CStackResource<int32_t>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CAmmo(const CStack * Owner, CSelector totalSelector);
|
|
||||||
|
|
||||||
int32_t available() const;
|
|
||||||
bool canUse(int32_t amount = 1) const;
|
|
||||||
virtual void reset() override;
|
|
||||||
virtual int32_t total() const;
|
|
||||||
virtual void use(int32_t amount = 1);
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler & h, const int version)
|
|
||||||
{
|
|
||||||
if(!h.saving)
|
|
||||||
reset();
|
|
||||||
h & used;
|
|
||||||
}
|
|
||||||
protected:
|
|
||||||
CBonusProxy totalProxy;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE CShots : public CAmmo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CShots(const CStack * Owner);
|
|
||||||
void use(int32_t amount = 1) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE CCasts : public CAmmo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CCasts(const CStack * Owner);
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE CRetaliations : public CAmmo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CRetaliations(const CStack * Owner);
|
|
||||||
int32_t total() const override;
|
|
||||||
void reset() override;
|
|
||||||
private:
|
|
||||||
mutable int32_t totalCache;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE IUnitHealthInfo
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual int32_t unitMaxHealth() const = 0;
|
|
||||||
virtual int32_t unitBaseAmount() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE CHealth
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CHealth(const IUnitHealthInfo * Owner);
|
|
||||||
CHealth(const CHealth & other);
|
|
||||||
|
|
||||||
void init();
|
|
||||||
void reset();
|
|
||||||
|
|
||||||
void damage(int32_t & amount);
|
|
||||||
void heal(int32_t & amount, EHealLevel level, EHealPower power);
|
|
||||||
|
|
||||||
int32_t getCount() const;
|
|
||||||
int32_t getFirstHPleft() const;
|
|
||||||
int32_t getResurrected() const;
|
|
||||||
|
|
||||||
int64_t available() const;
|
|
||||||
int64_t total() const;
|
|
||||||
|
|
||||||
void toInfo(CHealthInfo & info) const;
|
|
||||||
void fromInfo(const CHealthInfo & info);
|
|
||||||
|
|
||||||
void takeResurrected();
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler & h, const int version)
|
|
||||||
{
|
|
||||||
if(!h.saving)
|
|
||||||
reset();
|
|
||||||
h & firstHPleft;
|
|
||||||
h & fullUnits;
|
|
||||||
h & resurrected;
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
void addResurrected(int32_t amount);
|
|
||||||
void setFromTotal(const int64_t totalHealth);
|
|
||||||
const IUnitHealthInfo * owner;
|
|
||||||
|
|
||||||
int32_t firstHPleft;
|
|
||||||
int32_t fullUnits;
|
|
||||||
int32_t resurrected;
|
|
||||||
};
|
|
||||||
|
|
||||||
class DLL_LINKAGE CStack : public CBonusSystemNode, public ISpellCaster, public IUnitHealthInfo
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const CStackInstance * base; //garrison slot from which stack originates (nullptr for war machines, summoned cres, etc)
|
const CStackInstance * base; //garrison slot from which stack originates (nullptr for war machines, summoned cres, etc)
|
||||||
|
|
||||||
ui32 ID; //unique ID of stack
|
ui32 ID; //unique ID of stack
|
||||||
ui32 baseAmount;
|
|
||||||
const CCreature * type;
|
const CCreature * type;
|
||||||
|
ui32 baseAmount;
|
||||||
|
|
||||||
PlayerColor owner; //owner - player color (255 for neutrals)
|
PlayerColor owner; //owner - player color (255 for neutrals)
|
||||||
SlotID slot; //slot - position in garrison (may be 255 for neutrals/called creatures)
|
SlotID slot; //slot - position in garrison (may be 255 for neutrals/called creatures)
|
||||||
ui8 side;
|
ui8 side;
|
||||||
BattleHex position; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower
|
BattleHex initialPosition; //position on battlefield; -2 - keep, -3 - lower tower, -4 - upper tower
|
||||||
|
|
||||||
CRetaliations counterAttacks;
|
|
||||||
CShots shots;
|
|
||||||
CCasts casts;
|
|
||||||
CHealth health;
|
|
||||||
|
|
||||||
///id of alive clone of this stack clone if any
|
|
||||||
si32 cloneID;
|
|
||||||
std::set<EBattleStackState::EBattleStackState> state;
|
|
||||||
|
|
||||||
CStack(const CStackInstance * base, PlayerColor O, int I, ui8 Side, SlotID S);
|
CStack(const CStackInstance * base, PlayerColor O, int I, ui8 Side, SlotID S);
|
||||||
CStack(const CStackBasicDescriptor * stack, PlayerColor O, int I, ui8 Side, SlotID S = SlotID(255));
|
CStack(const CStackBasicDescriptor * stack, PlayerColor O, int I, ui8 Side, SlotID S = SlotID(255));
|
||||||
CStack();
|
CStack();
|
||||||
~CStack();
|
~CStack();
|
||||||
|
|
||||||
int32_t getKilled() const;
|
const CCreature * getCreature() const; //deprecated
|
||||||
int32_t getCount() const;
|
|
||||||
int32_t getFirstHPleft() const;
|
|
||||||
const CCreature * getCreature() const;
|
|
||||||
|
|
||||||
std::string nodeName() const override;
|
std::string nodeName() const override;
|
||||||
|
|
||||||
void init(); //set initial (invalid) values
|
|
||||||
void localInit(BattleInfo * battleInfo);
|
void localInit(BattleInfo * battleInfo);
|
||||||
std::string getName() const; //plural or singular
|
std::string getName() const; //plural or singular
|
||||||
bool willMove(int turn = 0) const; //if stack has remaining move this turn
|
|
||||||
bool ableToRetaliate() const; //if stack can retaliate after attacked
|
|
||||||
|
|
||||||
bool moved(int turn = 0) const; //if stack was already moved this turn
|
|
||||||
bool waited(int turn = 0) const;
|
|
||||||
|
|
||||||
bool canCast() const;
|
|
||||||
bool isCaster() const;
|
|
||||||
|
|
||||||
bool canMove(int turn = 0) const; //if stack can move
|
|
||||||
|
|
||||||
bool canShoot() const;
|
|
||||||
bool isShooter() const;
|
|
||||||
|
|
||||||
bool canBeHealed() const; //for first aid tent - only harmed stacks that are not war machines
|
bool canBeHealed() const; //for first aid tent - only harmed stacks that are not war machines
|
||||||
|
|
||||||
@@ -190,68 +53,32 @@ public:
|
|||||||
std::vector<si32> activeSpells() const; //returns vector of active spell IDs sorted by time of cast
|
std::vector<si32> activeSpells() const; //returns vector of active spell IDs sorted by time of cast
|
||||||
const CGHeroInstance * getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, nullptr otherwise
|
const CGHeroInstance * getMyHero() const; //if stack belongs to hero (directly or was by him summoned) returns hero, nullptr otherwise
|
||||||
|
|
||||||
static bool isMeleeAttackPossible(const CStack * attacker, const CStack * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID);
|
static bool isMeleeAttackPossible(const battle::Unit * attacker, const battle::Unit * defender, BattleHex attackerPos = BattleHex::INVALID, BattleHex defenderPos = BattleHex::INVALID);
|
||||||
|
|
||||||
bool doubleWide() const;
|
|
||||||
BattleHex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
|
|
||||||
BattleHex occupiedHex(BattleHex assumedPos) const; //returns number of occupied hex (not the position) if stack is double wide and would stand on assumedPos; otherwise -1
|
|
||||||
std::vector<BattleHex> getHexes() const; //up to two occupied hexes, starting from front
|
|
||||||
std::vector<BattleHex> getHexes(BattleHex assumedPos) const; //up to two occupied hexes, starting from front
|
|
||||||
static std::vector<BattleHex> getHexes(BattleHex assumedPos, bool twoHex, ui8 side); //up to two occupied hexes, starting from front
|
|
||||||
bool coversPos(BattleHex position) const; //checks also if unit is double-wide
|
|
||||||
std::vector<BattleHex> getSurroundingHexes(BattleHex attackerPos = BattleHex::INVALID) const; // get six or 8 surrounding hexes depending on creature size
|
|
||||||
|
|
||||||
BattleHex::EDir destShiftDir() const;
|
BattleHex::EDir destShiftDir() const;
|
||||||
|
|
||||||
CHealth healthAfterAttacked(int32_t & damage) const;
|
void prepareAttacked(BattleStackAttacked & bsa, vstd::RNG & rand) const; //requires bsa.damageAmout filled
|
||||||
CHealth healthAfterAttacked(int32_t & damage, const CHealth & customHealth) const;
|
static void prepareAttacked(BattleStackAttacked & bsa, vstd::RNG & rand, std::shared_ptr<battle::CUnitState> customState); //requires bsa.damageAmout filled
|
||||||
|
|
||||||
CHealth healthAfterHealed(int32_t & toHeal, EHealLevel level, EHealPower power) const;
|
const CCreature * unitType() const override;
|
||||||
|
|
||||||
void prepareAttacked(BattleStackAttacked & bsa, CRandomGenerator & rand) const; //requires bsa.damageAmout filled
|
|
||||||
void prepareAttacked(BattleStackAttacked & bsa, CRandomGenerator & rand, const CHealth & customHealth) const; //requires bsa.damageAmout filled
|
|
||||||
|
|
||||||
///ISpellCaster
|
|
||||||
|
|
||||||
ui8 getSpellSchoolLevel(const CSpell * spell, int * outSelectedSchool = nullptr) const override;
|
|
||||||
ui32 getSpellBonus(const CSpell * spell, ui32 base, const CStack * affectedStack) const override;
|
|
||||||
|
|
||||||
///default spell school level for effect calculation
|
|
||||||
int getEffectLevel(const CSpell * spell) const override;
|
|
||||||
|
|
||||||
///default spell-power for damage/heal calculation
|
|
||||||
int getEffectPower(const CSpell * spell) const override;
|
|
||||||
|
|
||||||
///default spell-power for timed effects duration
|
|
||||||
int getEnchantPower(const CSpell * spell) const override;
|
|
||||||
|
|
||||||
///damage/heal override(ignores spell configuration, effect level and effect power)
|
|
||||||
int getEffectValue(const CSpell * spell) const override;
|
|
||||||
|
|
||||||
const PlayerColor getOwner() const override;
|
|
||||||
void getCasterName(MetaString & text) const override;
|
|
||||||
void getCastDescription(const CSpell * spell, const std::vector<const CStack *> & attacked, MetaString & text) const override;
|
|
||||||
|
|
||||||
///IUnitHealthInfo
|
|
||||||
|
|
||||||
int32_t unitMaxHealth() const override;
|
|
||||||
int32_t unitBaseAmount() const override;
|
int32_t unitBaseAmount() const override;
|
||||||
|
|
||||||
///MetaStrings
|
uint32_t unitId() const override;
|
||||||
|
ui8 unitSide() const override;
|
||||||
|
PlayerColor unitOwner() const override;
|
||||||
|
SlotID unitSlot() const override;
|
||||||
|
|
||||||
void addText(MetaString & text, ui8 type, int32_t serial, const boost::logic::tribool & plural = boost::logic::indeterminate) const;
|
std::string getDescription() const override;
|
||||||
void addNameReplacement(MetaString & text, const boost::logic::tribool & plural = boost::logic::indeterminate) const;
|
|
||||||
std::string formatGeneralMessage(const int32_t baseTextId) const;
|
|
||||||
|
|
||||||
///Non const API for NetPacks
|
bool unitHasAmmoCart(const battle::Unit * unit) const override;
|
||||||
|
PlayerColor unitEffectiveOwner(const battle::Unit * unit) const override;
|
||||||
|
|
||||||
///stack will be ghost in next battle state update
|
void spendMana(const spells::PacketSender * server, const int spellCost) const override;
|
||||||
void makeGhost();
|
|
||||||
void setHealth(const CHealthInfo & value);
|
|
||||||
void setHealth(const CHealth & value);
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler & h, const int version)
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
{
|
{
|
||||||
|
//this assumes that stack objects is newly created
|
||||||
|
//stackState is not serialized here
|
||||||
assert(isIndependentNode());
|
assert(isIndependentNode());
|
||||||
h & static_cast<CBonusSystemNode&>(*this);
|
h & static_cast<CBonusSystemNode&>(*this);
|
||||||
h & type;
|
h & type;
|
||||||
@@ -260,12 +87,7 @@ public:
|
|||||||
h & owner;
|
h & owner;
|
||||||
h & slot;
|
h & slot;
|
||||||
h & side;
|
h & side;
|
||||||
h & position;
|
h & initialPosition;
|
||||||
h & state;
|
|
||||||
h & shots;
|
|
||||||
h & casts;
|
|
||||||
h & counterAttacks;
|
|
||||||
h & health;
|
|
||||||
|
|
||||||
const CArmedInstance * army = (base ? base->armyObj : nullptr);
|
const CArmedInstance * army = (base ? base->armyObj : nullptr);
|
||||||
SlotID extSlot = (base ? base->armyObj->findStack(base) : SlotID());
|
SlotID extSlot = (base ? base->armyObj->findStack(base) : SlotID());
|
||||||
@@ -279,6 +101,7 @@ public:
|
|||||||
{
|
{
|
||||||
h & army;
|
h & army;
|
||||||
h & extSlot;
|
h & extSlot;
|
||||||
|
|
||||||
if(extSlot == SlotID::COMMANDER_SLOT_PLACEHOLDER)
|
if(extSlot == SlotID::COMMANDER_SLOT_PLACEHOLDER)
|
||||||
{
|
{
|
||||||
auto hero = dynamic_cast<const CGHeroInstance *>(army);
|
auto hero = dynamic_cast<const CGHeroInstance *>(army);
|
||||||
@@ -300,17 +123,8 @@ public:
|
|||||||
base = &army->getStack(extSlot);
|
base = &army->getStack(extSlot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
bool alive() const;
|
|
||||||
|
|
||||||
bool isClone() const;
|
|
||||||
bool isDead() const;
|
|
||||||
bool isGhost() const; //determines if stack was removed
|
|
||||||
bool isValidTarget(bool allowDead = false) const; //non-turret non-ghost stacks (can be attacked or be object of magic effect)
|
|
||||||
bool isTurret() const;
|
|
||||||
|
|
||||||
friend class CShots; //for BattleInfo access
|
|
||||||
private:
|
private:
|
||||||
const BattleInfo * battle; //do not serialize
|
const BattleInfo * battle; //do not serialize
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,13 +25,11 @@ CThreadHelper::CThreadHelper(std::vector<std::function<void()> > *Tasks, int Thr
|
|||||||
void CThreadHelper::run()
|
void CThreadHelper::run()
|
||||||
{
|
{
|
||||||
boost::thread_group grupa;
|
boost::thread_group grupa;
|
||||||
std::vector<boost::thread *> thr;
|
|
||||||
for(int i=0;i<threads;i++)
|
for(int i=0;i<threads;i++)
|
||||||
thr.push_back(grupa.create_thread(std::bind(&CThreadHelper::processTasks,this)));
|
grupa.create_thread(std::bind(&CThreadHelper::processTasks,this));
|
||||||
grupa.join_all();
|
grupa.join_all();
|
||||||
|
|
||||||
for(auto thread : thr)
|
//thread group deletes threads, do not free manually
|
||||||
delete thread;
|
|
||||||
}
|
}
|
||||||
void CThreadHelper::processTasks()
|
void CThreadHelper::processTasks()
|
||||||
{
|
{
|
||||||
|
|||||||
+61
-18
@@ -24,6 +24,7 @@
|
|||||||
#include "CSkillHandler.h"
|
#include "CSkillHandler.h"
|
||||||
#include "StringConstants.h"
|
#include "StringConstants.h"
|
||||||
#include "CGeneralTextHandler.h"
|
#include "CGeneralTextHandler.h"
|
||||||
|
#include "CModHandler.h"
|
||||||
|
|
||||||
const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2);
|
const SlotID SlotID::COMMANDER_SLOT_PLACEHOLDER = SlotID(-2);
|
||||||
const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(-3);
|
const SlotID SlotID::SUMMONED_SLOT_PLACEHOLDER = SlotID(-3);
|
||||||
@@ -51,11 +52,39 @@ const CArtifact * ArtifactID::toArtifact() const
|
|||||||
return VLC->arth->artifacts.at(*this);
|
return VLC->arth->artifacts.at(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
si32 ArtifactID::decode(const std::string & identifier)
|
||||||
|
{
|
||||||
|
auto rawId = VLC->modh->identifiers.getIdentifier("core", "artifact", identifier);
|
||||||
|
if(rawId)
|
||||||
|
return rawId.get();
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ArtifactID::encode(const si32 index)
|
||||||
|
{
|
||||||
|
return VLC->arth->artifacts.at(index)->identifier;
|
||||||
|
}
|
||||||
|
|
||||||
const CCreature * CreatureID::toCreature() const
|
const CCreature * CreatureID::toCreature() const
|
||||||
{
|
{
|
||||||
return VLC->creh->creatures.at(*this);
|
return VLC->creh->creatures.at(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
si32 CreatureID::decode(const std::string & identifier)
|
||||||
|
{
|
||||||
|
auto rawId = VLC->modh->identifiers.getIdentifier("core", "creature", identifier);
|
||||||
|
if(rawId)
|
||||||
|
return rawId.get();
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CreatureID::encode(const si32 index)
|
||||||
|
{
|
||||||
|
return VLC->creh->creatures.at(index)->identifier;
|
||||||
|
}
|
||||||
|
|
||||||
const CSpell * SpellID::toSpell() const
|
const CSpell * SpellID::toSpell() const
|
||||||
{
|
{
|
||||||
if(num < 0 || num >= VLC->spellh->objects.size())
|
if(num < 0 || num >= VLC->spellh->objects.size())
|
||||||
@@ -66,6 +95,20 @@ const CSpell * SpellID::toSpell() const
|
|||||||
return VLC->spellh->objects[*this];
|
return VLC->spellh->objects[*this];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
si32 SpellID::decode(const std::string & identifier)
|
||||||
|
{
|
||||||
|
auto rawId = VLC->modh->identifiers.getIdentifier("core", "spell", identifier);
|
||||||
|
if(rawId)
|
||||||
|
return rawId.get();
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SpellID::encode(const si32 index)
|
||||||
|
{
|
||||||
|
return VLC->spellh->objects.at(index)->identifier;
|
||||||
|
}
|
||||||
|
|
||||||
const CSkill * SecondarySkill::toSkill() const
|
const CSkill * SecondarySkill::toSkill() const
|
||||||
{
|
{
|
||||||
return VLC->skillh->objects.at(*this);
|
return VLC->skillh->objects.at(*this);
|
||||||
@@ -110,26 +153,26 @@ std::string PlayerColor::getStrCap(bool L10n) const
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostream & operator<<(std::ostream & os, const Battle::ActionType actionType)
|
std::ostream & operator<<(std::ostream & os, const EActionType actionType)
|
||||||
{
|
{
|
||||||
static const std::map<Battle::ActionType, std::string> actionTypeToString =
|
static const std::map<EActionType, std::string> actionTypeToString =
|
||||||
{
|
{
|
||||||
{Battle::END_TACTIC_PHASE, "End tactic phase"},
|
{EActionType::END_TACTIC_PHASE, "End tactic phase"},
|
||||||
{Battle::INVALID, "Invalid"},
|
{EActionType::INVALID, "Invalid"},
|
||||||
{Battle::NO_ACTION, "No action"},
|
{EActionType::NO_ACTION, "No action"},
|
||||||
{Battle::HERO_SPELL, "Hero spell"},
|
{EActionType::HERO_SPELL, "Hero spell"},
|
||||||
{Battle::WALK, "Walk"},
|
{EActionType::WALK, "Walk"},
|
||||||
{Battle::DEFEND, "Defend"},
|
{EActionType::DEFEND, "Defend"},
|
||||||
{Battle::RETREAT, "Retreat"},
|
{EActionType::RETREAT, "Retreat"},
|
||||||
{Battle::SURRENDER, "Surrender"},
|
{EActionType::SURRENDER, "Surrender"},
|
||||||
{Battle::WALK_AND_ATTACK, "Walk and attack"},
|
{EActionType::WALK_AND_ATTACK, "Walk and attack"},
|
||||||
{Battle::SHOOT, "Shoot"},
|
{EActionType::SHOOT, "Shoot"},
|
||||||
{Battle::WAIT, "Wait"},
|
{EActionType::WAIT, "Wait"},
|
||||||
{Battle::CATAPULT, "Catapult"},
|
{EActionType::CATAPULT, "Catapult"},
|
||||||
{Battle::MONSTER_SPELL, "Monster spell"},
|
{EActionType::MONSTER_SPELL, "Monster spell"},
|
||||||
{Battle::BAD_MORALE, "Bad morale"},
|
{EActionType::BAD_MORALE, "Bad morale"},
|
||||||
{Battle::STACK_HEAL, "Stack heal"},
|
{EActionType::STACK_HEAL, "Stack heal"},
|
||||||
{Battle::DAEMON_SUMMONING, "Daemon summoning"}
|
{EActionType::DAEMON_SUMMONING, "Daemon summoning"}
|
||||||
};
|
};
|
||||||
|
|
||||||
auto it = actionTypeToString.find(actionType);
|
auto it = actionTypeToString.find(actionType);
|
||||||
|
|||||||
+34
-63
@@ -15,10 +15,6 @@ namespace GameConstants
|
|||||||
{
|
{
|
||||||
DLL_LINKAGE extern const std::string VCMI_VERSION;
|
DLL_LINKAGE extern const std::string VCMI_VERSION;
|
||||||
|
|
||||||
const int BFIELD_WIDTH = 17;
|
|
||||||
const int BFIELD_HEIGHT = 11;
|
|
||||||
const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT;
|
|
||||||
|
|
||||||
const int PUZZLE_MAP_PIECES = 48;
|
const int PUZZLE_MAP_PIECES = 48;
|
||||||
|
|
||||||
const int MAX_HEROES_PER_PLAYER = 8;
|
const int MAX_HEROES_PER_PLAYER = 8;
|
||||||
@@ -443,27 +439,15 @@ namespace ESpellCastProblem
|
|||||||
{
|
{
|
||||||
enum ESpellCastProblem
|
enum ESpellCastProblem
|
||||||
{
|
{
|
||||||
OK, NO_HERO_TO_CAST_SPELL, ALREADY_CASTED_THIS_TURN, NO_SPELLBOOK, ANOTHER_ELEMENTAL_SUMMONED,
|
OK, NO_HERO_TO_CAST_SPELL, CASTS_PER_TURN_LIMIT, NO_SPELLBOOK,
|
||||||
HERO_DOESNT_KNOW_SPELL, NOT_ENOUGH_MANA, ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL,
|
HERO_DOESNT_KNOW_SPELL, NOT_ENOUGH_MANA, ADVMAP_SPELL_INSTEAD_OF_BATTLE_SPELL,
|
||||||
SECOND_HEROS_SPELL_IMMUNITY, SPELL_LEVEL_LIMIT_EXCEEDED, NO_SPELLS_TO_DISPEL,
|
SPELL_LEVEL_LIMIT_EXCEEDED, NO_SPELLS_TO_DISPEL,
|
||||||
NO_APPROPRIATE_TARGET, STACK_IMMUNE_TO_SPELL, WRONG_SPELL_TARGET, ONGOING_TACTIC_PHASE,
|
NO_APPROPRIATE_TARGET, STACK_IMMUNE_TO_SPELL, WRONG_SPELL_TARGET, ONGOING_TACTIC_PHASE,
|
||||||
MAGIC_IS_BLOCKED, //For Orb of Inhibition and similar - no casting at all
|
MAGIC_IS_BLOCKED, //For Orb of Inhibition and similar - no casting at all
|
||||||
NOT_DECIDED,
|
|
||||||
INVALID
|
INVALID
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ECastingMode
|
|
||||||
{
|
|
||||||
enum ECastingMode
|
|
||||||
{
|
|
||||||
HERO_CASTING, AFTER_ATTACK_CASTING, //also includes cast before attack
|
|
||||||
MAGIC_MIRROR, CREATURE_ACTIVE_CASTING, ENCHANTER_CASTING,
|
|
||||||
SPELL_LIKE_ATTACK,
|
|
||||||
PASSIVE_CASTING//f.e. opening battle spells
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace EMarketMode
|
namespace EMarketMode
|
||||||
{
|
{
|
||||||
enum EMarketMode
|
enum EMarketMode
|
||||||
@@ -474,26 +458,6 @@ namespace EMarketMode
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace EBattleStackState
|
|
||||||
{
|
|
||||||
enum EBattleStackState
|
|
||||||
{
|
|
||||||
ALIVE = 180,
|
|
||||||
SUMMONED, CLONED,
|
|
||||||
GHOST, //stack was removed from battlefield
|
|
||||||
HAD_MORALE,
|
|
||||||
WAITING,
|
|
||||||
MOVED,
|
|
||||||
DEFENDING,
|
|
||||||
FEAR,
|
|
||||||
//remember to drain mana only once per turn
|
|
||||||
DRAINED_MANA,
|
|
||||||
//only for defending animation
|
|
||||||
DEFENDING_ANIM,
|
|
||||||
GHOST_PENDING// stack will become GHOST in next battle state update
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ECommander
|
namespace ECommander
|
||||||
{
|
{
|
||||||
enum SecondarySkills {ATTACK, DEFENSE, HEALTH, DAMAGE, SPEED, SPELL_POWER, CASTS, RESISTANCE};
|
enum SecondarySkills {ATTACK, DEFENSE, HEALTH, DAMAGE, SPEED, SPELL_POWER, CASTS, RESISTANCE};
|
||||||
@@ -518,8 +482,6 @@ namespace EWallState
|
|||||||
DESTROYED,
|
DESTROYED,
|
||||||
DAMAGED,
|
DAMAGED,
|
||||||
INTACT
|
INTACT
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -778,30 +740,27 @@ namespace Date
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Battle
|
enum class EActionType : int32_t
|
||||||
{
|
{
|
||||||
enum ActionType
|
CANCEL = -3,
|
||||||
{
|
END_TACTIC_PHASE = -2,
|
||||||
CANCEL = -3,
|
INVALID = -1,
|
||||||
END_TACTIC_PHASE = -2,
|
NO_ACTION = 0,
|
||||||
INVALID = -1,
|
HERO_SPELL,
|
||||||
NO_ACTION = 0,
|
WALK, DEFEND,
|
||||||
HERO_SPELL,
|
RETREAT,
|
||||||
WALK, DEFEND,
|
SURRENDER,
|
||||||
RETREAT,
|
WALK_AND_ATTACK,
|
||||||
SURRENDER,
|
SHOOT,
|
||||||
WALK_AND_ATTACK,
|
WAIT,
|
||||||
SHOOT,
|
CATAPULT,
|
||||||
WAIT,
|
MONSTER_SPELL,
|
||||||
CATAPULT,
|
BAD_MORALE,
|
||||||
MONSTER_SPELL,
|
STACK_HEAL,
|
||||||
BAD_MORALE,
|
DAEMON_SUMMONING
|
||||||
STACK_HEAL,
|
};
|
||||||
DAEMON_SUMMONING
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ostream & operator<<(std::ostream & os, const Battle::ActionType actionType);
|
DLL_LINKAGE std::ostream & operator<<(std::ostream & os, const EActionType actionType);
|
||||||
|
|
||||||
class DLL_LINKAGE ETerrainType
|
class DLL_LINKAGE ETerrainType
|
||||||
{
|
{
|
||||||
@@ -969,6 +928,10 @@ public:
|
|||||||
|
|
||||||
DLL_LINKAGE const CArtifact * toArtifact() const;
|
DLL_LINKAGE const CArtifact * toArtifact() const;
|
||||||
|
|
||||||
|
///json serialization helpers
|
||||||
|
static si32 decode(const std::string & identifier);
|
||||||
|
static std::string encode(const si32 index);
|
||||||
|
|
||||||
ID_LIKE_CLASS_COMMON(ArtifactID, EArtifactID)
|
ID_LIKE_CLASS_COMMON(ArtifactID, EArtifactID)
|
||||||
|
|
||||||
EArtifactID num;
|
EArtifactID num;
|
||||||
@@ -1017,6 +980,10 @@ public:
|
|||||||
ID_LIKE_CLASS_COMMON(CreatureID, ECreatureID)
|
ID_LIKE_CLASS_COMMON(CreatureID, ECreatureID)
|
||||||
|
|
||||||
ECreatureID num;
|
ECreatureID num;
|
||||||
|
|
||||||
|
///json serialization helpers
|
||||||
|
static si32 decode(const std::string & identifier);
|
||||||
|
static std::string encode(const si32 index);
|
||||||
};
|
};
|
||||||
|
|
||||||
ID_LIKE_OPERATORS(CreatureID, CreatureID::ECreatureID)
|
ID_LIKE_OPERATORS(CreatureID, CreatureID::ECreatureID)
|
||||||
@@ -1060,6 +1027,10 @@ public:
|
|||||||
ID_LIKE_CLASS_COMMON(SpellID, ESpellID)
|
ID_LIKE_CLASS_COMMON(SpellID, ESpellID)
|
||||||
|
|
||||||
ESpellID num;
|
ESpellID num;
|
||||||
|
|
||||||
|
///json serialization helpers
|
||||||
|
static si32 decode(const std::string & identifier);
|
||||||
|
static std::string encode(const si32 index);
|
||||||
};
|
};
|
||||||
|
|
||||||
ID_LIKE_OPERATORS(SpellID, SpellID::ESpellID)
|
ID_LIKE_OPERATORS(SpellID, SpellID::ESpellID)
|
||||||
@@ -1108,7 +1079,7 @@ enum class EHealPower : ui8
|
|||||||
// Typedef declarations
|
// Typedef declarations
|
||||||
typedef ui8 TFaction;
|
typedef ui8 TFaction;
|
||||||
typedef si64 TExpType;
|
typedef si64 TExpType;
|
||||||
typedef std::pair<ui32, ui32> TDmgRange;
|
typedef std::pair<si64, si64> TDmgRange;
|
||||||
typedef si32 TBonusSubtype;
|
typedef si32 TBonusSubtype;
|
||||||
typedef si32 TQuantity;
|
typedef si32 TQuantity;
|
||||||
|
|
||||||
|
|||||||
+99
-52
@@ -81,20 +81,59 @@ const std::map<std::string, TPropagatorPtr> bonusPropagatorMap =
|
|||||||
}; //untested
|
}; //untested
|
||||||
|
|
||||||
///CBonusProxy
|
///CBonusProxy
|
||||||
CBonusProxy::CBonusProxy(const IBonusBearer * Target, CSelector Selector):
|
CBonusProxy::CBonusProxy(const IBonusBearer * Target, CSelector Selector)
|
||||||
cachedLast(0), target(Target), selector(Selector), data()
|
: cachedLast(0),
|
||||||
|
target(Target),
|
||||||
|
selector(Selector),
|
||||||
|
data()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CBonusProxy::CBonusProxy(const CBonusProxy & other)
|
||||||
|
: cachedLast(other.cachedLast),
|
||||||
|
target(other.target),
|
||||||
|
selector(other.selector),
|
||||||
|
data(other.data)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusProxy::CBonusProxy(CBonusProxy && other)
|
||||||
|
: cachedLast(0),
|
||||||
|
target(other.target),
|
||||||
|
selector(),
|
||||||
|
data()
|
||||||
|
{
|
||||||
|
std::swap(cachedLast, other.cachedLast);
|
||||||
|
std::swap(selector, other.selector);
|
||||||
|
std::swap(data, other.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusProxy & CBonusProxy::operator=(const CBonusProxy & other)
|
||||||
|
{
|
||||||
|
cachedLast = other.cachedLast;
|
||||||
|
selector = other.selector;
|
||||||
|
data = other.data;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusProxy & CBonusProxy::operator=(CBonusProxy && other)
|
||||||
|
{
|
||||||
|
std::swap(cachedLast, other.cachedLast);
|
||||||
|
std::swap(selector, other.selector);
|
||||||
|
std::swap(data, other.data);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
TBonusListPtr CBonusProxy::get() const
|
TBonusListPtr CBonusProxy::get() const
|
||||||
{
|
{
|
||||||
if(CBonusSystemNode::treeChanged != cachedLast || !data)
|
if(target->getTreeVersion() != cachedLast || !data)
|
||||||
{
|
{
|
||||||
//TODO: support limiters
|
//TODO: support limiters
|
||||||
data = target->getAllBonuses(selector, nullptr);
|
data = target->getAllBonuses(selector, Selector::all);
|
||||||
data->eliminateDuplicates();
|
data->eliminateDuplicates();
|
||||||
cachedLast = CBonusSystemNode::treeChanged;
|
cachedLast = target->getTreeVersion();
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
@@ -104,7 +143,7 @@ const BonusList * CBonusProxy::operator->() const
|
|||||||
return get().get();
|
return get().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
int CBonusSystemNode::treeChanged = 1;
|
std::atomic<int32_t> CBonusSystemNode::treeChanged(1);
|
||||||
const bool CBonusSystemNode::cachingEnabled = true;
|
const bool CBonusSystemNode::cachingEnabled = true;
|
||||||
|
|
||||||
BonusList::BonusList(bool BelongsToTree) : belongsToTree(BelongsToTree)
|
BonusList::BonusList(bool BelongsToTree) : belongsToTree(BelongsToTree)
|
||||||
@@ -408,48 +447,44 @@ int IBonusBearer::LuckVal() const
|
|||||||
return vstd::abetween(ret, -3, +3);
|
return vstd::abetween(ret, -3, +3);
|
||||||
}
|
}
|
||||||
|
|
||||||
si32 IBonusBearer::Attack() const
|
|
||||||
{
|
|
||||||
si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
|
|
||||||
|
|
||||||
if (double frenzyPower = valOfBonuses(Bonus::IN_FRENZY)) //frenzy for attacker
|
|
||||||
{
|
|
||||||
ret += (frenzyPower/100) * (double)Defense(false);
|
|
||||||
}
|
|
||||||
vstd::amax(ret, 0);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
si32 IBonusBearer::Defense(bool withFrenzy) const
|
|
||||||
{
|
|
||||||
si32 ret = valOfBonuses(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE);
|
|
||||||
|
|
||||||
if(withFrenzy && hasBonusOfType(Bonus::IN_FRENZY)) //frenzy for defender
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
vstd::amax(ret, 0);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui32 IBonusBearer::MaxHealth() const
|
ui32 IBonusBearer::MaxHealth() const
|
||||||
{
|
{
|
||||||
return std::max(1, valOfBonuses(Bonus::STACK_HEALTH)); //never 0
|
const std::string cachingStr = "type_STACK_HEALTH";
|
||||||
|
static const auto selector = Selector::type(Bonus::STACK_HEALTH);
|
||||||
|
auto value = valOfBonuses(selector, cachingStr);
|
||||||
|
return std::max(1, value); //never 0
|
||||||
}
|
}
|
||||||
|
|
||||||
ui32 IBonusBearer::getMinDamage() const
|
int IBonusBearer::getAttack(bool ranged) const
|
||||||
{
|
{
|
||||||
std::stringstream cachingStr;
|
const std::string cachingStr = "type_PRIMARY_SKILLs_ATTACK";
|
||||||
cachingStr << "type_" << Bonus::CREATURE_DAMAGE << "s_0Otype_" << Bonus::CREATURE_DAMAGE << "s_1";
|
|
||||||
return valOfBonuses(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1)), cachingStr.str());
|
static const auto selector = Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);
|
||||||
|
|
||||||
|
return getBonuses(selector, nullptr, cachingStr)->totalValue();
|
||||||
}
|
}
|
||||||
ui32 IBonusBearer::getMaxDamage() const
|
|
||||||
|
int IBonusBearer::getDefence(bool ranged) const
|
||||||
{
|
{
|
||||||
std::stringstream cachingStr;
|
const std::string cachingStr = "type_PRIMARY_SKILLs_DEFENSE";
|
||||||
cachingStr << "type_" << Bonus::CREATURE_DAMAGE << "s_0Otype_" << Bonus::CREATURE_DAMAGE << "s_2";
|
|
||||||
return valOfBonuses(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2)), cachingStr.str());
|
static const auto selector = Selector::typeSubtype(Bonus::PRIMARY_SKILL, PrimarySkill::DEFENSE);
|
||||||
|
|
||||||
|
return getBonuses(selector, nullptr, cachingStr)->totalValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
int IBonusBearer::getMinDamage(bool ranged) const
|
||||||
|
{
|
||||||
|
const std::string cachingStr = "type_CREATURE_DAMAGEs_0Otype_CREATURE_DAMAGEs_1";
|
||||||
|
static const auto selector = Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 1));
|
||||||
|
return valOfBonuses(selector, cachingStr);
|
||||||
|
}
|
||||||
|
|
||||||
|
int IBonusBearer::getMaxDamage(bool ranged) const
|
||||||
|
{
|
||||||
|
const std::string cachingStr = "type_CREATURE_DAMAGEs_0Otype_CREATURE_DAMAGEs_2";
|
||||||
|
static const auto selector = Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 0).Or(Selector::typeSubtype(Bonus::CREATURE_DAMAGE, 2));
|
||||||
|
return valOfBonuses(selector, cachingStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
si32 IBonusBearer::manaLimit() const
|
si32 IBonusBearer::manaLimit() const
|
||||||
@@ -461,13 +496,7 @@ si32 IBonusBearer::manaLimit() const
|
|||||||
|
|
||||||
int IBonusBearer::getPrimSkillLevel(PrimarySkill::PrimarySkill id) const
|
int IBonusBearer::getPrimSkillLevel(PrimarySkill::PrimarySkill id) const
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = valOfBonuses(Bonus::PRIMARY_SKILL, id);
|
||||||
if(id == PrimarySkill::ATTACK)
|
|
||||||
ret = Attack();
|
|
||||||
else if(id == PrimarySkill::DEFENSE)
|
|
||||||
ret = Defense();
|
|
||||||
else
|
|
||||||
ret = valOfBonuses(Bonus::PRIMARY_SKILL, id);
|
|
||||||
|
|
||||||
vstd::amax(ret, id/2); //minimal value is 0 for attack and defense and 1 for spell power and knowledge
|
vstd::amax(ret, id/2); //minimal value is 0 for attack and defense and 1 for spell power and knowledge
|
||||||
return ret;
|
return ret;
|
||||||
@@ -478,7 +507,7 @@ si32 IBonusBearer::magicResistance() const
|
|||||||
return valOfBonuses(Bonus::MAGIC_RESISTANCE);
|
return valOfBonuses(Bonus::MAGIC_RESISTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
ui32 IBonusBearer::Speed(int turn, bool useBind ) const
|
ui32 IBonusBearer::Speed(int turn, bool useBind) const
|
||||||
{
|
{
|
||||||
//war machines cannot move
|
//war machines cannot move
|
||||||
if(hasBonus(Selector::type(Bonus::SIEGE_WEAPON).And(Selector::turns(turn))))
|
if(hasBonus(Selector::type(Bonus::SIEGE_WEAPON).And(Selector::turns(turn))))
|
||||||
@@ -505,8 +534,8 @@ bool IBonusBearer::isLiving() const //TODO: theoreticaly there exists "LIVING" b
|
|||||||
|
|
||||||
const std::shared_ptr<Bonus> IBonusBearer::getBonus(const CSelector &selector) const
|
const std::shared_ptr<Bonus> IBonusBearer::getBonus(const CSelector &selector) const
|
||||||
{
|
{
|
||||||
auto bonuses = getAllBonuses(Selector::all, Selector::all);
|
auto bonuses = getAllBonuses(selector, Selector::all);
|
||||||
return bonuses->getFirst(selector);
|
return bonuses->getFirst(Selector::all);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector &selector)
|
std::shared_ptr<Bonus> CBonusSystemNode::getBonusLocalFirst(const CSelector &selector)
|
||||||
@@ -657,7 +686,19 @@ const TBonusListPtr CBonusSystemNode::getAllBonusesWithoutCaching(const CSelecto
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
CBonusSystemNode::CBonusSystemNode() : bonuses(true), exportedBonuses(true), nodeType(UNKNOWN), cachedLast(0)
|
CBonusSystemNode::CBonusSystemNode()
|
||||||
|
: bonuses(true),
|
||||||
|
exportedBonuses(true),
|
||||||
|
nodeType(UNKNOWN),
|
||||||
|
cachedLast(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CBonusSystemNode::CBonusSystemNode(ENodeTypes NodeType)
|
||||||
|
: bonuses(true),
|
||||||
|
exportedBonuses(true),
|
||||||
|
nodeType(NodeType),
|
||||||
|
cachedLast(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1048,6 +1089,12 @@ void CBonusSystemNode::treeHasChanged()
|
|||||||
treeChanged++;
|
treeChanged++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int64_t CBonusSystemNode::getTreeVersion() const
|
||||||
|
{
|
||||||
|
int64_t ret = treeChanged;
|
||||||
|
return ret << 32;
|
||||||
|
}
|
||||||
|
|
||||||
int NBonus::valOf(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype)
|
int NBonus::valOf(const CBonusSystemNode *obj, Bonus::BonusType type, int subtype)
|
||||||
{
|
{
|
||||||
if(obj)
|
if(obj)
|
||||||
|
|||||||
+22
-9
@@ -64,16 +64,21 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CBonusProxy : public boost::noncopyable
|
class DLL_LINKAGE CBonusProxy
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CBonusProxy(const IBonusBearer * Target, CSelector Selector);
|
CBonusProxy(const IBonusBearer * Target, CSelector Selector);
|
||||||
|
CBonusProxy(const CBonusProxy & other);
|
||||||
|
CBonusProxy(CBonusProxy && other);
|
||||||
|
|
||||||
|
CBonusProxy & operator=(CBonusProxy && other);
|
||||||
|
CBonusProxy & operator=(const CBonusProxy & other);
|
||||||
|
|
||||||
TBonusListPtr get() const;
|
TBonusListPtr get() const;
|
||||||
|
|
||||||
const BonusList * operator->() const;
|
const BonusList * operator->() const;
|
||||||
private:
|
private:
|
||||||
mutable int cachedLast;
|
mutable int64_t cachedLast;
|
||||||
const IBonusBearer * target;
|
const IBonusBearer * target;
|
||||||
CSelector selector;
|
CSelector selector;
|
||||||
mutable TBonusListPtr data;
|
mutable TBonusListPtr data;
|
||||||
@@ -607,12 +612,15 @@ public:
|
|||||||
bool hasBonusFrom(Bonus::BonusSource source, ui32 sourceID) const;
|
bool hasBonusFrom(Bonus::BonusSource source, ui32 sourceID) const;
|
||||||
|
|
||||||
//various hlp functions for non-trivial values
|
//various hlp functions for non-trivial values
|
||||||
ui32 getMinDamage() const; //used for stacks and creatures only
|
//used for stacks and creatures only
|
||||||
ui32 getMaxDamage() const;
|
|
||||||
|
virtual int getMinDamage(bool ranged) const;
|
||||||
|
virtual int getMaxDamage(bool ranged) const;
|
||||||
|
virtual int getAttack(bool ranged) const;
|
||||||
|
virtual int getDefence(bool ranged) const;
|
||||||
|
|
||||||
int MoraleVal() const; //range [-3, +3]
|
int MoraleVal() const; //range [-3, +3]
|
||||||
int LuckVal() const; //range [-3, +3]
|
int LuckVal() const; //range [-3, +3]
|
||||||
si32 Attack() const; //get attack of stack with all modificators
|
|
||||||
si32 Defense(bool withFrenzy = true) const; //get defense of stack with all modificators
|
|
||||||
ui32 MaxHealth() const; //get max HP of stack with all modifiers
|
ui32 MaxHealth() const; //get max HP of stack with all modifiers
|
||||||
bool isLiving() const; //non-undead, non-non living or alive
|
bool isLiving() const; //non-undead, non-non living or alive
|
||||||
virtual si32 magicResistance() const;
|
virtual si32 magicResistance() const;
|
||||||
@@ -620,9 +628,11 @@ public:
|
|||||||
|
|
||||||
si32 manaLimit() const; //maximum mana value for this hero (basically 10*knowledge)
|
si32 manaLimit() const; //maximum mana value for this hero (basically 10*knowledge)
|
||||||
int getPrimSkillLevel(PrimarySkill::PrimarySkill id) const;
|
int getPrimSkillLevel(PrimarySkill::PrimarySkill id) const;
|
||||||
|
|
||||||
|
virtual int64_t getTreeVersion() const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DLL_LINKAGE CBonusSystemNode : public IBonusBearer, public boost::noncopyable
|
class DLL_LINKAGE CBonusSystemNode : public virtual IBonusBearer, public boost::noncopyable
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum ENodeTypes
|
enum ENodeTypes
|
||||||
@@ -642,8 +652,8 @@ private:
|
|||||||
|
|
||||||
static const bool cachingEnabled;
|
static const bool cachingEnabled;
|
||||||
mutable BonusList cachedBonuses;
|
mutable BonusList cachedBonuses;
|
||||||
mutable int cachedLast;
|
mutable int64_t cachedLast;
|
||||||
static int treeChanged;
|
static std::atomic<int32_t> treeChanged;
|
||||||
|
|
||||||
// Setting a value to cachingStr before getting any bonuses caches the result for later requests.
|
// Setting a value to cachingStr before getting any bonuses caches the result for later requests.
|
||||||
// This string needs to be unique, that's why it has to be setted in the following manner:
|
// This string needs to be unique, that's why it has to be setted in the following manner:
|
||||||
@@ -656,6 +666,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
explicit CBonusSystemNode();
|
explicit CBonusSystemNode();
|
||||||
|
explicit CBonusSystemNode(ENodeTypes NodeType);
|
||||||
CBonusSystemNode(CBonusSystemNode && other);
|
CBonusSystemNode(CBonusSystemNode && other);
|
||||||
virtual ~CBonusSystemNode();
|
virtual ~CBonusSystemNode();
|
||||||
|
|
||||||
@@ -711,6 +722,8 @@ public:
|
|||||||
|
|
||||||
static void treeHasChanged();
|
static void treeHasChanged();
|
||||||
|
|
||||||
|
int64_t getTreeVersion() const override;
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
// h & bonuses;
|
// h & bonuses;
|
||||||
|
|||||||
@@ -19,6 +19,6 @@ class IBonusTypeHandler
|
|||||||
public:
|
public:
|
||||||
virtual ~IBonusTypeHandler(){};
|
virtual ~IBonusTypeHandler(){};
|
||||||
|
|
||||||
virtual std::string bonusToString(const std::shared_ptr<Bonus>& bonus, const IBonusBearer *bearer, bool description) const = 0;
|
virtual std::string bonusToString(const std::shared_ptr<Bonus> & bonus, const IBonusBearer * bearer, bool description) const = 0;
|
||||||
virtual std::string bonusToGraphics(const std::shared_ptr<Bonus>& bonus) const = 0;
|
virtual std::string bonusToGraphics(const std::shared_ptr<Bonus> & bonus) const = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -32,12 +32,11 @@ struct Bonus;
|
|||||||
class IMarket;
|
class IMarket;
|
||||||
struct SetObjectProperty;
|
struct SetObjectProperty;
|
||||||
struct PackageApplied;
|
struct PackageApplied;
|
||||||
struct BattleAction;
|
class BattleAction;
|
||||||
struct BattleStackAttacked;
|
struct BattleStackAttacked;
|
||||||
struct BattleResult;
|
struct BattleResult;
|
||||||
struct BattleSpellCast;
|
struct BattleSpellCast;
|
||||||
struct CatapultAttack;
|
struct CatapultAttack;
|
||||||
struct BattleStacksRemoved;
|
|
||||||
class CStack;
|
class CStack;
|
||||||
class CCreatureSet;
|
class CCreatureSet;
|
||||||
struct BattleAttack;
|
struct BattleAttack;
|
||||||
@@ -47,6 +46,10 @@ class CComponent;
|
|||||||
struct CObstacleInstance;
|
struct CObstacleInstance;
|
||||||
struct CPackForServer;
|
struct CPackForServer;
|
||||||
class EVictoryLossCheckResult;
|
class EVictoryLossCheckResult;
|
||||||
|
struct MetaString;
|
||||||
|
struct CustomEffectInfo;
|
||||||
|
class ObstacleChanges;
|
||||||
|
class UnitChanges;
|
||||||
|
|
||||||
class DLL_LINKAGE IBattleEventsReceiver
|
class DLL_LINKAGE IBattleEventsReceiver
|
||||||
{
|
{
|
||||||
@@ -54,7 +57,7 @@ public:
|
|||||||
virtual void actionFinished(const BattleAction &action){};//occurs AFTER every action taken by any stack or by the hero
|
virtual void actionFinished(const BattleAction &action){};//occurs AFTER every action taken by any stack or by the hero
|
||||||
virtual void actionStarted(const BattleAction &action){};//occurs BEFORE every action taken by any stack or by the hero
|
virtual void actionStarted(const BattleAction &action){};//occurs BEFORE every action taken by any stack or by the hero
|
||||||
virtual void battleAttack(const BattleAttack *ba){}; //called when stack is performing attack
|
virtual void battleAttack(const BattleAttack *ba){}; //called when stack is performing attack
|
||||||
virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa){}; //called when stack receives damage (after battleAttack())
|
virtual void battleStacksAttacked(const std::vector<BattleStackAttacked> & bsa, const std::vector<MetaString> & battleLog){}; //called when stack receives damage (after battleAttack())
|
||||||
virtual void battleEnd(const BattleResult *br){};
|
virtual void battleEnd(const BattleResult *br){};
|
||||||
virtual void battleNewRoundFirst(int round){}; //called at the beginning of each turn before changes are applied;
|
virtual void battleNewRoundFirst(int round){}; //called at the beginning of each turn before changes are applied;
|
||||||
virtual void battleNewRound(int round){}; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
virtual void battleNewRound(int round){}; //called at the beginning of each turn, round=-1 is the tactic phase, round=0 is the first "normal" turn
|
||||||
@@ -64,12 +67,9 @@ public:
|
|||||||
virtual void battleTriggerEffect(const BattleTriggerEffect & bte){}; //called for various one-shot effects
|
virtual void battleTriggerEffect(const BattleTriggerEffect & bte){}; //called for various one-shot effects
|
||||||
virtual void battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) {}; //called just before battle start
|
virtual void battleStartBefore(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2) {}; //called just before battle start
|
||||||
virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right
|
virtual void battleStart(const CCreatureSet *army1, const CCreatureSet *army2, int3 tile, const CGHeroInstance *hero1, const CGHeroInstance *hero2, bool side){}; //called by engine when battle starts; side=0 - left, side=1 - right
|
||||||
virtual void battleStacksHealedRes(const std::vector<std::pair<ui32, ui32> > & healedStacks, bool lifeDrain, bool tentHeal, si32 lifeDrainFrom){}; //called when stacks are healed / resurrected first element of pair - stack id, second - healed hp
|
virtual void battleUnitsChanged(const std::vector<UnitChanges> & units, const std::vector<CustomEffectInfo> & customEffects, const std::vector<MetaString> & battleLog){};
|
||||||
virtual void battleNewStackAppeared(const CStack * stack){}; //not called at the beginning of a battle or by resurrection; called eg. when elemental is summoned
|
virtual void battleObstaclesChanged(const std::vector<ObstacleChanges> & obstacles){};
|
||||||
virtual void battleObstaclesRemoved(const std::set<si32> & removedObstacles){}; //called when a certain set of obstacles is removed from batlefield; IDs of them are given
|
|
||||||
virtual void battleCatapultAttacked(const CatapultAttack & ca){}; //called when catapult makes an attack
|
virtual void battleCatapultAttacked(const CatapultAttack & ca){}; //called when catapult makes an attack
|
||||||
virtual void battleStacksRemoved(const BattleStacksRemoved & bsr){}; //called when certain stack is completely removed from battlefield
|
|
||||||
virtual void battleObstaclePlaced(const CObstacleInstance &obstacle){};
|
|
||||||
virtual void battleGateStateChanged(const EGateState state){};
|
virtual void battleGateStateChanged(const EGateState state){};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+3
-6
@@ -11,7 +11,6 @@
|
|||||||
|
|
||||||
#include "../lib/ConstTransitivePtr.h"
|
#include "../lib/ConstTransitivePtr.h"
|
||||||
#include "VCMI_Lib.h"
|
#include "VCMI_Lib.h"
|
||||||
//#include "CModHandler.h"
|
|
||||||
|
|
||||||
class JsonNode;
|
class JsonNode;
|
||||||
|
|
||||||
@@ -69,8 +68,7 @@ public:
|
|||||||
void loadObject(std::string scope, std::string name, const JsonNode & data) override
|
void loadObject(std::string scope, std::string name, const JsonNode & data) override
|
||||||
{
|
{
|
||||||
auto type_name = getTypeName();
|
auto type_name = getTypeName();
|
||||||
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
|
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name), objects.size());
|
||||||
object->id = _ObjectID(objects.size());
|
|
||||||
|
|
||||||
objects.push_back(object);
|
objects.push_back(object);
|
||||||
|
|
||||||
@@ -79,8 +77,7 @@ public:
|
|||||||
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override
|
void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override
|
||||||
{
|
{
|
||||||
auto type_name = getTypeName();
|
auto type_name = getTypeName();
|
||||||
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name));
|
auto object = loadFromJson(data, normalizeIdentifier(scope, "core", name), index);
|
||||||
object->id = _ObjectID(index);
|
|
||||||
|
|
||||||
assert(objects[index] == nullptr); // ensure that this id was not loaded before
|
assert(objects[index] == nullptr); // ensure that this id was not loaded before
|
||||||
objects[index] = object;
|
objects[index] = object;
|
||||||
@@ -101,7 +98,7 @@ public:
|
|||||||
return objects[raw_id];
|
return objects[raw_id];
|
||||||
}
|
}
|
||||||
protected:
|
protected:
|
||||||
virtual _Object * loadFromJson(const JsonNode & json, const std::string & identifier) = 0;
|
virtual _Object * loadFromJson(const JsonNode & json, const std::string & identifier, size_t index) = 0;
|
||||||
virtual const std::string getTypeName() const = 0;
|
virtual const std::string getTypeName() const = 0;
|
||||||
public: //todo: make private
|
public: //todo: make private
|
||||||
std::vector<ConstTransitivePtr<_Object>> objects;
|
std::vector<ConstTransitivePtr<_Object>> objects;
|
||||||
|
|||||||
+1
-1
@@ -305,7 +305,7 @@ namespace JsonDetail
|
|||||||
return node.Bool();
|
return node.Bool();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} // namespace JsonDetail
|
}
|
||||||
|
|
||||||
template<typename Type>
|
template<typename Type>
|
||||||
Type JsonNode::convertTo() const
|
Type JsonNode::convertTo() const
|
||||||
|
|||||||
+67
-187
@@ -12,7 +12,6 @@
|
|||||||
#include "NetPacksBase.h"
|
#include "NetPacksBase.h"
|
||||||
|
|
||||||
#include "battle/BattleAction.h"
|
#include "battle/BattleAction.h"
|
||||||
#include "JsonNode.h"
|
|
||||||
#include "mapObjects/CGHeroInstance.h"
|
#include "mapObjects/CGHeroInstance.h"
|
||||||
#include "ConstTransitivePtr.h"
|
#include "ConstTransitivePtr.h"
|
||||||
#include "int3.h"
|
#include "int3.h"
|
||||||
@@ -23,10 +22,6 @@
|
|||||||
|
|
||||||
#include "spells/ViewSpellInt.h"
|
#include "spells/ViewSpellInt.h"
|
||||||
|
|
||||||
class CClient;
|
|
||||||
class CGameState;
|
|
||||||
class CGameHandler;
|
|
||||||
class CConnection;
|
|
||||||
class CCampaignState;
|
class CCampaignState;
|
||||||
class CArtifact;
|
class CArtifact;
|
||||||
class CSelectionScreen;
|
class CSelectionScreen;
|
||||||
@@ -37,45 +32,7 @@ struct ArtSlotInfo;
|
|||||||
struct QuestInfo;
|
struct QuestInfo;
|
||||||
class CMapInfo;
|
class CMapInfo;
|
||||||
struct StartInfo;
|
struct StartInfo;
|
||||||
|
class IBattleState;
|
||||||
struct CPackForClient : public CPack
|
|
||||||
{
|
|
||||||
CPackForClient(){};
|
|
||||||
|
|
||||||
CGameState* GS(CClient *cl);
|
|
||||||
void applyFirstCl(CClient *cl)//called before applying to gs
|
|
||||||
{}
|
|
||||||
void applyCl(CClient *cl)//called after applying to gs
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CPackForServer : public CPack
|
|
||||||
{
|
|
||||||
PlayerColor player;
|
|
||||||
CConnection *c;
|
|
||||||
CGameState* GS(CGameHandler *gh);
|
|
||||||
CPackForServer():
|
|
||||||
player(PlayerColor::NEUTRAL),
|
|
||||||
c(nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool applyGh(CGameHandler *gh) //called after applying to gs
|
|
||||||
{
|
|
||||||
logGlobal->error("Should not happen... applying plain CPackForServer");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void throwNotAllowedAction();
|
|
||||||
void throwOnWrongOwner(CGameHandler * gh, ObjectInstanceID id);
|
|
||||||
void throwOnWrongPlayer(CGameHandler * gh, PlayerColor player);
|
|
||||||
void throwAndCompain(CGameHandler * gh, std::string txt);
|
|
||||||
bool isPlayerOwns(CGameHandler * gh, ObjectInstanceID id);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void wrongPlayerMessage(CGameHandler * gh, PlayerColor expectedplayer);
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Query : public CPackForClient
|
struct Query : public CPackForClient
|
||||||
{
|
{
|
||||||
@@ -1351,7 +1308,7 @@ struct MapObjectSelectDialog : public Query
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BattleInfo;
|
class BattleInfo;
|
||||||
struct BattleStart : public CPackForClient
|
struct BattleStart : public CPackForClient
|
||||||
{
|
{
|
||||||
BattleStart()
|
BattleStart()
|
||||||
@@ -1440,12 +1397,16 @@ struct BattleStackMoved : public CPackForClient
|
|||||||
{
|
{
|
||||||
ui32 stack;
|
ui32 stack;
|
||||||
std::vector<BattleHex> tilesToMove;
|
std::vector<BattleHex> tilesToMove;
|
||||||
ui8 distance, teleporting;
|
int distance;
|
||||||
|
bool teleporting;
|
||||||
BattleStackMoved()
|
BattleStackMoved()
|
||||||
:stack(0), distance(0), teleporting(0)
|
: stack(0),
|
||||||
|
distance(0),
|
||||||
|
teleporting(false)
|
||||||
{};
|
{};
|
||||||
void applyFirstCl(CClient *cl);
|
void applyFirstCl(CClient *cl);
|
||||||
void applyGs(CGameState *gs);
|
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||||
|
DLL_LINKAGE void applyBattle(IBattleState * battleState);
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & stack;
|
h & stack;
|
||||||
@@ -1454,52 +1415,46 @@ struct BattleStackMoved : public CPackForClient
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct StacksHealedOrResurrected : public CPackForClient
|
struct BattleUnitsChanged : public CPackForClient
|
||||||
{
|
{
|
||||||
StacksHealedOrResurrected()
|
BattleUnitsChanged(){}
|
||||||
:lifeDrain(false), tentHealing(false), drainedFrom(0), cure(false)
|
|
||||||
{}
|
|
||||||
|
|
||||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||||
|
DLL_LINKAGE void applyBattle(IBattleState * battleState);
|
||||||
void applyCl(CClient *cl);
|
void applyCl(CClient *cl);
|
||||||
|
|
||||||
std::vector<CHealthInfo> healedStacks;
|
std::vector<UnitChanges> changedStacks;
|
||||||
bool lifeDrain; //if true, this heal is an effect of life drain or soul steal
|
std::vector<MetaString> battleLog;
|
||||||
bool tentHealing; //if true, than it's healing via First Aid Tent
|
std::vector<CustomEffectInfo> customEffects;
|
||||||
si32 drainedFrom; //if life drain or soul steal - then stack life was drain from, if tentHealing - stack that is a healer
|
|
||||||
bool cure; //archangel cast also remove negative effects
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
{
|
{
|
||||||
h & healedStacks;
|
h & changedStacks;
|
||||||
h & lifeDrain;
|
h & battleLog;
|
||||||
h & tentHealing;
|
h & customEffects;
|
||||||
h & drainedFrom;
|
|
||||||
h & cure;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BattleStackAttacked : public CPackForClient
|
struct BattleStackAttacked
|
||||||
{
|
{
|
||||||
BattleStackAttacked():
|
BattleStackAttacked():
|
||||||
stackAttacked(0), attackerID(0),
|
stackAttacked(0), attackerID(0),
|
||||||
killedAmount(0), damageAmount(0),
|
killedAmount(0), damageAmount(0),
|
||||||
newHealth(),
|
newState(),
|
||||||
flags(0), effect(0), spellID(SpellID::NONE)
|
flags(0), effect(0), spellID(SpellID::NONE)
|
||||||
{};
|
{};
|
||||||
void applyFirstCl(CClient * cl);
|
|
||||||
//void applyCl(CClient *cl);
|
|
||||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||||
|
DLL_LINKAGE void applyBattle(IBattleState * battleState);
|
||||||
|
|
||||||
ui32 stackAttacked, attackerID;
|
ui32 stackAttacked, attackerID;
|
||||||
ui32 killedAmount;
|
ui32 killedAmount;
|
||||||
si32 damageAmount;
|
int64_t damageAmount;
|
||||||
CHealthInfo newHealth;
|
UnitChanges newState;
|
||||||
enum EFlags {KILLED = 1, EFFECT = 2/*deprecated */, SECONDARY = 4, REBIRTH = 8, CLONE_KILLED = 16, SPELL_EFFECT = 32 /*, BONUS_EFFECT = 64 */};
|
enum EFlags {KILLED = 1, EFFECT = 2/*deprecated */, SECONDARY = 4, REBIRTH = 8, CLONE_KILLED = 16, SPELL_EFFECT = 32 /*, BONUS_EFFECT = 64 */};
|
||||||
ui32 flags; //uses EFlags (above)
|
ui32 flags; //uses EFlags (above)
|
||||||
ui32 effect; //set only if flag EFFECT is set
|
ui32 effect; //set only if flag EFFECT is set
|
||||||
SpellID spellID; //only if flag SPELL_EFFECT is set
|
SpellID spellID; //only if flag SPELL_EFFECT is set
|
||||||
std::vector<StacksHealedOrResurrected> healedStacks; //used when life drain
|
|
||||||
|
|
||||||
bool killed() const//if target stack was killed
|
bool killed() const//if target stack was killed
|
||||||
{
|
{
|
||||||
@@ -1526,20 +1481,15 @@ struct BattleStackAttacked : public CPackForClient
|
|||||||
{
|
{
|
||||||
return flags & REBIRTH;
|
return flags & REBIRTH;
|
||||||
}
|
}
|
||||||
bool lifeDrain() const //if this attack involves life drain effect
|
|
||||||
{
|
|
||||||
return healedStacks.size() > 0;
|
|
||||||
}
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & stackAttacked;
|
h & stackAttacked;
|
||||||
h & attackerID;
|
h & attackerID;
|
||||||
h & newHealth;
|
h & newState;
|
||||||
h & flags;
|
h & flags;
|
||||||
h & killedAmount;
|
h & killedAmount;
|
||||||
h & damageAmount;
|
h & damageAmount;
|
||||||
h & effect;
|
h & effect;
|
||||||
h & healedStacks;
|
|
||||||
h & spellID;
|
h & spellID;
|
||||||
}
|
}
|
||||||
bool operator<(const BattleStackAttacked &b) const
|
bool operator<(const BattleStackAttacked &b) const
|
||||||
@@ -1557,6 +1507,8 @@ struct BattleAttack : public CPackForClient
|
|||||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
DLL_LINKAGE void applyGs(CGameState *gs);
|
||||||
void applyCl(CClient *cl);
|
void applyCl(CClient *cl);
|
||||||
|
|
||||||
|
BattleUnitsChanged attackerChanges;
|
||||||
|
|
||||||
std::vector<BattleStackAttacked> bsa;
|
std::vector<BattleStackAttacked> bsa;
|
||||||
ui32 stackAttacking;
|
ui32 stackAttacking;
|
||||||
ui32 flags; //uses Eflags (below)
|
ui32 flags; //uses Eflags (below)
|
||||||
@@ -1564,6 +1516,9 @@ struct BattleAttack : public CPackForClient
|
|||||||
|
|
||||||
SpellID spellID; //for SPELL_LIKE
|
SpellID spellID; //for SPELL_LIKE
|
||||||
|
|
||||||
|
std::vector<MetaString> battleLog;
|
||||||
|
std::vector<CustomEffectInfo> customEffects;
|
||||||
|
|
||||||
bool shot() const//distance attack - decrease number of shots
|
bool shot() const//distance attack - decrease number of shots
|
||||||
{
|
{
|
||||||
return flags & SHOT;
|
return flags & SHOT;
|
||||||
@@ -1598,6 +1553,9 @@ struct BattleAttack : public CPackForClient
|
|||||||
h & stackAttacking;
|
h & stackAttacking;
|
||||||
h & flags;
|
h & flags;
|
||||||
h & spellID;
|
h & spellID;
|
||||||
|
h & battleLog;
|
||||||
|
h & customEffects;
|
||||||
|
h & attackerChanges;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1627,24 +1585,9 @@ struct EndAction : public CPackForClient
|
|||||||
|
|
||||||
struct BattleSpellCast : public CPackForClient
|
struct BattleSpellCast : public CPackForClient
|
||||||
{
|
{
|
||||||
///custom effect (resistance, reflection, etc)
|
|
||||||
struct CustomEffect
|
|
||||||
{
|
|
||||||
/// WoG AC format
|
|
||||||
ui32 effect;
|
|
||||||
ui32 stack;
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
|
||||||
{
|
|
||||||
h & effect;
|
|
||||||
h & stack;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
BattleSpellCast()
|
BattleSpellCast()
|
||||||
{
|
{
|
||||||
side = 0;
|
side = 0;
|
||||||
id = 0;
|
|
||||||
skill = 0;
|
|
||||||
manaGained = 0;
|
manaGained = 0;
|
||||||
casterStack = -1;
|
casterStack = -1;
|
||||||
castByHero = true;
|
castByHero = true;
|
||||||
@@ -1655,11 +1598,10 @@ struct BattleSpellCast : public CPackForClient
|
|||||||
|
|
||||||
bool activeCast;
|
bool activeCast;
|
||||||
ui8 side; //which hero did cast spell: 0 - attacker, 1 - defender
|
ui8 side; //which hero did cast spell: 0 - attacker, 1 - defender
|
||||||
ui32 id; //id of spell
|
SpellID spellID; //id of spell
|
||||||
ui8 skill; //caster's skill level
|
|
||||||
ui8 manaGained; //mana channeling ability
|
ui8 manaGained; //mana channeling ability
|
||||||
BattleHex tile; //destination tile (may not be set in some global/mass spells
|
BattleHex tile; //destination tile (may not be set in some global/mass spells
|
||||||
std::vector<CustomEffect> customEffects;
|
std::vector<CustomEffectInfo> customEffects;
|
||||||
std::set<ui32> affectedCres; //ids of creatures affected by this spell, generally used if spell does not set any effect (like dispel or cure)
|
std::set<ui32> affectedCres; //ids of creatures affected by this spell, generally used if spell does not set any effect (like dispel or cure)
|
||||||
si32 casterStack;// -1 if not cated by creature, >=0 caster stack ID
|
si32 casterStack;// -1 if not cated by creature, >=0 caster stack ID
|
||||||
bool castByHero; //if true - spell has been cast by hero, otherwise by a creature
|
bool castByHero; //if true - spell has been cast by hero, otherwise by a creature
|
||||||
@@ -1668,8 +1610,7 @@ struct BattleSpellCast : public CPackForClient
|
|||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & side;
|
h & side;
|
||||||
h & id;
|
h & spellID;
|
||||||
h & skill;
|
|
||||||
h & manaGained;
|
h & manaGained;
|
||||||
h & tile;
|
h & tile;
|
||||||
h & customEffects;
|
h & customEffects;
|
||||||
@@ -1684,27 +1625,20 @@ struct BattleSpellCast : public CPackForClient
|
|||||||
struct SetStackEffect : public CPackForClient
|
struct SetStackEffect : public CPackForClient
|
||||||
{
|
{
|
||||||
SetStackEffect(){};
|
SetStackEffect(){};
|
||||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
DLL_LINKAGE void applyGs(CGameState * gs);
|
||||||
void applyCl(CClient *cl);
|
DLL_LINKAGE void applyBattle(IBattleState * battleState);
|
||||||
|
void applyCl(CClient * cl);
|
||||||
|
|
||||||
std::vector<ui32> stacks; //affected stacks (IDs)
|
std::vector<std::pair<ui32, std::vector<Bonus>>> toAdd;
|
||||||
|
std::vector<std::pair<ui32, std::vector<Bonus>>> toUpdate;
|
||||||
//regular effects
|
std::vector<std::pair<ui32, std::vector<Bonus>>> toRemove;
|
||||||
std::vector<Bonus> effect; //bonuses to apply
|
|
||||||
std::vector<std::pair<ui32, Bonus> > uniqueBonuses; //bonuses per single stack
|
|
||||||
|
|
||||||
//cumulative effects
|
|
||||||
std::vector<Bonus> cumulativeEffects; //bonuses to apply
|
|
||||||
std::vector<std::pair<ui32, Bonus> > cumulativeUniqueBonuses; //bonuses per single stack
|
|
||||||
|
|
||||||
std::vector<MetaString> battleLog;
|
std::vector<MetaString> battleLog;
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
{
|
{
|
||||||
h & stacks;
|
h & toAdd;
|
||||||
h & effect;
|
h & toUpdate;
|
||||||
h & uniqueBonuses;
|
h & toRemove;
|
||||||
h & cumulativeEffects;
|
|
||||||
h & cumulativeUniqueBonuses;
|
|
||||||
h & battleLog;
|
h & battleLog;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -1712,13 +1646,18 @@ struct SetStackEffect : public CPackForClient
|
|||||||
struct StacksInjured : public CPackForClient
|
struct StacksInjured : public CPackForClient
|
||||||
{
|
{
|
||||||
StacksInjured(){}
|
StacksInjured(){}
|
||||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
DLL_LINKAGE void applyGs(CGameState * gs);
|
||||||
void applyCl(CClient *cl);
|
DLL_LINKAGE void applyBattle(IBattleState * battleState);
|
||||||
|
|
||||||
|
void applyCl(CClient * cl);
|
||||||
|
|
||||||
std::vector<BattleStackAttacked> stacks;
|
std::vector<BattleStackAttacked> stacks;
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
std::vector<MetaString> battleLog;
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
{
|
{
|
||||||
h & stacks;
|
h & stacks;
|
||||||
|
h & battleLog;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1736,18 +1675,19 @@ struct BattleResultsApplied : public CPackForClient
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ObstaclesRemoved : public CPackForClient
|
struct BattleObstaclesChanged : public CPackForClient
|
||||||
{
|
{
|
||||||
ObstaclesRemoved(){}
|
BattleObstaclesChanged(){}
|
||||||
|
|
||||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
DLL_LINKAGE void applyGs(CGameState * gs);
|
||||||
void applyCl(CClient *cl);
|
DLL_LINKAGE void applyBattle(IBattleState * battleState);
|
||||||
|
void applyCl(CClient * cl);
|
||||||
|
|
||||||
std::set<si32> obstacles; //uniqueIDs of removed obstacles
|
std::vector<ObstacleChanges> changes;
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
{
|
{
|
||||||
h & obstacles;
|
h & changes;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1759,9 +1699,7 @@ struct ELF_VISIBILITY CatapultAttack : public CPackForClient
|
|||||||
ui8 attackedPart;
|
ui8 attackedPart;
|
||||||
ui8 damageDealt;
|
ui8 damageDealt;
|
||||||
|
|
||||||
DLL_LINKAGE std::string toString() const;
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
|
||||||
{
|
{
|
||||||
h & destinationTile;
|
h & destinationTile;
|
||||||
h & attackedPart;
|
h & attackedPart;
|
||||||
@@ -1772,9 +1710,9 @@ struct ELF_VISIBILITY CatapultAttack : public CPackForClient
|
|||||||
DLL_LINKAGE CatapultAttack();
|
DLL_LINKAGE CatapultAttack();
|
||||||
DLL_LINKAGE ~CatapultAttack();
|
DLL_LINKAGE ~CatapultAttack();
|
||||||
|
|
||||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
DLL_LINKAGE void applyGs(CGameState * gs);
|
||||||
void applyCl(CClient *cl);
|
DLL_LINKAGE void applyBattle(IBattleState * battleState);
|
||||||
DLL_LINKAGE std::string toString() const override;
|
void applyCl(CClient * cl);
|
||||||
|
|
||||||
std::vector< AttackInfo > attackedParts;
|
std::vector< AttackInfo > attackedParts;
|
||||||
int attacker; //if -1, then a spell caused this
|
int attacker; //if -1, then a spell caused this
|
||||||
@@ -1786,49 +1724,6 @@ struct ELF_VISIBILITY CatapultAttack : public CPackForClient
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BattleStacksRemoved : public CPackForClient
|
|
||||||
{
|
|
||||||
BattleStacksRemoved(){}
|
|
||||||
|
|
||||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
|
||||||
void applyFirstCl(CClient *cl);//inform client before stack objects are destroyed
|
|
||||||
|
|
||||||
std::set<ui32> stackIDs; //IDs of removed stacks
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
|
||||||
{
|
|
||||||
h & stackIDs;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BattleStackAdded : public CPackForClient
|
|
||||||
{
|
|
||||||
BattleStackAdded()
|
|
||||||
: side(0), amount(0), pos(0), summoned(0), newStackID(0)
|
|
||||||
{};
|
|
||||||
|
|
||||||
DLL_LINKAGE void applyGs(CGameState *gs);
|
|
||||||
void applyCl(CClient *cl);
|
|
||||||
|
|
||||||
ui8 side;
|
|
||||||
CreatureID creID;
|
|
||||||
int amount;
|
|
||||||
int pos;
|
|
||||||
int summoned; //if true, remove it afterwards
|
|
||||||
|
|
||||||
///Actual stack ID, set on apply, do not serialize
|
|
||||||
int newStackID;
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
|
||||||
{
|
|
||||||
h & side;
|
|
||||||
h & creID;
|
|
||||||
h & amount;
|
|
||||||
h & pos;
|
|
||||||
h & summoned;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BattleSetStackProperty : public CPackForClient
|
struct BattleSetStackProperty : public CPackForClient
|
||||||
{
|
{
|
||||||
BattleSetStackProperty()
|
BattleSetStackProperty()
|
||||||
@@ -1877,21 +1772,6 @@ struct BattleTriggerEffect : public CPackForClient
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BattleObstaclePlaced : public CPackForClient
|
|
||||||
{
|
|
||||||
BattleObstaclePlaced(){};
|
|
||||||
|
|
||||||
DLL_LINKAGE void applyGs(CGameState *gs); //effect
|
|
||||||
void applyCl(CClient *cl); //play animations & stuff
|
|
||||||
|
|
||||||
std::shared_ptr<CObstacleInstance> obstacle;
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
|
||||||
{
|
|
||||||
h & obstacle;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BattleUpdateGateState : public CPackForClient
|
struct BattleUpdateGateState : public CPackForClient
|
||||||
{
|
{
|
||||||
BattleUpdateGateState():state(EGateState::NONE){};
|
BattleUpdateGateState():state(EGateState::NONE){};
|
||||||
|
|||||||
+139
-17
@@ -9,7 +9,10 @@
|
|||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
class CClient;
|
||||||
class CGameState;
|
class CGameState;
|
||||||
|
class CGameHandler;
|
||||||
|
class CConnection;
|
||||||
class CStackBasicDescriptor;
|
class CStackBasicDescriptor;
|
||||||
class CGHeroInstance;
|
class CGHeroInstance;
|
||||||
class CStackInstance;
|
class CStackInstance;
|
||||||
@@ -17,9 +20,11 @@ class CArmedInstance;
|
|||||||
class CArtifactSet;
|
class CArtifactSet;
|
||||||
class CBonusSystemNode;
|
class CBonusSystemNode;
|
||||||
struct ArtSlotInfo;
|
struct ArtSlotInfo;
|
||||||
|
class BattleInfo;
|
||||||
|
|
||||||
#include "ConstTransitivePtr.h"
|
#include "ConstTransitivePtr.h"
|
||||||
#include "GameConstants.h"
|
#include "GameConstants.h"
|
||||||
|
#include "JsonNode.h"
|
||||||
|
|
||||||
struct DLL_LINKAGE CPack
|
struct DLL_LINKAGE CPack
|
||||||
{
|
{
|
||||||
@@ -31,11 +36,49 @@ struct DLL_LINKAGE CPack
|
|||||||
logNetwork->error("CPack serialized... this should not happen!");
|
logNetwork->error("CPack serialized... this should not happen!");
|
||||||
assert(false && "CPack serialized");
|
assert(false && "CPack serialized");
|
||||||
}
|
}
|
||||||
void applyGs(CGameState *gs) { }
|
|
||||||
virtual std::string toString() const { return boost::str(boost::format("{CPack: type '%s'}") % typeid(this).name()); }
|
void applyGs(CGameState * gs)
|
||||||
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::ostream & operator<<(std::ostream & out, const CPack * pack);
|
struct CPackForClient : public CPack
|
||||||
|
{
|
||||||
|
CPackForClient(){};
|
||||||
|
|
||||||
|
CGameState* GS(CClient *cl);
|
||||||
|
void applyFirstCl(CClient *cl)//called before applying to gs
|
||||||
|
{}
|
||||||
|
void applyCl(CClient *cl)//called after applying to gs
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CPackForServer : public CPack
|
||||||
|
{
|
||||||
|
PlayerColor player;
|
||||||
|
CConnection *c;
|
||||||
|
CGameState* GS(CGameHandler *gh);
|
||||||
|
CPackForServer():
|
||||||
|
player(PlayerColor::NEUTRAL),
|
||||||
|
c(nullptr)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool applyGh(CGameHandler *gh) //called after applying to gs
|
||||||
|
{
|
||||||
|
logGlobal->error("Should not happen... applying plain CPackForServer");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void throwNotAllowedAction();
|
||||||
|
void throwOnWrongOwner(CGameHandler * gh, ObjectInstanceID id);
|
||||||
|
void throwOnWrongPlayer(CGameHandler * gh, PlayerColor player);
|
||||||
|
void throwAndCompain(CGameHandler * gh, std::string txt);
|
||||||
|
bool isPlayerOwns(CGameHandler * gh, ObjectInstanceID id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void wrongPlayerMessage(CGameHandler * gh, PlayerColor expectedplayer);
|
||||||
|
};
|
||||||
|
|
||||||
struct DLL_LINKAGE MetaString
|
struct DLL_LINKAGE MetaString
|
||||||
{
|
{
|
||||||
@@ -196,25 +239,104 @@ struct ArtifactLocation
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class CHealthInfo
|
///custom effect (resistance, reflection, etc)
|
||||||
|
struct CustomEffectInfo
|
||||||
{
|
{
|
||||||
public:
|
CustomEffectInfo()
|
||||||
CHealthInfo():
|
:effect(0),
|
||||||
stackId(0), delta(0), firstHPleft(0), fullUnits(0), resurrected(0)
|
sound(0),
|
||||||
|
stack(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
/// WoG AC format
|
||||||
|
ui32 effect;
|
||||||
|
ui32 sound;
|
||||||
|
ui32 stack;
|
||||||
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
|
{
|
||||||
|
h & effect;
|
||||||
|
h & sound;
|
||||||
|
h & stack;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class BattleChanges
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class EOperation : si8
|
||||||
|
{
|
||||||
|
ADD,
|
||||||
|
RESET_STATE,
|
||||||
|
UPDATE,
|
||||||
|
REMOVE
|
||||||
|
};
|
||||||
|
|
||||||
|
JsonNode data;
|
||||||
|
EOperation operation;
|
||||||
|
|
||||||
|
BattleChanges()
|
||||||
|
: operation(EOperation::RESET_STATE),
|
||||||
|
data()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BattleChanges(EOperation operation_)
|
||||||
|
: operation(operation_),
|
||||||
|
data()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class UnitChanges : public BattleChanges
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint32_t id;
|
||||||
|
int64_t healthDelta;
|
||||||
|
|
||||||
|
UnitChanges()
|
||||||
|
: BattleChanges(EOperation::RESET_STATE),
|
||||||
|
id(0),
|
||||||
|
healthDelta(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
UnitChanges(uint32_t id_, EOperation operation_)
|
||||||
|
: BattleChanges(operation_),
|
||||||
|
id(id_),
|
||||||
|
healthDelta(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
uint32_t stackId;
|
|
||||||
int32_t delta;
|
|
||||||
int32_t firstHPleft;
|
|
||||||
int32_t fullUnits;
|
|
||||||
int32_t resurrected;
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler & h, const int version)
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
{
|
{
|
||||||
h & stackId;
|
h & id;
|
||||||
h & delta;
|
h & healthDelta;
|
||||||
h & firstHPleft;
|
h & data;
|
||||||
h & fullUnits;
|
h & operation;
|
||||||
h & resurrected;
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class ObstacleChanges : public BattleChanges
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
uint32_t id;
|
||||||
|
|
||||||
|
ObstacleChanges()
|
||||||
|
: BattleChanges(EOperation::RESET_STATE),
|
||||||
|
id(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
ObstacleChanges(uint32_t id_, EOperation operation_)
|
||||||
|
: BattleChanges(operation_),
|
||||||
|
id(id_)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
|
{
|
||||||
|
h & id;
|
||||||
|
h & data;
|
||||||
|
h & operation;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
+109
-396
@@ -32,11 +32,6 @@
|
|||||||
#undef max
|
#undef max
|
||||||
|
|
||||||
|
|
||||||
std::ostream & operator<<(std::ostream & out, const CPack * pack)
|
|
||||||
{
|
|
||||||
return out << (pack? pack->toString() : "<nullptr>");
|
|
||||||
}
|
|
||||||
|
|
||||||
DLL_LINKAGE void SetResources::applyGs(CGameState *gs)
|
DLL_LINKAGE void SetResources::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
assert(player < PlayerColor::PLAYER_LIMIT);
|
assert(player < PlayerColor::PLAYER_LIMIT);
|
||||||
@@ -1232,50 +1227,12 @@ DLL_LINKAGE void BattleStart::applyGs(CGameState *gs)
|
|||||||
|
|
||||||
DLL_LINKAGE void BattleNextRound::applyGs(CGameState *gs)
|
DLL_LINKAGE void BattleNextRound::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 2; ++i)
|
gs->curB->nextRound(round);
|
||||||
{
|
|
||||||
gs->curB->sides[i].castSpellsCount = 0;
|
|
||||||
vstd::amax(--gs->curB->sides[i].enchanterCounter, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
gs->curB->round = round;
|
|
||||||
|
|
||||||
for(CStack *s : gs->curB->stacks)
|
|
||||||
{
|
|
||||||
s->state -= EBattleStackState::DEFENDING;
|
|
||||||
s->state -= EBattleStackState::WAITING;
|
|
||||||
s->state -= EBattleStackState::MOVED;
|
|
||||||
s->state -= EBattleStackState::HAD_MORALE;
|
|
||||||
s->state -= EBattleStackState::FEAR;
|
|
||||||
s->state -= EBattleStackState::DRAINED_MANA;
|
|
||||||
s->counterAttacks.reset();
|
|
||||||
// new turn effects
|
|
||||||
s->updateBonuses(Bonus::NTurns);
|
|
||||||
|
|
||||||
if(s->alive() && s->isClone())
|
|
||||||
{
|
|
||||||
//cloned stack has special lifetime marker
|
|
||||||
//check it after bonuses updated in battleTurnPassed()
|
|
||||||
|
|
||||||
if(!s->hasBonus(Selector::type(Bonus::NONE).And(Selector::source(Bonus::SPELL_EFFECT, SpellID::CLONE))))
|
|
||||||
s->makeGhost();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto &obst : gs->curB->obstacles)
|
|
||||||
obst->battleTurnPassed();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void BattleSetActiveStack::applyGs(CGameState *gs)
|
DLL_LINKAGE void BattleSetActiveStack::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
gs->curB->activeStack = stack;
|
gs->curB->nextTurn(stack);
|
||||||
CStack *st = gs->curB->getStack(stack);
|
|
||||||
|
|
||||||
//remove bonuses that last until when stack gets new turn
|
|
||||||
st->popBonuses(Bonus::UntilGetsTurn);
|
|
||||||
|
|
||||||
if(vstd::contains(st->state,EBattleStackState::MOVED)) //if stack is moving second time this turn it must had a high morale bonus
|
|
||||||
st->state.insert(EBattleStackState::HAD_MORALE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void BattleTriggerEffect::applyGs(CGameState *gs)
|
DLL_LINKAGE void BattleTriggerEffect::applyGs(CGameState *gs)
|
||||||
@@ -1286,15 +1243,14 @@ DLL_LINKAGE void BattleTriggerEffect::applyGs(CGameState *gs)
|
|||||||
{
|
{
|
||||||
case Bonus::HP_REGENERATION:
|
case Bonus::HP_REGENERATION:
|
||||||
{
|
{
|
||||||
int32_t toHeal = val;
|
int64_t toHeal = val;
|
||||||
CHealth health = st->healthAfterHealed(toHeal, EHealLevel::HEAL, EHealPower::PERMANENT);
|
st->heal(toHeal, EHealLevel::HEAL, EHealPower::PERMANENT);
|
||||||
st->setHealth(health);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Bonus::MANA_DRAIN:
|
case Bonus::MANA_DRAIN:
|
||||||
{
|
{
|
||||||
CGHeroInstance * h = gs->getHero(ObjectInstanceID(additionalInfo));
|
CGHeroInstance * h = gs->getHero(ObjectInstanceID(additionalInfo));
|
||||||
st->state.insert (EBattleStackState::DRAINED_MANA);
|
st->drainedMana = true;
|
||||||
h->mana -= val;
|
h->mana -= val;
|
||||||
vstd::amax(h->mana, 0);
|
vstd::amax(h->mana, 0);
|
||||||
break;
|
break;
|
||||||
@@ -1310,18 +1266,13 @@ DLL_LINKAGE void BattleTriggerEffect::applyGs(CGameState *gs)
|
|||||||
case Bonus::ENCHANTER:
|
case Bonus::ENCHANTER:
|
||||||
break;
|
break;
|
||||||
case Bonus::FEAR:
|
case Bonus::FEAR:
|
||||||
st->state.insert(EBattleStackState::FEAR);
|
st->fear = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
logNetwork->error("Unrecognized trigger effect type %d", effect);
|
logNetwork->error("Unrecognized trigger effect type %d", effect);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void BattleObstaclePlaced::applyGs(CGameState *gs)
|
|
||||||
{
|
|
||||||
gs->curB->obstacles.push_back(obstacle);
|
|
||||||
}
|
|
||||||
|
|
||||||
DLL_LINKAGE void BattleUpdateGateState::applyGs(CGameState *gs)
|
DLL_LINKAGE void BattleUpdateGateState::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
if(gs->curB)
|
if(gs->curB)
|
||||||
@@ -1330,15 +1281,6 @@ DLL_LINKAGE void BattleUpdateGateState::applyGs(CGameState *gs)
|
|||||||
|
|
||||||
void BattleResult::applyGs(CGameState *gs)
|
void BattleResult::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
for (CStack *s : gs->curB->stacks)
|
|
||||||
{
|
|
||||||
if (s->base && s->base->armyObj && vstd::contains(s->state, EBattleStackState::SUMMONED))
|
|
||||||
{
|
|
||||||
//stack with SUMMONED flag but coming from garrison -> most likely resurrected, needs to be removed
|
|
||||||
assert(&s->base->armyObj->getStack(s->slot) == s->base);
|
|
||||||
const_cast<CArmedInstance*>(s->base->armyObj)->eraseStack(s->slot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto & elem : gs->curB->stacks)
|
for (auto & elem : gs->curB->stacks)
|
||||||
delete elem;
|
delete elem;
|
||||||
|
|
||||||
@@ -1373,90 +1315,24 @@ void BattleResult::applyGs(CGameState *gs)
|
|||||||
gs->curB.dellNull();
|
gs->curB.dellNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BattleStackMoved::applyGs(CGameState *gs)
|
DLL_LINKAGE void BattleStackMoved::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
CStack *s = gs->curB->getStack(stack);
|
applyBattle(gs->curB);
|
||||||
assert(s);
|
|
||||||
BattleHex dest = tilesToMove.back();
|
|
||||||
|
|
||||||
//if unit ended movement on quicksands that were created by enemy, that quicksand patch becomes visible for owner
|
|
||||||
for(auto &oi : gs->curB->obstacles)
|
|
||||||
{
|
|
||||||
if(oi->obstacleType == CObstacleInstance::QUICKSAND
|
|
||||||
&& vstd::contains(oi->getAffectedTiles(), tilesToMove.back()))
|
|
||||||
{
|
|
||||||
SpellCreatedObstacle *sands = dynamic_cast<SpellCreatedObstacle*>(oi.get());
|
|
||||||
assert(sands);
|
|
||||||
if(sands->casterSide != s->side)
|
|
||||||
sands->visibleForAnotherSide = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s->position = dest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void BattleStackAttacked::applyGs(CGameState *gs)
|
DLL_LINKAGE void BattleStackMoved::applyBattle(IBattleState * battleState)
|
||||||
{
|
{
|
||||||
CStack * at = gs->curB->getStack(stackAttacked);
|
battleState->moveUnit(stack, tilesToMove.back());
|
||||||
assert(at);
|
}
|
||||||
at->popBonuses(Bonus::UntilBeingAttacked);
|
|
||||||
|
|
||||||
if(willRebirth())
|
DLL_LINKAGE void BattleStackAttacked::applyGs(CGameState * gs)
|
||||||
at->health.reset();//kill stack first
|
{
|
||||||
else
|
applyBattle(gs->curB);
|
||||||
at->setHealth(newHealth);
|
}
|
||||||
|
|
||||||
if(killed())
|
DLL_LINKAGE void BattleStackAttacked::applyBattle(IBattleState * battleState)
|
||||||
{
|
{
|
||||||
at->state -= EBattleStackState::ALIVE;
|
battleState->setUnitState(newState.id, newState.data, newState.healthDelta);
|
||||||
|
|
||||||
if(at->cloneID >= 0)
|
|
||||||
{
|
|
||||||
//remove clone as well
|
|
||||||
CStack * clone = gs->curB->getStack(at->cloneID);
|
|
||||||
if(clone)
|
|
||||||
clone->makeGhost();
|
|
||||||
|
|
||||||
at->cloneID = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//life drain handling
|
|
||||||
for(auto & elem : healedStacks)
|
|
||||||
elem.applyGs(gs);
|
|
||||||
|
|
||||||
if(willRebirth())
|
|
||||||
{
|
|
||||||
//TODO: handle rebirth with StacksHealedOrResurrected
|
|
||||||
at->casts.use();
|
|
||||||
at->state.insert(EBattleStackState::ALIVE);
|
|
||||||
at->setHealth(newHealth);
|
|
||||||
|
|
||||||
//removing all spells effects
|
|
||||||
auto selector = [](const Bonus * b)
|
|
||||||
{
|
|
||||||
//Special case: DISRUPTING_RAY is "immune" to dispell
|
|
||||||
//Other even PERMANENT effects can be removed
|
|
||||||
if(b->source == Bonus::SPELL_EFFECT)
|
|
||||||
return b->sid != SpellID::DISRUPTING_RAY;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
at->popBonuses(selector);
|
|
||||||
}
|
|
||||||
if(cloneKilled())
|
|
||||||
{
|
|
||||||
//"hide" killed creatures instead so we keep info about it
|
|
||||||
at->makeGhost();
|
|
||||||
|
|
||||||
for(CStack * s : gs->curB->stacks)
|
|
||||||
{
|
|
||||||
if(s->cloneID == at->ID)
|
|
||||||
s->cloneID = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//killed summoned creature should be removed like clone
|
|
||||||
if(killed() && vstd::contains(at->state, EBattleStackState::SUMMONED))
|
|
||||||
at->makeGhost();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void BattleAttack::applyGs(CGameState * gs)
|
DLL_LINKAGE void BattleAttack::applyGs(CGameState * gs)
|
||||||
@@ -1464,11 +1340,7 @@ DLL_LINKAGE void BattleAttack::applyGs(CGameState * gs)
|
|||||||
CStack * attacker = gs->curB->getStack(stackAttacking);
|
CStack * attacker = gs->curB->getStack(stackAttacking);
|
||||||
assert(attacker);
|
assert(attacker);
|
||||||
|
|
||||||
if(counter())
|
attackerChanges.applyGs(gs);
|
||||||
attacker->counterAttacks.use();
|
|
||||||
|
|
||||||
if(shot())
|
|
||||||
attacker->shots.use();
|
|
||||||
|
|
||||||
for(BattleStackAttacked & stackAttacked : bsa)
|
for(BattleStackAttacked & stackAttacked : bsa)
|
||||||
stackAttacked.applyGs(gs);
|
stackAttacked.applyGs(gs);
|
||||||
@@ -1480,7 +1352,7 @@ DLL_LINKAGE void StartAction::applyGs(CGameState *gs)
|
|||||||
{
|
{
|
||||||
CStack *st = gs->curB->getStack(ba.stackNumber);
|
CStack *st = gs->curB->getStack(ba.stackNumber);
|
||||||
|
|
||||||
if(ba.actionType == Battle::END_TACTIC_PHASE)
|
if(ba.actionType == EActionType::END_TACTIC_PHASE)
|
||||||
{
|
{
|
||||||
gs->curB->tacticDistance = 0;
|
gs->curB->tacticDistance = 0;
|
||||||
return;
|
return;
|
||||||
@@ -1493,216 +1365,130 @@ DLL_LINKAGE void StartAction::applyGs(CGameState *gs)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ba.actionType != Battle::HERO_SPELL) //don't check for stack if it's custom action by hero
|
if(ba.actionType != EActionType::HERO_SPELL) //don't check for stack if it's custom action by hero
|
||||||
{
|
{
|
||||||
assert(st);
|
assert(st);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gs->curB->sides[ba.side].usedSpellsHistory.push_back(SpellID(ba.additionalInfo).toSpell());
|
gs->curB->sides[ba.side].usedSpellsHistory.push_back(SpellID(ba.actionSubtype).toSpell());
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(ba.actionType)
|
switch(ba.actionType)
|
||||||
{
|
{
|
||||||
case Battle::DEFEND:
|
case EActionType::DEFEND:
|
||||||
st->state -= EBattleStackState::DEFENDING_ANIM;
|
st->waiting = false;
|
||||||
st->state.insert(EBattleStackState::DEFENDING);
|
st->defending = true;
|
||||||
st->state.insert(EBattleStackState::DEFENDING_ANIM);
|
st->defendingAnim = true;
|
||||||
|
break;
|
||||||
|
case EActionType::WAIT:
|
||||||
|
st->defendingAnim = false;
|
||||||
|
st->waiting = true;
|
||||||
|
break;
|
||||||
|
case EActionType::HERO_SPELL: //no change in current stack state
|
||||||
break;
|
break;
|
||||||
case Battle::WAIT:
|
|
||||||
st->state -= EBattleStackState::DEFENDING_ANIM;
|
|
||||||
st->state.insert(EBattleStackState::WAITING);
|
|
||||||
return;
|
|
||||||
case Battle::HERO_SPELL: //no change in current stack state
|
|
||||||
return;
|
|
||||||
default: //any active stack action - attack, catapult, heal, spell...
|
default: //any active stack action - attack, catapult, heal, spell...
|
||||||
st->state -= EBattleStackState::DEFENDING_ANIM;
|
st->waiting = false;
|
||||||
st->state.insert(EBattleStackState::MOVED);
|
st->defendingAnim = false;
|
||||||
|
st->movedThisRound = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(st)
|
|
||||||
st->state -= EBattleStackState::WAITING; //if stack was waiting it has made move, so it won't be "waiting" anymore (if the action was WAIT, then we have returned)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void BattleSpellCast::applyGs(CGameState *gs)
|
DLL_LINKAGE void BattleSpellCast::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
assert(gs->curB);
|
assert(gs->curB);
|
||||||
|
|
||||||
const CSpell * spell = SpellID(id).toSpell();
|
if(castByHero)
|
||||||
|
|
||||||
spell->applyBattle(gs->curB, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void actualizeEffect(CStack * s, const Bonus & ef)
|
|
||||||
{
|
|
||||||
for(auto stackBonus : s->getBonusList()) //TODO: optimize
|
|
||||||
{
|
{
|
||||||
if(stackBonus->source == Bonus::SPELL_EFFECT && stackBonus->type == ef.type && stackBonus->subtype == ef.subtype)
|
if(side < 2)
|
||||||
{
|
{
|
||||||
stackBonus->turnsRemain = std::max(stackBonus->turnsRemain, ef.turnsRemain);
|
gs->curB->sides[side].castSpellsCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CBonusSystemNode::treeHasChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void actualizeEffect(CStack * s, const std::vector<Bonus> & ef)
|
|
||||||
{
|
|
||||||
//actualizing features vector
|
|
||||||
|
|
||||||
for(const Bonus &fromEffect : ef)
|
|
||||||
{
|
|
||||||
actualizeEffect(s, fromEffect);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void SetStackEffect::applyGs(CGameState *gs)
|
DLL_LINKAGE void SetStackEffect::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
if(effect.empty() && cumulativeEffects.empty())
|
applyBattle(gs->curB);
|
||||||
{
|
|
||||||
logGlobal->error("Trying to apply SetStackEffect with no effects");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
si32 spellid = effect.empty() ? cumulativeEffects.begin()->sid : effect.begin()->sid; //effects' source ID
|
|
||||||
|
|
||||||
auto processEffect = [spellid, this](CStack * sta, const Bonus & effect, bool cumulative)
|
|
||||||
{
|
|
||||||
if(cumulative || !sta->hasBonus(Selector::source(Bonus::SPELL_EFFECT, spellid).And(Selector::typeSubtype(effect.type, effect.subtype))))
|
|
||||||
{
|
|
||||||
//no such effect or cumulative - add new
|
|
||||||
logBonus->trace("%s receives a new bonus: %s", sta->nodeName(), effect.Description());
|
|
||||||
sta->addNewBonus(std::make_shared<Bonus>(effect));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logBonus->trace("%s updated bonus: %s", sta->nodeName(), effect.Description());
|
|
||||||
actualizeEffect(sta, effect);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
for(ui32 id : stacks)
|
|
||||||
{
|
|
||||||
CStack *s = gs->curB->getStack(id);
|
|
||||||
if(s)
|
|
||||||
{
|
|
||||||
for(const Bonus & fromEffect : effect)
|
|
||||||
processEffect(s, fromEffect, false);
|
|
||||||
for(const Bonus & fromEffect : cumulativeEffects)
|
|
||||||
processEffect(s, fromEffect, true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
logNetwork->error("Cannot find stack %d", id);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto & para : uniqueBonuses)
|
|
||||||
{
|
|
||||||
CStack *s = gs->curB->getStack(para.first);
|
|
||||||
if(s)
|
|
||||||
processEffect(s, para.second, false);
|
|
||||||
else
|
|
||||||
logNetwork->error("Cannot find stack %d", para.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
for(auto & para : cumulativeUniqueBonuses)
|
|
||||||
{
|
|
||||||
CStack *s = gs->curB->getStack(para.first);
|
|
||||||
if(s)
|
|
||||||
processEffect(s, para.second, true);
|
|
||||||
else
|
|
||||||
logNetwork->error("Cannot find stack %d", para.first);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DLL_LINKAGE void SetStackEffect::applyBattle(IBattleState * battleState)
|
||||||
|
{
|
||||||
|
for(const auto & stackData : toRemove)
|
||||||
|
battleState->removeUnitBonus(stackData.first, stackData.second);
|
||||||
|
|
||||||
|
for(const auto & stackData : toUpdate)
|
||||||
|
battleState->updateUnitBonus(stackData.first, stackData.second);
|
||||||
|
|
||||||
|
for(const auto & stackData : toAdd)
|
||||||
|
battleState->addUnitBonus(stackData.first, stackData.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
DLL_LINKAGE void StacksInjured::applyGs(CGameState *gs)
|
DLL_LINKAGE void StacksInjured::applyGs(CGameState *gs)
|
||||||
|
{
|
||||||
|
applyBattle(gs->curB);
|
||||||
|
}
|
||||||
|
|
||||||
|
DLL_LINKAGE void StacksInjured::applyBattle(IBattleState * battleState)
|
||||||
{
|
{
|
||||||
for(BattleStackAttacked stackAttacked : stacks)
|
for(BattleStackAttacked stackAttacked : stacks)
|
||||||
stackAttacked.applyGs(gs);
|
stackAttacked.applyBattle(battleState);
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void StacksHealedOrResurrected::applyGs(CGameState *gs)
|
DLL_LINKAGE void BattleUnitsChanged::applyGs(CGameState *gs)
|
||||||
{
|
{
|
||||||
for(auto & elem : healedStacks)
|
applyBattle(gs->curB);
|
||||||
|
}
|
||||||
|
|
||||||
|
DLL_LINKAGE void BattleUnitsChanged::applyBattle(IBattleState * battleState)
|
||||||
|
{
|
||||||
|
for(auto & elem : changedStacks)
|
||||||
{
|
{
|
||||||
CStack * changedStack = gs->curB->getStack(elem.stackId, false);
|
switch(elem.operation)
|
||||||
assert(changedStack);
|
|
||||||
|
|
||||||
//checking if we resurrect a stack that is under a living stack
|
|
||||||
auto accessibility = gs->curB->getAccesibility();
|
|
||||||
|
|
||||||
if(!changedStack->alive() && !accessibility.accessible(changedStack->position, changedStack))
|
|
||||||
{
|
{
|
||||||
logNetwork->error("Cannot resurrect %s because hex %d is occupied!", changedStack->nodeName(), changedStack->position.hex);
|
case BattleChanges::EOperation::RESET_STATE:
|
||||||
return; //position is already occupied
|
battleState->setUnitState(elem.id, elem.data, elem.healthDelta);
|
||||||
}
|
break;
|
||||||
|
case BattleChanges::EOperation::REMOVE:
|
||||||
//applying changes
|
battleState->removeUnit(elem.id);
|
||||||
bool resurrected = !changedStack->alive(); //indicates if stack is resurrected or just healed
|
break;
|
||||||
if(resurrected)
|
case BattleChanges::EOperation::ADD:
|
||||||
{
|
battleState->addUnit(elem.id, elem.data);
|
||||||
if(auto totalHealth = changedStack->health.available())
|
break;
|
||||||
logGlobal->warn("Dead stack %s with positive total HP %d", changedStack->nodeName(), totalHealth);
|
default:
|
||||||
|
logNetwork->error("Unknown unit operation %d", (int)elem.operation);
|
||||||
changedStack->state.insert(EBattleStackState::ALIVE);
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
changedStack->setHealth(elem);
|
|
||||||
|
|
||||||
if(resurrected)
|
|
||||||
{
|
|
||||||
//removing all spells effects
|
|
||||||
auto selector = [](const Bonus * b)
|
|
||||||
{
|
|
||||||
//Special case: DISRUPTING_RAY is "immune" to dispell
|
|
||||||
//Other even PERMANENT effects can be removed
|
|
||||||
if(b->source == Bonus::SPELL_EFFECT)
|
|
||||||
return b->sid != SpellID::DISRUPTING_RAY;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
changedStack->popBonuses(selector);
|
|
||||||
}
|
|
||||||
else if(cure)
|
|
||||||
{
|
|
||||||
//removing all effects from negative spells
|
|
||||||
auto selector = [](const Bonus * b)
|
|
||||||
{
|
|
||||||
//Special case: DISRUPTING_RAY is "immune" to dispell
|
|
||||||
//Other even PERMANENT effects can be removed
|
|
||||||
if(b->source == Bonus::SPELL_EFFECT)
|
|
||||||
{
|
|
||||||
const CSpell * sourceSpell = SpellID(b->sid).toSpell();
|
|
||||||
if(!sourceSpell)
|
|
||||||
return false;
|
|
||||||
return sourceSpell->id != SpellID::DISRUPTING_RAY && sourceSpell->isNegative();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
changedStack->popBonuses(selector);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void ObstaclesRemoved::applyGs(CGameState *gs)
|
DLL_LINKAGE void BattleObstaclesChanged::applyGs(CGameState * gs)
|
||||||
{
|
{
|
||||||
if(gs->curB) //if there is a battle
|
if(gs->curB)
|
||||||
|
applyBattle(gs->curB);
|
||||||
|
}
|
||||||
|
|
||||||
|
DLL_LINKAGE void BattleObstaclesChanged::applyBattle(IBattleState * battleState)
|
||||||
|
{
|
||||||
|
for(const auto & change : changes)
|
||||||
{
|
{
|
||||||
for(const si32 rem_obst :obstacles)
|
switch(change.operation)
|
||||||
{
|
{
|
||||||
for(int i=0; i<gs->curB->obstacles.size(); ++i)
|
case BattleChanges::EOperation::REMOVE:
|
||||||
{
|
battleState->removeObstacle(change.id);
|
||||||
if(gs->curB->obstacles[i]->uniqueID == rem_obst) //remove this obstacle
|
break;
|
||||||
{
|
case BattleChanges::EOperation::ADD:
|
||||||
gs->curB->obstacles.erase(gs->curB->obstacles.begin() + i);
|
battleState->addObstacle(change);
|
||||||
break;
|
break;
|
||||||
}
|
default:
|
||||||
}
|
logNetwork->error("Unknown obstacle operation %d", (int)change.operation);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
DLL_LINKAGE CatapultAttack::CatapultAttack()
|
DLL_LINKAGE CatapultAttack::CatapultAttack()
|
||||||
{
|
{
|
||||||
attacker = -1;
|
attacker = -1;
|
||||||
@@ -1712,99 +1498,26 @@ DLL_LINKAGE CatapultAttack::~CatapultAttack()
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void CatapultAttack::applyGs(CGameState *gs)
|
DLL_LINKAGE void CatapultAttack::applyGs(CGameState * gs)
|
||||||
{
|
{
|
||||||
if(gs->curB && gs->curB->town && gs->curB->town->fortLevel() != CGTownInstance::NONE) //if there is a battle and it's a siege
|
if(gs->curB)
|
||||||
{
|
applyBattle(gs->curB);
|
||||||
for(const auto &it :attackedParts)
|
|
||||||
{
|
|
||||||
gs->curB->si.wallState[it.attackedPart] =
|
|
||||||
SiegeInfo::applyDamage(EWallState::EWallState(gs->curB->si.wallState[it.attackedPart]), it.damageDealt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE std::string CatapultAttack::AttackInfo::toString() const
|
DLL_LINKAGE void CatapultAttack::applyBattle(IBattleState * battleState)
|
||||||
{
|
{
|
||||||
return boost::str(boost::format("{AttackInfo: destinationTile '%d', attackedPart '%d', damageDealt '%d'}")
|
auto town = battleState->getDefendedTown();
|
||||||
% destinationTile % static_cast<int>(attackedPart) % static_cast<int>(damageDealt));
|
if(!town)
|
||||||
}
|
|
||||||
|
|
||||||
DLL_LINKAGE std::string CatapultAttack::toString() const
|
|
||||||
{
|
|
||||||
std::ostringstream out;
|
|
||||||
out << "[";
|
|
||||||
for(auto it = attackedParts.begin(); it != attackedParts.end(); ++it)
|
|
||||||
{
|
|
||||||
out << it->toString();
|
|
||||||
if(std::prev(attackedParts.end()) != it) out << ", ";
|
|
||||||
}
|
|
||||||
out << "]";
|
|
||||||
|
|
||||||
return boost::str(boost::format("{CatapultAttack: attackedParts '%s', attacker '%d'}") % out.str() % attacker);
|
|
||||||
}
|
|
||||||
|
|
||||||
DLL_LINKAGE void BattleStacksRemoved::applyGs(CGameState *gs)
|
|
||||||
{
|
|
||||||
if(!gs->curB)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
while(!stackIDs.empty())
|
if(town->fortLevel() == CGTownInstance::NONE)
|
||||||
{
|
|
||||||
ui32 rem_stack = *stackIDs.begin();
|
|
||||||
|
|
||||||
for(int b=0; b<gs->curB->stacks.size(); ++b) //find it in vector of stacks
|
|
||||||
{
|
|
||||||
if(gs->curB->stacks[b]->ID == rem_stack) //if found
|
|
||||||
{
|
|
||||||
CStack * toRemove = gs->curB->stacks[b];
|
|
||||||
|
|
||||||
toRemove->state.erase(EBattleStackState::ALIVE);
|
|
||||||
toRemove->state.erase(EBattleStackState::GHOST_PENDING);
|
|
||||||
toRemove->state.insert(EBattleStackState::GHOST);
|
|
||||||
toRemove->detachFromAll();//TODO: may be some bonuses should remain
|
|
||||||
|
|
||||||
//stack may be removed instantly (not being killed first)
|
|
||||||
//handle clone remove also here
|
|
||||||
if(toRemove->cloneID >= 0)
|
|
||||||
{
|
|
||||||
stackIDs.insert(toRemove->cloneID);
|
|
||||||
toRemove->cloneID = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//cleanup remaining clone links if any
|
|
||||||
for(CStack * s : gs->curB->stacks)
|
|
||||||
{
|
|
||||||
if(s->cloneID == toRemove->ID)
|
|
||||||
s->cloneID = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stackIDs.erase(rem_stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DLL_LINKAGE void BattleStackAdded::applyGs(CGameState *gs)
|
|
||||||
{
|
|
||||||
newStackID = 0;
|
|
||||||
if(!BattleHex(pos).isValid())
|
|
||||||
{
|
|
||||||
logNetwork->warn("No place found for new stack!");
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
for(const auto & part : attackedParts)
|
||||||
|
{
|
||||||
|
auto newWallState = SiegeInfo::applyDamage(EWallState::EWallState(battleState->getWallState(part.attackedPart)), part.damageDealt);
|
||||||
|
battleState->setWallState(part.attackedPart, newWallState);
|
||||||
}
|
}
|
||||||
|
|
||||||
CStackBasicDescriptor csbd(creID, amount);
|
|
||||||
CStack * addedStack = gs->curB->generateNewStack(csbd, side, SlotID::SUMMONED_SLOT_PLACEHOLDER, pos); //TODO: netpacks?
|
|
||||||
if(summoned)
|
|
||||||
addedStack->state.insert(EBattleStackState::SUMMONED);
|
|
||||||
|
|
||||||
addedStack->localInit(gs->curB.get());
|
|
||||||
gs->curB->stacks.push_back(addedStack);
|
|
||||||
|
|
||||||
newStackID = addedStack->ID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void BattleSetStackProperty::applyGs(CGameState * gs)
|
DLL_LINKAGE void BattleSetStackProperty::applyGs(CGameState * gs)
|
||||||
@@ -1837,7 +1550,7 @@ DLL_LINKAGE void BattleSetStackProperty::applyGs(CGameState * gs)
|
|||||||
}
|
}
|
||||||
case CLONED:
|
case CLONED:
|
||||||
{
|
{
|
||||||
stack->state.insert(EBattleStackState::CLONED);
|
stack->cloned = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case HAS_CLONE:
|
case HAS_CLONE:
|
||||||
|
|||||||
+8
-8
@@ -33,13 +33,13 @@
|
|||||||
|
|
||||||
LibClasses * VLC = nullptr;
|
LibClasses * VLC = nullptr;
|
||||||
|
|
||||||
DLL_LINKAGE void preinitDLL(CConsoleHandler *Console)
|
DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential)
|
||||||
{
|
{
|
||||||
console = Console;
|
console = Console;
|
||||||
VLC = new LibClasses();
|
VLC = new LibClasses();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
VLC->loadFilesystem();
|
VLC->loadFilesystem(onlyEssential);
|
||||||
}
|
}
|
||||||
catch(...)
|
catch(...)
|
||||||
{
|
{
|
||||||
@@ -48,9 +48,9 @@ DLL_LINKAGE void preinitDLL(CConsoleHandler *Console)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DLL_LINKAGE void loadDLLClasses()
|
DLL_LINKAGE void loadDLLClasses(bool onlyEssential)
|
||||||
{
|
{
|
||||||
VLC->init();
|
VLC->init(onlyEssential);
|
||||||
}
|
}
|
||||||
|
|
||||||
const IBonusTypeHandler * LibClasses::getBth() const
|
const IBonusTypeHandler * LibClasses::getBth() const
|
||||||
@@ -58,7 +58,7 @@ const IBonusTypeHandler * LibClasses::getBth() const
|
|||||||
return bth;
|
return bth;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibClasses::loadFilesystem()
|
void LibClasses::loadFilesystem(bool onlyEssential)
|
||||||
{
|
{
|
||||||
CStopWatch totalTime;
|
CStopWatch totalTime;
|
||||||
CStopWatch loadTime;
|
CStopWatch loadTime;
|
||||||
@@ -72,7 +72,7 @@ void LibClasses::loadFilesystem()
|
|||||||
modh = new CModHandler();
|
modh = new CModHandler();
|
||||||
logGlobal->info("\tMod handler: %d ms", loadTime.getDiff());
|
logGlobal->info("\tMod handler: %d ms", loadTime.getDiff());
|
||||||
|
|
||||||
modh->loadMods();
|
modh->loadMods(onlyEssential);
|
||||||
modh->loadModFilesystems();
|
modh->loadModFilesystems();
|
||||||
logGlobal->info("\tMod filesystems: %d ms", loadTime.getDiff());
|
logGlobal->info("\tMod filesystems: %d ms", loadTime.getDiff());
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ template <class Handler> void createHandler(Handler *&handler, const std::string
|
|||||||
logHandlerLoaded(name, timer);
|
logHandlerLoaded(name, timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibClasses::init()
|
void LibClasses::init(bool onlyEssential)
|
||||||
{
|
{
|
||||||
CStopWatch pomtime, totalTime;
|
CStopWatch pomtime, totalTime;
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ void LibClasses::init()
|
|||||||
|
|
||||||
modh->load();
|
modh->load();
|
||||||
|
|
||||||
modh->afterLoad();
|
modh->afterLoad(onlyEssential);
|
||||||
|
|
||||||
//FIXME: make sure that everything is ok after game restart
|
//FIXME: make sure that everything is ok after game restart
|
||||||
//TODO: This should be done every time mod config changes
|
//TODO: This should be done every time mod config changes
|
||||||
|
|||||||
+4
-4
@@ -53,11 +53,11 @@ public:
|
|||||||
|
|
||||||
LibClasses(); //c-tor, loads .lods and NULLs handlers
|
LibClasses(); //c-tor, loads .lods and NULLs handlers
|
||||||
~LibClasses();
|
~LibClasses();
|
||||||
void init(); //uses standard config file
|
void init(bool onlyEssential); //uses standard config file
|
||||||
void clear(); //deletes all handlers and its data
|
void clear(); //deletes all handlers and its data
|
||||||
|
|
||||||
|
|
||||||
void loadFilesystem();// basic initialization. should be called before init()
|
void loadFilesystem(bool onlyEssential);// basic initialization. should be called before init()
|
||||||
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
@@ -85,6 +85,6 @@ public:
|
|||||||
|
|
||||||
extern DLL_LINKAGE LibClasses * VLC;
|
extern DLL_LINKAGE LibClasses * VLC;
|
||||||
|
|
||||||
DLL_LINKAGE void preinitDLL(CConsoleHandler *Console);
|
DLL_LINKAGE void preinitDLL(CConsoleHandler * Console, bool onlyEssential = false);
|
||||||
DLL_LINKAGE void loadDLLClasses();
|
DLL_LINKAGE void loadDLLClasses(bool onlyEssential = false);
|
||||||
|
|
||||||
|
|||||||
+61
-9
@@ -17,8 +17,10 @@
|
|||||||
<Option run_host_application_in_terminal="1" />
|
<Option run_host_application_in_terminal="1" />
|
||||||
<Option createStaticLib="1" />
|
<Option createStaticLib="1" />
|
||||||
<Compiler>
|
<Compiler>
|
||||||
<Add option="-g" />
|
|
||||||
<Add option="-Og" />
|
<Add option="-Og" />
|
||||||
|
<Add option="-g" />
|
||||||
|
<Add directory="$(#zlib.include)" />
|
||||||
|
<Add directory="lib/" />
|
||||||
</Compiler>
|
</Compiler>
|
||||||
<Linker>
|
<Linker>
|
||||||
<Add option="-lws2_32" />
|
<Add option="-lws2_32" />
|
||||||
@@ -33,6 +35,8 @@
|
|||||||
<Add option="-liconv" />
|
<Add option="-liconv" />
|
||||||
<Add option="-ldbghelp" />
|
<Add option="-ldbghelp" />
|
||||||
<Add directory="$(#boost.lib32)" />
|
<Add directory="$(#boost.lib32)" />
|
||||||
|
<Add directory="$(#sdl2.lib)" />
|
||||||
|
<Add directory="$(#zlib.lib)" />
|
||||||
</Linker>
|
</Linker>
|
||||||
</Target>
|
</Target>
|
||||||
<Target title="Release-win32">
|
<Target title="Release-win32">
|
||||||
@@ -45,6 +49,8 @@
|
|||||||
<Compiler>
|
<Compiler>
|
||||||
<Add option="-fomit-frame-pointer" />
|
<Add option="-fomit-frame-pointer" />
|
||||||
<Add option="-O2" />
|
<Add option="-O2" />
|
||||||
|
<Add directory="$(#zlib.include)" />
|
||||||
|
<Add directory="lib/" />
|
||||||
</Compiler>
|
</Compiler>
|
||||||
<Linker>
|
<Linker>
|
||||||
<Add option="-s" />
|
<Add option="-s" />
|
||||||
@@ -59,6 +65,8 @@
|
|||||||
<Add option="-lboost_date_time$(#boost.libsuffix)" />
|
<Add option="-lboost_date_time$(#boost.libsuffix)" />
|
||||||
<Add option="-liconv" />
|
<Add option="-liconv" />
|
||||||
<Add directory="$(#boost.lib32)" />
|
<Add directory="$(#boost.lib32)" />
|
||||||
|
<Add directory="$(#sdl2.lib)" />
|
||||||
|
<Add directory="$(#zlib.lib)" />
|
||||||
</Linker>
|
</Linker>
|
||||||
</Target>
|
</Target>
|
||||||
<Target title="Debug-win64">
|
<Target title="Debug-win64">
|
||||||
@@ -74,6 +82,7 @@
|
|||||||
<Add option="-Og" />
|
<Add option="-Og" />
|
||||||
<Add option="-g" />
|
<Add option="-g" />
|
||||||
<Add directory="$(#zlib64.include)" />
|
<Add directory="$(#zlib64.include)" />
|
||||||
|
<Add directory="lib/" />
|
||||||
</Compiler>
|
</Compiler>
|
||||||
<Linker>
|
<Linker>
|
||||||
<Add option="-lws2_32" />
|
<Add option="-lws2_32" />
|
||||||
@@ -87,8 +96,8 @@
|
|||||||
<Add option="-lboost_date_time$(#boost.libsuffix)" />
|
<Add option="-lboost_date_time$(#boost.libsuffix)" />
|
||||||
<Add option="-liconv" />
|
<Add option="-liconv" />
|
||||||
<Add option="-ldbghelp" />
|
<Add option="-ldbghelp" />
|
||||||
<Add directory="$(#sdl2.lib64)" />
|
|
||||||
<Add directory="$(#boost.lib64)" />
|
<Add directory="$(#boost.lib64)" />
|
||||||
|
<Add directory="$(#sdl2.lib64)" />
|
||||||
<Add directory="$(#zlib64.lib)" />
|
<Add directory="$(#zlib64.lib)" />
|
||||||
</Linker>
|
</Linker>
|
||||||
</Target>
|
</Target>
|
||||||
@@ -113,17 +122,15 @@
|
|||||||
<Add option="-DVCMI_NO_EXTRA_VERSION" />
|
<Add option="-DVCMI_NO_EXTRA_VERSION" />
|
||||||
<Add directory="." />
|
<Add directory="." />
|
||||||
<Add directory="$(#sdl2.include)" />
|
<Add directory="$(#sdl2.include)" />
|
||||||
<Add directory="$(#zlib.include)" />
|
|
||||||
<Add directory="../include" />
|
<Add directory="../include" />
|
||||||
</Compiler>
|
</Compiler>
|
||||||
<Linker>
|
<Linker>
|
||||||
<Add directory="../" />
|
<Add directory="../" />
|
||||||
<Add directory="$(#sdl2.lib)" />
|
|
||||||
<Add directory="$(#zlib.lib)" />
|
|
||||||
</Linker>
|
</Linker>
|
||||||
<Unit filename="../Global.h" />
|
<Unit filename="../Global.h" />
|
||||||
<Unit filename="../Version.h" />
|
<Unit filename="../Version.h" />
|
||||||
<Unit filename="../include/vstd/CLoggerBase.h" />
|
<Unit filename="../include/vstd/CLoggerBase.h" />
|
||||||
|
<Unit filename="../include/vstd/RNG.h" />
|
||||||
<Unit filename="AI_Base.h" />
|
<Unit filename="AI_Base.h" />
|
||||||
<Unit filename="CArtHandler.cpp" />
|
<Unit filename="CArtHandler.cpp" />
|
||||||
<Unit filename="CArtHandler.h" />
|
<Unit filename="CArtHandler.h" />
|
||||||
@@ -216,6 +223,8 @@
|
|||||||
<Unit filename="battle/BattleHex.h" />
|
<Unit filename="battle/BattleHex.h" />
|
||||||
<Unit filename="battle/BattleInfo.cpp" />
|
<Unit filename="battle/BattleInfo.cpp" />
|
||||||
<Unit filename="battle/BattleInfo.h" />
|
<Unit filename="battle/BattleInfo.h" />
|
||||||
|
<Unit filename="battle/BattleProxy.cpp" />
|
||||||
|
<Unit filename="battle/BattleProxy.h" />
|
||||||
<Unit filename="battle/CBattleInfoCallback.cpp" />
|
<Unit filename="battle/CBattleInfoCallback.cpp" />
|
||||||
<Unit filename="battle/CBattleInfoCallback.h" />
|
<Unit filename="battle/CBattleInfoCallback.h" />
|
||||||
<Unit filename="battle/CBattleInfoEssentials.cpp" />
|
<Unit filename="battle/CBattleInfoEssentials.cpp" />
|
||||||
@@ -226,12 +235,21 @@
|
|||||||
<Unit filename="battle/CObstacleInstance.h" />
|
<Unit filename="battle/CObstacleInstance.h" />
|
||||||
<Unit filename="battle/CPlayerBattleCallback.cpp" />
|
<Unit filename="battle/CPlayerBattleCallback.cpp" />
|
||||||
<Unit filename="battle/CPlayerBattleCallback.h" />
|
<Unit filename="battle/CPlayerBattleCallback.h" />
|
||||||
|
<Unit filename="battle/CUnitState.cpp" />
|
||||||
|
<Unit filename="battle/CUnitState.h" />
|
||||||
|
<Unit filename="battle/Destination.cpp" />
|
||||||
|
<Unit filename="battle/Destination.h" />
|
||||||
|
<Unit filename="battle/IBattleState.cpp" />
|
||||||
|
<Unit filename="battle/IBattleState.h" />
|
||||||
|
<Unit filename="battle/IUnitInfo.h" />
|
||||||
<Unit filename="battle/ReachabilityInfo.cpp" />
|
<Unit filename="battle/ReachabilityInfo.cpp" />
|
||||||
<Unit filename="battle/ReachabilityInfo.h" />
|
<Unit filename="battle/ReachabilityInfo.h" />
|
||||||
<Unit filename="battle/SideInBattle.cpp" />
|
<Unit filename="battle/SideInBattle.cpp" />
|
||||||
<Unit filename="battle/SideInBattle.h" />
|
<Unit filename="battle/SideInBattle.h" />
|
||||||
<Unit filename="battle/SiegeInfo.cpp" />
|
<Unit filename="battle/SiegeInfo.cpp" />
|
||||||
<Unit filename="battle/SiegeInfo.h" />
|
<Unit filename="battle/SiegeInfo.h" />
|
||||||
|
<Unit filename="battle/Unit.cpp" />
|
||||||
|
<Unit filename="battle/Unit.h" />
|
||||||
<Unit filename="filesystem/AdapterLoaders.cpp" />
|
<Unit filename="filesystem/AdapterLoaders.cpp" />
|
||||||
<Unit filename="filesystem/AdapterLoaders.h" />
|
<Unit filename="filesystem/AdapterLoaders.h" />
|
||||||
<Unit filename="filesystem/CArchiveLoader.cpp" />
|
<Unit filename="filesystem/CArchiveLoader.cpp" />
|
||||||
@@ -362,21 +380,55 @@
|
|||||||
<Unit filename="serializer/JsonSerializeFormat.h" />
|
<Unit filename="serializer/JsonSerializeFormat.h" />
|
||||||
<Unit filename="serializer/JsonSerializer.cpp" />
|
<Unit filename="serializer/JsonSerializer.cpp" />
|
||||||
<Unit filename="serializer/JsonSerializer.h" />
|
<Unit filename="serializer/JsonSerializer.h" />
|
||||||
|
<Unit filename="serializer/JsonTreeSerializer.h" />
|
||||||
<Unit filename="spells/AdventureSpellMechanics.cpp" />
|
<Unit filename="spells/AdventureSpellMechanics.cpp" />
|
||||||
<Unit filename="spells/AdventureSpellMechanics.h" />
|
<Unit filename="spells/AdventureSpellMechanics.h" />
|
||||||
<Unit filename="spells/BattleSpellMechanics.cpp" />
|
<Unit filename="spells/BattleSpellMechanics.cpp" />
|
||||||
<Unit filename="spells/BattleSpellMechanics.h" />
|
<Unit filename="spells/BattleSpellMechanics.h" />
|
||||||
<Unit filename="spells/CDefaultSpellMechanics.cpp" />
|
|
||||||
<Unit filename="spells/CDefaultSpellMechanics.h" />
|
|
||||||
<Unit filename="spells/CSpellHandler.cpp" />
|
<Unit filename="spells/CSpellHandler.cpp" />
|
||||||
<Unit filename="spells/CSpellHandler.h" />
|
<Unit filename="spells/CSpellHandler.h" />
|
||||||
<Unit filename="spells/CreatureSpellMechanics.cpp" />
|
|
||||||
<Unit filename="spells/CreatureSpellMechanics.h" />
|
|
||||||
<Unit filename="spells/ISpellMechanics.cpp" />
|
<Unit filename="spells/ISpellMechanics.cpp" />
|
||||||
<Unit filename="spells/ISpellMechanics.h" />
|
<Unit filename="spells/ISpellMechanics.h" />
|
||||||
<Unit filename="spells/Magic.h" />
|
<Unit filename="spells/Magic.h" />
|
||||||
|
<Unit filename="spells/Problem.cpp" />
|
||||||
|
<Unit filename="spells/Problem.h" />
|
||||||
|
<Unit filename="spells/TargetCondition.cpp" />
|
||||||
|
<Unit filename="spells/TargetCondition.h" />
|
||||||
<Unit filename="spells/ViewSpellInt.cpp" />
|
<Unit filename="spells/ViewSpellInt.cpp" />
|
||||||
<Unit filename="spells/ViewSpellInt.h" />
|
<Unit filename="spells/ViewSpellInt.h" />
|
||||||
|
<Unit filename="spells/effects/Catapult.cpp" />
|
||||||
|
<Unit filename="spells/effects/Catapult.h" />
|
||||||
|
<Unit filename="spells/effects/Clone.cpp" />
|
||||||
|
<Unit filename="spells/effects/Clone.h" />
|
||||||
|
<Unit filename="spells/effects/Damage.cpp" />
|
||||||
|
<Unit filename="spells/effects/Damage.h" />
|
||||||
|
<Unit filename="spells/effects/Dispel.cpp" />
|
||||||
|
<Unit filename="spells/effects/Dispel.h" />
|
||||||
|
<Unit filename="spells/effects/Effect.cpp" />
|
||||||
|
<Unit filename="spells/effects/Effect.h" />
|
||||||
|
<Unit filename="spells/effects/Effects.cpp" />
|
||||||
|
<Unit filename="spells/effects/Effects.h" />
|
||||||
|
<Unit filename="spells/effects/EffectsFwd.h" />
|
||||||
|
<Unit filename="spells/effects/Heal.cpp" />
|
||||||
|
<Unit filename="spells/effects/Heal.h" />
|
||||||
|
<Unit filename="spells/effects/LocationEffect.cpp" />
|
||||||
|
<Unit filename="spells/effects/LocationEffect.h" />
|
||||||
|
<Unit filename="spells/effects/Obstacle.cpp" />
|
||||||
|
<Unit filename="spells/effects/Obstacle.h" />
|
||||||
|
<Unit filename="spells/effects/Registry.cpp" />
|
||||||
|
<Unit filename="spells/effects/Registry.h" />
|
||||||
|
<Unit filename="spells/effects/RemoveObstacle.cpp" />
|
||||||
|
<Unit filename="spells/effects/RemoveObstacle.h" />
|
||||||
|
<Unit filename="spells/effects/Sacrifice.cpp" />
|
||||||
|
<Unit filename="spells/effects/Sacrifice.h" />
|
||||||
|
<Unit filename="spells/effects/Summon.cpp" />
|
||||||
|
<Unit filename="spells/effects/Summon.h" />
|
||||||
|
<Unit filename="spells/effects/Teleport.cpp" />
|
||||||
|
<Unit filename="spells/effects/Teleport.h" />
|
||||||
|
<Unit filename="spells/effects/Timed.cpp" />
|
||||||
|
<Unit filename="spells/effects/Timed.h" />
|
||||||
|
<Unit filename="spells/effects/UnitEffect.cpp" />
|
||||||
|
<Unit filename="spells/effects/UnitEffect.h" />
|
||||||
<Unit filename="vcmi_endian.h" />
|
<Unit filename="vcmi_endian.h" />
|
||||||
<Extensions>
|
<Extensions>
|
||||||
<code_completion />
|
<code_completion />
|
||||||
|
|||||||
@@ -222,7 +222,18 @@
|
|||||||
<ClCompile Include="spells\BattleSpellMechanics.cpp" />
|
<ClCompile Include="spells\BattleSpellMechanics.cpp" />
|
||||||
<ClCompile Include="spells\CreatureSpellMechanics.cpp" />
|
<ClCompile Include="spells\CreatureSpellMechanics.cpp" />
|
||||||
<ClCompile Include="spells\CDefaultSpellMechanics.cpp" />
|
<ClCompile Include="spells\CDefaultSpellMechanics.cpp" />
|
||||||
|
<ClCompile Include="spells\Problem.cpp" />
|
||||||
<ClCompile Include="spells\ViewSpellInt.cpp" />
|
<ClCompile Include="spells\ViewSpellInt.cpp" />
|
||||||
|
<ClCompile Include="spells\effects\Effect.cpp" />
|
||||||
|
<ClCompile Include="spells\effects\Effects.cpp" />
|
||||||
|
<ClCompile Include="spells\effects\Clone.cpp" />
|
||||||
|
<ClCompile Include="spells\effects\Damage.cpp" />
|
||||||
|
<ClCompile Include="spells\effects\GlobalEffect.cpp" />
|
||||||
|
<ClCompile Include="spells\effects\LocationEffect.cpp" />
|
||||||
|
<ClCompile Include="spells\effects\Registry.cpp" />
|
||||||
|
<ClCompile Include="spells\effects\StackEffect.cpp" />
|
||||||
|
<ClCompile Include="spells\effects\Summon.cpp" />
|
||||||
|
<ClCompile Include="spells\effects\Timed.cpp" />
|
||||||
<ClCompile Include="filesystem\AdapterLoaders.cpp" />
|
<ClCompile Include="filesystem\AdapterLoaders.cpp" />
|
||||||
<ClCompile Include="filesystem\CArchiveLoader.cpp" />
|
<ClCompile Include="filesystem\CArchiveLoader.cpp" />
|
||||||
<ClCompile Include="filesystem\CBinaryReader.cpp" />
|
<ClCompile Include="filesystem\CBinaryReader.cpp" />
|
||||||
@@ -429,7 +440,18 @@
|
|||||||
<ClInclude Include="spells\ISpellMechanics.h" />
|
<ClInclude Include="spells\ISpellMechanics.h" />
|
||||||
<ClInclude Include="spells\Magic.h" />
|
<ClInclude Include="spells\Magic.h" />
|
||||||
<ClInclude Include="spells\SpellMechanics.h" />
|
<ClInclude Include="spells\SpellMechanics.h" />
|
||||||
|
<ClInclude Include="spells\Problem.h" />
|
||||||
<ClInclude Include="spells\ViewSpellInt.h" />
|
<ClInclude Include="spells\ViewSpellInt.h" />
|
||||||
|
<ClInclude Include="spells\effects\Effect.h" />
|
||||||
|
<ClInclude Include="spells\effects\Effects.h" />
|
||||||
|
<ClInclude Include="spells\effects\Clone.h" />
|
||||||
|
<ClInclude Include="spells\effects\Damage.h" />
|
||||||
|
<ClInclude Include="spells\effects\GlobalEffect.h" />
|
||||||
|
<ClInclude Include="spells\effects\LocationEffect.h" />
|
||||||
|
<ClInclude Include="spells\effects\Registry.h" />
|
||||||
|
<ClInclude Include="spells\effects\StackEffect.h" />
|
||||||
|
<ClInclude Include="spells\effects\Summon.h" />
|
||||||
|
<ClInclude Include="spells\effects\Timed.h" />
|
||||||
<ClInclude Include="StartInfo.h" />
|
<ClInclude Include="StartInfo.h" />
|
||||||
<ClInclude Include="StdInc.h" />
|
<ClInclude Include="StdInc.h" />
|
||||||
<ClInclude Include="StringConstants.h" />
|
<ClInclude Include="StringConstants.h" />
|
||||||
|
|||||||
@@ -9,29 +9,31 @@
|
|||||||
*/
|
*/
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "AccessibilityInfo.h"
|
#include "AccessibilityInfo.h"
|
||||||
#include "../CStack.h"
|
#include "Unit.h"
|
||||||
#include "../GameConstants.h"
|
#include "../GameConstants.h"
|
||||||
|
|
||||||
bool AccessibilityInfo::accessible(BattleHex tile, const CStack * stack) const
|
bool AccessibilityInfo::accessible(BattleHex tile, const battle::Unit * stack) const
|
||||||
{
|
{
|
||||||
return accessible(tile, stack->doubleWide(), stack->side);
|
return accessible(tile, stack->doubleWide(), stack->unitSide());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AccessibilityInfo::accessible(BattleHex tile, bool doubleWide, ui8 side) const
|
bool AccessibilityInfo::accessible(BattleHex tile, bool doubleWide, ui8 side) const
|
||||||
{
|
{
|
||||||
// All hexes that stack would cover if standing on tile have to be accessible.
|
// All hexes that stack would cover if standing on tile have to be accessible.
|
||||||
for(auto hex : CStack::getHexes(tile, doubleWide, side))
|
//do not use getHexes for speed reasons
|
||||||
|
if(!tile.isValid())
|
||||||
|
return false;
|
||||||
|
if(at(tile) != EAccessibility::ACCESSIBLE && !(at(tile) == EAccessibility::GATE && side == BattleSide::DEFENDER))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if(doubleWide)
|
||||||
{
|
{
|
||||||
// If the hex is out of range then the tile isn't accessible
|
auto otherHex = battle::Unit::occupiedHex(tile, doubleWide, side);
|
||||||
if(!hex.isValid())
|
if(!otherHex.isValid())
|
||||||
return false;
|
return false;
|
||||||
// If we're no defender which step on gate and the hex isn't accessible, then the tile
|
if(at(otherHex) != EAccessibility::ACCESSIBLE && !(at(otherHex) == EAccessibility::GATE && side == BattleSide::DEFENDER))
|
||||||
// isn't accessible
|
|
||||||
else if(at(hex) != EAccessibility::ACCESSIBLE &&
|
|
||||||
!(at(hex) == EAccessibility::GATE && side == BattleSide::DEFENDER))
|
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,10 @@
|
|||||||
#include "BattleHex.h"
|
#include "BattleHex.h"
|
||||||
#include "../GameConstants.h"
|
#include "../GameConstants.h"
|
||||||
|
|
||||||
class CStack;
|
namespace battle
|
||||||
|
{
|
||||||
|
class Unit;
|
||||||
|
}
|
||||||
|
|
||||||
//Accessibility is property of hex in battle. It doesn't depend on stack, side's perspective and so on.
|
//Accessibility is property of hex in battle. It doesn't depend on stack, side's perspective and so on.
|
||||||
enum class EAccessibility
|
enum class EAccessibility
|
||||||
@@ -30,6 +33,6 @@ typedef std::array<EAccessibility, GameConstants::BFIELD_SIZE> TAccessibilityArr
|
|||||||
|
|
||||||
struct DLL_LINKAGE AccessibilityInfo : TAccessibilityArray
|
struct DLL_LINKAGE AccessibilityInfo : TAccessibilityArray
|
||||||
{
|
{
|
||||||
bool accessible(BattleHex tile, const CStack * stack) const; //checks for both tiles if stack is double wide
|
bool accessible(BattleHex tile, const battle::Unit * stack) const; //checks for both tiles if stack is double wide
|
||||||
bool accessible(BattleHex tile, bool doubleWide, ui8 side) const; //checks for both tiles if stack is double wide
|
bool accessible(BattleHex tile, bool doubleWide, ui8 side) const; //checks for both tiles if stack is double wide
|
||||||
};
|
};
|
||||||
|
|||||||
+107
-44
@@ -10,77 +10,77 @@
|
|||||||
|
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "BattleAction.h"
|
#include "BattleAction.h"
|
||||||
#include "../CStack.h"
|
#include "Unit.h"
|
||||||
|
#include "CBattleInfoCallback.h"
|
||||||
|
|
||||||
using namespace Battle;
|
static const int32_t INVALID_UNIT_ID = -1000;
|
||||||
|
|
||||||
BattleAction::BattleAction():
|
BattleAction::BattleAction():
|
||||||
side(-1),
|
side(-1),
|
||||||
stackNumber(-1),
|
stackNumber(-1),
|
||||||
actionType(INVALID),
|
actionType(EActionType::INVALID),
|
||||||
destinationTile(-1),
|
actionSubtype(-1)
|
||||||
additionalInfo(-1),
|
|
||||||
selectedStack(-1)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
BattleAction BattleAction::makeHeal(const CStack * healer, const CStack * healed)
|
BattleAction BattleAction::makeHeal(const battle::Unit * healer, const battle::Unit * healed)
|
||||||
{
|
{
|
||||||
BattleAction ba;
|
BattleAction ba;
|
||||||
ba.side = healer->side;
|
ba.side = healer->unitSide();
|
||||||
ba.actionType = STACK_HEAL;
|
ba.actionType = EActionType::STACK_HEAL;
|
||||||
ba.stackNumber = healer->ID;
|
ba.stackNumber = healer->unitId();
|
||||||
ba.destinationTile = healed->position;
|
ba.aimToUnit(healed);
|
||||||
return ba;
|
return ba;
|
||||||
}
|
}
|
||||||
|
|
||||||
BattleAction BattleAction::makeDefend(const CStack * stack)
|
BattleAction BattleAction::makeDefend(const battle::Unit * stack)
|
||||||
{
|
{
|
||||||
BattleAction ba;
|
BattleAction ba;
|
||||||
ba.side = stack->side;
|
ba.side = stack->unitSide();
|
||||||
ba.actionType = DEFEND;
|
ba.actionType = EActionType::DEFEND;
|
||||||
ba.stackNumber = stack->ID;
|
ba.stackNumber = stack->unitId();
|
||||||
return ba;
|
return ba;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BattleAction BattleAction::makeMeleeAttack(const battle::Unit * stack, BattleHex destination, BattleHex attackFrom, bool returnAfterAttack)
|
||||||
BattleAction BattleAction::makeMeleeAttack(const CStack * stack, const CStack * attacked, BattleHex attackFrom)
|
|
||||||
{
|
{
|
||||||
BattleAction ba;
|
BattleAction ba;
|
||||||
ba.side = stack->side;
|
ba.side = stack->unitSide(); //FIXME: will it fail if stack mind controlled?
|
||||||
ba.actionType = WALK_AND_ATTACK;
|
ba.actionType = EActionType::WALK_AND_ATTACK;
|
||||||
ba.stackNumber = stack->ID;
|
ba.stackNumber = stack->unitId();
|
||||||
ba.destinationTile = attackFrom;
|
ba.aimToHex(attackFrom);
|
||||||
ba.additionalInfo = attacked->position;
|
ba.aimToHex(destination);
|
||||||
return ba;
|
if(returnAfterAttack && stack->hasBonusOfType(Bonus::RETURN_AFTER_STRIKE))
|
||||||
|
ba.aimToHex(stack->getPosition());
|
||||||
}
|
|
||||||
BattleAction BattleAction::makeWait(const CStack * stack)
|
|
||||||
{
|
|
||||||
BattleAction ba;
|
|
||||||
ba.side = stack->side;
|
|
||||||
ba.actionType = WAIT;
|
|
||||||
ba.stackNumber = stack->ID;
|
|
||||||
return ba;
|
return ba;
|
||||||
}
|
}
|
||||||
|
|
||||||
BattleAction BattleAction::makeShotAttack(const CStack * shooter, const CStack * target)
|
BattleAction BattleAction::makeWait(const battle::Unit * stack)
|
||||||
{
|
{
|
||||||
BattleAction ba;
|
BattleAction ba;
|
||||||
ba.side = shooter->side;
|
ba.side = stack->unitSide();
|
||||||
ba.actionType = SHOOT;
|
ba.actionType = EActionType::WAIT;
|
||||||
ba.stackNumber = shooter->ID;
|
ba.stackNumber = stack->unitId();
|
||||||
ba.destinationTile = target->position;
|
|
||||||
return ba;
|
return ba;
|
||||||
}
|
}
|
||||||
|
|
||||||
BattleAction BattleAction::makeMove(const CStack * stack, BattleHex dest)
|
BattleAction BattleAction::makeShotAttack(const battle::Unit * shooter, const battle::Unit * target)
|
||||||
{
|
{
|
||||||
BattleAction ba;
|
BattleAction ba;
|
||||||
ba.side = stack->side;
|
ba.side = shooter->unitSide();
|
||||||
ba.actionType = WALK;
|
ba.actionType = EActionType::SHOOT;
|
||||||
ba.stackNumber = stack->ID;
|
ba.stackNumber = shooter->unitId();
|
||||||
ba.destinationTile = dest;
|
ba.aimToUnit(target);
|
||||||
|
return ba;
|
||||||
|
}
|
||||||
|
|
||||||
|
BattleAction BattleAction::makeMove(const battle::Unit * stack, BattleHex dest)
|
||||||
|
{
|
||||||
|
BattleAction ba;
|
||||||
|
ba.side = stack->unitSide();
|
||||||
|
ba.actionType = EActionType::WALK;
|
||||||
|
ba.stackNumber = stack->unitId();
|
||||||
|
ba.aimToHex(dest);
|
||||||
return ba;
|
return ba;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,7 +88,7 @@ BattleAction BattleAction::makeEndOFTacticPhase(ui8 side)
|
|||||||
{
|
{
|
||||||
BattleAction ba;
|
BattleAction ba;
|
||||||
ba.side = side;
|
ba.side = side;
|
||||||
ba.actionType = END_TACTIC_PHASE;
|
ba.actionType = EActionType::END_TACTIC_PHASE;
|
||||||
return ba;
|
return ba;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,11 +97,74 @@ std::string BattleAction::toString() const
|
|||||||
std::stringstream actionTypeStream;
|
std::stringstream actionTypeStream;
|
||||||
actionTypeStream << actionType;
|
actionTypeStream << actionType;
|
||||||
|
|
||||||
boost::format fmt("{BattleAction: side '%d', stackNumber '%d', actionType '%s', destinationTile '%s', additionalInfo '%d', selectedStack '%d'}");
|
std::stringstream targetStream;
|
||||||
fmt % static_cast<int>(side) % stackNumber % actionTypeStream.str() % destinationTile % additionalInfo % selectedStack;
|
|
||||||
|
for(const DestinationInfo & info : target)
|
||||||
|
{
|
||||||
|
if(info.unitValue == INVALID_UNIT_ID)
|
||||||
|
{
|
||||||
|
targetStream << info.hexValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
targetStream << info.unitValue;
|
||||||
|
targetStream << "@";
|
||||||
|
targetStream << info.hexValue;
|
||||||
|
}
|
||||||
|
targetStream << ",";
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::format fmt("{BattleAction: side '%d', stackNumber '%d', actionType '%s', actionSubtype '%d', target {%s}}");
|
||||||
|
fmt % static_cast<int>(side) % stackNumber % actionTypeStream.str() % actionSubtype % targetStream.str();
|
||||||
return fmt.str();
|
return fmt.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BattleAction::aimToHex(const BattleHex & destination)
|
||||||
|
{
|
||||||
|
DestinationInfo info;
|
||||||
|
info.hexValue = destination;
|
||||||
|
info.unitValue = INVALID_UNIT_ID;
|
||||||
|
|
||||||
|
target.push_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleAction::aimToUnit(const battle::Unit * destination)
|
||||||
|
{
|
||||||
|
DestinationInfo info;
|
||||||
|
info.hexValue = destination->getPosition();
|
||||||
|
info.unitValue = destination->unitId();
|
||||||
|
|
||||||
|
target.push_back(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
battle::Target BattleAction::getTarget(const CBattleInfoCallback * cb) const
|
||||||
|
{
|
||||||
|
battle::Target ret;
|
||||||
|
|
||||||
|
for(auto & destination : target)
|
||||||
|
{
|
||||||
|
if(destination.unitValue == INVALID_UNIT_ID)
|
||||||
|
ret.emplace_back(destination.hexValue);
|
||||||
|
else
|
||||||
|
ret.emplace_back(cb->battleGetUnitByID(destination.unitValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleAction::setTarget(const battle::Target & target_)
|
||||||
|
{
|
||||||
|
target.clear();
|
||||||
|
for(auto & destination : target_)
|
||||||
|
{
|
||||||
|
if(destination.unitValue == nullptr)
|
||||||
|
aimToHex(destination.hexValue);
|
||||||
|
else
|
||||||
|
aimToUnit(destination.unitValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::ostream & operator<<(std::ostream & os, const BattleAction & ba)
|
std::ostream & operator<<(std::ostream & os, const BattleAction & ba)
|
||||||
{
|
{
|
||||||
os << ba.toString();
|
os << ba.toString();
|
||||||
|
|||||||
+46
-21
@@ -8,42 +8,67 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "BattleHex.h"
|
#include "Destination.h"
|
||||||
#include "../GameConstants.h"
|
#include "../GameConstants.h"
|
||||||
|
|
||||||
class CStack;
|
class CBattleInfoCallback;
|
||||||
|
|
||||||
|
namespace battle
|
||||||
|
{
|
||||||
|
class Unit;
|
||||||
|
}
|
||||||
|
|
||||||
/// A struct which handles battle actions like defending, walking,... - represents a creature stack in a battle
|
/// A struct which handles battle actions like defending, walking,... - represents a creature stack in a battle
|
||||||
struct DLL_LINKAGE BattleAction
|
class DLL_LINKAGE BattleAction
|
||||||
{
|
{
|
||||||
ui8 side; //who made this action: false - left, true - right player
|
public:
|
||||||
|
ui8 side; //who made this action
|
||||||
ui32 stackNumber; //stack ID, -1 left hero, -2 right hero,
|
ui32 stackNumber; //stack ID, -1 left hero, -2 right hero,
|
||||||
Battle::ActionType actionType; //use ActionType enum for values
|
EActionType actionType; //use ActionType enum for values
|
||||||
BattleHex destinationTile;
|
|
||||||
si32 additionalInfo; // e.g. spell number if type is 1 || 10; tile to attack if type is 6
|
|
||||||
si32 selectedStack; //spell subject for teleport / sacrifice
|
|
||||||
|
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
si32 actionSubtype;
|
||||||
|
|
||||||
|
BattleAction();
|
||||||
|
|
||||||
|
static BattleAction makeHeal(const battle::Unit * healer, const battle::Unit * healed);
|
||||||
|
static BattleAction makeDefend(const battle::Unit * stack);
|
||||||
|
static BattleAction makeWait(const battle::Unit * stack);
|
||||||
|
static BattleAction makeMeleeAttack(const battle::Unit * stack, BattleHex destination, BattleHex attackFrom, bool returnAfterAttack = true);
|
||||||
|
static BattleAction makeShotAttack(const battle::Unit * shooter, const battle::Unit * target);
|
||||||
|
static BattleAction makeMove(const battle::Unit * stack, BattleHex dest);
|
||||||
|
static BattleAction makeEndOFTacticPhase(ui8 side);
|
||||||
|
|
||||||
|
std::string toString() const;
|
||||||
|
|
||||||
|
void aimToHex(const BattleHex & destination);
|
||||||
|
void aimToUnit(const battle::Unit * destination);
|
||||||
|
|
||||||
|
battle::Target getTarget(const CBattleInfoCallback * cb) const;
|
||||||
|
void setTarget(const battle::Target & target_);
|
||||||
|
|
||||||
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
{
|
{
|
||||||
h & side;
|
h & side;
|
||||||
h & stackNumber;
|
h & stackNumber;
|
||||||
h & actionType;
|
h & actionType;
|
||||||
h & destinationTile;
|
h & actionSubtype;
|
||||||
h & additionalInfo;
|
h & target;
|
||||||
h & selectedStack;
|
|
||||||
}
|
}
|
||||||
|
private:
|
||||||
|
|
||||||
BattleAction();
|
struct DestinationInfo
|
||||||
|
{
|
||||||
|
int32_t unitValue;
|
||||||
|
BattleHex hexValue;
|
||||||
|
|
||||||
static BattleAction makeHeal(const CStack * healer, const CStack * healed);
|
template <typename Handler> void serialize(Handler & h, const int version)
|
||||||
static BattleAction makeDefend(const CStack * stack);
|
{
|
||||||
static BattleAction makeWait(const CStack * stack);
|
h & unitValue;
|
||||||
static BattleAction makeMeleeAttack(const CStack * stack, const CStack * attacked, BattleHex attackFrom = BattleHex::INVALID);
|
h & hexValue;
|
||||||
static BattleAction makeShotAttack(const CStack * shooter, const CStack * target);
|
}
|
||||||
static BattleAction makeMove(const CStack * stack, BattleHex dest);
|
};
|
||||||
static BattleAction makeEndOFTacticPhase(ui8 side);
|
|
||||||
|
|
||||||
std::string toString() const;
|
std::vector<DestinationInfo> target;
|
||||||
};
|
};
|
||||||
|
|
||||||
DLL_EXPORT std::ostream & operator<<(std::ostream & os, const BattleAction & ba); //todo: remove
|
DLL_EXPORT std::ostream & operator<<(std::ostream & os, const BattleAction & ba); //todo: remove
|
||||||
|
|||||||
@@ -9,40 +9,29 @@
|
|||||||
*/
|
*/
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "BattleAttackInfo.h"
|
#include "BattleAttackInfo.h"
|
||||||
|
#include "CUnitState.h"
|
||||||
|
|
||||||
|
BattleAttackInfo::BattleAttackInfo(const battle::Unit * Attacker, const battle::Unit * Defender, bool Shooting)
|
||||||
BattleAttackInfo::BattleAttackInfo(const CStack * Attacker, const CStack * Defender, bool Shooting):
|
: attacker(Attacker),
|
||||||
attackerHealth(Attacker->health), defenderHealth(Defender->health)
|
defender(Defender)
|
||||||
{
|
{
|
||||||
attacker = Attacker;
|
|
||||||
defender = Defender;
|
|
||||||
|
|
||||||
attackerBonuses = Attacker;
|
|
||||||
defenderBonuses = Defender;
|
|
||||||
|
|
||||||
attackerPosition = Attacker->position;
|
|
||||||
defenderPosition = Defender->position;
|
|
||||||
|
|
||||||
shooting = Shooting;
|
shooting = Shooting;
|
||||||
chargedFields = 0;
|
chargedFields = 0;
|
||||||
|
additiveBonus = 0.0;
|
||||||
luckyHit = false;
|
multBonus = 1.0;
|
||||||
unluckyHit = false;
|
|
||||||
deathBlow = false;
|
|
||||||
ballistaDoubleDamage = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BattleAttackInfo BattleAttackInfo::reverse() const
|
BattleAttackInfo BattleAttackInfo::reverse() const
|
||||||
{
|
{
|
||||||
BattleAttackInfo ret = *this;
|
BattleAttackInfo ret = *this;
|
||||||
|
|
||||||
std::swap(ret.attacker, ret.defender);
|
std::swap(ret.attacker, ret.defender);
|
||||||
std::swap(ret.attackerBonuses, ret.defenderBonuses);
|
|
||||||
std::swap(ret.attackerPosition, ret.defenderPosition);
|
|
||||||
std::swap(ret.attackerHealth, ret.defenderHealth);
|
|
||||||
|
|
||||||
ret.shooting = false;
|
ret.shooting = false;
|
||||||
ret.chargedFields = 0;
|
ret.chargedFields = 0;
|
||||||
ret.luckyHit = ret.ballistaDoubleDamage = ret.deathBlow = false;
|
|
||||||
|
ret.additiveBonus = 0.0;
|
||||||
|
ret.multBonus = 1.0;
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,27 +8,24 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "BattleHex.h"
|
|
||||||
#include "../CStack.h"
|
|
||||||
|
|
||||||
class IBonusBearer;
|
namespace battle
|
||||||
|
{
|
||||||
|
class Unit;
|
||||||
|
class CUnitState;
|
||||||
|
}
|
||||||
|
|
||||||
struct DLL_LINKAGE BattleAttackInfo
|
struct DLL_LINKAGE BattleAttackInfo
|
||||||
{
|
{
|
||||||
const IBonusBearer *attackerBonuses, *defenderBonuses;
|
const battle::Unit * attacker;
|
||||||
const CStack *attacker, *defender;
|
const battle::Unit * defender;
|
||||||
BattleHex attackerPosition, defenderPosition;
|
|
||||||
|
|
||||||
CHealth attackerHealth, defenderHealth;
|
|
||||||
|
|
||||||
bool shooting;
|
bool shooting;
|
||||||
int chargedFields;
|
int chargedFields;
|
||||||
|
|
||||||
bool luckyHit;
|
double additiveBonus;
|
||||||
bool unluckyHit;
|
double multBonus;
|
||||||
bool deathBlow;
|
|
||||||
bool ballistaDoubleDamage;
|
|
||||||
|
|
||||||
BattleAttackInfo(const CStack * Attacker, const CStack * Defender, bool Shooting = false);
|
BattleAttackInfo(const battle::Unit * Attacker, const battle::Unit * Defender, bool Shooting = false);
|
||||||
BattleAttackInfo reverse() const;
|
BattleAttackInfo reverse() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
*/
|
*/
|
||||||
#include "StdInc.h"
|
#include "StdInc.h"
|
||||||
#include "BattleHex.h"
|
#include "BattleHex.h"
|
||||||
#include "../GameConstants.h"
|
|
||||||
|
|
||||||
BattleHex::BattleHex() : hex(INVALID) {}
|
BattleHex::BattleHex() : hex(INVALID) {}
|
||||||
|
|
||||||
@@ -53,7 +52,11 @@ void BattleHex::setY(si16 y)
|
|||||||
void BattleHex::setXY(si16 x, si16 y, bool hasToBeValid)
|
void BattleHex::setXY(si16 x, si16 y, bool hasToBeValid)
|
||||||
{
|
{
|
||||||
if(hasToBeValid)
|
if(hasToBeValid)
|
||||||
assert(x >= 0 && x < GameConstants::BFIELD_WIDTH && y >= 0 && y < GameConstants::BFIELD_HEIGHT);
|
{
|
||||||
|
if(!(x >= 0 && x < GameConstants::BFIELD_WIDTH && y >= 0 && y < GameConstants::BFIELD_HEIGHT))
|
||||||
|
throw std::runtime_error("Valid hex required");
|
||||||
|
}
|
||||||
|
|
||||||
hex = x + y * GameConstants::BFIELD_WIDTH;
|
hex = x + y * GameConstants::BFIELD_WIDTH;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,6 +132,7 @@ BattleHex BattleHex::operator+(BattleHex::EDir dir) const
|
|||||||
std::vector<BattleHex> BattleHex::neighbouringTiles() const
|
std::vector<BattleHex> BattleHex::neighbouringTiles() const
|
||||||
{
|
{
|
||||||
std::vector<BattleHex> ret;
|
std::vector<BattleHex> ret;
|
||||||
|
ret.reserve(6);
|
||||||
for(EDir dir = EDir(0); dir <= EDir(5); dir = EDir(dir+1))
|
for(EDir dir = EDir(0); dir <= EDir(5); dir = EDir(dir+1))
|
||||||
checkAndPush(cloneInDirection(dir, false), ret);
|
checkAndPush(cloneInDirection(dir, false), ret);
|
||||||
return ret;
|
return ret;
|
||||||
@@ -201,3 +205,22 @@ std::ostream & operator<<(std::ostream & os, const BattleHex & hex)
|
|||||||
{
|
{
|
||||||
return os << boost::str(boost::format("{BattleHex: x '%d', y '%d', hex '%d'}") % hex.getX() % hex.getY() % hex.hex);
|
return os << boost::str(boost::format("{BattleHex: x '%d', y '%d', hex '%d'}") % hex.getX() % hex.getY() % hex.hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static BattleHex::NeighbouringTilesCache calculateNeighbouringTiles()
|
||||||
|
{
|
||||||
|
BattleHex::NeighbouringTilesCache ret;
|
||||||
|
ret.resize(GameConstants::BFIELD_SIZE);
|
||||||
|
|
||||||
|
for(si16 hex = 0; hex < GameConstants::BFIELD_SIZE; hex++)
|
||||||
|
{
|
||||||
|
auto hexes = BattleHex(hex).neighbouringTiles();
|
||||||
|
|
||||||
|
size_t index = 0;
|
||||||
|
for(auto neighbour : hexes)
|
||||||
|
ret[hex].at(index++) = neighbour;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
const BattleHex::NeighbouringTilesCache BattleHex::neighbouringTilesCache = calculateNeighbouringTiles();
|
||||||
|
|||||||
@@ -20,6 +20,13 @@ namespace BattleSide
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace GameConstants
|
||||||
|
{
|
||||||
|
const int BFIELD_WIDTH = 17;
|
||||||
|
const int BFIELD_HEIGHT = 11;
|
||||||
|
const int BFIELD_SIZE = BFIELD_WIDTH * BFIELD_HEIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
typedef boost::optional<ui8> BattleSideOpt;
|
typedef boost::optional<ui8> BattleSideOpt;
|
||||||
|
|
||||||
// for battle stacks' positions
|
// for battle stacks' positions
|
||||||
@@ -67,6 +74,11 @@ struct DLL_LINKAGE BattleHex //TODO: decide if this should be changed to class f
|
|||||||
{
|
{
|
||||||
h & hex;
|
h & hex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using NeighbouringTiles = std::array<BattleHex, 6>;
|
||||||
|
using NeighbouringTilesCache = std::vector<NeighbouringTiles>;
|
||||||
|
|
||||||
|
static const NeighbouringTilesCache neighbouringTilesCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
DLL_EXPORT std::ostream & operator<<(std::ostream & os, const BattleHex & hex);
|
DLL_EXPORT std::ostream & operator<<(std::ostream & os, const BattleHex & hex);
|
||||||
|
|||||||
+433
-129
@@ -16,48 +16,7 @@
|
|||||||
#include "../mapObjects/CGTownInstance.h"
|
#include "../mapObjects/CGTownInstance.h"
|
||||||
#include "../CGeneralTextHandler.h"
|
#include "../CGeneralTextHandler.h"
|
||||||
|
|
||||||
const CStack * BattleInfo::getNextStack() const
|
///BattleInfo
|
||||||
{
|
|
||||||
std::vector<const CStack *> hlp;
|
|
||||||
battleGetStackQueue(hlp, 1, -1);
|
|
||||||
|
|
||||||
if(hlp.size())
|
|
||||||
return hlp[0];
|
|
||||||
else
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
int BattleInfo::getAvaliableHex(CreatureID creID, ui8 side, int initialPos) const
|
|
||||||
{
|
|
||||||
bool twoHex = VLC->creh->creatures[creID]->isDoubleWide();
|
|
||||||
//bool flying = VLC->creh->creatures[creID]->isFlying();
|
|
||||||
|
|
||||||
int pos;
|
|
||||||
if (initialPos > -1)
|
|
||||||
pos = initialPos;
|
|
||||||
else //summon elementals depending on player side
|
|
||||||
{
|
|
||||||
if(side == BattleSide::ATTACKER)
|
|
||||||
pos = 0; //top left
|
|
||||||
else
|
|
||||||
pos = GameConstants::BFIELD_WIDTH - 1; //top right
|
|
||||||
}
|
|
||||||
|
|
||||||
auto accessibility = getAccesibility();
|
|
||||||
|
|
||||||
std::set<BattleHex> occupyable;
|
|
||||||
for(int i = 0; i < accessibility.size(); i++)
|
|
||||||
if(accessibility.accessible(i, twoHex, side))
|
|
||||||
occupyable.insert(i);
|
|
||||||
|
|
||||||
if (occupyable.empty())
|
|
||||||
{
|
|
||||||
return BattleHex::INVALID; //all tiles are covered
|
|
||||||
}
|
|
||||||
|
|
||||||
return BattleHex::getClosestTile(side, pos, occupyable);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const CStack * stack)
|
std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, BattleHex dest, const CStack * stack)
|
||||||
{
|
{
|
||||||
auto reachability = getReachability(stack);
|
auto reachability = getReachability(stack);
|
||||||
@@ -79,31 +38,6 @@ std::pair< std::vector<BattleHex>, int > BattleInfo::getPath(BattleHex start, Ba
|
|||||||
return std::make_pair(path, reachability.distances[dest]);
|
return std::make_pair(path, reachability.distances[dest]);
|
||||||
}
|
}
|
||||||
|
|
||||||
ui32 BattleInfo::calculateDmg(const CStack * attacker, const CStack * defender,
|
|
||||||
bool shooting, ui8 charge, bool lucky, bool unlucky, bool deathBlow, bool ballistaDoubleDmg, CRandomGenerator & rand)
|
|
||||||
{
|
|
||||||
BattleAttackInfo bai(attacker, defender, shooting);
|
|
||||||
bai.chargedFields = charge;
|
|
||||||
bai.luckyHit = lucky;
|
|
||||||
bai.unluckyHit = unlucky;
|
|
||||||
bai.deathBlow = deathBlow;
|
|
||||||
bai.ballistaDoubleDamage = ballistaDoubleDmg;
|
|
||||||
|
|
||||||
TDmgRange range = calculateDmgRange(bai);
|
|
||||||
|
|
||||||
if(range.first != range.second)
|
|
||||||
{
|
|
||||||
ui32 sum = 0;
|
|
||||||
ui32 howManyToAv = std::min<ui32>(10, attacker->getCount());
|
|
||||||
for(int g=0; g<howManyToAv; ++g)
|
|
||||||
sum += (ui32)rand.nextInt(range.first, range.second);
|
|
||||||
|
|
||||||
return sum / howManyToAv;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return range.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
void BattleInfo::calculateCasualties(std::map<ui32,si32> * casualties) const
|
void BattleInfo::calculateCasualties(std::map<ui32,si32> * casualties) const
|
||||||
{
|
{
|
||||||
for(auto & elem : stacks)//setting casualties
|
for(auto & elem : stacks)//setting casualties
|
||||||
@@ -115,26 +49,24 @@ void BattleInfo::calculateCasualties(std::map<ui32,si32> * casualties) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CStack * BattleInfo::generateNewStack(const CStackInstance & base, ui8 side, SlotID slot, BattleHex position) const
|
CStack * BattleInfo::generateNewStack(uint32_t id, const CStackInstance & base, ui8 side, SlotID slot, BattleHex position)
|
||||||
{
|
{
|
||||||
int stackID = getIdForNewStack();
|
|
||||||
PlayerColor owner = sides[side].color;
|
PlayerColor owner = sides[side].color;
|
||||||
assert((owner >= PlayerColor::PLAYER_LIMIT) ||
|
assert((owner >= PlayerColor::PLAYER_LIMIT) ||
|
||||||
(base.armyObj && base.armyObj->tempOwner == owner));
|
(base.armyObj && base.armyObj->tempOwner == owner));
|
||||||
|
|
||||||
auto ret = new CStack(&base, owner, stackID, side, slot);
|
auto ret = new CStack(&base, owner, id, side, slot);
|
||||||
ret->position = getAvaliableHex(base.getCreatureID(), side, position); //TODO: what if no free tile on battlefield was found?
|
ret->initialPosition = getAvaliableHex(base.getCreatureID(), side, position); //TODO: what if no free tile on battlefield was found?
|
||||||
ret->state.insert(EBattleStackState::ALIVE); //alive state indication
|
stacks.push_back(ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
CStack * BattleInfo::generateNewStack(const CStackBasicDescriptor & base, ui8 side, SlotID slot, BattleHex position) const
|
CStack * BattleInfo::generateNewStack(uint32_t id, const CStackBasicDescriptor & base, ui8 side, SlotID slot, BattleHex position)
|
||||||
{
|
{
|
||||||
int stackID = getIdForNewStack();
|
|
||||||
PlayerColor owner = sides[side].color;
|
PlayerColor owner = sides[side].color;
|
||||||
auto ret = new CStack(&base, owner, stackID, side, slot);
|
auto ret = new CStack(&base, owner, id, side, slot);
|
||||||
ret->position = position;
|
ret->initialPosition = position;
|
||||||
ret->state.insert(EBattleStackState::ALIVE); //alive state indication
|
stacks.push_back(ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,7 +368,7 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
|
|||||||
CreatureID cre = warMachineArt->artType->warMachine;
|
CreatureID cre = warMachineArt->artType->warMachine;
|
||||||
|
|
||||||
if(cre != CreatureID::NONE)
|
if(cre != CreatureID::NONE)
|
||||||
stacks.push_back(curB->generateNewStack(CStackBasicDescriptor(cre, 1), side, SlotID::WAR_MACHINES_SLOT, hex));
|
curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(cre, 1), side, SlotID::WAR_MACHINES_SLOT, hex);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -481,8 +413,7 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
|
|||||||
if(creatureBank && i->second->type->isDoubleWide())
|
if(creatureBank && i->second->type->isDoubleWide())
|
||||||
pos += side ? BattleHex::LEFT : BattleHex::RIGHT;
|
pos += side ? BattleHex::LEFT : BattleHex::RIGHT;
|
||||||
|
|
||||||
CStack * stack = curB->generateNewStack(*i->second, side, i->first, pos);
|
curB->generateNewStack(curB->nextUnitId(), *i->second, side, i->first, pos);
|
||||||
stacks.push_back(stack);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -491,9 +422,7 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
|
|||||||
{
|
{
|
||||||
if (heroes[i] && heroes[i]->commander && heroes[i]->commander->alive)
|
if (heroes[i] && heroes[i]->commander && heroes[i]->commander->alive)
|
||||||
{
|
{
|
||||||
CStack * stack = curB->generateNewStack (*heroes[i]->commander, i, SlotID::COMMANDER_SLOT_PLACEHOLDER,
|
curB->generateNewStack(curB->nextUnitId(), *heroes[i]->commander, i, SlotID::COMMANDER_SLOT_PLACEHOLDER, creatureBank ? commanderBank[i] : commanderField[i]);
|
||||||
creatureBank ? commanderBank[i] : commanderField[i]);
|
|
||||||
stacks.push_back(stack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -501,16 +430,14 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
|
|||||||
if (curB->town && curB->town->fortLevel() >= CGTownInstance::CITADEL)
|
if (curB->town && curB->town->fortLevel() >= CGTownInstance::CITADEL)
|
||||||
{
|
{
|
||||||
// keep tower
|
// keep tower
|
||||||
CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, -2);
|
curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, -2);
|
||||||
stacks.push_back(stack);
|
|
||||||
|
|
||||||
if (curB->town->fortLevel() >= CGTownInstance::CASTLE)
|
if (curB->town->fortLevel() >= CGTownInstance::CASTLE)
|
||||||
{
|
{
|
||||||
// lower tower + upper tower
|
// lower tower + upper tower
|
||||||
CStack * stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, -4);
|
curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, -4);
|
||||||
stacks.push_back(stack);
|
|
||||||
stack = curB->generateNewStack(CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, -3);
|
curB->generateNewStack(curB->nextUnitId(), CStackBasicDescriptor(CreatureID::ARROW_TOWERS, 1), 1, SlotID::ARROW_TOWERS_SLOT, -3);
|
||||||
stacks.push_back(stack);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//moat
|
//moat
|
||||||
@@ -523,10 +450,6 @@ BattleInfo * BattleInfo::setupBattle(int3 tile, ETerrainType terrain, BFieldType
|
|||||||
|
|
||||||
std::stable_sort(stacks.begin(),stacks.end(),cmpst);
|
std::stable_sort(stacks.begin(),stacks.end(),cmpst);
|
||||||
|
|
||||||
//spell level limiting bonus
|
|
||||||
curB->addNewBonus(std::make_shared<Bonus>(Bonus::ONE_BATTLE, Bonus::LEVEL_SPELL_IMMUNITY, Bonus::OTHER,
|
|
||||||
0, -1, -1, Bonus::INDEPENDENT_MAX));
|
|
||||||
|
|
||||||
auto neutral = std::make_shared<CreatureAlignmentLimiter>(EAlignment::NEUTRAL);
|
auto neutral = std::make_shared<CreatureAlignmentLimiter>(EAlignment::NEUTRAL);
|
||||||
auto good = std::make_shared<CreatureAlignmentLimiter>(EAlignment::GOOD);
|
auto good = std::make_shared<CreatureAlignmentLimiter>(EAlignment::GOOD);
|
||||||
auto evil = std::make_shared<CreatureAlignmentLimiter>(EAlignment::EVIL);
|
auto evil = std::make_shared<CreatureAlignmentLimiter>(EAlignment::EVIL);
|
||||||
@@ -660,11 +583,6 @@ const CGHeroInstance * BattleInfo::getHero(PlayerColor player) const
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
PlayerColor BattleInfo::theOtherPlayer(PlayerColor player) const
|
|
||||||
{
|
|
||||||
return sides[!whatSide(player)].color;
|
|
||||||
}
|
|
||||||
|
|
||||||
ui8 BattleInfo::whatSide(PlayerColor player) const
|
ui8 BattleInfo::whatSide(PlayerColor player) const
|
||||||
{
|
{
|
||||||
for(int i = 0; i < sides.size(); i++)
|
for(int i = 0; i < sides.size(); i++)
|
||||||
@@ -675,29 +593,6 @@ ui8 BattleInfo::whatSide(PlayerColor player) const
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int BattleInfo::getIdForNewStack() const
|
|
||||||
{
|
|
||||||
if(stacks.size())
|
|
||||||
{
|
|
||||||
//stacks vector may be sorted not by ID and they may be not contiguous -> find stack with max ID
|
|
||||||
auto highestIDStack = *std::max_element(stacks.begin(), stacks.end(),
|
|
||||||
[](const CStack *a, const CStack *b) { return a->ID < b->ID; });
|
|
||||||
|
|
||||||
return highestIDStack->ID + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<CObstacleInstance> BattleInfo::getObstacleOnTile(BattleHex tile) const
|
|
||||||
{
|
|
||||||
for(auto &obs : obstacles)
|
|
||||||
if(vstd::contains(obs->getAffectedTiles(), tile))
|
|
||||||
return obs;
|
|
||||||
|
|
||||||
return std::shared_ptr<CObstacleInstance>();
|
|
||||||
}
|
|
||||||
|
|
||||||
BattlefieldBI::BattlefieldBI BattleInfo::battlefieldTypeToBI(BFieldType bfieldType)
|
BattlefieldBI::BattlefieldBI BattleInfo::battlefieldTypeToBI(BFieldType bfieldType)
|
||||||
{
|
{
|
||||||
static const std::map<BFieldType, BattlefieldBI::BattlefieldBI> theMap =
|
static const std::map<BFieldType, BattlefieldBI::BattlefieldBI> theMap =
|
||||||
@@ -728,7 +623,7 @@ CStack * BattleInfo::getStack(int stackID, bool onlyAlive)
|
|||||||
}
|
}
|
||||||
|
|
||||||
BattleInfo::BattleInfo()
|
BattleInfo::BattleInfo()
|
||||||
: round(-1), activeStack(-1), selectedStack(-1), town(nullptr), tile(-1,-1,-1),
|
: round(-1), activeStack(-1), town(nullptr), tile(-1,-1,-1),
|
||||||
battlefieldType(BFieldType::NONE), terrainType(ETerrainType::WRONG),
|
battlefieldType(BFieldType::NONE), terrainType(ETerrainType::WRONG),
|
||||||
tacticsSide(0), tacticDistance(0)
|
tacticsSide(0), tacticDistance(0)
|
||||||
{
|
{
|
||||||
@@ -736,6 +631,416 @@ BattleInfo::BattleInfo()
|
|||||||
setNodeType(BATTLE);
|
setNodeType(BATTLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BattleInfo::~BattleInfo() = default;
|
||||||
|
|
||||||
|
int32_t BattleInfo::getActiveStackID() const
|
||||||
|
{
|
||||||
|
return activeStack;
|
||||||
|
}
|
||||||
|
|
||||||
|
TStacks BattleInfo::getStacksIf(TStackFilter predicate) const
|
||||||
|
{
|
||||||
|
TStacks ret;
|
||||||
|
vstd::copy_if(stacks, std::back_inserter(ret), predicate);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
battle::Units BattleInfo::getUnitsIf(battle::UnitFilter predicate) const
|
||||||
|
{
|
||||||
|
battle::Units ret;
|
||||||
|
vstd::copy_if(stacks, std::back_inserter(ret), predicate);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
BFieldType BattleInfo::getBattlefieldType() const
|
||||||
|
{
|
||||||
|
return battlefieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
ETerrainType BattleInfo::getTerrainType() const
|
||||||
|
{
|
||||||
|
return terrainType;
|
||||||
|
}
|
||||||
|
|
||||||
|
IBattleInfo::ObstacleCList BattleInfo::getAllObstacles() const
|
||||||
|
{
|
||||||
|
ObstacleCList ret;
|
||||||
|
|
||||||
|
for(auto iter = obstacles.cbegin(); iter != obstacles.cend(); iter++)
|
||||||
|
ret.push_back(*iter);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerColor BattleInfo::getSidePlayer(ui8 side) const
|
||||||
|
{
|
||||||
|
return sides.at(side).color;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CArmedInstance * BattleInfo::getSideArmy(ui8 side) const
|
||||||
|
{
|
||||||
|
return sides.at(side).armyObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CGHeroInstance * BattleInfo::getSideHero(ui8 side) const
|
||||||
|
{
|
||||||
|
return sides.at(side).hero;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui8 BattleInfo::getTacticDist() const
|
||||||
|
{
|
||||||
|
return tacticDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
ui8 BattleInfo::getTacticsSide() const
|
||||||
|
{
|
||||||
|
return tacticsSide;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CGTownInstance * BattleInfo::getDefendedTown() const
|
||||||
|
{
|
||||||
|
return town;
|
||||||
|
}
|
||||||
|
|
||||||
|
si8 BattleInfo::getWallState(int partOfWall) const
|
||||||
|
{
|
||||||
|
return si.wallState.at(partOfWall);
|
||||||
|
}
|
||||||
|
|
||||||
|
EGateState BattleInfo::getGateState() const
|
||||||
|
{
|
||||||
|
return si.gateState;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t BattleInfo::getCastSpells(ui8 side) const
|
||||||
|
{
|
||||||
|
return sides.at(side).castSpellsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t BattleInfo::getEnchanterCounter(ui8 side) const
|
||||||
|
{
|
||||||
|
return sides.at(side).enchanterCounter;
|
||||||
|
}
|
||||||
|
|
||||||
|
const IBonusBearer * BattleInfo::asBearer() const
|
||||||
|
{
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t BattleInfo::getActualDamage(const TDmgRange & damage, int32_t attackerCount, vstd::RNG & rng) const
|
||||||
|
{
|
||||||
|
|
||||||
|
if(damage.first != damage.second)
|
||||||
|
{
|
||||||
|
int64_t sum = 0;
|
||||||
|
|
||||||
|
auto howManyToAv = std::min<int32_t>(10, attackerCount);
|
||||||
|
auto rangeGen = rng.getInt64Range(damage.first, damage.second);
|
||||||
|
|
||||||
|
for(int32_t g = 0; g < howManyToAv; ++g)
|
||||||
|
sum += rangeGen();
|
||||||
|
|
||||||
|
return sum / howManyToAv;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return damage.first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::nextRound(int32_t roundNr)
|
||||||
|
{
|
||||||
|
for(int i = 0; i < 2; ++i)
|
||||||
|
{
|
||||||
|
sides.at(i).castSpellsCount = 0;
|
||||||
|
vstd::amax(--sides.at(i).enchanterCounter, 0);
|
||||||
|
}
|
||||||
|
round = roundNr;
|
||||||
|
|
||||||
|
for(CStack * s : stacks)
|
||||||
|
{
|
||||||
|
// new turn effects
|
||||||
|
s->updateBonuses(Bonus::NTurns);
|
||||||
|
|
||||||
|
s->afterNewRound();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto & obst : obstacles)
|
||||||
|
obst->battleTurnPassed();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::nextTurn(uint32_t unitId)
|
||||||
|
{
|
||||||
|
activeStack = unitId;
|
||||||
|
|
||||||
|
CStack * st = getStack(activeStack);
|
||||||
|
|
||||||
|
//remove bonuses that last until when stack gets new turn
|
||||||
|
st->popBonuses(Bonus::UntilGetsTurn);
|
||||||
|
|
||||||
|
st->afterGetsTurn();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::addUnit(uint32_t id, const JsonNode & data)
|
||||||
|
{
|
||||||
|
battle::UnitInfo info;
|
||||||
|
info.load(id, data);
|
||||||
|
CStackBasicDescriptor base(info.type, info.count);
|
||||||
|
|
||||||
|
PlayerColor owner = getSidePlayer(info.side);
|
||||||
|
|
||||||
|
auto ret = new CStack(&base, owner, info.id, info.side, SlotID::SUMMONED_SLOT_PLACEHOLDER);
|
||||||
|
ret->initialPosition = info.position;
|
||||||
|
stacks.push_back(ret);
|
||||||
|
ret->summoned = info.summoned;
|
||||||
|
ret->localInit(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::moveUnit(uint32_t id, BattleHex destination)
|
||||||
|
{
|
||||||
|
auto sta = getStack(id);
|
||||||
|
|
||||||
|
if(!sta)
|
||||||
|
{
|
||||||
|
logGlobal->error("Cannot find stack %d", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(auto & oi : obstacles)
|
||||||
|
{
|
||||||
|
if((oi->obstacleType == CObstacleInstance::SPELL_CREATED) && vstd::contains(oi->getAffectedTiles(), destination))
|
||||||
|
{
|
||||||
|
SpellCreatedObstacle * obstacle = dynamic_cast<SpellCreatedObstacle*>(oi.get());
|
||||||
|
assert(obstacle);
|
||||||
|
if(obstacle->casterSide != sta->unitSide() && obstacle->hidden)
|
||||||
|
obstacle->revealed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sta->position = destination;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::setUnitState(uint32_t id, const JsonNode & data, int64_t healthDelta)
|
||||||
|
{
|
||||||
|
CStack * changedStack = getStack(id, false);
|
||||||
|
if(!changedStack)
|
||||||
|
throw std::runtime_error("Invalid unit id in BattleInfo update");
|
||||||
|
|
||||||
|
if(!changedStack->alive() && healthDelta > 0)
|
||||||
|
{
|
||||||
|
//checking if we resurrect a stack that is under a living stack
|
||||||
|
auto accessibility = getAccesibility();
|
||||||
|
|
||||||
|
if(!accessibility.accessible(changedStack->getPosition(), changedStack))
|
||||||
|
{
|
||||||
|
logNetwork->error("Cannot resurrect %s because hex %d is occupied!", changedStack->nodeName(), changedStack->getPosition().hex);
|
||||||
|
return; //position is already occupied
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool killed = (-healthDelta) >= changedStack->getAvailableHealth();//todo: check using alive state once rebirth will be handled separately
|
||||||
|
|
||||||
|
bool resurrected = !changedStack->alive() && healthDelta > 0;
|
||||||
|
|
||||||
|
//applying changes
|
||||||
|
changedStack->load(data);
|
||||||
|
|
||||||
|
|
||||||
|
if(healthDelta < 0)
|
||||||
|
{
|
||||||
|
changedStack->popBonuses(Bonus::UntilBeingAttacked);
|
||||||
|
}
|
||||||
|
|
||||||
|
resurrected = resurrected || (killed && changedStack->alive());
|
||||||
|
|
||||||
|
if(killed)
|
||||||
|
{
|
||||||
|
if(changedStack->cloneID >= 0)
|
||||||
|
{
|
||||||
|
//remove clone as well
|
||||||
|
CStack * clone = getStack(changedStack->cloneID);
|
||||||
|
if(clone)
|
||||||
|
clone->makeGhost();
|
||||||
|
|
||||||
|
changedStack->cloneID = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(resurrected || killed)
|
||||||
|
{
|
||||||
|
//removing all spells effects
|
||||||
|
auto selector = [](const Bonus * b)
|
||||||
|
{
|
||||||
|
//Special case: DISRUPTING_RAY is absolutely permanent
|
||||||
|
if(b->source == Bonus::SPELL_EFFECT)
|
||||||
|
return b->sid != SpellID::DISRUPTING_RAY;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
changedStack->popBonuses(selector);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!changedStack->alive() && changedStack->isClone())
|
||||||
|
{
|
||||||
|
for(CStack * s : stacks)
|
||||||
|
{
|
||||||
|
if(s->cloneID == changedStack->unitId())
|
||||||
|
s->cloneID = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::removeUnit(uint32_t id)
|
||||||
|
{
|
||||||
|
std::set<uint32_t> ids;
|
||||||
|
ids.insert(id);
|
||||||
|
|
||||||
|
while(!ids.empty())
|
||||||
|
{
|
||||||
|
auto toRemoveId = *ids.begin();
|
||||||
|
auto toRemove = getStack(toRemoveId, false);
|
||||||
|
|
||||||
|
if(!toRemove)
|
||||||
|
{
|
||||||
|
logGlobal->error("Cannot find stack %d", toRemoveId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!toRemove->ghost)
|
||||||
|
{
|
||||||
|
toRemove->onRemoved();
|
||||||
|
toRemove->detachFromAll();
|
||||||
|
|
||||||
|
//stack may be removed instantly (not being killed first)
|
||||||
|
//handle clone remove also here
|
||||||
|
if(toRemove->cloneID >= 0)
|
||||||
|
{
|
||||||
|
ids.insert(toRemove->cloneID);
|
||||||
|
toRemove->cloneID = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//cleanup remaining clone links if any
|
||||||
|
for(auto s : stacks)
|
||||||
|
{
|
||||||
|
if(s->cloneID == toRemoveId)
|
||||||
|
s->cloneID = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ids.erase(toRemoveId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::addUnitBonus(uint32_t id, const std::vector<Bonus> & bonus)
|
||||||
|
{
|
||||||
|
CStack * sta = getStack(id, false);
|
||||||
|
|
||||||
|
if(!sta)
|
||||||
|
{
|
||||||
|
logGlobal->error("Cannot find stack %d", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const Bonus & b : bonus)
|
||||||
|
addOrUpdateUnitBonus(sta, b, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::updateUnitBonus(uint32_t id, const std::vector<Bonus> & bonus)
|
||||||
|
{
|
||||||
|
CStack * sta = getStack(id, false);
|
||||||
|
|
||||||
|
if(!sta)
|
||||||
|
{
|
||||||
|
logGlobal->error("Cannot find stack %d", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const Bonus & b : bonus)
|
||||||
|
addOrUpdateUnitBonus(sta, b, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::removeUnitBonus(uint32_t id, const std::vector<Bonus> & bonus)
|
||||||
|
{
|
||||||
|
CStack * sta = getStack(id, false);
|
||||||
|
|
||||||
|
if(!sta)
|
||||||
|
{
|
||||||
|
logGlobal->error("Cannot find stack %d", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(const Bonus & one : bonus)
|
||||||
|
{
|
||||||
|
auto selector = [one](const Bonus * b)
|
||||||
|
{
|
||||||
|
//compare everything but turnsRemain, limiter and propagator
|
||||||
|
return one.duration == b->duration
|
||||||
|
&& one.type == b->type
|
||||||
|
&& one.subtype == b->subtype
|
||||||
|
&& one.source == b->source
|
||||||
|
&& one.val == b->val
|
||||||
|
&& one.sid == b->sid
|
||||||
|
&& one.valType == b->valType
|
||||||
|
&& one.additionalInfo == b->additionalInfo
|
||||||
|
&& one.effectRange == b->effectRange
|
||||||
|
&& one.description == b->description;
|
||||||
|
};
|
||||||
|
sta->popBonuses(selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t BattleInfo::nextUnitId() const
|
||||||
|
{
|
||||||
|
return stacks.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::addOrUpdateUnitBonus(CStack * sta, const Bonus & value, bool forceAdd)
|
||||||
|
{
|
||||||
|
if(forceAdd || !sta->hasBonus(Selector::source(Bonus::SPELL_EFFECT, value.sid).And(Selector::typeSubtype(value.type, value.subtype))))
|
||||||
|
{
|
||||||
|
//no such effect or cumulative - add new
|
||||||
|
logBonus->trace("%s receives a new bonus: %s", sta->nodeName(), value.Description());
|
||||||
|
sta->addNewBonus(std::make_shared<Bonus>(value));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logBonus->trace("%s updated bonus: %s", sta->nodeName(), value.Description());
|
||||||
|
|
||||||
|
for(auto stackBonus : sta->getExportedBonusList()) //TODO: optimize
|
||||||
|
{
|
||||||
|
if(stackBonus->source == value.source && stackBonus->sid == value.sid && stackBonus->type == value.type && stackBonus->subtype == value.subtype)
|
||||||
|
{
|
||||||
|
stackBonus->turnsRemain = std::max(stackBonus->turnsRemain, value.turnsRemain);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CBonusSystemNode::treeHasChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::setWallState(int partOfWall, si8 state)
|
||||||
|
{
|
||||||
|
si.wallState.at(partOfWall) = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::addObstacle(const ObstacleChanges & changes)
|
||||||
|
{
|
||||||
|
std::shared_ptr<SpellCreatedObstacle> obstacle = std::make_shared<SpellCreatedObstacle>();
|
||||||
|
obstacle->fromInfo(changes);
|
||||||
|
obstacles.push_back(obstacle);
|
||||||
|
}
|
||||||
|
|
||||||
|
void BattleInfo::removeObstacle(uint32_t id)
|
||||||
|
{
|
||||||
|
for(int i=0; i < obstacles.size(); ++i)
|
||||||
|
{
|
||||||
|
if(obstacles[i]->uniqueID == id) //remove this obstacle
|
||||||
|
{
|
||||||
|
obstacles.erase(obstacles.begin() + i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CArmedInstance * BattleInfo::battleGetArmyObject(ui8 side) const
|
CArmedInstance * BattleInfo::battleGetArmyObject(ui8 side) const
|
||||||
{
|
{
|
||||||
return const_cast<CArmedInstance*>(CBattleInfoEssentials::battleGetArmyObject(side));
|
return const_cast<CArmedInstance*>(CBattleInfoEssentials::battleGetArmyObject(side));
|
||||||
@@ -746,30 +1051,29 @@ CGHeroInstance * BattleInfo::battleGetFightingHero(ui8 side) const
|
|||||||
return const_cast<CGHeroInstance*>(CBattleInfoEssentials::battleGetFightingHero(side));
|
return const_cast<CGHeroInstance*>(CBattleInfoEssentials::battleGetFightingHero(side));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CMP_stack::operator()(const battle::Unit * a, const battle::Unit * b)
|
||||||
bool CMP_stack::operator()(const CStack* a, const CStack* b)
|
|
||||||
{
|
{
|
||||||
switch(phase)
|
switch(phase)
|
||||||
{
|
{
|
||||||
case 0: //catapult moves after turrets
|
case 0: //catapult moves after turrets
|
||||||
return a->getCreature()->idNumber > b->getCreature()->idNumber; //catapult is 145 and turrets are 149
|
return a->creatureIndex() > b->creatureIndex(); //catapult is 145 and turrets are 149
|
||||||
case 1: //fastest first, upper slot first
|
case 1: //fastest first, upper slot first
|
||||||
{
|
{
|
||||||
int as = a->Speed(turn), bs = b->Speed(turn);
|
int as = a->getInitiative(turn), bs = b->getInitiative(turn);
|
||||||
if(as != bs)
|
if(as != bs)
|
||||||
return as > bs;
|
return as > bs;
|
||||||
else
|
else
|
||||||
return a->slot < b->slot;
|
return a->unitSlot() < b->unitSlot(); //FIXME: what about summoned stacks?
|
||||||
}
|
}
|
||||||
case 2: //fastest last, upper slot first
|
case 2: //fastest last, upper slot first
|
||||||
//TODO: should be replaced with order of receiving morale!
|
//TODO: should be replaced with order of receiving morale!
|
||||||
case 3: //fastest last, upper slot first
|
case 3: //fastest last, upper slot first
|
||||||
{
|
{
|
||||||
int as = a->Speed(turn), bs = b->Speed(turn);
|
int as = a->getInitiative(turn), bs = b->getInitiative(turn);
|
||||||
if(as != bs)
|
if(as != bs)
|
||||||
return as < bs;
|
return as < bs;
|
||||||
else
|
else
|
||||||
return a->slot < b->slot;
|
return a->unitSlot() < b->unitSlot();
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
assert(0);
|
assert(0);
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user