/* Update channel animation variables, called on each frame */ void animate(void) { uint8_t i; struct channel_t *c, *p; struct clip_t *d; channelsLock(); for(i = 0; i < 3; i++) { if(state.channels[i].clipCount == 0) { /* No clips on channel */ continue; } else if(clipGet(i, state.channels[i].current)->ready == 0) { /* Current clip not ready so no clips on channel, really */ continue; } c = &state.channels[i]; p = &state.prev[i]; d = clipGet(i, c->current); /* Playhead position */ if(c->current == p->current && c->now.pos == p->now.pos && c->locks.pos == 0.0) { /* Same clip is current as at last frame and pos hasn't been changed and isn't locked */ animPos(c, d); } } /* Store the current channel data for comparision next time around */ memcpy(state.prev, state.channels, 3 * sizeof(struct channel_t)); channelsUnlock(); }
bool CShootingAnimation::init() { if( !CAttackAnimation::checkInitialConditions() ) return false; const CStack * shooter = attackingStack; if(!shooter || myAnim->isDead()) { endAnim(); return false; } //reverse unit if necessary if (attackingStack && attackedStack && owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->position, attackedStack->position, owner->creDir[attackingStack->ID], attackingStack->doubleWide(), owner->creDir[attackedStack->ID])) { owner->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->position, true)); return false; } // opponent must face attacker ( = different directions) before he can be attacked if (attackingStack && attackedStack && owner->creDir[attackingStack->ID] == owner->creDir[attackedStack->ID]) return false; // Create the projectile animation //maximal angle in radians between straight horizontal line and shooting line for which shot is considered to be straight (absoulte value) static const double straightAngle = 0.2; // 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 // actual arrow tower shooter instead. const CCreature *shooterInfo = shooter->getCreature(); if (shooterInfo->idNumber == CreatureID::ARROW_TOWERS) { int creID = owner->siegeH->town->town->clientInfo.siegeShooter; shooterInfo = CGI->creh->creatures[creID]; } ProjectileInfo spi; spi.shotDone = false; spi.creID = shooter->getCreature()->idNumber; spi.stackID = shooter->ID; // 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; Point fromPos; Point destPos; // NOTE: two lines below return different positions (very notable with 2-hex creatures). Obtaining via creanims seems to be more precise fromPos = owner->creAnims[spi.stackID]->pos.topLeft(); //xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner); destPos = CClickableHex::getXYUnitAnim(dest, attackedStack, owner); // to properly translate coordinates when shooter is rotated int multiplier = spi.reverse ? -1 : 1; double projectileAngle = atan2(fabs((double)destPos.y - fromPos.y), fabs((double)destPos.x - fromPos.x)); if(shooter->position < dest) projectileAngle = -projectileAngle; // Calculate projectile start position. Offsets are read out of the CRANIM.TXT. if (projectileAngle > straightAngle) { //upper shot spi.x = fromPos.x + 222 + ( -25 + shooterInfo->animation.upperRightMissleOffsetX ) * multiplier; spi.y = 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; } else { //straight shot spi.x = fromPos.x + 222 + ( -25 + shooterInfo->animation.rightMissleOffsetX ) * multiplier; spi.y = fromPos.y + 265 + shooterInfo->animation.rightMissleOffsetY; } destPos += Point(225, 225); // recalculate angle taking in account offsets //projectileAngle = atan2(fabs(destPos.y - spi.y), fabs(destPos.x - spi.x)); //if(shooter->position < dest) // projectileAngle = -projectileAngle; 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); if(spi.lastStep == 0) spi.lastStep = 1; spi.dx = (destPos.x - spi.x) / spi.lastStep; spi.dy = (destPos.y - spi.y) / spi.lastStep; } else { // Catapult attack spi.catapultInfo.reset(new CatapultProjectileInfo(Point(spi.x, spi.y), destPos)); double animSpeed = AnimationControls::getProjectileSpeed() / 10; spi.lastStep = abs((destPos.x - spi.x) / animSpeed); spi.dx = animSpeed; spi.dy = 0; SDL_Surface * img = owner->idToProjectile[spi.creID]->ourImages[0].bitmap; // Add explosion anim Point animPos(destPos.x - 126 + img->w / 2, destPos.y - 105 + img->h / 2); owner->addNewAnim( new CSpellEffectAnimation(owner, catapultDamage ? "SGEXPL.DEF" : "CSGRCK.DEF", animPos.x, animPos.y)); } 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 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); //attack animation shooting = true; if(projectileAngle > straightAngle) //upper shot group = CCreatureAnim::SHOOT_UP; else if(projectileAngle < -straightAngle) //lower shot group = CCreatureAnim::SHOOT_DOWN; else //straight shot group = CCreatureAnim::SHOOT_FRONT; return true; }