1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00

Implemented basic hero movement animation & object fade-out

This commit is contained in:
Ivan Savenko 2023-02-18 17:37:09 +02:00
parent 4501036a04
commit 7cf00ba87d
15 changed files with 491 additions and 509 deletions

View File

@ -53,7 +53,6 @@ set(client_SRCS
render/CAnimation.cpp render/CAnimation.cpp
render/CBitmapHandler.cpp render/CBitmapHandler.cpp
render/CDefFile.cpp render/CDefFile.cpp
render/CFadeAnimation.cpp
render/Canvas.cpp render/Canvas.cpp
render/ColorFilter.cpp render/ColorFilter.cpp
render/Colors.cpp render/Colors.cpp
@ -169,7 +168,6 @@ set(client_HEADERS
render/CAnimation.h render/CAnimation.h
render/CBitmapHandler.h render/CBitmapHandler.h
render/CDefFile.h render/CDefFile.h
render/CFadeAnimation.h
render/Canvas.h render/Canvas.h
render/ColorFilter.h render/ColorFilter.h
render/Colors.h render/Colors.h

View File

@ -457,22 +457,10 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
return; return;
} }
adventureInt->centerOn(hero); //actualizing screen pos
adventureInt->minimap->redraw(); adventureInt->minimap->redraw();
adventureInt->heroList->redraw(); adventureInt->heroList->redraw();
auto waitFrame = [&]() CGI->mh->waitForOngoingAnimations();
{
int frameNumber = GH.mainFPSmng->getFrameNumber();
auto unlockPim = vstd::makeUnlockGuard(*pim);
while(frameNumber == GH.mainFPSmng->getFrameNumber())
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
};
//main moving
while (CGI->mh->hasActiveAnimations())
waitFrame(); //for animation purposes
//finishing move //finishing move
hero->isStanding = true; hero->isStanding = true;

View File

@ -344,6 +344,8 @@ void ApplyFirstClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack)
CGObjectInstance *obj = gs.getObjInstance(pack.objid); CGObjectInstance *obj = gs.getObjInstance(pack.objid);
if(CGI->mh) if(CGI->mh)
CGI->mh->onObjectFadeOut(obj); CGI->mh->onObjectFadeOut(obj);
CGI->mh->waitForOngoingAnimations();
} }
void ApplyClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack) void ApplyClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack)
{ {
@ -351,6 +353,7 @@ void ApplyClientNetPackVisitor::visitChangeObjPos(ChangeObjPos & pack)
if(CGI->mh) if(CGI->mh)
CGI->mh->onObjectFadeIn(obj); CGI->mh->onObjectFadeIn(obj);
CGI->mh->waitForOngoingAnimations();
cl.invalidatePaths(); cl.invalidatePaths();
} }
@ -426,6 +429,8 @@ void ApplyFirstClientNetPackVisitor::visitRemoveObject(RemoveObject & pack)
if(gs.isVisible(o, i->first) || (!cl.getPlayerState(i->first)->human && o->ID == Obj::HERO && o->tempOwner != i->first)) if(gs.isVisible(o, i->first) || (!cl.getPlayerState(i->first)->human && o->ID == Obj::HERO && o->tempOwner != i->first))
i->second->objectRemoved(o); i->second->objectRemoved(o);
} }
CGI->mh->waitForOngoingAnimations();
} }
void ApplyClientNetPackVisitor::visitRemoveObject(RemoveObject & pack) void ApplyClientNetPackVisitor::visitRemoveObject(RemoveObject & pack)
@ -938,7 +943,6 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
callInterfaceIfPresent(cl, obj1->tempOwner, &IGameEventsReceiver::showTavernWindow, obj2); callInterfaceIfPresent(cl, obj1->tempOwner, &IGameEventsReceiver::showTavernWindow, obj2);
break; break;
} }
} }
void ApplyClientNetPackVisitor::visitCenterView(CenterView & pack) void ApplyClientNetPackVisitor::visitCenterView(CenterView & pack)
@ -959,6 +963,7 @@ void ApplyClientNetPackVisitor::visitNewObject(NewObject & pack)
if(gs.isVisible(obj, i->first)) if(gs.isVisible(obj, i->first))
i->second->newObject(obj); i->second->newObject(obj);
} }
CGI->mh->waitForOngoingAnimations();
} }
void ApplyClientNetPackVisitor::visitSetAvailableArtifacts(SetAvailableArtifacts & pack) void ApplyClientNetPackVisitor::visitSetAvailableArtifacts(SetAvailableArtifacts & pack)

View File

