Forráskód Böngészése

* 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

mateuszb 16 éve
szülő
commit
7319e5cb0e
8 módosított fájl, 153 hozzáadás és 58 törlés
  1. 37 10
      CBattleInterface.cpp
  2. 4 1
      CBattleInterface.h
  3. 12 1
      CCallback.cpp
  4. 2 1
      CCallback.h
  5. 2 2
      CGameState.h
  6. 6 0
      hch/CCreatureHandler.cpp
  7. 1 0
      hch/CCreatureHandler.h
  8. 89 43
      server/CGameHandler.cpp

+ 37 - 10
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<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)

+ 4 - 1
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)
 

+ 12 - 1
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<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;

+ 2 - 1
CCallback.h

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

+ 2 - 2
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<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)
 	{

+ 6 - 0
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<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();

+ 1 - 0
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<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
 

+ 89 - 43
server/CGameHandler.cpp

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