experimental minimap

* a lot of rework in MapView
* new APIs for UIMap
This commit is contained in:
Eduardo Bart 2012-04-06 20:12:46 -03:00
parent fba5f188d7
commit 2835a66bab
18 changed files with 583 additions and 185 deletions

View File

@ -20,6 +20,7 @@ Module
- game_containers
- game_viplist
- game_battle
- game_minimap
- game_hotkeys
@onLoad: |

View File

@ -2,6 +2,8 @@ UIGameMap = extends(UIMap)
function UIGameMap.create()
local gameMap = UIGameMap.internalCreate()
gameMap:setKeepAspectRatio(true)
gameMap:setVisibleDimension({width = 15, height = 11})
return gameMap
end

View File

@ -0,0 +1,64 @@
Minimap = {}
-- private variables
local minimapWidget
local minimapButton
-- private functions
function onMinimapMouseRelease(self, mousePosition, mouseButton)
local tile = self:getTile(mousePosition)
if tile and mouseButton == MouseLeftButton and self:isPressed() then
local dirs = g_map.findPath(g_game.getLocalPlayer():getPosition(), tile:getPosition(), 255)
if #dirs == 0 then
TextMessage.displayStatus('There is no way.')
return true
end
g_game.autoWalk(dirs)
return true
end
return false
end
function onMinimapMouseWheel(self, mousePos, direction)
if direction == MouseWheelUp then
self:zoomIn()
else
self:zoomOut()
end
end
-- public functions
function Minimap.init()
connect(g_game, { onLogin = Minimap.reset })
Keyboard.bindKeyDown('Ctrl+M', Minimap.toggle)
minimapButton = TopMenu.addGameToggleButton('minimapButton', 'Minimap (Ctrl+M)', 'minimap.png', Minimap.toggle)
minimapButton:setOn(true)
minimapWidget = loadUI('minimap.otui', GameInterface.getMapPanel())
minimapWidget.onMouseRelease = onMinimapMouseRelease
minimapWidget.onMouseWheel = onMinimapMouseWheel
end
function Minimap.terminate()
Keyboard.unbindKeyDown('Ctrl+M')
disconnect(g_game, { onLogin = Minimap.reset })
minimapWidget:destroy()
minimapWidget = nil
minimapButton:destroy()
minimapButton = nil
end
function Minimap.toggle()
local visible = not minimapWidget:isExplicitlyVisible()
minimapWidget:setVisible(visible)
minimapButton:setOn(visible)
end
function Minimap.reset()
minimapWidget:followCreature(g_game.getLocalPlayer())
for i=1,10 do minimapWidget:zoomOut() end
end

View File

@ -0,0 +1,15 @@
Module
name: game_minimap
description: Manage minimap
author: OTClient team
website: https://github.com/edubart/otclient
dependecies:
- game
@onLoad: |
dofile 'minimap'
Minimap.init()
@onUnload:
Minimap.terminate()

View File

@ -0,0 +1,21 @@
UIMap
id: minimap
anchors.top: parent.top
anchors.right: parent.right
size: 256 192
margin-top: 2
margin-right: 2
border-width: 1
border-color: #888888
padding: 1
//draw-minimap-colors: true
multifloor: false
draw-texts: false
CheckBox
anchors.top: parent.top
anchors.right: parent.right
margin-top: 2
margin-right: 2
size: 16 16
@onCheckChange: self:getParent():setDrawMinimapColors(self:isChecked())

Binary file not shown.

After

Width:  |  Height:  |  Size: 910 B

View File

@ -3,8 +3,84 @@ uniform vec4 color; // painter color
uniform float time; // time in seconds since shader linkage
uniform sampler2D texture; // map texture
varying vec2 textureCoords; // map texture coords
//uniform vec4 awareArea;
void main()
{
gl_FragColor = texture2D(texture, textureCoords);
}
/*
vec4 grayScale(vec4 color, float factor)
{
float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
return vec4(gray*factor + (1-factor)*color.r,
gray*factor + (1-factor)*color.g,
gray*factor + (1-factor)*color.b, 1);
}
// grayscale fog
void main()
{
vec4 color = texture2D(texture, textureCoords);
float dist = 0;
// left
if(textureCoords.x < awareArea.x && textureCoords.y < awareArea.y)
dist = distance(textureCoords, awareArea.xy);
else if(textureCoords.x < awareArea.x && textureCoords.y < awareArea.w)
dist = distance(textureCoords, vec2(awareArea.x, textureCoords.y));
else if(textureCoords.x < awareArea.x)
dist = distance(textureCoords, awareArea.xw);
// right
else if(textureCoords.x > awareArea.z && textureCoords.y < awareArea.y)
dist = distance(textureCoords, awareArea.zy);
else if(textureCoords.x > awareArea.z && textureCoords.y < awareArea.w)
dist = distance(textureCoords, vec2(awareArea.z, textureCoords.y));
else if(textureCoords.x > awareArea.z)
dist = distance(textureCoords, awareArea.zw);
// top
else if(textureCoords.y < awareArea.y)
dist = distance(textureCoords, vec2(textureCoords.x, awareArea.y));
// bottom
else if(textureCoords.y > awareArea.w)
dist = distance(textureCoords, vec2(textureCoords.x, awareArea.w));
if(dist > 0) {
float range = 0.01;
float factor = min(dist/range, 1.0);
color = grayScale(color, factor);
//color.rgb *= 1 - (factor * 0.5);
}
gl_FragColor = color;
}
*/
/*
sepia
void main()
{
vec4 color = texture2D(texture, textureCoords);
if(textureCoords.x < awareArea.x || textureCoords.y < awareArea.y || textureCoords.x > awareArea.z || textureCoords.y > awareArea.w) {
gl_FragColor.r = dot(color, vec4(.393, .769, .189, .0));
gl_FragColor.g = dot(color, vec4(.349, .686, .168, .0));
gl_FragColor.b = dot(color, vec4(.272, .534, .131, .0));
gl_FragColor.a = 1;
} else
gl_FragColor = color;
}
*/
/*
void main()
{
vec4 color = texture2D(texture, textureCoords);
vec2 awareRange = (awareArea.zw - awareArea.xy)/2.0;
float dist = distance(textureCoords, awareArea.xy + awareRange);
float start = min(awareRange.x, awareRange.y);
float range = awareRange*0.1;
float endFactor = 0.3;
if(dist >= start) {
color.rgb *= 1 - (min((dist - start)/range, 1.0) * (2.0 - endFactor));
}
gl_FragColor = color;
}
*/

