Explorar o código

- projectile blitting should be closer to original H3. But still not perfect.
- removed no longer needed field "projectile spins"

Ivan Savenko %!s(int64=12) %!d(string=hai) anos
pai
achega
619a07da03

+ 47 - 27
client/BattleInterface/CBattleAnimations.cpp

@@ -717,12 +717,9 @@ bool CShootingAnimation::init()
 
 	// Create the projectile animation
 
-	double projectileAngle; //in radians; if positive, projectiles goes up
 	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;
-	projectileAngle = atan2(static_cast<double>(abs(dest - fromHex) / GameConstants::BFIELD_WIDTH), static_cast<double>(abs(dest - fromHex) % GameConstants::BFIELD_WIDTH));
-	if(fromHex < dest)
-		projectileAngle = -projectileAngle;
 
 	// 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
@@ -737,42 +734,51 @@ bool CShootingAnimation::init()
 	ProjectileInfo spi;
 	spi.creID = shooter->getCreature()->idNumber;
 	spi.stackID = shooter->ID;
-	spi.reverse = !shooter->attackerOwned;
+	// reverse if creature is facing right OR this is non-existing stack that is not tower (war machines)
+	spi.reverse = attackingStack ? !owner->creDir[attackingStack->ID] : shooter->getCreature()->idNumber != CreatureID::ARROW_TOWERS ;
 
 	spi.step = 0;
 	spi.frameNum = 0;
-	spi.spin = shooterInfo->animation.projectileSpin;
+	//spi.spin = shooterInfo->animation.projectileSpin;
 
-	Point xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner);
+	Point xycoord;
 	Point destcoord;
 
-
-	// The "master" point where all projectile positions relate to.
-	static const Point projectileOrigin(181, 252);
+	// 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();
+	//xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner);
 
 	if (attackedStack)
 	{
 		destcoord = CClickableHex::getXYUnitAnim(dest, false, attackedStack, owner); 
-		destcoord.x += 250; destcoord.y += 210; //TODO: find a better place to shoot
+		destcoord.x += 225; destcoord.y += 225; //TODO: find a better place to shoot
+
+		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;
 
 		// Calculate projectile start position. Offsets are read out of the CRANIM.TXT.
 		if (projectileAngle > straightAngle)
 		{
 			//upper shot
-			spi.x = xycoord.x + projectileOrigin.x + shooterInfo->animation.upperRightMissleOffsetX;
-			spi.y = xycoord.y + projectileOrigin.y + shooterInfo->animation.upperRightMissleOffsetY;
+			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 + projectileOrigin.x + shooterInfo->animation.lowerRightMissleOffsetX;
-			spi.y = xycoord.y + projectileOrigin.y + shooterInfo->animation.lowerRightMissleOffsetY;
+			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 + projectileOrigin.x + shooterInfo->animation.rightMissleOffsetX;
-			spi.y = xycoord.y + projectileOrigin.y + shooterInfo->animation.rightMissleOffsetY;
+			spi.x = xycoord.x + 222 + ( -25 + shooterInfo->animation.rightMissleOffsetX ) * multiplier;
+			spi.y = xycoord.y + 265 + shooterInfo->animation.rightMissleOffsetY;
 		}
 
 		double animSpeed = 23.0 * owner->getAnimSpeed(); // flight speed of projectile
@@ -811,8 +817,8 @@ bool CShootingAnimation::init()
 			spi.lastStep = static_cast<int>((spi.catapultInfo->toX - spi.catapultInfo->fromX) / animSpeed);
 			spi.dx = animSpeed;
 			spi.dy = 0;
-			spi.x = xycoord.x + projectileOrigin.x + shooterInfo->animation.rightMissleOffsetX + 17.;
-			spi.y = xycoord.y + projectileOrigin.y + shooterInfo->animation.rightMissleOffsetY + 10.;
+			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);
@@ -821,17 +827,31 @@ bool CShootingAnimation::init()
 		}
 	}
 
