Browse Source

- removed hardcoded set of catapult trajectories
- some more improvements into projectile blitting

Remaining issues:
- timing is off when catapult hits walls/tower
- catapult trajectory may need tweaking

Ivan Savenko 12 years ago
parent
commit
2a469d4ffc

+ 53 - 70
client/BattleInterface/CBattleAnimations.cpp

@@ -717,9 +717,8 @@ bool CShootingAnimation::init()
 
 
 	// Create the projectile animation
 	// Create the projectile animation
 
 
-	double straightAngle = 0.2; //maximal angle in radians between straight horizontal line and shooting line for which shot is considered to be straight (absoulte value)
-	double projectileAngle = 0;
-	int fromHex = shooter->position;
+	//maximal angle in radians between straight horizontal line and shooting line for which shot is considered to be straight (absoulte value)
+	static const double straightAngle = 0.2;
 
 
 	// Get further info about the shooter e.g. relative pos of projectile to unit.
 	// Get further info about the shooter e.g. relative pos of projectile to unit.
 	// If the creature id is 149 then it's a arrow tower which has no additional info so get the
 	// If the creature id is 149 then it's a arrow tower which has no additional info so get the
@@ -739,92 +738,76 @@ bool CShootingAnimation::init()
 
 
 	spi.step = 0;
 	spi.step = 0;
 	spi.frameNum = 0;
 	spi.frameNum = 0;
-	//spi.spin = shooterInfo->animation.projectileSpin;
 
 
-	Point xycoord;
-	Point destcoord;
+	Point fromPos;
+	Point destPos;
 
 
 	// NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise
 	// NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise
-	xycoord = owner->creAnims[spi.stackID]->pos.topLeft();
+	fromPos = owner->creAnims[spi.stackID]->pos.topLeft();
 	//xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner);
 	//xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner);
 
 
