From 7319e5cb0ee71c30e9b8fcbc58e1ae6bbae3b1a8 Mon Sep 17 00:00:00 2001 From: mateuszb Date: Thu, 16 Apr 2009 14:01:27 +0000 Subject: [PATCH] * 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 --- CBattleInterface.cpp | 47 +++++++++++--- CBattleInterface.h | 5 +- CCallback.cpp | 13 +++- CCallback.h | 3 +- CGameState.h | 4 +- hch/CCreatureHandler.cpp | 6 ++ hch/CCreatureHandler.h | 1 + server/CGameHandler.cpp | 132 ++++++++++++++++++++++++++------------- 8 files changed, 153 insertions(+), 58 deletions(-) diff --git a/CBattleInterface.cpp b/CBattleInterface.cpp index 6758b0333..adcea1f85 100644 --- a/CBattleInterface.cpp +++ b/CBattleInterface.cpp @@ -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 obstacles = LOCPLINT->cb->battleGetAllObstacles(); for(int b=0; b &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 &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::vectormainFPSmng); for(size_t g=0; gaddText(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) diff --git a/CBattleInterface.h b/CBattleInterface.h index ddbb4095d..85938ae25 100644 --- a/CBattleInterface.h +++ b/CBattleInterface.h @@ -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) diff --git a/CCallback.cpp b/CCallback.cpp index 5d2cec300..f10d2e05f 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -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 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; diff --git a/CCallback.h b/CCallback.h index f8a654583..695f1c488 100644 --- a/CCallback.h +++ b/CCallback.h @@ -105,6 +105,7 @@ public: virtual std::vector 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 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 diff --git a/CGameState.h b/CGameState.h index 78ab3d7fa..66ead4b1c 100644 --- a/CGameState.h +++ b/CGameState.h @@ -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 stacks; std::vector obstacles; - ui8 castedSpells[2]; + ui8 castedSpells[2]; //[0] - attacker, [1] - defender template void serialize(Handler &h, const int version) { diff --git a/hch/CCreatureHandler.cpp b/hch/CCreatureHandler.cpp index 0b34d9746..81c1f355d 100644 --- a/hch/CCreatureHandler.cpp +++ b/hch/CCreatureHandler.cpp @@ -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 &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(); diff --git a/hch/CCreatureHandler.h b/hch/CCreatureHandler.h index 039dd3a99..7bc3ab754 100644 --- a/hch/CCreatureHandler.h +++ b/hch/CCreatureHandler.h @@ -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 &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 diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 90fd7a101..aac020e3f 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -519,6 +519,7 @@ void CGameHandler::handleConnection(std::set 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 attackedHexes = s->rangeInHexes(ba.destinationTile, h->getSpellSchoolLevel(s)); \ + std::set attackedCres; /*std::set to exclude multiple occurences of two hex creatures*/ \ + for(std::set::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::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 attackedHexes = s->rangeInHexes(ba.destinationTile, h->getSpellSchoolLevel(s)); - std::set attackedCres; //set to exclude multiple occurences of two hex creatures - for(std::set::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 attackedCres; + + for(int it=0; itcurB->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::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 attackedCres; + + for(int it=0; itcurB->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::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