mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Initial WIP of adventure map rendering rewrite
This commit is contained in:
parent
84fa19dadf
commit
bb6e1f7ee1
@ -11,6 +11,8 @@ set(client_SRCS
|
||||
adventureMap/CMinimap.cpp
|
||||
adventureMap/CResDataBar.cpp
|
||||
adventureMap/CTerrainRect.cpp
|
||||
adventureMap/MapRenderer.cpp
|
||||
adventureMap/MapView.cpp
|
||||
adventureMap/mapHandler.cpp
|
||||
|
||||
battle/BattleActionsController.cpp
|
||||
@ -121,6 +123,9 @@ set(client_HEADERS
|
||||
adventureMap/CMinimap.h
|
||||
adventureMap/CResDataBar.h
|
||||
adventureMap/CTerrainRect.h
|
||||
adventureMap/MapRenderer.h
|
||||
adventureMap/MapRendererContext.h
|
||||
adventureMap/MapView.h
|
||||
adventureMap/mapHandler.h
|
||||
|
||||
battle/BattleActionsController.h
|
||||
|
@ -475,19 +475,6 @@ void CPlayerInterface::openTownWindow(const CGTownInstance * town)
|
||||
GH.pushInt(newCastleInt);
|
||||
}
|
||||
|
||||
int3 CPlayerInterface::repairScreenPos(int3 pos)
|
||||
{
|
||||
if (pos.x<-CGI->mh->frameW)
|
||||
pos.x = -CGI->mh->frameW;
|
||||
if (pos.y<-CGI->mh->frameH)
|
||||
pos.y = -CGI->mh->frameH;
|
||||
if (pos.x>CGI->mh->sizes.x - adventureInt->terrain->tilesw + CGI->mh->frameW)
|
||||
pos.x = CGI->mh->sizes.x - adventureInt->terrain->tilesw + CGI->mh->frameW;
|
||||
if (pos.y>CGI->mh->sizes.y - adventureInt->terrain->tilesh + CGI->mh->frameH)
|
||||
pos.y = CGI->mh->sizes.y - adventureInt->terrain->tilesh + CGI->mh->frameH;
|
||||
return pos;
|
||||
}
|
||||
|
||||
void CPlayerInterface::activateForSpectator()
|
||||
{
|
||||
adventureInt->state = CAdvMapInt::INGAME;
|
||||
@ -1719,8 +1706,8 @@ void CPlayerInterface::movementPxStep( const TryMoveHero &details, int i, const
|
||||
}
|
||||
}
|
||||
|
||||
adventureInt->terrain->moveX = (32 - i) * (heroImageNewX - heroImageOldX) / 32;
|
||||
adventureInt->terrain->moveY = (32 - i) * (heroImageNewY - heroImageOldY) / 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 )
|
||||
|
@ -220,7 +220,6 @@ public:
|
||||
void openHeroWindow(const CGHeroInstance * hero); //shows hero window with given hero
|
||||
void updateInfo(const CGObjectInstance * specific);
|
||||
void initGameInterface(std::shared_ptr<Environment> ENV, std::shared_ptr<CCallback> CB) override;
|
||||
int3 repairScreenPos(int3 pos); //returns position closest to pos we can center screen on
|
||||
void activateForSpectator(); // TODO: spectator probably need own player interface class
|
||||
|
||||
// show dialogs
|
||||
|
@ -90,11 +90,11 @@ CAdvMapInt::CAdvMapInt():
|
||||
resdatabar(new CResDataBar),
|
||||
terrain(new CTerrainRect),
|
||||
state(NA),
|
||||
spellBeingCasted(nullptr), position(int3(0, 0, 0)), selection(nullptr),
|
||||
spellBeingCasted(nullptr), selection(nullptr),
|
||||
redrawOnNextFrame(false), anim(0), animValHitCount(0), heroAnim(0), heroAnimValHitCount(0),
|
||||
activeMapPanel(nullptr), duringAITurn(false), scrollingDir(0), scrollingState(false),
|
||||
swipeEnabled(settings["general"]["swipe"].Bool()), swipeMovementRequested(false),
|
||||
swipeTargetPosition(int3(-1, -1, -1))
|
||||
swipeTargetPosition(Point(0, 0))
|
||||
{
|
||||
pos.x = pos.y = 0;
|
||||
pos.w = GH.screenDimensions().x;
|
||||
@ -285,16 +285,16 @@ void CAdvMapInt::fswitchLevel()
|
||||
if (maxLevels < 2)
|
||||
return;
|
||||
|
||||
position.z = (position.z + 1) % maxLevels;
|
||||
terrain->setLevel((terrain->getLevel() + 1) % maxLevels);
|
||||
|
||||
underground->setIndex(position.z, true);
|
||||
underground->setIndex(terrain->getLevel(), true);
|
||||
underground->redraw();
|
||||
|
||||
worldViewUnderground->setIndex(position.z, true);
|
||||
worldViewUnderground->setIndex(terrain->getLevel(), true);
|
||||
worldViewUnderground->redraw();
|
||||
|
||||
redrawOnNextFrame = true;
|
||||
minimap->setLevel(position.z);
|
||||
minimap->setLevel(terrain->getLevel());
|
||||
|
||||
if (mode == EAdvMapMode::WORLD_VIEW)
|
||||
terrain->redraw();
|
||||
@ -520,8 +520,6 @@ void CAdvMapInt::showAll(SDL_Surface * to)
|
||||
infoBar->showAll(to);
|
||||
break;
|
||||
case EAdvMapMode::WORLD_VIEW:
|
||||
|
||||
terrain->showAll(to);
|
||||
break;
|
||||
}
|
||||
activeMapPanel->showAll(to);
|
||||
@ -594,25 +592,15 @@ void CAdvMapInt::show(SDL_Surface * to)
|
||||
}
|
||||
if(redrawOnNextFrame)
|
||||
{
|
||||
int3 betterPos = LOCPLINT->repairScreenPos(position);
|
||||
if (betterPos != position)
|
||||
{
|
||||
logGlobal->warn("Incorrect position for adventure map!");
|
||||
position = betterPos;
|
||||
}
|
||||
|
||||
terrain->show(to);
|
||||
for(int i = 0; i < 4; i++)
|
||||
gems[i]->showAll(to);
|
||||
redrawOnNextFrame=false;
|
||||
LOCPLINT->cingconsole->show(to);
|
||||
}
|
||||
else
|
||||
{
|
||||
terrain->showAnim(to);
|
||||
for(int i = 0; i < 4; i++)
|
||||
gems[i]->showAll(to);
|
||||
}
|
||||
|
||||
terrain->show(to);
|
||||
for(int i = 0; i < 4; i++)
|
||||
gems[i]->showAll(to);
|
||||
|
||||
infoBar->show(to);
|
||||
statusbar->showAll(to);
|
||||
@ -624,26 +612,22 @@ void CAdvMapInt::handleMapScrollingUpdate()
|
||||
//if advmap needs updating AND (no dialog is shown OR ctrl is pressed)
|
||||
if((animValHitCount % (4 / scrollSpeed)) == 0)
|
||||
{
|
||||
if((scrollingDir & LEFT) && (position.x > -CGI->mh->frameW))
|
||||
position.x--;
|
||||
if(scrollingDir & LEFT)
|
||||
terrain->moveViewBy(Point(-4, 0));
|
||||
|
||||
if((scrollingDir & RIGHT) && (position.x < CGI->mh->map->width - CGI->mh->tilesW + CGI->mh->frameW))
|
||||
position.x++;
|
||||
if(scrollingDir & RIGHT)
|
||||
terrain->moveViewBy(Point(+4, 0));
|
||||
|
||||
if((scrollingDir & UP) && (position.y > -CGI->mh->frameH))
|
||||
position.y--;
|
||||
if(scrollingDir & UP)
|
||||
terrain->moveViewBy(Point(0, -4));
|
||||
|
||||
if((scrollingDir & DOWN) && (position.y < CGI->mh->map->height - CGI->mh->tilesH + CGI->mh->frameH))
|
||||
position.y++;
|
||||
if(scrollingDir & DOWN)
|
||||
terrain->moveViewBy(Point(0, +4));
|
||||
|
||||
if(scrollingDir)
|
||||
{
|
||||
setScrollingCursor(scrollingDir);
|
||||
scrollingState = true;
|
||||
redrawOnNextFrame = true;
|
||||
minimap->redraw();
|
||||
if(mode == EAdvMapMode::WORLD_VIEW)
|
||||
terrain->redraw();
|
||||
}
|
||||
else if(scrollingState)
|
||||
{
|
||||
@ -657,9 +641,7 @@ void CAdvMapInt::handleSwipeUpdate()
|
||||
{
|
||||
if(swipeMovementRequested)
|
||||
{
|
||||
auto fixedPos = LOCPLINT->repairScreenPos(swipeTargetPosition);
|
||||
position.x = fixedPos.x;
|
||||
position.y = fixedPos.y;
|
||||
terrain->setViewCenter(swipeTargetPosition, terrain->getLevel());
|
||||
CCS->curh->set(Cursor::Map::POINTER);
|
||||
redrawOnNextFrame = true;
|
||||
minimap->redraw();
|
||||
@ -676,37 +658,22 @@ void CAdvMapInt::selectionChanged()
|
||||
|
||||
void CAdvMapInt::centerOn(int3 on, bool fade)
|
||||
{
|
||||
bool switchedLevels = on.z != position.z;
|
||||
bool switchedLevels = on.z != terrain->getLevel();
|
||||
|
||||
if (fade)
|
||||
{
|
||||
terrain->fadeFromCurrentView();
|
||||
}
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
default:
|
||||
case EAdvMapMode::NORMAL:
|
||||
on.x -= CGI->mh->frameW; // is this intentional? frame size doesn't really have to correspond to camera size...
|
||||
on.y -= CGI->mh->frameH;
|
||||
break;
|
||||
case EAdvMapMode::WORLD_VIEW:
|
||||
on.x -= static_cast<si32>(CGI->mh->tilesW / 2 / worldViewScale);
|
||||
on.y -= static_cast<si32>(CGI->mh->tilesH / 2 / worldViewScale);
|
||||
break;
|
||||
}
|
||||
terrain->setViewCenter(on);
|
||||
|
||||
|
||||
on = LOCPLINT->repairScreenPos(on);
|
||||
|
||||
position = on;
|
||||
redrawOnNextFrame=true;
|
||||
underground->setIndex(on.z,true); //change underground switch button image
|
||||
underground->redraw();
|
||||
worldViewUnderground->setIndex(on.z, true);
|
||||
worldViewUnderground->redraw();
|
||||
if (switchedLevels)
|
||||
minimap->setLevel(position.z);
|
||||
minimap->setLevel(terrain->getLevel());
|
||||
minimap->redraw();
|
||||
|
||||
if (mode == EAdvMapMode::WORLD_VIEW)
|
||||
|
@ -72,7 +72,7 @@ private:
|
||||
|
||||
bool swipeEnabled;
|
||||
bool swipeMovementRequested;
|
||||
int3 swipeTargetPosition;
|
||||
Point swipeTargetPosition;
|
||||
|
||||
EGameStates state;
|
||||
|
||||
@ -80,7 +80,7 @@ private:
|
||||
ui8 heroAnim, heroAnimValHitCount; //animation frame
|
||||
|
||||
/// top left corner of visible map part
|
||||
int3 position;
|
||||
//int3 position;
|
||||
|
||||
EAdvMapMode mode;
|
||||
float worldViewScale;
|
||||
|
@ -180,6 +180,7 @@ void CMinimap::showAll(SDL_Surface * to)
|
||||
};
|
||||
|
||||
Canvas clippedTarget(target, pos);
|
||||
CSDL_Ext::CClipRectGuard guard(to, pos);
|
||||
clippedTarget.drawBorderDashed(radar, CSDL_Ext::fromSDL(Colors::PURPLE));
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "CTerrainRect.h"
|
||||
|
||||
#include "mapHandler.h"
|
||||
#include "MapView.h"
|
||||
#include "CAdvMapInt.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
@ -40,14 +41,15 @@ CTerrainRect::CTerrainRect()
|
||||
curHoveredTile(-1,-1,-1),
|
||||
currentPath(nullptr)
|
||||
{
|
||||
tilesw=(ADVOPT.advmapW+31)/32;
|
||||
tilesh=(ADVOPT.advmapH+31)/32;
|
||||
OBJ_CONSTRUCTION_CAPTURING_ALL_NO_DISPOSE;
|
||||
|
||||
pos.x=ADVOPT.advmapX;
|
||||
pos.y=ADVOPT.advmapY;
|
||||
pos.w=ADVOPT.advmapW;
|
||||
pos.h=ADVOPT.advmapH;
|
||||
moveX = moveY = 0;
|
||||
addUsedEvents(LCLICK | RCLICK | MCLICK | HOVER | MOVE);
|
||||
|
||||
renderer = std::make_shared<MapView>( Point(0,0), pos.dimensions() );
|
||||
}
|
||||
|
||||
CTerrainRect::~CTerrainRect()
|
||||
@ -56,6 +58,16 @@ CTerrainRect::~CTerrainRect()
|
||||
SDL_FreeSurface(fadeSurface);
|
||||
}
|
||||
|
||||
void CTerrainRect::setViewCenter(const int3 &coordinates)
|
||||
{
|
||||
renderer->setViewCenter(coordinates);
|
||||
}
|
||||
|
||||
void CTerrainRect::setViewCenter(const Point & position, int level)
|
||||
{
|
||||
renderer->setViewCenter(position, level);
|
||||
}
|
||||
|
||||
void CTerrainRect::deactivate()
|
||||
{
|
||||
CIntObject::deactivate();
|
||||
@ -143,10 +155,8 @@ void CTerrainRect::handleSwipeMove(const Point & cursorPosition)
|
||||
|
||||
if(isSwiping)
|
||||
{
|
||||
adventureInt->swipeTargetPosition.x =
|
||||
swipeInitialMapPos.x + static_cast<si32>(swipeInitialRealPos.x - cursorPosition.x) / 32;
|
||||
adventureInt->swipeTargetPosition.y =
|
||||
swipeInitialMapPos.y + static_cast<si32>(swipeInitialRealPos.y - cursorPosition.y) / 32;
|
||||
adventureInt->swipeTargetPosition.x = swipeInitialViewPos.x + swipeInitialRealPos.x - cursorPosition.x;
|
||||
adventureInt->swipeTargetPosition.y = swipeInitialViewPos.y + swipeInitialRealPos.y - cursorPosition.y;
|
||||
adventureInt->swipeMovementRequested = true;
|
||||
}
|
||||
}
|
||||
@ -156,7 +166,7 @@ bool CTerrainRect::handleSwipeStateChange(bool btnPressed)
|
||||
if(btnPressed)
|
||||
{
|
||||
swipeInitialRealPos = Point(GH.getCursorPosition().x, GH.getCursorPosition().y);
|
||||
swipeInitialMapPos = int3(adventureInt->position);
|
||||
swipeInitialViewPos = getViewCenter();
|
||||
return true;
|
||||
}
|
||||
else if(isSwiping) // only accept this touch if it wasn't a swipe
|
||||
@ -169,7 +179,7 @@ bool CTerrainRect::handleSwipeStateChange(bool btnPressed)
|
||||
|
||||
void CTerrainRect::handleHover(const Point & cursorPosition)
|
||||
{
|
||||
int3 tHovered = whichTileIsIt(cursorPosition.x, cursorPosition.y);
|
||||
int3 tHovered = whichTileIsIt(cursorPosition);
|
||||
int3 pom = adventureInt->verifyPos(tHovered);
|
||||
|
||||
if(tHovered != pom) //tile outside the map
|
||||
@ -196,6 +206,7 @@ void CTerrainRect::hover(bool on)
|
||||
}
|
||||
void CTerrainRect::showPath(const Rect & extRect, SDL_Surface * to)
|
||||
{
|
||||
/*
|
||||
const static int pns[9][9] = {
|
||||
{16, 17, 18, 7, -1, 19, 6, 5, -1},
|
||||
{ 8, 9, 18, 7, -1, 19, 6, -1, 20},
|
||||
@ -228,18 +239,18 @@ void CTerrainRect::showPath(const Rect & extRect, SDL_Surface * to)
|
||||
const int3 &prevPos = currentPath->nodes[i-1].coord;
|
||||
std::vector<CGPathNode> & cv = currentPath->nodes;
|
||||
|
||||
/* Vector directions
|
||||
* 0 1 2
|
||||
* \ | /
|
||||
* 3 - 4 - 5
|
||||
* / | \
|
||||
* 6 7 8
|
||||
*For example:
|
||||
* |
|
||||
* |__\
|
||||
* /
|
||||
* is id1=7, id2=5 (pns[7][5])
|
||||
*/
|
||||
// Vector directions
|
||||
// 0 1 2
|
||||
// \ | /
|
||||
// 3 - 4 - 5
|
||||
// / | \
|
||||
// 6 7 8
|
||||
//For example:
|
||||
// |
|
||||
// |__\
|
||||
// /
|
||||
// is id1=7, id2=5 (pns[7][5])
|
||||
//
|
||||
bool pathContinuous = curPos.areNeighbours(nextPos) && curPos.areNeighbours(prevPos);
|
||||
if(pathContinuous && cv[i].action != CGPathNode::EMBARK && cv[i].action != CGPathNode::DISEMBARK)
|
||||
{
|
||||
@ -317,8 +328,8 @@ void CTerrainRect::showPath(const Rect & extRect, SDL_Surface * to)
|
||||
|
||||
}
|
||||
} //for (int i=0;i<currentPath->nodes.size()-1;i++)
|
||||
}
|
||||
|
||||
*/}
|
||||
/*
|
||||
void CTerrainRect::show(SDL_Surface * to)
|
||||
{
|
||||
if (adventureInt->mode == EAdvMapMode::NORMAL)
|
||||
@ -338,7 +349,7 @@ void CTerrainRect::show(SDL_Surface * to)
|
||||
fadeAnim->draw(to, r.topLeft());
|
||||
}
|
||||
|
||||
if (currentPath/* && adventureInt->position.z==currentPath->startPos().z*/) //drawing path
|
||||
if (currentPath) //drawing path
|
||||
{
|
||||
showPath(pos, to);
|
||||
}
|
||||
@ -366,33 +377,20 @@ void CTerrainRect::showAnim(SDL_Surface * to)
|
||||
if (fadeAnim->isFading() || lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED)
|
||||
show(to);
|
||||
}
|
||||
|
||||
int3 CTerrainRect::whichTileIsIt(const int x, const int y)
|
||||
*/
|
||||
int3 CTerrainRect::whichTileIsIt(const Point &position)
|
||||
{
|
||||
int3 ret;
|
||||
ret.x = adventureInt->position.x + ((x-CGI->mh->offsetX-pos.x)/32);
|
||||
ret.y = adventureInt->position.y + ((y-CGI->mh->offsetY-pos.y)/32);
|
||||
ret.z = adventureInt->position.z;
|
||||
return ret;
|
||||
return renderer->getTileAtPoint(position - pos);
|
||||
}
|
||||
|
||||
int3 CTerrainRect::whichTileIsIt()
|
||||
{
|
||||
return whichTileIsIt(GH.getCursorPosition().x, GH.getCursorPosition().y);
|
||||
return whichTileIsIt(GH.getCursorPosition());
|
||||
}
|
||||
|
||||
Rect CTerrainRect::visibleTilesArea()
|
||||
{
|
||||
switch (adventureInt->mode)
|
||||
{
|
||||
default:
|
||||
logGlobal->error("Unknown map mode %d", (int)adventureInt->mode);
|
||||
return Rect();
|
||||
case EAdvMapMode::NORMAL:
|
||||
return Rect(adventureInt->position.x, adventureInt->position.y, tilesw, tilesh);
|
||||
case EAdvMapMode::WORLD_VIEW:
|
||||
return Rect(adventureInt->position.x, adventureInt->position.y, tilesw / adventureInt->worldViewScale, tilesh / adventureInt->worldViewScale);
|
||||
}
|
||||
return renderer->getVisibleAreaTiles();
|
||||
}
|
||||
|
||||
void CTerrainRect::fadeFromCurrentView()
|
||||
@ -413,3 +411,27 @@ bool CTerrainRect::needsAnimUpdate()
|
||||
return fadeAnim->isFading() || lastRedrawStatus == EMapAnimRedrawStatus::REDRAW_REQUESTED;
|
||||
}
|
||||
|
||||
void CTerrainRect::setLevel(int level)
|
||||
{
|
||||
renderer->setViewCenter(renderer->getViewCenter(), level);
|
||||
}
|
||||
|
||||
void CTerrainRect::moveViewBy(const Point & delta)
|
||||
{
|
||||
renderer->setViewCenter(renderer->getViewCenter() + delta, getLevel());
|
||||
}
|
||||
|
||||
int3 CTerrainRect::getTileCenter()
|
||||
{
|
||||
return renderer->getTileCenter();
|
||||
}
|
||||
|
||||
Point CTerrainRect::getViewCenter()
|
||||
{
|
||||
return renderer->getViewCenter();
|
||||
}
|
||||
|
||||
int CTerrainRect::getLevel()
|
||||
{
|
||||
return renderer->getTileCenter().z;
|
||||
}
|
||||
|
@ -18,18 +18,26 @@ VCMI_LIB_NAMESPACE_END
|
||||
|
||||
enum class EMapAnimRedrawStatus;
|
||||
class CFadeAnimation;
|
||||
class MapView;
|
||||
|
||||
/// Holds information about which tiles of the terrain are shown/not shown at the screen
|
||||
class CTerrainRect : public CIntObject
|
||||
{
|
||||
std::shared_ptr<MapView> renderer;
|
||||
|
||||
SDL_Surface * fadeSurface;
|
||||
EMapAnimRedrawStatus lastRedrawStatus;
|
||||
std::shared_ptr<CFadeAnimation> fadeAnim;
|
||||
|
||||
int3 swipeInitialMapPos;
|
||||
Point swipeInitialViewPos;
|
||||
Point swipeInitialRealPos;
|
||||
bool isSwiping;
|
||||
static constexpr float SwipeTouchSlop = 16.0f;
|
||||
|
||||
#if defined(VCMI_ANDROID) || defined(VCMI_IOS)
|
||||
static constexpr float SwipeTouchSlop = 16.0f; // touch UI
|
||||
#else
|
||||
static constexpr float SwipeTouchSlop = 1.0f; // mouse UI
|
||||
#endif
|
||||
|
||||
void handleHover(const Point & cursorPosition);
|
||||
void handleSwipeMove(const Point & cursorPosition);
|
||||
@ -37,19 +45,26 @@ class CTerrainRect : public CIntObject
|
||||
bool handleSwipeStateChange(bool btnPressed);
|
||||
int3 curHoveredTile;
|
||||
|
||||
int3 whichTileIsIt(const int x, const int y); //x,y are cursor position
|
||||
int3 whichTileIsIt(const Point & position); //x,y are cursor position
|
||||
int3 whichTileIsIt(); //uses current cursor pos
|
||||
void showPath(const Rect &extRect, SDL_Surface * to);
|
||||
|
||||
bool needsAnimUpdate();
|
||||
public:
|
||||
int tilesw, tilesh; //width and height of terrain to blit in tiles
|
||||
int moveX, moveY; //shift between actual position of screen and the one we wil blit; ranges from -31 to 31 (in pixels)
|
||||
CGPath * currentPath;
|
||||
|
||||
CTerrainRect();
|
||||
~CTerrainRect();
|
||||
|
||||
void moveViewBy(const Point & delta);
|
||||
void setViewCenter(const int3 & coordinates);
|
||||
void setViewCenter(const Point & position, int level);
|
||||
void setLevel(int level);
|
||||
|
||||
int3 getTileCenter();
|
||||
Point getViewCenter();
|
||||
int getLevel();
|
||||
|
||||
// CIntObject interface implementation
|
||||
void deactivate() override;
|
||||
void clickLeft(tribool down, bool previousState) override;
|
||||
@ -57,10 +72,10 @@ public:
|
||||
void clickMiddle(tribool down, bool previousState) override;
|
||||
void hover(bool on) override;
|
||||
void mouseMoved (const Point & cursorPosition) override;
|
||||
void show(SDL_Surface * to) override;
|
||||
void showAll(SDL_Surface * to) override;
|
||||
//void show(SDL_Surface * to) override;
|
||||
//void showAll(SDL_Surface * to) override;
|
||||
|
||||
void showAnim(SDL_Surface * to);
|
||||
//void showAnim(SDL_Surface * to);
|
||||
|
||||
/// @returns number of visible tiles on screen respecting current map scaling
|
||||
Rect visibleTilesArea();
|
||||
@ -68,4 +83,3 @@ public:
|
||||
/// animates view by caching current surface and crossfading it with normal screen
|
||||
void fadeFromCurrentView();
|
||||
};
|
||||
|
||||
|
578
client/adventureMap/MapRenderer.cpp
Normal file
578
client/adventureMap/MapRenderer.cpp
Normal file
@ -0,0 +1,578 @@
|
||||
/*
|
||||
* MapRenderer.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 "MapRenderer.h"
|
||||
|
||||
#include "MapRendererContext.h"
|
||||
#include "mapHandler.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../render/IImage.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../../lib/RiverHandler.h"
|
||||
#include "../../lib/RoadHandler.h"
|
||||
#include "../../lib/TerrainHandler.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
|
||||
struct NeighborTilesInfo
|
||||
{
|
||||
bool d7, //789
|
||||
d8, //456
|
||||
d9, //123
|
||||
d4,
|
||||
d5,
|
||||
d6,
|
||||
d1,
|
||||
d2,
|
||||
d3;
|
||||
NeighborTilesInfo( const IMapRendererContext & context, const int3 & pos)
|
||||
{
|
||||
auto getTile = [&](int dx, int dy)->bool
|
||||
{
|
||||
if ( dx + pos.x < 0 || dx + pos.x >= context.getMapSize().x
|
||||
|| dy + pos.y < 0 || dy + pos.y >= context.getMapSize().y)
|
||||
return false;
|
||||
|
||||
//FIXME: please do not read settings for every tile...
|
||||
return context.isVisible( pos + int3(dx, dy, 0));
|
||||
};
|
||||
d7 = getTile(-1, -1); //789
|
||||
d8 = getTile( 0, -1); //456
|
||||
d9 = getTile(+1, -1); //123
|
||||
d4 = getTile(-1, 0);
|
||||
d5 = getTile( 0, 0);
|
||||
d6 = getTile(+1, 0);
|
||||
d1 = getTile(-1, +1);
|
||||
d2 = getTile( 0, +1);
|
||||
d3 = getTile(+1, +1);
|
||||
}
|
||||
|
||||
bool areAllHidden() const
|
||||
{
|
||||
return !(d1 || d2 || d3 || d4 || d5 || d6 || d7 || d8 || d9);
|
||||
}
|
||||
|
||||
int getBitmapID() const
|
||||
{
|
||||
//NOTE: some images have unused in VCMI pair (same blockmap but a bit different look)
|
||||
// 0-1, 2-3, 4-5, 11-13, 12-14
|
||||
static const int visBitmaps[256] = {
|
||||
-1, 34, 4, 4, 22, 23, 4, 4, 36, 36, 38, 38, 47, 47, 38, 38, //16
|
||||
3, 25, 12, 12, 3, 25, 12, 12, 9, 9, 6, 6, 9, 9, 6, 6, //32
|
||||
35, 39, 48, 48, 41, 43, 48, 48, 36, 36, 38, 38, 47, 47, 38, 38, //48
|
||||
26, 49, 28, 28, 26, 49, 28, 28, 9, 9, 6, 6, 9, 9, 6, 6, //64
|
||||
0, 45, 29, 29, 24, 33, 29, 29, 37, 37, 7, 7, 50, 50, 7, 7, //80
|
||||
13, 27, 44, 44, 13, 27, 44, 44, 8, 8, 10, 10, 8, 8, 10, 10, //96
|
||||
0, 45, 29, 29, 24, 33, 29, 29, 37, 37, 7, 7, 50, 50, 7, 7, //112
|
||||
13, 27, 44, 44, 13, 27, 44, 44, 8, 8, 10, 10, 8, 8, 10, 10, //128
|
||||
15, 17, 30, 30, 16, 19, 30, 30, 46, 46, 40, 40, 32, 32, 40, 40, //144
|
||||
2, 25, 12, 12, 2, 25, 12, 12, 9, 9, 6, 6, 9, 9, 6, 6, //160
|
||||
18, 42, 31, 31, 20, 21, 31, 31, 46, 46, 40, 40, 32, 32, 40, 40, //176
|
||||
26, 49, 28, 28, 26, 49, 28, 28, 9, 9, 6, 6, 9, 9, 6, 6, //192
|
||||
0, 45, 29, 29, 24, 33, 29, 29, 37, 37, 7, 7, 50, 50, 7, 7, //208
|
||||
13, 27, 44, 44, 13, 27, 44, 44, 8, 8, 10, 10, 8, 8, 10, 10, //224
|
||||
0, 45, 29, 29, 24, 33, 29, 29, 37, 37, 7, 7, 50, 50, 7, 7, //240
|
||||
13, 27, 44, 44, 13, 27, 44, 44, 8, 8, 10, 10, 8, 8, 10, 10 //256
|
||||
};
|
||||
|
||||
return visBitmaps[d1 + d2 * 2 + d3 * 4 + d4 * 8 + d6 * 16 + d7 * 32 + d8 * 64 + d9 * 128]; // >=0 -> partial hide, <0 - full hide
|
||||
}
|
||||
};
|
||||
|
||||
MapObjectsSorter::MapObjectsSorter(const IMapRendererContext & context)
|
||||
: context(context)
|
||||
{
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
MapTileStorage::MapTileStorage(size_t capacity)
|
||||
: animations(capacity)
|
||||
{
|
||||
}
|
||||
|
||||
void MapTileStorage::load(size_t index, const std::string & filename)
|
||||
{
|
||||
auto & terrainAnimations = animations[index];
|
||||
|
||||
for(auto & entry : terrainAnimations)
|
||||
{
|
||||
entry = std::make_unique<CAnimation>(filename);
|
||||
entry->preload();
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < terrainAnimations[0]->size(); ++i)
|
||||
{
|
||||
terrainAnimations[1]->getImage(i)->verticalFlip();
|
||||
terrainAnimations[3]->getImage(i)->verticalFlip();
|
||||
|
||||
terrainAnimations[2]->getImage(i)->horizontalFlip();
|
||||
terrainAnimations[3]->getImage(i)->horizontalFlip();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<IImage> MapTileStorage::find(size_t fileIndex, size_t rotationIndex, size_t imageIndex)
|
||||
{
|
||||
const auto & animation = animations[fileIndex][rotationIndex];
|
||||
return animation->getImage(imageIndex);
|
||||
}
|
||||
|
||||
MapRendererTerrain::MapRendererTerrain()
|
||||
: storage(VLC->terrainTypeHandler->objects.size())
|
||||
{
|
||||
for(const auto & terrain : VLC->terrainTypeHandler->objects)
|
||||
storage.load(terrain->getIndex(), terrain->tilesFilename);
|
||||
}
|
||||
|
||||
void MapRendererTerrain::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
|
||||
{
|
||||
const TerrainTile & mapTile = context.getMapTile(coordinates);
|
||||
|
||||
int32_t terrainIndex = mapTile.terType->getIndex();
|
||||
int32_t imageIndex = mapTile.terView;
|
||||
int32_t rotationIndex = mapTile.extTileFlags % 4;
|
||||
|
||||
const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
|
||||
|
||||
target.draw(image, Point(0, 0));
|
||||
}
|
||||
|
||||
MapRendererRiver::MapRendererRiver()
|
||||
: storage(VLC->riverTypeHandler->objects.size())
|
||||
{
|
||||
for(const auto & river : VLC->riverTypeHandler->objects)
|
||||
storage.load(river->getIndex(), river->tilesFilename);
|
||||
}
|
||||
|
||||
void MapRendererRiver::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
|
||||
{
|
||||
const TerrainTile & mapTile = context.getMapTile(coordinates);
|
||||
|
||||
if(mapTile.riverType->getId() != River::NO_RIVER)
|
||||
{
|
||||
int32_t terrainIndex = mapTile.riverType->getIndex();
|
||||
int32_t imageIndex = mapTile.riverDir;
|
||||
int32_t rotationIndex = (mapTile.extTileFlags >> 2) % 4;
|
||||
|
||||
const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
|
||||
target.draw(image, Point(0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
MapRendererRoad::MapRendererRoad():
|
||||
storage(VLC->roadTypeHandler->objects.size())
|
||||
{
|
||||
for(const auto & road : VLC->roadTypeHandler->objects)
|
||||
storage.load(road->getIndex(), road->tilesFilename);
|
||||
}
|
||||
|
||||
void MapRendererRoad::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
|
||||
{
|
||||
const int3 coordinatesAbove = coordinates - int3(0,1,0);
|
||||
|
||||
if (context.isInMap(coordinatesAbove))
|
||||
{
|
||||
const TerrainTile & mapTileAbove = context.getMapTile(coordinatesAbove);
|
||||
if (mapTileAbove.roadType->getId() != Road::NO_ROAD)
|
||||
{
|
||||
int32_t terrainIndex = mapTileAbove.roadType->getIndex();
|
||||
int32_t imageIndex = mapTileAbove.roadDir;
|
||||
int32_t rotationIndex = (mapTileAbove.extTileFlags >> 4) % 4;
|
||||
|
||||
const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
|
||||
target.draw(image, Point(0,0), Rect(0, 16, 32, 16));
|
||||
}
|
||||
}
|
||||
|
||||
const TerrainTile & mapTile = context.getMapTile(coordinates);
|
||||
if(mapTile.roadType->getId() != Road::NO_ROAD)
|
||||
{
|
||||
int32_t terrainIndex = mapTile.roadType->getIndex();
|
||||
int32_t imageIndex = mapTile.roadDir;
|
||||
int32_t rotationIndex = (mapTile.extTileFlags >> 4) % 4;
|
||||
|
||||
const auto & image = storage.find(terrainIndex, rotationIndex, imageIndex);
|
||||
target.draw(image, Point(0,16), Rect(0, 0, 32, 16));
|
||||
}
|
||||
}
|
||||
|
||||
MapRendererBorder::MapRendererBorder()
|
||||
{
|
||||
animation = std::make_unique<CAnimation>("EDG");
|
||||
animation->preload();
|
||||
}
|
||||
|
||||
size_t MapRendererBorder::getIndexForTile(const IMapRendererContext & context, const int3 & tile)
|
||||
{
|
||||
assert(!context.isInMap(tile));
|
||||
|
||||
int3 size = context.getMapSize();
|
||||
|
||||
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);
|
||||
|
||||
if (tile.x == -1 && tile.y == -1)
|
||||
return 16;
|
||||
|
||||
if (tile.x == size.x && tile.y == -1)
|
||||
return 17;
|
||||
|
||||
if (tile.x == size.x && tile.y == size.y)
|
||||
return 18;
|
||||
|
||||
if (tile.x == -1 && tile.y == size.y)
|
||||
return 19;
|
||||
|
||||
if (tile.y == -1)
|
||||
return 20 + (tile.x % 4);
|
||||
|
||||
if (tile.x == size.x)
|
||||
return 24 + (tile.y % 4);
|
||||
|
||||
if (tile.y == size.y)
|
||||
return 28 + (tile.x % 4);
|
||||
|
||||
if (tile.x == -1)
|
||||
return 32 + (tile.y % 4);
|
||||
|
||||
//else - visible area, no renderable border
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MapRendererBorder::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
|
||||
{
|
||||
const auto & image = animation->getImage(getIndexForTile(context, coordinates));
|
||||
target.draw(image, Point(0,0));
|
||||
}
|
||||
|
||||
MapRendererFow::MapRendererFow()
|
||||
{
|
||||
fogOfWarFullHide = std::make_unique<CAnimation>("TSHRC");
|
||||
fogOfWarFullHide->preload();
|
||||
fogOfWarPartialHide = std::make_unique<CAnimation>("TSHRE");
|
||||
fogOfWarPartialHide->preload();
|
||||
|
||||
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
|
||||
|
||||
for(const int rotation : rotations)
|
||||
{
|
||||
fogOfWarPartialHide->duplicateImage(0, rotation, 0);
|
||||
auto image = fogOfWarPartialHide->getImage(size, 0);
|
||||
image->verticalFlip();
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
void MapRendererFow::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
|
||||
{
|
||||
assert(!context.isVisible(coordinates));
|
||||
|
||||
const NeighborTilesInfo neighborInfo(context, coordinates);
|
||||
|
||||
int retBitmapID = neighborInfo.getBitmapID();// >=0 -> partial hide, <0 - full hide
|
||||
if (retBitmapID < 0)
|
||||
{
|
||||
// generate a number that is predefined for each tile,
|
||||
// but appears random to break visible pattern in large areas of fow
|
||||
// current approach (use primes as magic numbers for formula) looks to be suitable
|
||||
size_t pseudorandomNumber = ((coordinates.x * 997) ^ (coordinates.y * 1009)) / 101;
|
||||
size_t imageIndex = pseudorandomNumber % fogOfWarFullHide->size();
|
||||
|
||||
target.draw(fogOfWarFullHide->getImage(imageIndex), Point(0,0));
|
||||
}
|
||||
else
|
||||
{
|
||||
target.draw(fogOfWarPartialHide->getImage(retBitmapID), Point(0,0));
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const CGObjectInstance* obj)
|
||||
{
|
||||
const auto & info = obj->appearance;
|
||||
|
||||
//the only(?) invisible object
|
||||
if(info->id == Obj::EVENT)
|
||||
return std::shared_ptr<CAnimation>();
|
||||
|
||||
if(info->animationFile.empty())
|
||||
{
|
||||
logGlobal->warn("Def name for obj (%d,%d) is empty!", info->id, info->subid);
|
||||
return std::shared_ptr<CAnimation>();
|
||||
}
|
||||
|
||||
return getAnimation(info->animationFile);
|
||||
}
|
||||
|
||||
std::shared_ptr<CAnimation> MapRendererObjects::getAnimation(const std::string & filename)
|
||||
{
|
||||
if (animations.count(filename))
|
||||
return animations[filename];
|
||||
|
||||
auto ret = std::make_shared<CAnimation>(filename);
|
||||
animations[filename] = ret;
|
||||
ret->preload();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void MapRendererObjects::initializeObjects(const IMapRendererContext & context)
|
||||
{
|
||||
auto mapSize = context.getMapSize();
|
||||
|
||||
objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
|
||||
|
||||
for(const auto & obj : context.getAllObjects())
|
||||
{
|
||||
if(!obj)
|
||||
continue;
|
||||
|
||||
if(obj->ID == Obj::HERO && dynamic_cast<const CGHeroInstance *>(obj.get())->inTownGarrison)
|
||||
continue;
|
||||
|
||||
if(obj->ID == Obj::BOAT && dynamic_cast<const CGBoat *>(obj.get())->hero)
|
||||
continue;
|
||||
|
||||
std::shared_ptr<CAnimation> animation = getAnimation(obj);
|
||||
|
||||
//no animation at all, e.g. Event
|
||||
if(!animation)
|
||||
continue;
|
||||
|
||||
//empty animation. Illegal?
|
||||
assert(animation->size(0) > 0);
|
||||
if(animation->size(0) == 0)
|
||||
continue;
|
||||
|
||||
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))
|
||||
objects[currTile.z][currTile.x][currTile.y].push_back(obj->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(int z = 0; z < mapSize.z; z++)
|
||||
{
|
||||
for(int x = 0; x < mapSize.x; x++)
|
||||
{
|
||||
for(int y = 0; y < mapSize.y; y++)
|
||||
{
|
||||
auto & array = objects[z][x][y];
|
||||
std::sort(array.begin(), array.end(), MapObjectsSorter(context));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapRendererObjects::MapRendererObjects(const IMapRendererContext & context)
|
||||
{
|
||||
initializeObjects(context);
|
||||
}
|
||||
|
||||
std::shared_ptr<CAnimation> MapRendererObjects::getFlagAnimation(const CGObjectInstance* obj)
|
||||
{
|
||||
//TODO: relocate to config file?
|
||||
static const std::vector<std::string> heroFlags = {
|
||||
"AF00", "AF01", "AF02", "AF03", "AF04", "AF05", "AF06", "AF07"
|
||||
};
|
||||
|
||||
//TODO: relocate to config file?
|
||||
static const std::vector<std::vector<std::string>> boatFlags = {
|
||||
{"ABF01L", "ABF01G", "ABF01R", "ABF01D", "ABF01B", "ABF01P", "ABF01W", "ABF01K"},
|
||||
{"ABF02L", "ABF02G", "ABF02R", "ABF02D", "ABF02B", "ABF02P", "ABF02W", "ABF02K"},
|
||||
{"ABF03L", "ABF03G", "ABF03R", "ABF03D", "ABF03B", "ABF03P", "ABF03W", "ABF03K"}
|
||||
};
|
||||
|
||||
if(obj->ID == Obj::HERO)
|
||||
{
|
||||
assert(dynamic_cast<const CGHeroInstance *>(obj) != nullptr);
|
||||
assert(obj->tempOwner.isValidPlayer());
|
||||
return getAnimation(heroFlags[obj->tempOwner.getNum()]);
|
||||
}
|
||||
if(obj->ID == Obj::BOAT)
|
||||
{
|
||||
const auto * boat = dynamic_cast<const CGBoat *>(obj);
|
||||
assert(boat);
|
||||
assert(boat->subID < boatFlags.size());
|
||||
assert(!boat->hero || boat->hero->tempOwner.isValidPlayer());
|
||||
|
||||
if(boat->hero)
|
||||
return getAnimation(boatFlags[obj->subID][boat->hero->tempOwner.getNum()]);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<IImage> MapRendererObjects::getImage(const IMapRendererContext & context, const CGObjectInstance * obj, const std::shared_ptr<CAnimation>& animation) const
|
||||
{
|
||||
if(!animation)
|
||||
return nullptr;
|
||||
|
||||
size_t groupIndex = getAnimationGroup(context, obj);
|
||||
|
||||
if(animation->size(groupIndex) == 0)
|
||||
return nullptr;
|
||||
|
||||
size_t frameCounter = context.getAnimationTime() / context.getAnimationPeriod();
|
||||
size_t frameIndex = frameCounter % animation->size(groupIndex);
|
||||
|
||||
return animation->getImage(frameIndex, groupIndex);
|
||||
}
|
||||
|
||||
size_t MapRendererObjects::getAnimationGroup(const IMapRendererContext & context, const CGObjectInstance * obj) const
|
||||
{
|
||||
// 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)
|
||||
return;
|
||||
|
||||
image->setFlagColor(object->tempOwner);
|
||||
|
||||
int3 offsetTiles(object->getPosition() - coordinates);
|
||||
|
||||
Point offsetPixels(offsetTiles.x * 32, offsetTiles.y * 32);
|
||||
Point imagePos = image->dimensions() - offsetPixels - Point(32, 32);
|
||||
Point tileDimensions(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)
|
||||
{
|
||||
renderImage(target, coordinates, instance, getImage(context, instance, getAnimation(instance)));
|
||||
renderImage(target, coordinates, instance, getImage(context, instance, getFlagAnimation(instance)));
|
||||
}
|
||||
|
||||
void MapRendererObjects::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
|
||||
{
|
||||
for(const auto & objectID : objects[coordinates.z][coordinates.x][coordinates.y])
|
||||
{
|
||||
const auto * objectInstance = context.getObject(objectID);
|
||||
|
||||
assert(objectInstance);
|
||||
if(!objectInstance)
|
||||
{
|
||||
logGlobal->error("Stray map object that isn't fading");
|
||||
continue;
|
||||
}
|
||||
|
||||
renderObject(context, target, coordinates, objectInstance);
|
||||
}
|
||||
}
|
||||
|
||||
void MapRendererObjects::addObject(const IMapRendererContext & context, const CGObjectInstance * object)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MapRendererObjects::removeObject(const IMapRendererContext & context, const CGObjectInstance * object)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MapRendererDebugGrid::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
|
||||
{
|
||||
if(context.showGrid())
|
||||
{
|
||||
target.drawLine(Point(0, 0), Point(0, 31), {128, 128, 128, 128}, {128, 128, 128, 128});
|
||||
target.drawLine(Point(0, 0), Point(31, 0), {128, 128, 128, 128}, {128, 128, 128, 128});
|
||||
}
|
||||
}
|
||||
|
||||
MapRenderer::MapRenderer(const IMapRendererContext & context)
|
||||
: rendererObjects(context)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void MapRenderer::renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates)
|
||||
{
|
||||
if (!context.isInMap(coordinates))
|
||||
{
|
||||
rendererBorder.renderTile(context, target, coordinates);
|
||||
return;
|
||||
}
|
||||
|
||||
const NeighborTilesInfo neighborInfo(context, coordinates);
|
||||
|
||||
if (neighborInfo.areAllHidden())
|
||||
{
|
||||
rendererFow.renderTile(context, target, coordinates);
|
||||
}
|
||||
else
|
||||
{
|
||||
rendererTerrain.renderTile(context, target, coordinates);
|
||||
rendererRiver.renderTile(context, target, coordinates);
|
||||
rendererRoad.renderTile(context, target, coordinates);
|
||||
rendererObjects.renderTile(context, target, coordinates);
|
||||
|
||||
if (!context.isVisible(coordinates))
|
||||
rendererFow.renderTile(context, target, coordinates);
|
||||
}
|
||||
rendererDebugGrid.renderTile(context, target,coordinates);
|
||||
}
|
||||
|
||||
void MapRenderer::addObject(const IMapRendererContext & context, const CGObjectInstance * object)
|
||||
{
|
||||
rendererObjects.addObject(context, object);
|
||||
}
|
||||
|
||||
void MapRenderer::removeObject(const IMapRendererContext & context, const CGObjectInstance * object)
|
||||
{
|
||||
rendererObjects.addObject(context, object);
|
||||
}
|
140
client/adventureMap/MapRenderer.h
Normal file
140
client/adventureMap/MapRenderer.h
Normal file
@ -0,0 +1,140 @@
|
||||
/*
|
||||
* MapRenderer.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
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class int3;
|
||||
class ObjectInstanceID;
|
||||
class CGObjectInstance;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class CAnimation;
|
||||
class IImage;
|
||||
class Canvas;
|
||||
class IMapRendererContext;
|
||||
|
||||
class MapObjectsSorter
|
||||
{
|
||||
const IMapRendererContext & context;
|
||||
public:
|
||||
explicit MapObjectsSorter(const IMapRendererContext & context);
|
||||
|
||||
bool operator ()(const ObjectInstanceID & left, const ObjectInstanceID & right) const;
|
||||
bool operator ()(const CGObjectInstance * left, const CGObjectInstance * right) const;
|
||||
};
|
||||
|
||||
class MapTileStorage
|
||||
{
|
||||
using TerrainAnimation = std::array<std::unique_ptr<CAnimation>, 4>;
|
||||
std::vector<TerrainAnimation> animations;
|
||||
public:
|
||||
explicit MapTileStorage( size_t capacity);
|
||||
void load(size_t index, const std::string& filename);
|
||||
std::shared_ptr<IImage> find(size_t fileIndex, size_t rotationIndex, size_t imageIndex );
|
||||
};
|
||||
|
||||
class MapRendererTerrain
|
||||
{
|
||||
MapTileStorage storage;
|
||||
public:
|
||||
MapRendererTerrain();
|
||||
void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates);
|
||||
};
|
||||
|
||||
class MapRendererRiver
|
||||
{
|
||||
MapTileStorage storage;
|
||||
public:
|
||||
MapRendererRiver();
|
||||
void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates);
|
||||
};
|
||||
|
||||
class MapRendererRoad
|
||||
{
|
||||
MapTileStorage storage;
|
||||
public:
|
||||
MapRendererRoad();
|
||||
void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates);
|
||||
};
|
||||
|
||||
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::shared_ptr<CAnimation> getFlagAnimation(const CGObjectInstance* obj);
|
||||
std::shared_ptr<CAnimation> getAnimation(const CGObjectInstance* obj);
|
||||
std::shared_ptr<CAnimation> getAnimation(const std::string & filename);
|
||||
|
||||
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 initializeObjects(const IMapRendererContext & context);
|
||||
void renderImage(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);
|
||||
public:
|
||||
explicit MapRendererObjects(const IMapRendererContext & context);
|
||||
void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates);
|
||||
|
||||
void addObject(const IMapRendererContext & context, const CGObjectInstance * object);
|
||||
void removeObject(const IMapRendererContext & context, const CGObjectInstance * object);
|
||||
};
|
||||
|
||||
class MapRendererBorder
|
||||
{
|
||||
std::unique_ptr<CAnimation> animation;
|
||||
|
||||
size_t getIndexForTile(const IMapRendererContext & context, const int3 & coordinates);
|
||||
public:
|
||||
MapRendererBorder();
|
||||
void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates);
|
||||
};
|
||||
|
||||
class MapRendererFow
|
||||
{
|
||||
std::unique_ptr<CAnimation> fogOfWarFullHide;
|
||||
std::unique_ptr<CAnimation> fogOfWarPartialHide;
|
||||
|
||||
public:
|
||||
MapRendererFow();
|
||||
void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates);
|
||||
};
|
||||
|
||||
class MapRendererDebugGrid
|
||||
{
|
||||
public:
|
||||
void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates);
|
||||
};
|
||||
|
||||
class MapRenderer
|
||||
{
|
||||
MapRendererTerrain rendererTerrain;
|
||||
MapRendererRiver rendererRiver;
|
||||
MapRendererRoad rendererRoad;
|
||||
MapRendererBorder rendererBorder;
|
||||
MapRendererFow rendererFow;
|
||||
MapRendererObjects rendererObjects;
|
||||
MapRendererDebugGrid rendererDebugGrid;
|
||||
|
||||
public:
|
||||
explicit MapRenderer(const IMapRendererContext & context);
|
||||
|
||||
void renderTile(const IMapRendererContext & context, Canvas & target, const int3 & coordinates);
|
||||
|
||||
void addObject(const IMapRendererContext & context, const CGObjectInstance * object);
|
||||
void removeObject(const IMapRendererContext & context, const CGObjectInstance * object);
|
||||
|
||||
};
|
47
client/adventureMap/MapRendererContext.h
Normal file
47
client/adventureMap/MapRendererContext.h
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* MapRenderer.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/ConstTransitivePtr.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class int3;
|
||||
class Point;
|
||||
class ObjectInstanceID;
|
||||
class CGObjectInstance;
|
||||
struct TerrainTile;
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
||||
class IMapRendererContext
|
||||
{
|
||||
public:
|
||||
virtual ~IMapRendererContext() = default;
|
||||
|
||||
using VisibilityMap = std::shared_ptr<const boost::multi_array<ui8, 3>>;
|
||||
using ObjectsVector = std::vector< ConstTransitivePtr<CGObjectInstance> >;
|
||||
|
||||
virtual int3 getMapSize() const = 0;
|
||||
virtual bool isInMap(const int3 & coordinates) const = 0;
|
||||
virtual const TerrainTile & getMapTile(const int3 & coordinates) const = 0;
|
||||
|
||||
virtual ObjectsVector getAllObjects() const = 0;
|
||||
virtual const CGObjectInstance * getObject( ObjectInstanceID objectID ) const = 0;
|
||||
|
||||
virtual bool isVisible(const int3 & coordinates) const = 0;
|
||||
virtual VisibilityMap getVisibilityMap() const = 0;
|
||||
|
||||
virtual uint32_t getAnimationPeriod() const = 0;
|
||||
virtual uint32_t getAnimationTime() const = 0;
|
||||
virtual Point tileSize() const = 0;
|
||||
|
||||
virtual bool showGrid() const = 0;
|
||||
};
|
297
client/adventureMap/MapView.cpp
Normal file
297
client/adventureMap/MapView.cpp
Normal file
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* MapView.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 "MapView.h"
|
||||
|
||||
#include "MapRenderer.h"
|
||||
#include "mapHandler.h"
|
||||
|
||||
#include "../CGameInfo.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/CFadeAnimation.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../render/Graphics.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../../lib/CConfigHandler.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
#include "../../lib/CRandomGenerator.h"
|
||||
#include "../../lib/CStopWatch.h"
|
||||
#include "../../lib/Color.h"
|
||||
#include "../../lib/RiverHandler.h"
|
||||
#include "../../lib/RoadHandler.h"
|
||||
#include "../../lib/TerrainHandler.h"
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapObjects/CObjectClassesHandler.h"
|
||||
#include "../../lib/mapping/CMap.h"
|
||||
|
||||
MapCache::~MapCache() = default;
|
||||
|
||||
MapCache::MapCache(const Point & tileSize, const Point & dimensions)
|
||||
: tileSize(tileSize)
|
||||
, context(new MapRendererContext())
|
||||
, mapRenderer(new MapRenderer(*context))
|
||||
, targetDimensionsPixels(dimensions)
|
||||
, viewCenter(0, 0)
|
||||
, mapLevel(0)
|
||||
{
|
||||
// total number of potentially visible tiles is:
|
||||
// 1) number of completely visible tiles
|
||||
// 2) additional tile that might be partially visible from left/top size
|
||||
// 3) additional tile that might be partially visible from right/bottom size
|
||||
Point visibleTiles{
|
||||
dimensions.x / tileSize.x + 2,
|
||||
dimensions.y / tileSize.y + 2,
|
||||
};
|
||||
|
||||
viewDimensionsTiles = visibleTiles;
|
||||
viewDimensionsPixels = visibleTiles * tileSize;
|
||||
|
||||
terrain = std::make_unique<Canvas>(viewDimensionsPixels);
|
||||
}
|
||||
|
||||
void MapCache::setViewCenter(const Point & center, int newLevel)
|
||||
{
|
||||
viewCenter = center;
|
||||
mapLevel = newLevel;
|
||||
|
||||
int3 mapSize = LOCPLINT->cb->getMapSize();
|
||||
Point viewMax = Point(mapSize) * tileSize;
|
||||
|
||||
vstd::abetween(viewCenter.x, 0, viewMax.x);
|
||||
vstd::abetween(viewCenter.y, 0, viewMax.y);
|
||||
}
|
||||
|
||||
Canvas MapCache::getTile(const int3 & coordinates)
|
||||
{
|
||||
assert(mapLevel == coordinates.z);
|
||||
assert(viewDimensionsTiles.x + coordinates.x >= 0);
|
||||
assert(viewDimensionsTiles.y + coordinates.y >= 0);
|
||||
|
||||
Point tileIndex{
|
||||
(viewDimensionsTiles.x + coordinates.x) % viewDimensionsTiles.x,
|
||||
(viewDimensionsTiles.y + coordinates.y) % viewDimensionsTiles.y
|
||||
};
|
||||
|
||||
Rect terrainSection(tileIndex * tileSize, tileSize);
|
||||
|
||||
return Canvas(*terrain, terrainSection);
|
||||
}
|
||||
|
||||
void MapCache::updateTile(const int3 & coordinates)
|
||||
{
|
||||
Canvas target = getTile(coordinates);
|
||||
|
||||
mapRenderer->renderTile(*context, target, coordinates);
|
||||
}
|
||||
|
||||
Rect MapCache::getVisibleAreaTiles() const
|
||||
{
|
||||
Rect visibleAreaPixels = {
|
||||
viewCenter.x - targetDimensionsPixels.x / 2,
|
||||
viewCenter.y - targetDimensionsPixels.y / 2,
|
||||
targetDimensionsPixels.x,
|
||||
targetDimensionsPixels.y
|
||||
};
|
||||
|
||||
// NOTE: use division via double in order to use floor (which rounds to negative infinity and not towards zero)
|
||||
Point topLeftTile{
|
||||
static_cast<int>(std::floor(static_cast<double>(visibleAreaPixels.left()) / tileSize.x)),
|
||||
static_cast<int>(std::floor(static_cast<double>(visibleAreaPixels.top()) / tileSize.y)),
|
||||
};
|
||||
|
||||
Point bottomRightTile{
|
||||
visibleAreaPixels.right() / tileSize.x,
|
||||
visibleAreaPixels.bottom() / tileSize.y
|
||||
};
|
||||
|
||||
return Rect(topLeftTile, bottomRightTile - topLeftTile + Point(1, 1));
|
||||
}
|
||||
|
||||
int3 MapCache::getTileAtPoint(const Point & position) const
|
||||
{
|
||||
Point topLeftOffset = viewCenter - targetDimensionsPixels / 2;
|
||||
|
||||
Point absolutePosition = position + topLeftOffset;
|
||||
|
||||
return {
|
||||
static_cast<int>(std::floor(static_cast<double>(absolutePosition.x) / tileSize.x)),
|
||||
static_cast<int>(std::floor(static_cast<double>(absolutePosition.y) / tileSize.y)),
|
||||
mapLevel
|
||||
};
|
||||
}
|
||||
|
||||
int3 MapCache::getTileCenter() const
|
||||
{
|
||||
return getTileAtPoint(getViewCenter());
|
||||
}
|
||||
|
||||
Point MapCache::getViewCenter() const
|
||||
{
|
||||
return viewCenter;
|
||||
}
|
||||
|
||||
void MapCache::update(uint32_t timeDelta)
|
||||
{
|
||||
context->advanceAnimations(timeDelta);
|
||||
|
||||
Rect dimensions = getVisibleAreaTiles();
|
||||
|
||||
for(int y = dimensions.top(); y < dimensions.bottom(); ++y)
|
||||
for(int x = dimensions.left(); x < dimensions.right(); ++x)
|
||||
updateTile({x, y, mapLevel});
|
||||
}
|
||||
|
||||
void MapCache::render(Canvas & target)
|
||||
{
|
||||
update(GH.mainFPSmng->getElapsedMilliseconds());
|
||||
|
||||
Rect dimensions = getVisibleAreaTiles();
|
||||
|
||||
for(int y = dimensions.top(); y < dimensions.bottom(); ++y)
|
||||
{
|
||||
for(int x = dimensions.left(); x < dimensions.right(); ++x)
|
||||
{
|
||||
Point topLeftOffset = viewCenter - targetDimensionsPixels / 2;
|
||||
Point tilePosAbsolute = Point(x, y) * tileSize;
|
||||
Point tilePosRelative = tilePosAbsolute - topLeftOffset;
|
||||
|
||||
Canvas source = getTile(int3(x, y, mapLevel));
|
||||
target.draw(source, tilePosRelative);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MapView::MapView(const Point & offset, const Point & dimensions)
|
||||
: tilesCache(new MapCache(Point(32, 32), dimensions))
|
||||
, tileSize(Point(32, 32))
|
||||
{
|
||||
pos += offset;
|
||||
pos.w = dimensions.x;
|
||||
pos.h = dimensions.y;
|
||||
}
|
||||
|
||||
Rect MapView::getVisibleAreaTiles() const
|
||||
{
|
||||
return tilesCache->getVisibleAreaTiles();
|
||||
}
|
||||
|
||||
int3 MapView::getTileCenter() const
|
||||
{
|
||||
return tilesCache->getTileCenter();
|
||||
}
|
||||
|
||||
int3 MapView::getTileAtPoint(const Point & position) const
|
||||
{
|
||||
return tilesCache->getTileAtPoint(position);
|
||||
}
|
||||
|
||||
Point MapView::getViewCenter() const
|
||||
{
|
||||
return tilesCache->getViewCenter();
|
||||
}
|
||||
|
||||
void MapView::setViewCenter(const int3 & position)
|
||||
{
|
||||
setViewCenter(Point(position.x, position.y) * tileSize, position.z);
|
||||
}
|
||||
|
||||
void MapView::setViewCenter(const Point & position, int level)
|
||||
{
|
||||
tilesCache->setViewCenter(position, level);
|
||||
}
|
||||
|
||||
void MapView::show(SDL_Surface * to)
|
||||
{
|
||||
Canvas target(to);
|
||||
Canvas targetClipped(target, pos);
|
||||
|
||||
CSDL_Ext::CClipRectGuard guard(to, pos);
|
||||
|
||||
tilesCache->render(targetClipped);
|
||||
}
|
||||
|
||||
void MapView::showAll(SDL_Surface * to)
|
||||
{
|
||||
show(to);
|
||||
}
|
||||
|
||||
void MapRendererContext::advanceAnimations(uint32_t ms)
|
||||
{
|
||||
animationTime += ms;
|
||||
}
|
||||
|
||||
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->map->getTile(coordinates);
|
||||
}
|
||||
|
||||
MapRendererContext::ObjectsVector MapRendererContext::getAllObjects() const
|
||||
{
|
||||
return CGI->mh->map->objects;
|
||||
}
|
||||
|
||||
const CGObjectInstance * MapRendererContext::getObject(ObjectInstanceID objectID) const
|
||||
{
|
||||
return CGI->mh->map->objects.at(objectID.getNum());
|
||||
}
|
||||
|
||||
bool MapRendererContext::isVisible(const int3 & coordinates) const
|
||||
{
|
||||
return LOCPLINT->cb->isVisible(coordinates) || settings["session"]["spectate"].Bool();
|
||||
}
|
||||
|
||||
MapRendererContext::VisibilityMap MapRendererContext::getVisibilityMap() const
|
||||
{
|
||||
return LOCPLINT->cb->getVisibilityMap();
|
||||
}
|
||||
|
||||
uint32_t MapRendererContext::getAnimationPeriod() const
|
||||
{
|
||||
// H3 timing for adventure map objects animation is 180 ms
|
||||
// Terrain animations also use identical interval, however it is only present in HotA and/or HD Mod
|
||||
// TODO: duration of fade-in/fade-out for teleport, entering/leaving boat, removal of objects
|
||||
// TOOD: duration of hero movement animation, frame timing of hero movement animation, effect of hero speed option
|
||||
// TOOD: duration of enemy hero movement animation, frame timing of enemy hero movement animation, effect of enemy hero speed option
|
||||
return 180;
|
||||
}
|
||||
|
||||
uint32_t MapRendererContext::getAnimationTime() const
|
||||
{
|
||||
return animationTime;
|
||||
}
|
||||
|
||||
Point MapRendererContext::tileSize() const
|
||||
{
|
||||
return Point(32, 32);
|
||||
}
|
||||
|
||||
bool MapRendererContext::showGrid() const
|
||||
{
|
||||
return true; // settings["session"]["showGrid"].Bool();
|
||||
}
|
96
client/adventureMap/MapView.h
Normal file
96
client/adventureMap/MapView.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*
|
||||
* MapView.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 "../gui/CIntObject.h"
|
||||
|
||||
#include "MapRendererContext.h"
|
||||
|
||||
class Canvas;
|
||||
class MapRenderer;
|
||||
|
||||
class MapRendererContext : public IMapRendererContext
|
||||
{
|
||||
uint32_t animationTime = 0;
|
||||
|
||||
public:
|
||||
void advanceAnimations(uint32_t ms);
|
||||
|
||||
int3 getMapSize() const override;
|
||||
bool isInMap(const int3 & coordinates) const override;
|
||||
const TerrainTile & getMapTile(const int3 & coordinates) const override;
|
||||
|
||||
ObjectsVector getAllObjects() const override;
|
||||
const CGObjectInstance * getObject(ObjectInstanceID objectID) const override;
|
||||
|
||||
bool isVisible(const int3 & coordinates) const override;
|
||||
VisibilityMap getVisibilityMap() const override;
|
||||
|
||||
uint32_t getAnimationPeriod() const override;
|
||||
uint32_t getAnimationTime() const override;
|
||||
Point tileSize() const override;
|
||||
|
||||
bool showGrid() const override;
|
||||
};
|
||||
|
||||
class MapCache
|
||||
{
|
||||
std::unique_ptr<Canvas> terrain;
|
||||
|
||||
Point tileSize;
|
||||
Point viewCenter;
|
||||
Point viewDimensionsTiles;
|
||||
Point viewDimensionsPixels;
|
||||
Point targetDimensionsPixels;
|
||||
|
||||
int mapLevel;
|
||||
|
||||
std::unique_ptr<MapRendererContext> context;
|
||||
std::unique_ptr<MapRenderer> mapRenderer;
|
||||
|
||||
Canvas getTile(const int3 & coordinates);
|
||||
|
||||
void updateTile(const int3 & coordinates);
|
||||
|
||||
public:
|
||||
explicit MapCache(const Point & tileSize, const Point & dimensions);
|
||||
~MapCache();
|
||||
|
||||
void setViewCenter(const Point & center, int level);
|
||||
|
||||
Rect getVisibleAreaTiles() const;
|
||||
int3 getTileCenter() const;
|
||||
int3 getTileAtPoint(const Point & position) const;
|
||||
Point getViewCenter() const;
|
||||
|
||||
void update(uint32_t timeDelta);
|
||||
void render(Canvas & target);
|
||||
};
|
||||
|
||||
class MapView : public CIntObject
|
||||
{
|
||||
std::unique_ptr<MapCache> tilesCache;
|
||||
|
||||
Point tileSize;
|
||||
|
||||
public:
|
||||
MapView(const Point & offset, const Point & dimensions);
|
||||
|
||||
Rect getVisibleAreaTiles() const;
|
||||
Point getViewCenter() const;
|
||||
int3 getTileCenter() const;
|
||||
int3 getTileAtPoint(const Point & position) const;
|
||||
|
||||
void setViewCenter(const int3 & position);
|
||||
void setViewCenter(const Point & position, int level);
|
||||
|
||||
void show(SDL_Surface * to) override;
|
||||
void showAll(SDL_Surface * to) override;
|
||||
};
|
@ -14,11 +14,16 @@
|
||||
#include "../render/CAnimation.h"
|
||||
#include "../render/CFadeAnimation.h"
|
||||
#include "../render/Colors.h"
|
||||
#include "../gui/CGuiHandler.h"
|
||||
#include "../renderSDL/SDL_Extensions.h"
|
||||
#include "../CGameInfo.h"
|
||||
#include "../render/Graphics.h"
|
||||
#include "../render/IImage.h"
|
||||
#include "../render/Canvas.h"
|
||||
#include "../CMusicHandler.h"
|
||||
#include "../CPlayerInterface.h"
|
||||
|
||||
#include "../../CCallback.h"
|
||||
|
||||
#include "../../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../../lib/mapObjects/CObjectClassesHandler.h"
|
||||
@ -1483,4 +1488,5 @@ TerrainTileObject::TerrainTileObject(const CGObjectInstance * obj_, Rect rect_,
|
||||
|
||||
TerrainTileObject::~TerrainTileObject()
|
||||
{
|
||||
|
||||
}
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "../../lib/spells/ViewSpellInt.h"
|
||||
#include "../../lib/Rect.h"
|
||||
|
||||
#include "../gui/CIntObject.h"
|
||||
|
||||
#ifdef IN
|
||||
#undef IN
|
||||
#endif
|
||||
@ -346,7 +348,7 @@ public: //TODO: make private
|
||||
boost::multi_array<TerrainTile2, 3> ttiles; //informations about map tiles [z][x][y]
|
||||
int3 sizes; //map size (x = width, y = height, z = number of levels)
|
||||
const CMap * map;
|
||||
|
||||
private:
|
||||
// Max number of tiles that will fit in the map screen. Tiles
|
||||
// can be partial on each edges.
|
||||
int tilesW;
|
||||
|
@ -18,14 +18,14 @@
|
||||
|
||||
Canvas::Canvas(SDL_Surface * surface):
|
||||
surface(surface),
|
||||
renderOffset(0,0)
|
||||
renderArea(0,0, surface->w, surface->h)
|
||||
{
|
||||
surface->refcount++;
|
||||
}
|
||||
|
||||
Canvas::Canvas(const Canvas & other):
|
||||
surface(other.surface),
|
||||
renderOffset(other.renderOffset)
|
||||
renderArea(other.renderArea)
|
||||
{
|
||||
surface->refcount++;
|
||||
}
|
||||
@ -33,25 +33,23 @@ Canvas::Canvas(const Canvas & other):
|
||||
Canvas::Canvas(const Canvas & other, const Rect & newClipRect):
|
||||
Canvas(other)
|
||||
{
|
||||
clipRect.emplace();
|
||||
CSDL_Ext::getClipRect(surface, clipRect.get());
|
||||
//clipRect.emplace();
|
||||
//CSDL_Ext::getClipRect(surface, clipRect.get());
|
||||
|
||||
Rect currClipRect = newClipRect + renderOffset;
|
||||
CSDL_Ext::setClipRect(surface, currClipRect);
|
||||
|
||||
renderOffset += newClipRect.topLeft();
|
||||
renderArea = other.renderArea.intersect(newClipRect + other.renderArea.topLeft());
|
||||
//CSDL_Ext::setClipRect(surface, currClipRect);
|
||||
}
|
||||
|
||||
Canvas::Canvas(const Point & size):
|
||||
renderOffset(0,0),
|
||||
renderArea(Point(0,0), size),
|
||||
surface(CSDL_Ext::newSurface(size.x, size.y))
|
||||
{
|
||||
}
|
||||
|
||||
Canvas::~Canvas()
|
||||
{
|
||||
if (clipRect)
|
||||
CSDL_Ext::setClipRect(surface, clipRect.get());
|
||||
//if (clipRect)
|
||||
// CSDL_Ext::setClipRect(surface, clipRect.get());
|
||||
|
||||
SDL_FreeSurface(surface);
|
||||
}
|
||||
@ -60,19 +58,19 @@ void Canvas::draw(std::shared_ptr<IImage> image, const Point & pos)
|
||||
{
|
||||
assert(image);
|
||||
if (image)
|
||||
image->draw(surface, renderOffset.x + pos.x, renderOffset.y + pos.y);
|
||||
image->draw(surface, renderArea.x + pos.x, renderArea.y + pos.y);
|
||||
}
|
||||
|
||||
void Canvas::draw(std::shared_ptr<IImage> image, const Point & pos, const Rect & sourceRect)
|
||||
{
|
||||
assert(image);
|
||||
if (image)
|
||||
image->draw(surface, renderOffset.x + pos.x, renderOffset.y + pos.y, &sourceRect);
|
||||
image->draw(surface, renderArea.x + pos.x, renderArea.y + pos.y, &sourceRect);
|
||||
}
|
||||
|
||||
void Canvas::draw(Canvas & image, const Point & pos)
|
||||
{
|
||||
CSDL_Ext::blitAt(image.surface, renderOffset.x + pos.x, renderOffset.y + pos.y, surface);
|
||||
CSDL_Ext::blitSurface(image.surface, image.renderArea, surface, renderArea.topLeft() + pos);
|
||||
}
|
||||
|
||||
void Canvas::draw(Canvas & image, const Point & pos, const Point & targetSize)
|
||||
@ -88,17 +86,17 @@ void Canvas::drawPoint(const Point & dest, const ColorRGBA & color)
|
||||
|
||||
void Canvas::drawLine(const Point & from, const Point & dest, const ColorRGBA & colorFrom, const ColorRGBA & colorDest)
|
||||
{
|
||||
CSDL_Ext::drawLine(surface, renderOffset + from, renderOffset + dest, CSDL_Ext::toSDL(colorFrom), CSDL_Ext::toSDL(colorDest));
|
||||
CSDL_Ext::drawLine(surface, renderArea.topLeft() + from, renderArea.topLeft() + dest, CSDL_Ext::toSDL(colorFrom), CSDL_Ext::toSDL(colorDest));
|
||||
}
|
||||
|
||||
void Canvas::drawLineDashed(const Point & from, const Point & dest, const ColorRGBA & color)
|
||||
{
|
||||
CSDL_Ext::drawLineDashed(surface, renderOffset + from, renderOffset + dest, CSDL_Ext::toSDL(color));
|
||||
CSDL_Ext::drawLineDashed(surface, renderArea.topLeft() + from, renderArea.topLeft() + dest, CSDL_Ext::toSDL(color));
|
||||
}
|
||||
|
||||
void Canvas::drawBorderDashed(const Rect & target, const ColorRGBA & color)
|
||||
{
|
||||
Rect realTarget = target + renderOffset;
|
||||
Rect realTarget = target + renderArea.topLeft();
|
||||
|
||||
CSDL_Ext::drawLineDashed(surface, realTarget.topLeft(), realTarget.topRight(), CSDL_Ext::toSDL(color));
|
||||
CSDL_Ext::drawLineDashed(surface, realTarget.bottomLeft(), realTarget.bottomRight(), CSDL_Ext::toSDL(color));
|
||||
@ -110,9 +108,9 @@ void Canvas::drawText(const Point & position, const EFonts & font, const SDL_Col
|
||||
{
|
||||
switch (alignment)
|
||||
{
|
||||
case ETextAlignment::TOPLEFT: return graphics->fonts[font]->renderTextLeft (surface, text, colorDest, renderOffset + position);
|
||||
case ETextAlignment::CENTER: return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, renderOffset + position);
|
||||
case ETextAlignment::BOTTOMRIGHT: return graphics->fonts[font]->renderTextRight (surface, text, colorDest, renderOffset + position);
|
||||
case ETextAlignment::TOPLEFT: return graphics->fonts[font]->renderTextLeft (surface, text, colorDest, renderArea.topLeft() + position);
|
||||
case ETextAlignment::CENTER: return graphics->fonts[font]->renderTextCenter(surface, text, colorDest, renderArea.topLeft() + position);
|
||||
case ETextAlignment::BOTTOMRIGHT: return graphics->fonts[font]->renderTextRight (surface, text, colorDest, renderArea.topLeft() + position);
|
||||
}
|
||||
}
|
||||
|
||||
@ -120,9 +118,9 @@ void Canvas::drawText(const Point & position, const EFonts & font, const SDL_Col
|
||||
{
|
||||
switch (alignment)
|
||||
{
|
||||
case ETextAlignment::TOPLEFT: return graphics->fonts[font]->renderTextLinesLeft (surface, text, colorDest, renderOffset + position);
|
||||
case ETextAlignment::CENTER: return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, renderOffset + position);
|
||||
case ETextAlignment::BOTTOMRIGHT: return graphics->fonts[font]->renderTextLinesRight (surface, text, colorDest, renderOffset + position);
|
||||
case ETextAlignment::TOPLEFT: return graphics->fonts[font]->renderTextLinesLeft (surface, text, colorDest, renderArea.topLeft() + position);
|
||||
case ETextAlignment::CENTER: return graphics->fonts[font]->renderTextLinesCenter(surface, text, colorDest, renderArea.topLeft() + position);
|
||||
case ETextAlignment::BOTTOMRIGHT: return graphics->fonts[font]->renderTextLinesRight (surface, text, colorDest, renderArea.topLeft() + position);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -27,8 +27,8 @@ class Canvas
|
||||
/// Clip rect that was in use on surface originally and needs to be restored on destruction
|
||||
boost::optional<Rect> clipRect;
|
||||
|
||||
/// Current rendering area offset, all rendering operations will be moved into selected area
|
||||
Point renderOffset;
|
||||
/// Current rendering area, all rendering operations will be moved into selected area
|
||||
Rect renderArea;
|
||||
|
||||
Canvas & operator = (Canvas & other) = delete;
|
||||
public:
|
||||
|
@ -28,7 +28,7 @@ public:
|
||||
:x(X),y(Y)
|
||||
{};
|
||||
|
||||
Point(const int3 &a);
|
||||
explicit DLL_LINKAGE Point(const int3 &a);
|
||||
|
||||
template<typename T>
|
||||
Point operator+(const T &b) const
|
||||
@ -48,6 +48,11 @@ public:
|
||||
return Point(x*mul, y*mul);
|
||||
}
|
||||
|
||||
Point operator*(const Point &b) const
|
||||
{
|
||||
return Point(x*b.x,y*b.y);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
Point& operator+=(const T &b)
|
||||
{
|
||||
|
@ -10,9 +10,16 @@
|
||||
|
||||
#include "StdInc.h"
|
||||
#include "Rect.h"
|
||||
#include "int3.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
Point::Point(const int3 & a)
|
||||
: x(a.x)
|
||||
, y(a.x)
|
||||
{
|
||||
}
|
||||
|
||||
/// Returns rect union - rect that covers both this rect and provided rect
|
||||
Rect Rect::include(const Rect & other) const
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user