@ -319,7 +319,7 @@ void CAdvMapInt::fsleepWake()
void CAdvMapInt::fmoveHero() void CAdvMapInt::fmoveHero()
{ {
const CGHeroInstance *h = curHero(); const CGHeroInstance *h = curHero();
if (!h || !LOCPLINT->paths.hasPath(h) || CGI->mh->hasActiveAnimations()) if (!h || !LOCPLINT->paths.hasPath(h) || CGI->mh->hasOngoingAnimations())
return; return;
LOCPLINT->moveHero(h, LOCPLINT->paths.getPath(h)); LOCPLINT->moveHero(h, LOCPLINT->paths.getPath(h));
@ -833,7 +833,7 @@ void CAdvMapInt::keyPressed(const SDL_Keycode & key)
if(!h || !isActive()) if(!h || !isActive())
return; return;
if (CGI->mh->hasActiveAnimations()) if (CGI->mh->hasOngoingAnimations())
return; return;
if(*direction == Point(0,0)) if(*direction == Point(0,0))
@ -1138,7 +1138,7 @@ void CAdvMapInt::tileLClicked(const int3 &mapPos)
if(LOCPLINT->paths.hasPath(currentHero) && if(LOCPLINT->paths.hasPath(currentHero) &&
LOCPLINT->paths.getPath(currentHero).endPos() == mapPos)//we'll be moving LOCPLINT->paths.getPath(currentHero).endPos() == mapPos)//we'll be moving
{ {
if(!CGI->mh->hasActiveAnimations()) if(!CGI->mh->hasOngoingAnimations())
LOCPLINT->moveHero(currentHero, LOCPLINT->paths.getPath(currentHero)); LOCPLINT->moveHero(currentHero, LOCPLINT->paths.getPath(currentHero));
return; return;
} }

View File

@ -19,7 +19,6 @@
#include "../gui/CursorHandler.h" #include "../gui/CursorHandler.h"
#include "../gui/CGuiHandler.h" #include "../gui/CGuiHandler.h"
#include "../render/CAnimation.h" #include "../render/CAnimation.h"
#include "../render/CFadeAnimation.h"
#include "../render/IImage.h" #include "../render/IImage.h"
#include "../renderSDL/SDL_Extensions.h" #include "../renderSDL/SDL_Extensions.h"
#include "../widgets/TextControls.h" #include "../widgets/TextControls.h"
@ -35,9 +34,7 @@
#define ADVOPT (conf.go()->ac) #define ADVOPT (conf.go()->ac)
CTerrainRect::CTerrainRect() CTerrainRect::CTerrainRect()
: fadeSurface(nullptr) : curHoveredTile(-1, -1, -1)
, fadeAnim(std::make_shared<CFadeAnimation>())
, curHoveredTile(-1, -1, -1)
, isSwiping(false) , isSwiping(false)
{ {
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE; OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
@ -51,20 +48,14 @@ CTerrainRect::CTerrainRect()
renderer = std::make_shared<MapView>( Point(0,0), pos.dimensions() ); renderer = std::make_shared<MapView>( Point(0,0), pos.dimensions() );
} }
CTerrainRect::~CTerrainRect()
{
if(fadeSurface)
SDL_FreeSurface(fadeSurface);
}
void CTerrainRect::setViewCenter(const int3 &coordinates) void CTerrainRect::setViewCenter(const int3 &coordinates)
{ {
renderer->setViewCenter(coordinates); renderer->getController()->setViewCenter(coordinates);
} }
void CTerrainRect::setViewCenter(const Point & position, int level) void CTerrainRect::setViewCenter(const Point & position, int level)
{ {
renderer->setViewCenter(position, level); renderer->getController()->setViewCenter(position, level);
} }
void CTerrainRect::deactivate() void CTerrainRect::deactivate()
@ -220,30 +211,17 @@ Rect CTerrainRect::visibleTilesArea()
void CTerrainRect::fadeFromCurrentView() void CTerrainRect::fadeFromCurrentView()
{ {
if (!ADVOPT.screenFading) assert(0);//TODO
return;
if (adventureInt->mode == EAdvMapMode::WORLD_VIEW)
return;
if (!fadeSurface)
fadeSurface = CSDL_Ext::newSurface(pos.w, pos.h);
CSDL_Ext::blitSurface(screen, fadeSurface, Point(0,0));
fadeAnim->init(CFadeAnimation::EMode::OUT, fadeSurface);
} }
void CTerrainRect::setLevel(int level) void CTerrainRect::setLevel(int level)
{ {
renderer->setViewCenter(renderer->getModel()->getMapViewCenter(), level); renderer->getController()->setViewCenter(renderer->getModel()->getMapViewCenter(), level);
} }
void CTerrainRect::moveViewBy(const Point & delta) void CTerrainRect::moveViewBy(const Point & delta)
{ {
renderer->setViewCenter(renderer->getModel()->getMapViewCenter() + delta, getLevel()); renderer->getController()->setViewCenter(renderer->getModel()->getMapViewCenter() + delta, getLevel());
}
int3 CTerrainRect::getTileCenter()
{
return renderer->getModel()->getTileCenter();
} }
Point CTerrainRect::getViewCenter() Point CTerrainRect::getViewCenter()
@ -258,5 +236,5 @@ int CTerrainRect::getLevel()
void CTerrainRect::setTileSize(int sizePixels) void CTerrainRect::setTileSize(int sizePixels)
{ {
renderer->setTileSize(Point(sizePixels, sizePixels)); renderer->getController()->setTileSize(Point(sizePixels, sizePixels));
} }

View File

@ -16,7 +16,6 @@ VCMI_LIB_NAMESPACE_BEGIN
struct CGPath; struct CGPath;
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END
class CFadeAnimation;
class MapView; class MapView;
/// Holds information about which tiles of the terrain are shown/not shown at the screen /// Holds information about which tiles of the terrain are shown/not shown at the screen
@ -24,9 +23,6 @@ class CTerrainRect : public CIntObject
{ {
std::shared_ptr<MapView> renderer; std::shared_ptr<MapView> renderer;
SDL_Surface * fadeSurface;
std::shared_ptr<CFadeAnimation> fadeAnim;
Point swipeInitialViewPos; Point swipeInitialViewPos;
Point swipeInitialRealPos; Point swipeInitialRealPos;
bool isSwiping; bool isSwiping;
@ -48,7 +44,6 @@ class CTerrainRect : public CIntObject
public: public:
CTerrainRect(); CTerrainRect();
~CTerrainRect();
void moveViewBy(const Point & delta); void moveViewBy(const Point & delta);
void setViewCenter(const int3 & coordinates); void setViewCenter(const int3 & coordinates);
@ -56,7 +51,6 @@ public:
void setLevel(int level); void setLevel(int level);
void setTileSize(int sizePixels); void setTileSize(int sizePixels);
int3 getTileCenter();
Point getViewCenter(); Point getViewCenter();
int getLevel(); int getLevel();

View File

@ -182,8 +182,8 @@ void MapRendererRiver::renderTile(const IMapRendererContext & context, Canvas &
} }
} }
MapRendererRoad::MapRendererRoad(): MapRendererRoad::MapRendererRoad()
storage(VLC->roadTypeHandler->objects.size()) : storage(VLC->roadTypeHandler->objects.size())
{ {
for(const auto & road : VLC->roadTypeHandler->objects) for(const auto & road : VLC->roadTypeHandler->objects)
storage.load(road->getIndex(), road->tilesFilename); storage.load(road->getIndex(), road->tilesFilename);
@ -191,19 +191,19 @@ MapRendererRoad::MapRendererRoad():
void MapRendererRoad::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates) void MapRendererRoad::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
{ {
const int3 coordinatesAbove = coordinates - int3(0,1,0); const int3 coordinatesAbove = coordinates - int3(0, 1, 0);
if (context.isInMap(coordinatesAbove)) if(context.isInMap(coordinatesAbove))
{ {
const TerrainTile & mapTileAbove = context.getMapTile(coordinatesAbove); const TerrainTile & mapTileAbove = context.getMapTile(coordinatesAbove);
if (mapTileAbove.roadType->getId() != Road::NO_ROAD) if(mapTileAbove.roadType->getId() != Road::NO_ROAD)
{ {
int32_t terrainIndex = mapTileAbove.roadType->getIndex(); int32_t terrainIndex = mapTileAbove.roadType->getIndex();
int32_t imageIndex = mapTileAbove.roadDir; int32_t imageIndex = mapTileAbove.roadDir;
int32_t rotationIndex = (mapTileAbove.extTileFlags >> 4) % 4; int32_t rotationIndex = (mapTileAbove.extTileFlags >> 4) % 4;
const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex); const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
target.draw(image, Point(0,0), Rect(0, 16, 32, 16)); target.draw(image, Point(0, 0), Rect(0, 16, 32, 16));
} }
} }
@ -215,7 +215,7 @@ void MapRendererRoad::renderTile(const IMapRendererContext & context, Canvas & t
int32_t rotationIndex = (mapTile.extTileFlags >> 4) % 4; int32_t rotationIndex = (mapTile.extTileFlags >> 4) % 4;
const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex); const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
target.draw(image, Point(0,16), Rect(0, 0, 32, 16)); target.draw(image, Point(0, 16), Rect(0, 0, 32, 16));
} }
} }
@ -231,31 +231,31 @@ size_t MapRendererBorder::getIndexForTile(const IMapRendererContext & context, c
int3 size = context.getMapSize(); int3 size = context.getMapSize();
if (tile.x < -1 || tile.x > size.x || tile.y < -1 || tile.y > size.y) if(tile.x < -1 || tile.x > size.x || tile.y < -1 || tile.y > size.y)
return std::abs(tile.x) % 4 + 4*(std::abs(tile.y) % 4); return std::abs(tile.x) % 4 + 4 * (std::abs(tile.y) % 4);
if (tile.x == -1 && tile.y == -1) if(tile.x == -1 && tile.y == -1)
return 16; return 16;
if (tile.x == size.x && tile.y == -1) if(tile.x == size.x && tile.y == -1)
return 17; return 17;
if (tile.x == size.x && tile.y == size.y) if(tile.x == size.x && tile.y == size.y)
return 18; return 18;
if (tile.x == -1 && tile.y == size.y) if(tile.x == -1 && tile.y == size.y)
return 19; return 19;
if (tile.y == -1) if(tile.y == -1)
return 20 + (tile.x % 4); return 20 + (tile.x % 4);
if (tile.x == size.x) if(tile.x == size.x)
return 24 + (tile.y % 4); return 24 + (tile.y % 4);
if (tile.y == size.y) if(tile.y == size.y)
return 28 + (tile.x % 4); return 28 + (tile.x % 4);
if (tile.x == -1) if(tile.x == -1)
return 32 + (tile.y % 4); return 32 + (tile.y % 4);
//else - visible area, no renderable border //else - visible area, no renderable border
@ -266,7 +266,7 @@ size_t MapRendererBorder::getIndexForTile(const IMapRendererContext & context, c
void MapRendererBorder::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates) void MapRendererBorder::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
{ {
const auto & image = animation->getImage(getIndexForTile(context, coordinates)); const auto & image = animation->getImage(getIndexForTile(context, coordinates));
target.draw(image, Point(0,0)); target.draw(image, Point(0, 0));
} }
MapRendererFow::MapRendererFow() MapRendererFow::MapRendererFow()
@ -278,7 +278,7 @@ MapRendererFow::MapRendererFow()
static const std::vector<int> rotations = {22, 15, 2, 13, 12, 16, 28, 17, 20, 19, 7, 24, 26, 25, 30, 32, 27}; static const std::vector<int> rotations = {22, 15, 2, 13, 12, 16, 28, 17, 20, 19, 7, 24, 26, 25, 30, 32, 27};
size_t size = fogOfWarPartialHide->size(0);//group size after next rotation size_t size = fogOfWarPartialHide->size(0); //group size after next rotation
for(const int rotation : rotations) for(const int rotation : rotations)
{ {
@ -295,8 +295,8 @@ void MapRendererFow::renderTile(const IMapRendererContext & context, Canvas & ta
const NeighborTilesInfo neighborInfo(context, coordinates); const NeighborTilesInfo neighborInfo(context, coordinates);
int retBitmapID = neighborInfo.getBitmapID();// >=0 -> partial hide, <0 - full hide int retBitmapID = neighborInfo.getBitmapID(); // >=0 -> partial hide, <0 - full hide
if (retBitmapID < 0) if(retBitmapID < 0)
{ {
// generate a number that is predefined for each tile, // generate a number that is predefined for each tile,
// but appears random to break visible pattern in large areas of fow // but appears random to break visible pattern in large areas of fow
@ -304,15 +304,15 @@ void MapRendererFow::renderTile(const IMapRendererContext & context, Canvas & ta
size_t pseudorandomNumber = ((coordinates.x * 997) ^ (coordinates.y * 1009)) / 101; size_t pseudorandomNumber = ((coordinates.x * 997) ^ (coordinates.y * 1009)) / 101;
size_t imageIndex = pseudorandomNumber % fogOfWarFullHide->size(); size_t imageIndex = pseudorandomNumber % fogOfWarFullHide->size();
target.draw(fogOfWarFullHide->getImage(imageIndex), Point(0,0)); target.draw(fogOfWarFullHide->getImage(imageIndex), Point(0, 0));
} }
else else
{ {
target.draw(fogOfWarPartialHide->getImage(retBitmapID), Point(0,0)); target.draw(fogOfWarPartialHide->getImage(retBitmapID), Point(0, 0));
} }
} }
std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const CGObjectInstance* obj) std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const CGObjectInstance * obj)
{ {
const auto & info = obj->appearance; const auto & info = obj->appearance;
@ -333,36 +333,26 @@ std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const CGObjectInsta
std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const std::string & filename, bool generateMovementGroups) std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const std::string & filename, bool generateMovementGroups)
{ {
if (animations.count(filename)) if(animations.count(filename))
return animations[filename]; return animations[filename];
auto ret = std::make_shared<CAnimation>(filename); auto ret = std::make_shared<CAnimation>(filename);
animations[filename] = ret; animations[filename] = ret;
ret->preload(); ret->preload();
if (generateMovementGroups) if(generateMovementGroups)
{ {
ret->createFlippedGroup(1,13); ret->createFlippedGroup(1, 13);
ret->createFlippedGroup(2,14); ret->createFlippedGroup(2, 14);
ret->createFlippedGroup(3,15); ret->createFlippedGroup(3, 15);
ret->createFlippedGroup(6,10); ret->createFlippedGroup(6, 10);
ret->createFlippedGroup(7,11); ret->createFlippedGroup(7, 11);
ret->createFlippedGroup(8,12); ret->createFlippedGroup(8, 12);
} }
return ret; return ret;
} }
MapRendererObjects::MapRendererObjects(const IMapRendererContext & context)
{
auto mapSize = context.getMapSize();
objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
for(const auto & obj : context.getAllObjects())
onObjectInstantAdd(context, obj);
}
std::shared_ptr<CAnimation> MapRendererObjects::getFlagAnimation(const CGObjectInstance* obj) std::shared_ptr<CAnimation> MapRendererObjects::getFlagAnimation(const CGObjectInstance* obj)
{ {
//TODO: relocate to config file? //TODO: relocate to config file?
@ -401,7 +391,7 @@ std::shared_ptr<IImage> MapRendererObjects::getImage(const IMapRendererContext &
if(!animation) if(!animation)
return nullptr; return nullptr;
size_t groupIndex = getAnimationGroup(context, obj); size_t groupIndex = context.objectGroupIndex(obj->id);
if(animation->size(groupIndex) == 0) if(animation->size(groupIndex) == 0)
return nullptr; return nullptr;
@ -412,52 +402,37 @@ std::shared_ptr<IImage> MapRendererObjects::getImage(const IMapRendererContext &
return animation->getImage(frameIndex, groupIndex); return animation->getImage(frameIndex, groupIndex);
} }
size_t MapRendererObjects::getAnimationGroup(const IMapRendererContext & context, const CGObjectInstance * obj) const void MapRendererObjects::renderImage(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr<IImage>& image)
{
// TODO
//static const std::vector<size_t> moveGroups = {99, 10, 5, 6, 7, 8, 9, 12, 11};
static const std::vector<size_t> idleGroups = {99, 13, 0, 1, 2, 3, 4, 15, 14};
if(obj->ID == Obj::HERO)
{
const auto * hero = dynamic_cast<const CGHeroInstance *>(obj);
return idleGroups[hero->moveDir];
}
if(obj->ID == Obj::BOAT)
{
const auto * boat = dynamic_cast<const CGBoat *>(obj);
return idleGroups[boat->direction];
}
return 0;
}
void MapRendererObjects::renderImage(Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr<IImage>& image)
{ {
if(!image) if(!image)
return; return;
uint8_t transparency = static_cast<uint8_t>(std::round(255 * context.objectTransparency(object->id)));
if (transparency == 0)
return;
image->setAlpha(transparency);
image->setFlagColor(object->tempOwner); image->setFlagColor(object->tempOwner);
int3 offsetTiles(object->getPosition() - coordinates); Point offsetPixels = context.objectImageOffset(object->id, coordinates);
Point offsetPixels(offsetTiles.x * 32, offsetTiles.y * 32); if ( offsetPixels.x < image->dimensions().x && offsetPixels.y < image->dimensions().y)
Point imagePos = image->dimensions() - offsetPixels - Point(32, 32); {
Point tileDimensions(32, 32); Point imagePos = image->dimensions() - offsetPixels - Point(32, 32);
target.draw(image, Point(0, 0), Rect(imagePos, Point(32,32)));
target.draw(image, Point(0, 0), Rect(imagePos, tileDimensions)); }
} }
void MapRendererObjects::renderObject(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance* instance) void MapRendererObjects::renderObject(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance* instance)
{ {
renderImage(target, coordinates, instance, getImage(context, instance, getAnimation(instance))); renderImage(context, target, coordinates, instance, getImage(context, instance, getAnimation(instance)));
renderImage(target, coordinates, instance, getImage(context, instance, getFlagAnimation(instance))); renderImage(context, target, coordinates, instance, getImage(context, instance, getFlagAnimation(instance)));
} }
void MapRendererObjects::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates) void MapRendererObjects::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
{ {
for(const auto & objectID : objects[coordinates.z][coordinates.x][coordinates.y]) for(const auto & objectID : context.getObjects(coordinates))
{ {
const auto * objectInstance = context.getObject(objectID); const auto * objectInstance = context.getObject(objectID);
@ -472,61 +447,6 @@ void MapRendererObjects::renderTile(const IMapRendererContext & context, Canvas
} }
} }
void MapRendererObjects::onObjectInstantAdd(const IMapRendererContext & context, const CGObjectInstance * obj)
{
if(!obj)
return;
if(obj->ID == Obj::HERO && dynamic_cast<const CGHeroInstance *>(obj)->inTownGarrison)
return;
if(obj->ID == Obj::BOAT && dynamic_cast<const CGBoat *>(obj)->hero)
return;
std::shared_ptr<CAnimation> animation = getAnimation(obj);
//no animation at all, e.g. Event
if(!animation)
return;
//empty animation. Illegal?
assert(animation->size(0) > 0);
if(animation->size(0) == 0)
return;
auto image = animation->getImage(0, 0);
int imageWidthTiles = (image->width() + 31) / 32;
int imageHeightTiles = (image->height() + 31) / 32;
int objectWidth = std::min(obj->getWidth(), imageWidthTiles);
int objectHeight = std::min(obj->getHeight(), imageHeightTiles);
for(int fx = 0; fx < objectWidth; ++fx)
{
for(int fy = 0; fy < objectHeight; ++fy)
{
int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);
if(context.isInMap(currTile) && obj->coveringAt(currTile.x, currTile.y))
{
auto & container = objects[currTile.z][currTile.x][currTile.y];
container.push_back(obj->id);
boost::range::sort(container, MapObjectsSorter(context));
}
}
}
}
void MapRendererObjects::onObjectInstantRemove(const IMapRendererContext & context, const CGObjectInstance * object)
{
for(int z = 0; z < context.getMapSize().z; z++)
for(int x = 0; x < context.getMapSize().x; x++)
for(int y = 0; y < context.getMapSize().y; y++)
vstd::erase(objects[z][x][y], object->id);
}
void MapRendererDebugGrid::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates) void MapRendererDebugGrid::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
{ {
if(context.showGrid()) if(context.showGrid())
@ -625,14 +545,9 @@ void MapRendererPath::renderTile(const IMapRendererContext & context, Canvas & t
renderImageCross(target, reachableToday, iter->coord); renderImageCross(target, reachableToday, iter->coord);
} }
MapRenderer::MapRenderer(const IMapRendererContext & context)
: rendererObjects(context)
{
}
void MapRenderer::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates) void MapRenderer::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
{ {
if (!context.isInMap(coordinates)) if(!context.isInMap(coordinates))
{ {
rendererBorder.renderTile(context, target, coordinates); rendererBorder.renderTile(context, target, coordinates);
return; return;
@ -640,7 +555,7 @@ void MapRenderer::renderTile(const IMapRendererContext & context, Canvas & targe
const NeighborTilesInfo neighborInfo(context, coordinates); const NeighborTilesInfo neighborInfo(context, coordinates);
if (neighborInfo.areAllHidden()) if(neighborInfo.areAllHidden())
{ {
rendererFow.renderTile(context, target, coordinates); rendererFow.renderTile(context, target, coordinates);
} }
@ -652,8 +567,8 @@ void MapRenderer::renderTile(const IMapRendererContext & context, Canvas & targe
rendererObjects.renderTile(context, target, coordinates); rendererObjects.renderTile(context, target, coordinates);
rendererPath.renderTile(context, target, coordinates); rendererPath.renderTile(context, target, coordinates);
if (!context.isVisible(coordinates)) if(!context.isVisible(coordinates))
rendererFow.renderTile(context, target, coordinates); rendererFow.renderTile(context, target, coordinates);
} }
rendererDebugGrid.renderTile(context, target,coordinates); rendererDebugGrid.renderTile(context, target, coordinates);
} }

View File

@ -72,12 +72,8 @@ public:
void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates); void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates);
}; };
class MapRendererObjects : public IMapObjectObserver class MapRendererObjects
{ {
using MapObject = ObjectInstanceID;
using MapTile = std::vector<MapObject>;
boost::multi_array<MapTile, 3> objects;
std::map<std::string, std::shared_ptr<CAnimation>> animations; std::map<std::string, std::shared_ptr<CAnimation>> animations;
std::shared_ptr<CAnimation> getFlagAnimation(const CGObjectInstance * obj); std::shared_ptr<CAnimation> getFlagAnimation(const CGObjectInstance * obj);
@ -85,17 +81,12 @@ class MapRendererObjects : public IMapObjectObserver
std::shared_ptr<CAnimation> getAnimation(const std::string & filename, bool generateMovementGroups); std::shared_ptr<CAnimation> getAnimation(const std::string & filename, bool generateMovementGroups);
std::shared_ptr<IImage> getImage(const IMapRendererContext & context, const CGObjectInstance * obj, const std::shared_ptr<CAnimation> & animation) const; std::shared_ptr<IImage> getImage(const IMapRendererContext & context, const CGObjectInstance * obj, const std::shared_ptr<CAnimation> & animation) const;
size_t getAnimationGroup(const IMapRendererContext & context, const CGObjectInstance * obj) const;
void renderImage(Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr<IImage> & image); void renderImage(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance * object, const std::shared_ptr<IImage> & image);
void renderObject(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance * obj); void renderObject(const IMapRendererContext & context, Canvas & target, const int3 & coordinates, const CGObjectInstance * obj);
public: public:
explicit MapRendererObjects(const IMapRendererContext & context);
void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates); void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates);
void onObjectInstantAdd(const IMapRendererContext & context, const CGObjectInstance * object) final override;
void onObjectInstantRemove(const IMapRendererContext & context, const CGObjectInstance * object) final override;
}; };
class MapRendererBorder class MapRendererBorder
@ -150,7 +141,5 @@ class MapRenderer
MapRendererDebugGrid rendererDebugGrid; MapRendererDebugGrid rendererDebugGrid;
public: public:
explicit MapRenderer(const IMapRendererContext & context);
void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates); void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates);
}; };