View File

@ -76,6 +76,8 @@ void Graphics::init()
#endif
glHint(GL_LINE_SMOOTH_HINT,GL_NICEST);
glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);
glEnable(GL_BLEND);
glClear(GL_COLOR_BUFFER_BIT);
@ -99,8 +101,6 @@ bool Graphics::parseOption(const std::string& option)
m_generateMipmaps = false;
else if(option == "-no-smoothing")
m_useBilinearFiltering = false;
else if(option == "-realtime-mipmapping")
m_generateRealtimeMipmaps = true;
else if(option == "-no-hardware-buffering")
m_useHardwareBuffers = false;
else

View File

@ -38,7 +38,6 @@ public:
bool canUseHardwareBuffers() { return m_useHardwareBuffers; }
bool canGenerateMipmaps() { return m_generateMipmaps; }
bool canGenerateHardwareMipmaps() { return m_generateHardwareMipmaps; }
bool canGenerateRealtimeMipmaps() { return m_generateRealtimeMipmaps; }
void resize(const Size& size);
void beginRender();
@ -59,7 +58,6 @@ private:
Boolean<true> m_useBilinearFiltering;
Boolean<true> m_generateMipmaps;
Boolean<true> m_generateHardwareMipmaps;
Boolean<false> m_generateRealtimeMipmaps;
};
extern Graphics g_graphics;

View File

@ -33,6 +33,8 @@ public:
Color() : m_r(1.0f), m_g(1.0f), m_b(1.0f), m_a(1.0f) { }
Color(uint32 rgba) { setRGBA(rgba); }
Color(uint8 r, uint8 g, uint8 b, uint8 a = 0xFF) : m_r(r/255.0f), m_g(g/255.0f), m_b(b/255.0f), m_a(a/255.0f) { }
Color(int r, int g, int b, int a = 0xFF) : m_r(r/255.0f), m_g(g/255.0f), m_b(b/255.0f), m_a(a/255.0f) { }
Color(float r, float g, float b, float a = 1.0f) : m_r(r), m_g(g), m_b(b), m_a(a) { }
uint8 a() const { return m_a*255.0f; }
uint8 b() const { return m_b*255.0f; }

View File

