瀏覽代碼

Handling of cursor for creatures casting spells.
Lots of refactoring.

DjWarmonger 14 年之前
父節點
當前提交
9a1460c2e0
共有 5 個文件被更改,包括 296 次插入228 次删除
  1. 261 222
      client/CBattleInterface.cpp
  2. 2 2
      client/CBattleInterface.h
  3. 12 4
      lib/BattleState.cpp
  4. 16 0
      lib/IGameCallback.cpp
  5. 5 0
      lib/IGameCallback.h

+ 261 - 222
client/CBattleInterface.cpp

@@ -2006,6 +2006,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 	if(activeStack && !spellDestSelectMode)
 	{
         int lastMouseHoveredStack = mouseHoveredStack;
+		bool stackCastsSpell;
 		mouseHoveredStack = -1;
 		int myNumber = -1; //number of hovered tile
 		for(int g = 0; g < BFIELD_SIZE; ++g)
@@ -2024,7 +2025,7 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 				console->alterTxt = "";
 			}
 		}
-		else
+		else //battlefield hex
 		{
             if(!vstd::contains(occupyableHexes, myNumber) || activeStack->coversPos(myNumber))
 			{
@@ -2032,41 +2033,65 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 				const CStack *sactive = activeStack;
 				if(shere)
 				{
-					if(shere->owner == curInt->playerID) //our stack
+					bool ourStack = shere->owner == curInt->playerID;
+					//determine if creature spell is going to be cast
+					stackCastsSpell = false;
+					if (stackCanCastSpell && spellSelMode > STACK_SPELL_CANCELLED) //player did not decide to cancel this spell
 					{
-						if (shere->alive())
+						if ((int)creatureSpellToCast > -1) //use randomized spell (Faerie Dragon), or only avaliable spell (Archangel)
 						{
-							if(sactive->hasBonusOfType(Bonus::HEALER))
+							const CSpell * spell =  CGI->spellh->spells[creatureSpellToCast];
+							if (curInt->cb->battleCanCastThisSpell(spell, THex(myNumber)) == SpellCasting::OK)
 							{
-								//display the possibility to heal this creature
-								CCS->curh->changeGraphic(1,17);
+								if (spell->positiveness > -1 && ourStack || spell->positiveness < 1 && !ourStack)
+								CCS->curh->changeGraphic(3, 0);
+								stackCastsSpell = true;
 							}
-							else if (stackCanCastSpell && spellSelMode > STACK_SPELL_CANCELLED) //player did not decide to cancel this spell
+						}
+						else if (ourStack) //must have only random positive spell (genie)
+						{
+							if (shere != sactive) //can't cast on itself
 							{
-								//spellDestSelectMode
-								if (curInt->cb->battleCanCastThisSpell(creatureSpellToCast, THex(myNumber)) == SpellCasting::OK)
+								int spellID = curInt->cb->battleGetRandomStackSpell(shere, CBattleInfoCallback::RANDOM_GENIE);
+								if (spellID > -1) //can cast any spell on target stack
+								{
 									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
-								CCS->curh->changeGraphic(1,5);
+									stackCastsSpell = true;
+								}
 							}
-							//setting console text
-							char buf[500];
-							sprintf(buf, CGI->generaltexth->allTexts[297].c_str(), shere->count == 1 ? shere->getCreature()->nameSing.c_str() : shere->getCreature()->namePl.c_str());
-							console->alterTxt = buf;
-							console->whoSetAlter = 0;
-							const time_t curTime = time(NULL);
-							if(shere->ID != lastMouseHoveredStack &&
-							   curTime > lastMouseHoveredStackAnimationTime + HOVER_ANIM_DELTA &&
-							   creAnims[shere->ID]->getType() == CCreatureAnim::HOLDING &&
-							   creAnims[shere->ID]->framesInGroup(CCreatureAnim::MOUSEON) > 0)
+						}
+					}
+
+					if(ourStack) //our stack
+					{
+						if (shere->alive())
+						{
+							if (!stackCastsSpell) //use other abilities of display info
 							{
-								creAnims[shere->ID]->playOnce(CCreatureAnim::MOUSEON);
-								lastMouseHoveredStackAnimationTime = curTime;
+								if(sactive->hasBonusOfType(Bonus::HEALER))
+								{
+									//display the possibility to heal this creature
+									CCS->curh->changeGraphic(1, 17);
+								}
+								else
+								{
+									//info about creature
+									CCS->curh->changeGraphic(1,5);
+								}
+								//setting console text
+								char buf[500];
+								sprintf(buf, CGI->generaltexth->allTexts[297].c_str(), shere->count == 1 ? shere->getCreature()->nameSing.c_str() : shere->getCreature()->namePl.c_str());
+								console->alterTxt = buf;
+								console->whoSetAlter = 0;
+								const time_t curTime = time(NULL);
+								if (shere->ID != lastMouseHoveredStack &&
+								   curTime > lastMouseHoveredStackAnimationTime + HOVER_ANIM_DELTA &&
+								   creAnims[shere->ID]->getType() == CCreatureAnim::HOLDING &&
+								   creAnims[shere->ID]->framesInGroup(CCreatureAnim::MOUSEON) > 0)
+								{
+									creAnims[shere->ID]->playOnce(CCreatureAnim::MOUSEON);
+									lastMouseHoveredStackAnimationTime = curTime;
+								}
 							}
 						} //end of alive
 						else if (sactive->hasBonusOfType(Bonus::DAEMON_SUMMONING) && sactive->casts)
@@ -2074,212 +2099,60 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 							CCS->curh->changeGraphic(3, 0);
 						}
 						mouseHoveredStack = shere->ID; //for dead also?
-					} //not our stack
-					else if(curInt->cb->battleCanShoot(activeStack,myNumber)) //we can shoot enemy
-					{
-						if(curInt->cb->battleHasDistancePenalty(activeStack, myNumber) ||
-							curInt->cb->battleHasWallPenalty(activeStack, myNumber))
-						{
-							CCS->curh->changeGraphic(1,15);
-						}
-						else
-						{
-							CCS->curh->changeGraphic(1,3);
-						}
-						//setting console text
-						char buf[500];
-						//calculating estimated dmg
-						std::pair<ui32, ui32> estimatedDmg = curInt->cb->battleEstimateDamage(sactive, shere);
-						std::ostringstream estDmg;
-						estDmg << estimatedDmg.first << " - " << estimatedDmg.second;
-						//printing
-						sprintf(buf, CGI->generaltexth->allTexts[296].c_str(), shere->count == 1 ? shere->getCreature()->nameSing.c_str() : shere->getCreature()->namePl.c_str(), sactive->shots, estDmg.str().c_str());
-						console->alterTxt = buf;
-						console->whoSetAlter = 0;
 					}
-					else if(isTileAttackable(myNumber)) //available enemy (melee attackable)
+					//end of our stack
+					else if (!stackCastsSpell) //if not, then try attack
 					{
-						CCursorHandler *cursor = CCS->curh;
-						const CBattleHex &hoveredHex = bfield[myNumber];
-
-						const double subdividingAngle = 2.0*M_PI/6.0; // Divide a hex into six sectors.
-						const double hexMidX = hoveredHex.pos.x + hoveredHex.pos.w/2;
-						const double hexMidY = hoveredHex.pos.y + hoveredHex.pos.h/2;
-						const double cursorHexAngle = M_PI - atan2(hexMidY - cursor->ypos, cursor->xpos - hexMidX) + subdividingAngle/2; //TODO: refactor this nightmare
-						const double sector = fmod(cursorHexAngle/subdividingAngle, 6.0);
-						const int zigzagCorrection = !((myNumber/BFIELD_WIDTH)%2); // Off-by-one correction needed to deal with the odd battlefield rows.
-
-						std::vector<int> sectorCursor; // From left to bottom left.
-						sectorCursor.push_back(8);
-						sectorCursor.push_back(9);
-						sectorCursor.push_back(10);
-						sectorCursor.push_back(11);
-						sectorCursor.push_back(12);
-						sectorCursor.push_back(7);
-
-						const bool doubleWide = activeStack->doubleWide();
-						bool aboveAttackable = true, belowAttackable = true;
-
-						// Exclude directions which cannot be attacked from.
-						// Check to the left.
-						if (myNumber%BFIELD_WIDTH <= 1 || !vstd::contains(occupyableHexes, myNumber - 1)) 
-						{
-							sectorCursor[0] = -1;
-						}
-						// Check top left, top right as well as above for 2-hex creatures.
-						if (myNumber/BFIELD_WIDTH == 0) 
+						if (curInt->cb->battleCanShoot(activeStack,myNumber)) //we can shoot enemy
 						{
-								sectorCursor[1] = -1;
-								sectorCursor[2] = -1;
-								aboveAttackable = false;
-						} 
-						else 
-						{
-							if (doubleWide) 
+							if(curInt->cb->battleHasDistancePenalty(activeStack, myNumber) ||
+								curInt->cb->battleHasWallPenalty(activeStack, myNumber))
 							{
-								bool attackRow[4] = {true, true, true, true};
-
-								if (myNumber%BFIELD_WIDTH <= 1 || !vstd::contains(occupyableHexes, myNumber - BFIELD_WIDTH - 2 + zigzagCorrection))
-									attackRow[0] = false;
-								if (!vstd::contains(occupyableHexes, myNumber - BFIELD_WIDTH - 1 + zigzagCorrection))
-									attackRow[1] = false;
-								if (!vstd::contains(occupyableHexes, myNumber - BFIELD_WIDTH + zigzagCorrection))
-									attackRow[2] = false;
-								if (myNumber%BFIELD_WIDTH >= BFIELD_WIDTH - 2 || !vstd::contains(occupyableHexes, myNumber - BFIELD_WIDTH + 1 + zigzagCorrection))
-									attackRow[3] = false;
-
-								if (!(attackRow[0] && attackRow[1]))
-									sectorCursor[1] = -1;
-								if (!(attackRow[1] && attackRow[2]))
-									aboveAttackable = false;
-								if (!(attackRow[2] && attackRow[3]))
-									sectorCursor[2] = -1;
+								CCS->curh->changeGraphic(1,15);
 							}
 							else
 							{
-								if (!vstd::contains(occupyableHexes, myNumber - BFIELD_WIDTH - 1 + zigzagCorrection))
-									sectorCursor[1] = -1;
-								if (!vstd::contains(occupyableHexes, myNumber - BFIELD_WIDTH + zigzagCorrection))
-									sectorCursor[2] = -1;
+								CCS->curh->changeGraphic(1,3);
 							}
+							//setting console text
+							char buf[500];
+							//calculating estimated dmg
+							std::pair<ui32, ui32> estimatedDmg = curInt->cb->battleEstimateDamage(sactive, shere);
+							std::ostringstream estDmg;
+							estDmg << estimatedDmg.first << " - " << estimatedDmg.second;
+							//printing
+							sprintf(buf, CGI->generaltexth->allTexts[296].c_str(), shere->count == 1 ? shere->getCreature()->nameSing.c_str() : shere->getCreature()->namePl.c_str(),
+								sactive->shots, estDmg.str().c_str());
+							console->alterTxt = buf;
+							console->whoSetAlter = 0;
 						}
-						// Check to the right.
-						if (myNumber%BFIELD_WIDTH >= BFIELD_WIDTH - 2 || !vstd::contains(occupyableHexes, myNumber + 1))
-						{
-							sectorCursor[3] = -1;
-						}
-						// Check bottom right, bottom left as well as below for 2-hex creatures.
-						if (myNumber/BFIELD_WIDTH == BFIELD_HEIGHT - 1)
-						{
-							sectorCursor[4] = -1;
-							sectorCursor[5] = -1;
-							belowAttackable = false;
-						} 
-						else 
+						else if (isTileAttackable(myNumber)) //available enemy (melee attackable)
 						{
-							if (doubleWide)
-							{
-								bool attackRow[4] = {true, true, true, true};
-
-								if (myNumber%BFIELD_WIDTH <= 1 || !vstd::contains(occupyableHexes, myNumber + BFIELD_WIDTH - 2 + zigzagCorrection))
-									attackRow[0] = false;
-								if (!vstd::contains(occupyableHexes, myNumber + BFIELD_WIDTH - 1 + zigzagCorrection))
-									attackRow[1] = false;
-								if (!vstd::contains(occupyableHexes, myNumber + BFIELD_WIDTH + zigzagCorrection))
-									attackRow[2] = false;
-								if (myNumber%BFIELD_WIDTH >= BFIELD_WIDTH - 2 || !vstd::contains(occupyableHexes, myNumber + BFIELD_WIDTH + 1 + zigzagCorrection))
-									attackRow[3] = false;
-
-								if (!(attackRow[0] && attackRow[1]))
-									sectorCursor[5] = -1;
-								if (!(attackRow[1] && attackRow[2]))
-									belowAttackable = false;
-								if (!(attackRow[2] && attackRow[3]))
-									sectorCursor[4] = -1;
-							} 
-							else 
-							{
-								if (!vstd::contains(occupyableHexes, myNumber + BFIELD_WIDTH + zigzagCorrection))
-									sectorCursor[4] = -1;
-								if (!vstd::contains(occupyableHexes, myNumber + BFIELD_WIDTH - 1 + zigzagCorrection))
-									sectorCursor[5] = -1;
-							}
-						}
+							//handle direction of cursor and attackable tile
+							setBattleCursor(myNumber);
 
-						// Determine index from sector.
-						int cursorIndex;
-						if (doubleWide) 
-						{
-							sectorCursor.insert(sectorCursor.begin() + 5, belowAttackable ? 13 : -1);
-							sectorCursor.insert(sectorCursor.begin() + 2, aboveAttackable ? 14 : -1);
-
-							if (sector < 1.5)
-								cursorIndex = sector;
-							else if (sector >= 1.5 && sector < 2.5)
-								cursorIndex = 2;
-							else if (sector >= 2.5 && sector < 4.5)
-								cursorIndex = (int) sector + 1;
-							else if (sector >= 4.5 && sector < 5.5)
-								cursorIndex = 6;
-							else
-								cursorIndex = (int) sector + 2;
-						} 
-						else 
-						{
-							cursorIndex = sector;
+							//setting console info
+							char buf[500];
+							//calculating estimated dmg
+							std::pair<ui32, ui32> estimatedDmg = curInt->cb->battleEstimateDamage(sactive, shere);
+							std::ostringstream estDmg;
+							estDmg << estimatedDmg.first << " - " << estimatedDmg.second;
+							//printing
+							sprintf(buf, CGI->generaltexth->allTexts[36].c_str(), shere->count == 1 ? shere->getCreature()->nameSing.c_str() : shere->getCreature()->namePl.c_str(),
+								estDmg.str().c_str());
+							console->alterTxt = buf;
+							console->whoSetAlter = 0;
 						}
-
-						// Find the closest direction attackable, starting with the right one.
-						// FIXME: Is this really how the original H3 client does it?
-						int i = 0;
-						while (sectorCursor[(cursorIndex + i)%sectorCursor.size()] == -1) //Why hast thou forsaken me?
-							i = i <= 0 ? 1 - i : -i; // 0, 1, -1, 2, -2, 3, -3 etc..
-						int index = (cursorIndex + i)%sectorCursor.size(); //hopefully we get elements from sectorCursor
-						cursor->changeGraphic(1, sectorCursor[index]);
-						switch (index)
+						else //unavailable enemy
 						{
-							case 0:
-								attackingHex = myNumber - 1; //left
-								break;
-							case 1:
-								attackingHex = myNumber - BFIELD_WIDTH - 1 + zigzagCorrection; //top left
-								break;
-							case 2:
-								attackingHex = myNumber - BFIELD_WIDTH + zigzagCorrection; //top right
-								break;
-							case 3:
-								break;
-								attackingHex = myNumber + 1; //right
-							case 4:
-								break;
-								attackingHex = myNumber + BFIELD_WIDTH + zigzagCorrection; //bottom right
-							case 5:
-								attackingHex = myNumber + BFIELD_WIDTH - 1 + zigzagCorrection; //bottom left
-								break;
+							CCS->curh->changeGraphic(1,0);
+							console->alterTxt = "";
+							console->whoSetAlter = 0;
 						}
-						THex hex(attackingHex);
-						if (!hex.isValid())
-							attackingHex = -1;
-
-						//setting console info
-						char buf[500];
-						//calculating estimated dmg
-						std::pair<ui32, ui32> estimatedDmg = curInt->cb->battleEstimateDamage(sactive, shere);
-						std::ostringstream estDmg;
-						estDmg << estimatedDmg.first << " - " << estimatedDmg.second;
-						//printing
-						sprintf(buf, CGI->generaltexth->allTexts[36].c_str(), shere->count == 1 ? shere->getCreature()->nameSing.c_str() : shere->getCreature()->namePl.c_str(), estDmg.str().c_str());
-						console->alterTxt = buf;
-						console->whoSetAlter = 0;
-					}
-					else //unavailable enemy
-					{
-						CCS->curh->changeGraphic(1,0);
-						console->alterTxt = "";
-						console->whoSetAlter = 0;
 					}
-				}
-				else if( sactive && sactive->hasBonusOfType(Bonus::CATAPULT) && isCatapultAttackable(myNumber) ) //catapulting
+				} //end of stack
+				//TODO: allow aiming for creature spells
+				else if (sactive && sactive->hasBonusOfType(Bonus::CATAPULT) && isCatapultAttackable(myNumber)) //catapulting
 				{
 					CCS->curh->changeGraphic(1,16);
 					console->alterTxt = "";
@@ -2401,6 +2274,171 @@ void CBattleInterface::mouseMoved(const SDL_MouseMotionEvent &sEvent)
 	}
 }
 
