1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-01-12 02:28:11 +02:00

- First part of battle interface rewrite. Untested. May work or not work randomly.

- Tweaks for AI logging.
This commit is contained in:
DjWarmonger 2012-04-16 17:12:39 +00:00
parent 66f5b5e2d7
commit aeb9cf9460
4 changed files with 310 additions and 234 deletions

View File

@ -80,6 +80,8 @@ std::string goalName(EGoals goalType)
return "WIN"; return "WIN";
case CONQUER: case CONQUER:
return "CONQUER"; return "CONQUER";
case BUILD:
return "BUILD";
case EXPLORE: case EXPLORE:
return "EXPLORE"; return "EXPLORE";
case GATHER_ARMY: case GATHER_ARMY:
@ -1496,7 +1498,7 @@ void getVisibleNeighbours(const std::vector<int3> &tiles, std::vector<int3> &out
void VCAI::tryRealize(CGoal g) void VCAI::tryRealize(CGoal g)
{ {
BNLOG("Attempting realizing goal with code %d", g.goalType); BNLOG("Attempting realizing goal with code %s", goalName(g.goalType));
switch(g.goalType) switch(g.goalType)
{ {
case EXPLORE: case EXPLORE:
@ -2849,4 +2851,4 @@ void SectorMap::makeParentBFS(crint3 source)
unsigned char & SectorMap::retreiveTile(crint3 pos) unsigned char & SectorMap::retreiveTile(crint3 pos)
{ {
return retreiveTileN(sector, pos); return retreiveTileN(sector, pos);
} }

View File

@ -37,7 +37,8 @@ public:
enum EGoals enum EGoals
{ {
INVALID = -1, INVALID = -1,
WIN, DO_NOT_LOSE, CONQUER, BUILD, EXPLORE, GATHER_ARMY, BOOST_HERO, WIN, DO_NOT_LOSE, CONQUER, BUILD, //build needs to get a real reasoning
EXPLORE, GATHER_ARMY, BOOST_HERO,
RECRUIT_HERO, RECRUIT_HERO,
BUILD_STRUCTURE, //if hero set, then in visited town BUILD_STRUCTURE, //if hero set, then in visited town
COLLECT_RES, COLLECT_RES,

View File

@ -93,7 +93,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) 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), : queue(NULL), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
activeStack(NULL), stackToActivate(NULL), mouseHoveredStack(-1), lastMouseHoveredStackAnimationTime(-1), previouslyHoveredHex(-1), activeStack(NULL), stackToActivate(NULL), mouseHoveredStack(-1), lastMouseHoveredStackAnimationTime(-1), previouslyHoveredHex(-1),
currentlyHoveredHex(-1), attackingHex(-1), tacticianInterface(NULL), stackCanCastSpell(false), spellDestSelectMode(false), spellSelMode(NO_LOCATION), spellToCast(NULL), currentlyHoveredHex(-1), attackingHex(-1), tacticianInterface(NULL), stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellSelMode(NO_LOCATION), spellToCast(NULL), sp(NULL),
siegeH(NULL), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0), bfield(GameConstants::BFIELD_SIZE), siegeH(NULL), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0), bfield(GameConstants::BFIELD_SIZE),
givenCommand(NULL), myTurn(false), resWindow(NULL), moveStarted(false), moveSh(-1), bresult(NULL) givenCommand(NULL), myTurn(false), resWindow(NULL), moveStarted(false), moveSh(-1), bresult(NULL)
@ -1833,7 +1833,7 @@ void CBattleInterface::castThisSpell(int spellID)
spellSelMode = ANY_LOCATION; spellSelMode = ANY_LOCATION;
} }
if(spellSelMode == NO_LOCATION) //user does not have to select location if (spellSelMode == NO_LOCATION) //user does not have to select location
{ {
spellToCast->destinationTile = -1; spellToCast->destinationTile = -1;
curInt->cb->battleMakeAction(spellToCast); curInt->cb->battleMakeAction(spellToCast);
@ -1841,6 +1841,8 @@ void CBattleInterface::castThisSpell(int spellID)
} }
else else
{ {
possibleActions.clear();
possibleActions.push_back (spellSelMode); //only this one actions can be performed at the moment
GH.fakeMouseMove();//update cursor GH.fakeMouseMove();//update cursor
} }
} }
@ -1986,10 +1988,16 @@ void CBattleInterface::getPossibleActionsForStack(const CStack * stack)
if (stack->hasBonusOfType (Bonus::SPELLCASTER)) if (stack->hasBonusOfType (Bonus::SPELLCASTER))
{ {
//TODO: poll possible spells //TODO: poll possible spells
possibleActions.push_back (OFFENSIVE_SPELL); BOOST_FOREACH (Bonus * spellBonus, *stack->getBonuses (Selector::type(Bonus::SPELLCASTER)))
possibleActions.push_back (FRIENDLY_SPELL); {
possibleActions.push_back (RISING_SPELL); possibleActions.push_back (selectionTypeByPositiveness (*CGI->spellh->spells[spellBonus->subtype]));
//TODO: stacks casting remove obstacle? }
//TODO: determine whether spell is rising
//possibleActions.push_back (RISING_SPELL);
//possibleActions.push_back (OBSTACLE);
//possibleActions.push_back (SACRIFICE);
//possibleActions.push_back (NO_LOCATION);
//possibleActions.push_back (ANY_LOCATION);
//possibleActions.push_back (OTHER_SPELL); //possibleActions.push_back (OTHER_SPELL);
} }
if (stack->hasBonusOfType (Bonus::RANDOM_SPELLCASTER)) if (stack->hasBonusOfType (Bonus::RANDOM_SPELLCASTER))
@ -2557,7 +2565,7 @@ void CBattleInterface::bTacticNextStack(const CStack *current /*= NULL*/)
possibleActions += MOVE_TACTICS, CHOOSE_TACTICS_STACK; possibleActions += MOVE_TACTICS, CHOOSE_TACTICS_STACK;
} }
CBattleInterface::SpellSelectionType CBattleInterface::selectionTypeByPositiveness(const CSpell & spell) CBattleInterface::PossibleActions CBattleInterface::selectionTypeByPositiveness(const CSpell & spell)
{ {
switch(spell.positiveness) switch(spell.positiveness)
{ {
@ -2600,6 +2608,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
std::function<void()> realizeAction; std::function<void()> realizeAction;
//helper lambda that appropriately realizes action / sets cursor and tooltip //helper lambda that appropriately realizes action / sets cursor and tooltip
//TODO: separate action at mouse move and clicking
auto realizeThingsToDo = [&]() auto realizeThingsToDo = [&]()
{ {
if(eventType == MOVE) if(eventType == MOVE)
@ -2620,198 +2629,167 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
const CStack * const shere = curInt->cb->battleGetStackByPos(myNumber, false); const CStack * const shere = curInt->cb->battleGetStackByPos(myNumber, false);
const CStack * const sactive = activeStack; const CStack * const sactive = activeStack;
if(!sactive) if (!sactive)
return; return;
bool creatureCasting = !spellDestSelectMode && stackCanCastSpell && spellSelMode > STACK_SPELL_CANCELLED bool ourStack = false;
&& shere != sactive; if (shere)
ourStack = shere->owner == curInt->playerID;
bool noStackIsHovered = true; //will cause removing a blue glow bool noStackIsHovered = true; //will cause removing a blue glow
localActions.clear(); localActions.clear();
BOOST_FOREACH (int action, localActions) BOOST_FOREACH (PossibleActions action, possibleActions)
{ {
bool legalAction = false;
bool illegalAction = false;
//TODO: copy actions avaliable to perform at this hex to localActions. set currentAction //TODO: copy actions avaliable to perform at this hex to localActions. set currentAction
switch (action)
{
case MOVE_TACTICS:
break;
case CHOOSE_TACTICS_STACK:
if (shere && ourStack)
legalAction = true;
break;
case MOVE_STACK:
if (vstd::contains(occupyableHexes, myNumber) || activeStack->coversPos(myNumber))
//TODO
legalAction = true;
break;
case ATTACK:
case WALK_AND_ATTACK:
case ATTACK_AND_RETURN:
{
std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes (activeStack, false);
BattleHex attackFromHex = fromWhichHexAttack(myNumber);
if(shere->alive() && isTileAttackable(myNumber) && attackFromHex >= 0) //we can be in this line when unreachable creature is L - clicked (as of revision 1308)
legalAction = true;
}
break;
case SHOOT:
if(curInt->cb->battleCanShoot (activeStack, myNumber))
legalAction = true;
break;
case HOSTILE_CREATURE: //TODO: check spell immunity
if (shere && !ourStack && isCastingPossibleHere (sactive, shere, myNumber))
legalAction = true;
break;
case FRIENDLY_CREATURE:
if (shere && ourStack && isCastingPossibleHere (sactive, shere, myNumber))
legalAction = true;
break;
case RISING_SPELL:
if (shere && shere->canBeHealed() && isCastingPossibleHere (sactive, shere, myNumber)) //TODO: at least one stack has to be raised by resurrection / animate dead
legalAction = true;
break;
case RANDOM_GENIE_SPELL:
{
if (shere)
{
int spellID = curInt->cb->battleGetRandomStackSpell(shere, CBattleInfoCallback::RANDOM_GENIE);
if (spellID > -1)
{
sp = CGI->spellh->spells[spellID];
legalAction = true;
}
}
}
break;
case OBSTACLE:
legalAction = false; //TODO
break;
case TELEPORT:
{
const ui8 skill = spellToCast ? getActiveHero()->getSpellSchoolLevel(CGI->spellh->spells[spellToCast->additionalInfo]) : 0; //skill level
//TODO: creature can cast a spell with some skill / spellpower as well
//TODO: explicitely save spell_to_cast * CSpell, power, skill
if (curInt->cb->battleCanTeleportTo(activeStack, myNumber, skill))
legalAction = true;
else
illegalAction = true;
}
break;
case OTHER_SPELL: //TODO
break;
case CATAPULT:
if (isCatapultAttackable(myNumber))
legalAction = true;
break;
case HEAL:
if (shere && ourStack && shere->canBeHealed())
legalAction = true;
break;
case RISE_DEMONS:
if (shere && ourStack && !shere->alive())
legalAction = true;
break;
}
if (legalAction)
localActions.push_back (action);
else if (illegalAction)
illegalActions.push_back (action);
} }
//handle spellcasting (by hero or creature) if (vstd::contains(localActions, selectedAction)) //try to use last selected action by default
if(!tacticsMode && (spellDestSelectMode || creatureCasting)) currentAction = selectedAction;
else if (localActions.size()) //if not possible, select first avaliable action 9they are sorted by suggested priority)
currentAction = localActions.front();
else //no legal action possible
{ {
bool isCastingPossible = true; currentAction = INVALID; //don't allow to do anything
int spellID = -1; if (vstd::contains(illegalActions, selectedAction))
if(creatureCasting) illegalAction = selectedAction;
{ else if (illegalActions.size())
if(creatureSpellToCast > -1) illegalAction = illegalActions.front();
spellID = creatureSpellToCast;
else
{
if(!shere || curInt->cb->battleGetRandomStackSpell(shere, CBattleInfoCallback::RANDOM_GENIE)< 0) //no possible spell for this dest
goto pastCastingSpells; //behave as if stack were not a caster
}
}
else if(spellDestSelectMode) //hero casting
spellID = spellToCast->additionalInfo;
const CSpell *sp = NULL;
if(spellID >= 0)
sp = CGI->spellh->spells[spellID];
if(!myNumber.isAvailable() && !shere) //empty tile outside battlefield (or in the unavailable border column)
isCastingPossible = false;
const ui8 skill = sp ? getActiveHero()->getSpellSchoolLevel(sp) : 0; //skill level
//TODO master genies cast on adv level, that was somewhere in bonuses
if(sp)
{
if(creatureCasting)
isCastingPossible = (curInt->cb->battleCanCreatureCastThisSpell(sp, myNumber) == ESpellCastProblem::OK);
else
isCastingPossible = (curInt->cb->battleCanCastThisSpell(sp, myNumber) == ESpellCastProblem::OK);
}
else else
{ illegalAction = INVALID; //we should never be here
//needed for genie, otherwise covered by battleCan*CastThisSpell
if( !shere
|| !shere->alive()
|| (spellSelMode == HOSTILE_CREATURE && shere->owner == sactive->owner)
|| (spellSelMode == FRIENDLY_CREATURE && shere->owner != sactive->owner))
{
isCastingPossible = false;
}
}
switch(spellSelMode)
{
case FRIENDLY_CREATURE:
case HOSTILE_CREATURE:
case ANY_CREATURE:
if(isCastingPossible && shere)
{
if(sp)
consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[27]) % sp->name % shere->getName()); //Cast %s on %s
else
consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[301]) % shere->getName()); //Cast a spell on %s
}
if(!shere) //there must be a creature as target
isCastingPossible = false;
break;
case OBSTACLE:
if(isCastingPossible)
consoleMsg = CGI->generaltexth->allTexts[550]; //Remove this obstacle
break;
case TELEPORT: //teleport
if (!curInt->cb->battleCanTeleportTo(activeStack, myNumber, skill))
isCastingPossible = false;
if(!isCastingPossible)
consoleMsg = CGI->generaltexth->allTexts[24]; //Invalid Teleport Destination
else
consoleMsg = CGI->generaltexth->allTexts[25]; //Teleport Here
break;
}
//destination checked
if(isCastingPossible)
{
cursorType = ECursor::SPELLBOOK;
cursorFrame = 0;
if(consoleMsg.empty() && sp)
consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % sp->name); //Cast %s
realizeAction = [=]
{
if(creatureCasting)
{
giveCommand(BattleAction::MONSTER_SPELL, myNumber, sactive->ID, creatureSpellToCast);
}
else
{
spellToCast->destinationTile = myNumber;
curInt->cb->battleMakeAction(spellToCast);
endCastingSpell();
}
};
}
else if(creatureCasting) //if creature can't cast on dest tile, we act as if it were not a caster
{
goto pastCastingSpells;
}
else
{
cursorFrame = ECursor::COMBAT_BLOCKED;
consoleMsg = CGI->generaltexth->allTexts[23];
}
//consume our settings and do not look further
realizeThingsToDo();
return;
} }
pastCastingSpells: bool isCastingPossible = false;
if(!vstd::contains(occupyableHexes, myNumber) || activeStack->coversPos(myNumber)) if (currentAction > INVALID)
{ {
if(shere) switch (currentAction) //display console message, realize selected action
{ {
bool ourStack = shere->owner == curInt->playerID; case CHOOSE_TACTICS_STACK:
consoleMsg = (boost::format(CGI->generaltexth->allTexts[481]) % shere->getName()).str(); //Select %s
if(ourStack) //our stack realizeAction = [=]{ stackActivated(shere); };
{ break;
if (shere->alive()) case MOVE:
if(activeStack->hasBonusOfType(Bonus::FLYING))
{ {
if(tacticsMode) //select stack in tactics mdoe cursorFrame = ECursor::COMBAT_FLY;
{ consoleMsg = (boost::format(CGI->generaltexth->allTexts[295]) % activeStack->getName()).str(); //Fly %s here
consoleMsg = (boost::format(CGI->generaltexth->allTexts[481]) % shere->getName()).str(); //Select %s
realizeAction = [=]{ stackActivated(shere); };
}
else if(sactive->hasBonusOfType(Bonus::HEALER) && shere->canBeHealed()) //heal
{
cursorFrame = ECursor::COMBAT_HEAL;
consoleMsg = (boost::format(CGI->generaltexth->allTexts[419]) % shere->getName()).str(); //Apply first aid to the %s
realizeAction = [=]{ giveCommand(BattleAction::STACK_HEAL, myNumber, activeStack->ID); }; //command healing
}
else //info about creature
{
cursorFrame = ECursor::COMBAT_QUERY;
consoleMsg = (boost::format(CGI->generaltexth->allTexts[297]) % shere->getName()).str();
realizeAction = [=]{ GH.pushInt(createCreWindow(shere, true)); };
}
//setting console text
const time_t curTime = time(NULL);
CCreatureAnimation *hoveredStackAnim = creAnims[shere->ID];
if (shere->ID != mouseHoveredStack
&& curTime > lastMouseHoveredStackAnimationTime + HOVER_ANIM_DELTA
&& hoveredStackAnim->getType() == CCreatureAnim::HOLDING
&& hoveredStackAnim->framesInGroup(CCreatureAnim::MOUSEON) > 0)
{
hoveredStackAnim->playOnce(CCreatureAnim::MOUSEON);
lastMouseHoveredStackAnimationTime = curTime;
}
noStackIsHovered = false;
mouseHoveredStack = shere->ID;
} //end of alive
else if (sactive->hasBonusOfType(Bonus::DAEMON_SUMMONING) && sactive->casts)
{
cursorType = ECursor::SPELLBOOK;
realizeAction = [=]{ giveCommand(BattleAction::DAEMON_SUMMONING, myNumber, activeStack->ID); };
} }
}
//end of our stack hovered
else if(curInt->cb->battleCanShoot(activeStack,myNumber)) //we can shoot enemy
{
if(curInt->cb->battleHasShootingPenalty(activeStack, myNumber))
cursorFrame = ECursor::COMBAT_SHOOT_PENALTY;
else else
cursorFrame = ECursor::COMBAT_SHOOT; {
cursorFrame = ECursor::COMBAT_MOVE;
consoleMsg = (boost::format(CGI->generaltexth->allTexts[294]) % activeStack->getName()).str(); //Move %s here
}
realizeAction = [=] {giveCommand(BattleAction::SHOOT, myNumber, activeStack->ID);}; realizeAction = [=]
std::string estDmgText = formatDmgRange(curInt->cb->battleEstimateDamage(sactive, shere)); //calculating estimated dmg {
//printing - Shoot %s (%d shots left, %s damage) if(activeStack->doubleWide())
consoleMsg = (boost::format(CGI->generaltexth->allTexts[296]) % shere->getName() % sactive->shots % estDmgText).str(); {
} std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
else if (shere->alive() && isTileAttackable(myNumber)) //available enemy (melee attackable) int shiftedDest = myNumber + (activeStack->attackerOwned ? 1 : -1);
if(vstd::contains(acc, myNumber))
giveCommand (BattleAction::WALK ,myNumber, activeStack->ID);
else if(vstd::contains(acc, shiftedDest))
giveCommand (BattleAction::WALK, shiftedDest, activeStack->ID);
}
else
{
giveCommand(BattleAction::WALK, myNumber, activeStack->ID);
}
};
break;
case ATTACK:
case WALK_AND_ATTACK:
case ATTACK_AND_RETURN: //TODO: allow to disable return
{ {
setBattleCursor(myNumber); //handle direction of cursor and attackable tile setBattleCursor(myNumber); //handle direction of cursor and attackable tile
setCursor = false; //don't overwrite settings from the call above setCursor = false; //don't overwrite settings from the call above
@ -2827,65 +2805,159 @@ pastCastingSpells:
std::string estDmgText = formatDmgRange(curInt->cb->battleEstimateDamage(sactive, shere)); //calculating estimated dmg std::string estDmgText = formatDmgRange(curInt->cb->battleEstimateDamage(sactive, shere)); //calculating estimated dmg
consoleMsg = (boost::format(CGI->generaltexth->allTexts[36]) % shere->getName() % estDmgText).str(); //Attack %s (%s damage) consoleMsg = (boost::format(CGI->generaltexth->allTexts[36]) % shere->getName() % estDmgText).str(); //Attack %s (%s damage)
} }
else //unavailable enemy break;
case SHOOT:
{ {
cursorFrame = ECursor::COMBAT_BLOCKED; if(curInt->cb->battleHasShootingPenalty(activeStack, myNumber))
cursorFrame = ECursor::COMBAT_SHOOT_PENALTY;
else
cursorFrame = ECursor::COMBAT_SHOOT;
realizeAction = [=] {giveCommand(BattleAction::SHOOT, myNumber, activeStack->ID);};
std::string estDmgText = formatDmgRange(curInt->cb->battleEstimateDamage(sactive, shere)); //calculating estimated dmg
//printing - Shoot %s (%d shots left, %s damage)
consoleMsg = (boost::format(CGI->generaltexth->allTexts[296]) % shere->getName() % sactive->shots % estDmgText).str();
} }
} //end of stack on dest break;
else if (sactive && sactive->hasBonusOfType(Bonus::CATAPULT) && isCatapultAttackable(myNumber)) //"catapulting" case HOSTILE_CREATURE:
{ case FRIENDLY_CREATURE:
cursorFrame = ECursor::COMBAT_SHOOT_CATAPULT; case RISING_SPELL:
realizeAction = [=]{ giveCommand(BattleAction::CATAPULT, myNumber, activeStack->ID); }; case RANDOM_GENIE_SPELL:
} if (spellToCast) //TODO: merge hero spell and creature spell into it
else //empty unavailable tile consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[27]) % sp->name % shere->getName()); //Cast %s on %s
{ else
cursorFrame = ECursor::COMBAT_BLOCKED; consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[301]) % shere->getName()); //Cast a spell on %s
isCastingPossible = true;
//TODO: refactor -> include Teleport and Remove Obstacle
break;
case TELEPORT:
consoleMsg = CGI->generaltexth->allTexts[25]; //Teleport Here
isCastingPossible = true;
break;
case OBSTACLE:
consoleMsg = CGI->generaltexth->allTexts[550];
isCastingPossible = true;
break;
case OTHER_SPELL:
break;
case HEAL:
cursorFrame = ECursor::COMBAT_HEAL;
consoleMsg = (boost::format(CGI->generaltexth->allTexts[419]) % shere->getName()).str(); //Apply first aid to the %s
realizeAction = [=]{ giveCommand(BattleAction::STACK_HEAL, myNumber, activeStack->ID); }; //command healing
break;
case RISE_DEMONS:
cursorType = ECursor::SPELLBOOK;
realizeAction = [=]{ giveCommand(BattleAction::DAEMON_SUMMONING, myNumber, activeStack->ID); };
break;
case CATAPULT:
cursorFrame = ECursor::COMBAT_SHOOT_CATAPULT;
realizeAction = [=]{ giveCommand(BattleAction::CATAPULT, myNumber, activeStack->ID); };
break;
} }
} }
else //occuppiable tile else
{ {
if (activeStack) //there can be a moment when stack is dead ut next is not yet activated switch (illegalAction)
{ {
if(activeStack->hasBonusOfType(Bonus::FLYING)) case HOSTILE_CREATURE:
case FRIENDLY_CREATURE:
case RISING_SPELL:
case RANDOM_GENIE_SPELL:
cursorFrame = ECursor::COMBAT_BLOCKED;
consoleMsg = CGI->generaltexth->allTexts[23];
break;
case TELEPORT:
consoleMsg = CGI->generaltexth->allTexts[24]; //Invalid Teleport Destination
break;
default:
cursorFrame = ECursor::COMBAT_BLOCKED;
break;
}
}
if (isCastingPossible) //common part
{
cursorType = ECursor::SPELLBOOK;
cursorFrame = 0;
if(consoleMsg.empty() && sp)
consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[26]) % sp->name); //Cast %s
realizeAction = [=]
{
if(creatureCasting)
{ {
cursorFrame = ECursor::COMBAT_FLY; giveCommand(BattleAction::MONSTER_SPELL, myNumber, sactive->ID, creatureSpellToCast);
consoleMsg = (boost::format(CGI->generaltexth->allTexts[295]) % activeStack->getName()).str(); //Fly %s here
} }
else else
{ {
cursorFrame = ECursor::COMBAT_MOVE; spellToCast->destinationTile = myNumber;
consoleMsg = (boost::format(CGI->generaltexth->allTexts[294]) % activeStack->getName()).str(); //Move %s here curInt->cb->battleMakeAction(spellToCast);
endCastingSpell();
} }
};
realizeAction = [=]
{
if(activeStack->doubleWide())
{
std::vector<BattleHex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
int shiftedDest = myNumber + (activeStack->attackerOwned ? 1 : -1);
if(vstd::contains(acc, myNumber))
giveCommand (BattleAction::WALK ,myNumber, activeStack->ID);
else if(vstd::contains(acc, shiftedDest))
giveCommand (BattleAction::WALK, shiftedDest, activeStack->ID);
}
else
{
giveCommand(BattleAction::WALK, myNumber, activeStack->ID);
}
};
}
} }
if (vstd::contains(localActions, selectedAction))
currentAction = selectedAction;
else if (localActions.size())
currentAction = localActions.front();
else
currentAction = INVALID;
realizeThingsToDo(); realizeThingsToDo();
if(noStackIsHovered) if(noStackIsHovered)
mouseHoveredStack = -1; mouseHoveredStack = -1;
if (shere && ourStack && shere->alive()) //TODO: handle hover properly
{
cursorFrame = ECursor::COMBAT_QUERY;
consoleMsg = (boost::format(CGI->generaltexth->allTexts[297]) % shere->getName()).str();
realizeAction = [=]{ GH.pushInt(createCreWindow(shere, true)); };
//setting console text
const time_t curTime = time(NULL);
CCreatureAnimation *hoveredStackAnim = creAnims[shere->ID];
if (shere->ID != mouseHoveredStack
&& curTime > lastMouseHoveredStackAnimationTime + HOVER_ANIM_DELTA
&& hoveredStackAnim->getType() == CCreatureAnim::HOLDING
&& hoveredStackAnim->framesInGroup(CCreatureAnim::MOUSEON) > 0)
{
hoveredStackAnim->playOnce(CCreatureAnim::MOUSEON);
lastMouseHoveredStackAnimationTime = curTime;
}
noStackIsHovered = false;
mouseHoveredStack = shere->ID;
}
}
bool CBattleInterface::isCastingPossibleHere (const CStack * sactive, const CStack * shere, BattleHex myNumber)
{
creatureCasting = (spellDestSelectMode >= NO_LOCATION && spellDestSelectMode <= OTHER_SPELL) && //what does it really check?
stackCanCastSpell && shere != sactive;
//TODO: use currentAction
bool isCastingPossible = true;
int spellID = -1;
if (creatureCasting)
{
if (creatureSpellToCast > -1)
spellID = creatureSpellToCast;
}
else if(spellDestSelectMode) //hero casting
spellID = spellToCast->additionalInfo;
sp = NULL;
if (spellID >= 0)
sp = CGI->spellh->spells[spellID];
if (sp) //TODO: refactor?
{
if (creatureCasting)
isCastingPossible = (curInt->cb->battleCanCreatureCastThisSpell (sp, myNumber) == ESpellCastProblem::OK);
else
isCastingPossible = (curInt->cb->battleCanCastThisSpell (sp, myNumber) == ESpellCastProblem::OK);
}
if(!myNumber.isAvailable() && !shere) //empty tile outside battlefield (or in the unavailable border column)
isCastingPossible = false;
return isCastingPossible;
} }
BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber) BattleHex CBattleInterface::fromWhichHexAttack(BattleHex myNumber)

