mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	- projectile blitting should be closer to original H3. But still not perfect.
- removed no longer needed field "projectile spins"
This commit is contained in:
		| @@ -717,12 +717,9 @@ bool CShootingAnimation::init() | |||||||
|  |  | ||||||
| 	// Create the projectile animation | 	// 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 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; | 	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. | 	// 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 | ||||||
| @@ -737,42 +734,51 @@ bool CShootingAnimation::init() | |||||||
| 	ProjectileInfo spi; | 	ProjectileInfo spi; | ||||||
| 	spi.creID = shooter->getCreature()->idNumber; | 	spi.creID = shooter->getCreature()->idNumber; | ||||||
| 	spi.stackID = shooter->ID; | 	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.step = 0; | ||||||
| 	spi.frameNum = 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; | 	Point destcoord; | ||||||
|  |  | ||||||
|  | 	// NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise | ||||||
| 	// The "master" point where all projectile positions relate to. | 	xycoord = owner->creAnims[spi.stackID]->pos.topLeft(); | ||||||
| 	static const Point projectileOrigin(181, 252); | 	//xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner); | ||||||
|  |  | ||||||
| 	if (attackedStack) | 	if (attackedStack) | ||||||
| 	{ | 	{ | ||||||
| 		destcoord = CClickableHex::getXYUnitAnim(dest, false, attackedStack, owner);  | 		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. | 		// Calculate projectile start position. Offsets are read out of the CRANIM.TXT. | ||||||
| 		if (projectileAngle > straightAngle) | 		if (projectileAngle > straightAngle) | ||||||
| 		{ | 		{ | ||||||
| 			//upper shot | 			//upper shot | ||||||
| 			spi.x = xycoord.x + projectileOrigin.x + shooterInfo->animation.upperRightMissleOffsetX; | 			spi.x = xycoord.x + 222 + ( -25 + shooterInfo->animation.upperRightMissleOffsetX ) * multiplier; | ||||||
| 			spi.y = xycoord.y + projectileOrigin.y + shooterInfo->animation.upperRightMissleOffsetY; | 			spi.y = xycoord.y + 265 + shooterInfo->animation.upperRightMissleOffsetY; | ||||||
| 		} | 		} | ||||||
| 		else if (projectileAngle < -straightAngle)  | 		else if (projectileAngle < -straightAngle)  | ||||||
| 		{ | 		{ | ||||||
| 			//lower shot | 			//lower shot | ||||||
| 			spi.x = xycoord.x + projectileOrigin.x + shooterInfo->animation.lowerRightMissleOffsetX; | 			spi.x = xycoord.x + 222 + ( -25 + shooterInfo->animation.lowerRightMissleOffsetX ) * multiplier; | ||||||
| 			spi.y = xycoord.y + projectileOrigin.y + shooterInfo->animation.lowerRightMissleOffsetY; | 			spi.y = xycoord.y + 265 + shooterInfo->animation.lowerRightMissleOffsetY; | ||||||
| 		} | 		} | ||||||
| 		else  | 		else  | ||||||
| 		{ | 		{ | ||||||
| 			//straight shot | 			//straight shot | ||||||
| 			spi.x = xycoord.x + projectileOrigin.x + shooterInfo->animation.rightMissleOffsetX; | 			spi.x = xycoord.x + 222 + ( -25 + shooterInfo->animation.rightMissleOffsetX ) * multiplier; | ||||||
| 			spi.y = xycoord.y + projectileOrigin.y + shooterInfo->animation.rightMissleOffsetY; | 			spi.y = xycoord.y + 265 + shooterInfo->animation.rightMissleOffsetY; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		double animSpeed = 23.0 * owner->getAnimSpeed(); // flight speed of projectile | 		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.lastStep = static_cast<int>((spi.catapultInfo->toX - spi.catapultInfo->fromX) / animSpeed); | ||||||
| 			spi.dx = animSpeed; | 			spi.dx = animSpeed; | ||||||
| 			spi.dy = 0; | 			spi.dy = 0; | ||||||
| 			spi.x = xycoord.x + projectileOrigin.x + shooterInfo->animation.rightMissleOffsetX + 17.; | 			spi.x = xycoord.x + 225 + shooterInfo->animation.rightMissleOffsetX; | ||||||
| 			spi.y = xycoord.y + projectileOrigin.y + shooterInfo->animation.rightMissleOffsetY + 10.; | 			spi.y = xycoord.y + 250 + shooterInfo->animation.rightMissleOffsetY; | ||||||
|  |  | ||||||
| 			// Add explosion anim | 			// Add explosion anim | ||||||
| 			int xEnd = static_cast<int>(spi.x + spi.lastStep * spi.dx); | 			int xEnd = static_cast<int>(spi.x + spi.lastStep * spi.dx); | ||||||
| @@ -821,17 +827,31 @@ bool CShootingAnimation::init() | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Set starting frame | 	auto & angles = shooterInfo->animation.missleFrameAngles; | ||||||
| 	if(spi.spin) | 	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++) | ||||||
| 	{ | 	{ | ||||||
| 		spi.frameNum = 0; | 		double currentDiff = fabs( angles[i] / 180 * pi - projectileAngle ); | ||||||
| 	} | 		if (currentDiff < bestDiff) | ||||||
| 	else | 		{ | ||||||
| 	{ | 			bestID = i; | ||||||
| 		double pi = boost::math::constants::pi<double>(); | 			bestDiff = currentDiff; | ||||||
| 		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)); | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	spi.frameNum = bestID; | ||||||
|  |  | ||||||
| 	// Set projectile animation start delay which is specified in frames | 	// Set projectile animation start delay which is specified in frames | ||||||
| 	spi.animStartDelay = shooterInfo->animation.attackClimaxFrame; | 	spi.animStartDelay = shooterInfo->animation.attackClimaxFrame; | ||||||
| 	owner->projectiles.push_back(spi); | 	owner->projectiles.push_back(spi); | ||||||
|   | |||||||
| @@ -183,7 +183,7 @@ struct ProjectileInfo | |||||||
| 	int creID; //ID of creature that shot this projectile | 	int creID; //ID of creature that shot this projectile | ||||||
| 	int stackID; //ID of stack | 	int stackID; //ID of stack | ||||||
| 	int frameNum; //frame to display form projectile animation | 	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) | 	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 | 	CatapultProjectileInfo * catapultInfo; // holds info about the parabolic trajectory of the cannon | ||||||
|   | |||||||
| @@ -302,17 +302,6 @@ CBattleInterface::CBattleInterface(const CCreatureSet * army1, const CCreatureSe | |||||||
|  |  | ||||||
| 			projectile = CDefHandler::giveDef(creature->animation.projectileImageName); | 			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 | 			for(size_t s = 0; s < projectile->ourImages.size(); ++s) //alpha transforming | ||||||
| 			{ | 			{ | ||||||
| 				CSDL_Ext::alphaTransform(projectile->ourImages[s].bitmap); | 				CSDL_Ext::alphaTransform(projectile->ourImages[s].bitmap); | ||||||
| @@ -2456,8 +2445,8 @@ void CBattleInterface::projectileShowHelper(SDL_Surface * to) | |||||||
| 		SDL_Rect dst; | 		SDL_Rect dst; | ||||||
| 		dst.h = idToProjectile[it->creID]->ourImages[it->frameNum].bitmap->h; | 		dst.h = idToProjectile[it->creID]->ourImages[it->frameNum].bitmap->h; | ||||||
| 		dst.w = idToProjectile[it->creID]->ourImages[it->frameNum].bitmap->w; | 		dst.w = idToProjectile[it->creID]->ourImages[it->frameNum].bitmap->w; | ||||||
| 		dst.x = it->x; | 		dst.x = it->x - dst.w / 2; | ||||||
| 		dst.y = it->y; | 		dst.y = it->y - dst.h / 2; | ||||||
|  |  | ||||||
| 		// The equation below calculates the center pos of the canon, but we need the top left pos | 		// The equation below calculates the center pos of the canon, but we need the top left pos | ||||||
| 		// of it for drawing | 		// 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 | 				// 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 - this->pos.x) + this->pos.y; | ||||||
|  |  | ||||||
|  | 				++(it->frameNum); | ||||||
|  | 				it->frameNum %= idToProjectile[it->creID]->ourImages.size(); | ||||||
| 			} | 			} | ||||||
| 			else | 			else | ||||||
| 			{ | 			{ | ||||||
| @@ -2498,12 +2490,6 @@ void CBattleInterface::projectileShowHelper(SDL_Surface * to) | |||||||
| 				it->x += it->dx; | 				it->x += it->dx; | ||||||
| 				it->y += it->dy; | 				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) | 	for(std::list< std::list<ProjectileInfo>::iterator >::iterator it = toBeDeleted.begin(); it!= toBeDeleted.end(); ++it) | ||||||
|   | |||||||
| @@ -159,7 +159,7 @@ | |||||||
| 			"animation": "CENCH.DEF", | 			"animation": "CENCH.DEF", | ||||||
| 			"missile" : | 			"missile" : | ||||||
| 			{ | 			{ | ||||||
| 				"projectile": "SMBALX.DEF", | 				"projectile": "CPRZEAX.DEF", | ||||||
| 				"spinning": false | 				"spinning": false | ||||||
| 			} | 			} | ||||||
| 		}, | 		}, | ||||||
|   | |||||||
| @@ -193,7 +193,11 @@ | |||||||
| 							"type":"array", | 							"type":"array", | ||||||
| 							"description": "Angles of missile images, should go from 90 to -90", | 							"description": "Angles of missile images, should go from 90 to -90", | ||||||
| 							"minItems" : 1, | 							"minItems" : 1, | ||||||
| 							"items": { "type":"number" } | 							"items": { | ||||||
|  | 								"minimum" : -90, | ||||||
|  | 								"maximum" :  90, | ||||||
|  | 								"type":"number" | ||||||
|  | 							} | ||||||
| 						}, | 						}, | ||||||
| 						"offset": { | 						"offset": { | ||||||
| 							"type":"object", | 							"type":"object", | ||||||
| @@ -207,10 +211,6 @@ | |||||||
| 								"upperX":  { "type":"number" }, | 								"upperX":  { "type":"number" }, | ||||||
| 								"upperY":  { "type":"number" } | 								"upperY":  { "type":"number" } | ||||||
| 							} | 							} | ||||||
| 						}, |  | ||||||
| 						"spinning": { |  | ||||||
| 							"type":"boolean", |  | ||||||
| 							"description": "Flying projectile spins. Has some requirements to .def file" |  | ||||||
| 						} | 						} | ||||||
| 					} | 					} | ||||||
| 				}, | 				}, | ||||||
|   | |||||||
| @@ -493,9 +493,7 @@ void CCreatureHandler::loadUnitAnimInfo(CCreature & unit, CLegacyConfigParser & | |||||||
| 	/////////////////////// | 	/////////////////////// | ||||||
|  |  | ||||||
| 	for(int jjj=0; jjj<12; ++jjj) | 	for(int jjj=0; jjj<12; ++jjj) | ||||||
| 	{ | 		unit.animation.missleFrameAngles.push_back(parser.readNumber()); | ||||||
| 		unit.animation.missleFrameAngles[jjj] = parser.readNumber(); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	unit.animation.troopCountLocationOffset= parser.readNumber(); | 	unit.animation.troopCountLocationOffset= parser.readNumber(); | ||||||
| 	unit.animation.attackClimaxFrame = 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.rightMissleOffsetY = offsets["middleY"].Float(); | ||||||
| 	cre->animation.lowerRightMissleOffsetX = offsets["lowerX"].Float(); | 	cre->animation.lowerRightMissleOffsetX = offsets["lowerX"].Float(); | ||||||
| 	cre->animation.lowerRightMissleOffsetY = offsets["lowerY"].Float(); | 	cre->animation.lowerRightMissleOffsetY = offsets["lowerY"].Float(); | ||||||
| 	int i = 0; |  | ||||||
| 	BOOST_FOREACH (auto & angle, missile["frameAngles"].Vector()) | 	cre->animation.missleFrameAngles = missile["frameAngles"].convertTo<std::vector<double>>(); | ||||||
| 	{ |  | ||||||
| 		cre->animation.missleFrameAngles[i++] = angle.Float(); |  | ||||||
| 	} |  | ||||||
| 	cre->advMapDef = graphics["map"].String(); | 	cre->advMapDef = graphics["map"].String(); | ||||||
| 	cre->iconIndex = graphics["iconIndex"].Float(); | 	cre->iconIndex = graphics["iconIndex"].Float(); | ||||||
|  |  | ||||||
| @@ -622,7 +618,7 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c | |||||||
| 		doubledCreatures.insert(creature->idNumber); | 		doubledCreatures.insert(creature->idNumber); | ||||||
|  |  | ||||||
| 	creature->animation.projectileImageName = config["graphics"]["missile"]["projectile"].String(); | 	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(); | 	creature->special = config["special"].Bool(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -54,11 +54,11 @@ public: | |||||||
| 		int upperRightMissleOffsetX, rightMissleOffsetX, lowerRightMissleOffsetX, | 		int upperRightMissleOffsetX, rightMissleOffsetX, lowerRightMissleOffsetX, | ||||||
| 		    upperRightMissleOffsetY, rightMissleOffsetY, lowerRightMissleOffsetY; | 		    upperRightMissleOffsetY, rightMissleOffsetY, lowerRightMissleOffsetY; | ||||||
|  |  | ||||||
| 		double missleFrameAngles[12]; | 		std::vector<double> missleFrameAngles; | ||||||
| 		int troopCountLocationOffset, attackClimaxFrame; | 		int troopCountLocationOffset, attackClimaxFrame; | ||||||
|  |  | ||||||
| 		std::string projectileImageName; | 		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) | 		template <typename Handler> void serialize(Handler &h, const int version) | ||||||
| 		{ | 		{ | ||||||
| @@ -66,7 +66,7 @@ public: | |||||||
| 			h & upperRightMissleOffsetX & rightMissleOffsetX & lowerRightMissleOffsetX; | 			h & upperRightMissleOffsetX & rightMissleOffsetX & lowerRightMissleOffsetX; | ||||||
| 			h & upperRightMissleOffsetY & rightMissleOffsetY & lowerRightMissleOffsetY; | 			h & upperRightMissleOffsetY & rightMissleOffsetY & lowerRightMissleOffsetY; | ||||||
| 			h & missleFrameAngles & troopCountLocationOffset & attackClimaxFrame; | 			h & missleFrameAngles & troopCountLocationOffset & attackClimaxFrame; | ||||||
| 			h & projectileImageName & projectileSpin; | 			h & projectileImageName; | ||||||
| 		} | 		} | ||||||
| 	} animation; | 	} animation; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user