-	// Set starting frame
-	if(spi.spin)
-	{
-		spi.frameNum = 0;
-	}
-	else
+	auto & angles = shooterInfo->animation.missleFrameAngles;
+	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());
+
+	assert(maxFrame > 0);
+
+	// values in angles array indicate position from which this frame was rendered, in degrees.
+	// find frame that has closest angle to one that we need for this shot
+	size_t bestID = 0;
+	double bestDiff = fabs( angles[0] / 180 * pi - projectileAngle );
+
+	for (size_t i=1; i<maxFrame; i++)
 	{
-		double pi = boost::math::constants::pi<double>();
-		spi.frameNum = static_cast<int>(((pi / 2.0 - projectileAngle) / (2.0 * pi) + 1/(static_cast<double>(2*(owner->idToProjectile[spi.creID]->ourImages.size()-1)))) * (owner->idToProjectile[spi.creID]->ourImages.size()-1));
+		double currentDiff = fabs( angles[i] / 180 * pi - projectileAngle );
+		if (currentDiff < bestDiff)
+		{
+			bestID = i;
+			bestDiff = currentDiff;
+		}
 	}
 
+	spi.frameNum = bestID;
+
 	// Set projectile animation start delay which is specified in frames
 	spi.animStartDelay = shooterInfo->animation.attackClimaxFrame;
 	owner->projectiles.push_back(spi);

+ 1 - 1
client/BattleInterface/CBattleAnimations.h

@@ -183,7 +183,7 @@ struct ProjectileInfo
 	int creID; //ID of creature that shot this projectile
 	int stackID; //ID of stack
 	int frameNum; //frame to display form projectile animation
-	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)
 	bool reverse; //if true, projectile will be flipped by vertical asix
 	CatapultProjectileInfo * catapultInfo; // holds info about the parabolic trajectory of the cannon

+ 5 - 19
client/BattleInterface/CBattleInterface.cpp

@@ -302,17 +302,6 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe
 
 			projectile = CDefHandler::giveDef(creature->animation.projectileImageName);
 
-			if(projectile->ourImages.size() > 2) //add symmetric images
-			{
-				for(int k = projectile->ourImages.size()-2; k > 1; --k)
-				{
-					Cimage ci;
-					ci.bitmap = CSDL_Ext::rotate01(projectile->ourImages[k].bitmap);
-					ci.groupNumber = 0;
-					ci.imName = std::string();
-					projectile->ourImages.push_back(ci);
-				}
-			}
 			for(size_t s = 0; s < projectile->ourImages.size(); ++s) //alpha transforming
 			{
 				CSDL_Ext::alphaTransform(projectile->ourImages[s].bitmap);
@@ -2456,8 +2445,8 @@ void CBattleInterface::projectileShowHelper(SDL_Surface * to)
 		SDL_Rect dst;
 		dst.h = idToProjectile[it->creID]->ourImages[it->frameNum].bitmap->h;
 		dst.w = idToProjectile[it->creID]->ourImages[it->frameNum].bitmap->w;
-		dst.x = it->x;
-		dst.y = it->y;
+		dst.x = it->x - dst.w / 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
@@ -2491,6 +2480,9 @@ void CBattleInterface::projectileShowHelper(SDL_Surface * to)
 				// Parabolic shot of the trajectory, as follows: f(x) = ax^2 + bx + c
 				it->x += it->dx;
 				it->y = it->catapultInfo->calculateY(it->x - this->pos.x) + this->pos.y;
+
+				++(it->frameNum);
+				it->frameNum %= idToProjectile[it->creID]->ourImages.size();
 			}
 			else
 			{
@@ -2498,12 +2490,6 @@ void CBattleInterface::projectileShowHelper(SDL_Surface * to)
 				it->x += it->dx;
 				it->y += it->dy;
 			}
-
-			if(it->spin)
-			{
-				++(it->frameNum);
-				it->frameNum %= idToProjectile[it->creID]->ourImages.size();
-			}
 		}
 	}
 	for(std::list< std::list<ProjectileInfo>::iterator >::iterator it = toBeDeleted.begin(); it!= toBeDeleted.end(); ++it)

