mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
* canceling of casting a spell by pressing Escape or R-click (R-click on a creatures does not cancel a spell)
* new spells: - frost ring - fireball - inferno - meteor shower - death ripple - destroy undead * spellbook button is inactive when hero cannot cast any spell * obstacles will be placed more properly when resolution is different than 800x600 * minor changes
This commit is contained in:
parent
06bb6dcce9
commit
7319e5cb0e
@ -344,6 +344,7 @@ void CBattleInterface::activate()
|
||||
{
|
||||
KeyInterested::activate();
|
||||
MotionInterested::activate();
|
||||
ClickableR::activate();
|
||||
subInt = NULL;
|
||||
bOptions->activate();
|
||||
bSurrender->activate();
|
||||
@ -370,6 +371,7 @@ void CBattleInterface::deactivate()
|
||||
{
|
||||
KeyInterested::deactivate();
|
||||
MotionInterested::deactivate();
|
||||
ClickableR::deactivate();
|
||||
bOptions->deactivate();
|
||||
bSurrender->deactivate();
|
||||
bFlee->deactivate();
|
||||
@ -482,7 +484,7 @@ void CBattleInterface::show(SDL_Surface * to)
|
||||
bConsoleUp->show(to);
|
||||
bConsoleDown->show(to);
|
||||
|
||||
|
||||
//prevents blitting outside this window
|
||||
SDL_GetClipRect(to, &buf);
|
||||
SDL_SetClipRect(to, &pos);
|
||||
|
||||
@ -490,9 +492,9 @@ void CBattleInterface::show(SDL_Surface * to)
|
||||
std::vector<CObstacleInstance> obstacles = LOCPLINT->cb->battleGetAllObstacles();
|
||||
for(int b=0; b<obstacles.size(); ++b)
|
||||
{
|
||||
int x = ((obstacles[b].pos/BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(obstacles[b].pos%BFIELD_WIDTH);
|
||||
int y = 86 + 42 * (obstacles[b].pos/BFIELD_WIDTH);
|
||||
std::vector<Cimage> &images = idToObstacle[obstacles[b].ID]->ourImages;
|
||||
int x = ((obstacles[b].pos/BFIELD_WIDTH)%2==0 ? 22 : 0) + 44*(obstacles[b].pos%BFIELD_WIDTH) + pos.x;
|
||||
int y = 86 + 42 * (obstacles[b].pos/BFIELD_WIDTH) + pos.y;
|
||||
std::vector<Cimage> &images = idToObstacle[obstacles[b].ID]->ourImages; //reference to animation of obstacle
|
||||
blitAt(images[((animCount+1)/(4/settings.animSpeed))%images.size()].bitmap, x, y, to);
|
||||
}
|
||||
|
||||
@ -711,6 +713,10 @@ void CBattleInterface::keyPressed(const SDL_KeyboardEvent & key)
|
||||
{
|
||||
showStackQueue = key.state==SDL_PRESSED;
|
||||
}
|
||||
else if(key.keysym.sym == SDLK_ESCAPE && spellDestSelectMode)
|
||||
{
|
||||
endCastingSpell();
|
||||
}
|
||||
}
|
||||
void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
|
||||
{
|
||||
@ -928,6 +934,14 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
|
||||
}
|
||||
}
|
||||
|
||||
void CBattleInterface::clickRight(boost::logic::tribool down)
|
||||
{
|
||||
if(!down && spellDestSelectMode)
|
||||
{
|
||||
endCastingSpell();
|
||||
}
|
||||
}
|
||||
|
||||
bool CBattleInterface::reverseCreature(int number, int hex, bool wideTrick)
|
||||
{
|
||||
if(creAnims[number]==NULL)
|
||||
@ -1076,11 +1090,13 @@ void CBattleInterface::stackActivated(int number)
|
||||
//block cast spell button if hero doesn't have a spellbook
|
||||
if(attackingHeroInstance && attackingHeroInstance->tempOwner==LOCPLINT->cb->battleGetStackByID(number)->owner)
|
||||
{
|
||||
bSpell->block(!attackingHeroInstance->getArt(17));
|
||||
if(!attackingHeroInstance->getArt(17)) //don't unlock if already locked
|
||||
bSpell->block(!attackingHeroInstance->getArt(17));
|
||||
}
|
||||
else if(defendingHeroInstance && defendingHeroInstance->tempOwner==LOCPLINT->cb->battleGetStackByID(number)->owner)
|
||||
{
|
||||
bSpell->block(!defendingHeroInstance->getArt(17));
|
||||
if(!defendingHeroInstance->getArt(17)) //don't unlock if already locked
|
||||
bSpell->block(!defendingHeroInstance->getArt(17));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1293,6 +1309,7 @@ void CBattleInterface::stacksAreAttacked(std::vector<CBattleInterface::SStackAtt
|
||||
{
|
||||
show(screen);
|
||||
CSDL_Ext::update(screen);
|
||||
SDL_Delay(5);
|
||||
SDL_framerateDelay(LOCPLINT->mainFPSmng);
|
||||
for(size_t g=0; g<attackedInfos.size(); ++g)
|
||||
{
|
||||
@ -1492,6 +1509,9 @@ void CBattleInterface::stackAttacking(int ID, int dest)
|
||||
void CBattleInterface::newRound(int number)
|
||||
{
|
||||
console->addText(CGI->generaltexth->allTexts[412]);
|
||||
|
||||
//unlock spellbook
|
||||
bSpell->block(!LOCPLINT->cb->battleCanCastSpell());
|
||||
}
|
||||
|
||||
void CBattleInterface::giveCommand(ui8 action, ui16 tile, ui32 stack, si32 additional)
|
||||
@ -1549,10 +1569,7 @@ void CBattleInterface::hexLclicked(int whichOne)
|
||||
{
|
||||
spellToCast->destinationTile = whichOne;
|
||||
LOCPLINT->cb->battleMakeAction(spellToCast);
|
||||
delete spellToCast;
|
||||
spellToCast = NULL;
|
||||
spellDestSelectMode = false;
|
||||
CGI->curh->changeGraphic(1, 6);
|
||||
endCastingSpell();
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1979,6 +1996,16 @@ float CBattleInterface::getAnimSpeedMultiplier() const
|
||||
}
|
||||
}
|
||||
|
||||
void CBattleInterface::endCastingSpell()
|
||||
{
|
||||
assert(spellDestSelectMode);
|
||||
|
||||
delete spellToCast;
|
||||
spellToCast = NULL;
|
||||
spellDestSelectMode = false;
|
||||
CGI->curh->changeGraphic(1, 6);
|
||||
}
|
||||
|
||||
void CBattleInterface::attackingShowHelper()
|
||||
{
|
||||
if(attackingInfo && !attackingInfo->reversing)
|
||||
|
@ -151,7 +151,7 @@ struct BattleSettings
|
||||
}
|
||||
};
|
||||
|
||||
class CBattleInterface : public CMainInterface, public MotionInterested, public KeyInterested
|
||||
class CBattleInterface : public CMainInterface, public MotionInterested, public KeyInterested, public ClickableR
|
||||
{
|
||||
private:
|
||||
SDL_Surface * background, * menu, * amountNormal, * amountNegative, * amountPositive, * amountEffNeutral, * cellBorders, * backgroundWithHexes;
|
||||
@ -178,6 +178,7 @@ private:
|
||||
bool spellDestSelectMode; //if true, player is choosing destination for his spell
|
||||
int spellSelMode; //0 - any location, 1 - any firendly creature, 2 - any hostile creature, 3 - any creature, 4 - obstacle, -1 - no location
|
||||
BattleAction * spellToCast; //spell for which player is choosing destination
|
||||
void endCastingSpell(); //ends casting spell (eg. when spell has been casted or cancelled)
|
||||
|
||||
class CAttHelper
|
||||
{
|
||||
@ -259,6 +260,8 @@ public:
|
||||
void show(SDL_Surface * to);
|
||||
void keyPressed(const SDL_KeyboardEvent & key);
|
||||
void mouseMoved(const SDL_MouseMotionEvent &sEvent);
|
||||
void clickRight(boost::logic::tribool down);
|
||||
|
||||
bool reverseCreature(int number, int hex, bool wideTrick = false); //reverses animation of given creature playing animation of reversing
|
||||
void handleStartMoving(int number); //animation of starting move; some units don't have this animation (ie. halberdier)
|
||||
|
||||
|
@ -570,6 +570,17 @@ bool CCallback::battleCanShoot(int ID, int dest)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CCallback::battleCanCastSpell()
|
||||
{
|
||||
if(!gs->curB) //there is no battle
|
||||
return false;
|
||||
|
||||
if(gs->curB->side1 == player)
|
||||
return gs->curB->castedSpells[0] == 0 && gs->getHero(gs->curB->hero1)->getArt(17);
|
||||
else
|
||||
return gs->curB->castedSpells[1] == 0 && gs->getHero(gs->curB->hero2)->getArt(17);
|
||||
}
|
||||
|
||||
void CCallback::swapGarrisonHero( const CGTownInstance *town )
|
||||
{
|
||||
if(town->tempOwner != player) return;
|
||||
|
@ -105,6 +105,7 @@ public:
|
||||
virtual std::vector<int> battleGetAvailableHexes(int ID, bool addOccupiable)=0; //reutrns numbers of hexes reachable by creature with id ID
|
||||
virtual bool battleIsStackMine(int ID)=0; //returns true if stack with id ID belongs to caller
|
||||
virtual bool battleCanShoot(int ID, int dest)=0; //returns true if unit with id ID can shoot to dest
|
||||
virtual bool battleCanCastSpell()=0; //returns true, if caller can cast a spell
|
||||
};
|
||||
|
||||
struct HeroMoveDetails
|
||||
@ -197,7 +198,7 @@ public:
|
||||
std::vector<int> battleGetAvailableHexes(int ID, bool addOccupiable); //reutrns numbers of hexes reachable by creature with id ID
|
||||
bool battleIsStackMine(int ID); //returns true if stack with id ID belongs to caller
|
||||
bool battleCanShoot(int ID, int dest); //returns true if unit with id ID can shoot to dest
|
||||
|
||||
bool battleCanCastSpell(); //returns true, if caller can cast a spell
|
||||
|
||||
//XXX hmmm _tmain on _GNUC_ wtf?
|
||||
//friends
|
||||
|
@ -107,7 +107,7 @@ struct DLL_EXPORT CObstacleInstance
|
||||
|
||||
struct DLL_EXPORT BattleInfo
|
||||
{
|
||||
ui8 side1, side2;
|
||||
ui8 side1, side2; //side1 - attacker, side2 - defender
|
||||
si32 round, activeStack;
|
||||
ui8 siege; // = 0 ordinary battle = 1 a siege with a Fort = 2 a siege with a Citadel = 3 a siege with a Castle
|
||||
int3 tile; //for background and bonuses
|
||||
@ -115,7 +115,7 @@ struct DLL_EXPORT BattleInfo
|
||||
CCreatureSet army1, army2;
|
||||
std::vector<CStack*> stacks;
|
||||
std::vector<CObstacleInstance> obstacles;
|
||||
ui8 castedSpells[2];
|
||||
ui8 castedSpells[2]; //[0] - attacker, [1] - defender
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
|
@ -63,6 +63,10 @@ bool CCreature::isShooting() const
|
||||
{
|
||||
return vstd::contains(abilities,SHOOTER);
|
||||
}
|
||||
bool CCreature::isUndead() const
|
||||
{
|
||||
return vstd::contains(abilities,UNDEAD);
|
||||
}
|
||||
si32 CCreature::maxAmount(const std::vector<si32> &res) const //how many creatures can be bought
|
||||
{
|
||||
int ret = 2147483645;
|
||||
@ -331,6 +335,8 @@ void CCreatureHandler::loadCreatures()
|
||||
ncre.abilities.insert(TWICE_ATTACK);
|
||||
if(boost::algorithm::find_first(ncre.abilityRefs, "const_free_attack"))
|
||||
ncre.abilities.insert(NO_ENEMY_RETALIATION);
|
||||
if(boost::algorithm::find_first(ncre.abilityRefs, "IS_UNDEAD"))
|
||||
ncre.abilities.insert(UNDEAD);
|
||||
if(ncre.nameSing!=std::string("") && ncre.namePl!=std::string(""))
|
||||
{
|
||||
ncre.idNumber = creatures.size();
|
||||
|
@ -45,6 +45,7 @@ public:
|
||||
bool isDoubleWide() const; //returns true if unit is double wide on battlefield
|
||||
bool isFlying() const; //returns true if it is a flying unit
|
||||
bool isShooting() const; //returns true if unit can shoot
|
||||
bool isUndead() const; //returns true if unit is undead
|
||||
si32 maxAmount(const std::vector<si32> &res) const; //how many creatures can be bought
|
||||
static int getQuantityID(const int & quantity); //0 - a few, 1 - several, 2 - pack, 3 - lots, 4 - horde, 5 - throng, 6 - swarm, 7 - zounds, 8 - legion
|
||||
|
||||
|
@ -519,6 +519,7 @@ void CGameHandler::handleConnection(std::set<int> players, CConnection &c)
|
||||
handleConEnd:
|
||||
tlog1 << "Ended handling connection\n";
|
||||
#undef SPELL_CAST_TEMPLATE_1
|
||||
#undef SPELL_CAST_TEMPLATE_2
|
||||
}
|
||||
void CGameHandler::moveStack(int stack, int dest)
|
||||
{
|
||||
@ -2306,6 +2307,28 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
|
||||
sendAndApply(&sse); \
|
||||
}
|
||||
|
||||
#define SPELL_CAST_TEMPLATE_2(EFFECT_ID, DAMAGE) std::set<ui16> attackedHexes = s->rangeInHexes(ba.destinationTile, h->getSpellSchoolLevel(s)); \
|
||||
std::set<CStack*> attackedCres; /*std::set to exclude multiple occurences of two hex creatures*/ \
|
||||
for(std::set<ui16>::iterator it = attackedHexes.begin(); it != attackedHexes.end(); ++it) \
|
||||
{ \
|
||||
CStack * st = gs->curB->getStackT(*it); \
|
||||
if(st) \
|
||||
attackedCres.insert(st); \
|
||||
} \
|
||||
if(attackedCres.size() == 0) break; \
|
||||
StacksInjured si; \
|
||||
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it) \
|
||||
{ \
|
||||
BattleStackAttacked bsa; \
|
||||
bsa.flags |= 2; \
|
||||
bsa.effect = EFFECT_ID; \
|
||||
bsa.damageAmount = DAMAGE; \
|
||||
bsa.stackAttacked = (*it)->ID; \
|
||||
prepareAttacked(bsa,*it); \
|
||||
si.stacks.insert(bsa); \
|
||||
} \
|
||||
sendAndApply(&si); \
|
||||
|
||||
SpellCasted sc;
|
||||
sc.side = ba.side;
|
||||
sc.id = ba.additionalInfo;
|
||||
@ -2316,67 +2339,90 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
|
||||
{
|
||||
case 15: //magic arrow
|
||||
{
|
||||
CStack * attacked = gs->curB->getStackT(ba.destinationTile);
|
||||
if(!attacked) break;
|
||||
BattleStackAttacked bsa;
|
||||
bsa.flags |= 2;
|
||||
bsa.effect = 64;
|
||||
bsa.damageAmount = h->getPrimSkillLevel(2) * 10 + s->powers[h->getSpellSchoolLevel(s)];
|
||||
bsa.stackAttacked = attacked->ID;
|
||||
prepareAttacked(bsa,attacked);
|
||||
sendAndApply(&bsa);
|
||||
SPELL_CAST_TEMPLATE_2(64, h->getPrimSkillLevel(2) * 10 + s->powers[h->getSpellSchoolLevel(s)]);
|
||||
break;
|
||||
}
|
||||
case 16: //ice bolt
|
||||
{
|
||||
CStack * attacked = gs->curB->getStackT(ba.destinationTile);
|
||||
if(!attacked) break;
|
||||
BattleStackAttacked bsa;
|
||||
bsa.flags |= 2;
|
||||
bsa.effect = 46;
|
||||
bsa.damageAmount = h->getPrimSkillLevel(2) * 20 + s->powers[h->getSpellSchoolLevel(s)];
|
||||
bsa.stackAttacked = attacked->ID;
|
||||
prepareAttacked(bsa,attacked);
|
||||
sendAndApply(&bsa);
|
||||
SPELL_CAST_TEMPLATE_2(46, h->getPrimSkillLevel(2) * 20 + s->powers[h->getSpellSchoolLevel(s)]);
|
||||
break;
|
||||
}
|
||||
case 17: //lightning bolt
|
||||
{
|
||||
CStack * attacked = gs->curB->getStackT(ba.destinationTile);
|
||||
if(!attacked) break;
|
||||
BattleStackAttacked bsa;
|
||||
bsa.flags |= 2;
|
||||
bsa.effect = 38;
|
||||
bsa.damageAmount = h->getPrimSkillLevel(2) * 25 + s->powers[h->getSpellSchoolLevel(s)];
|
||||
bsa.stackAttacked = attacked->ID;
|
||||
prepareAttacked(bsa,attacked);
|
||||
sendAndApply(&bsa);
|
||||
SPELL_CAST_TEMPLATE_2(38, h->getPrimSkillLevel(2) * 25 + s->powers[h->getSpellSchoolLevel(s)]);
|
||||
break;
|
||||
}
|
||||
case 18: //implosion
|
||||
{
|
||||
CStack * attacked = gs->curB->getStackT(ba.destinationTile);
|
||||
if(!attacked) break;
|
||||
BattleStackAttacked bsa;
|
||||
bsa.flags |= 2;
|
||||
bsa.effect = 10;
|
||||
bsa.damageAmount = h->getPrimSkillLevel(2) * 75 + s->powers[h->getSpellSchoolLevel(s)];
|
||||
bsa.stackAttacked = attacked->ID;
|
||||
prepareAttacked(bsa,attacked);
|
||||
sendAndApply(&bsa);
|
||||
SPELL_CAST_TEMPLATE_2(10, h->getPrimSkillLevel(2) * 75 + s->powers[h->getSpellSchoolLevel(s)]);
|
||||
break;
|
||||
}
|
||||
case 20: //frost ring
|
||||
{
|
||||
SPELL_CAST_TEMPLATE_2(45, h->getPrimSkillLevel(2) * 10 + s->powers[h->getSpellSchoolLevel(s)]);
|
||||
break;
|
||||
}
|
||||
case 21: //fireball
|
||||
{
|
||||
std::set<ui16> attackedHexes = s->rangeInHexes(ba.destinationTile, h->getSpellSchoolLevel(s));
|
||||
std::set<CStack*> attackedCres; //set to exclude multiple occurences of two hex creatures
|
||||
for(std::set<ui16>::iterator it = attackedHexes.begin(); it != attackedHexes.end(); ++it)
|
||||
{
|
||||
attackedCres.insert(gs->curB->getStackT(*it));
|
||||
}
|
||||
SPELL_CAST_TEMPLATE_2(53, h->getPrimSkillLevel(2) * 10 + s->powers[h->getSpellSchoolLevel(s)]);
|
||||
break;
|
||||
}
|
||||
case 22: //inferno
|
||||
{
|
||||
SPELL_CAST_TEMPLATE_2(9, h->getPrimSkillLevel(2) * 10 + s->powers[h->getSpellSchoolLevel(s)]);
|
||||
break;
|
||||
}
|
||||
case 23: //meteor shower
|
||||
{
|
||||
SPELL_CAST_TEMPLATE_2(16, h->getPrimSkillLevel(2) * 10 + s->powers[h->getSpellSchoolLevel(s)]);
|
||||
break;
|
||||
}
|
||||
case 24: //death ripple
|
||||
{
|
||||
std::set<CStack*> attackedCres;
|
||||
|
||||
if(attackedCres.size()) break;
|
||||
//TODO: the rest of it
|
||||
for(int it=0; it<gs->curB->stacks.size(); ++it)
|
||||
{
|
||||
if(!gs->curB->stacks[it]->creature->isUndead())
|
||||
attackedCres.insert(gs->curB->stacks[it]);
|
||||
}
|
||||
if(attackedCres.size() == 0) break;
|
||||
StacksInjured si;
|
||||
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
|
||||
{
|
||||
BattleStackAttacked bsa;
|
||||
bsa.flags |= 2;
|
||||
bsa.effect = 8;
|
||||
bsa.damageAmount = h->getPrimSkillLevel(2) * 5 + s->powers[h->getSpellSchoolLevel(s)];
|
||||
bsa.stackAttacked = (*it)->ID;
|
||||
prepareAttacked(bsa,*it);
|
||||
si.stacks.insert(bsa);
|
||||
}
|
||||
sendAndApply(&si);
|
||||
break;
|
||||
}
|
||||
case 25: //destroy undead
|
||||
{
|
||||
std::set<CStack*> attackedCres;
|
||||
|
||||
for(int it=0; it<gs->curB->stacks.size(); ++it)
|
||||
{
|
||||
if(gs->curB->stacks[it]->creature->isUndead())
|
||||
attackedCres.insert(gs->curB->stacks[it]);
|
||||
}
|
||||
if(attackedCres.size() == 0) break;
|
||||
StacksInjured si;
|
||||
for(std::set<CStack*>::iterator it = attackedCres.begin(); it != attackedCres.end(); ++it)
|
||||
{
|
||||
BattleStackAttacked bsa;
|
||||
bsa.flags |= 2;
|
||||
bsa.effect = 29;
|
||||
bsa.damageAmount = h->getPrimSkillLevel(2) * 10 + s->powers[h->getSpellSchoolLevel(s)];
|
||||
bsa.stackAttacked = (*it)->ID;
|
||||
prepareAttacked(bsa,*it);
|
||||
si.stacks.insert(bsa);
|
||||
}
|
||||
sendAndApply(&si);
|
||||
break;
|
||||
}
|
||||
case 27: //shield
|
||||
|
Loading…
Reference in New Issue
Block a user