1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Lots of refactoring & work for creature spells.

No new features yet, but summon elementals now work properly again.
This commit is contained in:
DjWarmonger 2011-10-01 19:56:54 +00:00
parent 623325ca61
commit 6fdb984799
13 changed files with 412 additions and 255 deletions

View File

@ -1184,7 +1184,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),
activeStack(NULL), stackToActivate(NULL), mouseHoveredStack(-1), lastMouseHoveredStackAnimationTime(-1), previouslyHoveredHex(-1), spellSelMode(NO_LOCATION),
currentlyHoveredHex(-1), attackingHex(-1), tacticianInterface(NULL), stackCanCastSpell(false), 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)
@ -2003,7 +2003,7 @@ void CBattleInterface::keyPressed(const SDL_KeyboardEvent & key)
}
void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
{
if(activeStack!= NULL && !spellDestSelectMode)
if(activeStack && !spellDestSelectMode)
{
int lastMouseHoveredStack = mouseHoveredStack;
mouseHoveredStack = -1;
@ -2039,6 +2039,18 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
//display the possibility to heal this creature
CCS->curh->changeGraphic(1,17);
}
else if (sactive->hasBonusOfType(Bonus::DAEMON_SUMMONING))
{
CCS->curh->changeGraphic(3, 0);
}
else if (stackCanCastSpell && spellSelMode > STACK_SPELL_CANCELLED) //player did not decide to cancel this spell
{
//spellDestSelectMode
if (curInt->cb->battleCanCastThisSpell(creatureSpellToCast, THex(myNumber)) == SpellCasting::OK)
CCS->curh->changeGraphic(3, 0);
//if (battleIsImmune(NULL, spellToCast, SpellCasting::CREATURE_ACTIVE_CASTING, myNumber) == SpellCasting::OK)
//if (battleCanCastThisSpell(curInt->playerID, spellToCast, SpellCasting::CREATURE_ACTIVE_CASTING))
}
else
{
//info about creature
@ -2709,11 +2721,13 @@ void CBattleInterface::giveCommand(ui8 action, THex tile, ui32 stack, si32 addit
//some basic validations
switch(action)
{
case 6:
assert(curInt->cb->battleGetStackByPos(additional)); //stack to attack must exist
case 2: case 7: case 9:
assert(tile < BFIELD_SIZE);
break;
case BattleAction::WALK_AND_ATTACK:
assert(curInt->cb->battleGetStackByPos(additional)); //stack to attack must exist
case BattleAction::WALK:
case BattleAction::SHOOT:
case BattleAction::CATAPULT:
assert(tile < BFIELD_SIZE);
break;
}
if(!tacticsMode)
@ -2784,7 +2798,7 @@ const CGHeroInstance * CBattleInterface::getActiveHero()
void CBattleInterface::hexLclicked(int whichOne)
{
const CStack * actSt = activeStack;
const CStack* dest = curInt->cb->battleGetStackByPos(whichOne); //creature at destination tile; -1 if there is no one
const CStack* dest = curInt->cb->battleGetStackByPos(whichOne, false); //creature at destination tile; -1 if there is no one
if(!actSt)
{
tlog3 << "Hex l-clicked when no active stack!\n";
@ -2797,29 +2811,30 @@ void CBattleInterface::hexLclicked(int whichOne)
{
if(!myTurn)
return; //we are not permit to do anything
if(spellDestSelectMode)
if(spellDestSelectMode) //TODO: choose target for area creature spell
{
//checking destination
bool allowCasting = true;
bool onlyAlive = spellToCast->additionalInfo != 38 && spellToCast->additionalInfo != 39; //when casting resurrection or animate dead we should be allow to select dead stack
//TODO: more general handling of dead targets
switch(spellSelMode)
{
case 1:
if(!curInt->cb->battleGetStackByPos(whichOne, onlyAlive) || curInt->playerID != curInt->cb->battleGetStackByPos(whichOne, onlyAlive)->owner )
case FRIENDLY_CREATURE:
if(!curInt->cb->battleGetStackByPos(whichOne, onlyAlive) || curInt->playerID != dest->owner )
allowCasting = false;
break;
case 2:
if(!curInt->cb->battleGetStackByPos(whichOne, onlyAlive) || curInt->playerID == curInt->cb->battleGetStackByPos(whichOne, onlyAlive)->owner )
case HOSTILE_CREATURE:
if(!curInt->cb->battleGetStackByPos(whichOne, onlyAlive) || curInt->playerID == dest->owner )
allowCasting = false;
break;
case 3:
case ANY_CREATURE:
if(!curInt->cb->battleGetStackByPos(whichOne, onlyAlive))
allowCasting = false;
break;
case 4:
case OBSTACLE:
if(!blockedByObstacle(whichOne))
allowCasting = false;
case 5: //teleport
case TELEPORT: //teleport
const CSpell *s = CGI->spellh->spells[spellToCast->additionalInfo];
ui8 skill = getActiveHero()->getSpellSchoolLevel(s); //skill level
if (!curInt->cb->battleCanTeleportTo(activeStack, whichOne, skill))
@ -2836,11 +2851,198 @@ void CBattleInterface::hexLclicked(int whichOne)
endCastingSpell();
}
}
else //we don't cast any spell
else //we don't aim for spell target
{
if(!dest || !dest->alive()) //no creature at that tile
bool walkableTile = false;
if (dest)
{
if(std::find(occupyableHexes.begin(),occupyableHexes.end(),whichOne)!=occupyableHexes.end())// and it's in our range
if (dest->alive())
{
if(dest->owner != actSt->owner && curInt->cb->battleCanShoot(activeStack, whichOne)) //shooting
{
CCS->curh->changeGraphic(1, 6); //cursor should be changed
giveCommand (BattleAction::SHOOT, whichOne, activeStack->ID);
}
else if(dest->owner != actSt->owner) //attacking
{
const CStack * actStack = activeStack;
int attackFromHex = -1; //hex from which we will attack chosen stack
switch(CCS->curh->number)
{
case 12: //from bottom right
{
bool doubleWide = actStack->doubleWide();
int destHex = whichOne + ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH : BFIELD_WIDTH+1 ) +
(actStack->attackerOwned && doubleWide ? 1 : 0);
if(vstd::contains(occupyableHexes, destHex))
attackFromHex = destHex;
else if(actStack->attackerOwned) //if we are attacker
{
if(vstd::contains(occupyableHexes, destHex+1))
attackFromHex = destHex+1;
}
else //if we are defender
{
if(vstd::contains(occupyableHexes, destHex-1))
attackFromHex = destHex-1;
}
break;
}
case 7: //from bottom left
{
int destHex = whichOne + ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH-1 : BFIELD_WIDTH );
if(vstd::contains(occupyableHexes, destHex))
attackFromHex = destHex;
else if(actStack->attackerOwned) //if we are attacker
{
if(vstd::contains(occupyableHexes, destHex+1))
attackFromHex = destHex+1;
}
else //if we are defender
{
if(vstd::contains(occupyableHexes, destHex-1))
attackFromHex = destHex-1;
}
break;
}
case 8: //from left
{
if(actStack->doubleWide() && !actStack->attackerOwned)
{
std::vector<THex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
if(vstd::contains(acc, whichOne))
attackFromHex = whichOne - 1;
else
attackFromHex = whichOne - 2;
}
else
{
attackFromHex = whichOne - 1;
}
break;
}
case 9: //from top left
{
int destHex = whichOne - ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH+1 : BFIELD_WIDTH );
if(vstd::contains(occupyableHexes, destHex))
attackFromHex = destHex;
else if(actStack->attackerOwned) //if we are attacker
{
if(vstd::contains(occupyableHexes, destHex+1))
attackFromHex = destHex+1;
}
else //if we are defender
{
if(vstd::contains(occupyableHexes, destHex-1))
attackFromHex = destHex-1;
}
break;
}
case 10: //from top right
{
bool doubleWide = actStack->doubleWide();
int destHex = whichOne - ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH : BFIELD_WIDTH-1 ) +
(actStack->attackerOwned && doubleWide ? 1 : 0);
if(vstd::contains(occupyableHexes, destHex))
attackFromHex = destHex;
else if(actStack->attackerOwned) //if we are attacker
{
if(vstd::contains(occupyableHexes, destHex+1))
attackFromHex = destHex+1;
}
else //if we are defender
{
if(vstd::contains(occupyableHexes, destHex-1))
attackFromHex = destHex-1;
}
break;
}
case 11: //from right
{
if(actStack->doubleWide() && actStack->attackerOwned)
{
std::vector<THex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
if(vstd::contains(acc, whichOne))
attackFromHex = whichOne + 1;
else
attackFromHex = whichOne + 2;
}
else
{
attackFromHex = whichOne + 1;
}
break;
}
case 13: //from bottom
{
int destHex = whichOne + ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH : BFIELD_WIDTH+1 );
if(vstd::contains(occupyableHexes, destHex))
attackFromHex = destHex;
else if(attackingHeroInstance->tempOwner == curInt->cb->getMyColor()) //if we are attacker
{
if(vstd::contains(occupyableHexes, destHex+1))
attackFromHex = destHex+1;
}
else //if we are defender
{
if(vstd::contains(occupyableHexes, destHex-1))
attackFromHex = destHex-1;
}
break;
}
case 14: //from top
{
int destHex = whichOne - ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH : BFIELD_WIDTH-1 );
if(vstd::contains(occupyableHexes, destHex))
attackFromHex = destHex;
else if(attackingHeroInstance->tempOwner == curInt->cb->getMyColor()) //if we are attacker
{
if(vstd::contains(occupyableHexes, destHex+1))
attackFromHex = destHex+1;
}
else //if we are defender
{
if(vstd::contains(occupyableHexes, destHex-1))
attackFromHex = destHex-1;
}
break;
}
}
if(attackFromHex >= 0) //we can be in this line when unreachable creature is L - clicked (as of revision 1308)
{
giveCommand(BattleAction::WALK_AND_ATTACK, attackFromHex, activeStack->ID, whichOne);
CCS->curh->changeGraphic(1, 6); //cursor should be changed
}
}
else if (actSt->hasBonusOfType(Bonus::HEALER) && actSt->owner == dest->owner) //friendly creature we can heal
{ //TODO: spellDestSelectMode > -2 if we don't want to heal but perform some other (?) action
giveCommand(BattleAction::STACK_HEAL, whichOne, activeStack->ID); //command healing
CCS->curh->changeGraphic(1, 6); //cursor should be changed
}
} //stack is not alive
else if (actSt->hasBonusOfType(Bonus::DAEMON_SUMMONING) && actSt->casts &&
actSt->owner == dest->owner && spellSelMode > -2)//friendly body we can (and want) rise
{
giveCommand(BattleAction::DAEMON_SUMMONING, whichOne, activeStack->ID);
CCS->curh->changeGraphic(1, 6); //cursor should be changed
}
else //not a subject of resurrection
walkableTile = true;
}
else
{
walkableTile = true;
}
if (walkableTile) // we can try to move to this tile
{
if(std::find(occupyableHexes.begin(), occupyableHexes.end(), whichOne) != occupyableHexes.end())// and it's in our range
{
CCS->curh->changeGraphic(1, 6); //cursor should be changed
if(activeStack->doubleWide())
@ -2848,186 +3050,20 @@ void CBattleInterface::hexLclicked(int whichOne)
std::vector<THex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
int shiftedDest = whichOne + (activeStack->attackerOwned ? 1 : -1);
if(vstd::contains(acc, whichOne))
giveCommand(2,whichOne,activeStack->ID);
giveCommand (BattleAction::WALK ,whichOne, activeStack->ID);
else if(vstd::contains(acc, shiftedDest))
giveCommand(2,shiftedDest,activeStack->ID);
giveCommand (BattleAction::WALK, shiftedDest, activeStack->ID);
}
else
{
giveCommand(2,whichOne,activeStack->ID);
giveCommand(BattleAction::WALK, whichOne, activeStack->ID);
}
}
else if(actSt->hasBonusOfType(Bonus::CATAPULT) && isCatapultAttackable(whichOne)) //attacking (catapult)
{
giveCommand(9,whichOne,activeStack->ID);
giveCommand(BattleAction::CATAPULT, whichOne, activeStack->ID);
}
}
else if(dest->owner != actSt->owner
&& curInt->cb->battleCanShoot(activeStack, whichOne) ) //shooting
{
CCS->curh->changeGraphic(1, 6); //cursor should be changed
giveCommand(7,whichOne,activeStack->ID);
}
else if(dest->owner != actSt->owner) //attacking
{
const CStack * actStack = activeStack;
int attackFromHex = -1; //hex from which we will attack chosen stack
switch(CCS->curh->number)
{
case 12: //from bottom right
{
bool doubleWide = actStack->doubleWide();
int destHex = whichOne + ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH : BFIELD_WIDTH+1 ) +
(actStack->attackerOwned && doubleWide ? 1 : 0);
if(vstd::contains(occupyableHexes, destHex))
attackFromHex = destHex;
else if(actStack->attackerOwned) //if we are attacker
{
if(vstd::contains(occupyableHexes, destHex+1))
attackFromHex = destHex+1;
}
else //if we are defender
{
if(vstd::contains(occupyableHexes, destHex-1))
attackFromHex = destHex-1;
}
break;
}
case 7: //from bottom left
{
int destHex = whichOne + ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH-1 : BFIELD_WIDTH );
if(vstd::contains(occupyableHexes, destHex))
attackFromHex = destHex;
else if(actStack->attackerOwned) //if we are attacker
{
if(vstd::contains(occupyableHexes, destHex+1))
attackFromHex = destHex+1;
}
else //if we are defender
{
if(vstd::contains(occupyableHexes, destHex-1))
attackFromHex = destHex-1;
}
break;
}
case 8: //from left
{
if(actStack->doubleWide() && !actStack->attackerOwned)
{
std::vector<THex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
if(vstd::contains(acc, whichOne))
attackFromHex = whichOne - 1;
else
attackFromHex = whichOne - 2;
}
else
{
attackFromHex = whichOne - 1;
}
break;
}
case 9: //from top left
{
int destHex = whichOne - ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH+1 : BFIELD_WIDTH );
if(vstd::contains(occupyableHexes, destHex))
attackFromHex = destHex;
else if(actStack->attackerOwned) //if we are attacker
{
if(vstd::contains(occupyableHexes, destHex+1))
attackFromHex = destHex+1;
}
else //if we are defender
{
if(vstd::contains(occupyableHexes, destHex-1))
attackFromHex = destHex-1;
}
break;
}
case 10: //from top right
{
bool doubleWide = actStack->doubleWide();
int destHex = whichOne - ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH : BFIELD_WIDTH-1 ) +
(actStack->attackerOwned && doubleWide ? 1 : 0);
if(vstd::contains(occupyableHexes, destHex))
attackFromHex = destHex;
else if(actStack->attackerOwned) //if we are attacker
{
if(vstd::contains(occupyableHexes, destHex+1))
attackFromHex = destHex+1;
}
else //if we are defender
{
if(vstd::contains(occupyableHexes, destHex-1))
attackFromHex = destHex-1;
}
break;
}
case 11: //from right
{
if(actStack->doubleWide() && actStack->attackerOwned)
{
std::vector<THex> acc = curInt->cb->battleGetAvailableHexes(activeStack, false);
if(vstd::contains(acc, whichOne))
attackFromHex = whichOne + 1;
else
attackFromHex = whichOne + 2;
}
else
{
attackFromHex = whichOne + 1;
}
break;
}
case 13: //from bottom
{
int destHex = whichOne + ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH : BFIELD_WIDTH+1 );
if(vstd::contains(occupyableHexes, destHex))
attackFromHex = destHex;
else if(attackingHeroInstance->tempOwner == curInt->cb->getMyColor()) //if we are attacker
{
if(vstd::contains(occupyableHexes, destHex+1))
attackFromHex = destHex+1;
}
else //if we are defender
{
if(vstd::contains(occupyableHexes, destHex-1))
attackFromHex = destHex-1;
}
break;
}
case 14: //from top
{
int destHex = whichOne - ( (whichOne/BFIELD_WIDTH)%2 ? BFIELD_WIDTH : BFIELD_WIDTH-1 );
if(vstd::contains(occupyableHexes, destHex))
attackFromHex = destHex;
else if(attackingHeroInstance->tempOwner == curInt->cb->getMyColor()) //if we are attacker
{
if(vstd::contains(occupyableHexes, destHex+1))
attackFromHex = destHex+1;
}
else //if we are defender
{
if(vstd::contains(occupyableHexes, destHex-1))
attackFromHex = destHex-1;
}
break;
}
}
if(attackFromHex >= 0) //we can be in this line when unreachable creature is L - clicked (as of revision 1308)
{
giveCommand(6, attackFromHex, activeStack->ID, whichOne);
CCS->curh->changeGraphic(1, 6); //cursor should be changed
}
}
else if (actSt->hasBonusOfType(Bonus::HEALER) && actSt->owner == dest->owner) //friendly creature we can heal
{
giveCommand(12, whichOne, activeStack->ID); //command healing
CCS->curh->changeGraphic(1, 6); //cursor should be changed
}
}
}
}
@ -3451,6 +3487,7 @@ void CBattleInterface::activateStack()
activeStack = stackToActivate;
stackToActivate = NULL;
const CStack *s = activeStack;
stackSpells.clear();
myTurn = true;
if(attackerInt && defenderInt) //hotseat -> need to pick which interface "takes over" as active
@ -3468,8 +3505,18 @@ void CBattleInterface::activateStack()
//set casting flag to true if creature can use it to not check it every time
if (s->casts && s->hasBonus(Selector::type(Bonus::SPELLCASTER) || Selector::type(Bonus::DAEMON_SUMMONING)))
{
stackCanCastSpell = true;
TBonusListPtr bl = s->getBonuses(Selector::type(Bonus::SPELLCASTER));
BOOST_FOREACH(Bonus * b, *bl)
{
stackSpells.push_back(CGI->spellh->spells[b->subtype]);
}
if (stackSpells.size())
creatureSpellToCast = stackSpells[111 % stackSpells.size()]; //TODO: randomize? weighted chance?
}
GH.fakeMouseMove();
if(!pendingAnims.size() && !active)

