2012-01-30 01:00:12 +01:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2010-2012 OTClient <https://github.com/edubart/otclient>
|
|
|
|
*
|
|
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
|
|
* in the Software without restriction, including without limitation the rights
|
|
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
|
|
* furnished to do so, subject to the following conditions:
|
|
|
|
*
|
|
|
|
* The above copyright notice and this permission notice shall be included in
|
|
|
|
* all copies or substantial portions of the Software.
|
|
|
|
*
|
|
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
* THE SOFTWARE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "mapview.h"
|
|
|
|
|
|
|
|
#include "creature.h"
|
|
|
|
#include "map.h"
|
|
|
|
#include "tile.h"
|
|
|
|
#include "statictext.h"
|
|
|
|
#include "animatedtext.h"
|
2012-02-02 21:54:49 +01:00
|
|
|
#include "missile.h"
|
2012-06-14 20:26:55 +02:00
|
|
|
#include "shadermanager.h"
|
2012-06-18 10:13:52 +02:00
|
|
|
|
|
|
|
#include <framework/graphics/graphics.h>
|
|
|
|
#include <framework/graphics/framebuffermanager.h>
|
2012-01-31 18:06:55 +01:00
|
|
|
#include <framework/core/eventdispatcher.h>
|
2012-07-14 03:10:24 +02:00
|
|
|
#include <framework/core/application.h>
|
2012-01-30 01:00:12 +01:00
|
|
|
|
2012-07-29 12:32:44 +02:00
|
|
|
|
|
|
|
enum {
|
|
|
|
// 3840x2160 => 1080p optimized
|
|
|
|
// 2560x1440 => 720p optimized
|
|
|
|
// 1728x972 => 480p optimized
|
|
|
|
|
|
|
|
NEAR_VIEW_AREA = 32*32,
|
|
|
|
MID_VIEW_AREA = 64*64,
|
|
|
|
FAR_VIEW_AREA = 128*128,
|
|
|
|
MAX_TILE_DRAWS = NEAR_VIEW_AREA*7
|
|
|
|
};
|
|
|
|
|
2012-01-30 01:00:12 +01:00
|
|
|
MapView::MapView()
|
|
|
|
{
|
2012-04-07 01:12:46 +02:00
|
|
|
m_viewMode = NEAR_VIEW;
|
2012-03-18 14:34:39 +01:00
|
|
|
m_lockedFirstVisibleFloor = -1;
|
2012-04-07 01:12:46 +02:00
|
|
|
m_cachedFirstVisibleFloor = 7;
|
2012-03-18 14:34:39 +01:00
|
|
|
m_cachedLastVisibleFloor = 7;
|
2012-07-19 08:34:22 +02:00
|
|
|
m_updateTilesPos = 0;
|
2012-04-07 01:12:46 +02:00
|
|
|
m_optimizedSize = Size(Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES) * Otc::TILE_PIXELS;
|
2012-03-18 14:34:39 +01:00
|
|
|
|
2012-06-18 10:13:52 +02:00
|
|
|
m_framebuffer = g_framebuffers.createFrameBuffer();
|
2012-01-30 01:00:12 +01:00
|
|
|
setVisibleDimension(Size(15, 11));
|
|
|
|
|
2012-06-14 20:26:55 +02:00
|
|
|
m_shader = g_shaders.getDefaultMapShader();
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
|
|
|
|
2012-06-18 10:13:52 +02:00
|
|
|
MapView::~MapView()
|
|
|
|
{
|
2012-07-30 14:29:13 +02:00
|
|
|
#ifndef NDEBUG
|
2012-07-31 16:42:26 +02:00
|
|
|
assert(!g_app.isTerminated());
|
2012-07-30 14:29:13 +02:00
|
|
|
#endif
|
2012-06-18 10:13:52 +02:00
|
|
|
}
|
|
|
|
|
2012-01-30 01:00:12 +01:00
|
|
|
void MapView::draw(const Rect& rect)
|
|
|
|
{
|
|
|
|
// update visible tiles cache when needed
|
2012-07-19 08:34:22 +02:00
|
|
|
if(m_mustUpdateVisibleTilesCache || m_updateTilesPos > 0)
|
|
|
|
updateVisibleTilesCache(m_mustUpdateVisibleTilesCache ? 0 : m_updateTilesPos);
|
2012-01-30 01:00:12 +01:00
|
|
|
|
|
|
|
float scaleFactor = m_tileSize/(float)Otc::TILE_PIXELS;
|
2012-01-30 04:11:05 +01:00
|
|
|
Position cameraPosition = getCameraPosition();
|
2012-01-30 01:00:12 +01:00
|
|
|
|
2012-02-02 21:54:49 +01:00
|
|
|
int drawFlags = 0;
|
2012-04-07 01:12:46 +02:00
|
|
|
if(m_viewMode == NEAR_VIEW)
|
2012-02-02 21:54:49 +01:00
|
|
|
drawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls |
|
|
|
|
Otc::DrawItems | Otc::DrawCreatures | Otc::DrawEffects | Otc::DrawMissiles | Otc::DrawAnimations;
|
2012-07-19 08:34:22 +02:00
|
|
|
else
|
2012-02-02 21:54:49 +01:00
|
|
|
drawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls | Otc::DrawItems;
|
2012-01-30 19:18:10 +01:00
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
Size tileSize = Size(1,1) * m_tileSize;
|
2012-02-02 21:54:49 +01:00
|
|
|
if(m_mustDrawVisibleTilesCache || (drawFlags & Otc::DrawAnimations)) {
|
2012-04-04 22:18:24 +02:00
|
|
|
m_framebuffer->bind();
|
|
|
|
|
|
|
|
if(m_mustCleanFramebuffer) {
|
|
|
|
Rect clearRect = Rect(0, 0, m_drawDimension * m_tileSize);
|
2012-06-11 01:48:53 +02:00
|
|
|
g_painter->setColor(Color::black);
|
|
|
|
g_painter->drawFilledRect(clearRect);
|
2012-04-04 22:18:24 +02:00
|
|
|
}
|
2012-06-11 01:48:53 +02:00
|
|
|
g_painter->setColor(Color::white);
|
2012-01-30 19:18:10 +01:00
|
|
|
|
2012-02-02 21:54:49 +01:00
|
|
|
auto it = m_cachedVisibleTiles.begin();
|
|
|
|
auto end = m_cachedVisibleTiles.end();
|
|
|
|
for(int z=m_cachedLastVisibleFloor;z>=m_cachedFirstVisibleFloor;--z) {
|
|
|
|
while(it != end) {
|
|
|
|
const TilePtr& tile = *it;
|
2012-04-26 21:54:16 +02:00
|
|
|
Position tilePos = tile->getPosition();
|
|
|
|
if(tilePos.z != z)
|
2012-02-02 21:54:49 +01:00
|
|
|
break;
|
|
|
|
else
|
|
|
|
++it;
|
2012-01-30 19:18:10 +01:00
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
if(!m_drawMinimapColors)
|
2012-06-05 17:36:27 +02:00
|
|
|
tile->draw(transformPositionTo2D(tilePos, cameraPosition), scaleFactor, drawFlags);
|
2012-04-07 01:12:46 +02:00
|
|
|
else {
|
2012-06-22 07:26:22 +02:00
|
|
|
uint8 c = tile->getMinimapColorByte();
|
|
|
|
if(c == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
g_painter->setColor(Color::from8bit(c));
|
2012-06-05 17:36:27 +02:00
|
|
|
g_painter->drawFilledRect(Rect(transformPositionTo2D(tilePos, cameraPosition), tileSize));
|
2012-04-07 01:12:46 +02:00
|
|
|
}
|
2012-02-02 21:54:49 +01:00
|
|
|
}
|
2012-01-31 18:06:55 +01:00
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
if(drawFlags & Otc::DrawMissiles && !m_drawMinimapColors) {
|
2012-02-02 21:54:49 +01:00
|
|
|
for(const MissilePtr& missile : g_map.getFloorMissiles(z)) {
|
2012-06-05 17:36:27 +02:00
|
|
|
missile->draw(transformPositionTo2D(missile->getPosition(), cameraPosition), scaleFactor, drawFlags & Otc::DrawAnimations);
|
2012-02-02 21:54:49 +01:00
|
|
|
}
|
|
|
|
}
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
2012-03-21 13:41:43 +01:00
|
|
|
|
2012-01-30 01:00:12 +01:00
|
|
|
m_framebuffer->release();
|
2012-01-31 18:06:55 +01:00
|
|
|
|
2012-03-21 13:41:43 +01:00
|
|
|
// generating mipmaps each frame can be slow in older cards
|
2012-06-14 20:26:55 +02:00
|
|
|
//m_framebuffer->getTexture()->buildHardwareMipmaps();
|
2012-03-21 13:41:43 +01:00
|
|
|
|
2012-01-31 18:06:55 +01:00
|
|
|
m_mustDrawVisibleTilesCache = false;
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-01-31 18:06:55 +01:00
|
|
|
Point drawOffset = ((m_drawDimension - m_visibleDimension - Size(1,1)).toPoint()/2) * m_tileSize;
|
2012-07-09 13:37:47 +02:00
|
|
|
if(isFollowingCreature())
|
2012-01-30 01:00:12 +01:00
|
|
|
drawOffset += m_followingCreature->getWalkOffset() * scaleFactor;
|
2012-04-07 01:12:46 +02:00
|
|
|
|
|
|
|
Size srcSize = rect.size();
|
|
|
|
Size srcVisible = m_visibleDimension * m_tileSize;
|
|
|
|
srcSize.scale(srcVisible, Fw::KeepAspectRatio);
|
|
|
|
drawOffset.x += (srcVisible.width() - srcSize.width()) / 2;
|
|
|
|
drawOffset.y += (srcVisible.height() - srcSize.height()) / 2;
|
|
|
|
Rect srcRect = Rect(drawOffset, srcSize);
|
|
|
|
|
2012-04-28 00:17:51 +02:00
|
|
|
g_painter->setColor(Color::white);
|
2012-06-09 15:00:08 +02:00
|
|
|
glDisable(GL_BLEND);
|
2012-06-14 20:26:55 +02:00
|
|
|
g_painter->setShaderProgram(m_shader);
|
2012-04-07 01:12:46 +02:00
|
|
|
#if 0
|
|
|
|
// debug source area
|
2012-04-19 01:03:43 +02:00
|
|
|
g_painter->saveAndResetState();
|
2012-04-07 01:12:46 +02:00
|
|
|
m_framebuffer->bind();
|
2012-04-19 01:03:43 +02:00
|
|
|
g_painter->setColor(Color::green);
|
|
|
|
g_painter->drawBoundingRect(srcRect, 2);
|
2012-04-07 01:12:46 +02:00
|
|
|
m_framebuffer->release();
|
2012-04-19 01:03:43 +02:00
|
|
|
g_painter->restoreSavedState();
|
2012-04-07 01:12:46 +02:00
|
|
|
m_framebuffer->draw(rect);
|
|
|
|
#else
|
2012-01-30 01:00:12 +01:00
|
|
|
m_framebuffer->draw(rect, srcRect);
|
2012-04-07 01:12:46 +02:00
|
|
|
#endif
|
2012-06-14 20:26:55 +02:00
|
|
|
g_painter->resetShaderProgram();
|
2012-06-09 15:00:08 +02:00
|
|
|
glEnable(GL_BLEND);
|
2012-06-02 01:21:45 +02:00
|
|
|
|
2012-01-30 01:00:12 +01:00
|
|
|
|
2012-01-30 04:11:05 +01:00
|
|
|
// this could happen if the player position is not known yet
|
|
|
|
if(!cameraPosition.isValid())
|
|
|
|
return;
|
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
float horizontalStretchFactor = rect.width() / (float)srcRect.width();
|
|
|
|
float verticalStretchFactor = rect.height() / (float)srcRect.height();
|
2012-01-30 01:00:12 +01:00
|
|
|
|
|
|
|
// avoid drawing texts on map in far zoom outs
|
2012-04-07 01:12:46 +02:00
|
|
|
if(m_viewMode == NEAR_VIEW && m_drawTexts) {
|
2012-01-30 01:00:12 +01:00
|
|
|
for(const CreaturePtr& creature : m_cachedFloorVisibleCreatures) {
|
2012-04-22 02:45:05 +02:00
|
|
|
Point creatureOffset = Point(16 - creature->getDisplacementX(), -3 - creature->getDisplacementY());
|
2012-02-02 22:20:34 +01:00
|
|
|
Position pos = creature->getPosition();
|
2012-06-05 17:36:27 +02:00
|
|
|
Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
|
2012-04-22 02:45:05 +02:00
|
|
|
p += (creature->getDrawOffset() + creatureOffset) * scaleFactor;
|
2012-01-30 01:00:12 +01:00
|
|
|
p.x = p.x * horizontalStretchFactor;
|
|
|
|
p.y = p.y * verticalStretchFactor;
|
|
|
|
p += rect.topLeft();
|
|
|
|
|
|
|
|
creature->drawInformation(p, g_map.isCovered(pos, m_cachedFirstVisibleFloor), rect);
|
|
|
|
}
|
|
|
|
|
|
|
|
for(const StaticTextPtr& staticText : g_map.getStaticTexts()) {
|
|
|
|
Position pos = staticText->getPosition();
|
|
|
|
|
|
|
|
// ony draw static texts from current camera floor, unless yells
|
2012-06-16 00:18:30 +02:00
|
|
|
//if(pos.z != cameraPosition.z && !staticText->isYell())
|
|
|
|
// continue;
|
2012-01-30 01:00:12 +01:00
|
|
|
|
2012-06-05 17:36:27 +02:00
|
|
|
Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
|
2012-01-30 01:00:12 +01:00
|
|
|
p.x = p.x * horizontalStretchFactor;
|
|
|
|
p.y = p.y * verticalStretchFactor;
|
|
|
|
p += rect.topLeft();
|
2012-06-17 23:47:05 +02:00
|
|
|
staticText->drawText(p, rect);
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for(const AnimatedTextPtr& animatedText : g_map.getAnimatedTexts()) {
|
|
|
|
Position pos = animatedText->getPosition();
|
|
|
|
|
|
|
|
// only draw animated texts from visible floors
|
|
|
|
if(pos.z < m_cachedFirstVisibleFloor || pos.z > m_cachedLastVisibleFloor)
|
|
|
|
continue;
|
|
|
|
|
2012-01-31 05:12:54 +01:00
|
|
|
// dont draw animated texts from covered tiles
|
|
|
|
if(pos.z != cameraPosition.z && g_map.isCovered(pos, m_cachedFirstVisibleFloor))
|
|
|
|
continue;
|
|
|
|
|
2012-06-05 17:36:27 +02:00
|
|
|
Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
|
2012-01-30 01:00:12 +01:00
|
|
|
p.x = p.x * horizontalStretchFactor;
|
|
|
|
p.y = p.y * verticalStretchFactor;
|
|
|
|
p += rect.topLeft();
|
2012-06-17 23:47:05 +02:00
|
|
|
animatedText->drawText(p, rect);
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
2012-04-07 01:12:46 +02:00
|
|
|
} else if(m_viewMode > NEAR_VIEW) {
|
2012-02-02 15:07:02 +01:00
|
|
|
// draw a cross in the center instead of our creature
|
2012-07-09 13:37:47 +02:00
|
|
|
/*
|
|
|
|
Known Issue: Changing Z axis causes the cross to go off a little bit.
|
|
|
|
*/
|
2012-02-02 15:07:02 +01:00
|
|
|
Rect vRect(0, 0, 2, 10);
|
|
|
|
Rect hRect(0, 0, 10, 2);
|
2012-04-19 01:03:43 +02:00
|
|
|
g_painter->setColor(Color::white);
|
2012-07-09 13:37:47 +02:00
|
|
|
|
|
|
|
if(!m_follow && m_followingCreature)
|
|
|
|
{
|
|
|
|
Position pos = m_followingCreature->getPosition();
|
|
|
|
Point p = transformPositionTo2D(pos, cameraPosition) - drawOffset;
|
|
|
|
p.x = p.x * horizontalStretchFactor;
|
|
|
|
p.y = p.y * verticalStretchFactor;
|
|
|
|
p += rect.topLeft();
|
|
|
|
|
|
|
|
vRect.setX(p.x); vRect.setY(p.y - 4);
|
|
|
|
hRect.setX(p.x - 4); hRect.setY(p.y);
|
|
|
|
|
|
|
|
hRect.setWidth(10); hRect.setHeight(2);
|
|
|
|
vRect.setWidth(2); vRect.setHeight(10);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
vRect.moveCenter(rect.center());
|
|
|
|
hRect.moveCenter(rect.center());
|
|
|
|
}
|
|
|
|
|
2012-04-19 01:03:43 +02:00
|
|
|
g_painter->drawFilledRect(vRect);
|
|
|
|
g_painter->drawFilledRect(hRect);
|
2012-01-30 19:18:10 +01:00
|
|
|
}
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
|
|
|
|
2012-01-31 18:06:55 +01:00
|
|
|
void MapView::updateVisibleTilesCache(int start)
|
2012-01-30 01:00:12 +01:00
|
|
|
{
|
2012-01-31 18:06:55 +01:00
|
|
|
if(start == 0) {
|
2012-03-28 21:09:45 +02:00
|
|
|
m_cachedFirstVisibleFloor = calcFirstVisibleFloor();
|
|
|
|
m_cachedLastVisibleFloor = calcLastVisibleFloor();
|
|
|
|
assert(m_cachedFirstVisibleFloor >= 0 && m_cachedLastVisibleFloor >= 0 &&
|
|
|
|
m_cachedFirstVisibleFloor <= Otc::MAX_Z && m_cachedLastVisibleFloor <= Otc::MAX_Z);
|
2012-01-30 22:28:08 +01:00
|
|
|
|
2012-01-31 18:06:55 +01:00
|
|
|
if(m_cachedLastVisibleFloor < m_cachedFirstVisibleFloor)
|
|
|
|
m_cachedLastVisibleFloor = m_cachedFirstVisibleFloor;
|
2012-01-30 22:28:08 +01:00
|
|
|
|
2012-01-31 18:06:55 +01:00
|
|
|
m_cachedFloorVisibleCreatures.clear();
|
|
|
|
m_cachedVisibleTiles.clear();
|
2012-01-30 01:00:12 +01:00
|
|
|
|
2012-01-31 18:06:55 +01:00
|
|
|
m_mustCleanFramebuffer = true;
|
|
|
|
m_mustDrawVisibleTilesCache = true;
|
|
|
|
m_mustUpdateVisibleTilesCache = false;
|
2012-07-19 08:34:22 +02:00
|
|
|
m_updateTilesPos = 0;
|
2012-02-01 04:47:00 +01:00
|
|
|
} else
|
|
|
|
m_mustCleanFramebuffer = false;
|
2012-01-30 04:11:05 +01:00
|
|
|
|
|
|
|
// there is no tile to render on invalid positions
|
2012-01-31 18:06:55 +01:00
|
|
|
Position cameraPosition = getCameraPosition();
|
2012-01-30 04:11:05 +01:00
|
|
|
if(!cameraPosition.isValid())
|
2012-01-31 18:06:55 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
bool stop = false;
|
|
|
|
|
|
|
|
// clear current visible tiles cache
|
|
|
|
m_cachedVisibleTiles.clear();
|
|
|
|
m_mustDrawVisibleTilesCache = true;
|
2012-07-19 08:34:22 +02:00
|
|
|
m_updateTilesPos = 0;
|
2012-01-30 01:00:12 +01:00
|
|
|
|
|
|
|
// cache visible tiles in draw order
|
|
|
|
// draw from last floor (the lower) to first floor (the higher)
|
2012-01-31 18:06:55 +01:00
|
|
|
for(int iz = m_cachedLastVisibleFloor; iz >= m_cachedFirstVisibleFloor && !stop; --iz) {
|
2012-04-07 01:12:46 +02:00
|
|
|
if(m_viewMode <= FAR_VIEW) {
|
2012-01-31 21:50:35 +01:00
|
|
|
const int numDiagonals = m_drawDimension.width() + m_drawDimension.height() - 1;
|
2012-02-03 00:01:57 +01:00
|
|
|
// loop through / diagonals beginning at top left and going to top right
|
2012-01-31 21:50:35 +01:00
|
|
|
for(int diagonal = 0; diagonal < numDiagonals && !stop; ++diagonal) {
|
2012-02-03 00:01:57 +01:00
|
|
|
// loop current diagonal tiles
|
2012-04-07 01:12:46 +02:00
|
|
|
int advance = std::max(diagonal - m_drawDimension.height(), 0);
|
|
|
|
for(int iy = diagonal - advance, ix = advance; iy >= 0 && ix < m_drawDimension.width() && !stop; --iy, ++ix) {
|
2012-01-31 21:50:35 +01:00
|
|
|
// only start really looking tiles in the desired start
|
2012-07-19 08:34:22 +02:00
|
|
|
if(m_updateTilesPos < start) {
|
|
|
|
m_updateTilesPos++;
|
2012-01-31 21:50:35 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2012-07-19 08:34:22 +02:00
|
|
|
// avoid rendering too much tiles at once
|
|
|
|
if((int)m_cachedVisibleTiles.size() > MAX_TILE_DRAWS && m_viewMode >= HUGE_VIEW) {
|
2012-01-31 21:50:35 +01:00
|
|
|
stop = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// position on current floor
|
|
|
|
//TODO: check position limits
|
|
|
|
Position tilePos = cameraPosition.translated(ix - m_virtualCenterOffset.x, iy - m_virtualCenterOffset.y);
|
|
|
|
// adjust tilePos to the wanted floor
|
|
|
|
tilePos.coveredUp(cameraPosition.z - iz);
|
|
|
|
if(const TilePtr& tile = g_map.getTile(tilePos)) {
|
|
|
|
// skip tiles that have nothing
|
|
|
|
if(tile->isEmpty())
|
|
|
|
continue;
|
|
|
|
// skip tiles that are completely behind another tile
|
2012-02-01 04:47:00 +01:00
|
|
|
if(g_map.isCompletelyCovered(tilePos, m_cachedFirstVisibleFloor))
|
2012-01-31 21:50:35 +01:00
|
|
|
continue;
|
|
|
|
m_cachedVisibleTiles.push_back(tile);
|
|
|
|
}
|
2012-07-19 08:34:22 +02:00
|
|
|
m_updateTilesPos++;
|
2012-01-31 18:06:55 +01:00
|
|
|
}
|
2012-01-31 21:50:35 +01:00
|
|
|
}
|
|
|
|
} else {
|
2012-07-19 08:34:22 +02:00
|
|
|
// cache tiles in spiral mode
|
|
|
|
static std::vector<Point> m_spiral;
|
|
|
|
if(start == 0) {
|
|
|
|
m_spiral.resize(m_drawDimension.area());
|
|
|
|
int width = m_drawDimension.width();
|
|
|
|
int height = m_drawDimension.height();
|
|
|
|
int tpx = width/2 - 2;
|
|
|
|
int tpy = height/2 - 2;
|
|
|
|
int count = 0;
|
|
|
|
Rect area(0, 0, m_drawDimension);
|
|
|
|
m_spiral[count++] = Point(tpx+1,tpy+1);
|
|
|
|
for(int step = 1; tpx >= 0 || tpy >= 0; ++step, --tpx, --tpy) {
|
|
|
|
int qs = 2*step;
|
|
|
|
Rect lines[4] = {
|
|
|
|
Rect(tpx, tpy, qs, 1),
|
|
|
|
Rect(tpx + qs, tpy, 1, qs),
|
|
|
|
Rect(tpx + 1, tpy + qs, qs, 1),
|
|
|
|
Rect(tpx, tpy + 1, 1, qs),
|
|
|
|
};
|
|
|
|
|
|
|
|
for(int i=0;i<4;++i) {
|
|
|
|
int sx = std::max(lines[i].left(), area.left());
|
|
|
|
int ex = std::min(lines[i].right(), area.right());
|
|
|
|
int sy = std::max(lines[i].top(), area.top());
|
|
|
|
int ey = std::min(lines[i].bottom(), area.bottom());
|
|
|
|
for(int qx=sx;qx<=ex;++qx)
|
|
|
|
for(int qy=sy;qy<=ey;++qy)
|
|
|
|
m_spiral[count++] = Point(qx, qy);
|
2012-01-31 21:50:35 +01:00
|
|
|
}
|
2012-01-31 18:06:55 +01:00
|
|
|
}
|
2012-07-19 08:34:22 +02:00
|
|
|
}
|
2012-01-31 21:50:35 +01:00
|
|
|
|
2012-07-19 08:34:22 +02:00
|
|
|
for(m_updateTilesPos = start; m_updateTilesPos < (int)m_spiral.size(); ++m_updateTilesPos) {
|
|
|
|
// avoid rendering too much tiles at once
|
|
|
|
if((int)m_cachedVisibleTiles.size() > MAX_TILE_DRAWS) {
|
|
|
|
stop = true;
|
|
|
|
break;
|
2012-01-31 21:50:35 +01:00
|
|
|
}
|
2012-01-31 18:06:55 +01:00
|
|
|
|
2012-07-19 08:34:22 +02:00
|
|
|
const Point& p = m_spiral[m_updateTilesPos];
|
|
|
|
Position tilePos = cameraPosition.translated(p.x - m_virtualCenterOffset.x, p.y - m_virtualCenterOffset.y);
|
2012-01-30 01:00:12 +01:00
|
|
|
tilePos.coveredUp(cameraPosition.z - iz);
|
2012-01-30 19:18:10 +01:00
|
|
|
if(const TilePtr& tile = g_map.getTile(tilePos)) {
|
2012-07-19 08:34:22 +02:00
|
|
|
if(!tile->isEmpty())
|
|
|
|
m_cachedVisibleTiles.push_back(tile);
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2012-01-31 18:06:55 +01:00
|
|
|
}
|
2012-01-30 01:00:12 +01:00
|
|
|
|
2012-07-19 08:34:22 +02:00
|
|
|
if(!stop) {
|
|
|
|
m_updateTilesPos = 0;
|
|
|
|
m_spiral.clear();
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
2012-04-07 01:12:46 +02:00
|
|
|
|
|
|
|
if(start == 0 && m_drawTexts && m_viewMode <= NEAR_VIEW)
|
2012-01-31 18:06:55 +01:00
|
|
|
m_cachedFloorVisibleCreatures = g_map.getSpectators(cameraPosition, false);
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
void MapView::updateGeometry(const Size& visibleDimension, const Size& optimizedSize)
|
|
|
|
{
|
|
|
|
int tileSize = 0;
|
|
|
|
Size bufferSize;
|
|
|
|
|
2012-06-22 07:26:22 +02:00
|
|
|
if(!m_drawMinimapColors) {
|
|
|
|
int possiblesTileSizes[] = {1,2,4,8,16,32};
|
|
|
|
for(int candidateTileSize : possiblesTileSizes) {
|
|
|
|
bufferSize = (visibleDimension + Size(3,3)) * candidateTileSize;
|
|
|
|
if(bufferSize.width() > g_graphics.getMaxTextureSize() || bufferSize.height() > g_graphics.getMaxTextureSize())
|
|
|
|
break;
|
2012-04-07 01:12:46 +02:00
|
|
|
|
2012-06-22 07:26:22 +02:00
|
|
|
tileSize = candidateTileSize;
|
|
|
|
if(optimizedSize.width() < bufferSize.width() - 3*candidateTileSize && optimizedSize.height() < bufferSize.height() - 3*candidateTileSize)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(tileSize == 0) {
|
|
|
|
g_logger.traceError("reached max zoom out");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
tileSize = 1;
|
|
|
|
bufferSize = visibleDimension + Size(3,3);
|
2012-04-07 01:12:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
Size drawDimension = visibleDimension + Size(3,3);
|
|
|
|
Point virtualCenterOffset = (drawDimension/2 - Size(1,1)).toPoint();
|
|
|
|
Point visibleCenterOffset = virtualCenterOffset;
|
|
|
|
|
|
|
|
ViewMode viewMode = m_viewMode;
|
|
|
|
if(m_autoViewMode) {
|
|
|
|
if(tileSize >= 32 && visibleDimension.area() <= NEAR_VIEW_AREA)
|
|
|
|
viewMode = NEAR_VIEW;
|
|
|
|
else if(tileSize >= 16 && visibleDimension.area() <= MID_VIEW_AREA)
|
|
|
|
viewMode = MID_VIEW;
|
|
|
|
else if(tileSize >= 8 && visibleDimension.area() <= FAR_VIEW_AREA)
|
|
|
|
viewMode = FAR_VIEW;
|
|
|
|
else
|
|
|
|
viewMode = HUGE_VIEW;
|
2012-07-19 08:34:22 +02:00
|
|
|
|
|
|
|
if(viewMode >= FAR_VIEW)
|
|
|
|
m_multifloor = false;
|
|
|
|
else
|
|
|
|
m_multifloor = true;
|
2012-04-07 01:12:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// draw actually more than what is needed to avoid massive recalculations on huge views
|
|
|
|
/*
|
|
|
|
if(viewMode >= HUGE_VIEW) {
|
|
|
|
Size oldDimension = drawDimension;
|
|
|
|
drawDimension = (m_framebuffer->getSize() / tileSize);
|
|
|
|
virtualCenterOffset += (drawDimension - oldDimension).toPoint() / 2;
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
2012-07-19 08:34:22 +02:00
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
m_viewMode = viewMode;
|
|
|
|
m_visibleDimension = visibleDimension;
|
|
|
|
m_drawDimension = drawDimension;
|
|
|
|
m_tileSize = tileSize;
|
|
|
|
m_virtualCenterOffset = virtualCenterOffset;
|
|
|
|
m_visibleCenterOffset = visibleCenterOffset;
|
|
|
|
m_optimizedSize = optimizedSize;
|
|
|
|
m_framebuffer->resize(bufferSize);
|
|
|
|
|
|
|
|
requestVisibleTilesCacheUpdate();
|
|
|
|
}
|
|
|
|
|
2012-01-30 01:00:12 +01:00
|
|
|
void MapView::onTileUpdate(const Position& pos)
|
|
|
|
{
|
2012-06-22 07:26:22 +02:00
|
|
|
if(!m_drawMinimapColors)
|
2012-01-31 18:06:55 +01:00
|
|
|
requestVisibleTilesCacheUpdate();
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
|
|
|
|
2012-06-22 07:26:22 +02:00
|
|
|
void MapView::onMapCenterChange(const Position& pos)
|
|
|
|
{
|
|
|
|
requestVisibleTilesCacheUpdate();
|
|
|
|
}
|
|
|
|
|
2012-01-30 01:00:12 +01:00
|
|
|
void MapView::lockFirstVisibleFloor(int firstVisibleFloor)
|
|
|
|
{
|
|
|
|
m_lockedFirstVisibleFloor = firstVisibleFloor;
|
|
|
|
requestVisibleTilesCacheUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MapView::unlockFirstVisibleFloor()
|
|
|
|
{
|
|
|
|
m_lockedFirstVisibleFloor = -1;
|
|
|
|
requestVisibleTilesCacheUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void MapView::setVisibleDimension(const Size& visibleDimension)
|
|
|
|
{
|
2012-04-07 01:12:46 +02:00
|
|
|
if(visibleDimension == m_visibleDimension)
|
|
|
|
return;
|
|
|
|
|
2012-01-30 01:00:12 +01:00
|
|
|
if(visibleDimension.width() % 2 != 1 || visibleDimension.height() % 2 != 1) {
|
2012-06-01 22:39:23 +02:00
|
|
|
g_logger.traceError("visible dimension must be odd");
|
2012-01-30 01:00:12 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-02-04 01:43:28 +01:00
|
|
|
if(visibleDimension < Size(3,3)) {
|
2012-06-01 22:39:23 +02:00
|
|
|
g_logger.traceError("reach max zoom in");
|
2012-01-31 18:06:55 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
updateGeometry(visibleDimension, m_optimizedSize);
|
|
|
|
}
|
2012-01-31 18:06:55 +01:00
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
void MapView::setViewMode(MapView::ViewMode viewMode)
|
|
|
|
{
|
|
|
|
m_viewMode = viewMode;
|
|
|
|
requestVisibleTilesCacheUpdate();
|
|
|
|
}
|
2012-01-30 01:00:12 +01:00
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
void MapView::setAutoViewMode(bool enable)
|
|
|
|
{
|
|
|
|
m_autoViewMode = enable;
|
|
|
|
if(enable)
|
|
|
|
updateGeometry(m_visibleDimension, m_optimizedSize);
|
|
|
|
}
|
2012-01-31 18:06:55 +01:00
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
void MapView::optimizeForSize(const Size& visibleSize)
|
|
|
|
{
|
|
|
|
updateGeometry(m_visibleDimension, visibleSize);
|
|
|
|
}
|
2012-01-31 18:06:55 +01:00
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
void MapView::followCreature(const CreaturePtr& creature)
|
|
|
|
{
|
2012-07-09 13:37:47 +02:00
|
|
|
m_follow = true;
|
2012-04-07 01:12:46 +02:00
|
|
|
m_followingCreature = creature;
|
|
|
|
requestVisibleTilesCacheUpdate();
|
|
|
|
}
|
2012-01-30 01:00:12 +01:00
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
void MapView::setCameraPosition(const Position& pos)
|
|
|
|
{
|
2012-07-09 13:37:47 +02:00
|
|
|
m_follow = false;
|
2012-04-07 01:12:46 +02:00
|
|
|
m_customCameraPosition = pos;
|
|
|
|
requestVisibleTilesCacheUpdate();
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
|
|
|
|
2012-03-28 21:09:45 +02:00
|
|
|
int MapView::calcFirstVisibleFloor()
|
2012-01-30 01:00:12 +01:00
|
|
|
{
|
2012-03-28 21:09:45 +02:00
|
|
|
int z = 7;
|
2012-01-30 01:00:12 +01:00
|
|
|
// return forced first visible floor
|
2012-03-28 21:09:45 +02:00
|
|
|
if(m_lockedFirstVisibleFloor != -1) {
|
|
|
|
z = m_lockedFirstVisibleFloor;
|
|
|
|
} else {
|
|
|
|
Position cameraPosition = getCameraPosition();
|
2012-01-30 01:00:12 +01:00
|
|
|
|
2012-03-28 21:32:18 +02:00
|
|
|
// this could happens if the player is not known yet
|
|
|
|
if(cameraPosition.isValid()) {
|
|
|
|
// avoid rendering multifloors in far views
|
2012-07-19 08:34:22 +02:00
|
|
|
if(!m_multifloor) {
|
2012-03-28 21:32:18 +02:00
|
|
|
z = cameraPosition.z;
|
|
|
|
} else {
|
|
|
|
// if nothing is limiting the view, the first visible floor is 0
|
|
|
|
int firstFloor = 0;
|
|
|
|
|
|
|
|
// limits to underground floors while under sea level
|
|
|
|
if(cameraPosition.z > Otc::SEA_FLOOR)
|
|
|
|
firstFloor = std::max(cameraPosition.z - Otc::AWARE_UNDEGROUND_FLOOR_RANGE, (int)Otc::UNDERGROUND_FLOOR);
|
|
|
|
|
|
|
|
// loop in 3x3 tiles around the camera
|
|
|
|
for(int ix = -1; ix <= 1 && firstFloor < cameraPosition.z; ++ix) {
|
|
|
|
for(int iy = -1; iy <= 1 && firstFloor < cameraPosition.z; ++iy) {
|
|
|
|
Position pos = cameraPosition.translated(ix, iy);
|
|
|
|
|
|
|
|
// process tiles that we can look through, e.g. windows, doors
|
|
|
|
if((ix == 0 && iy == 0) || (/*(std::abs(ix) != std::abs(iy)) && */g_map.isLookPossible(pos))) {
|
|
|
|
Position upperPos = pos;
|
|
|
|
Position coveredPos = pos;
|
|
|
|
|
|
|
|
while(coveredPos.coveredUp() && upperPos.up() && upperPos.z >= firstFloor) {
|
|
|
|
// check tiles physically above
|
|
|
|
TilePtr tile = g_map.getTile(upperPos);
|
|
|
|
if(tile && tile->limitsFloorsView()) {
|
|
|
|
firstFloor = upperPos.z + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// check tiles geometrically above
|
|
|
|
tile = g_map.getTile(coveredPos);
|
|
|
|
if(tile && tile->limitsFloorsView()) {
|
|
|
|
firstFloor = coveredPos.z + 1;
|
|
|
|
break;
|
|
|
|
}
|
2012-03-28 21:09:45 +02:00
|
|
|
}
|
|
|
|
}
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
|
|
|
}
|
2012-03-28 21:32:18 +02:00
|
|
|
z = firstFloor;
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-28 21:32:18 +02:00
|
|
|
// just ensure the that the floor is in the valid range
|
2012-03-28 21:09:45 +02:00
|
|
|
z = std::min(std::max(z, 0), (int)Otc::MAX_Z);
|
|
|
|
return z;
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
|
|
|
|
2012-03-28 21:09:45 +02:00
|
|
|
int MapView::calcLastVisibleFloor()
|
2012-01-30 01:00:12 +01:00
|
|
|
{
|
2012-07-19 08:34:22 +02:00
|
|
|
if(!m_multifloor)
|
2012-04-07 01:12:46 +02:00
|
|
|
return calcFirstVisibleFloor();
|
|
|
|
|
2012-03-28 21:09:45 +02:00
|
|
|
int z = 7;
|
2012-01-30 01:00:12 +01:00
|
|
|
|
2012-03-28 21:09:45 +02:00
|
|
|
Position cameraPosition = getCameraPosition();
|
2012-03-28 21:32:18 +02:00
|
|
|
// this could happens if the player is not known yet
|
|
|
|
if(cameraPosition.isValid()) {
|
2012-04-07 01:12:46 +02:00
|
|
|
// view only underground floors when below sea level
|
|
|
|
if(cameraPosition.z > Otc::SEA_FLOOR)
|
|
|
|
z = cameraPosition.z + Otc::AWARE_UNDEGROUND_FLOOR_RANGE;
|
|
|
|
else
|
|
|
|
z = Otc::SEA_FLOOR;
|
2012-03-28 21:09:45 +02:00
|
|
|
}
|
2012-01-30 19:18:10 +01:00
|
|
|
|
2012-04-07 01:12:46 +02:00
|
|
|
if(m_lockedFirstVisibleFloor != -1)
|
|
|
|
z = std::max(m_lockedFirstVisibleFloor, z);
|
|
|
|
|
2012-03-28 21:32:18 +02:00
|
|
|
// just ensure the that the floor is in the valid range
|
2012-03-28 21:09:45 +02:00
|
|
|
z = std::min(std::max(z, 0), (int)Otc::MAX_Z);
|
|
|
|
return z;
|
2012-01-30 01:00:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Position MapView::getCameraPosition()
|
|
|
|
{
|
2012-07-09 13:37:47 +02:00
|
|
|
if(isFollowingCreature())
|
2012-01-30 01:00:12 +01:00
|
|
|
return m_followingCreature->getPosition();
|
2012-07-09 13:37:47 +02:00
|
|
|
|
2012-01-30 01:00:12 +01:00
|
|
|
return m_customCameraPosition;
|
|
|
|
}
|
|
|
|
|
2012-02-07 02:35:46 +01:00
|
|
|
TilePtr MapView::getTile(const Point& mousePos, const Rect& mapRect)
|
|
|
|
{
|
|
|
|
Point relativeMousePos = mousePos - mapRect.topLeft();
|
2012-04-07 01:12:46 +02:00
|
|
|
Size visibleSize = m_visibleDimension * m_tileSize;
|
2012-02-07 02:35:46 +01:00
|
|
|
Position cameraPosition = getCameraPosition();
|
|
|
|
|
2012-03-28 21:32:18 +02:00
|
|
|
// if we have no camera, its impossible to get the tile
|
|
|
|
if(!cameraPosition.isValid())
|
|
|
|
return nullptr;
|
|
|
|
|
2012-02-07 02:35:46 +01:00
|
|
|
float scaleFactor = m_tileSize / (float)Otc::TILE_PIXELS;
|
|
|
|
|
|
|
|
|
|
|
|
float horizontalStretchFactor = visibleSize.width() / (float)mapRect.width();
|
|
|
|
float verticalStretchFactor = visibleSize.height() / (float)mapRect.height();
|
|
|
|
|
|
|
|
Point tilePos2D = Point(relativeMousePos.x * horizontalStretchFactor, relativeMousePos.y * verticalStretchFactor);
|
|
|
|
|
2012-07-09 13:37:47 +02:00
|
|
|
if(isFollowingCreature())
|
2012-02-07 02:35:46 +01:00
|
|
|
tilePos2D += m_followingCreature->getWalkOffset() * scaleFactor;
|
|
|
|
tilePos2D /= m_tileSize;
|
|
|
|
|
2012-03-23 21:36:58 +01:00
|
|
|
Position tilePos = Position(1 + (int)tilePos2D.x - m_visibleCenterOffset.x, 1 + (int)tilePos2D.y - m_visibleCenterOffset.y, 0) + cameraPosition;
|
2012-02-07 02:35:46 +01:00
|
|
|
if(!tilePos.isValid())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
// we must check every floor, from top to bottom to check for a clickable tile
|
|
|
|
TilePtr tile;
|
|
|
|
tilePos.coveredUp(tilePos.z - m_cachedFirstVisibleFloor);
|
|
|
|
for(int i = m_cachedFirstVisibleFloor; i <= m_cachedLastVisibleFloor; i++) {
|
|
|
|
tile = g_map.getTile(tilePos);
|
|
|
|
if(tile && tile->isClickable())
|
|
|
|
break;
|
|
|
|
tilePos.coveredDown();
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!tile || !tile->isClickable())
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
return tile;
|
|
|
|
}
|
|
|
|
|
2012-06-22 07:26:22 +02:00
|
|
|
void MapView::setDrawMinimapColors(bool enable)
|
|
|
|
{
|
|
|
|
if(m_drawMinimapColors == enable)
|
|
|
|
return;
|
|
|
|
m_drawMinimapColors = enable;
|
|
|
|
updateGeometry(m_visibleDimension, m_optimizedSize);
|
|
|
|
requestVisibleTilesCacheUpdate();
|
|
|
|
m_smooth = !enable;
|
|
|
|
m_framebuffer->setSmooth(m_smooth);
|
|
|
|
}
|
|
|
|
|