mirror of
https://github.com/vcmi/vcmi.git
synced 2025-03-27 21:49:10 +02:00
New feature: All tiles affected by multi-hex attack will be highlighted when attack is possible.
This commit is contained in:
parent
ebacb433fe
commit
899aeeb748
@ -1185,7 +1185,7 @@ void CBattleInterface::addNewAnim(CBattleAnimation * anim)
|
||||
CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2, CGHeroInstance *hero1, CGHeroInstance *hero2, const SDL_Rect & myRect, CPlayerInterface * att, CPlayerInterface * defen)
|
||||
: queue(NULL), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
|
||||
activeStack(NULL), stackToActivate(NULL), mouseHoveredStack(-1), lastMouseHoveredStackAnimationTime(-1), previouslyHoveredHex(-1),
|
||||
currentlyHoveredHex(-1), tacticianInterface(NULL), spellDestSelectMode(false), spellToCast(NULL),
|
||||
currentlyHoveredHex(-1), attackingHex(-1), tacticianInterface(NULL), spellDestSelectMode(false), spellToCast(NULL),
|
||||
siegeH(NULL), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0), givenCommand(NULL),
|
||||
myTurn(false), resWindow(NULL), moveStarted(false), moveSh(-1), bresult(NULL)
|
||||
|
||||
@ -1678,11 +1678,25 @@ void CBattleInterface::show(SDL_Surface * to)
|
||||
}
|
||||
}
|
||||
else if(curInt->sysOpts.printMouseShadow) //when not casting spell
|
||||
{
|
||||
int x = 14 + ((b/BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(b%BFIELD_WIDTH) + pos.x;
|
||||
int y = 86 + 42 * (b/BFIELD_WIDTH) + pos.y;
|
||||
SDL_Rect temp_rect = genRect(cellShade->h, cellShade->w, x, y);
|
||||
CSDL_Ext::blit8bppAlphaTo24bpp(cellShade, NULL, to, &temp_rect);
|
||||
{//TODO: do not check it every frame
|
||||
if (activeStack) //highlight all attackable hexes
|
||||
{
|
||||
std::set<THex> set = curInt->cb->battleGetAttackedHexes(activeStack, currentlyHoveredHex, attackingHex);
|
||||
BOOST_FOREACH(THex hex, set)
|
||||
{
|
||||
int x = 14 + ((hex/BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(hex%BFIELD_WIDTH) + pos.x;
|
||||
int y = 86 + 42 * (hex/BFIELD_WIDTH) + pos.y;
|
||||
SDL_Rect temp_rect = genRect(cellShade->h, cellShade->w, x, y);
|
||||
CSDL_Ext::blit8bppAlphaTo24bpp(cellShade, NULL, to, &temp_rect);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int x = 14 + ((b/BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(b%BFIELD_WIDTH) + pos.x;
|
||||
int y = 86 + 42 * (b/BFIELD_WIDTH) + pos.y;
|
||||
SDL_Rect temp_rect = genRect(cellShade->h, cellShade->w, x, y);
|
||||
CSDL_Ext::blit8bppAlphaTo24bpp(cellShade, NULL, to, &temp_rect);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2078,7 +2092,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
|
||||
const double subdividingAngle = 2.0*M_PI/6.0; // Divide a hex into six sectors.
|
||||
const double hexMidX = hoveredHex.pos.x + hoveredHex.pos.w/2;
|
||||
const double hexMidY = hoveredHex.pos.y + hoveredHex.pos.h/2;
|
||||
const double cursorHexAngle = M_PI - atan2(hexMidY - cursor->ypos, cursor->xpos - hexMidX) + subdividingAngle/2;
|
||||
const double cursorHexAngle = M_PI - atan2(hexMidY - cursor->ypos, cursor->xpos - hexMidX) + subdividingAngle/2; //TODO: refactor this nightmare
|
||||
const double sector = fmod(cursorHexAngle/subdividingAngle, 6.0);
|
||||
const int zigzagCorrection = !((myNumber/BFIELD_WIDTH)%2); // Off-by-one correction needed to deal with the odd battlefield rows.
|
||||
|
||||
@ -2205,9 +2219,34 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
|
||||
// Find the closest direction attackable, starting with the right one.
|
||||
// FIXME: Is this really how the original H3 client does it?
|
||||
int i = 0;
|
||||
while (sectorCursor[(cursorIndex + i)%sectorCursor.size()] == -1)
|
||||
while (sectorCursor[(cursorIndex + i)%sectorCursor.size()] == -1) //Why hast thou forsaken me?
|
||||
i = i <= 0 ? 1 - i : -i; // 0, 1, -1, 2, -2, 3, -3 etc..
|
||||
cursor->changeGraphic(1, sectorCursor[(cursorIndex + i)%sectorCursor.size()]);
|
||||
int index = (cursorIndex + i)%sectorCursor.size(); //hopefully we get elements from sectorCursor
|
||||
cursor->changeGraphic(1, sectorCursor[index]);
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
attackingHex = myNumber - 1; //left
|
||||
break;
|
||||
case 1:
|
||||
attackingHex = myNumber - BFIELD_WIDTH - 1 + zigzagCorrection; //top left
|
||||
break;
|
||||
case 2:
|
||||
attackingHex = myNumber - BFIELD_WIDTH + zigzagCorrection; //top right
|
||||
break;
|
||||
case 3:
|
||||
break;
|
||||
attackingHex = myNumber + 1; //right
|
||||
case 4:
|
||||
break;
|
||||
attackingHex = myNumber + BFIELD_WIDTH + zigzagCorrection; //bottom right
|
||||
case 5:
|
||||
attackingHex = myNumber + BFIELD_WIDTH - 1 + zigzagCorrection; //bottom left
|
||||
break;
|
||||
}
|
||||
THex hex(attackingHex);
|
||||
if (!hex.isValid())
|
||||
attackingHex = -1;
|
||||
|
||||
//setting console info
|
||||
char buf[500];
|
||||
|
@ -442,6 +442,7 @@ private:
|
||||
bool stackCountOutsideHexes[BFIELD_SIZE]; // hexes that when in front of a unit cause it's amount box to move back
|
||||
int previouslyHoveredHex; //number of hex that was hovered by the cursor a while ago
|
||||
int currentlyHoveredHex; //number of hex that is supposed to be hovered (for a while it may be inappropriately set, but will be renewed soon)
|
||||
int attackingHex; //hex from which the stack would perform attack with current cursor
|
||||
float getAnimSpeedMultiplier() const; //returns multiplier for number of frames in a group
|
||||
std::map<int, int> standingFrame; //number of frame in standing animation by stack ID, helps in showing 'random moves'
|
||||
|
||||
|
@ -779,35 +779,30 @@ std::set<CStack*> BattleInfo::getAttackedCreatures(const CSpell * s, int skillLe
|
||||
}
|
||||
return attackedCres;
|
||||
}
|
||||
std::set<CStack*> BattleInfo::getAttackedCreatures(const CStack* attacker, THex destinationTile)
|
||||
{ //TODO: caching?
|
||||
std::set<CStack*> attackedCres;
|
||||
void BattleInfo::getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, THex destinationTile, THex attackerPos)
|
||||
{
|
||||
const int WN = BFIELD_WIDTH;
|
||||
ui16 hex = (attackerPos != THex::INVALID) ? attackerPos : attacker->position.hex; //real or hypothetical (cursor) position
|
||||
if (attacker->hasBonusOfType(Bonus::ATTACKS_ALL_ADJACENT))
|
||||
{
|
||||
std::vector<THex> hexes = attacker->getSurroundingHexes();
|
||||
std::vector<THex> hexes = attacker->getSurroundingHexes(attackerPos);
|
||||
BOOST_FOREACH (THex tile, hexes)
|
||||
{
|
||||
CStack * st = getStackT(tile);
|
||||
if(st && st->owner != attacker->owner) //only hostile stacks - does it work well with Berserk?
|
||||
{
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
at.hostileCreaturePositions.insert(tile);
|
||||
}
|
||||
}
|
||||
ui16 hex = attacker->position.hex;
|
||||
if (attacker->hasBonusOfType(Bonus::THREE_HEADED_ATTACK))
|
||||
{
|
||||
std::vector<THex> hexes = attacker->getSurroundingHexes();
|
||||
std::vector<THex> hexes = attacker->getSurroundingHexes(attackerPos);
|
||||
BOOST_FOREACH (THex tile, hexes)
|
||||
{
|
||||
if (THex::mutualPosition(tile, destinationTile) > -1 && THex::mutualPosition(tile, hex) > -1 //adjacent both to attacker's head and attacked tile
|
||||
|| tile == destinationTile) //or simply attacked directly
|
||||
{
|
||||
CStack * st = getStackT(tile);
|
||||
CStack * st = getStackT(tile, true);
|
||||
if(st && st->owner != attacker->owner) //only hostile stacks - does it work well with Berserk?
|
||||
{
|
||||
attackedCres.insert(st);
|
||||
at.hostileCreaturePositions.insert(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -835,16 +830,63 @@ std::set<CStack*> BattleInfo::getAttackedCreatures(const CStack* attacker, THex
|
||||
}
|
||||
BOOST_FOREACH (THex tile, hexes)
|
||||
{
|
||||
CStack * st = getStackT(tile);
|
||||
CStack * st = getStackT(tile, true);
|
||||
if(st) //friendly stacks can also be damaged by Dragon Breath
|
||||
{
|
||||
attackedCres.insert(st);
|
||||
at.friendlyCreaturePositions.insert(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
std::set<CStack*> BattleInfo::getAttackedCreatures(const CStack* attacker, THex destinationTile, THex attackerPos)
|
||||
{ //TODO: caching?
|
||||
AttackableTiles at;
|
||||
getPotentiallyAttackableHexes(at, attacker, destinationTile, attackerPos);
|
||||
std::set<CStack*> attackedCres;
|
||||
const int WN = BFIELD_WIDTH;
|
||||
BOOST_FOREACH (THex tile, at.hostileCreaturePositions) //all around & three-headed attack
|
||||
{
|
||||
CStack * st = getStackT(tile, true);
|
||||
if(st && st->owner != attacker->owner) //only hostile stacks - does it work well with Berserk?
|
||||
{
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
}
|
||||
BOOST_FOREACH (THex tile, at.friendlyCreaturePositions)
|
||||
{
|
||||
CStack * st = getStackT(tile, true);
|
||||
if(st) //friendly stacks can also be damaged by Dragon Breath
|
||||
{
|
||||
attackedCres.insert(st);
|
||||
}
|
||||
}
|
||||
return attackedCres;
|
||||
}
|
||||
|
||||
std::set<THex> BattleInfo::getAttackedHexes(const CStack* attacker, THex destinationTile, THex attackerPos)
|
||||
{
|
||||
AttackableTiles at;
|
||||
getPotentiallyAttackableHexes(at, attacker, destinationTile, attackerPos);
|
||||
std::set<THex> attackedHexes;
|
||||
BOOST_FOREACH (THex tile, at.hostileCreaturePositions)
|
||||
{
|
||||
CStack * st = getStackT(tile);
|
||||
if(st && st->owner != attacker->owner) //only hostile stacks - does it work well with Berserk?
|
||||
{
|
||||
attackedHexes.insert(tile);
|
||||
}
|
||||
}
|
||||
BOOST_FOREACH (THex tile, at.hostileCreaturePositions)
|
||||
{
|
||||
CStack * st = getStackT(tile);
|
||||
if(st) //friendly stacks can also be damaged by Dragon Breath
|
||||
{
|
||||
attackedHexes.insert(tile);
|
||||
}
|
||||
}
|
||||
return attackedHexes;
|
||||
}
|
||||
|
||||
int BattleInfo::calculateSpellDuration( const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower)
|
||||
{
|
||||
if(!caster)
|
||||
@ -2402,39 +2444,40 @@ bool CStack::coversPos(THex pos) const
|
||||
return vstd::contains(getHexes(), pos);
|
||||
}
|
||||
|
||||
std::vector<THex> CStack::getSurroundingHexes() const
|
||||
std::vector<THex> CStack::getSurroundingHexes(THex attackerPos) const
|
||||
{
|
||||
THex hex = (attackerPos != THex::INVALID) ? attackerPos : position.hex; //use hypothetical position
|
||||
std::vector<THex> hexes;
|
||||
if (doubleWide())
|
||||
{
|
||||
const int WN = BFIELD_WIDTH;
|
||||
if(attackerOwned)
|
||||
{ //position is equal to front hex
|
||||
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN+1 : WN ), hexes);
|
||||
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN : WN-1 ), hexes);
|
||||
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN-1 : WN-2 ), hexes);
|
||||
THex::checkAndPush(position.hex - 2, hexes);
|
||||
THex::checkAndPush(position.hex + 1, hexes);
|
||||
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN-2 : WN-1 ), hexes);
|
||||
THex::checkAndPush(position.hex + ( (position.hex/WN)%2 ? WN-1 : WN ), hexes);
|
||||
THex::checkAndPush(position.hex + ( (position.hex/WN)%2 ? WN : WN+1 ), hexes);
|
||||
THex::checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), hexes);
|
||||
THex::checkAndPush(hex - ( (hex/WN)%2 ? WN : WN-1 ), hexes);
|
||||
THex::checkAndPush(hex - ( (hex/WN)%2 ? WN-1 : WN-2 ), hexes);
|
||||
THex::checkAndPush(hex - 2, hexes);
|
||||
THex::checkAndPush(hex + 1, hexes);
|
||||
THex::checkAndPush(hex - ( (hex/WN)%2 ? WN-2 : WN-1 ), hexes);
|
||||
THex::checkAndPush(hex + ( (hex/WN)%2 ? WN-1 : WN ), hexes);
|
||||
THex::checkAndPush(hex + ( (hex/WN)%2 ? WN : WN+1 ), hexes);
|
||||
}
|
||||
else
|
||||
{
|
||||
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN+2 : WN+1 ), hexes);
|
||||
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN+1 : WN ), hexes);
|
||||
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN : WN-1 ), hexes);
|
||||
THex::checkAndPush(position.hex + 2, hexes);
|
||||
THex::checkAndPush(position.hex - 1, hexes);
|
||||
THex::checkAndPush(position.hex - ( (position.hex/WN)%2 ? WN-1 : WN ), hexes);
|
||||
THex::checkAndPush(position.hex + ( (position.hex/WN)%2 ? WN : WN+1 ), hexes);
|
||||
THex::checkAndPush(position.hex + ( (position.hex/WN)%2 ? WN+1 : WN+2 ), hexes);
|
||||
THex::checkAndPush(hex - ( (hex/WN)%2 ? WN+2 : WN+1 ), hexes);
|
||||
THex::checkAndPush(hex - ( (hex/WN)%2 ? WN+1 : WN ), hexes);
|
||||
THex::checkAndPush(hex - ( (hex/WN)%2 ? WN : WN-1 ), hexes);
|
||||
THex::checkAndPush(hex + 2, hexes);
|
||||
THex::checkAndPush(hex - 1, hexes);
|
||||
THex::checkAndPush(hex - ( (hex/WN)%2 ? WN-1 : WN ), hexes);
|
||||
THex::checkAndPush(hex + ( (hex/WN)%2 ? WN : WN+1 ), hexes);
|
||||
THex::checkAndPush(hex + ( (hex/WN)%2 ? WN+1 : WN+2 ), hexes);
|
||||
}
|
||||
return hexes;
|
||||
}
|
||||
else
|
||||
{
|
||||
return position.neighbouringTiles();
|
||||
return hex.neighbouringTiles();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -36,6 +36,16 @@ struct DLL_EXPORT SiegeInfo
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_EXPORT AttackableTiles
|
||||
{
|
||||
std::set<THex> hostileCreaturePositions;
|
||||
std::set<THex> friendlyCreaturePositions; //for Dragon Breath
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & hostileCreaturePositions & friendlyCreaturePositions;
|
||||
}
|
||||
};
|
||||
|
||||
struct DLL_EXPORT BattleInfo : public CBonusSystemNode
|
||||
{
|
||||
ui8 sides[2]; //sides[0] - attacker, sides[1] - defender
|
||||
@ -88,7 +98,9 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
|
||||
TDmgRange calculateDmgRange(const CStack* attacker, const CStack* defender, const CGHeroInstance * attackerHero, const CGHeroInstance * defendingHero, bool shooting, ui8 charge, bool lucky, bool deathBlow, bool ballistaDoubleDmg) const; //charge - number of hexes travelled before attack (for champion's jousting); returns pair <min dmg, max dmg>
|
||||
void calculateCasualties(std::map<ui32,si32> *casualties) const; //casualties are array of maps size 2 (attacker, defeneder), maps are (crid => amount)
|
||||
std::set<CStack*> getAttackedCreatures(const CSpell * s, int skillLevel, ui8 attackerOwner, THex destinationTile); //calculates stack affected by given spell
|
||||
std::set<CStack*> getAttackedCreatures(const CStack* attacker, THex destinationTile); //calculates range of multi-hex attacks
|
||||
void getPotentiallyAttackableHexes(AttackableTiles &at, const CStack* attacker, THex destinationTile, THex attackerPos); //hexes around target that could be attacked in melee
|
||||
std::set<CStack*> getAttackedCreatures(const CStack* attacker, THex destinationTile, THex attackerPos = THex::INVALID); //calculates range of multi-hex attacks
|
||||
std::set<THex> getAttackedHexes(const CStack* attacker, THex destinationTile, THex attackerPos = THex::INVALID); //calculates range of multi-hex attacks
|
||||
static int calculateSpellDuration(const CSpell * spell, const CGHeroInstance * caster, int usedSpellPower);
|
||||
CStack * generateNewStack(const CStackInstance &base, int stackID, bool attackerOwned, int slot, THex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
|
||||
CStack * generateNewStack(const CStackBasicDescriptor &base, int stackID, bool attackerOwned, int slot, THex position) const; //helper for CGameHandler::setupBattle and spells addign new stacks to the battlefield
|
||||
@ -191,7 +203,7 @@ public:
|
||||
THex occupiedHex() const; //returns number of occupied hex (not the position) if stack is double wide; otherwise -1
|
||||
std::vector<THex> getHexes() const; //up to two occupied hexes, starting from front
|
||||
bool coversPos(THex position) const; //checks also if unit is double-wide
|
||||
std::vector<THex> getSurroundingHexes() const; // get six or 8 surrounding hexes depending on creature size
|
||||
std::vector<THex> getSurroundingHexes(THex attackerPos = THex::INVALID) const; // get six or 8 surrounding hexes depending on creature size
|
||||
|
||||
void prepareAttacked(BattleStackAttacked &bsa) const; //requires bsa.damageAmout filled
|
||||
|
||||
|
@ -82,6 +82,18 @@ std::vector<int> CBattleInfoCallback::battleGetDistances(const CStack * stack, T
|
||||
|
||||
return ret;
|
||||
}
|
||||
std::set<THex> CBattleInfoCallback::battleGetAttackedHexes(const CStack* attacker, THex destinationTile, THex attackerPos /*= THex::INVALID*/)
|
||||
{
|
||||
if(!gs->curB)
|
||||
{
|
||||
|
||||
tlog1 << "battleGetAttackedHexes called when there is no battle!\n";
|
||||
std::set<THex> set;
|
||||
return set;
|
||||
}
|
||||
|
||||
return gs->curB->getAttackedHexes(attacker, destinationTile, attackerPos);
|
||||
}
|
||||
|
||||
SpellCasting::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell( const CSpell * spell )
|
||||
{
|
||||
|
@ -100,6 +100,7 @@ public:
|
||||
void battleGetStackCountOutsideHexes(bool *ac); // returns hexes which when in front of a stack cause us to move the amount box back
|
||||
std::vector<THex> battleGetAvailableHexes(const CStack * stack, bool addOccupiable, std::vector<THex> * attackable = NULL); //returns numbers of hexes reachable by creature with id ID
|
||||
std::vector<int> battleGetDistances(const CStack * stack, THex hex = THex::INVALID, THex * predecessors = NULL); //returns vector of distances to [dest hex number]
|
||||
std::set<THex> battleGetAttackedHexes(const CStack* attacker, THex destinationTile, THex attackerPos = THex::INVALID);
|
||||
bool battleCanShoot(const CStack * stack, THex dest); //returns true if unit with id ID can shoot to dest
|
||||
bool battleCanCastSpell(); //returns true, if caller can cast a spell
|
||||
SpellCasting::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell); //determines if given spell can be casted (and returns problem description)
|
||||
|
Loading…
x
Reference in New Issue
Block a user