View File

@ -91,16 +91,12 @@ struct CatapultProjectileInfo
/// drawing everything correctly. /// drawing everything correctly.
class CBattleInterface : public CIntObject class CBattleInterface : public CIntObject
{ {
enum SpellSelectionType
{
ANY_LOCATION = 0, FRIENDLY_CREATURE, HOSTILE_CREATURE, ANY_CREATURE, OBSTACLE, TELEPORT, NO_LOCATION = -1, STACK_SPELL_CANCELLED = -2
};
enum PossibleActions // actions performed at l-click enum PossibleActions // actions performed at l-click
{ {
INVALID = -1, INVALID = -1,
MOVE_TACTICS, CHOOSE_TACTICS_STACK, MOVE_TACTICS, CHOOSE_TACTICS_STACK,
MOVE_STACK, ATTACK, WALK_AND_ATTACK, ATTACK_AND_RETURN, SHOOT, //OPEN_GATE, //we can open castle gate during siege MOVE_STACK, ATTACK, WALK_AND_ATTACK, ATTACK_AND_RETURN, SHOOT, //OPEN_GATE, //we can open castle gate during siege
OFFENSIVE_SPELL, FRIENDLY_SPELL, RISING_SPELL, RANDOM_GENIE_SPELL, OTHER_SPELL, //use SpellSelectionType for non-standard spells - should we merge it? NO_LOCATION, ANY_LOCATION, FRIENDLY_CREATURE, HOSTILE_CREATURE, RISING_SPELL, ANY_CREATURE, OBSTACLE, TELEPORT, SACRIFICE, RANDOM_GENIE_SPELL, OTHER_SPELL,
CATAPULT, HEAL, RISE_DEMONS CATAPULT, HEAL, RISE_DEMONS
}; };
private: private:
@ -135,14 +131,18 @@ private:
CPlayerInterface * tacticianInterface; //used during tactics mode, points to the interface of player with higher tactics (can be either attacker or defender in hot-seat), valid onloy for human players CPlayerInterface * tacticianInterface; //used during tactics mode, points to the interface of player with higher tactics (can be either attacker or defender in hot-seat), valid onloy for human players
bool tacticsMode; bool tacticsMode;
bool stackCanCastSpell; //if true, active stack could possibly cats some target spell bool stackCanCastSpell; //if true, active stack could possibly cats some target spell
bool creatureCasting; //if true, stack currently aims to cats a spell
bool spellDestSelectMode; //if true, player is choosing destination for his spell bool spellDestSelectMode; //if true, player is choosing destination for his spell
SpellSelectionType spellSelMode; PossibleActions spellSelMode;
BattleAction * spellToCast; //spell for which player is choosing destination BattleAction * spellToCast; //spell for which player is choosing destination
const CSpell * sp; //spell pointer for convenience
si32 creatureSpellToCast; si32 creatureSpellToCast;
std::vector<int> possibleActions; //all actions possible to call at the moment by player std::vector<PossibleActions> possibleActions; //all actions possible to call at the moment by player
std::vector<int> localActions; //actions possible to take on hovered hex std::vector<PossibleActions> localActions; //actions possible to take on hovered hex
int currentAction; //action that will be performed on l-click std::vector<PossibleActions> illegalActions; //these actions display message in case of illegal target
int selectedAction; //last action chosen (and saved) by player PossibleActions currentAction; //action that will be performed on l-click
PossibleActions selectedAction; //last action chosen (and saved) by player
PossibleActions illegalAction; //most likely action that can't be performed here
void getPossibleActionsForStack (const CStack * stack); //called when stack gets its turn void getPossibleActionsForStack (const CStack * stack); //called when stack gets its turn
void endCastingSpell(); //ends casting spell (eg. when spell has been cast or canceled) void endCastingSpell(); //ends casting spell (eg. when spell has been cast or canceled)
@ -260,9 +260,10 @@ public:
void endAction(const BattleAction* action); void endAction(const BattleAction* action);
void hideQueue(); void hideQueue();
void showQueue(); void showQueue();
SpellSelectionType selectionTypeByPositiveness(const CSpell & spell); PossibleActions selectionTypeByPositiveness(const CSpell & spell);
void handleHex(BattleHex myNumber, int eventType); void handleHex(BattleHex myNumber, int eventType);
bool isCastingPossibleHere (const CStack * sactive, const CStack * shere, BattleHex myNumber);
BattleHex fromWhichHexAttack(BattleHex myNumber); BattleHex fromWhichHexAttack(BattleHex myNumber);