2
0
Эх сурвалжийг харах

- Fixed wrong creature teleported
- Support for Sacrifice

DjWarmonger 13 жил өмнө
parent
commit
25bdcd3cab

+ 35 - 9
client/BattleInterface/CBattleInterface.cpp

@@ -92,7 +92,7 @@ void CBattleInterface::addNewAnim(CBattleAnimation * anim)
 
 CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSet * army2, CGHeroInstance *hero1, CGHeroInstance *hero2, const SDL_Rect & myRect, CPlayerInterface * att, CPlayerInterface * defen)
 	: queue(NULL), attackingHeroInstance(hero1), defendingHeroInstance(hero2), animCount(0),
-	  activeStack(NULL), stackToActivate(NULL), mouseHoveredStack(-1), lastMouseHoveredStackAnimationTime(-1), previouslyHoveredHex(-1),
+	  activeStack(NULL), stackToActivate(NULL), selectedStack(NULL), mouseHoveredStack(-1), lastMouseHoveredStackAnimationTime(-1), previouslyHoveredHex(-1),
 	  currentlyHoveredHex(-1), attackingHex(-1), tacticianInterface(NULL),  stackCanCastSpell(false), creatureCasting(false), spellDestSelectMode(false), spellSelMode(NO_LOCATION), spellToCast(NULL), sp(NULL),
 	  siegeH(NULL), attackerInt(att), defenderInt(defen), curInt(att), animIDhelper(0), bfield(GameConstants::BFIELD_SIZE),
 	  givenCommand(NULL), myTurn(false), resWindow(NULL), moveStarted(false), moveSh(-1), bresult(NULL)
@@ -1385,7 +1385,7 @@ void CBattleInterface::newRound(int number)
 
 }
 
