Browse Source

Add santa gremlin missing creature ability, fix shooter init bug

dydzio 8 years ago
parent
commit
3e285c2004
4 changed files with 118 additions and 25 deletions
  1. 1 1
      client/battle/CBattleAnimations.cpp
  2. 18 23
      client/battle/CBattleInterface.cpp
  3. 1 0
      lib/HeroBonus.h
  4. 98 1
      server/CGameHandler.cpp

+ 1 - 1
client/battle/CBattleAnimations.cpp

@@ -799,7 +799,7 @@ bool CShootingAnimation::init()
 	double pi = boost::math::constants::pi<double>();
 
 	// only frames below maxFrame are usable: anything  higher is either no present or we don't know when it should be used
-	size_t maxFrame = std::min<size_t>(angles.size(), owner->idToProjectile[spi.creID]->ourImages.size());
+	size_t maxFrame = std::min<size_t>(angles.size(), owner->idToProjectile.at(spi.creID)->ourImages.size());
 
 	assert(maxFrame > 0);
 

+ 18 - 23
client/battle/CBattleInterface.cpp

@@ -302,29 +302,6 @@ CBattleInterface::CBattleInterface(const CCreatureSet *army1, const CCreatureSet
 		if (s->position >= 0) //turrets have position < 0
 			bfield[s->position]->accessible = false;
 
-	//loading projectiles for units
-	for (const CStack *s : stacks)
-	{
-		if (s->getCreature()->isShooting())
-		{
-			CDefHandler *&projectile = idToProjectile[s->getCreature()->idNumber];
-
-			const CCreature *creature;//creature whose shots should be loaded
-			if (s->getCreature()->idNumber == CreatureID::ARROW_TOWERS)
-				creature = CGI->creh->creatures[siegeH->town->town->clientInfo.siegeShooter];
-			else
-				creature = s->getCreature();
-
-			projectile = CDefHandler::giveDef(creature->animation.projectileImageName);
-
-			for (auto & elem : projectile->ourImages) //alpha transforming
-			{
-				CSDL_Ext::alphaTransform(elem.bitmap);
-			}
-		}
-	}
-
-
 	//preparing graphic with cell borders
 	cellBorders = CSDL_Ext::newSurface(background->w, background->h, cellBorder);
 	//copying palette
@@ -1005,6 +982,24 @@ void CBattleInterface::newStack(const CStack *stack)
 	creAnims[stack->ID]->pos.w = creAnims[stack->ID]->getWidth();
 	creAnims[stack->ID]->setType(CCreatureAnim::HOLDING);
 
+	//loading projectiles for units
+	if (stack->getCreature()->isShooting())
+	{
+		CDefHandler *&projectile = idToProjectile[stack->getCreature()->idNumber];
+
+		const CCreature *creature;//creature whose shots should be loaded
+		if (stack->getCreature()->idNumber == CreatureID::ARROW_TOWERS)
+			creature = CGI->creh->creatures[siegeH->town->town->clientInfo.siegeShooter];
+		else
+			creature = stack->getCreature();
+
+		projectile = CDefHandler::giveDef(creature->animation.projectileImageName);
+
+		for (auto & elem : projectile->ourImages) //alpha transforming
+		{
+			CSDL_Ext::alphaTransform(elem.bitmap);
+		}
+	}
 }
 
 void CBattleInterface::stackRemoved(int stackID)

+ 1 - 0
lib/HeroBonus.h

@@ -212,6 +212,7 @@ public:
 	BONUS_NAME(REBIRTH) /* val - percent of life restored, subtype = 0 - regular, 1 - at least one unit (sacred Phoenix) */\
 	BONUS_NAME(SOUL_STEAL) /*val - number of units gained per enemy killed, subtype = 0 - gained units survive after battle, 1 - they do not*/ \
 	BONUS_NAME(TRANSMUTATION) /*val - chance to trigger in %, subtype = 0 - resurrection based on HP, 1 - based on unit count, additional info - target creature ID (attacker default)*/\
+	BONUS_NAME(SUMMON_GUARDIANS) /*val - amount in % of stack count, subtype = creature ID*/\
 	BONUS_NAME(ADDITIONAL_UNITS) /*val of units with id = subtype will be added to hero's army at the beginning of battle */\
 	BONUS_NAME(SPOILS_OF_WAR) /*val * 10^-6 * gained exp resources of subtype will be given to hero after battle*/\
 	BONUS_NAME(BLOCK)\

+ 98 - 1
server/CGameHandler.cpp

@@ -5537,8 +5537,105 @@ void CGameHandler::runBattle()
 	}
 
 	//initial stacks appearance triggers, e.g. built-in bonus spells
