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

Refactoring of new code, multiple functionality changes:

- Implemented separate Map View contexts for different map modes
- Puzzle map now works
- Enemy movement now works as in H3
- Removed no longer used code
- Implemented missing embark/disembark sounds
- Fixed view focusing on embarking/disembarking
- Fixed focusing on movement near edge of terra incognita
- Fixed sea movement sound
This commit is contained in:
Ivan Savenko 2023-02-26 18:17:53 +02:00
parent 46200b133f
commit 387a7b421a
21 changed files with 1152 additions and 770 deletions

View File

@ -48,6 +48,7 @@ set(client_SRCS
mapRenderer/MapRenderer.cpp
mapRenderer/MapRendererContext.cpp
mapRenderer/MapRendererContextState.cpp
mapRenderer/MapView.cpp
mapRenderer/MapViewActions.cpp
mapRenderer/MapViewCache.cpp
@ -170,6 +171,7 @@ set(client_HEADERS
mapRenderer/IMapRendererObserver.h
mapRenderer/MapRenderer.h
mapRenderer/MapRendererContext.h
mapRenderer/MapRendererContextState.h
mapRenderer/MapView.h
mapRenderer/MapViewActions.h
mapRenderer/MapViewCache.h

View File

@ -328,6 +328,12 @@ void CPlayerInterface::heroMoved(const TryMoveHero & details, bool verbose)
if (!hero)
return;
if (details.result == TryMoveHero::EMBARK || details.result == TryMoveHero::DISEMBARK)
{
if (hero->getRemovalSound())
CCS->soundh->playSound(hero->getRemovalSound().get());
}
adventureInt->minimap->updateTile(hero->convertToVisitablePos(details.start));
adventureInt->minimap->updateTile(hero->convertToVisitablePos(details.end));
@ -1603,149 +1609,7 @@ int CPlayerInterface::getLastIndex( std::string namePrefix)
return (--dates.end())->second; //return latest file number
return 0;
}
/*
void CPlayerInterface::initMovement( const TryMoveHero &details, const CGHeroInstance * ho, const int3 &hp )
{
auto subArr = (CGI->mh->ttiles)[hp.z];
int heroWidth = ho->appearance->getWidth();
int heroHeight = ho->appearance->getHeight();
int tileMinX = std::min(details.start.x, details.end.x) - heroWidth;
int tileMaxX = std::max(details.start.x, details.end.x);
int tileMinY = std::min(details.start.y, details.end.y) - heroHeight;
int tileMaxY = std::max(details.start.y, details.end.y);
// determine tiles on which hero will be visible during movement and add hero as visible object on these tiles where necessary
for ( int tileX = tileMinX; tileX <= tileMaxX; ++tileX)
{
for ( int tileY = tileMinY; tileY <= tileMaxY; ++tileY)
{
bool heroVisibleHere = false;
auto & tile = subArr[tileX][tileY];
for(const auto & obj : tile.objects)
{
if (obj.obj == ho)
{
heroVisibleHere = true;
break;
}
}
if ( !heroVisibleHere)
{
tile.objects.push_back(TerrainTileObject(ho, {0,0,32,32}));
std::stable_sort(tile.objects.begin(), tile.objects.end(), objectBlitOrderSorter);
}
}
}
}
void CPlayerInterface::movementPxStep( const TryMoveHero &details, int i, const int3 &hp, const CGHeroInstance * ho )
{
auto subArr = (CGI->mh->ttiles)[hp.z];
int heroWidth = ho->appearance->getWidth();
int heroHeight = ho->appearance->getHeight();
int tileMinX = std::min(details.start.x, details.end.x) - heroWidth;
int tileMaxX = std::max(details.start.x, details.end.x);
int tileMinY = std::min(details.start.y, details.end.y) - heroHeight;
int tileMaxY = std::max(details.start.y, details.end.y);
std::shared_ptr<CAnimation> animation = graphics->getAnimation(ho);
assert(animation);
assert(animation->size(0) != 0);
auto image = animation->getImage(0,0);
int heroImageOldX = details.start.x * 32;
int heroImageOldY = details.start.y * 32;
int heroImageNewX = details.end.x * 32;
int heroImageNewY = details.end.y * 32;
int heroImageCurrX = heroImageOldX + i*(heroImageNewX - heroImageOldX)/32;
int heroImageCurrY = heroImageOldY + i*(heroImageNewY - heroImageOldY)/32;
// recompute which part of hero sprite will be visible on each tile at this point of movement animation
for ( int tileX = tileMinX; tileX <= tileMaxX; ++tileX)
{
for ( int tileY = tileMinY; tileY <= tileMaxY; ++tileY)
{
auto & tile = subArr[tileX][tileY];
for ( auto & obj : tile.objects)
{
if (obj.obj == ho)
{
int tilePosX = tileX * 32;
int tilePosY = tileY * 32;
obj.rect.x = tilePosX - heroImageCurrX + image->width() - 32;
obj.rect.y = tilePosY - heroImageCurrY + image->height() - 32;
}
}
}
}
//adventureInt->terrain->moveX = (32 - i) * (heroImageNewX - heroImageOldX) / 32;
//adventureInt->terrain->moveY = (32 - i) * (heroImageNewY - heroImageOldY) / 32;
}
void CPlayerInterface::finishMovement( const TryMoveHero &details, const int3 &hp, const CGHeroInstance * ho )
{
auto subArr = (CGI->mh->ttiles)[hp.z];
int heroWidth = ho->appearance->getWidth();
int heroHeight = ho->appearance->getHeight();
int tileMinX = std::min(details.start.x, details.end.x) - heroWidth;
int tileMaxX = std::max(details.start.x, details.end.x);
int tileMinY = std::min(details.start.y, details.end.y) - heroHeight;
int tileMaxY = std::max(details.start.y, details.end.y);
// erase hero from all tiles on which he is currently visible
for ( int tileX = tileMinX; tileX <= tileMaxX; ++tileX)
{
for ( int tileY = tileMinY; tileY <= tileMaxY; ++tileY)
{
auto & tile = subArr[tileX][tileY];
for (size_t i = 0; i < tile.objects.size(); ++i)
{
if ( tile.objects[i].obj == ho)
{
tile.objects.erase(tile.objects.begin() + i);
break;
}
}
}
}
// re-add hero to all tiles on which he will still be visible after animation is over
for ( int tileX = details.end.x - heroWidth + 1; tileX <= details.end.x; ++tileX)
{
for ( int tileY = details.end.y - heroHeight + 1; tileY <= details.end.y; ++tileY)
{
auto & tile = subArr[tileX][tileY];
tile.objects.push_back(TerrainTileObject(ho, {0,0,32,32}));
}
}
// update object list on all tiles that were affected during previous operations
for ( int tileX = tileMinX; tileX <= tileMaxX; ++tileX)
{
for ( int tileY = tileMinY; tileY <= tileMaxY; ++tileY)
{
auto & tile = subArr[tileX][tileY];
std::stable_sort(tile.objects.begin(), tile.objects.end(), objectBlitOrderSorter);
}
}
//recompute hero sprite positioning using hero's final position
movementPxStep(details, 32, hp, ho);
}
*/
void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult )
{
EVENT_HANDLER_CALLED_BY_CLIENT;

View File

@ -444,20 +444,20 @@ void ApplyFirstClientNetPackVisitor::visitTryMoveHero(TryMoveHero & pack)
{
CGHeroInstance *h = gs.getHero(pack.id);
//check if playerint will have the knowledge about movement - if not, directly update maphandler
for(auto i=cl.playerint.begin(); i!=cl.playerint.end(); i++)
if(CGI->mh)
{
auto ps = gs.getPlayerState(i->first);
if(ps && (gs.isVisible(h->convertToVisitablePos(pack.start), i->first) || gs.isVisible(h->convertToVisitablePos(pack.end), i->first)))
switch (pack.result)
{
if(ps->human)
pack.humanKnows = true;
case TryMoveHero::EMBARK:
CGI->mh->onBeforeHeroEmbark(h, pack.start, pack.end);
break;
case TryMoveHero::TELEPORTATION:
CGI->mh->onBeforeHeroTeleported(h, pack.start, pack.end);
break;
case TryMoveHero::DISEMBARK:
CGI->mh->onBeforeHeroDisembark(h, pack.start, pack.end);
break;
}
}
if(CGI->mh && pack.result == TryMoveHero::EMBARK)
{
CGI->mh->onObjectFadeOut(h);
CGI->mh->waitForOngoingAnimations();
}
}
@ -471,23 +471,17 @@ void ApplyClientNetPackVisitor::visitTryMoveHero(TryMoveHero & pack)
{
switch(pack.result)
{
case TryMoveHero::FAILED:
break; // no-op
case TryMoveHero::SUCCESS:
CGI->mh->onHeroMoved(h, pack.start, pack.end);
break;
case TryMoveHero::TELEPORTATION:
CGI->mh->onHeroTeleported(h, pack.start, pack.end);
break;
case TryMoveHero::BLOCKING_VISIT:
CGI->mh->onHeroRotated(h, pack.start, pack.end);
break;
case TryMoveHero::EMBARK:
// handled in ApplyFirst
//CGI->mh->onObjectFadeOut(h);
CGI->mh->onAfterHeroEmbark(h, pack.start, pack.end);
break;
case TryMoveHero::TELEPORTATION:
CGI->mh->onAfterHeroTeleported(h, pack.start, pack.end);
break;
case TryMoveHero::DISEMBARK:
CGI->mh->onObjectFadeIn(h);
CGI->mh->onAfterHeroDisembark(h, pack.start, pack.end);
break;
}
}

View File

@ -37,12 +37,16 @@ public:
/// Removes object from map instantly, with no animation
virtual void onObjectInstantRemove(const CGObjectInstance * obj) {}
/// Perform hero teleportation animation with terrain fade animation
virtual void onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}
/// Perform hero movement animation, moving hero across terrain
virtual void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}
/// Instantly rotates hero to face destination tile
virtual void onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}
/// Perform initialization of hero teleportation animation with terrain fade animation
virtual void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}
virtual void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}
virtual void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {};
virtual void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {};
virtual void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {};
virtual void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {};
};

