mirror of
https://github.com/vcmi/vcmi.git
synced 2024-11-24 08:32:34 +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:
parent
655ade9a00
commit
619a07da03
@ -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,16 +827,30 @@ bool CShootingAnimation::init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set starting frame
|
auto & angles = shooterInfo->animation.missleFrameAngles;
|
||||||
if(spi.spin)
|
|
||||||
{
|
|
||||||
spi.frameNum = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
double pi = boost::math::constants::pi<double>();
|
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));
|
|
||||||
|
// 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 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
|
// Set projectile animation start delay which is specified in frames
|
||||||
spi.animStartDelay = shooterInfo->animation.attackClimaxFrame;
|
spi.animStartDelay = shooterInfo->animation.attackClimaxFrame;
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user