-	if (attackedStack)
-	{
-		destcoord = CClickableHex::getXYUnitAnim(dest, false, attackedStack, owner); 
-		destcoord.x += 225; destcoord.y += 225; //TODO: find a better place to shoot
+	destPos = CClickableHex::getXYUnitAnim(dest, false, attackedStack, owner);
 
 
-		double projectileAngle = atan2(fabs(static_cast<double>(destcoord.x - xycoord.x)),
-		                               fabs(static_cast<double>(destcoord.y - xycoord.y)));
-		if(fromHex < dest)
-			projectileAngle = -projectileAngle;
+	// to properly translate coordinates when shooter is rotated
+	int multiplier = spi.reverse ? -1 : 1;
 
 
-		// to properly translate coordinates when shooter is rotated
-		int multiplier = spi.reverse ? -1 : 1;
+	double projectileAngle = atan2(fabs(destPos.y - fromPos.y), fabs(destPos.x - fromPos.x));
+	if(shooter->position < dest)
+		projectileAngle = -projectileAngle;
 
 
-		// Calculate projectile start position. Offsets are read out of the CRANIM.TXT.
-		if (projectileAngle > straightAngle)
-		{
-			//upper shot
-			spi.x = xycoord.x + 222 + ( -25 + shooterInfo->animation.upperRightMissleOffsetX ) * multiplier;
-			spi.y = xycoord.y + 265 + shooterInfo->animation.upperRightMissleOffsetY;
-		}
-		else if (projectileAngle < -straightAngle) 
-		{
-			//lower shot
-			spi.x = xycoord.x + 222 + ( -25 + shooterInfo->animation.lowerRightMissleOffsetX ) * multiplier;
-			spi.y = xycoord.y + 265 + shooterInfo->animation.lowerRightMissleOffsetY;
-		}
-		else 
-		{
-			//straight shot
-			spi.x = xycoord.x + 222 + ( -25 + shooterInfo->animation.rightMissleOffsetX ) * multiplier;
-			spi.y = xycoord.y + 265 + shooterInfo->animation.rightMissleOffsetY;
-		}
+	// Calculate projectile start position. Offsets are read out of the CRANIM.TXT.
+	if (projectileAngle > straightAngle)
+	{
+		//upper shot
+		spi.x = fromPos.x + 222 + ( -25 + shooterInfo->animation.upperRightMissleOffsetX ) * multiplier;
+		spi.y = fromPos.y + 265 + shooterInfo->animation.upperRightMissleOffsetY;
+	}
+	else if (projectileAngle < -straightAngle)
+	{
+		//lower shot
+		spi.x = fromPos.x + 222 + ( -25 + shooterInfo->animation.lowerRightMissleOffsetX ) * multiplier;
+		spi.y = fromPos.y + 265 + shooterInfo->animation.lowerRightMissleOffsetY;
+	}
+	else
+	{
+		//straight shot
+		spi.x = fromPos.x + 222 + ( -25 + shooterInfo->animation.rightMissleOffsetX ) * multiplier;
+		spi.y = fromPos.y + 265 + shooterInfo->animation.rightMissleOffsetY;
+	}
+
+	destPos += Point(225, 225);
 
 
+	// recalculate angle taking in account offsets
+	//projectileAngle = atan2(fabs(destPos.y - spi.y), fabs(destPos.x - spi.x));
+	//if(shooter->position < dest)
+	//	projectileAngle = -projectileAngle;
+
+	if (attackedStack)
+	{
 		double animSpeed = 23.0 * owner->getAnimSpeed(); // flight speed of projectile
 		double animSpeed = 23.0 * owner->getAnimSpeed(); // flight speed of projectile
-		spi.lastStep = static_cast<int>(sqrt(static_cast<double>((destcoord.x - spi.x) * (destcoord.x - spi.x) + (destcoord.y - spi.y) * (destcoord.y - spi.y))) / animSpeed);
+		spi.lastStep = static_cast<int>(sqrt(static_cast<double>((destPos.x - spi.x) * (destPos.x - spi.x) + (destPos.y - spi.y) * (destPos.y - spi.y))) / animSpeed);
 		if(spi.lastStep == 0)
 		if(spi.lastStep == 0)
 			spi.lastStep = 1;
 			spi.lastStep = 1;
-		spi.dx = (destcoord.x - spi.x) / spi.lastStep;
-		spi.dy = (destcoord.y - spi.y) / spi.lastStep;
-		spi.catapultInfo = 0;
+		spi.dx = (destPos.x - spi.x) / spi.lastStep;
+		spi.dy = (destPos.y - spi.y) / spi.lastStep;
 	}
 	}
 	else 
 	else 
 	{
 	{
 		// Catapult attack
 		// Catapult attack
-		// These are the values for equations of this kind: f(x) = ax^2 + bx + c
-		static const std::vector<CatapultProjectileInfo*> trajectoryCurves = boost::assign::list_of<CatapultProjectileInfo*>(new CatapultProjectileInfo(4.309, -3.198, 569.2, -296, 182))
-			(new CatapultProjectileInfo(4.710, -3.11, 558.68, -258, 175))(new CatapultProjectileInfo(5.056, -3.003, 546.9, -236, 174))
-			(new CatapultProjectileInfo(4.760, -2.74, 526.47, -216, 215))(new CatapultProjectileInfo(4.288, -2.496, 508.98, -223, 274))
-			(new CatapultProjectileInfo(3.683, -3.018, 558.39, -324, 176))(new CatapultProjectileInfo(2.884, -2.607, 528.95, -366, 312))
-			(new CatapultProjectileInfo(3.783, -2.364, 501.35, -227, 318));
+		spi.catapultInfo.reset(new CatapultProjectileInfo(Point(spi.x, spi.y), destPos));
 
 
-		static std::map<int, int> hexToCurve = boost::assign::map_list_of<int, int>(29, 0)(62, 1)(95, 2)(130, 3)(182, 4)(12, 5)(50, 6)(183, 7);
+		double animSpeed = 3.318 * owner->getAnimSpeed();
+		spi.lastStep = abs((destPos.x - spi.x) / animSpeed);
+		spi.dx = animSpeed;
+		spi.dy = 0;
 
 
-		std::map<int, int>::iterator it = hexToCurve.find(dest.hex);
+		SDL_Surface * img = owner->idToProjectile[spi.creID]->ourImages[0].bitmap;
 
 
-		if (it == hexToCurve.end())
-		{
-			tlog1 << "For the hex position " << dest.hex << " is no curve defined.";
-			endAnim();
-			return false;
-		}
-		else
-		{
-			int curveID = it->second;
-			spi.catapultInfo = trajectoryCurves[curveID];
-			double animSpeed = 3.318 * owner->getAnimSpeed();
-			spi.lastStep = static_cast<int>((spi.catapultInfo->toX - spi.catapultInfo->fromX) / animSpeed);
-			spi.dx = animSpeed;
-			spi.dy = 0;
-			spi.x = xycoord.x + 225 + shooterInfo->animation.rightMissleOffsetX;
-			spi.y = xycoord.y + 250 + shooterInfo->animation.rightMissleOffsetY;
-
-			// Add explosion anim
-			int xEnd = static_cast<int>(spi.x + spi.lastStep * spi.dx);
-			int yEnd = static_cast<int>(spi.catapultInfo->calculateY(xEnd));
-			owner->addNewAnim( new CSpellEffectAnimation(owner, catapultDamage ? "SGEXPL.DEF" : "CSGRCK.DEF", xEnd - 126, yEnd - 105));
-		}
+		// Add explosion anim
+		Point animPos(destPos.x - 126 + img->w / 2,
+		              destPos.y - 105 + img->h / 2);
+
+		owner->addNewAnim( new CSpellEffectAnimation(owner, catapultDamage ? "SGEXPL.DEF" : "CSGRCK.DEF", animPos.x, animPos.y));
 	}
 	}
 
 
 	auto & angles = shooterInfo->animation.missleFrameAngles;
 	auto & angles = shooterInfo->animation.missleFrameAngles;

+ 1 - 1
client/BattleInterface/CBattleAnimations.h