@ -34,23 +34,24 @@
#include "missile.h"
#include <framework/core/eventdispatcher.h>
//int AWARE_AREA_UNIFORM = 10;
MapView::MapView()
{
m_viewRange = NEAR_VIEW;
m_viewMode = NEAR_VIEW;
m_lockedFirstVisibleFloor = -1;
m_cachedFirstVisibleFloor = 0;
m_cachedFirstVisibleFloor = 7;
m_cachedLastVisibleFloor = 7;
m_optimizedSize = Size(Otc::AWARE_X_TILES, Otc::AWARE_Y_TILES) * Otc::TILE_PIXELS;
Size frameBufferSize(std::min(g_graphics.getMaxTextureSize(), (int)DEFAULT_FRAMBUFFER_WIDTH),
std::min(g_graphics.getMaxTextureSize(), (int)DEFAULT_FRAMBUFFER_HEIGHT));
m_framebuffer = FrameBufferPtr(new FrameBuffer(frameBufferSize));
m_framebuffer = FrameBufferPtr(new FrameBuffer());
setVisibleDimension(Size(15, 11));
m_shaderProgram = PainterShaderProgramPtr(new PainterShaderProgram);
m_shaderProgram->addShaderFromSourceCode(Shader::Vertex, glslMainWithTexCoordsVertexShader + glslPositionOnlyVertexShader);
m_shaderProgram->addShaderFromSourceFile(Shader::Fragment, "/game_shaders/map.frag");
m_shaderProgram->link();
//m_shaderProgram->bindUniformLocation(AWARE_AREA_UNIFORM, "awareArea");
}
void MapView::draw(const Rect& rect)
@ -63,18 +64,19 @@ void MapView::draw(const Rect& rect)
Position cameraPosition = getCameraPosition();
int drawFlags = 0;
if(m_viewRange == NEAR_VIEW)
if(m_viewMode == NEAR_VIEW)
drawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls |
Otc::DrawItems | Otc::DrawCreatures | Otc::DrawEffects | Otc::DrawMissiles | Otc::DrawAnimations;
else if(m_viewRange == MID_VIEW)
else if(m_viewMode == MID_VIEW)
drawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls | Otc::DrawItems;
else if(m_viewRange == FAR_VIEW)
else if(m_viewMode == FAR_VIEW)
drawFlags = Otc::DrawGround | Otc::DrawGroundBorders | Otc::DrawWalls;
else if(m_tileSize >= 4) // HUGE_VIEW 1
drawFlags = Otc::DrawGround | Otc::DrawGroundBorders;
else // HUGE_VIEW 2
drawFlags = Otc::DrawGround;
Size tileSize = Size(1,1) * m_tileSize;
if(m_mustDrawVisibleTilesCache || (drawFlags & Otc::DrawAnimations)) {
g_painter.saveAndResetState();
m_framebuffer->bind();
@ -86,6 +88,8 @@ void MapView::draw(const Rect& rect)
g_painter.setColor(Color::black);
g_painter.drawFilledRect(clearRect);
g_painter.setColor(Color::white);
// m_framebuffer->clear(Color::black);
}
auto it = m_cachedVisibleTiles.begin();
@ -98,29 +102,26 @@ void MapView::draw(const Rect& rect)
else
++it;
tile->draw(transformPositionTo2D(tile->getPosition()), scaleFactor, drawFlags);
if(!m_drawMinimapColors)
tile->draw(transformPositionTo2D(tile->getPosition()), scaleFactor, drawFlags);
else {
g_painter.setColor(tile->getMinimapColor());
g_painter.drawFilledRect(Rect(transformPositionTo2D(tile->getPosition()), tileSize));
}
}
if(drawFlags & Otc::DrawMissiles) {
if(drawFlags & Otc::DrawMissiles && !m_drawMinimapColors) {
for(const MissilePtr& missile : g_map.getFloorMissiles(z)) {
missile->draw(transformPositionTo2D(missile->getPosition()), scaleFactor, drawFlags & Otc::DrawAnimations);
}
}
}
/*
// debug source area
g_painter.setColor(Color(255,255,255,100));
Point drawOffset = ((m_drawDimension - m_visibleDimension - Size(1,1)).toPoint()/2) * m_tileSize;
g_painter.drawFilledRect(Rect(drawOffset, m_visibleDimension * m_tileSize));
*/
m_framebuffer->release();
g_painter.restoreSavedState();
// generating mipmaps each frame can be slow in older cards
if(g_graphics.canGenerateRealtimeMipmaps())
m_framebuffer->getTexture()->generateHardwareMipmaps();
//m_framebuffer->getTexture()->generateHardwareMipmaps();
m_mustDrawVisibleTilesCache = false;
}
@ -130,20 +131,57 @@ void MapView::draw(const Rect& rect)
Point drawOffset = ((m_drawDimension - m_visibleDimension - Size(1,1)).toPoint()/2) * m_tileSize;
if(m_followingCreature)
drawOffset += m_followingCreature->getWalkOffset() * scaleFactor;
Rect srcRect = Rect(drawOffset, m_visibleDimension * m_tileSize);
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);
/*
// pass aware area to the shader program
Rect awareRect;
if(m_followingCreature) {
Point awareOffset = transformPositionTo2D(m_followingCreature->getPosition().translated(-Otc::AWARE_X_LEFT_TILES + 1, -Otc::AWARE_Y_TOP_TILES + 1));
awareOffset += m_followingCreature->getWalkOffset() * scaleFactor;
awareRect.setTopLeft(awareOffset);
awareRect.resize(Otc::VISIBLE_X_TILES * m_tileSize, Otc::VISIBLE_Y_TILES * m_tileSize);
}
m_shaderProgram->bind();
m_shaderProgram->setUniformValue(AWARE_AREA_UNIFORM,
awareRect.left()/(float)m_framebuffer->getSize().width(),
awareRect.top()/(float)m_framebuffer->getSize().height(),
awareRect.right()/(float)m_framebuffer->getSize().width(),
awareRect.bottom()/(float)m_framebuffer->getSize().height());
*/
#if 0
// debug source area
g_painter.saveAndResetState();
m_framebuffer->bind();
g_painter.setColor(Color::green);
g_painter.drawBoundingRect(srcRect, 2);
m_framebuffer->release();
g_painter.restoreSavedState();
m_framebuffer->draw(rect);
#else
m_framebuffer->draw(rect, srcRect);
#endif
g_painter.releaseCustomProgram();
// this could happen if the player position is not known yet
if(!cameraPosition.isValid())
return;
float horizontalStretchFactor = rect.width() / (float)(m_visibleDimension.width() * m_tileSize);
float verticalStretchFactor = rect.height() / (float)(m_visibleDimension.height() * m_tileSize);
//Size tileStretchedSize = Size(m_tileSize * horizontalStretchFactor, m_tileSize * verticalStretchFactor);
float horizontalStretchFactor = rect.width() / (float)srcRect.width();
float verticalStretchFactor = rect.height() / (float)srcRect.height();
// avoid drawing texts on map in far zoom outs
if(m_viewRange == NEAR_VIEW) {
if(m_viewMode == NEAR_VIEW && m_drawTexts) {
for(const CreaturePtr& creature : m_cachedFloorVisibleCreatures) {
Position pos = creature->getPosition();
@ -187,7 +225,7 @@ void MapView::draw(const Rect& rect)
p += rect.topLeft();
animatedText->draw(p, rect);
}
} else {
} else if(m_viewMode > NEAR_VIEW) {
// draw a cross in the center instead of our creature
Rect vRect(0, 0, 2, 10);
Rect hRect(0, 0, 10, 2);
@ -239,16 +277,13 @@ void MapView::updateVisibleTilesCache(int start)
// cache visible tiles in draw order
// draw from last floor (the lower) to first floor (the higher)
for(int iz = m_cachedLastVisibleFloor; iz >= m_cachedFirstVisibleFloor && !stop; --iz) {
if(m_viewRange <= FAR_VIEW) {
if(m_viewMode <= FAR_VIEW) {
const int numDiagonals = m_drawDimension.width() + m_drawDimension.height() - 1;
// loop through / diagonals beginning at top left and going to top right
for(int diagonal = 0; diagonal < numDiagonals && !stop; ++diagonal) {
// loop current diagonal tiles
for(int iy = std::min(diagonal, m_drawDimension.width() - 1), ix = std::max(diagonal - m_drawDimension.width() + 1, 0); iy >= 0 && ix < m_drawDimension.width() && !stop; --iy, ++ix) {
// skip bottom tiles that are outside the draw dimension
if(iy >= m_drawDimension.height())
continue;
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) {
// only start really looking tiles in the desired start
if(count < start) {
count++;
@ -256,7 +291,7 @@ void MapView::updateVisibleTilesCache(int start)
}
// avoid rendering too much tiles at once on far views
if(count - start + 1 > MAX_TILE_UPDATES && m_viewRange >= HUGE_VIEW) {
if(count - start + 1 > MAX_TILE_UPDATES && m_viewMode >= HUGE_VIEW) {
stop = true;
break;
}
@ -378,13 +413,71 @@ void MapView::updateVisibleTilesCache(int start)
// schedule next update continuation to avoid freezes
m_updateTilesCacheEvent = g_eventDispatcher.scheduleEvent(std::bind(&MapView::updateVisibleTilesCache, asMapView(), count), 1);
}
if(start == 0)
if(start == 0 && m_drawTexts && m_viewMode <= NEAR_VIEW)
m_cachedFloorVisibleCreatures = g_map.getSpectators(cameraPosition, false);
}
void MapView::updateGeometry(const Size& visibleDimension, const Size& optimizedSize)
{
int possiblesTileSizes[] = {1,2,4,8,16,32};
int tileSize = 0;
Size bufferSize;
for(int candidateTileSize : possiblesTileSizes) {
bufferSize = (visibleDimension + Size(3,3)) * candidateTileSize;
if(bufferSize.width() > g_graphics.getMaxTextureSize() || bufferSize.height() > g_graphics.getMaxTextureSize())
break;
tileSize = candidateTileSize;
if(optimizedSize.width() < bufferSize.width() - 3*candidateTileSize && optimizedSize.height() < bufferSize.height() - 3*candidateTileSize)
break;
}
if(tileSize == 0) {
logTraceError("reached max zoom out");
return;
}
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;
}
// 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;
}
*/
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();
}
void MapView::onTileUpdate(const Position& pos)
{
//if(m_viewRange <= FAR_VIEW)
//if(m_viewMode <= FAR_VIEW)
requestVisibleTilesCacheUpdate();
}
@ -400,6 +493,42 @@ void MapView::unlockFirstVisibleFloor()
requestVisibleTilesCacheUpdate();
}
void MapView::setVisibleDimension(const Size& visibleDimension)
{
if(visibleDimension == m_visibleDimension)
return;
if(visibleDimension.width() % 2 != 1 || visibleDimension.height() % 2 != 1) {
logTraceError("visible dimension must be odd");
return;
}
if(visibleDimension < Size(3,3)) {
logTraceError("reach max zoom in");
return;
}
updateGeometry(visibleDimension, m_optimizedSize);
}
void MapView::setViewMode(MapView::ViewMode viewMode)
{
m_viewMode = viewMode;
requestVisibleTilesCacheUpdate();
}
void MapView::setAutoViewMode(bool enable)
{
m_autoViewMode = enable;
if(enable)
updateGeometry(m_visibleDimension, m_optimizedSize);
}
void MapView::optimizeForSize(const Size& visibleSize)
{
updateGeometry(m_visibleDimension, visibleSize);
}
void MapView::followCreature(const CreaturePtr& creature)
{
m_followingCreature = creature;
@ -413,67 +542,6 @@ void MapView::setCameraPosition(const Position& pos)
requestVisibleTilesCacheUpdate();
}
void MapView::setVisibleDimension(const Size& visibleDimension)
{
if(visibleDimension.width() % 2 != 1 || visibleDimension.height() % 2 != 1) {
logTraceError("visible dimension must be odd");
return;
}
if(visibleDimension < Size(3,3)) {
logTraceError("reach max zoom in");
return;
}
int possiblesTileSizes[] = {32,16,8,4,2,1};
int tileSize = 0;
Size drawDimension = visibleDimension + Size(3,3);
Size framebufferSize = m_framebuffer->getSize();
for(int candidateTileSize : possiblesTileSizes) {
Size candidateDrawSize = drawDimension * candidateTileSize;
// found a valid size
if(candidateDrawSize.width() <= framebufferSize.width() && candidateDrawSize.height() <= framebufferSize.height()) {
tileSize = candidateTileSize;
break;
}
}
if(tileSize == 0) {
logTraceError("reached max zoom out");
return;
}
Point virtualCenterOffset = (drawDimension/2 - Size(1,1)).toPoint();
Point visibleCenterOffset = virtualCenterOffset;
ViewRange viewRange;
if(tileSize >= 32 && visibleDimension.area() <= NEAR_VIEW_AREA)
viewRange = NEAR_VIEW;
else if(tileSize >= 16 && visibleDimension.area() <= MID_VIEW_AREA)
viewRange = MID_VIEW;
else if(tileSize >= 8 && visibleDimension.area() <= FAR_VIEW_AREA)
viewRange = FAR_VIEW;
else
viewRange = HUGE_VIEW;
bool mustUpdate = (m_drawDimension != drawDimension ||
m_viewRange != viewRange ||
m_virtualCenterOffset != virtualCenterOffset ||
m_visibleCenterOffset != visibleCenterOffset ||
m_tileSize != tileSize);
m_visibleDimension = visibleDimension;
m_drawDimension = drawDimension;
m_tileSize = tileSize;
m_viewRange = viewRange;
m_virtualCenterOffset = virtualCenterOffset;
m_visibleCenterOffset = visibleCenterOffset;
if(mustUpdate)
requestVisibleTilesCacheUpdate();
}
int MapView::calcFirstVisibleFloor()
{
int z = 7;
@ -486,7 +554,7 @@ int MapView::calcFirstVisibleFloor()
// this could happens if the player is not known yet
if(cameraPosition.isValid()) {
// avoid rendering multifloors in far views
if(m_viewRange >= FAR_VIEW) {
if(m_viewMode >= FAR_VIEW || !m_multifloor) {
z = cameraPosition.z;
} else {
// if nothing is limiting the view, the first visible floor is 0
@ -536,23 +604,24 @@ int MapView::calcFirstVisibleFloor()
int MapView::calcLastVisibleFloor()
{
if(!m_multifloor || m_viewMode >= FAR_VIEW)
return calcFirstVisibleFloor();
int z = 7;
Position cameraPosition = getCameraPosition();
// this could happens if the player is not known yet
if(cameraPosition.isValid()) {
// avoid rendering multifloors in far views
if(m_viewRange >= FAR_VIEW) {
z = cameraPosition.z;
} else {
// 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;
}
// 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;
}
if(m_lockedFirstVisibleFloor != -1)
z = std::max(m_lockedFirstVisibleFloor, z);
// just ensure the that the floor is in the valid range
z = std::min(std::max(z, 0), (int)Otc::MAX_Z);
return z;
@ -568,7 +637,7 @@ Position MapView::getCameraPosition()
TilePtr MapView::getTile(const Point& mousePos, const Rect& mapRect)
{
Point relativeMousePos = mousePos - mapRect.topLeft();
Size visibleSize = getVisibleSize();
Size visibleSize = m_visibleDimension * m_tileSize;
Position cameraPosition = getCameraPosition();
// if we have no camera, its impossible to get the tile
@ -608,6 +677,7 @@ TilePtr MapView::getTile(const Point& mousePos, const Rect& mapRect)
return tile;
}
Point MapView::transformPositionTo2D(const Position& position)
{
Position cameraPosition = getCameraPosition();

View File

@ -34,8 +34,6 @@ class MapView : public LuaObject
// 3840x2160 => 1080p optimized
// 2560x1440 => 720p optimized
// 1728x972 => 480p optimized
DEFAULT_FRAMBUFFER_WIDTH = 2560,
DEFAULT_FRAMBUFFER_HEIGHT = 1440,
NEAR_VIEW_AREA = 32*32,
MID_VIEW_AREA = 64*64,
@ -43,18 +41,19 @@ class MapView : public LuaObject
MAX_TILE_UPDATES = NEAR_VIEW_AREA*7
};
enum ViewRange {
public:
enum ViewMode {
NEAR_VIEW,
MID_VIEW,
FAR_VIEW,
HUGE_VIEW
};
public:
MapView();
void draw(const Rect& rect);
private:
void updateGeometry(const Size& visibleDimension, const Size& optimizedSize);
void updateVisibleTilesCache(int start = 0);
void requestVisibleTilesCacheUpdate() { m_mustUpdateVisibleTilesCache = true; }
@ -64,55 +63,82 @@ protected:
friend class Map;
public:
// floor visibility related
void lockFirstVisibleFloor(int firstVisibleFloor);
void unlockFirstVisibleFloor();
void followCreature(const CreaturePtr& creature);
int getLockedFirstVisibleFloor() { return m_lockedFirstVisibleFloor; }
void setCameraPosition(const Position& pos);
void setMultifloor(bool enable) { m_multifloor = enable; requestVisibleTilesCacheUpdate(); }
bool isMultifloor() { return m_multifloor; }
// map dimension related
void setVisibleDimension(const Size& visibleDimension);
void setAnimated(bool animated) { m_animated = animated; }
//void zoomIn(float factor);
//void zoomOut(float factor);
bool isFollowingCreature() { return !!m_followingCreature; }
int calcFirstVisibleFloor();
int calcLastVisibleFloor();
Position getCameraPosition();
TilePtr getTile(const Point& mousePos, const Rect& mapRect);
Size getVisibleDimension() { return m_visibleDimension; }
Size getVisibleSize() { return m_visibleDimension * m_tileSize; }
// view mode related
void setViewMode(ViewMode viewMode);
ViewMode getViewMode() { return m_viewMode; }
void optimizeForSize(const Size& visibleSize);
void setAutoViewMode(bool enable);
bool isAutoViewModeEnabled() { return m_autoViewMode; }
// camera related
void followCreature(const CreaturePtr& creature);
CreaturePtr getFollowingCreature() { return m_followingCreature; }
bool getViewRange() { return m_viewRange; }
bool isAnimated() { return m_animated; }
void setCameraPosition(const Position& pos);
Position getCameraPosition();
Point transformPositionTo2D(const Position& position);
// drawing related
void setDrawFlags(Otc::DrawFlags drawFlags) { m_drawFlags = drawFlags; requestVisibleTilesCacheUpdate(); }
Otc::DrawFlags getDrawFlags() { return m_drawFlags; }
void setDrawTexts(bool enable) { m_drawTexts = enable; }
bool isDrawingTexts() { return m_drawTexts; }
void setDrawMinimapColors(bool enable) { m_drawMinimapColors = enable; requestVisibleTilesCacheUpdate(); }
bool isDrawingMinimapColors() { return m_drawMinimapColors; }
void setAnimated(bool animated) { m_animated = animated; requestVisibleTilesCacheUpdate(); }
bool isAnimating() { return m_animated; }
// get tile
TilePtr getTile(const Point& mousePos, const Rect& mapRect);
MapViewPtr asMapView() { return std::static_pointer_cast<MapView>(shared_from_this()); }
private:
int calcFirstVisibleFloor();
int calcLastVisibleFloor();
Point transformPositionTo2D(const Position& position);
int m_lockedFirstVisibleFloor;
int m_cachedFirstVisibleFloor;
int m_cachedLastVisibleFloor;
int m_tileSize;
Size m_drawDimension;
Size m_visibleDimension;
Size m_optimizedSize;
Point m_virtualCenterOffset;
Point m_visibleCenterOffset;
Position m_customCameraPosition;
Boolean<true> m_mustUpdateVisibleTilesCache;
Boolean<true> m_mustDrawVisibleTilesCache;
Boolean<true> m_mustCleanFramebuffer;
Boolean<true> m_multifloor;
Boolean<true> m_animated;
Boolean<true> m_autoViewMode;
Boolean<true> m_drawTexts;
Boolean<false> m_drawMinimapColors;
std::vector<TilePtr> m_cachedVisibleTiles;
std::vector<CreaturePtr> m_cachedFloorVisibleCreatures;
EventPtr m_updateTilesCacheEvent;
CreaturePtr m_followingCreature;
FrameBufferPtr m_framebuffer;
PainterShaderProgramPtr m_shaderProgram;
ViewRange m_viewRange;
ViewMode m_viewMode;
Otc::DrawFlags m_drawFlags;
};
#endif

View File

@ -104,6 +104,7 @@ public:
int getAnimationPhases() { return m_type->dimensions[ThingType::AnimationPhases]; }
int getGroundSpeed() { return m_type->parameters[ThingType::GroundSpeed]; }
int getElevation() { return m_type->parameters[ThingType::Elevation]; }
int getMinimapColor() { return m_type->parameters[ThingType::MiniMapColor]; }
int getSpriteId(int w = 0, int h = 0, int layer = 0,
int xPattern = 0, int yPattern = 0, int zPattern = 0,

View File

@ -265,6 +265,19 @@ int Tile::getGroundSpeed()
return groundSpeed;
}
Color Tile::getMinimapColor()
{
Color color = Color::black;
for(const ThingPtr& thing : m_things) {
if(!thing->isGround() && !thing->isGroundBorder() && !thing->isOnBottom() && !thing->isOnTop())
break;
int c = thing->getMinimapColor();
if(c != 0)
color = Color::from8bit(c);
}
return color;
}
ThingPtr Tile::getTopLookThing()
{
if(isEmpty())

View File

@ -57,6 +57,7 @@ public:
const std::vector<ThingPtr>& getThings() { return m_things; }
ItemPtr getGround();
int getGroundSpeed();
Color getMinimapColor();
int getThingCount() { return m_things.size() + m_effects.size(); }
bool isPathable();
bool isWalkable();

View File

@ -333,10 +333,37 @@ void OTClient::registerLuaFunctions()
g_lua.bindClassMemberFunction<UICreature>("isFixedCreatureSize", &UICreature::isFixedCreatureSize);
g_lua.registerClass<UIMap, UIWidget>();
g_lua.bindClassStaticFunction<UIMap>("create", []{ return UIMapPtr(new UIMap); } );
g_lua.bindClassMemberFunction<UIMap>("followCreature", &UIMap::followCreature);
g_lua.bindClassMemberFunction<UIMap>("setCameraPosition", &UIMap::setCameraPosition);
g_lua.bindClassMemberFunction<UIMap>("getTile", &UIMap::getTile);
g_lua.bindClassStaticFunction<UIMap>("create", []{ return UIMapPtr(new UIMap); });
g_lua.bindClassMemberFunction<UIMap>("drawSelf", &UIMap::drawSelf);
g_lua.bindClassMemberFunction<UIMap>("setZoom", &UIMap::setZoom);
g_lua.bindClassMemberFunction<UIMap>("zoomIn", &UIMap::zoomIn);
g_lua.bindClassMemberFunction<UIMap>("zoomOut", &UIMap::zoomOut);
g_lua.bindClassMemberFunction<UIMap>("followCreature", &UIMap::followCreature);
g_lua.bindClassMemberFunction<UIMap>("setCameraPosition", &UIMap::setCameraPosition);
g_lua.bindClassMemberFunction<UIMap>("setMaxZoomIn", &UIMap::setMaxZoomIn);
g_lua.bindClassMemberFunction<UIMap>("setMaxZoomOut", &UIMap::setMaxZoomOut);
g_lua.bindClassMemberFunction<UIMap>("setMultifloor", &UIMap::setMultifloor);
g_lua.bindClassMemberFunction<UIMap>("setVisibleDimension", &UIMap::setVisibleDimension);
g_lua.bindClassMemberFunction<UIMap>("setViewMode", &UIMap::setViewMode);
g_lua.bindClassMemberFunction<UIMap>("setAutoViewMode", &UIMap::setAutoViewMode);
g_lua.bindClassMemberFunction<UIMap>("setDrawFlags", &UIMap::setDrawFlags);
g_lua.bindClassMemberFunction<UIMap>("setDrawTexts", &UIMap::setDrawTexts);
g_lua.bindClassMemberFunction<UIMap>("setDrawMinimapColors", &UIMap::setDrawMinimapColors);
g_lua.bindClassMemberFunction<UIMap>("setAnimated", &UIMap::setAnimated);
g_lua.bindClassMemberFunction<UIMap>("setKeepAspectRatio", &UIMap::setKeepAspectRatio);
g_lua.bindClassMemberFunction<UIMap>("isMultifloor", &UIMap::isMultifloor);
g_lua.bindClassMemberFunction<UIMap>("isAutoViewModeEnabled", &UIMap::isAutoViewModeEnabled);
g_lua.bindClassMemberFunction<UIMap>("isDrawingTexts", &UIMap::isDrawingTexts);
g_lua.bindClassMemberFunction<UIMap>("isDrawingMinimapColors", &UIMap::isDrawingMinimapColors);
g_lua.bindClassMemberFunction<UIMap>("isAnimating", &UIMap::isAnimating);
g_lua.bindClassMemberFunction<UIMap>("isKeepAspectRatioEnabled", &UIMap::isKeepAspectRatioEnabled);
g_lua.bindClassMemberFunction<UIMap>("getVisibleDimension", &UIMap::getVisibleDimension);
g_lua.bindClassMemberFunction<UIMap>("getViewMode", &UIMap::getViewMode);
g_lua.bindClassMemberFunction<UIMap>("getFollowingCreature", &UIMap::getFollowingCreature);
g_lua.bindClassMemberFunction<UIMap>("getDrawFlags", &UIMap::getDrawFlags);
g_lua.bindClassMemberFunction<UIMap>("getCameraPosition", &UIMap::getCameraPosition);
g_lua.bindClassMemberFunction<UIMap>("getTile", &UIMap::getTile);
g_lua.bindClassMemberFunction<UIMap>("getMaxZoomIn", &UIMap::getMaxZoomIn);
g_lua.bindClassMemberFunction<UIMap>("getMaxZoomOut", &UIMap::getMaxZoomOut);
g_lua.bindClassMemberFunction<UIMap>("getZoom", &UIMap::getZoom);
}

View File

@ -32,6 +32,10 @@ UIMap::UIMap()
{
m_dragable = true;
m_mapView = MapViewPtr(new MapView);
m_zoom = m_mapView->getVisibleDimension().height();
m_aspectRatio = 0.0f;
m_maxZoomIn = 3;
m_maxZoomOut = 512;
g_map.addMapView(m_mapView);
}
@ -51,56 +55,48 @@ void UIMap::drawSelf()
m_mapView->draw(m_mapRect);
}
void UIMap::zoomIn()
bool UIMap::setZoom(int zoom)
{
int dimensionHeight = m_mapView->getVisibleDimension().height() * 0.99;
if(dimensionHeight == m_mapView->getVisibleDimension().height())
dimensionHeight -= 1;
if(dimensionHeight % 2 == 0)
dimensionHeight -= 1;
int dimensionWidth = dimensionHeight * getSize().ratio();
if(dimensionWidth % 2 == 0)
dimensionWidth -= 1;
m_mapView->setVisibleDimension(Size(dimensionWidth, dimensionHeight));
Rect mapRect = getClippingRect().expanded(-1);
Size mapSize = m_mapView->getVisibleSize();
mapSize.scale(mapRect.size(), Fw::KeepAspectRatio);
m_mapRect.resize(mapSize);
m_mapRect.moveCenter(m_rect.center());
//TODO
return false;
}
void UIMap::zoomOut()
bool UIMap::zoomIn()
{
int dimensionHeight = m_mapView->getVisibleDimension().height() * 1.01;
if(dimensionHeight == m_mapView->getVisibleDimension().height())
dimensionHeight += 1;
if(dimensionHeight % 2 == 0)
dimensionHeight += 1;
int dimensionWidth = dimensionHeight * getSize().ratio();
if(dimensionWidth % 2 == 0)
dimensionWidth += 1;
int delta = 2;
if(m_zoom - delta <= m_maxZoomIn)
return false;
m_mapView->setVisibleDimension(Size(dimensionWidth, dimensionHeight));
Rect mapRect = getClippingRect().expanded(-1);
Size mapSize = m_mapView->getVisibleSize();
mapSize.scale(mapRect.size(), Fw::KeepAspectRatio);
m_mapRect.resize(mapSize);
m_mapRect.moveCenter(m_rect.center());
m_zoom -= delta;
updateVisibleDimension();
return true;
}
void UIMap::followCreature(const CreaturePtr& creature)
bool UIMap::zoomOut()
{
m_mapView->followCreature(creature);
int delta = 2;
if(m_zoom + delta >= m_maxZoomOut)
return false;
m_zoom += 2;
updateVisibleDimension();
return true;
}
void UIMap::setCameraPosition(const Position& pos)
void UIMap::setVisibleDimension(const Size& visibleDimension)
{
m_mapView->setCameraPosition(pos);
m_mapView->setVisibleDimension(visibleDimension);
if(m_aspectRatio != 0.0f)
m_aspectRatio = visibleDimension.ratio();
}
void UIMap::setKeepAspectRatio(bool enable)
{
if(enable)
m_aspectRatio = getVisibleDimension().ratio();
else
m_aspectRatio = 0.0f;
}
TilePtr UIMap::getTile(const Point& mousePos)
@ -112,14 +108,61 @@ TilePtr UIMap::getTile(const Point& mousePos)
return m_mapView->getTile(mousePos, m_mapRect);
}
void UIMap::onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode)
{
UIWidget::onStyleApply(styleName, styleNode);
for(const OTMLNodePtr& node : styleNode->children()) {
if(node->tag() == "multifloor")
setMultifloor(node->value<bool>());
else if(node->tag() == "auto-view-mode")
setAutoViewMode(node->value<bool>());
else if(node->tag() == "draw-texts")
setDrawTexts(node->value<bool>());
else if(node->tag() == "draw-minimap-colors")
setDrawMinimapColors(node->value<bool>());
else if(node->tag() == "animated")
setAnimated(node->value<bool>());
}
}
void UIMap::onGeometryChange(const Rect& oldRect, const Rect& newRect)
{
UIWidget::onGeometryChange(oldRect, newRect);
updateMapSize();
}
Rect mapRect = getClippingRect().expanded(-1);
Size mapSize = m_mapView->getVisibleSize();
mapSize.scale(mapRect.size(), Fw::KeepAspectRatio);
void UIMap::updateVisibleDimension()
{
int dimensionHeight = m_zoom;
if(dimensionHeight % 2 == 0)
dimensionHeight += 1;
int dimensionWidth = m_zoom * m_mapRect.size().ratio();
if(dimensionWidth % 2 == 0)
dimensionWidth += 1;
m_mapView->setVisibleDimension(Size(dimensionWidth, dimensionHeight));
if(m_aspectRatio != 0.0f)
updateMapSize();
}
void UIMap::updateMapSize()
{
Rect clippingRect = getClippingRect();
Size mapSize;
if(m_aspectRatio != 0.0f) {
Rect mapRect = clippingRect.expanded(-1);
mapSize = Size(m_aspectRatio*m_zoom, m_zoom);
mapSize.scale(mapRect.size(), Fw::KeepAspectRatio);
} else {
mapSize = clippingRect.expanded(-1).size();
}
m_mapRect.resize(mapSize);
m_mapRect.moveCenter(newRect.center());
m_mapRect.moveCenter(clippingRect.center());
m_mapView->optimizeForSize(mapSize);
if(m_aspectRatio == 0.0f)
updateVisibleDimension();
}

View File

@ -27,6 +27,8 @@
#include <framework/ui/uiwidget.h>
#include <otclient/core/tile.h>
#include <otclient/core/mapview.h>
class UIMap : public UIWidget
{
public:
@ -35,19 +37,55 @@ public:
void drawSelf();
void zoomIn();
void zoomOut();
void followCreature(const CreaturePtr& creature);
void setCameraPosition(const Position& pos);
bool setZoom(int zoom);
bool zoomIn();
bool zoomOut();
void followCreature(const CreaturePtr& creature) { m_mapView->followCreature(creature); }
void setCameraPosition(const Position& pos) { m_mapView->setCameraPosition(pos); }
void setMaxZoomIn(int maxZoomIn) { m_maxZoomIn = maxZoomIn; }
void setMaxZoomOut(int maxZoomOut) { m_maxZoomOut = maxZoomOut; }
void setMultifloor(bool enable) { m_mapView->setMultifloor(enable); }
void setVisibleDimension(const Size& visibleDimension);
void setViewMode(MapView::ViewMode viewMode) { m_mapView->setViewMode(viewMode); }
void setAutoViewMode(bool enable) { m_mapView->setAutoViewMode(enable); }
void setDrawFlags(Otc::DrawFlags drawFlags) { m_mapView->setDrawFlags(drawFlags); }
void setDrawTexts(bool enable) { m_mapView->setDrawTexts(enable); }
void setDrawMinimapColors(bool enable) { m_mapView->setDrawMinimapColors(enable); }
void setAnimated(bool enable) { m_mapView->setAnimated(enable); }
void setKeepAspectRatio(bool enable);
bool isMultifloor() { return m_mapView->isMultifloor(); }
bool isAutoViewModeEnabled() { return m_mapView->isAutoViewModeEnabled(); }
bool isDrawingTexts() { return m_mapView->isDrawingTexts(); }
bool isDrawingMinimapColors() { return m_mapView->isDrawingMinimapColors(); }
bool isAnimating() { return m_mapView->isAnimating(); }
float isKeepAspectRatioEnabled() { return m_aspectRatio != 0.0f; }
Size getVisibleDimension() { return m_mapView->getVisibleDimension(); }
MapView::ViewMode getViewMode() { return m_mapView->getViewMode(); }
CreaturePtr getFollowingCreature() { return m_mapView->getFollowingCreature(); }
Otc::DrawFlags getDrawFlags() { return m_mapView->getDrawFlags(); }
Position getCameraPosition() { return m_mapView->getCameraPosition(); }
TilePtr getTile(const Point& mousePos);
int getMaxZoomIn() { return m_maxZoomIn; }
int getMaxZoomOut() { return m_maxZoomOut; }
int getZoom() { return m_zoom; }
protected:
virtual void onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode);
virtual void onGeometryChange(const Rect& oldRect, const Rect& newRect);
private:
void updateVisibleDimension();
void updateMapSize();
int m_zoom;
MapViewPtr m_mapView;
Rect m_mapRect;
float m_aspectRatio;
int m_maxZoomIn;
int m_maxZoomOut;
};
#endif