-void CBattleInterface::giveCommand(ui8 action, BattleHex tile, ui32 stackID, si32 additional)
+void CBattleInterface::giveCommand(ui8 action, BattleHex tile, ui32 stackID, si32 additional, si32 selected)
 {
 	const CStack *stack = curInt->cb->battleGetStackByID(stackID);
 	if(!stack && action != BattleAction::HERO_SPELL && action != BattleAction::RETREAT && action != BattleAction::SURRENDER)
@@ -1402,6 +1402,7 @@ void CBattleInterface::giveCommand(ui8 action, BattleHex tile, ui32 stackID, si3
 	ba->destinationTile = tile;
 	ba->stackNumber = stackID;
 	ba->additionalInfo = additional;
+	ba->selectedStack = selected;
 
 	//some basic validations
 	switch(action)
@@ -1591,6 +1592,7 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 	case Spells::RESURRECTION:
 	case Spells::ANIMATE_DEAD:
 	case Spells::DISPEL_HELPFUL_SPELLS:
+	case Spells::SACRIFICE: //TODO: animation upon killed stack
 		for(std::set<ui32>::const_iterator it = sc->affectedCres.begin(); it != sc->affectedCres.end(); ++it)
 		{
 			displayEffect(spell.mainEffectAnim, curInt->cb->battleGetStackByID(*it, false)->position);
@@ -1600,10 +1602,10 @@ void CBattleInterface::spellCast( const BattleSpellCast * sc )
 	case Spells::SUMMON_EARTH_ELEMENTAL:
 	case Spells::SUMMON_WATER_ELEMENTAL:
 	case Spells::SUMMON_AIR_ELEMENTAL:
-	case Spells::CLONE: //TODO: make it smarter?
+	case Spells::CLONE:
 	case Spells::REMOVE_OBSTACLE:
 	case Spells::CHAIN_LIGHTNING:
-		addNewAnim(new CDummyAnimation(this, 2));
+		addNewAnim(new CDummyAnimation(this, 2)); //interface won't return until animation is played. TODO: make it smarter?
 		break;
 	} //switch(sc->id)
 
@@ -2744,7 +2746,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 					legalAction = true;
 				break;
 			case RISING_SPELL:
-				if (shere && shere->canBeHealed() && isCastingPossibleHere (sactive, shere, myNumber)) //TODO: at least one stack has to be raised by resurrection / animate dead
+				if (shere && shere->canBeHealed() && ourStack && isCastingPossibleHere (sactive, shere, myNumber)) //TODO: at least one stack has to be raised by resurrection / animate dead
 					legalAction = true;
 				break;
 			case RANDOM_GENIE_SPELL:
@@ -2771,13 +2773,17 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 				else
 					skill = getActiveHero()->getSpellSchoolLevel (CGI->spellh->spells[spellToCast->additionalInfo]); 
 				//TODO: explicitely save power, skill
-				if (curInt->cb->battleCanTeleportTo(activeStack, myNumber, skill))
+				if (curInt->cb->battleCanTeleportTo(selectedStack, myNumber, skill))
 					legalAction = true;
 				else
 					notLegal = true;
 			}
 				break;
-			case SACRIFICE: //TODO
+			case SACRIFICE: //choose our living stack to sacrifice
+				if (shere && shere != selectedStack && ourStack && shere->alive())
+					legalAction = true;
+				else
+					notLegal = true;
 				break;
 			case CATAPULT:
 				if (isCatapultAttackable(myNumber))
@@ -2899,8 +2905,9 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 				consoleMsg = boost::str(boost::format(CGI->generaltexth->allTexts[27]) % sp->name % shere->getName()); //Cast %s on %s
 				switch (sp->id)
 				{
-					case Spells::TELEPORT:
 					case Spells::SACRIFICE:
+					case Spells::TELEPORT:
+						selectedStack = shere; //remember firts target
 						secondaryTarget = true;
 						break;
 				}
@@ -2919,6 +2926,12 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 				consoleMsg = CGI->generaltexth->allTexts[550];
 				isCastingPossible = true;
 				break;
+			case SACRIFICE:
+				cursorFrame = ECursor::COMBAT_SACRIFICE;
+				consoleMsg = (boost::format(CGI->generaltexth->allTexts[549]) % shere->getName()).str(); //sacrifice the %s
+				spellToCast->selectedStack = shere->ID; //sacrificed creature is selected
+				isCastingPossible = true;
+				break;
 			case HEAL:
 				cursorFrame = ECursor::COMBAT_HEAL;
 				consoleMsg = (boost::format(CGI->generaltexth->allTexts[419]) % shere->getName()).str(); //Apply first aid to the %s
@@ -2970,6 +2983,9 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 			case TELEPORT:
 				consoleMsg = CGI->generaltexth->allTexts[24]; //Invalid Teleport Destination
 				break;
+			case SACRIFICE:
+				consoleMsg = CGI->generaltexth->allTexts[543]; //choose army to sacrifice
+				break;
 			default:
 				cursorFrame = ECursor::COMBAT_BLOCKED;
 				break;
@@ -2992,6 +3008,7 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 				{
 					case Spells::TELEPORT: //don't cast spell yet, only select target		
 						possibleActions.push_back (TELEPORT);
+						spellToCast->selectedStack = selectedStack->ID;
 						break;
 					case Spells::SACRIFICE:
 						possibleActions.push_back (SACRIFICE);
@@ -3013,10 +3030,19 @@ void CBattleInterface::handleHex(BattleHex myNumber, int eventType)
 				}
 				else
 				{
-					spellToCast->destinationTile = myNumber;
+					switch (sp->id)
+					{
+						case Spells::SACRIFICE:
+							spellToCast->destinationTile = selectedStack->position; //cast on first creature that will be resurrected
+							break;
+						default:
+							spellToCast->destinationTile = myNumber;
+							break;
+					}
 					curInt->cb->battleMakeAction(spellToCast);
 					endCastingSpell();
 				}
+				selectedStack = NULL;
 			}
 		};
 	}

