1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-21 21:17:49 +02:00

vcmi: now obstacles can have disappearing anim

It is a reverse version of appearingAnimation.
This commit is contained in:
Konstantin 2023-03-26 19:57:21 +03:00
parent 7543fdf787
commit eff41f66ed
8 changed files with 108 additions and 13 deletions

@ -759,6 +759,7 @@ void CPlayerInterface::battleObstaclesChanged(const std::vector<ObstacleChanges>
BATTLE_EVENT_POSSIBLE_RETURN;
std::vector<std::shared_ptr<const CObstacleInstance>> newObstacles;
std::vector<ObstacleChanges> removedObstacles;
for(auto & change : obstacles)
{
@ -770,11 +771,16 @@ void CPlayerInterface::battleObstaclesChanged(const std::vector<ObstacleChanges>
else
logNetwork->error("Invalid obstacle instance %d", change.id);
}
if(change.operation == BattleChanges::EOperation::REMOVE)
removedObstacles.push_back(change); //Obstacles are already removed, so, show animation based on json struct
}
if (!newObstacles.empty())
battleInt->obstaclePlaced(newObstacles);
if (!removedObstacles.empty())
battleInt->obstacleRemoved(removedObstacles);
battleInt->fieldController->redrawBackgroundWithHexes();
}

@ -653,6 +653,11 @@ void BattleInterface::obstaclePlaced(const std::vector<std::shared_ptr<const COb
obstacleController->obstaclePlaced(oi);
}
void BattleInterface::obstacleRemoved(const std::vector<ObstacleChanges> & obstacles)
{
obstacleController->obstacleRemoved(obstacles);
}
const CGHeroInstance *BattleInterface::currentHero() const
{
if (attackingHeroInstance && attackingHeroInstance->tempOwner == curInt->playerID)

@ -29,6 +29,7 @@ struct CatapultAttack;
struct BattleTriggerEffect;
struct BattleHex;
struct InfoAboutHero;
class ObstacleChanges;
VCMI_LIB_NAMESPACE_END
@ -214,6 +215,7 @@ public:
void endAction(const BattleAction* action);
void obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> oi);
void obstacleRemoved(const std::vector<ObstacleChanges> & obstacles);
void gateStateChanged(const EGateState state);