@@ -186,7 +186,7 @@ struct ProjectileInfo
 	//bool spin; //if true, frameNum will be increased
 	//bool spin; //if true, frameNum will be increased
 	int animStartDelay; //how many times projectile must be attempted to be shown till it's really show (decremented after hit)
 	int animStartDelay; //how many times projectile must be attempted to be shown till it's really show (decremented after hit)
 	bool reverse; //if true, projectile will be flipped by vertical asix
 	bool reverse; //if true, projectile will be flipped by vertical asix
-	CatapultProjectileInfo * catapultInfo; // holds info about the parabolic trajectory of the cannon
+	std::shared_ptr<CatapultProjectileInfo> catapultInfo; // holds info about the parabolic trajectory of the cannon
 };
 };
 
 
 /// Shooting attack
 /// Shooting attack

+ 26 - 10
client/BattleInterface/CBattleInterface.cpp

@@ -2447,14 +2447,6 @@ void CBattleInterface::projectileShowHelper(SDL_Surface * to)
 		dst.x = it->x - dst.w / 2;
 		dst.x = it->x - dst.w / 2;
 		dst.y = it->y - dst.h / 2;
 		dst.y = it->y - dst.h / 2;
 
 
-		// The equation below calculates the center pos of the canon, but we need the top left pos
-		// of it for drawing
-		if (it->catapultInfo)
-		{
-			dst.x -= 17.;
-			dst.y -= 10.;
-		}
-
 		if(it->reverse)
 		if(it->reverse)
 		{
 		{
 			SDL_Surface * rev = CSDL_Ext::rotate01(idToProjectile[it->creID]->ourImages[it->frameNum].bitmap);
 			SDL_Surface * rev = CSDL_Ext::rotate01(idToProjectile[it->creID]->ourImages[it->frameNum].bitmap);
@@ -2478,7 +2470,7 @@ void CBattleInterface::projectileShowHelper(SDL_Surface * to)
 			{
 			{
 				// Parabolic shot of the trajectory, as follows: f(x) = ax^2 + bx + c
 				// Parabolic shot of the trajectory, as follows: f(x) = ax^2 + bx + c
 				it->x += it->dx;
 				it->x += it->dx;
-				it->y = it->catapultInfo->calculateY(it->x - this->pos.x) + this->pos.y;
+				it->y = it->catapultInfo->calculateY(it->x);
 
 
 				++(it->frameNum);
 				++(it->frameNum);
 				it->frameNum %= idToProjectile[it->creID]->ourImages.size();
 				it->frameNum %= idToProjectile[it->creID]->ourImages.size();
@@ -3642,7 +3634,31 @@ void CBattleInterface::SiegeHelper::printPartOfWall(SDL_Surface * to, int what)
 	}
 	}
 }
 }
 
 
+CatapultProjectileInfo::CatapultProjectileInfo(Point from, Point dest)
+{
+	facA = 0.005; // seems to be constant
+
+	// system of 2 linear equations, solutions of which are missing coefficients
+	// for quadratic equation a*x*x + b*x + c
+	double eq[2][3] = {
+		{ static_cast<double>(from.x), 1.0, from.y - facA*from.x*from.x },
+		{ static_cast<double>(dest.x), 1.0, dest.y - facA*dest.x*dest.x }
+	};
+
+	// solve system via determinants
+	double det  = eq[0][0] * eq[1][1] - eq[1][0] * eq[0][1];
+	double detB = eq[0][2] * eq[1][1] - eq[1][2] * eq[0][1];
+	double detC = eq[0][0] * eq[1][2] - eq[1][0] * eq[0][2];
+
+	facB = detB / det;
+	facC = detC / det;
+
+	// make sure that parabola is correct e.g. passes through from and dest
+	assert(fabs(calculateY(from.x) - from.y) < 1.0);
+	assert(fabs(calculateY(dest.x) - dest.y) < 1.0);
+}
+
 double CatapultProjectileInfo::calculateY(double x)
 double CatapultProjectileInfo::calculateY(double x)
 {
 {
-	return (facA * pow(10., -3.)) * pow(x, 2.0) + facB * x + facC;
+	return facA * pow(x, 2.0) + facB * x + facC;
 }
 }

+ 2 - 5
client/BattleInterface/CBattleInterface.h

@@ -80,12 +80,9 @@ struct BattleEffect
 /// Small struct which is needed for drawing the parabolic trajectory of the catapult cannon
 /// Small struct which is needed for drawing the parabolic trajectory of the catapult cannon
 struct CatapultProjectileInfo
 struct CatapultProjectileInfo
 {
 {
-	const double facA, facB, facC;
-	const int fromX, toX;
+	CatapultProjectileInfo(Point from, Point dest);
 
 
-	CatapultProjectileInfo() : facA(0), facB(0), facC(0), fromX(0), toX(0) { };
-	CatapultProjectileInfo(double factorA, double factorB, double factorC, int fromXX, int toXX) : facA(factorA), facB(factorB), facC(factorC),
-		fromX(fromXX), toX(toXX) { };
+	double facA, facB, facC;
 
 
 	double calculateY(double x);
 	double calculateY(double x);
 };
 };