+ 2 - 1
client/BattleInterface/CBattleInterface.h

@@ -116,6 +116,7 @@ private:
 	ui8 animCount;
 	const CStack * activeStack; //number of active stack; NULL - no one
 	const CStack * stackToActivate; //when animation is playing, we should wait till the end to make the next stack active; NULL of none
+	const CStack * selectedStack; //for Teleport / Sacrifice
 	void activateStack(); //sets activeStack to stackToActivate etc.
 	int mouseHoveredStack; //stack hovered by mouse; if -1 -> none
     time_t lastMouseHoveredStackAnimationTime; // time when last mouse hovered animation occurred
@@ -157,7 +158,7 @@ private:
 
 	std::list<ProjectileInfo> projectiles; //projectiles flying on battlefield
 	void projectileShowHelper(SDL_Surface * to); //prints projectiles present on the battlefield
-	void giveCommand(ui8 action, BattleHex tile, ui32 stackID, si32 additional=-1);
+	void giveCommand(ui8 action, BattleHex tile, ui32 stackID, si32 additional=-1, si32 selectedStack = -1);
 	bool isTileAttackable(const BattleHex & number) const; //returns true if tile 'number' is neighboring any tile from active stack's range or is one of these tiles
 	bool isCatapultAttackable(BattleHex hex) const; //returns true if given tile can be attacked by catapult
 

+ 1 - 1
config/spell_info.json

@@ -48,7 +48,7 @@
 		{ "id": 37, "effect": 1, "anim": 39, "ranges": [ "0", "0", "0", "0" ] },
 		{ "id": 38, "effect": 1, "anim": 79, "ranges": [ "0", "0", "0", "0" ] },
 		{ "id": 39, "effect": 1, "anim": 79, "ranges": [ "0", "0", "0", "0" ] },
-		{ "id": 40, "effect": 1, "anim": -1, "ranges": [ "0", "0", "0", "0" ] },
+		{ "id": 40, "effect": 1, "anim": 79, "ranges": [ "0", "0", "0", "0" ] },
 		{ "id": 41, "effect": 1, "anim": 36, "ranges": [ "0", "0", "0", "X" ] },
 		{ "id": 42, "effect": -1, "anim": 40, "ranges": [ "0", "0", "0", "X" ] },
 		{ "id": 43, "effect": 1, "anim": 4, "ranges": [ "0", "0", "0", "X" ] },

+ 3 - 1
lib/BattleAction.h

@@ -28,9 +28,11 @@ struct DLL_LINKAGE BattleAction
 	si8 actionType; //use ActionType enum for values
 	BattleHex destinationTile;
 	si32 additionalInfo; // e.g. spell number if type is 1 || 10; tile to attack if type is 6
+	si32 selectedStack; //spell subject for teleport / sacrifice
+
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & side & stackNumber & actionType & destinationTile & additionalInfo;
+		h & side & stackNumber & actionType & destinationTile & additionalInfo & selectedStack;
 	}
 
 	BattleAction();

+ 9 - 5
lib/BattleState.cpp

@@ -1168,10 +1168,14 @@ ui32 BattleInfo::calculateSpellDmg( const CSpell * sp, const CGHeroInstance * ca
 	return ret;
 }
 
-ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack) const
+ui32 BattleInfo::calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack) const
 {
 	bool resurrect = resurrects(spell->id);
-	int healedHealth = caster->getPrimSkillLevel(2) * spell->power + spell->powers[caster->getSpellSchoolLevel(spell)];
+	int healedHealth;
+	if (spell->id == Spells::SACRIFICE && sacrificedStack)
+		healedHealth = (caster->getPrimSkillLevel(2) + sacrificedStack->MaxHealth() + spell->powers[caster->getSpellSchoolLevel(spell)]) * sacrificedStack->count;
+	else 
+		healedHealth = caster->getPrimSkillLevel(2) * spell->power + spell->powers[caster->getSpellSchoolLevel(spell)];
 	healedHealth = calculateSpellBonus(healedHealth, spell, caster, stack);
 	return std::min<ui32>(healedHealth, stack->MaxHealth() - stack->firstHPleft + (resurrect ? stack->baseAmount * stack->MaxHealth() : 0));
 }