-	for (auto stack : gs->curB->stacks)
+	auto initialStacks = gs->curB->stacks;
+
+	for (CStack * stack : initialStacks)
 	{
+		if (stack->hasBonusOfType(Bonus::SUMMON_GUARDIANS))
+		{
+			const std::shared_ptr<Bonus> summonInfo = stack->getBonus(Selector::type(Bonus::SUMMON_GUARDIANS));
+			auto accessibility = getAccesibility();
+			CreatureID creatureData = CreatureID(summonInfo->subtype);
+			std::vector<BattleHex> targetHexes;
+			bool targetIsBig = stack->getCreature()->isDoubleWide(); //target = creature to guard
+
+			if (!creatureData.toCreature()->isDoubleWide())
+				targetHexes = stack->getSurroundingHexes();
+			else
+			{
+				if(stack->attackerOwned) //handle front guardians, TODO: should we handle situation when units start battle near opposite side of the battlefield? Cannot happen in normal H3...
+					BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::RIGHT, false).moveInDir(BattleHex::EDir::RIGHT, false), targetHexes);
+				else
+					BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::LEFT, false).moveInDir(BattleHex::EDir::LEFT, false), targetHexes);
+
+				//guardian spawn locations for four default position cases for attacker and defender, non-default starting location for att and def is handled in first two if's
+				if (stack->attackerOwned && ( (stack->position.getY() % 2 == 0) || (stack->position.getX() > 1) ))
+				{
+					if (targetIsBig && (stack->position.getY() % 2 == 1) && (stack->position.getX() == 2)) //handle exceptional case
+					{
+						BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::TOP_RIGHT, false), targetHexes);
+						BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::BOTTOM_RIGHT, false), targetHexes);
+					}
+					else
+					{	//add back-side guardians for two-hex target, side guardians for one-hex
+						BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(targetIsBig ? BattleHex::EDir::TOP_LEFT : BattleHex::EDir::TOP_RIGHT, false), targetHexes);
+						BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(targetIsBig ? BattleHex::EDir::BOTTOM_LEFT : BattleHex::EDir::BOTTOM_RIGHT, false), targetHexes);
+
+						if (!targetIsBig && stack->position.getX() > 2) //back guard for one-hex
+							BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::LEFT, false), targetHexes);
+						else if (targetIsBig)//front-side guardians for two-hex target
+						{
+							BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::RIGHT, false).moveInDir(BattleHex::EDir::TOP_RIGHT, false), targetHexes);
+							BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::RIGHT, false).moveInDir(BattleHex::EDir::BOTTOM_RIGHT, false), targetHexes);
+							if (stack->position.getX() > 3) //back guard for two-hex
+								BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::LEFT, false).moveInDir(BattleHex::EDir::LEFT, false), targetHexes);
+						}
+					}
+					
+				}
+
+				else if (!stack->attackerOwned && ((stack->position.getY() % 2 == 1) || (stack->position.getX() < GameConstants::BFIELD_WIDTH - 2)))
+				{
+					if (targetIsBig && (stack->position.getY() % 2 == 0) && (stack->position.getX() == GameConstants::BFIELD_WIDTH - 3 )) //handle exceptional case... equivalent for above for defender side
+					{
+						BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::TOP_LEFT, false), targetHexes);
+						BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::BOTTOM_LEFT, false), targetHexes);
+					}
+					else
+					{
+						BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(targetIsBig ? BattleHex::EDir::TOP_RIGHT : BattleHex::EDir::TOP_LEFT, false), targetHexes);
+						BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(targetIsBig ? BattleHex::EDir::BOTTOM_RIGHT : BattleHex::EDir::BOTTOM_LEFT, false), targetHexes);
+
+						if (!targetIsBig && stack->position.getX() < GameConstants::BFIELD_WIDTH - 3)
+							BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::RIGHT, false), targetHexes);
+						else if (targetIsBig)
+						{
+							BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::LEFT, false).moveInDir(BattleHex::EDir::TOP_LEFT, false), targetHexes);
+							BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::LEFT, false).moveInDir(BattleHex::EDir::BOTTOM_LEFT, false), targetHexes);
+							if (stack->position.getX() < GameConstants::BFIELD_WIDTH - 4)
+								BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::RIGHT, false).moveInDir(BattleHex::EDir::RIGHT, false), targetHexes);
+						}
+					}
+				}
+
+				else if (!stack->attackerOwned && stack->position.getY() % 2 == 0)
+				{
+					BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::LEFT, false).moveInDir(BattleHex::EDir::TOP_LEFT, false), targetHexes);
+					BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::LEFT, false).moveInDir(BattleHex::EDir::BOTTOM_LEFT, false), targetHexes);
+				}
+
+				else if (stack->attackerOwned && stack->position.getY() % 2 == 1)
+				{
+					BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::RIGHT, false).moveInDir(BattleHex::EDir::TOP_RIGHT, false), targetHexes);
+					BattleHex::checkAndPush(BattleHex(stack->position).moveInDir(BattleHex::EDir::RIGHT, false).moveInDir(BattleHex::EDir::BOTTOM_RIGHT, false), targetHexes);
+				}
+			}
+				
+
+			for (auto hex : targetHexes)
+			{
+				if (accessibility.accessible(hex, creatureData.toCreature()->isDoubleWide(), stack->attackerOwned)) //without this multiple creatures can occupy one hex
+				{
+					BattleStackAdded newStack;
+					newStack.amount = std::max(1, stack->count) * 0.01 * summonInfo->val;
+					newStack.creID = creatureData.num;
+					newStack.attacker = stack->attackerOwned;
+					newStack.summoned = true;
+					newStack.pos = hex.hex;
+					sendAndApply(&newStack);
+				}
+			}
+		}
 		stackAppearTrigger(stack);
 	}