@ -74,6 +74,37 @@ void BattleObstacleController::loadObstacleImage(const CObstacleInstance & oi)
obstacleAnimations[oi.uniqueID] = animationsCache[animationName];
}
void BattleObstacleController::obstacleRemoved(const std::vector<ObstacleChanges> & obstacles)
{
for (auto const & oi : obstacles)
{
auto & obstacle = oi.data["obstacle"];
if (!obstacle.isStruct())
{
logGlobal->error("I don't know how to animate removal of this obstacle");
continue;
}
auto animation = std::make_shared<CAnimation>(obstacle["appearAnimation"].String());
animation->preload();
auto first = animation->getImage(0, 0);
if(!first)
continue;
//we assume here that effect graphics have the same size as the usual obstacle image
// -> if we know how to blit obstacle, let's blit the effect in the same place
Point whereTo = getObstaclePosition(first, obstacle);
//AFAIK, in H3 there is no sound of obstacle removal
owner.stacksController->addNewAnim(new EffectAnimation(owner, obstacle["appearAnimation"].String(), whereTo, obstacle["position"].Integer(), 0, true));
obstacleAnimations.erase(oi.id);
//so when multiple obstacles are removed, they show up one after another
owner.waitForAnimations();
}
}
void BattleObstacleController::obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> & obstacles)
{
for (auto const & oi : obstacles)
@ -87,7 +118,7 @@ void BattleObstacleController::obstaclePlaced(const std::vector<std::shared_ptr<
if (!spellObstacle)
{
logGlobal->error("I don't know how to animate appearing obstacle of type %d", (int)oi->obstacleType);
logGlobal->error("I don't know how to animate removal of obstacle of type %d", (int)oi->obstacleType);
continue;
}
@ -180,3 +211,19 @@ Point BattleObstacleController::getObstaclePosition(std::shared_ptr<IImage> imag
return r.topLeft();
}
Point BattleObstacleController::getObstaclePosition(std::shared_ptr<IImage> image, const JsonNode & obstacle)
{
auto animationYOffset = obstacle["animationYOffset"].Integer();
auto offset = image->height() % 42;
if(obstacle["needAnimationOffsetFix"].Bool() && offset > 37)
animationYOffset -= 42;
offset += animationYOffset;
Rect r = owner.fieldController->hexPositionLocal(obstacle["position"].Integer());
r.y += 42 - image->height() + offset;
return r.topLeft();
}

@ -13,6 +13,8 @@ VCMI_LIB_NAMESPACE_BEGIN
struct BattleHex;
struct CObstacleInstance;
class JsonNode;
class ObstacleChanges;
class Point;
VCMI_LIB_NAMESPACE_END
@ -42,6 +44,7 @@ class BattleObstacleController
std::shared_ptr<IImage> getObstacleImage(const CObstacleInstance & oi);
Point getObstaclePosition(std::shared_ptr<IImage> image, const CObstacleInstance & obstacle);
Point getObstaclePosition(std::shared_ptr<IImage> image, const JsonNode & obstacle);
public:
BattleObstacleController(BattleInterface & owner);
@ -52,6 +55,9 @@ public:
/// call-in from network pack, add newly placed obstacles with any required animations
void obstaclePlaced(const std::vector<std::shared_ptr<const CObstacleInstance>> & oi);
/// call-in from network pack, remove required obstacles with any required animations
void obstacleRemoved(const std::vector<ObstacleChanges> & obstacles);
/// renders all "absolute" obstacles
void showAbsoluteObstacles(Canvas & canvas);

@ -86,6 +86,37 @@ bool CObstacleInstance::triggersEffects() const
return false;
}
void CObstacleInstance::serializeJson(JsonSerializeFormat & handler)
{
auto obstacleInfo = getInfo();
auto hidden = false;
auto needAnimationOffsetFix = obstacleType == CObstacleInstance::USUAL;
int animationYOffset = 0;
if(getInfo().blockedTiles.front() < 0) //TODO: holy ground ID=62,65,63
animationYOffset -= 42;
//We need only a subset of obstacle info for correct render
handler.serializeInt("position", pos);
handler.serializeString("appearSound", obstacleInfo.appearSound);
handler.serializeString("appearAnimation", obstacleInfo.appearAnimation);
handler.serializeString("animation", obstacleInfo.animation);
handler.serializeInt("animationYOffset", animationYOffset);
handler.serializeBool("hidden", hidden);
handler.serializeBool("needAnimationOffsetFix", needAnimationOffsetFix);
}
void CObstacleInstance::toInfo(ObstacleChanges & info, BattleChanges::EOperation operation)
{
info.id = uniqueID;
info.operation = operation;
info.data.clear();
JsonSerializer ser(nullptr, info.data);
ser.serializeStruct("obstacle", *this);
}
SpellCreatedObstacle::SpellCreatedObstacle()
: turnsRemaining(-1),
casterSpellPower(0),
@ -131,16 +162,6 @@ bool SpellCreatedObstacle::triggersEffects() const
return trigger;
}
void SpellCreatedObstacle::toInfo(ObstacleChanges & info)
{
info.id = uniqueID;
info.operation = ObstacleChanges::EOperation::ADD;
info.data.clear();
JsonSerializer ser(nullptr, info.data);
ser.serializeStruct("obstacle", *this);
}
void SpellCreatedObstacle::fromInfo(const ObstacleChanges & info)
{
uniqueID = info.id;

@ -9,6 +9,7 @@
*/
#pragma once
#include "BattleHex.h"
#include "NetPacksBase.h"
VCMI_LIB_NAMESPACE_BEGIN
@ -49,6 +50,10 @@ struct DLL_LINKAGE CObstacleInstance
virtual int getAnimationYOffset(int imageHeight) const;
void toInfo(ObstacleChanges & info, BattleChanges::EOperation operation = BattleChanges::EOperation::ADD);
virtual void serializeJson(JsonSerializeFormat & handler);
template <typename Handler> void serialize(Handler &h, const int version)
{
h & ID;
@ -96,10 +101,9 @@ struct DLL_LINKAGE SpellCreatedObstacle : CObstacleInstance
int getAnimationYOffset(int imageHeight) const override;
void toInfo(ObstacleChanges & info);
void fromInfo(const ObstacleChanges & info);
void serializeJson(JsonSerializeFormat & handler);
void serializeJson(JsonSerializeFormat & handler) override;
template <typename Handler> void serialize(Handler &h, const int version)
{

@ -50,7 +50,11 @@ void RemoveObstacle::apply(ServerCallback * server, const Mechanics * m, const E
BattleObstaclesChanged pack;
for(const auto & obstacle : getTargets(m, target, false))
{
auto * serializable = const_cast<CObstacleInstance*>(obstacle); //Workaround
pack.changes.emplace_back(obstacle->uniqueID, BattleChanges::EOperation::REMOVE);
serializable->toInfo(pack.changes.back(), BattleChanges::EOperation::REMOVE);
}
if(!pack.changes.empty())
server->apply(&pack);