1
0
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:
mateuszb 2009-04-16 14:01:27 +00:00
parent 06bb6dcce9
commit 7319e5cb0e
8 changed files with 153 additions and 58 deletions

View File

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

View File

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

View File

@ -547,7 +547,7 @@ bool CCallback::battleIsStackMine(int ID)
return gs->curB->stacks[h]->owner == player;
}
return false;
}
}
bool CCallback::battleCanShoot(int ID, int dest)
{
boost::shared_lock<boost::shared_mutex> lock(*gs->mx);
@ -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;

View File

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

View File

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

View File

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

View File

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

View File

@ -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)
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;
for(int it=0; it<gs->curB->stacks.size(); ++it)
{
attackedCres.insert(gs->curB->getStackT(*it));
if(!gs->curB->stacks[it]->creature->isUndead())
attackedCres.insert(gs->curB->stacks[it]);
}
if(attackedCres.size()) break;
//TODO: the rest of 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