1
0
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:
DjWarmonger 2011-09-10 13:04:20 +00:00
parent ebacb433fe
commit 899aeeb748
6 changed files with 152 additions and 44 deletions

View File

@ -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];

View File

@ -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'

View File

@ -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();
}
}

View File

@ -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

View File

@ -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 )
{

View File

@ -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)