diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/client/battle/CBattleAnimations.cpp vcmi-0.99+git201710111318~ubuntu15.04.1/client/battle/CBattleAnimations.cpp --- vcmi-0.99+git201710071442~ubuntu15.04.1/client/battle/CBattleAnimations.cpp 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/client/battle/CBattleAnimations.cpp 2017-10-11 14:18:44.000000000 +0000 @@ -138,11 +138,6 @@ dest(_dest), attackedStack(defender), attackingStack(attacker) { assert(attackingStack && "attackingStack is nullptr in CBattleAttack::CBattleAttack !\n"); - bool isCatapultAttack = attackingStack->hasBonusOfType(Bonus::CATAPULT) - && owner->getCurrentPlayerInterface()->cb->battleHexToWallPart(_dest) >= 0; - - assert(attackedStack || isCatapultAttack); - UNUSED(isCatapultAttack); attackingStackPosBeforeReturn = attackingStack->position; } @@ -241,7 +236,12 @@ CCreatureAnim::EAnimType CDefenceAnimation::getMyAnimType() { if(killed) - return CCreatureAnim::DEATH; + { + if(rangedAttack && myAnim->framesInGroup(CCreatureAnim::DEATH_RANGED) > 0) + return CCreatureAnim::DEATH_RANGED; + else + return CCreatureAnim::DEATH; + } if(vstd::contains(stack->state, EBattleStackState::DEFENDING_ANIM)) return CCreatureAnim::DEFENCE; @@ -272,7 +272,10 @@ { if(killed) { - myAnim->setType(CCreatureAnim::DEAD); + if(rangedAttack && myAnim->framesInGroup(CCreatureAnim::DEAD_RANGED) > 0) + myAnim->setType(CCreatureAnim::DEAD_RANGED); + else + myAnim->setType(CCreatureAnim::DEAD); } else { @@ -312,26 +315,25 @@ bool CMeleeAttackAnimation::init() { - if( !CAttackAnimation::checkInitialConditions() ) + if(!CAttackAnimation::checkInitialConditions()) return false; if(!attackingStack || myAnim->isDead()) { endAnim(); - return false; } bool toReverse = owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStackPosBeforeReturn, attackedStack->position, owner->creDir[stack->ID], attackedStack->doubleWide(), owner->creDir[attackedStack->ID]); - if (toReverse) + if(toReverse) { owner->addNewAnim(new CReverseAnimation(owner, stack, attackingStackPosBeforeReturn, true)); return false; } // opponent must face attacker ( = different directions) before he can be attacked - if (attackingStack && attackedStack && + if(attackingStack && attackedStack && owner->creDir[attackingStack->ID] == owner->creDir[attackedStack->ID]) return false; @@ -339,8 +341,25 @@ shooting = false; - static const CCreatureAnim::EAnimType mutPosToGroup[] = {CCreatureAnim::ATTACK_UP, CCreatureAnim::ATTACK_UP, - CCreatureAnim::ATTACK_FRONT, CCreatureAnim::ATTACK_DOWN, CCreatureAnim::ATTACK_DOWN, CCreatureAnim::ATTACK_FRONT}; + static const CCreatureAnim::EAnimType mutPosToGroup[] = + { + CCreatureAnim::ATTACK_UP, + CCreatureAnim::ATTACK_UP, + CCreatureAnim::ATTACK_FRONT, + CCreatureAnim::ATTACK_DOWN, + CCreatureAnim::ATTACK_DOWN, + CCreatureAnim::ATTACK_FRONT + }; + + static const CCreatureAnim::EAnimType mutPosToGroup2H[] = + { + CCreatureAnim::VCMI_2HEX_UP, + CCreatureAnim::VCMI_2HEX_UP, + CCreatureAnim::VCMI_2HEX_FRONT, + CCreatureAnim::VCMI_2HEX_DOWN, + CCreatureAnim::VCMI_2HEX_DOWN, + CCreatureAnim::VCMI_2HEX_FRONT + }; int revShiftattacker = (attackingStack->side == BattleSide::ATTACKER ? -1 : 1); @@ -361,8 +380,20 @@ switch(mutPos) //attack direction { - case 0: case 1: case 2: case 3: case 4: case 5: + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: group = mutPosToGroup[mutPos]; + if(attackingStack->hasBonusOfType(Bonus::TWO_HEX_ATTACK_BREATH)) + { + CCreatureAnim::EAnimType group2H = mutPosToGroup2H[mutPos]; + if(myAnim->framesInGroup(group2H)>0) + group = group2H; + } + break; default: logGlobal->error("Critical Error! Wrong dest in stackAttacking! dest: %d; attacking stack pos: %d; mutual pos: %d", dest.hex, attackingStackPosBeforeReturn, mutPos); @@ -672,8 +703,16 @@ endAnim(); } +CRangedAttackAnimation::CRangedAttackAnimation(CBattleInterface * owner_, const CStack * attacker, BattleHex dest_, const CStack * defender) + : CAttackAnimation(owner_, attacker, dest_, defender) +{ + +} + + CShootingAnimation::CShootingAnimation(CBattleInterface * _owner, const CStack * attacker, BattleHex _dest, const CStack * _attacked, bool _catapult, int _catapultDmg) -: CAttackAnimation(_owner, attacker, _dest, _attacked), catapultDamage(_catapultDmg) + : CRangedAttackAnimation(_owner, attacker, _dest, _attacked), + catapultDamage(_catapultDmg) { logAnim->debug("Created shooting anim for %s", stack->getName()); } @@ -835,9 +874,9 @@ shooting = true; - if(projectileAngle > straightAngle) //upper shot + if(projectileAngle > straightAngle) group = CCreatureAnim::SHOOT_UP; - else if(projectileAngle < -straightAngle) //lower shot + else if(projectileAngle < -straightAngle) group = CCreatureAnim::SHOOT_DOWN; else //straight shot group = CCreatureAnim::SHOOT_FRONT; @@ -860,23 +899,149 @@ void CShootingAnimation::endAnim() { - // play wall hit/miss sound for catapult attack - if(!attackedStack) - { - if(catapultDamage > 0) - { - CCS->soundh->playSound("WALLHIT"); - } - else - { - CCS->soundh->playSound("WALLMISS"); - } - } + // play wall hit/miss sound for catapult attack + if(!attackedStack) + { + if(catapultDamage > 0) + { + CCS->soundh->playSound("WALLHIT"); + } + else + { + CCS->soundh->playSound("WALLMISS"); + } + } CAttackAnimation::endAnim(); delete this; } +CCastAnimation::CCastAnimation(CBattleInterface * owner_, const CStack * attacker, BattleHex dest_, const CStack * defender) + : CRangedAttackAnimation(owner_, attacker, dest_, defender) +{ + if(!dest_.isValid() && defender) + dest = defender->position; +} + +bool CCastAnimation::init() +{ + if(!CAttackAnimation::checkInitialConditions()) + return false; + + if(!attackingStack || myAnim->isDead()) + { + endAnim(); + return false; + } + + //reverse unit if necessary + if(attackedStack) + { + if(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; + } + } + else + { + if(dest.isValid() && owner->getCurrentPlayerInterface()->cb->isToReverse(attackingStack->position, dest, owner->creDir[attackingStack->ID], false, false)) + { + owner->addNewAnim(new CReverseAnimation(owner, attackingStack, attackingStack->position, true)); + return false; + } + } + + //TODO: display spell projectile here + + static const double straightAngle = 0.2; + + + 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[attackingStack->ID]->pos.topLeft(); + //xycoord = CClickableHex::getXYUnitAnim(shooter->position, true, shooter, owner); + + destPos = CClickableHex::getXYUnitAnim(dest, attackedStack, owner); + + + double projectileAngle = atan2(fabs((double)destPos.y - fromPos.y), fabs((double)destPos.x - fromPos.x)); + if(attackingStack->position < dest) + projectileAngle = -projectileAngle; + + + if(projectileAngle > straightAngle) + group = CCreatureAnim::VCMI_CAST_UP; + else if(projectileAngle < -straightAngle) + group = CCreatureAnim::VCMI_CAST_DOWN; + else + group = CCreatureAnim::VCMI_CAST_FRONT; + + //fall back to H3 cast/2hex + //even if creature have 2hex attack instead of cast it is ok since we fall back to attack anyway + if(myAnim->framesInGroup(group) == 0) + { + if(projectileAngle > straightAngle) + group = CCreatureAnim::CAST_UP; + else if(projectileAngle < -straightAngle) + group = CCreatureAnim::CAST_DOWN; + else + group = CCreatureAnim::CAST_FRONT; + } + + //fall back to ranged attack + if(myAnim->framesInGroup(group) == 0) + { + if(projectileAngle > straightAngle) + group = CCreatureAnim::SHOOT_UP; + else if(projectileAngle < -straightAngle) + group = CCreatureAnim::SHOOT_DOWN; + else + group = CCreatureAnim::SHOOT_FRONT; + } + + //fall back to normal attack + if(myAnim->framesInGroup(group) == 0) + { + if(projectileAngle > straightAngle) + group = CCreatureAnim::ATTACK_UP; + else if(projectileAngle < -straightAngle) + group = CCreatureAnim::ATTACK_DOWN; + else + group = CCreatureAnim::ATTACK_FRONT; + } + + return true; +} + +void CCastAnimation::nextFrame() +{ + for(auto & it : owner->pendingAnims) + { + CReverseAnimation * anim = dynamic_cast(it.first); + if(anim && anim->stack->ID == stack->ID && anim->priority) + return; + } + + if(myAnim->getType() != group) + { + myAnim->setType(group); + myAnim->onAnimationReset += std::bind(&CAttackAnimation::endAnim, this); + } + + CBattleAnimation::nextFrame(); +} + + +void CCastAnimation::endAnim() +{ + CAttackAnimation::endAnim(); + delete this; +} + + CEffectAnimation::CEffectAnimation(CBattleInterface * _owner, std::string _customAnim, int _x, int _y, int _dx, int _dy, bool _Vflip, bool _alignToBottom) : CBattleAnimation(_owner), destTile(BattleHex::INVALID), diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/client/battle/CBattleAnimations.h vcmi-0.99+git201710111318~ubuntu15.04.1/client/battle/CBattleAnimations.h --- vcmi-0.99+git201710071442~ubuntu15.04.1/client/battle/CBattleAnimations.h 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/client/battle/CBattleAnimations.h 2017-10-11 14:18:44.000000000 +0000 @@ -197,8 +197,16 @@ std::shared_ptr catapultInfo; // holds info about the parabolic trajectory of the cannon }; +class CRangedAttackAnimation : public CAttackAnimation +{ +public: + CRangedAttackAnimation(CBattleInterface * owner_, const CStack * attacker, BattleHex dest_, const CStack * defender); +protected: + +}; + /// Shooting attack -class CShootingAnimation : public CAttackAnimation +class CShootingAnimation : public CRangedAttackAnimation { private: int catapultDamage; @@ -213,6 +221,18 @@ virtual ~CShootingAnimation(){}; }; +class CCastAnimation : public CRangedAttackAnimation +{ +public: + CCastAnimation(CBattleInterface * owner_, const CStack * attacker, BattleHex dest_, const CStack * defender); + + bool init() override; + void nextFrame() override; + void endAnim() override; + +}; + + /// This class manages effect animation class CEffectAnimation : public CBattleAnimation { diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/client/battle/CBattleInterface.cpp vcmi-0.99+git201710111318~ubuntu15.04.1/client/battle/CBattleInterface.cpp --- vcmi-0.99+git201710071442~ubuntu15.04.1/client/battle/CBattleInterface.cpp 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/client/battle/CBattleInterface.cpp 2017-10-11 14:18:44.000000000 +0000 @@ -1069,12 +1069,27 @@ } } waitForAnims(); - int targets = 0, killed = 0, damage = 0; - for (auto & attackedInfo : attackedInfos) + + std::array killedBySide = {0, 0}; + + int targets = 0, damage = 0; + for(const StackAttackedInfo & attackedInfo : attackedInfos) { ++targets; - killed += attackedInfo.amountKilled; damage += attackedInfo.dmg; + + ui8 side = attackedInfo.defender->side; + killedBySide.at(side) += attackedInfo.amountKilled; + } + + int killed = killedBySide[0] + killedBySide[1]; + + for(ui8 side = 0; side < 2; side++) + { + if(killedBySide.at(side) > killedBySide.at(1-side)) + setHeroAnimation(side, 2); + else if(killedBySide.at(side) < killedBySide.at(1-side)) + setHeroAnimation(side, 3); } for (auto & attackedInfo : attackedInfos) @@ -1266,7 +1281,7 @@ curInt->waitWhileDialog(); // Avoid freeze when AI end turn after battle. Check bug #1897 } -void CBattleInterface::spellCast(const BattleSpellCast *sc) +void CBattleInterface::spellCast(const BattleSpellCast * sc) { const SpellID spellID(sc->id); const CSpell & spell = *spellID.toSpell(); @@ -1276,24 +1291,32 @@ if (!castSoundPath.empty()) CCS->soundh->playSound(castSoundPath); - Point srccoord = (sc->side ? Point(770, 60) : Point(30, 60)) + pos; //hero position by default + const auto casterStackID = sc->casterStack; + const CStack * casterStack = nullptr; + if(casterStackID >= 0) { - const auto casterStackID = sc->casterStack; + casterStack = curInt->cb->battleGetStackByID(casterStackID); + } - if (casterStackID > 0) + Point srccoord = (sc->side ? Point(770, 60) : Point(30, 60)) + pos; //hero position by default + { + if(casterStack != nullptr) { - const CStack *casterStack = curInt->cb->battleGetStackByID(casterStackID); - if (casterStack != nullptr) - { - srccoord = CClickableHex::getXYUnitAnim(casterStack->position, casterStack, this); - srccoord.x += 250; - srccoord.y += 240; - } + srccoord = CClickableHex::getXYUnitAnim(casterStack->position, casterStack, this); + srccoord.x += 250; + srccoord.y += 240; } } - //todo: play custom cast animation - displaySpellCast(spellID, BattleHex::INVALID); + if(casterStack != nullptr && sc->activeCast) + { + //todo: custom cast animation for hero + displaySpellCast(spellID, casterStack->position); + + addNewAnim(new CCastAnimation(this, casterStack, sc->tile, curInt->cb->battleGetStackByPos(sc->tile))); + } + + waitForAnims(); //wait for cast animation //playing projectile animation if (sc->tile.isValid()) @@ -1328,7 +1351,8 @@ addNewAnim(new CEffectAnimation(this, animToDisplay, srccoord.x, srccoord.y, dx, dy, Vflip)); } } - waitForAnims(); + + waitForAnims(); //wait for projectile animation displaySpellHit(spellID, sc->tile); @@ -1389,6 +1413,20 @@ return spellSelMode; } +void CBattleInterface::setHeroAnimation(ui8 side, int phase) +{ + if(side == BattleSide::ATTACKER) + { + if(attackingHero) + attackingHero->setPhase(phase); + } + else + { + if(defendingHero) + defendingHero->setPhase(phase); + } +} + void CBattleInterface::castThisSpell(SpellID spellID) { auto ba = new BattleAction(); @@ -1772,13 +1810,8 @@ { const CStack *stack = curInt->cb->battleGetStackByID(action->stackNumber); - if (action->actionType == Battle::HERO_SPELL) - { - if (action->side) - defendingHero->setPhase(0); - else - attackingHero->setPhase(0); - } + if(action->actionType == Battle::HERO_SPELL) + setHeroAnimation(action->side, 0); if (stack && action->actionType == Battle::WALK && !creAnims[action->stackNumber]->isIdle()) //walk or walk & attack @@ -1926,14 +1959,12 @@ redraw(); // redraw after deactivation, including proper handling of hovered hexes - if (action->actionType == Battle::HERO_SPELL) //when hero casts spell + if(action->actionType == Battle::HERO_SPELL) //when hero casts spell { - if (action->side) - defendingHero->setPhase(4); - else - attackingHero->setPhase(4); + setHeroAnimation(action->side, 4); return; } + if (!stack) { logGlobal->error("Something wrong with stackNumber in actionStarted. Stack number: %d", action->stackNumber); diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/client/battle/CBattleInterface.h vcmi-0.99+git201710111318~ubuntu15.04.1/client/battle/CBattleInterface.h --- vcmi-0.99+git201710071442~ubuntu15.04.1/client/battle/CBattleInterface.h 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/client/battle/CBattleInterface.h 2017-10-11 14:18:44.000000000 +0000 @@ -265,6 +265,8 @@ /** End of battle screen blitting methods */ PossibleActions getCasterAction(const CSpell *spell, const ISpellCaster *caster, ECastingMode::ECastingMode mode) const; + + void setHeroAnimation(ui8 side, int phase); public: static CondSh animsAreDisplayed; //for waiting with the end of battle for end of anims static CondSh givenCommand; //data != nullptr if we have i.e. moved current unit @@ -388,5 +390,6 @@ friend class CAttackAnimation; friend class CMeleeAttackAnimation; friend class CShootingAnimation; + friend class CCastAnimation; friend class CClickableHex; }; diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/client/battle/CCreatureAnimation.cpp vcmi-0.99+git201710111318~ubuntu15.04.1/client/battle/CCreatureAnimation.cpp --- vcmi-0.99+git201710071442~ubuntu15.04.1/client/battle/CCreatureAnimation.cpp 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/client/battle/CCreatureAnimation.cpp 2017-10-11 14:18:44.000000000 +0000 @@ -72,6 +72,9 @@ case CCreatureAnim::CAST_UP: case CCreatureAnim::CAST_FRONT: case CCreatureAnim::CAST_DOWN: + case CCreatureAnim::VCMI_CAST_DOWN: + case CCreatureAnim::VCMI_CAST_FRONT: + case CCreatureAnim::VCMI_CAST_UP: return speed * 4 * creature->animation.attackAnimationTime / anim->framesInGroup(type); // as strange as it looks like "attackAnimationTime" does not affects melee attacks @@ -82,6 +85,10 @@ case CCreatureAnim::HITTED: case CCreatureAnim::DEFENCE: case CCreatureAnim::DEATH: + case CCreatureAnim::DEATH_RANGED: + case CCreatureAnim::VCMI_2HEX_DOWN: + case CCreatureAnim::VCMI_2HEX_FRONT: + case CCreatureAnim::VCMI_2HEX_UP: return speed * 3 / anim->framesInGroup(type); case CCreatureAnim::TURN_L: @@ -93,11 +100,11 @@ return speed / 3; case CCreatureAnim::DEAD: + case CCreatureAnim::DEAD_RANGED: return speed; default: - assert(0); - return 1; + return speed; } } @@ -128,9 +135,6 @@ void CCreatureAnimation::setType(CCreatureAnim::EAnimType type) { - assert(type >= 0); - assert(framesInGroup(type) != 0); - this->type = type; currentFrame = 0; once = false; @@ -162,6 +166,12 @@ reverse->duplicateImage(CCreatureAnim::DEATH, reverse->size(CCreatureAnim::DEATH)-1, CCreatureAnim::DEAD); } + if(forward->size(CCreatureAnim::DEAD_RANGED) == 0 && forward->size(CCreatureAnim::DEATH_RANGED) != 0) + { + forward->duplicateImage(CCreatureAnim::DEATH_RANGED, forward->size(CCreatureAnim::DEATH_RANGED)-1, CCreatureAnim::DEAD_RANGED); + reverse->duplicateImage(CCreatureAnim::DEATH_RANGED, reverse->size(CCreatureAnim::DEATH_RANGED)-1, CCreatureAnim::DEAD_RANGED); + } + //TODO: get dimensions form CAnimation IImage * first = forward->getImage(0, type, true); @@ -191,6 +201,7 @@ { elapsedTime += timePassed; currentFrame += timePassed * speed; + if (currentFrame >= float(framesInGroup(type))) { // just in case of extremely low fps (or insanely high speed) @@ -271,7 +282,7 @@ target[2] = addColors(genShadow(64), genBorderColor(getBorderStrength(elapsedTime), border)); } -void CCreatureAnimation::nextFrame(SDL_Surface *dest, bool attacker) +void CCreatureAnimation::nextFrame(SDL_Surface * dest, bool attacker) { size_t frame = floor(currentFrame); @@ -282,12 +293,15 @@ else image = reverse->getImage(frame, type); - IImage::BorderPallete borderPallete; - genBorderPalette(borderPallete); + if(image) + { + IImage::BorderPallete borderPallete; + genBorderPalette(borderPallete); - image->setBorderPallete(borderPallete); + image->setBorderPallete(borderPallete); - image->draw(dest, pos.x, pos.y); + image->draw(dest, pos.x, pos.y); + } } int CCreatureAnimation::framesInGroup(CCreatureAnim::EAnimType group) const @@ -298,7 +312,9 @@ bool CCreatureAnimation::isDead() const { return getType() == CCreatureAnim::DEAD - || getType() == CCreatureAnim::DEATH; + || getType() == CCreatureAnim::DEATH + || getType() == CCreatureAnim::DEAD_RANGED + || getType() == CCreatureAnim::DEATH_RANGED; } bool CCreatureAnimation::isIdle() const diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/client/battle/CCreatureAnimation.h vcmi-0.99+git201710111318~ubuntu15.04.1/client/battle/CCreatureAnimation.h --- vcmi-0.99+git201710071442~ubuntu15.04.1/client/battle/CCreatureAnimation.h 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/client/battle/CCreatureAnimation.h 2017-10-11 14:18:44.000000000 +0000 @@ -108,7 +108,7 @@ void playOnce(CCreatureAnim::EAnimType type); //plays once given stage of animation, then resets to 2 - int framesInGroup(CCreatureAnim::EAnimType group) const; //retirns number of fromes in given group + int framesInGroup(CCreatureAnim::EAnimType group) const; void pause(); void play(); diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/client/CBitmapHandler.cpp vcmi-0.99+git201710111318~ubuntu15.04.1/client/CBitmapHandler.cpp --- vcmi-0.99+git201710071442~ubuntu15.04.1/client/CBitmapHandler.cpp 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/client/CBitmapHandler.cpp 2017-10-11 14:18:44.000000000 +0000 @@ -178,7 +178,7 @@ SDL_Surface * BitmapHandler::loadBitmap(std::string fname, bool setKey) { - SDL_Surface *bitmap; + SDL_Surface * bitmap = nullptr; if (!(bitmap = loadBitmapFromDir("DATA/", fname, setKey)) && !(bitmap = loadBitmapFromDir("SPRITES/", fname, setKey))) diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/client/gui/CAnimation.cpp vcmi-0.99+git201710111318~ubuntu15.04.1/client/gui/CAnimation.cpp --- vcmi-0.99+git201710071442~ubuntu15.04.1/client/gui/CAnimation.cpp 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/client/gui/CAnimation.cpp 2017-10-11 14:18:44.000000000 +0000 @@ -82,6 +82,8 @@ SDLImage(CDefFile *data, size_t frame, size_t group=0, bool compressed=false); //Load from bitmap file SDLImage(std::string filename, bool compressed=false); + + SDLImage(const JsonNode & conf); //Create using existing surface, extraRef will increase refcount on SDL_Surface SDLImage(SDL_Surface * from, bool extraRef); ~SDLImage(); @@ -803,15 +805,19 @@ refCount++; } -SDLImage::SDLImage(CDefFile *data, size_t frame, size_t group, bool compressed): - surf(nullptr) +SDLImage::SDLImage(CDefFile * data, size_t frame, size_t group, bool compressed) + : surf(nullptr), + margins(0, 0), + fullSize(0, 0) { SDLImageLoader loader(this); data->loadFrame(frame, group, loader); } -SDLImage::SDLImage(SDL_Surface * from, bool extraRef): - margins(0,0) +SDLImage::SDLImage(SDL_Surface * from, bool extraRef) + : surf(nullptr), + margins(0, 0), + fullSize(0, 0) { surf = from; if (extraRef) @@ -820,8 +826,42 @@ fullSize.y = surf->h; } -SDLImage::SDLImage(std::string filename, bool compressed): - margins(0,0) +SDLImage::SDLImage(const JsonNode & conf) + : surf(nullptr), + margins(0, 0), + fullSize(0, 0) +{ + std::string filename = conf["file"].String(); + + surf = BitmapHandler::loadBitmap(filename); + + if(surf == nullptr) + return; + + const JsonNode & jsonMargins = conf["margins"]; + + margins.x = jsonMargins["left"].Integer(); + margins.y = jsonMargins["top"].Integer(); + + fullSize.x = conf["width"].Integer(); + fullSize.y = conf["height"].Integer(); + + if(fullSize.x == 0) + { + fullSize.x = margins.x + surf->w + jsonMargins["right"].Integer(); + } + + if(fullSize.y == 0) + { + fullSize.y = margins.y + surf->h + jsonMargins["bottom"].Integer(); + } +} + + +SDLImage::SDLImage(std::string filename, bool compressed) + : surf(nullptr), + margins(0, 0), + fullSize(0, 0) { surf = BitmapHandler::loadBitmap(filename); @@ -851,9 +891,10 @@ } } + void SDLImage::draw(SDL_Surface *where, int posX, int posY, Rect *src, ui8 alpha) const { - if (!surf) + if(!surf) return; Rect destRect(posX, posY, surf->w, surf->h); @@ -996,7 +1037,6 @@ } } - SDLImage::~SDLImage() { SDL_FreeSurface(surf); @@ -1300,34 +1340,34 @@ return ret; } -bool CAnimation::loadFrame(CDefFile * file, size_t frame, size_t group) +bool CAnimation::loadFrame(size_t frame, size_t group) { - if (size(group) <= frame) + if(size(group) <= frame) { printError(frame, group, "LoadFrame"); return false; } - IImage *image = getImage(frame, group, false); - if (image) + IImage * image = getImage(frame, group, false); + if(image) { image->increaseRef(); return true; } //try to get image from def - if (source[group][frame].getType() == JsonNode::DATA_NULL) + if(source[group][frame].getType() == JsonNode::DATA_NULL) { - if (file) + if(defFile) { - auto frameList = file->getEntries(); + auto frameList = defFile->getEntries(); - if (vstd::contains(frameList, group) && frameList.at(group) > frame) // frame is present + if(vstd::contains(frameList, group) && frameList.at(group) > frame) // frame is present { - if (compressed) - images[group][frame] = new CompImage(file, frame, group); + if(compressed) + images[group][frame] = new CompImage(defFile, frame, group); else - images[group][frame] = new SDLImage(file, frame, group); + images[group][frame] = new SDLImage(defFile, frame, group); return true; } } @@ -1338,11 +1378,9 @@ } else //load from separate file { - std::string filename = source[group][frame]["file"].String(); - - IImage * img = getFromExtraDef(filename); - if (!img) - img = new SDLImage(filename, compressed); + IImage * img = getFromExtraDef(source[group][frame]["file"].String()); + if(!img) + img = new SDLImage(source[group][frame]); images[group][frame] = img; return true; @@ -1373,30 +1411,37 @@ std::string basepath; basepath = config["basepath"].String(); - for(const JsonNode &group : config["sequences"].Vector()) + JsonNode base(JsonNode::DATA_STRUCT); + base["margins"] = config["margins"]; + base["width"] = config["width"]; + base["height"] = config["height"]; + + for(const JsonNode & group : config["sequences"].Vector()) { - size_t groupID = group["group"].Float();//TODO: string-to-value conversion("moving" -> MOVING) + size_t groupID = group["group"].Integer();//TODO: string-to-value conversion("moving" -> MOVING) source[groupID].clear(); - for(const JsonNode &frame : group["frames"].Vector()) + for(const JsonNode & frame : group["frames"].Vector()) { - source[groupID].push_back(JsonNode()); - std::string filename = frame.String(); - source[groupID].back()["file"].String() = basepath + filename; + JsonNode toAdd(JsonNode::DATA_STRUCT); + JsonUtils::inherit(toAdd, base); + toAdd["file"].String() = basepath + frame["file"].String(); + source[groupID].push_back(toAdd); } } - for(const JsonNode &node : config["images"].Vector()) + for(const JsonNode & node : config["images"].Vector()) { - size_t group = node["group"].Float(); - size_t frame = node["frame"].Float(); + size_t group = node["group"].Integer(); + size_t frame = node["frame"].Integer(); if (source[group].size() <= frame) source[group].resize(frame+1); - source[group][frame] = node; - std::string filename = node["file"].String(); - source[group][frame]["file"].String() = basepath + filename; + JsonNode toAdd(JsonNode::DATA_STRUCT); + JsonUtils::inherit(toAdd, base); + toAdd["file"].String() = basepath + node["file"].String(); + source[group][frame] = toAdd; } } @@ -1433,11 +1478,11 @@ logGlobal->info("Exported %d frames to %s", counter, actualPath.string()); } -void CAnimation::init(CDefFile * file) +void CAnimation::init() { - if (file) + if(defFile) { - const std::map defEntries = file->getEntries(); + const std::map defEntries = defFile->getEntries(); for (auto & defEntry : defEntries) source[defEntry.first].resize(defEntry.second); @@ -1462,15 +1507,6 @@ } } -CDefFile * CAnimation::getFile() const -{ - ResourceID identifier(std::string("SPRITES/") + name, EResType::ANIMATION); - - if (CResourceHandler::get()->existsResource(identifier)) - return new CDefFile(name); - return nullptr; -} - void CAnimation::printError(size_t frame, size_t group, std::string type) const { logGlobal->error("%s error: Request for frame not present in CAnimation! File name: %s, Group: %d, Frame: %d", type, name, group, frame); @@ -1479,15 +1515,20 @@ CAnimation::CAnimation(std::string Name, bool Compressed): name(Name), compressed(Compressed), - preloaded(false) + preloaded(false), + defFile(nullptr) { size_t dotPos = name.find_last_of('.'); if ( dotPos!=-1 ) name.erase(dotPos); std::transform(name.begin(), name.end(), name.begin(), toupper); - CDefFile * file = getFile(); - init(file); - delete file; + + ResourceID resource(std::string("SPRITES/") + name, EResType::ANIMATION); + + if(CResourceHandler::get()->existsResource(resource)) + defFile = new CDefFile(name); + + init(); if(source.empty()) logAnim->error("Animation %s failed to load", Name); @@ -1496,9 +1537,10 @@ CAnimation::CAnimation(): name(""), compressed(false), - preloaded(false) + preloaded(false), + defFile(nullptr) { - init(nullptr); + init(); } CAnimation::~CAnimation() @@ -1558,13 +1600,9 @@ void CAnimation::load() { - CDefFile * file = getFile(); - for (auto & elem : source) for (size_t image=0; image < elem.second.size(); image++) - loadFrame(file, image, elem.first); - - delete file; + loadFrame(image, elem.first); } void CAnimation::unload() @@ -1586,13 +1624,9 @@ void CAnimation::loadGroup(size_t group) { - CDefFile * file = getFile(); - if (vstd::contains(source, group)) for (size_t image=0; image < source[group].size(); image++) - loadFrame(file, image, group); - - delete file; + loadFrame(image, group); } void CAnimation::unloadGroup(size_t group) @@ -1604,9 +1638,7 @@ void CAnimation::load(size_t frame, size_t group) { - CDefFile * file = getFile(); - loadFrame(file, frame, group); - delete file; + loadFrame(frame, group); } void CAnimation::unload(size_t frame, size_t group) diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/client/gui/CAnimation.h vcmi-0.99+git201710111318~ubuntu15.04.1/client/gui/CAnimation.h --- vcmi-0.99+git201710071442~ubuntu15.04.1/client/gui/CAnimation.h 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/client/gui/CAnimation.h 2017-10-11 14:18:44.000000000 +0000 @@ -78,18 +78,17 @@ bool preloaded; + CDefFile * defFile; + //loader, will be called by load(), require opened def file for loading from it. Returns true if image is loaded - bool loadFrame(CDefFile * file, size_t frame, size_t group); + bool loadFrame(size_t frame, size_t group); //unloadFrame, returns true if image has been unloaded ( either deleted or decreased refCount) bool unloadFrame(size_t frame, size_t group); //initialize animation from file void initFromJson(const JsonNode & input); - void init(CDefFile * file); - - //try to open def file - CDefFile * getFile() const; + void init(); //to get rid of copy-pasting error message :] void printError(size_t frame, size_t group, std::string type) const; @@ -99,7 +98,6 @@ IImage * getFromExtraDef(std::string filename); public: - CAnimation(std::string Name, bool Compressed = false); CAnimation(); ~CAnimation(); diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/client/gui/SDL_Extensions.h vcmi-0.99+git201710111318~ubuntu15.04.1/client/gui/SDL_Extensions.h --- vcmi-0.99+git201710071442~ubuntu15.04.1/client/gui/SDL_Extensions.h 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/client/gui/SDL_Extensions.h 2017-10-11 14:18:44.000000000 +0000 @@ -218,8 +218,10 @@ void stopTextInput(); void setColorKey(SDL_Surface * surface, SDL_Color color); + ///set key-color to 0,255,255 void setDefaultColorKey(SDL_Surface * surface); + ///set key-color to 0,255,255 only if it exactly mapped void setDefaultColorKeyPresize(SDL_Surface * surface); } diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/client/widgets/Images.h vcmi-0.99+git201710111318~ubuntu15.04.1/client/widgets/Images.h --- vcmi-0.99+git201710071442~ubuntu15.04.1/client/widgets/Images.h 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/client/widgets/Images.h 2017-10-11 14:18:44.000000000 +0000 @@ -181,7 +181,7 @@ HITTED=3, DEFENCE=4, DEATH=5, - //DEATH2=6, //unused? + DEATH_RANGED=6, TURN_L=7, TURN_R=8, //same //TURN_L2=9, //identical to previous? @@ -197,8 +197,16 @@ CAST_DOWN=19, MOVE_START=20, MOVE_END=21, - DEAD = 22 // new group, used to show dead stacks. If empty - last frame from "DEATH" will be copied here + DEAD = 22, // new group, used to show dead stacks. If empty - last frame from "DEATH" will be copied here + DEAD_RANGED = 23, // new group, used to show dead stacks (if DEATH_RANGED was used). If empty - last frame from "DEATH_RANGED" will be copied here + + VCMI_CAST_UP = 30, + VCMI_CAST_FRONT = 31, + VCMI_CAST_DOWN = 32, + VCMI_2HEX_UP = 40, + VCMI_2HEX_FRONT = 41, + VCMI_2HEX_DOWN = 42 }; private: diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/debian/changelog vcmi-0.99+git201710111318~ubuntu15.04.1/debian/changelog --- vcmi-0.99+git201710071442~ubuntu15.04.1/debian/changelog 2017-10-07 20:17:01.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/debian/changelog 2017-10-11 14:18:51.000000000 +0000 @@ -1,8 +1,8 @@ -vcmi (0.99+git201710071442~ubuntu15.04.1) vivid; urgency=low +vcmi (0.99+git201710111318~ubuntu15.04.1) vivid; urgency=low * Auto build. - -- Launchpad Package Builder Sat, 07 Oct 2017 20:17:01 +0000 + -- Launchpad Package Builder Wed, 11 Oct 2017 14:18:51 +0000 vcmi (0.99) trusty; urgency=medium diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/debian/git-build-recipe.manifest vcmi-0.99+git201710111318~ubuntu15.04.1/debian/git-build-recipe.manifest --- vcmi-0.99+git201710071442~ubuntu15.04.1/debian/git-build-recipe.manifest 2017-10-07 20:17:01.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/debian/git-build-recipe.manifest 2017-10-11 14:18:51.000000000 +0000 @@ -1,3 +1,3 @@ -# git-build-recipe format 0.4 deb-version {debupstream}+git201710071442 -lp:vcmi git-commit:9cc2464cf670fca4beed23d74366b28061a9341f +# git-build-recipe format 0.4 deb-version {debupstream}+git201710111318 +lp:vcmi git-commit:4149662845187addce5c87273930dcdbadd3e38e nest release lp:fuzzylite AI/FuzzyLite git-commit:8bac66fc528f9a98984103b21c8ed17681069941 diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/lib/NetPacks.h vcmi-0.99+git201710111318~ubuntu15.04.1/lib/NetPacks.h --- vcmi-0.99+git201710071442~ubuntu15.04.1/lib/NetPacks.h 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/lib/NetPacks.h 2017-10-11 14:18:44.000000000 +0000 @@ -1638,10 +1638,12 @@ manaGained = 0; casterStack = -1; castByHero = true; + activeCast = true; }; DLL_LINKAGE void applyGs(CGameState *gs); void applyCl(CClient *cl); + bool activeCast; ui8 side; //which hero did cast spell: 0 - attacker, 1 - defender ui32 id; //id of spell ui8 skill; //caster's skill level @@ -1665,6 +1667,7 @@ h & casterStack; h & castByHero; h & battleLog; + h & activeCast; } }; diff -Nru vcmi-0.99+git201710071442~ubuntu15.04.1/lib/spells/CDefaultSpellMechanics.cpp vcmi-0.99+git201710111318~ubuntu15.04.1/lib/spells/CDefaultSpellMechanics.cpp --- vcmi-0.99+git201710071442~ubuntu15.04.1/lib/spells/CDefaultSpellMechanics.cpp 2017-10-07 20:16:55.000000000 +0000 +++ vcmi-0.99+git201710111318~ubuntu15.04.1/lib/spells/CDefaultSpellMechanics.cpp 2017-10-11 14:18:44.000000000 +0000 @@ -199,6 +199,10 @@ sc.manaGained = (manaChannel * spellCost) / 100; } } + + sc.activeCast = parameters.mode == ECastingMode::HERO_CASTING || + parameters.mode == ECastingMode::CREATURE_ACTIVE_CASTING || + parameters.mode == ECastingMode::ENCHANTER_CASTING; } void SpellCastContext::afterCast()