View File

@ -15,9 +15,9 @@ VCMI_LIB_NAMESPACE_BEGIN
class int3; class int3;
class Point; class Point;
class ObjectInstanceID;
class CGObjectInstance; class CGObjectInstance;
class CGHeroInstance; class CGHeroInstance;
class ObjectInstanceID;
struct TerrainTile; struct TerrainTile;
struct CGPath; struct CGPath;
@ -26,9 +26,10 @@ VCMI_LIB_NAMESPACE_END
class IMapRendererContext class IMapRendererContext
{ {
public: public:
virtual ~IMapRendererContext() = default; using MapObject = ObjectInstanceID;
using MapObjectsList = std::vector<MapObject>;
using ObjectsVector = std::vector<ConstTransitivePtr<CGObjectInstance>>; virtual ~IMapRendererContext() = default;
/// returns dimensions of current map /// returns dimensions of current map
virtual int3 getMapSize() const = 0; virtual int3 getMapSize() const = 0;
@ -39,8 +40,8 @@ public:
/// returns tile by selected coordinates. Coordinates MUST be valid /// returns tile by selected coordinates. Coordinates MUST be valid
virtual const TerrainTile & getMapTile(const int3 & coordinates) const = 0; virtual const TerrainTile & getMapTile(const int3 & coordinates) const = 0;
/// returns vector of all objects present on current map /// returns all objects visible on specified tile
virtual ObjectsVector getAllObjects() const = 0; virtual const MapObjectsList & getObjects(const int3 & coordinates) const = 0;
/// returns specific object by ID, or nullptr if not found /// returns specific object by ID, or nullptr if not found
virtual const CGObjectInstance * getObject(ObjectInstanceID objectID) const = 0; virtual const CGObjectInstance * getObject(ObjectInstanceID objectID) const = 0;
@ -51,6 +52,12 @@ public:
/// returns true if specified tile is visible in current context /// returns true if specified tile is visible in current context
virtual bool isVisible(const int3 & coordinates) const = 0; virtual bool isVisible(const int3 & coordinates) const = 0;
virtual size_t objectGroupIndex(ObjectInstanceID objectID) const = 0;
virtual Point objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const = 0;
/// returns object animation transparency. IF set to 0, object will not be visible
virtual double objectTransparency(ObjectInstanceID objectID) const = 0;
/// returns how long should each frame of animation be visible, in milliseconds /// returns how long should each frame of animation be visible, in milliseconds
virtual uint32_t getAnimationPeriod() const = 0; virtual uint32_t getAnimationPeriod() const = 0;
@ -70,24 +77,26 @@ public:
IMapObjectObserver(); IMapObjectObserver();
virtual ~IMapObjectObserver(); virtual ~IMapObjectObserver();
virtual bool hasOngoingAnimations() = 0;
/// Plays fade-in animation and adds object to map /// Plays fade-in animation and adds object to map
virtual void onObjectFadeIn(const IMapRendererContext & context, const CGObjectInstance * obj) {} virtual void onObjectFadeIn(const CGObjectInstance * obj) {}
/// Plays fade-out animation and removed object from map /// Plays fade-out animation and removed object from map
virtual void onObjectFadeOut(const IMapRendererContext & context, const CGObjectInstance * obj) {} virtual void onObjectFadeOut(const CGObjectInstance * obj) {}
/// Adds object to map instantly, with no animation /// Adds object to map instantly, with no animation
virtual void onObjectInstantAdd(const IMapRendererContext & context, const CGObjectInstance * obj) {} virtual void onObjectInstantAdd(const CGObjectInstance * obj) {}
/// Removes object from map instantly, with no animation /// Removes object from map instantly, with no animation
virtual void onObjectInstantRemove(const IMapRendererContext & context, const CGObjectInstance * obj) {} virtual void onObjectInstantRemove(const CGObjectInstance * obj) {}
/// Perform hero teleportation animation with terrain fade animation /// Perform hero teleportation animation with terrain fade animation
virtual void onHeroTeleported(const IMapRendererContext & context, const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} virtual void onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}
/// Perform hero movement animation, moving hero across terrain /// Perform hero movement animation, moving hero across terrain
virtual void onHeroMoved(const IMapRendererContext & context, const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} virtual void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}
/// Instantly rotates hero to face destination tile /// Instantly rotates hero to face destination tile
virtual void onHeroRotated(const IMapRendererContext & context, const CGHeroInstance * obj, const int3 & from, const int3 & dest) {} virtual void onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}
}; };