View File

@ -1,5 +1,5 @@
/*
* MapRendererContext.cpp, part of VCMI engine
* MapRendererContextState.cpp, part of VCMI engine
*
* Authors: listed in file AUTHORS in main folder
*
@ -11,305 +11,103 @@
#include "StdInc.h"
#include "MapRendererContext.h"
#include "MapRendererContextState.h"
#include "mapHandler.h"
#include "../../CCallback.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../adventureMap/CAdvMapInt.h"
#include "../../CCallback.h"
#include "../../lib/Point.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapping/CMap.h"
#include "../../lib/CPathfinder.h"
MapObjectsSorter::MapObjectsSorter(IMapRendererContext & context)
: context(context)
MapRendererBaseContext::MapRendererBaseContext(const MapRendererContextState & viewState)
: viewState(viewState)
{
}
bool MapObjectsSorter::operator()(const ObjectInstanceID & left, const ObjectInstanceID & right) const
{
return (*this)(context.getObject(left), context.getObject(right));
}
bool MapObjectsSorter::operator()(const CGObjectInstance * left, const CGObjectInstance * right) const
{
//FIXME: remove mh access
return CGI->mh->compareObjectBlitOrder(left, right);
}
int3 MapRendererContext::getMapSize() const
{
return LOCPLINT->cb->getMapSize();
}
bool MapRendererContext::isInMap(const int3 & coordinates) const
{
return LOCPLINT->cb->isInTheMap(coordinates);
}
const TerrainTile & MapRendererContext::getMapTile(const int3 & coordinates) const
{
return CGI->mh->getMap()->getTile(coordinates);
}
const CGObjectInstance * MapRendererContext::getObject(ObjectInstanceID objectID) const
{
return CGI->mh->getMap()->objects.at(objectID.getNum());
}
bool MapRendererContext::isVisible(const int3 & coordinates) const
{
if (settingsSessionSpectate || showAllTerrain)
return LOCPLINT->cb->isInTheMap(coordinates);
return LOCPLINT->cb->isVisible(coordinates);
}
const CGPath * MapRendererContext::currentPath() const
{
if (worldViewModeActive)
return nullptr;
const auto * hero = adventureInt->curHero();
if(!hero)
return nullptr;
if(!LOCPLINT->paths.hasPath(hero))
return nullptr;
return &LOCPLINT->paths.getPath(hero);
}
size_t MapRendererContext::objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const
{
assert(groupSize > 0);
if(groupSize == 0)
return 0;
if (!settingsAdventureObjectAnimation)
return 0;
if (worldViewModeActive)
return 0;
// H3 timing for adventure map objects animation is 180 ms
// Terrain animations also use identical interval, however those are only present in HotA and/or HD Mod
size_t baseFrameTime = 180;
// hero movement animation always plays at ~50ms / frame
// in-game setting only affect movement across screen
if(movementAnimation && movementAnimation->target == objectID)
baseFrameTime = 50;
size_t frameCounter = animationTime / baseFrameTime;
size_t frameIndex = frameCounter % groupSize;
return frameIndex;
}
size_t MapRendererContext::terrainImageIndex(size_t groupSize) const
{
if (!settingsAdventureTerrainAnimation)
return 0;
if (worldViewModeActive)
return 0;
size_t baseFrameTime = 180;
size_t frameCounter = animationTime / baseFrameTime;
size_t frameIndex = frameCounter % groupSize;
return frameIndex;
}
bool MapRendererContext::tileAnimated(const int3 & coordinates) const
{
if (!isInMap(coordinates))
return false;
if(movementAnimation)
{
auto objects = getObjects(coordinates);
if(vstd::contains(objects, movementAnimation->target))
return true;
}
if(fadeInAnimation)
{
auto objects = getObjects(coordinates);
if(vstd::contains(objects, fadeInAnimation->target))
return true;
}
if(fadeOutAnimation)
{
auto objects = getObjects(coordinates);
if(vstd::contains(objects, fadeOutAnimation->target))
return true;
}
return false;
}
bool MapRendererContext::filterGrayscale() const
{
return false;
}
bool MapRendererContext::showRoads() const
{
return true;
}
bool MapRendererContext::showRivers() const
{
return true;
}
bool MapRendererContext::showBorder() const
{
return !worldViewModeActive;
}
bool MapRendererContext::showOverlay() const
{
return worldViewModeActive;
}
bool MapRendererContext::showGrid() const
{
return settingsSessionShowGrid;
}
bool MapRendererContext::showVisitable() const
{
return settingsSessionShowVisitable;
}
bool MapRendererContext::showBlockable() const
{
return settingsSessionShowBlockable;
}
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
uint32_t MapRendererBaseContext::getObjectRotation(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];
return hero->moveDir;
}
if(obj->ID == Obj::BOAT)
{
const auto * boat = dynamic_cast<const CGBoat *>(obj);
uint8_t direction = boat->hero ? boat->hero->moveDir : boat->direction;
if(movementAnimation && movementAnimation->target == objectID)
return moveGroups[direction];
return idleGroups[direction];
if(boat->hero)
return boat->hero->moveDir;
return boat->direction;
}
return 0;
}
Point MapRendererContext::objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const
int3 MapRendererBaseContext::getMapSize() const
{
if(movementAnimation && movementAnimation->target == objectID)
{
int3 offsetTilesFrom = movementAnimation->tileFrom - coordinates;
int3 offsetTilesDest = movementAnimation->tileDest - coordinates;
return LOCPLINT->cb->getMapSize();
}
Point offsetPixelsFrom = Point(offsetTilesFrom) * Point(32, 32);
Point offsetPixelsDest = Point(offsetTilesDest) * Point(32, 32);
bool MapRendererBaseContext::isInMap(const int3 & coordinates) const
{
return LOCPLINT->cb->isInTheMap(coordinates);
}
Point result = vstd::lerp(offsetPixelsFrom, offsetPixelsDest, movementAnimation->progress);
bool MapRendererBaseContext::isVisible(const int3 & coordinates) const
{
if(settingsSessionSpectate)
return LOCPLINT->cb->isInTheMap(coordinates);
else
return LOCPLINT->cb->isVisible(coordinates);
}
return result;
}
bool MapRendererBaseContext::tileAnimated(const int3 & coordinates) const
{
return false;
}
const TerrainTile & MapRendererBaseContext::getMapTile(const int3 & coordinates) const
{
return CGI->mh->getMap()->getTile(coordinates);
}
const MapRendererBaseContext::MapObjectsList & MapRendererBaseContext::getObjects(const int3 & coordinates) const
{
assert(isInMap(coordinates));
return viewState.objects[coordinates.z][coordinates.x][coordinates.y];
}
const CGObjectInstance * MapRendererBaseContext::getObject(ObjectInstanceID objectID) const
{
return CGI->mh->getMap()->objects.at(objectID.getNum());
}
const CGPath * MapRendererBaseContext::currentPath() const
{
return nullptr;
}
size_t MapRendererBaseContext::objectGroupIndex(ObjectInstanceID objectID) const
{
static const std::vector<size_t> idleGroups = {0, 13, 0, 1, 2, 3, 4, 15, 14};
return idleGroups[getObjectRotation(objectID)];
}
Point MapRendererBaseContext::objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const
{
const CGObjectInstance * object = getObject(objectID);
int3 offsetTiles(object->getPosition() - coordinates);
return Point(offsetTiles) * Point(32, 32);
}
double MapRendererContext::objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const
double MapRendererBaseContext::objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const
{
const CGObjectInstance * object = getObject(objectID);
@ -323,23 +121,213 @@ double MapRendererContext::objectTransparency(ObjectInstanceID objectID, const i
if(hero->boat)
return 0;
}
return 1;
}
if(showAllTerrain)
{
if(object->isVisitable() && !LOCPLINT->cb->isVisible(coordinates))
return 0;
}
size_t MapRendererBaseContext::objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const
{
return 0;
}
if(fadeOutAnimation && objectID == fadeOutAnimation->target)
return 1.0 - fadeOutAnimation->progress;
size_t MapRendererBaseContext::terrainImageIndex(size_t groupSize) const
{
return 0;
}
if(fadeInAnimation && objectID == fadeInAnimation->target)
return fadeInAnimation->progress;
size_t MapRendererBaseContext::overlayImageIndex(const int3 & coordinates) const
{
return std::numeric_limits<size_t>::max();
}
bool MapRendererBaseContext::filterGrayscale() const
{
return false;
}
bool MapRendererBaseContext::showRoads() const
{
return true;
}
bool MapRendererBaseContext::showRivers() const
{
return true;
}
bool MapRendererBaseContext::showBorder() const
{
return false;
}
bool MapRendererBaseContext::showOverlay() const
{
return false;
}
bool MapRendererBaseContext::showGrid() const
{
return false;
}
bool MapRendererBaseContext::showVisitable() const
{
return false;
}
bool MapRendererBaseContext::showBlockable() const
{
return false;
}
MapRendererAdventureContext::MapRendererAdventureContext(const MapRendererContextState & viewState)
: MapRendererBaseContext(viewState)
{
}
const CGPath * MapRendererAdventureContext::currentPath() const
{
const auto * hero = adventureInt->curHero();
if(!hero)
return nullptr;
if(!LOCPLINT->paths.hasPath(hero))
return nullptr;
return &LOCPLINT->paths.getPath(hero);
}
size_t MapRendererAdventureContext::objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const
{
assert(groupSize > 0);
if(!settingsAdventureObjectAnimation)
return 0;
if(groupSize == 0)
return 0;
// usign objectID for frameCounter to add pseudo-random element per-object.
// Without it, animation of multiple visible objects of the same type will always be in sync
size_t baseFrameTime = 180;
size_t frameCounter = animationTime / baseFrameTime + objectID.getNum();
size_t frameIndex = frameCounter % groupSize;
return frameIndex;
}
size_t MapRendererAdventureContext::terrainImageIndex(size_t groupSize) const
{
if(!settingsAdventureTerrainAnimation)
return 0;
size_t baseFrameTime = 180;
size_t frameCounter = animationTime / baseFrameTime;
size_t frameIndex = frameCounter % groupSize;
return frameIndex;
}
bool MapRendererAdventureContext::showBorder() const
{
return true;
}
bool MapRendererAdventureContext::showGrid() const
{
return settingShowGrid;
}
bool MapRendererAdventureContext::showVisitable() const
{
return settingShowVisitable;
}
bool MapRendererAdventureContext::showBlockable() const
{
return settingShowBlockable;
}
MapRendererAdventureFadingContext::MapRendererAdventureFadingContext(const MapRendererContextState & viewState)
: MapRendererAdventureContext(viewState)
{
}
bool MapRendererAdventureFadingContext::tileAnimated(const int3 & coordinates) const
{
if(!isInMap(coordinates))
return false;
auto objects = getObjects(coordinates);
if(vstd::contains(objects, target))
return true;
return false;
}
double MapRendererAdventureFadingContext::objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const
{
if(objectID == target)
return progress;
return 1.0;
}
size_t MapRendererContext::selectOverlayImageForObject(const ObjectPosInfo & object) const
MapRendererAdventureMovingContext::MapRendererAdventureMovingContext(const MapRendererContextState & viewState)
: MapRendererAdventureContext(viewState)
{
}
size_t MapRendererAdventureMovingContext::objectGroupIndex(ObjectInstanceID objectID) const
{
if(target == objectID)
{
static const std::vector<size_t> moveGroups = {0, 10, 5, 6, 7, 8, 9, 12, 11};
return moveGroups[getObjectRotation(objectID)];
}
return MapRendererAdventureContext::objectGroupIndex(objectID);
}
bool MapRendererAdventureMovingContext::tileAnimated(const int3 & coordinates) const
{
if(!isInMap(coordinates))
return false;
auto objects = getObjects(coordinates);
if(vstd::contains(objects, target))
return true;
return false;
}
Point MapRendererAdventureMovingContext::objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const
{
if(target == objectID)
{
int3 offsetTilesFrom = tileFrom - coordinates;
int3 offsetTilesDest = tileDest - coordinates;
Point offsetPixelsFrom = Point(offsetTilesFrom) * Point(32, 32);
Point offsetPixelsDest = Point(offsetTilesDest) * Point(32, 32);
Point result = vstd::lerp(offsetPixelsFrom, offsetPixelsDest, progress);
return result;
}
return MapRendererAdventureContext::objectImageOffset(objectID, coordinates);
}
size_t MapRendererAdventureMovingContext::objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const
{
if(target != objectID)
return MapRendererAdventureContext::objectImageIndex(objectID, groupSize);
int32_t baseFrameTime = 50;
size_t frameCounter = animationTime / baseFrameTime;
size_t frameIndex = frameCounter % groupSize;
return frameIndex;
}
size_t MapRendererWorldViewContext::selectOverlayImageForObject(const ObjectPosInfo & object) const
{
size_t ownerIndex = PlayerColor::PLAYER_LIMIT.getNum() * static_cast<size_t>(EWorldViewIcon::ICONS_PER_PLAYER);
@ -368,19 +356,18 @@ size_t MapRendererContext::selectOverlayImageForObject(const ObjectPosInfo & obj
return std::numeric_limits<size_t>::max();
}
size_t MapRendererContext::overlayImageIndex(const int3 & coordinates) const
MapRendererWorldViewContext::MapRendererWorldViewContext(const MapRendererContextState & viewState)
: MapRendererBaseContext(viewState)
{
for(const auto & entry : additionalOverlayIcons)
{
if(entry.pos != coordinates)
continue;
}
size_t iconIndex = selectOverlayImageForObject(entry);
if(iconIndex != std::numeric_limits<size_t>::max())
return iconIndex;
}
bool MapRendererWorldViewContext::showOverlay() const
{
return true;
}
size_t MapRendererWorldViewContext::overlayImageIndex(const int3 & coordinates) const
{
if(!isVisible(coordinates))
return std::numeric_limits<size_t>::max();
@ -402,5 +389,93 @@ size_t MapRendererContext::overlayImageIndex(const int3 & coordinates) const
if(iconIndex != std::numeric_limits<size_t>::max())
return iconIndex;
}
return std::numeric_limits<size_t>::max();
}
MapRendererSpellViewContext::MapRendererSpellViewContext(const MapRendererContextState & viewState)
: MapRendererWorldViewContext(viewState)
{
}
double MapRendererSpellViewContext::objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const
{
if(showAllTerrain)
{
if(getObject(objectID)->isVisitable() && !MapRendererWorldViewContext::isVisible(coordinates))
return 0;
}
return MapRendererWorldViewContext::objectTransparency(objectID, coordinates);
}
bool MapRendererSpellViewContext::isVisible(const int3 & coordinates) const
{
if (showAllTerrain)
return isInMap(coordinates);
return MapRendererBaseContext::isVisible(coordinates);
}
size_t MapRendererSpellViewContext::overlayImageIndex(const int3 & coordinates) const
{
for(const auto & entry : additionalOverlayIcons)
{
if(entry.pos != coordinates)
continue;
size_t iconIndex = selectOverlayImageForObject(entry);
if(iconIndex != std::numeric_limits<size_t>::max())
return iconIndex;
}
return MapRendererWorldViewContext::overlayImageIndex(coordinates);
}
MapRendererPuzzleMapContext::MapRendererPuzzleMapContext(const MapRendererContextState & viewState)
: MapRendererBaseContext(viewState)
{
}
MapRendererPuzzleMapContext::~MapRendererPuzzleMapContext() = default;
const CGPath * MapRendererPuzzleMapContext::currentPath() const
{
return grailPos.get();
}
double MapRendererPuzzleMapContext::objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const
{
const auto * object = getObject(objectID);
if(!object)
return 0;
if(object->isVisitable())
return 0;
if(object->ID == Obj::HOLE)
return 0;
return MapRendererBaseContext::objectTransparency(objectID, coordinates);
}
bool MapRendererPuzzleMapContext::isVisible(const int3 & coordinates) const
{
return LOCPLINT->cb->isInTheMap(coordinates);
}
bool MapRendererPuzzleMapContext::filterGrayscale() const
{
return true;
}
bool MapRendererPuzzleMapContext::showRoads() const
{
return false;
}
bool MapRendererPuzzleMapContext::showRivers() const
{
return false;
}

View File

@ -11,101 +11,27 @@
#include "IMapRendererContext.h"
#include "../lib/int3.h"
#include "../lib/GameConstants.h"
#include "../lib/int3.h"
VCMI_LIB_NAMESPACE_BEGIN
struct ObjectPosInfo;
VCMI_LIB_NAMESPACE_END
class MapObjectsSorter
{
IMapRendererContext & context;
struct MapRendererContextState;
class MapRendererBaseContext : public IMapRendererContext
{
public:
explicit MapObjectsSorter(IMapRendererContext & context);
bool operator()(const ObjectInstanceID & left, const ObjectInstanceID & right) const;
bool operator()(const CGObjectInstance * left, const CGObjectInstance * right) const;
};
struct HeroAnimationState
{
ObjectInstanceID target;
int3 tileFrom;
int3 tileDest;
double progress;
};
struct FadingAnimationState
{
ObjectInstanceID target;
double progress;
};
// from VwSymbol.def
enum class EWorldViewIcon
{
TOWN = 0,
HERO = 1,
ARTIFACT = 2,
TELEPORT = 3,
GATE = 4,
MINE_WOOD = 5,
MINE_MERCURY = 6,
MINE_STONE = 7,
MINE_SULFUR = 8,
MINE_CRYSTAL = 9,
MINE_GEM = 10,
MINE_GOLD = 11,
RES_WOOD = 12,
RES_MERCURY = 13,
RES_STONE = 14,
RES_SULFUR = 15,
RES_CRYSTAL = 16,
RES_GEM = 17,
RES_GOLD = 18,
ICONS_PER_PLAYER = 19,
ICONS_TOTAL = 19 * 9 // 8 players + neutral set at the end
};
class MapRendererContext : public IMapRendererContext
{
friend class MapViewController;
boost::multi_array<MapObjectsList, 3> objects;
uint32_t animationTime = 0;
boost::optional<HeroAnimationState> movementAnimation;
boost::optional<HeroAnimationState> teleportAnimation;
boost::optional<FadingAnimationState> fadeOutAnimation;
boost::optional<FadingAnimationState> fadeInAnimation;
std::vector<ObjectPosInfo> additionalOverlayIcons;
bool worldViewModeActive = false;
bool showAllTerrain = false;
const MapRendererContextState & viewState;
bool settingsSessionSpectate = false;
bool settingsAdventureObjectAnimation = true;
bool settingsAdventureTerrainAnimation = true;
bool settingsSessionShowGrid = false;
bool settingsSessionShowVisitable = false;
bool settingsSessionShowBlockable = false;
size_t selectOverlayImageForObject(const ObjectPosInfo & objectID) const;
explicit MapRendererBaseContext(const MapRendererContextState & viewState);
public:
MapRendererContext();
uint32_t getObjectRotation(ObjectInstanceID objectID) const;
void addObject(const CGObjectInstance * object);
void addMovingObject(const CGObjectInstance * object, const int3 & tileFrom, const int3 & tileDest);
void removeObject(const CGObjectInstance * object);
int3 getMapSize() const final;
bool isInMap(const int3 & coordinates) const final;
int3 getMapSize() const override;
bool isInMap(const int3 & coordinates) const override;
bool isVisible(const int3 & coordinates) const override;
bool tileAnimated(const int3 & coordinates) const override;
@ -116,7 +42,7 @@ public:
size_t objectGroupIndex(ObjectInstanceID objectID) const override;
Point objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const override;
double objectTransparency(ObjectInstanceID objectID, const int3 &coordinates) const override;
double objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const override;
size_t objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const override;
size_t terrainImageIndex(size_t groupSize) const override;
size_t overlayImageIndex(const int3 & coordinates) const override;
@ -130,3 +56,94 @@ public:
bool showVisitable() const override;
bool showBlockable() const override;
};
class MapRendererAdventureContext : public MapRendererBaseContext
{
public:
uint32_t animationTime = 0;
bool settingShowGrid = false;
bool settingShowVisitable = false;
bool settingShowBlockable = false;
bool settingsAdventureObjectAnimation = true;
bool settingsAdventureTerrainAnimation = true;
explicit MapRendererAdventureContext(const MapRendererContextState & viewState);
const CGPath * currentPath() const override;
size_t objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const override;
size_t terrainImageIndex(size_t groupSize) const override;
bool showBorder() const override;
bool showGrid() const override;
bool showVisitable() const override;
bool showBlockable() const override;
};
class MapRendererAdventureFadingContext : public MapRendererAdventureContext
{
public:
ObjectInstanceID target;
double progress;
explicit MapRendererAdventureFadingContext(const MapRendererContextState & viewState);
bool tileAnimated(const int3 & coordinates) const override;
double objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const override;
};
class MapRendererAdventureMovingContext : public MapRendererAdventureContext
{
public:
ObjectInstanceID target;
int3 tileFrom;
int3 tileDest;
double progress;
explicit MapRendererAdventureMovingContext(const MapRendererContextState & viewState);
bool tileAnimated(const int3 & coordinates) const override;
size_t objectGroupIndex(ObjectInstanceID objectID) const override;
Point objectImageOffset(ObjectInstanceID objectID, const int3 & coordinates) const override;
size_t objectImageIndex(ObjectInstanceID objectID, size_t groupSize) const override;
};
class MapRendererWorldViewContext : public MapRendererBaseContext
{
protected:
size_t selectOverlayImageForObject(const ObjectPosInfo & object) const;
public:
explicit MapRendererWorldViewContext(const MapRendererContextState & viewState);
size_t overlayImageIndex(const int3 & coordinates) const override;
bool showOverlay() const override;
};
class MapRendererSpellViewContext : public MapRendererWorldViewContext
{
public:
std::vector<ObjectPosInfo> additionalOverlayIcons;
bool showAllTerrain = false;
explicit MapRendererSpellViewContext(const MapRendererContextState & viewState);
bool isVisible(const int3 & coordinates) const override;
double objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const override;
size_t overlayImageIndex(const int3 & coordinates) const override;
};
class MapRendererPuzzleMapContext : public MapRendererBaseContext
{
public:
std::unique_ptr<CGPath> grailPos;
explicit MapRendererPuzzleMapContext(const MapRendererContextState & viewState);
~MapRendererPuzzleMapContext();
const CGPath * currentPath() const override;
double objectTransparency(ObjectInstanceID objectID, const int3 & coordinates) const override;
bool isVisible(const int3 & coordinates) const override;
bool filterGrayscale() const override;
bool showRoads() const override;
bool showRivers() const override;
};

View File

@ -0,0 +1,93 @@
/*
* MapRendererContext.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 "MapRendererContextState.h"
#include "IMapRendererContext.h"
#include "mapHandler.h"
#include "../CGameInfo.h"
#include "../CPlayerInterface.h"
#include "../adventureMap/CAdvMapInt.h"
#include "../../CCallback.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapping/CMap.h"
static bool compareObjectBlitOrder(ObjectInstanceID left, ObjectInstanceID right)
{
//FIXME: remove mh access
return CGI->mh->compareObjectBlitOrder(CGI->mh->getMap()->objects[left.getNum()], CGI->mh->getMap()->objects[right.getNum()]);
}
MapRendererContextState::MapRendererContextState()
{
auto mapSize = LOCPLINT->cb->getMapSize();
objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
for(const auto & obj : CGI->mh->getMap()->objects)
addObject(obj);
}
void MapRendererContextState::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(LOCPLINT->cb->isInTheMap(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, compareObjectBlitOrder);
}
}
}
}
void MapRendererContextState::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(LOCPLINT->cb->isInTheMap(currTile))
{
auto & container = objects[currTile.z][currTile.x][currTile.y];
container.push_back(object->id);
boost::range::sort(container, compareObjectBlitOrder);
}
}
}
}
void MapRendererContextState::removeObject(const CGObjectInstance * object)
{
for(int z = 0; z < LOCPLINT->cb->getMapSize().z; z++)
for(int x = 0; x < LOCPLINT->cb->getMapSize().x; x++)
for(int y = 0; y < LOCPLINT->cb->getMapSize().y; y++)
vstd::erase(objects[z][x][y], object->id);
}

View File

@ -0,0 +1,62 @@
/*
* MapRendererContext.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
#include "../lib/int3.h"
#include "../lib/GameConstants.h"
VCMI_LIB_NAMESPACE_BEGIN
struct ObjectPosInfo;
class CGObjectInstance;
VCMI_LIB_NAMESPACE_END
class IMapRendererContext;
// from VwSymbol.def
enum class EWorldViewIcon
{
TOWN = 0,
HERO = 1,
ARTIFACT = 2,
TELEPORT = 3,
GATE = 4,
MINE_WOOD = 5,
MINE_MERCURY = 6,
MINE_STONE = 7,
MINE_SULFUR = 8,
MINE_CRYSTAL = 9,
MINE_GEM = 10,
MINE_GOLD = 11,
RES_WOOD = 12,
RES_MERCURY = 13,
RES_STONE = 14,
RES_SULFUR = 15,
RES_CRYSTAL = 16,
RES_GEM = 17,
RES_GOLD = 18,
ICONS_PER_PLAYER = 19,
ICONS_TOTAL = 19 * 9 // 8 players + neutral set at the end
};
struct MapRendererContextState
{
public:
MapRendererContextState();
using MapObject = ObjectInstanceID;
using MapObjectsList = std::vector<MapObject>;
boost::multi_array<MapObjectsList, 3> objects;
void addObject(const CGObjectInstance * object);
void addMovingObject(const CGObjectInstance * object, const int3 & tileFrom, const int3 & tileDest);
void removeObject(const CGObjectInstance * object);
};

View File

@ -32,9 +32,9 @@
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapping/CMap.h"
MapView::~MapView() = default;
BasicMapView::~BasicMapView() = default;
std::shared_ptr<MapViewModel> MapView::createModel(const Point & dimensions) const
std::shared_ptr<MapViewModel> BasicMapView::createModel(const Point & dimensions) const
{
auto result = std::make_shared<MapViewModel>();
@ -46,22 +46,18 @@ std::shared_ptr<MapViewModel> MapView::createModel(const Point & dimensions) con
return result;
}
MapView::MapView(const Point & offset, const Point & dimensions)
BasicMapView::BasicMapView(const Point & offset, const Point & dimensions)
: model(createModel(dimensions))
, controller(new MapViewController(model))
, tilesCache(new MapViewCache(model))
, isSwiping(false)
, controller(new MapViewController(model, tilesCache))
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
pos += offset;
pos.w = dimensions.x;
pos.h = dimensions.y;
actions = std::make_shared<MapViewActions>(*this, model);
actions->setContext(controller->getContext());
}
void MapView::render(Canvas & target, bool fullUpdate)
void BasicMapView::render(Canvas & target, bool fullUpdate)
{
Canvas targetClipped(target, pos);
@ -70,20 +66,35 @@ void MapView::render(Canvas & target, bool fullUpdate)
tilesCache->render(controller->getContext(), targetClipped, fullUpdate);
}
void MapView::show(SDL_Surface * to)
void BasicMapView::show(SDL_Surface * to)
{
Canvas target(to);
CSDL_Ext::CClipRectGuard guard(to, pos);
render(target, false);
}
void MapView::showAll(SDL_Surface * to)
void BasicMapView::showAll(SDL_Surface * to)
{
Canvas target(to);
CSDL_Ext::CClipRectGuard guard(to, pos);
render(target, true);
}
void MapView::show(SDL_Surface * to)
{
actions->setContext(controller->getContext());
BasicMapView::show(to);
}
MapView::MapView(const Point & offset, const Point & dimensions)
: BasicMapView(offset, dimensions)
, isSwiping(false)
{
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
actions = std::make_shared<MapViewActions>(*this, model);
actions->setContext(controller->getContext());
}
void MapView::onMapLevelSwitched()
{
if(LOCPLINT->cb->getMapSize().z > 1)
@ -124,6 +135,7 @@ void MapView::onCenteredObject(const CGObjectInstance * target)
void MapView::onViewSpellActivated(uint32_t tileSize, const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain)
{
controller->activateSpellViewContext();
controller->setTileSize(Point(tileSize, tileSize));
controller->setOverlayVisibility(objectPositions);
controller->setTerrainVisibility(showTerrain);
@ -131,12 +143,19 @@ void MapView::onViewSpellActivated(uint32_t tileSize, const std::vector<ObjectPo
void MapView::onViewWorldActivated(uint32_t tileSize)
{
controller->activateWorldViewContext();
controller->setTileSize(Point(tileSize, tileSize));
}
void MapView::onViewMapActivated()
{
controller->activateAdventureContext();
controller->setTileSize(Point(32, 32));
controller->setOverlayVisibility({});
controller->setTerrainVisibility(false);
}
PuzzleMapView::PuzzleMapView(const Point & offset, const Point & dimensions, const int3 & tileToCenter)
: BasicMapView(offset, dimensions)
{
controller->setViewCenter(tileToCenter);
controller->activatePuzzleMapContext(tileToCenter);
}

View File

@ -21,24 +21,38 @@ class MapViewController;
class MapViewModel;
class MapViewCache;
/// Main class that represents visible section of adventure map
/// Contains all public interface of view and translates calls to internal model
class MapView : public CIntObject
/// Internal class that contains logic shared between all map views
class BasicMapView : public CIntObject
{
protected:
std::shared_ptr<MapViewModel> model;
std::shared_ptr<MapViewCache> tilesCache;
std::shared_ptr<MapViewController> controller;
std::shared_ptr<MapViewActions> actions;
bool isSwiping;
std::shared_ptr<MapViewModel> createModel(const Point & dimensions) const;
void render(Canvas & target, bool fullUpdate);
public:
BasicMapView(const Point & offset, const Point & dimensions);
~BasicMapView() override;
void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override;
};
/// Main class that represents visible section of adventure map
/// Contains all public interface of view and translates calls to internal model
class MapView : public BasicMapView
{
std::shared_ptr<MapViewActions> actions;
bool isSwiping;
public:
void show(SDL_Surface * to) override;
MapView(const Point & offset, const Point & dimensions);
~MapView() override;
/// Moves current view to another level, preserving position
void onMapLevelSwitched();
@ -55,9 +69,7 @@ public:
/// Moves current view to specified tile
void onCenteredTile(const int3 & tile);
/// Centers view on object and starts "tracking" it
/// Whenever object changes position, so will the object
/// Tracking will be disabled on any call that moves view
/// Moves current view to specified object
void onCenteredObject(const CGObjectInstance * target);
/// Switches view to "View Earth" / "View Air" mode, displaying downscaled map with overlay
@ -68,7 +80,11 @@ public:
/// Switches view from View World mode back to standard view
void onViewMapActivated();
void show(SDL_Surface * to) override;
void showAll(SDL_Surface * to) override;
};
/// Main class that represents map view for puzzle map
class PuzzleMapView : public BasicMapView
{
public:
PuzzleMapView(const Point & offset, const Point & dimensions, const int3 & tileToCenter);
};

View File

@ -54,6 +54,33 @@ std::shared_ptr<IImage> MapViewCache::getOverlayImageForTile(const std::shared_p
return nullptr;
}
void MapViewCache::invalidate(const std::shared_ptr<IMapRendererContext> & context, const int3 & tile)
{
int cacheX = (terrainChecksum.shape()[0] + tile.x) % terrainChecksum.shape()[0];
int cacheY = (terrainChecksum.shape()[1] + tile.y) % terrainChecksum.shape()[1];
auto & entry = terrainChecksum[cacheX][cacheY];
if (entry.tileX == tile.x && entry.tileY ==tile.y)
entry = TileChecksum{};
}
void MapViewCache::invalidate(const std::shared_ptr<IMapRendererContext> & context, const ObjectInstanceID & object)
{
for (size_t cacheY = 0; cacheY < terrainChecksum.shape()[1]; ++cacheY)
{
for (size_t cacheX = 0; cacheX < terrainChecksum.shape()[0]; ++cacheX)
{
auto & entry = terrainChecksum[cacheX][cacheY];
int3 tile( entry.tileX, entry.tileY, cachedLevel);
if (context->isInMap(tile) && vstd::contains(context->getObjects(tile), object))
entry = TileChecksum{};
}
}
}
void MapViewCache::updateTile(const std::shared_ptr<IMapRendererContext> & context, const int3 & coordinates)
{
int cacheX = (terrainChecksum.shape()[0] + coordinates.x) % terrainChecksum.shape()[0];

View File

@ -17,6 +17,7 @@ class Canvas;
class MapRenderer;
class IMapRendererContext;
class MapViewModel;
class ObjectInstanceID;
/// Class responsible for rendering of entire map view
/// uses rendering parameters provided by owner class
@ -57,6 +58,9 @@ public:
explicit MapViewCache(const std::shared_ptr<MapViewModel> & model);
~MapViewCache();
void invalidate(const std::shared_ptr<IMapRendererContext> & context, const int3 & tile);
void invalidate(const std::shared_ptr<IMapRendererContext> & context, const ObjectInstanceID & object);
/// updates internal terrain cache according to provided time delta
void update(const std::shared_ptr<IMapRendererContext> & context);

View File

@ -12,14 +12,18 @@
#include "MapViewController.h"
#include "MapRendererContext.h"
#include "MapRendererContextState.h"
#include "MapViewModel.h"
#include "MapViewCache.h"
#include "../adventureMap/CAdvMapInt.h"
#include "../CPlayerInterface.h"
#include "../../lib/CConfigHandler.h"
#include "../../lib/mapObjects/CGHeroInstance.h"
#include "../../lib/mapObjects/MiscObjects.h"
#include "../../lib/spells/ViewSpellInt.h"
#include "../../lib/CPathfinder.h"
void MapViewController::setViewCenter(const int3 & position)
{
@ -32,7 +36,7 @@ void MapViewController::setViewCenter(const Point & position, int level)
Point upperLimit = Point(context->getMapSize()) * model->getSingleTileSize() + model->getSingleTileSize();
Point lowerLimit = Point(0,0);
if (context->worldViewModeActive)
if (worldViewContext)
{
Point area = model->getPixelsVisibleDimensions();
Point mapCenter = upperLimit / 2;
@ -74,10 +78,13 @@ void MapViewController::setTileSize(const Point & tileSize)
setViewCenter(model->getMapViewCenter(), model->getLevel());
}
MapViewController::MapViewController(std::shared_ptr<MapViewModel> model)
: context(new MapRendererContext())
MapViewController::MapViewController(std::shared_ptr<MapViewModel> model, std::shared_ptr<MapViewCache> view)
: state(new MapRendererContextState())
, model(std::move(model))
, view(view)
{
adventureContext = std::make_shared<MapRendererAdventureContext>(*state);
context = adventureContext;
}
std::shared_ptr<IMapRendererContext> MapViewController::getContext() const
@ -85,14 +92,6 @@ std::shared_ptr<IMapRendererContext> MapViewController::getContext() const
return context;
}
void MapViewController::moveFocusToSelection()
{
const auto * army = adventureInt->curArmy();
if (army)
setViewCenter(army->getSightCenter());
}
void MapViewController::update(uint32_t timeDelta)
{
// confirmed to match H3 for
@ -103,13 +102,13 @@ void MapViewController::update(uint32_t timeDelta)
// - teleporting ( 250 ms)
static const double fadeOutDuration = 500;
static const double fadeInDuration = 500;
static const double heroTeleportDuration = 250;
//static const double heroTeleportDuration = 250;
//FIXME: remove code duplication?
if(context->movementAnimation)
if(movementContext)
{
const auto * object = context->getObject(context->movementAnimation->target);
const auto * object = context->getObject(movementContext->target);
const auto * hero = dynamic_cast<const CGHeroInstance*>(object);
const auto * boat = dynamic_cast<const CGBoat*>(object);
@ -118,172 +117,360 @@ void MapViewController::update(uint32_t timeDelta)
if (!hero)
hero = boat->hero;
// TODO: enemyMoveTime
double heroMoveTime = settings["adventure"]["heroMoveTime"].Float();
double heroMoveTime =
LOCPLINT->makingTurn ?
settings["adventure"]["heroMoveTime"].Float():
settings["adventure"]["enemyMoveTime"].Float();
context->movementAnimation->progress += timeDelta / heroMoveTime;
movementContext->progress += timeDelta / heroMoveTime;
Point positionFrom = Point(hero->convertToVisitablePos(context->movementAnimation->tileFrom)) * model->getSingleTileSize() + model->getSingleTileSize() / 2;
Point positionDest = Point(hero->convertToVisitablePos(context->movementAnimation->tileDest)) * model->getSingleTileSize() + model->getSingleTileSize() / 2;
Point positionFrom = Point(hero->convertToVisitablePos(movementContext->tileFrom)) * model->getSingleTileSize() + model->getSingleTileSize() / 2;
Point positionDest = Point(hero->convertToVisitablePos(movementContext->tileDest)) * model->getSingleTileSize() + model->getSingleTileSize() / 2;
Point positionCurr = vstd::lerp(positionFrom, positionDest, context->movementAnimation->progress);
Point positionCurr = vstd::lerp(positionFrom, positionDest, movementContext->progress);
if(context->movementAnimation->progress >= 1.0)
if(movementContext->progress >= 1.0)
{
setViewCenter(hero->getSightCenter());
context->removeObject(context->getObject(context->movementAnimation->target));
context->addObject(context->getObject(context->movementAnimation->target));
context->movementAnimation.reset();
removeObject(context->getObject(movementContext->target));
addObject(context->getObject(movementContext->target));
activateAdventureContext(movementContext->animationTime);
}
else
{
setViewCenter(positionCurr, context->movementAnimation->tileDest.z);
setViewCenter(positionCurr, movementContext->tileDest.z);
}
}
if(context->teleportAnimation)
{
context->teleportAnimation->progress += timeDelta / heroTeleportDuration;
moveFocusToSelection();
if(context->teleportAnimation->progress >= 1.0)
context->teleportAnimation.reset();
}
//if(teleportContext)
//{
// teleportContext->progress += timeDelta / heroTeleportDuration;
// moveFocusToSelection();
// if(teleportContext->progress >= 1.0)
// teleportContext.reset();
//}
if(context->fadeOutAnimation)
if(fadingOutContext)
{
context->fadeOutAnimation->progress += timeDelta / fadeOutDuration;
moveFocusToSelection();
if(context->fadeOutAnimation->progress >= 1.0)
fadingOutContext->progress -= timeDelta / fadeOutDuration;
if(fadingOutContext->progress <= 0.0)
{
context->removeObject(context->getObject(context->fadeOutAnimation->target));
context->fadeOutAnimation.reset();
removeObject(context->getObject(fadingOutContext->target));
activateAdventureContext(fadingOutContext->animationTime);
}
}
if(context->fadeInAnimation)
if(fadingInContext)
{
context->fadeInAnimation->progress += timeDelta / fadeInDuration;
moveFocusToSelection();
if(context->fadeInAnimation->progress >= 1.0)
context->fadeInAnimation.reset();
fadingInContext->progress += timeDelta / fadeInDuration;
if(fadingInContext->progress >= 1.0)
{
activateAdventureContext(fadingInContext->animationTime);
}
}
context->animationTime += timeDelta;
context->worldViewModeActive = model->getSingleTileSize() != Point(32,32);
context->settingsSessionSpectate = settings["session"]["spectate"].Bool();
context->settingsAdventureObjectAnimation = settings["adventure"]["objectAnimation"].Bool();
context->settingsAdventureTerrainAnimation = settings["adventure"]["terrainAnimation"].Bool();
context->settingsSessionShowGrid = settings["gameTweaks"]["showGrid"].Bool();
context->settingsSessionShowVisitable = settings["session"]["showVisitable"].Bool();
context->settingsSessionShowBlockable = settings["session"]["showBlockable"].Bool();
if (adventureContext)
{
adventureContext->animationTime += timeDelta;
adventureContext->settingsSessionSpectate = settings["session"]["spectate"].Bool();
adventureContext->settingsAdventureObjectAnimation = settings["adventure"]["objectAnimation"].Bool();
adventureContext->settingsAdventureTerrainAnimation = settings["adventure"]["terrainAnimation"].Bool();
adventureContext->settingShowGrid = settings["gameTweaks"]["showGrid"].Bool();
adventureContext->settingShowVisitable = settings["session"]["showVisitable"].Bool();
adventureContext->settingShowBlockable = settings["session"]["showBlockable"].Bool();
}
}
void MapViewController::onObjectFadeIn(const CGObjectInstance * obj)
bool MapViewController::isEventVisible(const CGObjectInstance * obj)
{
bool actionVisible = context->isVisible(obj->pos);
if (adventureContext == nullptr)
return false;
assert(!context->fadeInAnimation);
if (!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() < 0)
return false; // enemy move speed set to "hidden/none"
if (actionVisible)
context->fadeInAnimation = FadingAnimationState{obj->id, 0.0};
context->addObject(obj);
}
void MapViewController::onObjectFadeOut(const CGObjectInstance * obj)
{
bool actionVisible = context->isVisible(obj->pos);
assert(!context->fadeOutAnimation);
if (actionVisible)
context->fadeOutAnimation = FadingAnimationState{obj->id, 0.0};
if (obj->isVisitable())
return context->isVisible(obj->visitablePos());
else
context->removeObject(obj);
return context->isVisible(obj->pos);
}
void MapViewController::onObjectInstantAdd(const CGObjectInstance * obj)
bool MapViewController::isEventVisible(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
context->addObject(obj);
};
if (adventureContext == nullptr)
return false;
void MapViewController::onObjectInstantRemove(const CGObjectInstance * obj)
{
context->removeObject(obj);
};
if (!LOCPLINT->makingTurn && settings["adventure"]["enemyMoveTime"].Float() < 0)
return false; // enemy move speed set to "hidden/none"
void MapViewController::onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
assert(!context->teleportAnimation);
bool actionVisible = context->isVisible(from) || context->isVisible(dest);
if (actionVisible)
{
context->teleportAnimation = HeroAnimationState{obj->id, from, dest, 0.0};
}
else
{
context->removeObject(obj);
context->addObject(obj);
}
}
void MapViewController::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
assert(!context->movementAnimation);
bool actionVisible = context->isVisible(from) || context->isVisible(dest);
const CGObjectInstance * movingObject = obj;
if(obj->boat)
movingObject = obj->boat;
context->removeObject(movingObject);
if(settings["adventure"]["heroMoveTime"].Float() > 1 && actionVisible)
{
context->addMovingObject(movingObject, from, dest);
context->movementAnimation = HeroAnimationState{movingObject->id, from, dest, 0.0};
}
else
{
// instant movement
context->addObject(movingObject);
if (actionVisible)
setViewCenter(movingObject->visitablePos());
}
}
void MapViewController::onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
//TODO. Or no-op?
}
bool MapViewController::hasOngoingAnimations()
{
if(context->movementAnimation)
if (context->isVisible(obj->convertToVisitablePos(from)))
return true;
if(context->teleportAnimation)
return true;
if(context->fadeOutAnimation)
return true;
if(context->fadeInAnimation)
if (context->isVisible(obj->convertToVisitablePos(dest)))
return true;
return false;
}
void MapViewController::fadeOutObject(const CGObjectInstance * obj)
{
fadingOutContext = std::make_shared<MapRendererAdventureFadingContext>(*state);
fadingOutContext->animationTime = adventureContext->animationTime;
adventureContext = fadingOutContext;
context = fadingOutContext;
fadingOutContext->target = obj->id;
fadingOutContext->progress = 1.0;
}
void MapViewController::fadeInObject(const CGObjectInstance * obj)
{
fadingInContext = std::make_shared<MapRendererAdventureFadingContext>(*state);
fadingInContext->animationTime = adventureContext->animationTime;
adventureContext = fadingInContext;
context = fadingInContext;
fadingInContext->target = obj->id;
fadingInContext->progress = 0.0;
}
void MapViewController::removeObject(const CGObjectInstance * obj)
{
view->invalidate(context, obj->id);
state->removeObject(obj);
}
void MapViewController::addObject(const CGObjectInstance * obj)
{
state->addObject(obj);
view->invalidate(context, obj->id);
}
void MapViewController::onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
if (isEventVisible(obj, from, dest))
{
onObjectFadeOut(obj);
setViewCenter(obj->getSightCenter());
}
else
removeObject(obj);
}
void MapViewController::onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
if (isEventVisible(obj, from, dest))
setViewCenter(obj->getSightCenter());
}
void MapViewController::onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
if (isEventVisible(obj, from, dest))
setViewCenter(obj->getSightCenter());
}
void MapViewController::onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
if (isEventVisible(obj, from, dest))
{
onObjectFadeIn(obj);
setViewCenter(obj->getSightCenter());
}
addObject(obj);
}
void MapViewController::onObjectFadeIn(const CGObjectInstance * obj)
{
assert(!hasOngoingAnimations());
if (isEventVisible(obj))
fadeInObject(obj);
addObject(obj);
}
void MapViewController::onObjectFadeOut(const CGObjectInstance * obj)
{
assert(!hasOngoingAnimations());
if (isEventVisible(obj))
fadeOutObject(obj);
else
removeObject(obj);
}
void MapViewController::onObjectInstantAdd(const CGObjectInstance * obj)
{
addObject(obj);
};
void MapViewController::onObjectInstantRemove(const CGObjectInstance * obj)
{
removeObject(obj);
};
void MapViewController::onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
assert(!hasOngoingAnimations());
if (isEventVisible(obj, from, dest))
{
// TODO: generate view with old state
setViewCenter(obj->getSightCenter());
}
}
void MapViewController::onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
assert(!hasOngoingAnimations());
if (isEventVisible(obj, from, dest))
{
// TODO: animation
setViewCenter(obj->getSightCenter());
}
else
{
removeObject(obj);
addObject(obj);
}
}
void MapViewController::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
assert(!hasOngoingAnimations());
const CGObjectInstance * movingObject = obj;
if(obj->boat)
movingObject = obj->boat;
removeObject(movingObject);
if (!isEventVisible(obj, from, dest))
{
addObject(movingObject);
return;
}
double movementTime =
LOCPLINT->playerID == obj->tempOwner ?
settings["adventure"]["heroMoveTime"].Float():
settings["adventure"]["enemyMoveTime"].Float();
if(movementTime > 1)
{
movementContext = std::make_shared<MapRendererAdventureMovingContext>(*state);
movementContext->animationTime = adventureContext->animationTime;
adventureContext = movementContext;
context = movementContext;
state->addMovingObject(movingObject, from, dest);
movementContext->target = movingObject->id;
movementContext->tileFrom = from;
movementContext->tileDest = dest;
movementContext->progress = 0.0;
}
else // instant movement
{
addObject(movingObject);
setViewCenter(movingObject->visitablePos());
}
}
bool MapViewController::hasOngoingAnimations()
{
if(movementContext)
return true;
if(fadingOutContext)
return true;
if(fadingInContext)
return true;
return false;
}
void MapViewController::activateAdventureContext(uint32_t animationTime)
{
resetContext();
adventureContext = std::make_shared<MapRendererAdventureContext>(*state);
adventureContext->animationTime = animationTime;
context = adventureContext;
}
void MapViewController::activateAdventureContext()
{
activateAdventureContext(0);
}
void MapViewController::activateWorldViewContext()
{
if (worldViewContext)
return;
resetContext();
worldViewContext = std::make_shared<MapRendererWorldViewContext>(*state);
context = worldViewContext;
}
void MapViewController::activateSpellViewContext()
{
if (spellViewContext)
return;
resetContext();
spellViewContext = std::make_shared<MapRendererSpellViewContext>(*state);
worldViewContext = spellViewContext;
context = spellViewContext;
}
void MapViewController::activatePuzzleMapContext(const int3 & grailPosition)
{
resetContext();
puzzleMapContext = std::make_shared<MapRendererPuzzleMapContext>(*state);
context = puzzleMapContext;
CGPathNode fakeNode;
fakeNode.coord = grailPosition;
puzzleMapContext->grailPos = std::make_unique<CGPath>();
// create two nodes since 1st one is normally not visible
puzzleMapContext->grailPos->nodes.push_back(fakeNode);
puzzleMapContext->grailPos->nodes.push_back(fakeNode);
}
void MapViewController::resetContext()
{
adventureContext.reset();
movementContext.reset();
fadingOutContext.reset();
fadingInContext.reset();
worldViewContext.reset();
spellViewContext.reset();
puzzleMapContext.reset();
}
void MapViewController::setTerrainVisibility(bool showAllTerrain)
{
context->showAllTerrain = showAllTerrain;
assert(spellViewContext);
spellViewContext->showAllTerrain = showAllTerrain;
}
void MapViewController::setOverlayVisibility(const std::vector<ObjectPosInfo> & objectPositions)
{
context->additionalOverlayIcons = objectPositions;
assert(spellViewContext);
spellViewContext->additionalOverlayIcons = objectPositions;
}

View File

@ -16,31 +16,66 @@ class Point;
struct ObjectPosInfo;
VCMI_LIB_NAMESPACE_END
struct MapRendererContextState;
class MapViewCache;
class MapViewModel;
class IMapRendererContext;
class MapRendererContext;
class MapRendererAdventureContext;
class MapRendererAdventureFadingContext;
class MapRendererAdventureMovingContext;
class MapRendererWorldViewContext;
class MapRendererSpellViewContext;
class MapRendererPuzzleMapContext;
/// 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<IMapRendererContext> context;
std::shared_ptr<MapRendererContextState> state;
std::shared_ptr<MapViewModel> model;
std::shared_ptr<MapViewCache> view;
// all potential contexts for view
// only some are present at any given moment, rest are nullptr's
std::shared_ptr<MapRendererAdventureContext> adventureContext;
std::shared_ptr<MapRendererAdventureMovingContext> movementContext;
//std::shared_ptr<IMapRendererContext> teleportContext;
std::shared_ptr<MapRendererAdventureFadingContext> fadingOutContext;
std::shared_ptr<MapRendererAdventureFadingContext> fadingInContext;
std::shared_ptr<MapRendererWorldViewContext> worldViewContext;
std::shared_ptr<MapRendererSpellViewContext> spellViewContext;
std::shared_ptr<MapRendererPuzzleMapContext> puzzleMapContext;
private:
bool isEventVisible(const CGObjectInstance * obj);
bool isEventVisible(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
void fadeOutObject(const CGObjectInstance * obj);
void fadeInObject(const CGObjectInstance * obj);
void removeObject(const CGObjectInstance * obj);
void addObject(const CGObjectInstance * obj);
// 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 onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onBeforeHeroTeleported(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;
void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void resetContext();
void moveFocusToSelection();
public:
explicit MapViewController(std::shared_ptr<MapViewModel> model);
MapViewController(std::shared_ptr<MapViewModel> model, std::shared_ptr<MapViewCache> view);
std::shared_ptr<IMapRendererContext> getContext() const;
@ -49,6 +84,12 @@ public:
void setTileSize(const Point & tileSize);
void update(uint32_t timeDelta);
void activateAdventureContext(uint32_t animationTime);
void activateAdventureContext();
void activateWorldViewContext();
void activateSpellViewContext();
void activatePuzzleMapContext(const int3 & grailPosition);
void setTerrainVisibility(bool showAllTerrain);
void setOverlayVisibility(const std::vector<ObjectPosInfo> & objectPositions);

View File

@ -22,49 +22,6 @@
#include "../../lib/CGeneralTextHandler.h"
#include "../../lib/TerrainHandler.h"
/*
void CMapPuzzleViewBlitter::drawObjects(SDL_Surface * targetSurf, const TerrainTile2 & tile) const
{
CMapBlitter::drawObjects(targetSurf, tile);
// grail X mark
if(pos.x == info->grailPos.x && pos.y == info->grailPos.y)
{
const auto mark = graphics->heroMoveArrows->getImage(0);
mark->draw(targetSurf,realTileRect.x,realTileRect.y);
}
}
*/
/*
void CMapPuzzleViewBlitter::postProcessing(SDL_Surface * targetSurf) const
{
CSDL_Ext::applyEffect(targetSurf, info->drawBounds, static_cast<int>(!ADVOPT.puzzleSepia));
}
*/
/*
bool CMapPuzzleViewBlitter::canDrawObject(const CGObjectInstance * obj) const
{
if (!CMapBlitter::canDrawObject(obj))
return false;
//don't print flaggable objects in puzzle mode
if (obj->isVisitable())
return false;
if(std::find(unblittableObjects.begin(), unblittableObjects.end(), obj->ID) != unblittableObjects.end())
return false;
return true;
}
*/
/*
CMapPuzzleViewBlitter::CMapPuzzleViewBlitter(CMapHandler * parent)
: CMapNormalBlitter(parent)
{
unblittableObjects.push_back(Obj::HOLE);
}
*/
bool CMapHandler::hasOngoingAnimations()
{
for (auto * observer : observers)
@ -196,6 +153,30 @@ void CMapHandler::onObjectFadeOut(const CGObjectInstance * obj)
observer->onObjectFadeOut(obj);
}
void CMapHandler::onBeforeHeroEmbark(const CGHeroInstance *obj, const int3 &from, const int3 &dest)
{
for (auto * observer : observers)
observer->onBeforeHeroEmbark(obj, from, dest);
}
void CMapHandler::onAfterHeroEmbark(const CGHeroInstance *obj, const int3 &from, const int3 &dest)
{
for (auto * observer : observers)
observer->onAfterHeroEmbark(obj, from, dest);
}
void CMapHandler::onBeforeHeroDisembark(const CGHeroInstance *obj, const int3 &from, const int3 &dest)
{
for (auto * observer : observers)
observer->onBeforeHeroDisembark(obj, from, dest);
}
void CMapHandler::onAfterHeroDisembark(const CGHeroInstance *obj, const int3 &from, const int3 &dest)
{
for (auto * observer : observers)
observer->onAfterHeroDisembark(obj, from, dest);
}
void CMapHandler::onObjectInstantAdd(const CGObjectInstance * obj)
{
for (auto * observer : observers)
@ -208,11 +189,18 @@ void CMapHandler::onObjectInstantRemove(const CGObjectInstance * obj)
observer->onObjectInstantRemove(obj);
}
void CMapHandler::onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
void CMapHandler::onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
assert(obj->pos == dest);
for (auto * observer : observers)
observer->onHeroTeleported(obj, from, dest);
observer->onAfterHeroTeleported(obj, from, dest);
}
void CMapHandler::onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
assert(obj->pos == from);
for (auto * observer : observers)
observer->onBeforeHeroTeleported(obj, from, dest);
}
void CMapHandler::onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
@ -222,13 +210,6 @@ void CMapHandler::onHeroMoved(const CGHeroInstance * obj, const int3 & from, con
observer->onHeroMoved(obj, from, dest);
}
void CMapHandler::onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest)
{
assert(obj->pos == from);
for (auto * observer : observers)
observer->onHeroRotated(obj, from, dest);
}
void CMapHandler::addMapObserver(IMapObjectObserver * object)
{
observers.push_back(object);

View File

@ -59,9 +59,13 @@ public:
void onObjectFadeOut(const CGObjectInstance * obj);
void onObjectInstantAdd(const CGObjectInstance * obj);
void onObjectInstantRemove(const CGObjectInstance * obj);
void onHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
void onHeroRotated(const CGHeroInstance * obj, const int3 & from, const int3 & dest);
/// Add object to receive notifications on any changes in visible map state
void addMapObserver(IMapObjectObserver * observer);

View File

@ -16,6 +16,7 @@
#include "../adventureMap/CResDataBar.h"
#include "../gui/CGuiHandler.h"
#include "../gui/TextAlignment.h"
#include "../mapRenderer/MapView.h"
#include "../widgets/Buttons.h"
#include "../widgets/Images.h"
#include "../widgets/TextControls.h"
@ -38,6 +39,8 @@ CPuzzleWindow::CPuzzleWindow(const int3 & GrailPos, double discoveredRatio)
quitb->assignedKeys.insert(SDLK_ESCAPE);
quitb->setBorderColor(Colors::METALLIC_GOLD);
mapView = std::make_shared<PuzzleMapView>(Point(8,9), Point(591, 544), grailPos);
logo = std::make_shared<CPicture>("PUZZLOGO", 607, 3);
title = std::make_shared<CLabel>(700, 95, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, CGI->generaltexth->allTexts[463]);
resDataBar = std::make_shared<CResDataBar>("ARESBAR.bmp", 3, 575, 32, 2, 85, 85);
@ -68,16 +71,6 @@ CPuzzleWindow::CPuzzleWindow(const int3 & GrailPos, double discoveredRatio)
void CPuzzleWindow::showAll(SDL_Surface * to)
{
assert(0);
//int3 moveInt = int3(8, 9, 0);
//Rect mapRect = Rect(Point(pos.x + 8, pos.y + 7), Point(544, 591));
//int3 topTile = grailPos - moveInt;
//MapDrawingInfo info(topTile, LOCPLINT->cb->getVisibilityMap(), mapRect);
//info.puzzleMode = true;
//info.grailPos = grailPos;
//CGI->mh->drawTerrainRectNew(to, &info);
CWindowObject::showAll(to);
}

View File

@ -15,12 +15,14 @@
class CLabel;
class CButton;
class CResDataBar;
class PuzzleMapView;
/// Puzzle screen which gets uncovered when you visit obilisks
class CPuzzleWindow : public CWindowObject
{
private:
int3 grailPos;
std::shared_ptr<PuzzleMapView> mapView;
std::shared_ptr<CPicture> logo;
std::shared_ptr<CLabel> title;
std::shared_ptr<CButton> quitb;

View File

@ -138,7 +138,7 @@
"transitionRequired" : true,
"terrainViewPatterns" : "water",
"horseSound" : "horse08",
"horseSoundPenalty" : "horse28",
"horseSoundPenalty" : "horse08",
"sounds": {
"ambient": ["LOOPOCEA"]
}

View File

@ -593,8 +593,6 @@ struct DLL_LINKAGE TryMoveHero : public CPackForClient
std::unordered_set<int3, ShashInt3> fowRevealed; //revealed tiles
boost::optional<int3> attackedFrom; // Set when stepping into endangered tile.
bool humanKnows = false; //used locally during applying to client
virtual void visitTyped(ICPackVisitor & visitor) override;
bool stopMovement() const

View File

@ -21,7 +21,6 @@
#include "../lib/CHeroHandler.h"
#include "../lib/CTownHandler.h"
#include "../lib/CModHandler.h"
#include "../lib/mapping/CMap.h"
#include "../lib/GameConstants.h"
#include "../lib/JsonDetail.h"