@@ -2127,16 +2131,16 @@ ESpellCastProblem::ESpellCastProblem BattleInfo::battleCanCastThisSpellHere( int
 
 	//get dead stack if we cast resurrection or animate dead
 	const CStack *deadStack = getStackIf([dest](const CStack *s) { return !s->alive() && s->position == dest; });
-	const CStack *aliveStack = getStackIf([dest](const CStack *s) { return s->alive() && s->position == dest; });
+	const CStack *aliveStack = getStackIf([dest](const CStack *s) { return s->alive() && s->position == dest;});
 
 
 	if(spell->isRisingSpell())
 	{
-		if(!deadStack || aliveStack)
+		if(!deadStack && !aliveStack)
 			return ESpellCastProblem::NO_APPROPRIATE_TARGET;
 		if(spell->id == Spells::ANIMATE_DEAD  &&  !deadStack->hasBonusOfType(Bonus::UNDEAD)) 
 			return ESpellCastProblem::NO_APPROPRIATE_TARGET;
-		if(deadStack->owner != player) //you can resurrect only your own stacks
+		if(deadStack && deadStack->owner != player) //you can resurrect only your own stacks //FIXME: it includes alive stacks as well
 			return ESpellCastProblem::NO_APPROPRIATE_TARGET;
 	}
 	else if(spell->getTargetType() == CSpell::CREATURE  ||  spell->getTargetType() == CSpell::CREATURE_EXPERT_MASSIVE)

+ 3 - 3
lib/BattleState.h

@@ -52,7 +52,7 @@ struct DLL_LINKAGE AttackableTiles
 struct DLL_LINKAGE BattleInfo : public CBonusSystemNode
 {
 	ui8 sides[2]; //sides[0] - attacker, sides[1] - defender
-	si32 round, activeStack;
+	si32 round, activeStack, selectedStack;
 	ui8 siege; //    = 0 ordinary battle    = 1 a siege with a Fort    = 2 a siege with a Citadel    = 3 a siege with a Castle
 	const CGTownInstance * town; //used during town siege - id of attacked town; -1 if not town defence
 	int3 tile; //for background and bonuses
@@ -71,7 +71,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & sides & round & activeStack & siege & town & tile & stacks & belligerents & obstacles
+		h & sides & round & activeStack & selectedStack & siege & town & tile & stacks & belligerents & obstacles
 			& castSpells & si & battlefieldType;
 		h & heroes;
 		h & usedSpellsHistory & enchanterCounter;
@@ -120,7 +120,7 @@ struct DLL_LINKAGE BattleInfo : public CBonusSystemNode
 	std::pair<const CStack *, BattleHex> getNearestStack(const CStack * closest, boost::logic::tribool attackerOwned) const; //if attackerOwned is indetermnate, returened stack is of any owner; hex is the number of hex we should be looking from; returns (nerarest creature, predecessorHex)
 	ui32 calculateSpellBonus(ui32 baseDamage, const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature) const;
 	ui32 calculateSpellDmg(const CSpell * sp, const CGHeroInstance * caster, const CStack * affectedCreature, int spellSchoolLevel, int usedSpellPower) const; //calculates damage inflicted by spell
-	ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack) const;
+	ui32 calculateHealedHP(const CGHeroInstance * caster, const CSpell * spell, const CStack * stack, const CStack * sacrificedStack = NULL) const;
 	ui32 calculateHealedHP(int healedHealth, const CSpell * spell, const CStack * stack) const; //for Archangel
 	ui32 calculateHealedHP(const CSpell * spell, int usedSpellPower, int spellSchoolLevel, const CStack * stack) const; //unused
 	bool resurrects(TSpell spellid) const; //TODO: move it to spellHandler?

+ 13 - 4
server/CGameHandler.cpp

@@ -3509,7 +3509,8 @@ void CGameHandler::playerMessage( ui8 player, const std::string &message )
 	}
 }
 
