From 619a07da0364103ad6bb9932cc1d11b6be176cbb Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Thu, 4 Apr 2013 13:18:38 +0000 Subject: [PATCH] - projectile blitting should be closer to original H3. But still not perfect. - removed no longer needed field "projectile spins" --- client/BattleInterface/CBattleAnimations.cpp | 74 +++++++++++++------- client/BattleInterface/CBattleAnimations.h | 2 +- client/BattleInterface/CBattleInterface.cpp | 24 ++----- config/creatures/neutral.json | 2 +- config/schemas/creature.json | 10 +-- lib/CCreatureHandler.cpp | 14 ++-- lib/CCreatureHandler.h | 6 +- 7 files changed, 67 insertions(+), 65 deletions(-) diff --git a/client/BattleInterface/CBattleAnimations.cpp b/client/BattleInterface/CBattleAnimations.cpp index 435a1626b..7dba59246 100644 --- a/client/BattleInterface/CBattleAnimations.cpp +++ b/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(abs(dest - fromHex) / GameConstants::BFIELD_WIDTH), static_cast(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(destcoord.x - xycoord.x)), + fabs(static_cast(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((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(spi.x + spi.lastStep * spi.dx); @@ -821,17 +827,31 @@ bool CShootingAnimation::init() } } - // Set starting frame - if(spi.spin) + auto & angles = shooterInfo->animation.missleFrameAngles; + double pi = boost::math::constants::pi(); + + // 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(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(); - spi.frameNum = static_cast(((pi / 2.0 - projectileAngle) / (2.0 * pi) + 1/(static_cast(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); diff --git a/client/BattleInterface/CBattleAnimations.h b/client/BattleInterface/CBattleAnimations.h index 69c843df1..d523889e7 100644 --- a/client/BattleInterface/CBattleAnimations.h +++ b/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 diff --git a/client/BattleInterface/CBattleInterface.cpp b/client/BattleInterface/CBattleInterface.cpp index db049bc4e..8a1f27965 100644 --- a/client/BattleInterface/CBattleInterface.cpp +++ b/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::iterator >::iterator it = toBeDeleted.begin(); it!= toBeDeleted.end(); ++it) diff --git a/config/creatures/neutral.json b/config/creatures/neutral.json index fa22446b4..44ec57ac1 100644 --- a/config/creatures/neutral.json +++ b/config/creatures/neutral.json @@ -159,7 +159,7 @@ "animation": "CENCH.DEF", "missile" : { - "projectile": "SMBALX.DEF", + "projectile": "CPRZEAX.DEF", "spinning": false } }, diff --git a/config/schemas/creature.json b/config/schemas/creature.json index 98f90fd1f..3ebc7a7a2 100644 --- a/config/schemas/creature.json +++ b/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" } } }, diff --git a/lib/CCreatureHandler.cpp b/lib/CCreatureHandler.cpp index 22a583eca..771bd05bb 100644 --- a/lib/CCreatureHandler.cpp +++ b/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>(); + 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(); diff --git a/lib/CCreatureHandler.h b/lib/CCreatureHandler.h index 9dc8ab5f7..7e980026d 100644 --- a/lib/CCreatureHandler.h +++ b/lib/CCreatureHandler.h @@ -54,11 +54,11 @@ public: int upperRightMissleOffsetX, rightMissleOffsetX, lowerRightMissleOffsetX, upperRightMissleOffsetY, rightMissleOffsetY, lowerRightMissleOffsetY; - double missleFrameAngles[12]; + std::vector 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 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;