View File

@ -11,6 +11,8 @@
#include "StdInc.h" #include "StdInc.h"
#include "MapView.h" #include "MapView.h"
#include <utility>
#include "MapRenderer.h" #include "MapRenderer.h"
#include "mapHandler.h" #include "mapHandler.h"
#include "CAdvMapInt.h" #include "CAdvMapInt.h"
@ -20,7 +22,6 @@
#include "../CPlayerInterface.h" #include "../CPlayerInterface.h"
#include "../gui/CGuiHandler.h" #include "../gui/CGuiHandler.h"
#include "../render/CAnimation.h" #include "../render/CAnimation.h"
#include "../render/CFadeAnimation.h"
#include "../render/Canvas.h" #include "../render/Canvas.h"
#include "../render/Colors.h" #include "../render/Colors.h"
#include "../render/Graphics.h" #include "../render/Graphics.h"
@ -41,51 +42,45 @@
#include "../../lib/mapObjects/CObjectClassesHandler.h" #include "../../lib/mapObjects/CObjectClassesHandler.h"
#include "../../lib/mapping/CMap.h" #include "../../lib/mapping/CMap.h"
MapCache::~MapCache() = default; MapViewCache::~MapViewCache() = default;
MapCache::MapCache(const std::shared_ptr<MapViewModel> & model) MapViewCache::MapViewCache(const std::shared_ptr<MapViewModel> & model)
: model(model) : model(model)
, context(new MapRendererContext()) , mapRenderer(new MapRenderer())
, mapRenderer(new MapRenderer(*context)) , terrain(new Canvas(model->getCacheDimensionsPixels()))
{ {
terrain = std::make_unique<Canvas>(model->getCacheDimensionsPixels());
} }
Canvas MapCache::getTile(const int3 & coordinates) Canvas MapViewCache::getTile(const int3 & coordinates)
{ {
return Canvas(*terrain, model->getCacheTileArea(coordinates)); return Canvas(*terrain, model->getCacheTileArea(coordinates));
} }
void MapCache::updateTile(const int3 & coordinates) void MapViewCache::updateTile(const std::shared_ptr<MapRendererContext> & context, const int3 & coordinates)
{ {
Canvas target = getTile(coordinates); Canvas target = getTile(coordinates);
mapRenderer->renderTile(*context, target, coordinates); mapRenderer->renderTile(*context, target, coordinates);
} }
void MapCache::update(uint32_t timeDelta) void MapViewCache::update(const std::shared_ptr<MapRendererContext> & context)
{ {
context->advanceAnimations(timeDelta);
context->setTileSize(model->getSingleTileSize());
Rect dimensions = model->getTilesTotalRect(); Rect dimensions = model->getTilesTotalRect();
for(int y = dimensions.top(); y < dimensions.bottom(); ++y) for(int y = dimensions.top(); y < dimensions.bottom(); ++y)
for(int x = dimensions.left(); x < dimensions.right(); ++x) for(int x = dimensions.left(); x < dimensions.right(); ++x)
updateTile({x, y, model->getLevel()}); updateTile(context, {x, y, model->getLevel()});
} }
void MapCache::render(Canvas & target) void MapViewCache::render(Canvas & target)
{ {
update(GH.mainFPSmng->getElapsedMilliseconds());
Rect dimensions = model->getTilesTotalRect(); Rect dimensions = model->getTilesTotalRect();
for(int y = dimensions.top(); y < dimensions.bottom(); ++y) for(int y = dimensions.top(); y < dimensions.bottom(); ++y)
{ {
for(int x = dimensions.left(); x < dimensions.right(); ++x) for(int x = dimensions.left(); x < dimensions.right(); ++x)
{ {
int3 tile( x,y,model->getLevel()); int3 tile(x, y, model->getLevel());
Canvas source = getTile(tile); Canvas source = getTile(tile);
Rect targetRect = model->getTargetTileArea(tile); Rect targetRect = model->getTargetTileArea(tile);
target.draw(source, targetRect.topLeft()); target.draw(source, targetRect.topLeft());
@ -98,8 +93,8 @@ std::shared_ptr<MapViewModel> MapView::createModel(const Point & dimensions) con
auto result = std::make_shared<MapViewModel>(); auto result = std::make_shared<MapViewModel>();
result->setLevel(0); result->setLevel(0);
result->setTileSize(Point(32,32)); result->setTileSize(Point(32, 32));
result->setViewCenter(Point(0,0)); result->setViewCenter(Point(0, 0));
result->setViewDimensions(dimensions); result->setViewDimensions(dimensions);
return result; return result;
@ -107,12 +102,13 @@ std::shared_ptr<MapViewModel> MapView::createModel(const Point & dimensions) con
MapView::MapView(const Point & offset, const Point & dimensions) MapView::MapView(const Point & offset, const Point & dimensions)
: model(createModel(dimensions)) : model(createModel(dimensions))
, tilesCache(new MapCache(model)) , context(new MapRendererContext())
, controller(new MapViewController(context, model))
, tilesCache(new MapViewCache(model))
{ {
pos += offset; pos += offset;
pos.w = dimensions.x; pos.w = dimensions.x;
pos.h = dimensions.y; pos.h = dimensions.y;
} }
void MapView::show(SDL_Surface * to) void MapView::show(SDL_Surface * to)
@ -122,6 +118,8 @@ void MapView::show(SDL_Surface * to)
CSDL_Ext::CClipRectGuard guard(to, pos); CSDL_Ext::CClipRectGuard guard(to, pos);
controller->update(GH.mainFPSmng->getElapsedMilliseconds());
tilesCache->update(context);
tilesCache->render(targetClipped); tilesCache->render(targetClipped);
} }
@ -130,11 +128,6 @@ void MapView::showAll(SDL_Surface * to)
show(to); show(to);
} }
void MapRendererContext::advanceAnimations(uint32_t ms)
{
animationTime += ms;
}
int3 MapRendererContext::getMapSize() const int3 MapRendererContext::getMapSize() const
{ {
return LOCPLINT->cb->getMapSize(); return LOCPLINT->cb->getMapSize();
@ -150,11 +143,6 @@ const TerrainTile & MapRendererContext::getMapTile(const int3 & coordinates) con
return CGI->mh->getMap()->getTile(coordinates); return CGI->mh->getMap()->getTile(coordinates);
} }
MapRendererContext::ObjectsVector MapRendererContext::getAllObjects() const
{
return CGI->mh->getMap()->objects;
}
const CGObjectInstance * MapRendererContext::getObject(ObjectInstanceID objectID) const const CGObjectInstance * MapRendererContext::getObject(ObjectInstanceID objectID) const
{ {
return CGI->mh->getMap()->objects.at(objectID.getNum()); return CGI->mh->getMap()->objects.at(objectID.getNum());
@ -165,11 +153,6 @@ bool MapRendererContext::isVisible(const int3 & coordinates) const
return LOCPLINT->cb->isVisible(coordinates) || settings["session"]["spectate"].Bool(); return LOCPLINT->cb->isVisible(coordinates) || settings["session"]["spectate"].Bool();
} }
void MapRendererContext::setTileSize(const Point & dimensions)
{
tileSize = dimensions;
}
const CGPath * MapRendererContext::currentPath() const const CGPath * MapRendererContext::currentPath() const
{ {
const auto * hero = adventureInt->curHero(); const auto * hero = adventureInt->curHero();
@ -208,19 +191,19 @@ bool MapRendererContext::showGrid() const
return true; // settings["session"]["showGrid"].Bool(); return true; // settings["session"]["showGrid"].Bool();
} }
void MapView::setViewCenter(const int3 & position) void MapViewController::setViewCenter(const int3 & position)
{ {
model->setViewCenter(Point(position.x, position.y) * model->getSingleTileSize()); model->setViewCenter(Point(position.x, position.y) * model->getSingleTileSize());
model->setLevel(position.z); model->setLevel(position.z);
} }
void MapView::setViewCenter(const Point & position, int level) void MapViewController::setViewCenter(const Point & position, int level)
{ {
model->setViewCenter(position); model->setViewCenter(position);
model->setLevel(level); model->setLevel(level);
} }
void MapView::setTileSize(const Point & tileSize) void MapViewController::setTileSize(const Point & tileSize)
{ {
model->setTileSize(tileSize); model->setTileSize(tileSize);
} }
@ -290,11 +273,6 @@ Rect MapViewModel::getTilesTotalRect() const
); );
} }
int3 MapViewModel::getTileCenter() const
{
return getTileAtPoint(getMapViewCenter());
}
int3 MapViewModel::getTileAtPoint(const Point & position) const int3 MapViewModel::getTileAtPoint(const Point & position) const
{ {
Point topLeftOffset = getMapViewCenter() - getPixelsVisibleDimensions() / 2; Point topLeftOffset = getMapViewCenter() - getPixelsVisibleDimensions() / 2;
@ -335,8 +313,259 @@ Rect MapViewModel::getTargetTileArea(const int3 & coordinates) const
Point tilePosAbsolute = Point(coordinates) * tileSize; Point tilePosAbsolute = Point(coordinates) * tileSize;
Point tilePosRelative = tilePosAbsolute - topLeftOffset; Point tilePosRelative = tilePosAbsolute - topLeftOffset;
return { return Rect(tilePosRelative, tileSize);
tilePosRelative, }
tileSize
}; MapRendererContext::MapRendererContext()
{
auto mapSize = getMapSize();
objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
for(const auto & obj : CGI->mh->getMap()->objects)
addObject(obj);
}
void MapRendererContext::addObject(const CGObjectInstance * obj)
{
if(!obj)
return;
for(int fx = 0; fx < obj->getWidth(); ++fx)
{
for(int fy = 0; fy < obj->getHeight(); ++fy)
{
int3 currTile(obj->pos.x - fx, obj->pos.y - fy, obj->pos.z);
if(isInMap(currTile) && obj->coveringAt(currTile.x, currTile.y))
{
auto & container = objects[currTile.z][currTile.x][currTile.y];
container.push_back(obj->id);
boost::range::sort(container, MapObjectsSorter(*this));
}
}
}
}
void MapRendererContext::addMovingObject(const CGObjectInstance * object, const int3 & tileFrom, const int3 & tileDest)
{
int xFrom = std::min(tileFrom.x, tileDest.x) - object->getWidth();
int xDest = std::max(tileFrom.x, tileDest.x);
int yFrom = std::min(tileFrom.y, tileDest.y) - object->getHeight();
int yDest = std::max(tileFrom.y, tileDest.y);
for(int x = xFrom; x <= xDest; ++x)
{
for(int y = yFrom; y <= yDest; ++y)
{
int3 currTile(x, y, object->pos.z);
if(isInMap(currTile))
{
auto & container = objects[currTile.z][currTile.x][currTile.y];
container.push_back(object->id);
boost::range::sort(container, MapObjectsSorter(*this));
}
}
}
}
void MapRendererContext::removeObject(const CGObjectInstance * object)
{
for(int z = 0; z < getMapSize().z; z++)
for(int x = 0; x < getMapSize().x; x++)
for(int y = 0; y < getMapSize().y; y++)
vstd::erase(objects[z][x][y], object->id);
}
const MapRendererContext::MapObjectsList & MapRendererContext::getObjects(const int3 & coordinates) const
{
assert(isInMap(coordinates));
return objects[coordinates.z][coordinates.x][coordinates.y];
}
size_t MapRendererContext::objectGroupIndex(ObjectInstanceID objectID) const
{
const CGObjectInstance * obj = getObject(objectID);
// TODO
static const std::vector<size_t> moveGroups = {99, 10, 5, 6, 7, 8, 9, 12, 11};
static const std::vector<size_t> idleGroups = {99, 13, 0, 1, 2, 3, 4, 15, 14};
if(obj->ID == Obj::HERO)
{
const auto * hero = dynamic_cast<const CGHeroInstance *>(obj);
if (movementAnimation && movementAnimation->target == objectID)
return moveGroups[hero->moveDir];
return idleGroups[hero->moveDir];
}
if(obj->ID == Obj::BOAT)
{
const auto * boat = dynamic_cast<const CGBoat *>(obj);
if (movementAnimation && movementAnimation->target == objectID)
return moveGroups[boat->direction];
return idleGroups[boat->direction];
}
return 0;
}
Point MapRendererContext::objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const
{
if (movementAnimation && movementAnimation->target == objectID)
{
int3 offsetTilesFrom = movementAnimation->tileFrom - coordinates;
int3 offsetTilesDest = movementAnimation->tileDest - coordinates;
Point offsetPixelsFrom = Point(offsetTilesFrom) * Point(32,32);
Point offsetPixelsDest = Point(offsetTilesDest) * Point(32,32);
Point result = vstd::lerp(offsetPixelsFrom, offsetPixelsDest, movementAnimation->progress);
return result;
}
const CGObjectInstance * object = getObject(objectID);
int3 offsetTiles(object->getPosition() - coordinates);
return Point(offsetTiles) * Point(32, 32);
}
double MapRendererContext::objectTransparency(ObjectInstanceID objectID) const
{
if (fadeOutAnimation && objectID == fadeOutAnimation->target)
return 1.0 - fadeOutAnimation->progress;
if (fadeInAnimation && objectID == fadeInAnimation->target)
return fadeInAnimation->progress;
return 1.0;
}
MapViewController::MapViewController(std::shared_ptr<MapRendererContext> context, std::shared_ptr<MapViewModel> model)
: context(std::move(context))
, model(std::move(model))
{
}
void MapViewController::update(uint32_t timeDelta)
{
static const double fadeOutDuration = 1.0;
static const double fadeInDuration = 1.0;
static const double heroMoveDuration = 1.0;
static const double heroTeleportDuration = 1.0;
//FIXME: remove code duplication?
if (context->movementAnimation)
{
context->movementAnimation->progress += heroMoveDuration * timeDelta / 1000;
Point positionFrom = Point(context->movementAnimation->tileFrom) * model->getSingleTileSize();
Point positionDest = Point(context->movementAnimation->tileDest) * model->getSingleTileSize();
Point positionCurr = vstd::lerp(positionFrom, positionDest, context->movementAnimation->progress);
setViewCenter(positionCurr, context->movementAnimation->tileDest.z);
if (context->movementAnimation->progress >= 1.0)
{
setViewCenter(context->movementAnimation->tileDest);
context->removeObject(context->getObject(context->movementAnimation->target));
context->addObject(context->getObject(context->movementAnimation->target));
context->movementAnimation.reset();
}
}
if (context->teleportAnimation)
{
context->teleportAnimation->progress += heroTeleportDuration * timeDelta / 1000;
if (context->teleportAnimation->progress >= 1.0)
context->teleportAnimation.reset();
}
if (context->fadeOutAnimation)
{
context->fadeOutAnimation->progress += fadeOutDuration * timeDelta / 1000;
if (context->fadeOutAnimation->progress >= 1.0)
{
context->removeObject(context->getObject(context->fadeOutAnimation->target));
context->fadeOutAnimation.reset();
}
}
if (context->fadeInAnimation)
{
context->fadeInAnimation->progress += fadeInDuration * timeDelta / 1000;
if (context->fadeInAnimation->progress >= 1.0)
context->fadeInAnimation.reset();
}
context->animationTime += timeDelta;
context->tileSize =model->getSingleTileSize();
}
void MapViewController::onObjectFadeIn(const CGObjectInstance * obj)
{
assert(!context->fadeInAnimation);
context->fadeInAnimation = FadingAnimationState{obj->id, 0.0};
context->addObject(obj);
}
void MapViewController::onObjectFadeOut(const CGObjectInstance * obj)
{
assert(!context->fadeOutAnimation);
context->fadeOutAnimation = FadingAnimationState{obj->id, 0.0};
}
void MapViewController::onObjectInstantAdd(const CGObjectInstance * obj)
{
context->addObject(obj);
};
void MapViewController::onObjectInstantRemove(const CGObjectInstance * obj)
{
context->removeObject(obj);
};
void MapViewController::onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
assert(!context->teleportAnimation);
context->teleportAnimation = HeroAnimationState{ obj->id, from, dest, 0.0 };
}
void MapViewController::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
assert(!context->movementAnimation);
context->removeObject(obj);
context->addMovingObject(obj, from, dest);
context->movementAnimation = HeroAnimationState{ obj->id, from, dest, 0.0 };
}
void MapViewController::onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
//TODO
}
std::shared_ptr<MapViewController> MapView::getController()
{
return controller;
}
bool MapViewController::hasOngoingAnimations()
{
if(context->movementAnimation)
return true;
if(context->teleportAnimation)
return true;
if(context->fadeOutAnimation)
return true;
if(context->fadeInAnimation)
return true;
return false;
} }

