1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Support for creature 2-hex attack, alternate death and cast animation types

* margin option .json animation
* Use ranged attack animation for spell cast if there is no cast animation, display cast animation only on active casting.
This commit is contained in:
AlexVinS 2017-09-08 14:25:12 +03:00
parent 494872937a
commit 270e1b75ce
13 changed files with 390 additions and 127 deletions

View File

@ -178,7 +178,7 @@ SDL_Surface * BitmapHandler::loadBitmapFromDir(std::string path, std::string fna
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)))

View File

@ -241,7 +241,12 @@ std::string CDefenceAnimation::getMySound()
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 +277,10 @@ void CDefenceAnimation::endAnim()
{
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 +320,25 @@ void CDummyAnimation::endAnim()
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 +346,25 @@ bool CMeleeAttackAnimation::init()
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 +385,20 @@ bool CMeleeAttackAnimation::init()
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 +708,16 @@ void CReverseAnimation::setupSecondPart()
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 +879,9 @@ bool CShootingAnimation::init()
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 +904,149 @@ void CShootingAnimation::nextFrame()
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<CReverseAnimation *>(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),

View File

@ -197,8 +197,16 @@ struct ProjectileInfo
std::shared_ptr<CatapultProjectileInfo> 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 @@ public:
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
{

View File

@ -1266,7 +1266,7 @@ void CBattleInterface::displayBattleFinished()
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 +1276,32 @@ void CBattleInterface::spellCast(const BattleSpellCast *sc)
if (!castSoundPath.empty())
CCS->soundh->playSound(castSoundPath);
const auto casterStackID = sc->casterStack;
const CStack * casterStack = nullptr;
if(casterStackID >= 0)
{
casterStack = curInt->cb->battleGetStackByID(casterStackID);
}
Point srccoord = (sc->side ? Point(770, 60) : Point(30, 60)) + pos; //hero position by default
{
const auto casterStackID = sc->casterStack;
if (casterStackID > 0)
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 +1336,8 @@ void CBattleInterface::spellCast(const BattleSpellCast *sc)
addNewAnim(new CEffectAnimation(this, animToDisplay, srccoord.x, srccoord.y, dx, dy, Vflip));
}
}
waitForAnims();
waitForAnims(); //wait for projectile animation
displaySpellHit(spellID, sc->tile);

View File

@ -388,5 +388,6 @@ public:
friend class CAttackAnimation;
friend class CMeleeAttackAnimation;
friend class CShootingAnimation;
friend class CCastAnimation;
friend class CClickableHex;
};

View File

@ -72,6 +72,9 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
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 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
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 @@ float AnimationControls::getCreatureAnimationSpeed(const CCreature * creature, c
return speed / 3;
case CCreatureAnim::DEAD:
case CCreatureAnim::DEAD_RANGED:
return speed;
default:
assert(0);
return 1;
return speed;
}
}
@ -128,9 +135,6 @@ CCreatureAnim::EAnimType CCreatureAnimation::getType() const
void CCreatureAnimation::setType(CCreatureAnim::EAnimType type)
{
assert(type >= 0);
assert(framesInGroup(type) != 0);
this->type = type;
currentFrame = 0;
once = false;
@ -162,6 +166,12 @@ CCreatureAnimation::CCreatureAnimation(const std::string & name_, TSpeedControll
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 @@ bool CCreatureAnimation::incrementFrame(float timePassed)
{
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 @@ void CCreatureAnimation::genBorderPalette(IImage::BorderPallete & target)
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 @@ void CCreatureAnimation::nextFrame(SDL_Surface *dest, bool attacker)
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 @@ int CCreatureAnimation::framesInGroup(CCreatureAnim::EAnimType group) const
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

View File

@ -108,7 +108,7 @@ public:
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();

View File

@ -82,6 +82,8 @@ public:
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 @@ void IImage::increaseRef()
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 @@ SDLImage::SDLImage(SDL_Surface * from, bool extraRef):
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 @@ SDLImage::SDLImage(std::string filename, bool compressed):
}
}
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 @@ void SDLImage::setBorderPallete(const IImage::BorderPallete & borderPallete)
}
}
SDLImage::~SDLImage()
{
SDL_FreeSurface(surf);
@ -1300,34 +1340,34 @@ IImage * CAnimation::getFromExtraDef(std::string filename)
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 @@ bool CAnimation::loadFrame(CDefFile * file, size_t frame, size_t group)
}
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 @@ void CAnimation::initFromJson(const JsonNode & config)
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 @@ void CAnimation::exportBitmaps(const boost::filesystem::path& path) const
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<size_t, size_t> defEntries = file->getEntries();
const std::map<size_t, size_t> defEntries = defFile->getEntries();
for (auto & defEntry : defEntries)
source[defEntry.first].resize(defEntry.second);
@ -1462,15 +1507,6 @@ void CAnimation::init(CDefFile * file)
}
}
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 @@ void CAnimation::printError(size_t frame, size_t group, std::string type) const
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(std::string Name, bool Compressed):
CAnimation::CAnimation():
name(""),
compressed(false),
preloaded(false)
preloaded(false),
defFile(nullptr)
{
init(nullptr);
init();
}
CAnimation::~CAnimation()
@ -1558,13 +1600,9 @@ IImage * CAnimation::getImage(size_t frame, size_t group, bool verbose) const
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::preload()
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::unloadGroup(size_t group)
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)

View File

@ -78,18 +78,17 @@ private:
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 @@ private:
IImage * getFromExtraDef(std::string filename);
public:
CAnimation(std::string Name, bool Compressed = false);
CAnimation();
~CAnimation();

View File

@ -218,8 +218,10 @@ namespace CSDL_Ext
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);
}

View File

@ -181,7 +181,7 @@ public:
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 @@ public:
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:

View File

@ -1636,10 +1636,12 @@ struct BattleSpellCast : public CPackForClient
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
@ -1663,6 +1665,7 @@ struct BattleSpellCast : public CPackForClient
h & casterStack;
h & castByHero;
h & battleLog;
h & activeCast;
}
};

View File

@ -199,6 +199,10 @@ void SpellCastContext::beforeCast()
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()