+ 1 - 1
config/creatures/neutral.json

@@ -159,7 +159,7 @@
 			"animation": "CENCH.DEF",
 			"missile" :
 			{
-				"projectile": "SMBALX.DEF",
+				"projectile": "CPRZEAX.DEF",
 				"spinning": false
 			}
 		},

+ 5 - 5
config/schemas/creature.json

@@ -193,7 +193,11 @@
 							"type":"array",
 							"description": "Angles of missile images, should go from 90 to -90",
 							"minItems" : 1,
-							"items": { "type":"number" }
+							"items": {
+								"minimum" : -90,
+								"maximum" :  90,
+								"type":"number"
+							}
 						},
 						"offset": {
 							"type":"object",
@@ -207,10 +211,6 @@
 								"upperX":  { "type":"number" },
 								"upperY":  { "type":"number" }
 							}
-						},
-						"spinning": {
-							"type":"boolean",
-							"description": "Flying projectile spins. Has some requirements to .def file"
 						}
 					}
 				},

+ 5 - 9
lib/CCreatureHandler.cpp

@@ -493,9 +493,7 @@ void CCreatureHandler::loadUnitAnimInfo(CCreature & unit, CLegacyConfigParser &
 	///////////////////////
 
 	for(int jjj=0; jjj<12; ++jjj)
-	{
-		unit.animation.missleFrameAngles[jjj] = parser.readNumber();
-	}
+		unit.animation.missleFrameAngles.push_back(parser.readNumber());
 
 	unit.animation.troopCountLocationOffset= parser.readNumber();
 	unit.animation.attackClimaxFrame = parser.readNumber();
@@ -570,11 +568,9 @@ CCreature * CCreatureHandler::loadCreature(const JsonNode & node)
 	cre->animation.rightMissleOffsetY = offsets["middleY"].Float();
 	cre->animation.lowerRightMissleOffsetX = offsets["lowerX"].Float();
 	cre->animation.lowerRightMissleOffsetY = offsets["lowerY"].Float();
-	int i = 0;
-	BOOST_FOREACH (auto & angle, missile["frameAngles"].Vector())
-	{
-		cre->animation.missleFrameAngles[i++] = angle.Float();
-	}
+
+	cre->animation.missleFrameAngles = missile["frameAngles"].convertTo<std::vector<double>>();
+
 	cre->advMapDef = graphics["map"].String();
 	cre->iconIndex = graphics["iconIndex"].Float();
 
@@ -622,7 +618,7 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c
 		doubledCreatures.insert(creature->idNumber);
 
 	creature->animation.projectileImageName = config["graphics"]["missile"]["projectile"].String();
-	creature->animation.projectileSpin = config["graphics"]["missile"]["spinning"].Bool();
+	//creature->animation.projectileSpin = config["graphics"]["missile"]["spinning"].Bool();
 
 	creature->special = config["special"].Bool();
 

+ 3 - 3
lib/CCreatureHandler.h

@@ -54,11 +54,11 @@ public:
 		int upperRightMissleOffsetX, rightMissleOffsetX, lowerRightMissleOffsetX,
 		    upperRightMissleOffsetY, rightMissleOffsetY, lowerRightMissleOffsetY;
 
-		double missleFrameAngles[12];
+		std::vector<double> missleFrameAngles;
 		int troopCountLocationOffset, attackClimaxFrame;
 
 		std::string projectileImageName;
-		bool projectileSpin; //if true, appropriate projectile is spinning during flight
+		//bool projectileSpin; //if true, appropriate projectile is spinning during flight
 
 		template <typename Handler> void serialize(Handler &h, const int version)
 		{
@@ -66,7 +66,7 @@ public:
 			h & upperRightMissleOffsetX & rightMissleOffsetX & lowerRightMissleOffsetX;
 			h & upperRightMissleOffsetY & rightMissleOffsetY & lowerRightMissleOffsetY;
 			h & missleFrameAngles & troopCountLocationOffset & attackClimaxFrame;
-			h & projectileImageName & projectileSpin;
+			h & projectileImageName;
 		}
 	} animation;