View File

@ -9,37 +9,65 @@
*/ */
#pragma once #pragma once
#include "../gui/CIntObject.h"
#include "MapRendererContext.h" #include "MapRendererContext.h"
#include "../gui/CIntObject.h"
#include "../lib/int3.h"
class Canvas; class Canvas;
class MapRenderer; class MapRenderer;
class MapViewController;
struct HeroAnimationState
{
ObjectInstanceID target;
int3 tileFrom;
int3 tileDest;
double progress;
};
struct FadingAnimationState
{
ObjectInstanceID target;
double progress;
};
class MapRendererContext : public IMapRendererContext class MapRendererContext : public IMapRendererContext
{ {
friend class MapViewController;
boost::multi_array<MapObjectsList, 3> objects;
Point tileSize = Point(32, 32); Point tileSize = Point(32, 32);
uint32_t animationTime = 0; uint32_t animationTime = 0;
boost::optional<HeroAnimationState> movementAnimation;
boost::optional<HeroAnimationState> teleportAnimation;
boost::optional<FadingAnimationState> fadeOutAnimation;
boost::optional<FadingAnimationState> fadeInAnimation;
public: public:
void advanceAnimations(uint32_t ms); MapRendererContext();
void setTileSize(const Point & dimensions);
int3 getMapSize() const override; void addObject(const CGObjectInstance * object);
bool isInMap(const int3 & coordinates) const override; void addMovingObject(const CGObjectInstance * object, const int3 & tileFrom, const int3 & tileDest);
const TerrainTile & getMapTile(const int3 & coordinates) const override; void removeObject(const CGObjectInstance * object);
ObjectsVector getAllObjects() const override;
const CGObjectInstance * getObject(ObjectInstanceID objectID) const override;
const CGPath * currentPath() const override;
int3 getMapSize() const final;
bool isInMap(const int3 & coordinates) const final;
bool isVisible(const int3 & coordinates) const override; bool isVisible(const int3 & coordinates) const override;
const TerrainTile & getMapTile(const int3 & coordinates) const override;
const MapObjectsList & getObjects(const int3 & coordinates) const override;
const CGObjectInstance * getObject(ObjectInstanceID objectID) const override;
const CGPath * currentPath() const override;
size_t objectGroupIndex(ObjectInstanceID objectID) const override;
Point objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const override;
double objectTransparency(ObjectInstanceID objectID) const override;
uint32_t getAnimationPeriod() const override; uint32_t getAnimationPeriod() const override;
uint32_t getAnimationTime() const override; uint32_t getAnimationTime() const override;
Point getTileSize() const override; Point getTileSize() const override;
bool showGrid() const override; bool showGrid() const override;
}; };
@ -81,50 +109,79 @@ public:
/// returns area covered by specified tile in target view /// returns area covered by specified tile in target view
Rect getTargetTileArea(const int3 & coordinates) const; Rect getTargetTileArea(const int3 & coordinates) const;
int getLevel() const;
int3 getTileCenter() const;
/// returns tile under specified position in target view /// returns tile under specified position in target view
int3 getTileAtPoint(const Point & position) const; int3 getTileAtPoint(const Point & position) const;
/// returns currently visible map level
int getLevel() const;
}; };
class MapCache /// Class responsible for rendering of entire map view
/// uses rendering parameters provided by owner class
class MapViewCache
{ {
std::unique_ptr<Canvas> terrain;
std::shared_ptr<MapViewModel> model; std::shared_ptr<MapViewModel> model;
std::unique_ptr<MapRendererContext> context; std::unique_ptr<Canvas> terrain;
std::unique_ptr<MapRenderer> mapRenderer; std::unique_ptr<MapRenderer> mapRenderer;
Canvas getTile(const int3 & coordinates); Canvas getTile(const int3 & coordinates);
void updateTile(const int3 & coordinates); void updateTile(const std::shared_ptr<MapRendererContext> & context, const int3 & coordinates);
public: public:
explicit MapCache(const std::shared_ptr<MapViewModel> & model); explicit MapViewCache(const std::shared_ptr<MapViewModel> & model);
~MapCache(); ~MapViewCache();
void update(uint32_t timeDelta); /// updates internal terrain cache according to provided time delta
void update(const std::shared_ptr<MapRendererContext> & context);
/// renders updated terrain cache onto provided canvas
void render(Canvas & target); void render(Canvas & target);
}; };
/// Class responsible for updating view state,
/// such as its position and any animations
class MapViewController : public IMapObjectObserver
{
std::shared_ptr<MapRendererContext> context;
std::shared_ptr<MapViewModel> model;
private:
// IMapObjectObserver impl
bool hasOngoingAnimations() override;
void onObjectFadeIn(const CGObjectInstance * obj) override;
void onObjectFadeOut(const CGObjectInstance * obj) override;
void onObjectInstantAdd(const CGObjectInstance * obj) override;
void onObjectInstantRemove(const CGObjectInstance * obj) override;
void onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
public:
MapViewController(std::shared_ptr<MapRendererContext> context, std::shared_ptr<MapViewModel> model);
void setViewCenter(const int3 & position);
void setViewCenter(const Point & position, int level);
void setTileSize(const Point & tileSize);
void update(uint32_t timeDelta);
};
/// Main map rendering class that mostly acts as container for component classes
class MapView : public CIntObject class MapView : public CIntObject
{ {
std::shared_ptr<MapViewModel> model; std::shared_ptr<MapViewModel> model;
std::unique_ptr<MapCache> tilesCache; std::shared_ptr<MapRendererContext> context;
std::unique_ptr<MapViewCache> tilesCache;
std::shared_ptr<MapViewController> controller;
std::shared_ptr<MapViewModel> createModel(const Point & dimensions) const; std::shared_ptr<MapViewModel> createModel(const Point & dimensions) const;
public: public:
std::shared_ptr<const MapViewModel> getModel() const; std::shared_ptr<const MapViewModel> getModel() const;
std::shared_ptr<MapViewController> getController();
MapView(const Point & offset, const Point & dimensions); MapView(const Point & offset, const Point & dimensions);
void setViewCenter(const int3 & position);
void setViewCenter(const Point & position, int level);
void setTileSize(const Point & tileSize);
void moveHero();
void show(SDL_Surface * to) override; void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override; void showAll(SDL_Surface * to) override;
}; };