-void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex destination, ui8 casterSide, ui8 casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero, int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack)
+void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex destination, ui8 casterSide, ui8 casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero,
+	int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack, si32 selectedStack)
 {
 	const CSpell *spell = VLC->spellh->spells[spellID];
 
@@ -3765,7 +3766,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
 		{
 			BattleStackMoved bsm;
 			bsm.distance = -1;
-			bsm.stack = gs->curB->activeStack;
+			bsm.stack = selectedStack;
 			std::vector<BattleHex> tiles;
 			tiles.push_back(destination);
 			bsm.tilesToMove = tiles;
@@ -3776,6 +3777,7 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
 	case Spells::CURE:
 	case Spells::RESURRECTION:
 	case Spells::ANIMATE_DEAD:
+	case Spells::SACRIFICE:
 		{
 			int hpGained = 0;
 			if (stack)
@@ -3807,12 +3809,18 @@ void CGameHandler::handleSpellCasting( int spellID, int spellLvl, BattleHex dest
 						hi.healedHP = gs->curB->calculateHealedHP(spell, usedSpellPower, spellLvl, *it);
 				}
 				else
-					hi.healedHP = gs->curB->calculateHealedHP(caster, spell, *it);
+					hi.healedHP = gs->curB->calculateHealedHP(caster, spell, *it, gs->curB->getStack(selectedStack));
 				hi.lowLevelResurrection = spellLvl <= 1;
 				shr.healedStacks.push_back(hi);
 			}
 			if(!shr.healedStacks.empty())
 				sendAndApply(&shr);
+			if (spellID == Spells::SACRIFICE) //remove victim
+			{
+				BattleStacksRemoved bsr;
+				bsr.stackIDs.insert (selectedStack); //somehow it works for teleport?
+				sendAndApply(&bsr);
+			}
 			break;
 		}
 	case Spells::SUMMON_FIRE_ELEMENTAL:
@@ -4013,7 +4021,8 @@ bool CGameHandler::makeCustomAction( BattleAction &ba )
 				StartAction start_action(ba);
 				sendAndApply(&start_action); //start spell casting
 
-				handleSpellCasting (ba.additionalInfo, skill, ba.destinationTile, ba.side, h->tempOwner, h, secondHero, h->getPrimSkillLevel(2), ECastingMode::HERO_CASTING, NULL);
+				handleSpellCasting (ba.additionalInfo, skill, ba.destinationTile, ba.side, h->tempOwner, h, secondHero, h->getPrimSkillLevel(2),
+									ECastingMode::HERO_CASTING, NULL, ba.selectedStack);
 
 				sendAndApply(&end_action);
 				if( !gs->curB->getStack(gs->curB->activeStack, false)->alive() )

+ 2 - 1
server/CGameHandler.h

@@ -193,7 +193,8 @@ public:
 
 	void playerMessage( ui8 player, const std::string &message);
 	bool makeBattleAction(BattleAction &ba);
-	void handleSpellCasting(int spellID, int spellLvl, BattleHex destination, ui8 casterSide, ui8 casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero, int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack);
+	void handleSpellCasting(int spellID, int spellLvl, BattleHex destination, ui8 casterSide, ui8 casterColor, const CGHeroInstance * caster, const CGHeroInstance * secHero,
+		int usedSpellPower, ECastingMode::ECastingMode mode, const CStack * stack, si32 selectedStack = -1);
 	bool makeCustomAction(BattleAction &ba);
 	void stackTurnTrigger(const CStack * stack);
 	bool queryReply( ui32 qid, ui32 answer, ui8 player );