mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	Merge pull request #1146 from IvanSavenko/ray_projectile
Implemented ray-like projectiles for shooters
This commit is contained in:
		| @@ -785,22 +785,25 @@ bool CShootingAnimation::init() | ||||
| 	if (projectileAngle > straightAngle) | ||||
| 	{ | ||||
| 		//upper shot | ||||
| 		spi.x = fromPos.x + 222 + ( -25 + shooterInfo->animation.upperRightMissleOffsetX ) * multiplier; | ||||
| 		spi.y = fromPos.y + 265 + shooterInfo->animation.upperRightMissleOffsetY; | ||||
| 		spi.x0 = fromPos.x + 222 + ( -25 + shooterInfo->animation.upperRightMissleOffsetX ) * multiplier; | ||||
| 		spi.y0 = 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; | ||||
| 		spi.x0 = fromPos.x + 222 + ( -25 + shooterInfo->animation.lowerRightMissleOffsetX ) * multiplier; | ||||
| 		spi.y0 = 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; | ||||
| 		spi.x0 = fromPos.x + 222 + ( -25 + shooterInfo->animation.rightMissleOffsetX ) * multiplier; | ||||
| 		spi.y0 = fromPos.y + 265 + shooterInfo->animation.rightMissleOffsetY; | ||||
| 	} | ||||
|  | ||||
| 	spi.x = spi.x0; | ||||
| 	spi.y = spi.y0; | ||||
|  | ||||
| 	destPos += Point(225, 225); | ||||
|  | ||||
| 	// recalculate angle taking in account offsets | ||||
| @@ -811,7 +814,9 @@ bool CShootingAnimation::init() | ||||
| 	if (attackedStack) | ||||
| 	{ | ||||
| 		double animSpeed = AnimationControls::getProjectileSpeed(); // flight speed of projectile | ||||
| 		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); | ||||
| 		double distanceSquared = (destPos.x - spi.x) * (destPos.x - spi.x) + (destPos.y - spi.y) * (destPos.y - spi.y); | ||||
| 		double distance = sqrt(distanceSquared); | ||||
| 		spi.lastStep = std::round(distance / animSpeed); | ||||
| 		if(spi.lastStep == 0) | ||||
| 			spi.lastStep = 1; | ||||
| 		spi.dx = (destPos.x - spi.x) / spi.lastStep; | ||||
| @@ -837,30 +842,42 @@ bool CShootingAnimation::init() | ||||
| 	} | ||||
| 	double pi = boost::math::constants::pi<double>(); | ||||
|  | ||||
| 	if (owner->idToProjectile.count(spi.creID) == 0) //in some cases (known one: hero grants shooter bonus to unit) the shooter stack's projectile may not be properly initialized | ||||
| 	//in some cases (known one: hero grants shooter bonus to unit) the shooter stack's projectile may not be properly initialized | ||||
| 	if (!owner->idToProjectile.count(spi.creID) && !owner->idToRay.count(spi.creID)) | ||||
| 		owner->initStackProjectile(shooter); | ||||
|  | ||||
| 	// 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.at(spi.creID)->size(0)); | ||||
|  | ||||
| 	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++) | ||||
| 	if (owner->idToProjectile.count(spi.creID)) | ||||
| 	{ | ||||
| 		double currentDiff = fabs( angles[i] / 180 * pi - projectileAngle ); | ||||
| 		if (currentDiff < bestDiff) | ||||
| 		{ | ||||
| 			bestID = i; | ||||
| 			bestDiff = currentDiff; | ||||
| 		} | ||||
| 	} | ||||
| 		// 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.at(spi.creID)->size(0)); | ||||
|  | ||||
| 	spi.frameNum = static_cast<int>(bestID); | ||||
| 		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 currentDiff = fabs( angles[i] / 180 * pi - projectileAngle ); | ||||
| 			if (currentDiff < bestDiff) | ||||
| 			{ | ||||
| 				bestID = i; | ||||
| 				bestDiff = currentDiff; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		spi.frameNum = static_cast<int>(bestID); | ||||
| 	} | ||||
| 	else if (owner->idToRay.count(spi.creID)) | ||||
| 	{ | ||||
| 		// no-op | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		logGlobal->error("Unable to find valid projectile for shooter %d", spi.creID); | ||||
| 	} | ||||
|  | ||||
| 	// Set projectile animation start delay which is specified in frames | ||||
| 	spi.animStartDelay = shooterInfo->animation.attackClimaxFrame; | ||||
|   | ||||
| @@ -189,6 +189,7 @@ public: | ||||
| /// Small struct which contains information about the position and the velocity of a projectile | ||||
| struct ProjectileInfo | ||||
| { | ||||
| 	double x0, y0; //initial position on the screen | ||||
| 	double x, y; //position on the screen | ||||
| 	double dx, dy; //change in position in one step | ||||
| 	int step, lastStep; //to know when finish showing this projectile | ||||
|   | ||||
| @@ -1020,15 +1020,22 @@ void CBattleInterface::initStackProjectile(const CStack * stack) | ||||
| 	else | ||||
| 		creature = stack->getCreature(); | ||||
|  | ||||
| 	std::shared_ptr<CAnimation> projectile = std::make_shared<CAnimation>(creature->animation.projectileImageName); | ||||
| 	projectile->preload(); | ||||
| 	if (creature->animation.projectileRay.empty()) | ||||
| 	{ | ||||
| 		std::shared_ptr<CAnimation> projectile = std::make_shared<CAnimation>(creature->animation.projectileImageName); | ||||
| 		projectile->preload(); | ||||
|  | ||||
| 	if(projectile->size(1) != 0) | ||||
| 		logAnim->error("Expected empty group 1 in stack projectile"); | ||||
| 		if(projectile->size(1) != 0) | ||||
| 			logAnim->error("Expected empty group 1 in stack projectile"); | ||||
| 		else | ||||
| 			projectile->createFlippedGroup(0, 1); | ||||
|  | ||||
| 		idToProjectile[stack->getCreature()->idNumber] = projectile; | ||||
| 	} | ||||
| 	else | ||||
| 		projectile->createFlippedGroup(0, 1); | ||||
|  | ||||
| 	idToProjectile[stack->getCreature()->idNumber] = projectile; | ||||
| 	{ | ||||
| 		idToRay[stack->getCreature()->idNumber] = creature->animation.projectileRay; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CBattleInterface::stackRemoved(uint32_t stackID) | ||||
| @@ -3206,23 +3213,63 @@ void CBattleInterface::showProjectiles(SDL_Surface *to) | ||||
| 				continue; // wait... | ||||
| 		} | ||||
|  | ||||
| 		size_t group = it->reverse ? 1 : 0; | ||||
| 		auto image = idToProjectile[it->creID]->getImage(it->frameNum, group, true); | ||||
|  | ||||
| 		if(image) | ||||
| 		if (idToProjectile.count(it->creID)) | ||||
| 		{ | ||||
| 			SDL_Rect dst; | ||||
| 			dst.h = image->height(); | ||||
| 			dst.w = image->width(); | ||||
| 			dst.x = static_cast<int>(it->x - dst.w / 2); | ||||
| 			dst.y = static_cast<int>(it->y - dst.h / 2); | ||||
| 			size_t group = it->reverse ? 1 : 0; | ||||
| 			auto image = idToProjectile[it->creID]->getImage(it->frameNum, group, true); | ||||
|  | ||||
| 			image->draw(to, &dst, nullptr); | ||||
| 			if(image) | ||||
| 			{ | ||||
| 				SDL_Rect dst; | ||||
| 				dst.h = image->height(); | ||||
| 				dst.w = image->width(); | ||||
| 				dst.x = static_cast<int>(it->x - dst.w / 2); | ||||
| 				dst.y = static_cast<int>(it->y - dst.h / 2); | ||||
|  | ||||
| 				image->draw(to, &dst, nullptr); | ||||
| 			} | ||||
| 		} | ||||
| 		if (idToRay.count(it->creID)) | ||||
| 		{ | ||||
| 			auto const & ray = idToRay[it->creID]; | ||||
|  | ||||
| 			if (std::abs(it->dx) > std::abs(it->dy)) // draw in horizontal axis | ||||
| 			{ | ||||
| 				int y1 =  it->y0 - ray.size() / 2; | ||||
| 				int y2 =  it->y - ray.size() / 2; | ||||
|  | ||||
| 				int x1 = it->x0; | ||||
| 				int x2 = it->x; | ||||
|  | ||||
| 				for (size_t i = 0; i < ray.size(); ++i) | ||||
| 				{ | ||||
| 					SDL_Color beginColor{ ray[i].r1, ray[i].g1, ray[i].b1, ray[i].a1}; | ||||
| 					SDL_Color endColor  { ray[i].r2, ray[i].g2, ray[i].b2, ray[i].a2}; | ||||
|  | ||||
| 					CSDL_Ext::drawLine(to, x1, y1 + i, x2, y2 + i, beginColor, endColor); | ||||
| 				} | ||||
| 			} | ||||
| 			else // draw in vertical axis | ||||
| 			{ | ||||
| 				int x1 = it->x0 - ray.size() / 2; | ||||
| 				int x2 = it->x - ray.size() / 2; | ||||
|  | ||||
| 				int y1 =  it->y0; | ||||
| 				int y2 =  it->y; | ||||
|  | ||||
| 				for (size_t i = 0; i < ray.size(); ++i) | ||||
| 				{ | ||||
| 					SDL_Color beginColor{ ray[i].r1, ray[i].g1, ray[i].b1, ray[i].a1}; | ||||
| 					SDL_Color endColor  { ray[i].r2, ray[i].g2, ray[i].b2, ray[i].a2}; | ||||
|  | ||||
| 					CSDL_Ext::drawLine(to, x1 + i, y1, x2 + i, y2, beginColor, endColor); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// Update projectile | ||||
| 		++it->step; | ||||
| 		if (it->step == it->lastStep) | ||||
| 		if (it->step > it->lastStep) | ||||
| 		{ | ||||
| 			toBeDeleted.insert(toBeDeleted.end(), it); | ||||
| 		} | ||||
|   | ||||
| @@ -17,6 +17,7 @@ | ||||
| #include "CBattleAnimations.h" | ||||
|  | ||||
| #include "../../lib/spells/CSpellHandler.h" //CSpell::TAnimation | ||||
| #include "../../lib/CCreatureHandler.h" | ||||
| #include "../../lib/battle/CBattleInfoCallback.h" | ||||
|  | ||||
| VCMI_LIB_NAMESPACE_BEGIN | ||||
| @@ -148,6 +149,7 @@ private: | ||||
| 	std::map<int32_t, std::shared_ptr<CCreatureAnimation>> creAnims; //animations of creatures from fighting armies (order by BattleInfo's stacks' ID) | ||||
|  | ||||
| 	std::map<int, std::shared_ptr<CAnimation>> idToProjectile; | ||||
| 	std::map<int, std::vector<CCreature::CreatureAnimation::RayColor>> idToRay; | ||||
|  | ||||
| 	std::map<std::string, std::shared_ptr<CAnimation>> animationsCache; | ||||
| 	std::map<si32, std::shared_ptr<CAnimation>> obstacleAnimations; | ||||
|   | ||||
| @@ -361,6 +361,75 @@ void CSDL_Ext::update(SDL_Surface * what) | ||||
| 	if(0 !=SDL_UpdateTexture(screenTexture, nullptr, what->pixels, what->pitch)) | ||||
| 		logGlobal->error("%s SDL_UpdateTexture %s", __FUNCTION__, SDL_GetError()); | ||||
| } | ||||
|  | ||||
| template<typename Int> | ||||
| Int lerp(Int a, Int b, float f) | ||||
| { | ||||
| 	return a + std::round((b - a) * f); | ||||
| } | ||||
|  | ||||
| static void drawLineX(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2) | ||||
| { | ||||
| 	for(int x = x1; x <= x2; x++) | ||||
| 	{ | ||||
| 		float f = float(x - x1) / float(x2 - x1); | ||||
| 		int y = lerp(y1, y2, f); | ||||
|  | ||||
| 		uint8_t r = lerp(color1.r, color2.r, f); | ||||
| 		uint8_t g = lerp(color1.g, color2.g, f); | ||||
| 		uint8_t b = lerp(color1.b, color2.b, f); | ||||
| 		uint8_t a = lerp(color1.a, color2.a, f); | ||||
|  | ||||
| 		Uint8 *p = CSDL_Ext::getPxPtr(sur, x, y); | ||||
| 		ColorPutter<4, 0>::PutColor(p, r,g,b,a); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void drawLineY(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2) | ||||
| { | ||||
| 	for(int y = y1; y <= y2; y++) | ||||
| 	{ | ||||
| 		float f = float(y - y1) / float(y2 - y1); | ||||
| 		int x = lerp(x1, x2, f); | ||||
|  | ||||
| 		uint8_t r = lerp(color1.r, color2.r, f); | ||||
| 		uint8_t g = lerp(color1.g, color2.g, f); | ||||
| 		uint8_t b = lerp(color1.b, color2.b, f); | ||||
| 		uint8_t a = lerp(color1.a, color2.a, f); | ||||
|  | ||||
| 		Uint8 *p = CSDL_Ext::getPxPtr(sur, x, y); | ||||
| 		ColorPutter<4, 0>::PutColor(p, r,g,b,a); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CSDL_Ext::drawLine(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2) | ||||
| { | ||||
| 	int width  = std::abs(x1-x2); | ||||
| 	int height = std::abs(y1-y2); | ||||
|  | ||||
| 	if ( width == 0 && height == 0) | ||||
| 	{ | ||||
| 		Uint8 *p = CSDL_Ext::getPxPtr(sur, x1, y1); | ||||
| 		ColorPutter<4, 0>::PutColorAlpha(p, color1); | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	if (width > height) | ||||
| 	{ | ||||
| 		if ( x1 < x2) | ||||
| 			drawLineX(sur, x1,y1,x2,y2, color1, color2); | ||||
| 		else | ||||
| 			drawLineX(sur, x2,y2,x1,y1, color2, color1); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if ( y1 < y2) | ||||
| 			drawLineY(sur, x1,y1,x2,y2, color1, color2); | ||||
| 		else | ||||
| 			drawLineY(sur, x2,y2,x1,y1, color2, color1); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void CSDL_Ext::drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const int3 &color) | ||||
| { | ||||
| 	for(int i = 0; i < w; i++) | ||||
|   | ||||
| @@ -236,6 +236,7 @@ namespace CSDL_Ext | ||||
| 	SDL_Color makeColor(ui8 r, ui8 g, ui8 b, ui8 a); | ||||
|  | ||||
| 	void update(SDL_Surface * what = screen); //updates whole surface (default - main screen) | ||||
| 	void drawLine(SDL_Surface * sur, int x1, int y1, int x2, int y2, const SDL_Color & color1, const SDL_Color & color2); | ||||
| 	void drawBorder(SDL_Surface * sur, int x, int y, int w, int h, const int3 &color); | ||||
| 	void drawBorder(SDL_Surface * sur, const SDL_Rect &r, const int3 &color); | ||||
| 	void drawDashedBorder(SDL_Surface * sur, const Rect &r, const int3 &color); | ||||
|   | ||||
| @@ -135,7 +135,14 @@ | ||||
| 			"animation": "CBEHOL.DEF", | ||||
| 			"missile" : | ||||
| 			{ | ||||
| 				"projectile": "SMBALX.DEF" | ||||
| 				"ray" : | ||||
| 				[ | ||||
| 					{ "start" : [ 160, 160, 160, 255 ], "end" : [ 160, 160, 160,  64 ] }, | ||||
| 					{ "start" : [ 192, 192, 192, 255 ], "end" : [ 192, 192, 192, 128 ] }, | ||||
| 					{ "start" : [ 224, 224, 224, 255 ], "end" : [ 224, 224, 224, 255 ] }, | ||||
| 					{ "start" : [ 192, 192, 192, 255 ], "end" : [ 192, 192, 192, 128 ] }, | ||||
| 					{ "start" : [ 160, 160, 160, 255 ], "end" : [ 160, 160, 160,  64 ] } | ||||
| 				] | ||||
| 			} | ||||
| 		}, | ||||
| 		"sound" : | ||||
| @@ -158,7 +165,14 @@ | ||||
| 			"animation": "CEVEYE.DEF", | ||||
| 			"missile" : | ||||
| 			{ | ||||
| 				"projectile": "SMBALX.DEF" | ||||
| 				"ray" : | ||||
| 				[ | ||||
| 					{ "start" : [ 160, 160, 160, 255 ], "end" : [ 160, 160, 160,  64 ] }, | ||||
| 					{ "start" : [ 192, 192, 192, 255 ], "end" : [ 192, 192, 192, 128 ] }, | ||||
| 					{ "start" : [ 224, 224, 224, 255 ], "end" : [ 224, 224, 224, 255 ] }, | ||||
| 					{ "start" : [ 192, 192, 192, 255 ], "end" : [ 192, 192, 192, 128 ] }, | ||||
| 					{ "start" : [ 160, 160, 160, 255 ], "end" : [ 160, 160, 160,  64 ] } | ||||
| 				] | ||||
| 			} | ||||
| 		}, | ||||
| 		"sound" : | ||||
|   | ||||
| @@ -208,7 +208,15 @@ | ||||
| 			"animation": "CAMAGE.DEF", | ||||
| 			"missile" : | ||||
| 			{ | ||||
| 				"projectile": "PMAGEX.DEF" | ||||
| 				"attackClimaxFrame" : 8, | ||||
| 				"ray" : | ||||
| 				[ | ||||
| 					{ "start" : [ 160, 192,   0, 255 ], "end" : [ 160, 192,   0,  64 ] }, | ||||
| 					{ "start" : [ 128, 224, 128, 255 ], "end" : [ 128, 224, 128, 128 ] }, | ||||
| 					{ "start" : [  32, 176,  32, 255 ], "end" : [  32, 176,  32, 255 ] }, | ||||
| 					{ "start" : [ 128, 224, 128, 255 ], "end" : [ 128, 224, 128, 128 ] }, | ||||
| 					{ "start" : [ 160, 192,   0, 255 ], "end" : [ 160, 192,   0,  64 ] } | ||||
| 				] | ||||
| 			} | ||||
| 		}, | ||||
| 		"sound" : | ||||
|   | ||||
| @@ -217,7 +217,7 @@ | ||||
| 				"missile": { | ||||
| 					"type":"object", | ||||
| 					"additionalProperties" : false, | ||||
| 					"required" : [ "projectile", "frameAngles", "offset", "attackClimaxFrame" ], | ||||
| 					"required" : [ "frameAngles", "offset", "attackClimaxFrame" ], | ||||
| 					"description": "Missile description for archers", | ||||
| 					"properties":{ | ||||
| 						"projectile": { | ||||
| @@ -225,6 +225,37 @@ | ||||
| 							"description": "Path to projectile animation", | ||||
| 							"format" : "defFile" | ||||
| 						}, | ||||
| 						"ray": { | ||||
| 							"type":"array", | ||||
| 							"description": "Colors of ray projectile animation", | ||||
| 							"minItems" : 1, | ||||
| 							"items": { | ||||
| 								"type":"object", | ||||
| 								"required" : [ "start", "end" ], | ||||
| 								"properties":{ | ||||
| 									"start":  { | ||||
| 										"type":"array", | ||||
| 										"minItems" : 4, | ||||
| 										"maxItems" : 4, | ||||
| 										"items": { | ||||
| 											"minimum" : 0, | ||||
| 											"maximum" : 255, | ||||
| 											"type":"number" | ||||
| 										} | ||||
| 									 }, | ||||
| 									"end":  { | ||||
| 										"type":"array", | ||||
| 										"minItems" : 4, | ||||
| 										"maxItems" : 4, | ||||
| 										"items": { | ||||
| 											"minimum" : 0, | ||||
| 											"maximum" : 255, | ||||
| 											"type":"number" | ||||
| 										} | ||||
| 									} | ||||
| 								} | ||||
| 							} | ||||
| 						}, | ||||
| 						"frameAngles": { | ||||
| 							"type":"array", | ||||
| 							"description": "Angles of missile images, should go from 90 to -90", | ||||
|   | ||||
| @@ -907,6 +907,23 @@ void CCreatureHandler::loadCreatureJson(CCreature * creature, const JsonNode & c | ||||
|  | ||||
| 	creature->animation.projectileImageName = config["graphics"]["missile"]["projectile"].String(); | ||||
|  | ||||
| 	for(const JsonNode & value : config["graphics"]["missile"]["ray"].Vector()) | ||||
| 	{ | ||||
| 		CCreature::CreatureAnimation::RayColor color; | ||||
|  | ||||
| 		color.r1 = value["start"].Vector()[0].Integer(); | ||||
| 		color.g1 = value["start"].Vector()[1].Integer(); | ||||
| 		color.b1 = value["start"].Vector()[2].Integer(); | ||||
| 		color.a1 = value["start"].Vector()[3].Integer(); | ||||
|  | ||||
| 		color.r2 = value["end"].Vector()[0].Integer(); | ||||
| 		color.g2 = value["end"].Vector()[1].Integer(); | ||||
| 		color.b2 = value["end"].Vector()[2].Integer(); | ||||
| 		color.a2 = value["end"].Vector()[3].Integer(); | ||||
|  | ||||
| 		creature->animation.projectileRay.push_back(color); | ||||
| 	} | ||||
|  | ||||
| 	creature->special = config["special"].Bool() || config["disabled"].Bool(); | ||||
|  | ||||
| 	const JsonNode & sounds = config["sound"]; | ||||
|   | ||||
| @@ -63,6 +63,16 @@ public: | ||||
|  | ||||
| 	struct CreatureAnimation | ||||
| 	{ | ||||
| 		struct RayColor { | ||||
| 			uint8_t r1, g1, b1, a1; | ||||
| 			uint8_t r2, g2, b2, a2; | ||||
|  | ||||
| 			template <typename Handler> void serialize(Handler &h, const int version) | ||||
| 			{ | ||||
| 				h & r1 & g1 & b1 & a1 & r2 & g2 & b2 & a2; | ||||
| 			} | ||||
| 		}; | ||||
|  | ||||
| 		double timeBetweenFidgets, idleAnimationTime, | ||||
| 			   walkAnimationTime, attackAnimationTime, flightAnimationDistance; | ||||
| 		int upperRightMissleOffsetX, rightMissleOffsetX, lowerRightMissleOffsetX, | ||||
| @@ -72,6 +82,7 @@ public: | ||||
| 		int troopCountLocationOffset, attackClimaxFrame; | ||||
|  | ||||
| 		std::string projectileImageName; | ||||
| 		std::vector<RayColor> projectileRay; | ||||
| 		//bool projectileSpin; //if true, appropriate projectile is spinning during flight | ||||
|  | ||||
| 		template <typename Handler> void serialize(Handler &h, const int version) | ||||
| @@ -91,6 +102,7 @@ public: | ||||
| 			h & troopCountLocationOffset; | ||||
| 			h & attackClimaxFrame; | ||||
| 			h & projectileImageName; | ||||
| 			h & projectileRay; | ||||
| 		} | ||||
| 	} animation; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user