View File

@ -14,7 +14,6 @@
#include "MapView.h" #include "MapView.h"
#include "../render/CAnimation.h" #include "../render/CAnimation.h"
#include "../render/CFadeAnimation.h"
#include "../render/Colors.h" #include "../render/Colors.h"
#include "../gui/CGuiHandler.h" #include "../gui/CGuiHandler.h"
#include "../renderSDL/SDL_Extensions.h" #include "../renderSDL/SDL_Extensions.h"
@ -27,6 +26,7 @@
#include "../../CCallback.h" #include "../../CCallback.h"
#include "../../lib/UnlockGuard.h"
#include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/CObjectClassesHandler.h" #include "../../lib/mapObjects/CObjectClassesHandler.h"
#include "../../lib/mapping/CMap.h" #include "../../lib/mapping/CMap.h"
@ -476,10 +476,24 @@ bool CMapHandler::hideObject(const CGObjectInstance * obj, bool fadeout)
} }
*/ */
bool CMapHandler::hasActiveAnimations() bool CMapHandler::hasOngoingAnimations()
{ {
return false; // !fadeAnims.empty(); // don't allow movement during fade animation for (auto * observer : observers)
if (observer->hasOngoingAnimations())
return true;
return false;
} }
void CMapHandler::waitForOngoingAnimations()
{
while (CGI->mh->hasOngoingAnimations())
{
auto unlockPim = vstd::makeUnlockGuard(*CPlayerInterface::pim);
boost::this_thread::sleep(boost::posix_time::milliseconds(1));
}
}
/* /*
void CMapHandler::updateWater() //shift colors in palettes of water tiles void CMapHandler::updateWater() //shift colors in palettes of water tiles
{ {
@ -600,26 +614,6 @@ bool CMapHandler::compareObjectBlitOrder(const CGObjectInstance * a, const CGObj
return false; return false;
} }
TerrainTileObject::TerrainTileObject(const CGObjectInstance * obj_, Rect rect_, bool visitablePos)
: obj(obj_),
rect(rect_),
fadeAnimKey(-1)
{
// We store information about ambient sound is here because object might disappear while sound is updating
if(obj->getAmbientSound())
{
// All tiles of static objects are sound sources. E.g Volcanos and special terrains
// For visitable object only their visitable tile is sound source
if(!CCS->soundh->ambientCheckVisitable() || !obj->isVisitable() || visitablePos)
ambientSound = obj->getAmbientSound();
}
}
TerrainTileObject::~TerrainTileObject()
{
}
CMapHandler::CMapHandler(const CMap * map) CMapHandler::CMapHandler(const CMap * map)
: map(map) : map(map)
{ {
@ -652,45 +646,47 @@ std::vector<std::string> CMapHandler::getAmbientSounds(const int3 & tile)
void CMapHandler::onObjectFadeIn(const CGObjectInstance * obj) void CMapHandler::onObjectFadeIn(const CGObjectInstance * obj)
{ {
onObjectInstantAdd(obj); for (auto * observer : observers)
observer->onObjectFadeIn(obj);
} }
void CMapHandler::onObjectFadeOut(const CGObjectInstance * obj) void CMapHandler::onObjectFadeOut(const CGObjectInstance * obj)
{ {
onObjectInstantRemove(obj); for (auto * observer : observers)
observer->onObjectFadeOut(obj);
} }
void CMapHandler::onObjectInstantAdd(const CGObjectInstance * obj) void CMapHandler::onObjectInstantAdd(const CGObjectInstance * obj)
{ {
MapRendererContext context;
for (auto * observer : observers) for (auto * observer : observers)
observer->onObjectInstantAdd(context, obj); observer->onObjectInstantAdd(obj);
} }
void CMapHandler::onObjectInstantRemove(const CGObjectInstance * obj) void CMapHandler::onObjectInstantRemove(const CGObjectInstance * obj)
{ {
MapRendererContext context;
for (auto * observer : observers) for (auto * observer : observers)
observer->onObjectInstantRemove(context, obj); observer->onObjectInstantRemove(obj);
} }
void CMapHandler::onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) void CMapHandler::onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{ {
assert(obj->pos == dest); assert(obj->pos == dest);
onObjectInstantRemove(obj); for (auto * observer : observers)
onObjectInstantAdd(obj); observer->onHeroTeleported(obj, from, dest);
} }
void CMapHandler::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) void CMapHandler::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{ {
assert(obj->pos == dest); assert(obj->pos == dest);
onObjectInstantRemove(obj); for (auto * observer : observers)
onObjectInstantAdd(obj); observer->onHeroMoved(obj, from, dest);
} }
void CMapHandler::onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) void CMapHandler::onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{ {
//TODO assert(obj->pos == from);
for (auto * observer : observers)
observer->onHeroRotated(obj, from, dest);
} }
void CMapHandler::addMapObserver(IMapObjectObserver * object) void CMapHandler::addMapObserver(IMapObjectObserver * object)

View File

@ -38,7 +38,6 @@ VCMI_LIB_NAMESPACE_END
struct SDL_Surface; struct SDL_Surface;
class CAnimation; class CAnimation;
class IImage; class IImage;
class CFadeAnimation;
class CMapHandler; class CMapHandler;
class IMapObjectObserver; class IMapObjectObserver;
@ -63,30 +62,6 @@ enum class EWorldViewIcon
RES_CRYSTAL, RES_CRYSTAL,
RES_GEM, RES_GEM,
RES_GOLD, RES_GOLD,
};
enum class EMapObjectFadingType
{
NONE,
IN,
OUT
};
struct TerrainTileObject
{
const CGObjectInstance *obj;
Rect rect;
int fadeAnimKey;
boost::optional<std::string> ambientSound;
TerrainTileObject(const CGObjectInstance *obj_, Rect rect_, bool visitablePos = false);
~TerrainTileObject();
};
struct TerrainTile2
{
std::vector<TerrainTileObject> objects; //pointers to objects being on this tile with rects to be easier to blit this tile on screen
}; };
class CMapHandler class CMapHandler
@ -125,7 +100,10 @@ public:
bool hasObjectHole(const int3 & pos) const; bool hasObjectHole(const int3 & pos) const;
/// determines if the map is ready to handle new hero movement (not available during fading animations) /// determines if the map is ready to handle new hero movement (not available during fading animations)
bool hasActiveAnimations(); bool hasOngoingAnimations();
/// blocking wait until all ongoing animatins are over
void waitForOngoingAnimations();
static bool compareObjectBlitOrder(const CGObjectInstance * a, const CGObjectInstance * b); static bool compareObjectBlitOrder(const CGObjectInstance * a, const CGObjectInstance * b);
}; };

View File

@ -1,101 +0,0 @@
/*
* CFadeAnimation.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#include "StdInc.h"
#include "CFadeAnimation.h"
#include "../renderSDL/SDL_Extensions.h"
#include <SDL_surface.h>
float CFadeAnimation::initialCounter() const
{
if (fadingMode == EMode::OUT)
return 1.0f;
return 0.0f;
}
void CFadeAnimation::update()
{
if (!fading)
return;
if (fadingMode == EMode::OUT)
fadingCounter -= delta;
else
fadingCounter += delta;
if (isFinished())
{
fading = false;
if (shouldFreeSurface)
{
SDL_FreeSurface(fadingSurface);
fadingSurface = nullptr;
}
}
}
bool CFadeAnimation::isFinished() const
{
if (fadingMode == EMode::OUT)
return fadingCounter <= 0.0f;
return fadingCounter >= 1.0f;
}
CFadeAnimation::CFadeAnimation()
: delta(0), fadingSurface(nullptr), fading(false), fadingCounter(0), shouldFreeSurface(false),
fadingMode(EMode::NONE)
{
}
CFadeAnimation::~CFadeAnimation()
{
if (fadingSurface && shouldFreeSurface)
SDL_FreeSurface(fadingSurface);
}
void CFadeAnimation::init(EMode mode, SDL_Surface * sourceSurface, bool freeSurfaceAtEnd, float animDelta)
{
if (fading)
{
// in that case, immediately finish the previous fade
// (alternatively, we could just return here to ignore the new fade request until this one finished (but we'd need to free the passed bitmap to avoid leaks))
logGlobal->warn("Tried to init fading animation that is already running.");
if (fadingSurface && shouldFreeSurface)
SDL_FreeSurface(fadingSurface);
}
if (animDelta <= 0.0f)
{
logGlobal->warn("Fade anim: delta should be positive; %f given.", animDelta);
animDelta = DEFAULT_DELTA;
}
if (sourceSurface)
fadingSurface = sourceSurface;
delta = animDelta;
fadingMode = mode;
fadingCounter = initialCounter();
fading = true;
shouldFreeSurface = freeSurfaceAtEnd;
}
void CFadeAnimation::draw(SDL_Surface * targetSurface, const Point &targetPoint)
{
if (!fading || !fadingSurface || fadingMode == EMode::NONE)
{
fading = false;
return;
}
CSDL_Ext::setAlpha(fadingSurface, (int)(fadingCounter * 255));
CSDL_Ext::blitSurface(fadingSurface, targetSurface, targetPoint); //FIXME
CSDL_Ext::setAlpha(fadingSurface, 255);
}

View File

@ -1,53 +0,0 @@
/*
* CFadeAnimation.h, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
* License: GNU General Public License v2.0 or later
* Full text of license available in license.txt file, in main folder
*
*/
#pragma once
#ifdef IN
#undef IN
#endif
#ifdef OUT
#undef OUT
#endif
VCMI_LIB_NAMESPACE_BEGIN
class Point;
VCMI_LIB_NAMESPACE_END
struct SDL_Surface;
const float DEFAULT_DELTA = 0.05f;
class CFadeAnimation
{
public:
enum class EMode
{
NONE, IN, OUT
};
private:
float delta;
SDL_Surface * fadingSurface;
bool fading;
float fadingCounter;
bool shouldFreeSurface;
float initialCounter() const;
bool isFinished() const;
public:
EMode fadingMode;
CFadeAnimation();
~CFadeAnimation();
void init(EMode mode, SDL_Surface * sourceSurface, bool freeSurfaceAtEnd = false, float animDelta = DEFAULT_DELTA);
void update();
void draw(SDL_Surface * targetSurface, const Point & targetPoint);
bool isFading() const { return fading; }
};