+void CBattleInterface::setBattleCursor(const int myNumber)
+{
+	const CBattleHex & hoveredHex = bfield[myNumber];
+	CCursorHandler *cursor = CCS->curh;
+
+	const double subdividingAngle = 2.0*M_PI/6.0; // Divide a hex into six sectors.
+	const double hexMidX = hoveredHex.pos.x + hoveredHex.pos.w/2;
+	const double hexMidY = hoveredHex.pos.y + hoveredHex.pos.h/2;
+	const double cursorHexAngle = M_PI - atan2(hexMidY - cursor->ypos, cursor->xpos - hexMidX) + subdividingAngle/2; //TODO: refactor this nightmare
+	const double sector = fmod(cursorHexAngle/subdividingAngle, 6.0);
+	const int zigzagCorrection = !((myNumber/BFIELD_WIDTH)%2); // Off-by-one correction needed to deal with the odd battlefield rows.
+
+	std::vector<int> sectorCursor; // From left to bottom left.
+	sectorCursor.push_back(8);
+	sectorCursor.push_back(9);
+	sectorCursor.push_back(10);
+	sectorCursor.push_back(11);
+	sectorCursor.push_back(12);
+	sectorCursor.push_back(7);
+
+	const bool doubleWide = activeStack->doubleWide();
+	bool aboveAttackable = true, belowAttackable = true;
+
+	// Exclude directions which cannot be attacked from.
+	// Check to the left.
+	if (myNumber%BFIELD_WIDTH <= 1 || !vstd::contains(occupyableHexes, myNumber - 1)) 
+	{
+		sectorCursor[0] = -1;
+	}
+	// Check top left, top right as well as above for 2-hex creatures.
+	if (myNumber/BFIELD_WIDTH == 0) 
+	{
+			sectorCursor[1] = -1;
+			sectorCursor[2] = -1;
+			aboveAttackable = false;
+	} 
+	else 
+	{
+		if (doubleWide) 
+		{
+			bool attackRow[4] = {true, true, true, true};
+
+			if (myNumber%BFIELD_WIDTH <= 1 || !vstd::contains(occupyableHexes, myNumber - BFIELD_WIDTH - 2 + zigzagCorrection))
+				attackRow[0] = false;
+			if (!vstd::contains(occupyableHexes, myNumber - BFIELD_WIDTH - 1 + zigzagCorrection))
+				attackRow[1] = false;
+			if (!vstd::contains(occupyableHexes, myNumber - BFIELD_WIDTH + zigzagCorrection))
+				attackRow[2] = false;
+			if (myNumber%BFIELD_WIDTH >= BFIELD_WIDTH - 2 || !vstd::contains(occupyableHexes, myNumber - BFIELD_WIDTH + 1 + zigzagCorrection))
+				attackRow[3] = false;
+
+			if (!(attackRow[0] && attackRow[1]))
+				sectorCursor[1] = -1;
+			if (!(attackRow[1] && attackRow[2]))
+				aboveAttackable = false;
+			if (!(attackRow[2] && attackRow[3]))
+				sectorCursor[2] = -1;
+		}
+		else
+		{
+			if (!vstd::contains(occupyableHexes, myNumber - BFIELD_WIDTH - 1 + zigzagCorrection))
+				sectorCursor[1] = -1;
+			if (!vstd::contains(occupyableHexes, myNumber - BFIELD_WIDTH + zigzagCorrection))
+				sectorCursor[2] = -1;
+		}
+	}
+	// Check to the right.
+	if (myNumber%BFIELD_WIDTH >= BFIELD_WIDTH - 2 || !vstd::contains(occupyableHexes, myNumber + 1))
+	{
+		sectorCursor[3] = -1;
+	}
+	// Check bottom right, bottom left as well as below for 2-hex creatures.
+	if (myNumber/BFIELD_WIDTH == BFIELD_HEIGHT - 1)
+	{
+		sectorCursor[4] = -1;
+		sectorCursor[5] = -1;
+		belowAttackable = false;
+	} 
+	else 
+	{
+		if (doubleWide)
+		{
+			bool attackRow[4] = {true, true, true, true};
+
+			if (myNumber%BFIELD_WIDTH <= 1 || !vstd::contains(occupyableHexes, myNumber + BFIELD_WIDTH - 2 + zigzagCorrection))
+				attackRow[0] = false;
+			if (!vstd::contains(occupyableHexes, myNumber + BFIELD_WIDTH - 1 + zigzagCorrection))
+				attackRow[1] = false;
+			if (!vstd::contains(occupyableHexes, myNumber + BFIELD_WIDTH + zigzagCorrection))
+				attackRow[2] = false;
+			if (myNumber%BFIELD_WIDTH >= BFIELD_WIDTH - 2 || !vstd::contains(occupyableHexes, myNumber + BFIELD_WIDTH + 1 + zigzagCorrection))
+				attackRow[3] = false;
+
+			if (!(attackRow[0] && attackRow[1]))
+				sectorCursor[5] = -1;
+			if (!(attackRow[1] && attackRow[2]))
+				belowAttackable = false;
+			if (!(attackRow[2] && attackRow[3]))
+				sectorCursor[4] = -1;
+		} 
+		else 
+		{
+			if (!vstd::contains(occupyableHexes, myNumber + BFIELD_WIDTH + zigzagCorrection))
+				sectorCursor[4] = -1;
+			if (!vstd::contains(occupyableHexes, myNumber + BFIELD_WIDTH - 1 + zigzagCorrection))
+				sectorCursor[5] = -1;
+		}
+	}
+
+	// Determine index from sector.
+	int cursorIndex;
+	if (doubleWide) 
+	{
+		sectorCursor.insert(sectorCursor.begin() + 5, belowAttackable ? 13 : -1);
+		sectorCursor.insert(sectorCursor.begin() + 2, aboveAttackable ? 14 : -1);
+
+		if (sector < 1.5)
+			cursorIndex = sector;
+		else if (sector >= 1.5 && sector < 2.5)
+			cursorIndex = 2;
+		else if (sector >= 2.5 && sector < 4.5)
+			cursorIndex = (int) sector + 1;
+		else if (sector >= 4.5 && sector < 5.5)
+			cursorIndex = 6;
+		else
+			cursorIndex = (int) sector + 2;
+	} 
+	else 
+	{
+		cursorIndex = sector;
+	}
+
+	// Find the closest direction attackable, starting with the right one.
+	// FIXME: Is this really how the original H3 client does it?
+	int i = 0;
+	while (sectorCursor[(cursorIndex + i)%sectorCursor.size()] == -1) //Why hast thou forsaken me?
+		i = i <= 0 ? 1 - i : -i; // 0, 1, -1, 2, -2, 3, -3 etc..
+	int index = (cursorIndex + i)%sectorCursor.size(); //hopefully we get elements from sectorCursor
+	cursor->changeGraphic(1, sectorCursor[index]);
+	switch (index)
+	{
+		case 0:
+			attackingHex = myNumber - 1; //left
+			break;
+		case 1:
+			attackingHex = myNumber - BFIELD_WIDTH - 1 + zigzagCorrection; //top left
+			break;
+		case 2:
+			attackingHex = myNumber - BFIELD_WIDTH + zigzagCorrection; //top right
+			break;
+		case 3:
+			break;
+			attackingHex = myNumber + 1; //right
+		case 4:
+			break;
+			attackingHex = myNumber + BFIELD_WIDTH + zigzagCorrection; //bottom right
+		case 5:
+			attackingHex = myNumber + BFIELD_WIDTH - 1 + zigzagCorrection; //bottom left
+			break;
+	}
+	THex hex(attackingHex);
+	if (!hex.isValid())
+		attackingHex = -1;
+}
+
 void CBattleInterface::clickRight(tribool down, bool previousState)
 {
 	if(!down && spellDestSelectMode)
@@ -3490,7 +3528,6 @@ 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
@@ -3510,11 +3547,13 @@ void CBattleInterface::activateStack()
 	if (s->casts && s->hasBonus(Selector::type(Bonus::SPELLCASTER) || Selector::type(Bonus::RANDOM_SPELLCASTER)))
 	{
 		stackCanCastSpell = true;
-
-
+		creatureSpellToCast = curInt->cb->battleGetRandomStackSpell(s, CBattleInfoCallback::RANDOM_AIMED); //faerie dragon can cast only one spell until their next move
 	}
 	else
+	{
 		stackCanCastSpell = false;
+		creatureSpellToCast = -1;
+	}
 
 	GH.fakeMouseMove();
 

+ 2 - 2
client/CBattleInterface.h

@@ -457,8 +457,7 @@ 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
+	TSpell creatureSpellToCast;
 	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
@@ -570,6 +569,7 @@ public:
 	void battleStacksEffectsSet(const SetStackEffect & sse); //called when a specific effect is set to stacks
 	void castThisSpell(int spellID); //called when player has chosen a spell from spellbook
 	void displayEffect(ui32 effect, int destTile); //displays effect of a spell on the battlefield; affected: true - attacker. false - defender
+	void setBattleCursor(const int myNumber); //really complex and messy
 	void endAction(const BattleAction* action);
 	void hideQueue();
 	void showQueue();

+ 12 - 4
lib/BattleState.cpp

@@ -1931,7 +1931,7 @@ TSpell BattleInfo::getRandomBeneficialSpell(const CStack * subject) const
 		if (spell->positiveness == 1) //only positive
 		{
 			if (subject->hasBonusFrom(Bonus::SPELL_EFFECT, i) ||
-				!battleCanCastThisSpellHere(subject->owner, spell, SpellCasting::CREATURE_ACTIVE_CASTING, subject->position))
+				battleCanCastThisSpellHere(subject->owner, spell, SpellCasting::CREATURE_ACTIVE_CASTING, subject->position) != SpellCasting::OK)
 				continue;
 			switch (i)
 			{
@@ -2018,8 +2018,10 @@ TSpell BattleInfo::getRandomBeneficialSpell(const CStack * subject) const
 }
 TSpell BattleInfo::getRandomCastedSpell(const CStack * caster) const
 {
-	int totalWeight = 0;
 	TBonusListPtr bl = caster->getBonuses(Selector::type(Bonus::SPELLCASTER));
+	if (!bl->size())
+		return -1;
+	int totalWeight = 0;
 	BOOST_FOREACH(Bonus * b, *bl)
 	{
 		totalWeight += std::max(b->additionalInfo, 1); //minimal chance to cast is 1
@@ -2027,7 +2029,7 @@ TSpell BattleInfo::getRandomCastedSpell(const CStack * caster) const
 	int randomPos = ran() % totalWeight;
 	BOOST_FOREACH(Bonus * b, *bl)
 	{
-		randomPos -= b->additionalInfo;
+		randomPos -= std::max(b->additionalInfo, 1);
 		if(randomPos < 0)
 		{
 			return b->subtype;
@@ -2110,7 +2112,7 @@ SpellCasting::ESpellCastProblem BattleInfo::battleIsImmune(const CGHeroInstance
 				break;
 		}
 		
-		bool damageSpell = (VLC->spellh->damageSpells.find(spell->id) != VLC->spellh->damageSpells.end());
+		bool damageSpell = (vstd::contains(VLC->spellh->damageSpells, spell->id));
 
 		if (damageSpell && subject->hasBonusOfType(Bonus::DIRECT_DAMAGE_IMMUNITY))
 			return SpellCasting::STACK_IMMUNE_TO_SPELL;
@@ -2136,6 +2138,12 @@ SpellCasting::ESpellCastProblem BattleInfo::battleIsImmune(const CGHeroInstance
 				return SpellCasting::STACK_IMMUNE_TO_SPELL;
 		}
 
+		if (vstd::contains(VLC->spellh->risingSpells, spell->id))
+		{
+			if (subject->count >= subject->baseAmount) //TODO: calculate potential hp raised
+				return SpellCasting::STACK_IMMUNE_TO_SPELL;
+		}
+
 		TBonusListPtr immunities = subject->getBonuses(Selector::type(Bonus::LEVEL_SPELL_IMMUNITY));
 		if(subject->hasBonusOfType(Bonus::NEGATE_ALL_NATURAL_IMMUNITIES))
 		{

+ 16 - 0
lib/IGameCallback.cpp

@@ -119,6 +119,22 @@ SpellCasting::ESpellCastProblem CBattleInfoCallback::battleCanCastThisSpell(cons
 	return gs->curB->battleCanCastThisSpellHere(player, spell, SpellCasting::CREATURE_ACTIVE_CASTING, destination);
 }
 
+TSpell CBattleInfoCallback::battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode)
+{
+	switch (mode)
+	{
+		case RANDOM_GENIE:
+			return gs->curB->getRandomBeneficialSpell(stack); //target
+			break;
+		case RANDOM_AIMED:
+			return gs->curB->getRandomCastedSpell(stack); //caster
+			break;
+		default:
+			tlog1 << "Incorrect mode of battleGetRandomSpell (" << mode <<")\n";
+			return -1;
+	}
+}
+
 si8 CBattleInfoCallback::battleGetTacticDist()
 {
 	if (!gs->curB)

+ 5 - 0
lib/IGameCallback.h

@@ -87,6 +87,10 @@ public:
 	{
 		ONLY_MINE, ONLY_ENEMY, MINE_AND_ENEMY
 	};
+	enum ERandomSpell
+	{
+		RANDOM_GENIE, RANDOM_AIMED
+	};
 
 	//battle
 	int battleGetBattlefieldType(); //   1. sand/shore   2. sand/mesas   3. dirt/birches   4. dirt/hills   5. dirt/pines   6. grass/hills   7. grass/pines   8. lava   9. magic plains   10. snow/mountains   11. snow/trees   12. subterranean   13. swamp/trees   14. fiery fields   15. rock lands   16. magic clouds   17. lucid pools   18. holy ground   19. clover field   20. evil fog   21. "favourable winds" text on magic plains background   22. cursed ground   23. rough   24. ship to ship   25. ship
@@ -105,6 +109,7 @@ public:
 	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
+	TSpell battleGetRandomStackSpell(const CStack * stack, ERandomSpell mode);
 	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