View File

@ -5,6 +5,7 @@
#include <list>
#include "GUIBase.h"
#include "../lib/CCreatureSet.h"
#include "../lib/ConstTransitivePtr.h" //may be reundant
#include "CAnimation.h"
/*
@ -419,7 +420,7 @@ class CBattleInterface : public CIntObject
{
enum SpellSelectionType
{
ANY_LOCATION = 0, FRIENDLY_CREATURE, HOSTILE_CREATURE, ANY_CREATURE, OBSTACLE, TELEPORT, NO_LOCATION = -1
ANY_LOCATION = 0, FRIENDLY_CREATURE, HOSTILE_CREATURE, ANY_CREATURE, OBSTACLE, TELEPORT, NO_LOCATION = -1, STACK_SPELL_CANCELLED = -2
};
private:
SDL_Surface * background, * menu, * amountNormal, * amountNegative, * amountPositive, * amountEffNeutral, * cellBorders, * backgroundWithHexes;
@ -456,6 +457,8 @@ private:
bool spellDestSelectMode; //if true, player is choosing destination for his spell
SpellSelectionType spellSelMode;
BattleAction * spellToCast; //spell for which player is choosing destination
CSpell * creatureSpellToCast;
std::vector< ConstTransitivePtr<CSpell> > stackSpells; //all spells possible to cast by the stack
void endCastingSpell(); //ends casting spell (eg. when spell has been cast or canceled)
void showAliveStack(const CStack *stack, SDL_Surface * to); //helper function for function show

View File

@ -632,11 +632,6 @@ void StartAction::applyFirstCl( CClient *cl )
void BattleSpellCast::applyCl( CClient *cl )
{
BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleSpellCast,this);
if(id >= 66 && id <= 69) //elemental summoning
{
BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleNewStackAppeared,GS(cl)->curB->stacks.back());
}
}
void SetStackEffect::applyCl( CClient *cl )
@ -691,6 +686,11 @@ void BattleStacksRemoved::applyCl( CClient *cl )
BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleStacksRemoved, *this);
}
void BattleStackAdded::applyCl( CClient *cl )
{
BATTLE_INTERFACE_CALL_IF_PRESENT_FOR_BOTH_SIDES(battleNewStackAppeared, GS(cl)->curB->stacks.back());
}
CGameState* CPackForClient::GS( CClient *cl )
{
return cl->gs;

View File

@ -327,7 +327,7 @@ namespace SpellCasting
};
enum ECastingMode {HERO_CASTING, AFTER_ATTACK_CASTING, //also includes cast before attack
MAGIC_MIRROR};
MAGIC_MIRROR, CREATURE_ACTIVE_CASTING};
}
namespace Buildings

View File

@ -22,10 +22,10 @@ struct DLL_EXPORT BattleAction
ui32 stackNumber;//stack ID, -1 left hero, -2 right hero,
enum ActionType
{
END_TACTIC_PHASE = -2, INVALID = -1, NO_ACTION = 0, HERO_SPELL, WALK, DEFEND, RETREAT, SURRENDER, WALK_AND_ATTACK, SHOOT, WAIT, CATAPULT, MONSTER_SPELL, BAD_MORALE, STACK_HEAL
END_TACTIC_PHASE = -2, INVALID = -1, NO_ACTION = 0, HERO_SPELL, WALK, DEFEND, RETREAT, SURRENDER, WALK_AND_ATTACK, SHOOT, WAIT, CATAPULT, MONSTER_SPELL, BAD_MORALE,
STACK_HEAL, DAEMON_SUMMONING
};
si8 actionType; //use ActionType enum for values
//10 = Monster casts a spell (i.e. Faerie Dragons) 11 - Bad morale freeze 12 - stacks heals another stack
THex destinationTile;
si32 additionalInfo; // e.g. spell number if type is 1 || 10; tile to attack if type is 6
template <typename Handler> void serialize(Handler &h, const int version)

View File

@ -376,6 +376,41 @@ std::vector<THex> BattleInfo::getAccessibility( const CStack * stack, bool addOc
return ret;
}
int BattleInfo::getAvaliableHex(TCreature creID, bool attackerOwned, int initialPos) const
{
int pos;
if (initialPos > -1)
pos = initialPos;
else
{
if (attackerOwned)
pos = 0; //top left
else
pos = BFIELD_WIDTH; //top right
}
bool ac[BFIELD_SIZE];
std::set<THex> occupyable;
bool twoHex = VLC->creh->creatures[creID]->isDoubleWide();
bool flying = VLC->creh->creatures[creID]->isFlying();// vstd::contains(VLC->creh->creatures[creID]->bonuses, Bonus::FLYING);
getAccessibilityMap(ac, twoHex, attackerOwned, true, occupyable, flying);
for (int g = pos; -1 < g < BFIELD_SIZE; )
{
if ((g % BFIELD_WIDTH != 0) && (g % BFIELD_WIDTH != BFIELD_WIDTH-1) && BattleInfo::isAccessible (g, ac, twoHex, attackerOwned, flying, true))
{
pos = g;
break;
}
if (attackerOwned)
++g; //probably some more sophisticated range-based iteration is needed
else
--g;
}
return pos;
}
bool BattleInfo::isStackBlocked(const CStack * stack) const
{
if(stack->hasBonusOfType(Bonus::SIEGE_WEAPON)) //siege weapons cannot be blocked
@ -1776,7 +1811,7 @@ SpellCasting::ESpellCastProblem BattleInfo::battleCanCastSpell(int player, Spell
switch (mode)
{
case SpellCasting::HERO_CASTING:
case SpellCasting::HERO_CASTING:
{
if(castSpells[side] > 0)
return SpellCasting::ALREADY_CASTED_THIS_TURN;
@ -1800,7 +1835,7 @@ SpellCasting::ESpellCastProblem BattleInfo::battleCanCastThisSpell( int player,
int cside = sides[0] == player ? 0 : 1; //caster's side
switch(mode)
{
case SpellCasting::HERO_CASTING:
case SpellCasting::HERO_CASTING:
{
const CGHeroInstance * caster = heroes[cside];
if(!caster->canCastThisSpell(spell))
@ -1900,7 +1935,10 @@ SpellCasting::ESpellCastProblem BattleInfo::battleCanCastThisSpellHere( int play
if(moreGeneralProblem != SpellCasting::OK)
return moreGeneralProblem;
return battleIsImmune(getHero(player), spell, mode, dest);
if (mode != SpellCasting::CREATURE_ACTIVE_CASTING)
return battleIsImmune(getHero(player), spell, mode, dest);
else
return battleIsImmune(NULL, spell, mode, dest);
}
const CGHeroInstance * BattleInfo::getHero( int player ) const

View File

@ -87,6 +87,7 @@ struct DLL_EXPORT BattleInfo : public CBonusSystemNode
const CStack * getStackT(THex tileID, bool onlyAlive = true) const;
void getAccessibilityMap(bool *accessibility, bool twoHex, bool attackerOwned, bool addOccupiable, std::set<THex> & occupyable, bool flying, const CStack* stackToOmmit = NULL) const; //send pointer to at least 187 allocated bytes
static bool isAccessible(THex hex, bool * accessibility, bool twoHex, bool attackerOwned, bool flying, bool lastPos); //helper for makeBFS
int getAvaliableHex(TCreature creID, bool attackerOwned, int initialPos = -1) const; //find place for summon / clone effects
void makeBFS(THex start, bool*accessibility, THex *predecessor, int *dists, bool twoHex, bool attackerOwned, bool flying, bool fillPredecessors) const; //*accessibility must be prepared bool[187] array; last two pointers must point to the at least 187-elements int arrays - there is written result
std::pair< std::vector<THex>, int > getPath(THex start, THex dest, bool*accessibility, bool flyingCreature, bool twoHex, bool attackerOwned); //returned value: pair<path, length>; length may be different than number of elements in path since flying vreatures jump between distant hexes
std::vector<THex> getAccessibility(const CStack * stack, bool addOccupiable, std::vector<THex> * attackable = NULL) const; //returns vector of accessible tiles (taking into account the creature range)

View File

@ -107,6 +107,18 @@ SpellCasting::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell( con
return gs->curB->battleCanCastThisSpell(player, spell, SpellCasting::HERO_CASTING);
}
SpellCasting::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell(const CSpell * spell, THex destination)
{
if(!gs->curB)
{
tlog1 << "battleCanCastThisSpell called when there is no battle!\n";
return SpellCasting::NO_HERO_TO_CAST_SPELL;
}
return gs->curB->battleCanCastThisSpellHere(player, spell, SpellCasting::CREATURE_ACTIVE_CASTING, destination);
}
si8 CBattleInfoCallback::battleGetTacticDist()
{
if (!gs->curB)

View File

@ -104,6 +104,7 @@ public:
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)
SpellCasting::ESpellCastProblem battleCanCastThisSpell(const CSpell * spell, THex destination); //determines if creature can cast a spell here
bool battleCanFlee(); //returns true if caller can flee from the battle
int battleGetSurrenderCost(); //returns cost of surrendering battle, -1 if surrendering is not possible
const CGTownInstance * battleGetDefendedTown(); //returns defended town if current battle is a siege, NULL instead

View File

@ -1483,6 +1483,25 @@ struct BattleStacksRemoved : public CPackForClient //3016
}
};
struct BattleStackAdded : public CPackForClient //3017
{
BattleStackAdded(){type = 3017;};
DLL_EXPORT void applyGs(CGameState *gs);
void applyCl(CClient *cl);
int attacker; // if true, stack belongs to attacker
int creID;
int amount;
int pos;
int summoned; //if true, remove it afterwards
template <typename Handler> void serialize(Handler &h, const int version)
{
h & attacker & creID & amount & pos & summoned;
}
};
struct ShowInInfobox : public CPackForClient //107
{
ShowInInfobox(){type = 107;};

View File

@ -1019,9 +1019,9 @@ DLL_EXPORT void StartAction::applyGs( CGameState *gs )
case BattleAction::WAIT:
st->state.insert(WAITING);
return;
case BattleAction::NO_ACTION: case BattleAction::WALK: case BattleAction::WALK_AND_ATTACK:
case BattleAction::SHOOT: case BattleAction::CATAPULT: case BattleAction::MONSTER_SPELL:
case BattleAction::BAD_MORALE: case BattleAction::STACK_HEAL:
case BattleAction::HERO_SPELL: //no change in current stack state
return;
default: //any active stack action - attack, catapult, heal, spell...
st->state.insert(MOVED);
break;
}
@ -1046,7 +1046,8 @@ DLL_EXPORT void BattleSpellCast::applyGs( CGameState *gs )
spellCost = VLC->spellh->spells[id]->costs[skill];
}
h->mana -= spellCost;
if(h->mana < 0) h->mana = 0;
if (h->mana < 0)
h->mana = 0;
}
if(side >= 0 && side < 2 && castedByHero)
{
@ -1069,55 +1070,6 @@ DLL_EXPORT void BattleSpellCast::applyGs( CGameState *gs )
}
}
}
//elemental summoning
if(id >= 66 && id <= 69)
{
int creID;
switch(id)
{
case 66:
creID = 114; //fire elemental
break;
case 67:
creID = 113; //earth elemental
break;
case 68:
creID = 115; //water elemental
break;
case 69:
creID = 112; //air elemental
break;
}
// const int3 & tile = gs->curB->tile;
// TerrainTile::EterrainType ter = gs->map->terrain[tile.x][tile.y][tile.z].tertype;
int pos; //position of stack on the battlefield - to be calculated
bool ac[BFIELD_SIZE];
std::set<THex> occupyable;
bool twoHex = VLC->creh->creatures[creID]->isDoubleWide();
bool flying = VLC->creh->creatures[creID]->isFlying();// vstd::contains(VLC->creh->creatures[creID]->bonuses, Bonus::FLYING);
gs->curB->getAccessibilityMap(ac, twoHex, !side, true, occupyable, flying);
for(int g=0; g<BFIELD_SIZE; ++g)
{
if(g % BFIELD_WIDTH != 0 && g % BFIELD_WIDTH != BFIELD_WIDTH-1 && BattleInfo::isAccessible(g, ac, twoHex, !side, flying, true) )
{
pos = g;
break;
}
}
int summonedElementals = h->getPrimSkillLevel(2) * VLC->spellh->spells[id]->powers[skill] * (100 + h->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, id) / 100.0f); //new feature - percentage bonus
CStackInstance *csi = new CStackInstance(creID, summonedElementals); //deleted by d-tor of summoned stack
csi->setArmyObj(h);
CStack * summonedStack = gs->curB->generateNewStack(*csi, gs->curB->stacks.size(), !side, 255, pos);
summonedStack->state.insert(SUMMONED);
summonedStack->attachTo(csi);
summonedStack->postInit();
//summonedStack->addNewBonus( makeFeature(HeroBonus::SUMMONED, HeroBonus::ONE_BATTLE, 0, 0, HeroBonus::BONUS_FROM_HERO) );
gs->curB->stacks.push_back(summonedStack);
}
}
void actualizeEffect(CStack * s, const std::vector<Bonus> & ef)
@ -1308,6 +1260,23 @@ DLL_EXPORT void BattleStacksRemoved::applyGs( CGameState *gs )
}
}
DLL_EXPORT void BattleStackAdded::applyGs(CGameState *gs)
{
if (!THex(pos).isValid())
{
tlog2 << "No place found for new stack!\n";
return;
}
CStackInstance *csi = new CStackInstance(creID, amount);
csi->setArmyObj(gs->curB->belligerents[attacker ? 0 : 1]);
CStack * summonedStack = gs->curB->generateNewStack(*csi, gs->curB->stacks.size(), attacker, 255, pos); //TODO: netpacks?
if (summoned)
summonedStack->state.insert(SUMMONED);
summonedStack->attachTo(csi);
summonedStack->postInit();
gs->curB->stacks.push_back(summonedStack); //the stack is not "SUMMONED", it is permanent
}
DLL_EXPORT void YourTurn::applyGs( CGameState *gs )
{
gs->currentPlayer = player;

View File

@ -156,6 +156,7 @@ void registerTypes2(Serializer &s)
s.template registerType<ObstaclesRemoved>();
s.template registerType<CatapultAttack>();
s.template registerType<BattleStacksRemoved>();
s.template registerType<BattleStackAdded>();
s.template registerType<ShowInInfobox>();
s.template registerType<AdvmapSpellCast>();
s.template registerType<OpenWindow>();

View File

@ -3280,6 +3280,35 @@ bool CGameHandler::makeBattleAction( BattleAction &ba )
}
sendAndApply(&end_action);
break;
}
case BattleAction::DAEMON_SUMMONING:
//TODO: From Strategija:
//Summon Demon is a level 2 spell.
//Cloned Pit Lord stack can use the specialty as well.
{
StartAction start_action(ba);
sendAndApply(&start_action);
CStack *summoner = gs->curB->getStack(ba.stackNumber),
*destStack = gs->curB->getStackT(ba.destinationTile, false);
BattleStackAdded bsa;
bsa.attacker = summoner->attackerOwned;
bsa.creID = summoner->getBonus(Selector::type(Bonus::DAEMON_SUMMONING))->subtype; //in case summoner can summon more than one type of monsters... scream!
ui64 risedHp = summoner->count * summoner->valOfBonuses(Bonus::DAEMON_SUMMONING, bsa.creID);
bsa.amount = std::min ((ui32)(risedHp/destStack->MaxHealth()), destStack->baseAmount);
bsa.pos = gs->curB->getAvaliableHex(bsa.creID, bsa.attacker, destStack->position);
bsa.summoned = false;
BattleStacksRemoved bsr; //remove body
bsr.stackIDs.insert(destStack->ID);
sendAndApply(&bsr);
sendAndApply(&bsa);
sendAndApply(&end_action);
break;
}
@ -3673,6 +3702,41 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, THex destinati
sendAndApply(&shr);
break;
}
case 66:
case 67:
case 68:
case 69:
{ //elemental summoning
int creID;
switch(spellID)
{
case 66:
creID = 114; //fire elemental
break;
case 67:
creID = 113; //earth elemental
break;
case 68:
creID = 115; //water elemental
break;
case 69:
creID = 112; //air elemental
break;
}
BattleStackAdded bsa;
bsa.pos = gs->curB->getAvaliableHex(creID, !(bool)casterSide); //TODO: unify it
bsa.amount = caster->getPrimSkillLevel(2) * VLC->spellh->spells[spellID]->powers[spellLvl] *
(100 + caster->valOfBonuses(Bonus::SPECIFIC_SPELL_DAMAGE, spellID)) / 100.0f; //new feature - percentage bonus
bsa.creID = creID;
bsa.attacker = !(bool)casterSide;
bsa.summoned = true;
sendAndApply(&bsa);
}
break;
case 64: //remove obstacle
{
ObstaclesRemoved obr;
@ -3731,6 +3795,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, THex destinati
sendAndApply(&sc);
if(!si.stacks.empty()) //after spellcast info shows
sendAndApply(&si);
//Magic Mirror effect
if (spell->positiveness < 0 && mode != SpellCasting::MAGIC_MIRROR && spell->level && spell->range[0] == "0") //it is actual spell and can be reflected to single target, no recurrence
{
@ -3779,7 +3844,8 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
}
const CSpell *s = VLC->spellh->spells[ba.additionalInfo];
if (s->mainEffectAnim > -1) //TODO: special effects, like Clone
if (s->mainEffectAnim > -1 || (s->id >= 66 || s->id <= 69)) //allow summon elementals
//TODO: special effects, like Clone
{
ui8 skill = h->getSpellSchoolLevel(s); //skill level