diff --git a/README.md b/README.md index a8dba827..93e80dfc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://secure.travis-ci.org/edubart/otclient.svg?branch=mobile_port)](http://travis-ci.org/edubart/otclient) +[![Build Status](https://secure.travis-ci.org/edubart/otclient.svg?branch=mobile_port)](http://travis-ci.org/edubart/otclient) [![Join the chat at https://gitter.im/edubart/otclient](https://img.shields.io/badge/GITTER-join%20chat-green.svg)](https://gitter.im/edubart/otclient?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ### What is otclient? Otclient is an alternative Tibia client for usage with otserv. It aims to be complete and flexible, diff --git a/modules/client_entergame/entergame.lua b/modules/client_entergame/entergame.lua index 81ab23cf..2356ec41 100644 --- a/modules/client_entergame/entergame.lua +++ b/modules/client_entergame/entergame.lua @@ -78,7 +78,7 @@ local function onCharacterList(protocol, characters, account, otui) if motdEnabled then local lastMotdNumber = g_settings.getNumber("motd") if G.motdNumber and G.motdNumber ~= lastMotdNumber then - g_settings.set("motd", motdNumber) + g_settings.set("motd", G.motdNumber) motdWindow = displayInfoBox(tr('Message of the day'), G.motdMessage) connect(motdWindow, { onOk = function() CharacterList.show() motdWindow = nil end }) CharacterList.hide() diff --git a/modules/corelib/corelib.otmod b/modules/corelib/corelib.otmod index 74ece316..3eda75fe 100644 --- a/modules/corelib/corelib.otmod +++ b/modules/corelib/corelib.otmod @@ -10,6 +10,7 @@ Module dofile 'string' dofile 'table' dofile 'bitwise' + dofile 'struct' dofile 'const' dofile 'util' diff --git a/modules/corelib/struct.lua b/modules/corelib/struct.lua new file mode 100644 index 00000000..2ed134d8 --- /dev/null +++ b/modules/corelib/struct.lua @@ -0,0 +1,173 @@ +Struct = {} + +function Struct.pack(format, ...) + local stream = {} + local vars = {...} + local endianness = true + + for i = 1, format:len() do + local opt = format:sub(i, i) + + if opt == '>' then + endianness = false + elseif opt:find('[bBhHiIlL]') then + local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1 + local val = tonumber(table.remove(vars, 1)) + + if val < 0 then + val = val + 2 ^ (n * 8 - 1) + end + + local bytes = {} + for j = 1, n do + table.insert(bytes, string.char(val % (2 ^ 8))) + val = math.floor(val / (2 ^ 8)) + end + + if not endianness then + table.insert(stream, string.reverse(table.concat(bytes))) + else + table.insert(stream, table.concat(bytes)) + end + elseif opt:find('[fd]') then + local val = tonumber(table.remove(vars, 1)) + local sign = 0 + + if val < 0 then + sign = 1 + val = -val + end + + local mantissa, exponent = math.frexp(val) + if val == 0 then + mantissa = 0 + exponent = 0 + else + mantissa = (mantissa * 2 - 1) * math.ldexp(0.5, (opt == 'd') and 53 or 24) + exponent = exponent + ((opt == 'd') and 1022 or 126) + end + + local bytes = {} + if opt == 'd' then + val = mantissa + for i = 1, 6 do + table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) + val = math.floor(val / (2 ^ 8)) + end + else + table.insert(bytes, string.char(math.floor(mantissa) % (2 ^ 8))) + val = math.floor(mantissa / (2 ^ 8)) + table.insert(bytes, string.char(math.floor(val) % (2 ^ 8))) + val = math.floor(val / (2 ^ 8)) + end + + table.insert(bytes, string.char(math.floor(exponent * ((opt == 'd') and 16 or 128) + val) % (2 ^ 8))) + val = math.floor((exponent * ((opt == 'd') and 16 or 128) + val) / (2 ^ 8)) + table.insert(bytes, string.char(math.floor(sign * 128 + val) % (2 ^ 8))) + val = math.floor((sign * 128 + val) / (2 ^ 8)) + + if not endianness then + table.insert(stream, string.reverse(table.concat(bytes))) + else + table.insert(stream, table.concat(bytes)) + end + elseif opt == 's' then + table.insert(stream, tostring(table.remove(vars, 1))) + table.insert(stream, string.char(0)) + elseif opt == 'c' then + local n = format:sub(i + 1):match('%d+') + local length = tonumber(n) + + if length > 0 then + local str = tostring(table.remove(vars, 1)) + if length - str:len() > 0 then + str = str .. string.rep(' ', length - str:len()) + end + table.insert(stream, str:sub(1, length)) + end + i = i + n:len() + end + end + + return table.concat(stream) +end + +function Struct.unpack(format, stream) + local vars = {} + local iterator = 1 + local endianness = true + + for i = 1, format:len() do + local opt = format:sub(i, i) + + if opt == '>' then + endianness = false + elseif opt:find('[bBhHiIlL]') then + local n = opt:find('[hH]') and 2 or opt:find('[iI]') and 4 or opt:find('[lL]') and 8 or 1 + local signed = opt:lower() == opt + + local val = 0 + for j = 1, n do + local byte = string.byte(stream:sub(iterator, iterator)) + if endianness then + val = val + byte * (2 ^ ((j - 1) * 8)) + else + val = val + byte * (2 ^ ((n - j) * 8)) + end + iterator = iterator + 1 + end + + if signed then + val = val - 2 ^ (n * 8 - 1) + end + + table.insert(vars, val) + elseif opt:find('[fd]') then + local n = (opt == 'd') and 8 or 4 + local x = stream:sub(iterator, iterator + n - 1) + iterator = iterator + n + + if not endianness then + x = string.reverse(x) + end + + local sign = 1 + local mantissa = string.byte(x, (opt == 'd') and 7 or 3) % ((opt == 'd') and 16 or 128) + for i = n - 2, 1, -1 do + mantissa = mantissa * (2 ^ 8) + string.byte(x, i) + end + + if string.byte(x, n) > 127 then + sign = -1 + end + + local exponent = (string.byte(x, n) % 128) * ((opt == 'd') and 16 or 2) + math.floor(string.byte(x, n - 1) / ((opt == 'd') and 16 or 128)) + if exponent == 0 then + table.insert(vars, 0.0) + else + mantissa = (math.ldexp(mantissa, (opt == 'd') and -52 or -23) + 1) * sign + table.insert(vars, math.ldexp(mantissa, exponent - ((opt == 'd') and 1023 or 127))) + end + elseif opt == 's' then + local bytes = {} + for j = iterator, stream:len() do + if stream:sub(j, j) == string.char(0) then + break + end + + table.insert(bytes, stream:sub(j, j)) + end + + local str = table.concat(bytes) + iterator = iterator + str:len() + 1 + table.insert(vars, str) + elseif opt == 'c' then + local n = format:sub(i + 1):match('%d+') + table.insert(vars, stream:sub(iterator, iterator + tonumber(n))) + iterator = iterator + tonumber(n) + i = i + n:len() + end + end + + return unpack(vars) +end diff --git a/modules/corelib/util.lua b/modules/corelib/util.lua index 0691fc88..157c28b0 100644 --- a/modules/corelib/util.lua +++ b/modules/corelib/util.lua @@ -296,6 +296,16 @@ function numbertoboolean(number) end end +function protectedcall(func, ...) + local status, ret = pcall(func, ...) + if status then + return ret + end + + perror(ret) + return false +end + function signalcall(param, ...) if type(param) == 'function' then local status, ret = pcall(param, ...) diff --git a/modules/game_console/console.lua b/modules/game_console/console.lua index 904bf9ea..cf6178ca 100644 --- a/modules/game_console/console.lua +++ b/modules/game_console/console.lua @@ -113,13 +113,10 @@ function init() local tab = consoleTabBar:getCurrentTab() if not tab then return false end - local consoleBuffer = tab.tabPanel:getChildById('consoleBuffer') - if not consoleBuffer then return false end + local selection = tab.tabPanel:getChildById('consoleBuffer').selectionText + if not selection then return false end - local consoleLabel = consoleBuffer:getFocusedChild() - if not consoleLabel or not consoleLabel:hasSelection() then return false end - - g_window.setClipboardText(consoleLabel:getSelection()) + g_window.setClipboardText(selection) return true end @@ -147,6 +144,27 @@ function init() end end +function clearSelection(consoleBuffer) + for _,label in pairs(consoleBuffer:getChildren()) do + label:clearSelection() + end + consoleBuffer.selectionText = nil + consoleBuffer.selection = nil +end + +function selectAll(consoleBuffer) + clearSelection(consoleBuffer) + if consoleBuffer:getChildCount() > 0 then + local text = {} + for _,label in pairs(consoleBuffer:getChildren()) do + label:selectAll() + table.insert(text, label:getSelection()) + end + consoleBuffer.selectionText = table.concat(text, '\n') + consoleBuffer.selection = { first = consoleBuffer:getChildIndex(consoleBuffer:getFirstChild()), last = consoleBuffer:getChildIndex(consoleBuffer:getLastChild()) } + end +end + function toggleChat() if consoleToggleChat:isChecked() then disableChat() @@ -577,12 +595,102 @@ function addTabText(text, speaktype, tab, creatureName) end label.name = creatureName - label.onMouseRelease = function (self, mousePos, mouseButton) + consoleBuffer.onMouseRelease = function(self, mousePos, mouseButton) + processMessageMenu(mousePos, mouseButton, nil, nil, nil, tab) + end + label.onMouseRelease = function(self, mousePos, mouseButton) processMessageMenu(mousePos, mouseButton, creatureName, text, self, tab) end + label.onMousePress = function(self, mousePos, button) + if button == MouseLeftButton then clearSelection(consoleBuffer) end + end + label.onDragEnter = function(self, mousePos) + clearSelection(consoleBuffer) + return true + end + label.onDragLeave = function(self, droppedWidget, mousePos) + local text = {} + for selectionChild = consoleBuffer.selection.first, consoleBuffer.selection.last do + local label = self:getParent():getChildByIndex(selectionChild) + table.insert(text, label:getSelection()) + end + consoleBuffer.selectionText = table.concat(text, '\n') + return true + end + label.onDragMove = function(self, mousePos, mouseMoved) + local parent = self:getParent() + local parentRect = parent:getPaddingRect() + local selfIndex = parent:getChildIndex(self) + local child = parent:getChildByPos(mousePos) + + -- find bonding children + if not child then + if mousePos.y < self:getY() then + for index = selfIndex - 1, 1, -1 do + local label = parent:getChildByIndex(index) + if label:getY() + label:getHeight() > parentRect.y then + if (mousePos.y >= label:getY() and mousePos.y <= label:getY() + label:getHeight()) or index == 1 then + child = label + break + end + else + child = parent:getChildByIndex(index + 1) + break + end + end + elseif mousePos.y > self:getY() + self:getHeight() then + for index = selfIndex + 1, parent:getChildCount(), 1 do + local label = parent:getChildByIndex(index) + if label:getY() < parentRect.y + parentRect.height then + if (mousePos.y >= label:getY() and mousePos.y <= label:getY() + label:getHeight()) or index == parent:getChildCount() then + child = label + break + end + else + child = parent:getChildByIndex(index - 1) + break + end + end + else + child = self + end + end + + if not child then return false end + + local childIndex = parent:getChildIndex(child) + + -- remove old selection + clearSelection(consoleBuffer) + + -- update self selection + local textBegin = self:getTextPos(self:getLastClickPosition()) + local textPos = self:getTextPos(mousePos) + self:setSelection(textBegin, textPos) + + consoleBuffer.selection = { first = math.min(selfIndex, childIndex), last = math.max(selfIndex, childIndex) } + + -- update siblings selection + if child ~= self then + for selectionChild = consoleBuffer.selection.first + 1, consoleBuffer.selection.last - 1 do + parent:getChildByIndex(selectionChild):selectAll() + end + + local textPos = child:getTextPos(mousePos) + if childIndex > selfIndex then + child:setSelection(0, textPos) + else + child:setSelection(string.len(child:getText()), textPos) + end + end + + return true + end if consoleBuffer:getChildCount() > MAX_LINES then - consoleBuffer:getFirstChild():destroy() + local child = consoleBuffer:getFirstChild() + clearSelection(consoleBuffer) + child:destroy() end end @@ -609,8 +717,28 @@ function processChannelTabMenu(tab, mousePos, mouseButton) if consoleTabBar:getCurrentTab() == tab then menu:addOption(tr('Clear Messages'), function() clearChannel(consoleTabBar) end) + menu:addOption(tr('Save Messages'), function() + local panel = consoleTabBar:getTabPanel(tab) + local consoleBuffer = panel:getChildById('consoleBuffer') + local lines = {} + for _,label in pairs(consoleBuffer:getChildren()) do + table.insert(lines, label:getText()) + end + + local filename = channelName .. '.txt' + local filepath = '/' .. filename + + -- extra information at the beginning + table.insert(lines, 1, os.date('\nChannel saved at %a %b %d %H:%M:%S %Y')) + + if g_resources.fileExists(filepath) then + table.insert(lines, 1, protectedcall(g_resources.readFileContents, filepath) or '') + end + + g_resources.writeFileContents(filepath, table.concat(lines, '\n')) + modules.game_textmessage.displayStatusMessage(tr('Channel appended to %s', filename)) + end) end - --menu:addOption(tr('Save Messages'), function() --[[TODO]] end) menu:display(mousePos) end @@ -644,12 +772,15 @@ function processMessageMenu(mousePos, mouseButton, creatureName, text, label, ta menu:addOption(tr('Copy name'), function () g_window.setClipboardText(creatureName) end) end - if label:hasSelection() then - menu:addOption(tr('Copy'), function() g_window.setClipboardText(label:getSelection()) end, '(Ctrl+C)') + local selection = tab.tabPanel:getChildById('consoleBuffer').selectionText + if selection and #selection > 0 then + menu:addOption(tr('Copy'), function() g_window.setClipboardText(selection) end, '(Ctrl+C)') end - menu:addOption(tr('Copy message'), function() g_window.setClipboardText(text) end) - menu:addOption(tr('Select all'), function() label:selectAll() end) - if tab.violations then + if text then + menu:addOption(tr('Copy message'), function() g_window.setClipboardText(text) end) + end + menu:addOption(tr('Select all'), function() selectAll(tab.tabPanel:getChildById('consoleBuffer')) end) + if tab.violations and creatureName then menu:addSeparator() menu:addOption(tr('Process') .. ' ' .. creatureName, function() processViolation(creatureName, text) end) menu:addOption(tr('Remove') .. ' ' .. creatureName, function() g_game.closeRuleViolation(creatureName) end) diff --git a/modules/game_console/console.otui b/modules/game_console/console.otui index 346df542..c946f88d 100644 --- a/modules/game_console/console.otui +++ b/modules/game_console/console.otui @@ -10,6 +10,9 @@ ConsoleLabel < UITextEdit change-cursor-image: false cursor-visible: false editable: false + draggable: true + selectable: false + focusable: false ConsolePhantomLabel < UILabel font: verdana-11px-antialised diff --git a/src/client/creature.cpp b/src/client/creature.cpp index 6e6bb63c..68a97d90 100644 --- a/src/client/creature.cpp +++ b/src/client/creature.cpp @@ -200,29 +200,24 @@ void Creature::drawOutfit(const Rect& destRect, bool resize) else exactSize = g_things.rawGetThingType(m_outfit.getAuxId(), m_outfit.getCategory())->getExactSize(); + int frameSize; + if(!resize) + frameSize = std::max(exactSize * 0.75f, 2 * Otc::TILE_PIXELS * 0.75f); + else if(!(frameSize = exactSize)) + return; + if(g_graphics.canUseFBO()) { const FrameBufferPtr& outfitBuffer = g_framebuffers.getTemporaryFrameBuffer(); - outfitBuffer->resize(Size(2*Otc::TILE_PIXELS, 2*Otc::TILE_PIXELS)); + outfitBuffer->resize(Size(frameSize, frameSize)); outfitBuffer->bind(); g_painter->setAlphaWriting(true); g_painter->clear(Color::alpha); - internalDrawOutfit(Point(Otc::TILE_PIXELS,Otc::TILE_PIXELS) + getDisplacement(), 1, false, true, Otc::South); + internalDrawOutfit(Point(frameSize - Otc::TILE_PIXELS, frameSize - Otc::TILE_PIXELS) + getDisplacement(), 1, false, true, Otc::South); outfitBuffer->release(); - - Rect srcRect; - if(resize) - srcRect.resize(exactSize, exactSize); - else - srcRect.resize(2*Otc::TILE_PIXELS*0.75f, 2*Otc::TILE_PIXELS*0.75f); - srcRect.moveBottomRight(Point(2*Otc::TILE_PIXELS - 1, 2*Otc::TILE_PIXELS - 1)); - outfitBuffer->draw(destRect, srcRect); + outfitBuffer->draw(destRect, Rect(0,0,frameSize,frameSize)); } else { - float scaleFactor; - if(resize) - scaleFactor = destRect.width() / (float)exactSize; - else - scaleFactor = destRect.width() / (float)(2*Otc::TILE_PIXELS*0.75f); - Point dest = destRect.bottomRight() - (Point(Otc::TILE_PIXELS,Otc::TILE_PIXELS) - getDisplacement())*scaleFactor; + float scaleFactor = destRect.width() / (float)frameSize; + Point dest = destRect.bottomRight() - (Point(Otc::TILE_PIXELS,Otc::TILE_PIXELS) - getDisplacement()) * scaleFactor; internalDrawOutfit(dest, scaleFactor, false, true, Otc::South); } } diff --git a/src/client/map.cpp b/src/client/map.cpp index dc061ee8..343b0c2d 100644 --- a/src/client/map.cpp +++ b/src/client/map.cpp @@ -394,6 +394,14 @@ void Map::setZoneColor(tileflags_t zone, const Color& color) m_zoneColors[zone] = color; } +Color Map::getZoneColor(tileflags_t flag) +{ + auto it = m_zoneColors.find(flag); + if(it == m_zoneColors.end()) + return Color::alpha; + return it->second; +} + void Map::setForceShowAnimations(bool force) { if(force) { diff --git a/src/client/map.h b/src/client/map.h index f25c9749..8dca1134 100644 --- a/src/client/map.h +++ b/src/client/map.h @@ -196,7 +196,7 @@ public: void setZoneOpacity(float opacity) { m_zoneOpacity = opacity; } float getZoneOpacity() { return m_zoneOpacity; } - Color getZoneColor(tileflags_t flag) { return m_zoneColors[flag]; } + Color getZoneColor(tileflags_t flag); tileflags_t getZoneFlags() { return (tileflags_t)m_zoneFlags; } bool showZones() { return m_zoneFlags != 0; } bool showZone(tileflags_t zone) { return (m_zoneFlags & zone) == zone; } @@ -257,7 +257,7 @@ private: uint8 m_animationFlags; uint32 m_zoneFlags; - std::array m_zoneColors; + std::map m_zoneColors; float m_zoneOpacity; Light m_light; @@ -272,5 +272,3 @@ private: extern Map g_map; #endif - -/* vim: set ts=4 sw=4 et: */ diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index 58004ad5..304909a7 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -333,6 +333,7 @@ bool Minimap::loadOtmm(const std::string& fileName) uint blockSize = MMBLOCK_SIZE * MMBLOCK_SIZE * sizeof(MinimapTile); std::vector compressBuffer(compressBound(blockSize)); + std::vector decompressBuffer(blockSize); while(true) { Position pos; @@ -340,17 +341,19 @@ bool Minimap::loadOtmm(const std::string& fileName) pos.y = fin->getU16(); pos.z = fin->getU8(); - // end of file - if(!pos.isValid()) + // end of file or file is corrupted + if(!pos.isValid() || pos.z >= Otc::MAX_Z+1) break; MinimapBlock& block = getBlock(pos); ulong len = fin->getU16(); ulong destLen = blockSize; fin->read(compressBuffer.data(), len); - int ret = uncompress((uchar*)&block.getTiles(), &destLen, compressBuffer.data(), len); - assert(ret == Z_OK); - assert(destLen == blockSize); + int ret = uncompress(decompressBuffer.data(), &destLen, compressBuffer.data(), len); + if(ret != Z_OK || destLen != blockSize) + break; + + memcpy((uchar*)&block.getTiles(), decompressBuffer.data(), blockSize); block.mustUpdate(); block.justSaw(); } diff --git a/src/client/protocolgamesend.cpp b/src/client/protocolgamesend.cpp index c3bbed03..5eb6a97d 100644 --- a/src/client/protocolgamesend.cpp +++ b/src/client/protocolgamesend.cpp @@ -583,10 +583,8 @@ void ProtocolGame::sendChangeFightModes(Otc::FightModes fightMode, Otc::ChaseMod msg->addU8(fightMode); msg->addU8(chaseMode); msg->addU8(safeFight ? 0x01: 0x00); - if(g_game.getFeature(Otc::GamePVPMode)) msg->addU8(pvpMode); - send(msg); } @@ -595,7 +593,8 @@ void ProtocolGame::sendAttack(uint creatureId, uint seq) OutputMessagePtr msg(new OutputMessage); msg->addU8(Proto::ClientAttack); msg->addU32(creatureId); - msg->addU32(seq); + if(g_game.getFeature(Otc::GameAttackSeq)) + msg->addU32(seq); send(msg); } @@ -604,7 +603,8 @@ void ProtocolGame::sendFollow(uint creatureId, uint seq) OutputMessagePtr msg(new OutputMessage); msg->addU8(Proto::ClientFollow); msg->addU32(creatureId); - msg->addU32(seq); + if(g_game.getFeature(Otc::GameAttackSeq)) + msg->addU32(seq); send(msg); } @@ -652,10 +652,8 @@ void ProtocolGame::sendShareExperience(bool active) OutputMessagePtr msg(new OutputMessage); msg->addU8(Proto::ClientShareExperience); msg->addU8(active ? 0x01 : 0x00); - if(g_game.getClientVersion() < 910) msg->addU8(0); - send(msg); } diff --git a/src/client/thingtype.cpp b/src/client/thingtype.cpp index fad20257..41fbf9d7 100644 --- a/src/client/thingtype.cpp +++ b/src/client/thingtype.cpp @@ -244,12 +244,12 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS m_attribs.set(attr, market); break; } - case ThingAttrUsable: case ThingAttrElevation: { m_elevation = fin->getU16(); m_attribs.set(attr, m_elevation); break; } + case ThingAttrUsable: case ThingAttrGround: case ThingAttrWritable: case ThingAttrWritableOnce: diff --git a/src/framework/CMakeLists.txt b/src/framework/CMakeLists.txt index 01b1fab4..4154bb32 100644 --- a/src/framework/CMakeLists.txt +++ b/src/framework/CMakeLists.txt @@ -174,7 +174,7 @@ set(CMAKE_CXX_FLAGS_COMPILESPEED "-O0") set(CMAKE_CXX_FLAGS_DEBUG "-O0 -g") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O1 -g -fno-omit-frame-pointer") set(CMAKE_CXX_FLAGS_RELEASE "-O2") -set(CMAKE_CXX_FLAGS_PERFORMANCE "-Ofast -mmmx -msse -msse2") +set(CMAKE_CXX_FLAGS_PERFORMANCE "-Ofast -march=native") if(USE_LTO) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fwhole-program -flto") diff --git a/src/framework/graphics/ogl/painterogl1.cpp b/src/framework/graphics/ogl/painterogl1.cpp index 7223e4c9..f47e5353 100644 --- a/src/framework/graphics/ogl/painterogl1.cpp +++ b/src/framework/graphics/ogl/painterogl1.cpp @@ -135,6 +135,12 @@ void PainterOGL1::drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode) #endif } +void PainterOGL1::drawFillCoords(CoordsBuffer& coordsBuffer) +{ + setTexture(nullptr); + drawCoords(coordsBuffer); +} + void PainterOGL1::drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture) { if(texture->isEmpty()) diff --git a/src/framework/graphics/ogl/painterogl1.h b/src/framework/graphics/ogl/painterogl1.h index 8799fb51..66c5dfae 100644 --- a/src/framework/graphics/ogl/painterogl1.h +++ b/src/framework/graphics/ogl/painterogl1.h @@ -50,6 +50,7 @@ public: void refreshState(); void drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode = Triangles); + void drawFillCoords(CoordsBuffer& coordsBuffer); void drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture); void drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src); void drawUpsideDownTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src); diff --git a/src/framework/graphics/ogl/painterogl2.cpp b/src/framework/graphics/ogl/painterogl2.cpp index fa4caa51..9e6ab4c5 100644 --- a/src/framework/graphics/ogl/painterogl2.cpp +++ b/src/framework/graphics/ogl/painterogl2.cpp @@ -120,6 +120,13 @@ void PainterOGL2::drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode) PainterShaderProgram::enableAttributeArray(PainterShaderProgram::TEXCOORD_ATTR); } +void PainterOGL2::drawFillCoords(CoordsBuffer& coordsBuffer) +{ + setDrawProgram(m_shaderProgram ? m_shaderProgram : m_drawSolidColorProgram.get()); + setTexture(nullptr); + drawCoords(coordsBuffer); +} + void PainterOGL2::drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture) { if(texture && texture->isEmpty()) diff --git a/src/framework/graphics/ogl/painterogl2.h b/src/framework/graphics/ogl/painterogl2.h index 2b02474e..f6d40582 100644 --- a/src/framework/graphics/ogl/painterogl2.h +++ b/src/framework/graphics/ogl/painterogl2.h @@ -41,6 +41,7 @@ public: void unbind(); void drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode = Triangles); + void drawFillCoords(CoordsBuffer& coordsBuffer); void drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture); void drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src); void drawUpsideDownTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src); diff --git a/src/framework/graphics/painter.h b/src/framework/graphics/painter.h index 1c710cdf..c8145aa4 100644 --- a/src/framework/graphics/painter.h +++ b/src/framework/graphics/painter.h @@ -62,6 +62,7 @@ public: virtual void clear(const Color& color) = 0; virtual void drawCoords(CoordsBuffer& coordsBuffer, DrawMode drawMode = Triangles) = 0; + virtual void drawFillCoords(CoordsBuffer& coordsBuffer) = 0; virtual void drawTextureCoords(CoordsBuffer& coordsBuffer, const TexturePtr& texture) = 0; virtual void drawTexturedRect(const Rect& dest, const TexturePtr& texture, const Rect& src) = 0; void drawTexturedRect(const Rect& dest, const TexturePtr& texture) { drawTexturedRect(dest, texture, Rect(Point(0,0), texture->getSize())); } diff --git a/src/framework/ui/uitextedit.cpp b/src/framework/ui/uitextedit.cpp index 2c5aceaa..b694ac08 100644 --- a/src/framework/ui/uitextedit.cpp +++ b/src/framework/ui/uitextedit.cpp @@ -49,6 +49,9 @@ UITextEdit::UITextEdit() m_updatesEnabled = true; m_selectionColor = Color::white; m_selectionBackgroundColor = Color::black; + m_glyphsTextCoordsBuffer.enableHardwareCaching(); + m_glyphsSelectCoordsBuffer.enableHardwareCaching(); + m_glyphsMustRecache = true; blinkCursor(); } @@ -62,38 +65,36 @@ void UITextEdit::drawSelf(Fw::DrawPane drawPane) drawImage(m_rect); drawIcon(m_rect); - //TODO: text rendering could be much optimized by using vertex buffer or caching the render into a texture - int textLength = m_text.length(); const TexturePtr& texture = m_font->getTexture(); if(!texture) return; - if(hasSelection()) { - if(m_color != Color::alpha) { - g_painter->setColor(m_color); - for(int i=0;idrawTexturedRect(m_glyphsCoords[i], texture, m_glyphsTexCoords[i]); - } + bool glyphsMustRecache = m_glyphsMustRecache; + if(glyphsMustRecache) + m_glyphsMustRecache = false; - for(int i=m_selectionStart;isetColor(m_selectionBackgroundColor); - g_painter->drawFilledRect(m_glyphsCoords[i]); - g_painter->setColor(m_selectionColor); - g_painter->drawTexturedRect(m_glyphsCoords[i], texture, m_glyphsTexCoords[i]); + if(m_color != Color::alpha) { + if(glyphsMustRecache) { + m_glyphsTextCoordsBuffer.clear(); + for(int i=0;isetColor(m_color); - for(int i=m_selectionEnd;idrawTexturedRect(m_glyphsCoords[i], texture, m_glyphsTexCoords[i]); - } - } else if(m_color != Color::alpha) { g_painter->setColor(m_color); - for(int i=0;idrawTexturedRect(m_glyphsCoords[i], texture, m_glyphsTexCoords[i]); + g_painter->drawTextureCoords(m_glyphsTextCoordsBuffer, texture); } + if(hasSelection()) { + if(glyphsMustRecache) { + m_glyphsSelectCoordsBuffer.clear(); + for(int i=m_selectionStart;isetColor(m_selectionBackgroundColor); + g_painter->drawFillCoords(m_glyphsSelectCoordsBuffer); + g_painter->setColor(m_selectionColor); + g_painter->drawTextureCoords(m_glyphsSelectCoordsBuffer, texture); + } // render cursor if(isExplicitlyEnabled() && m_cursorVisible && m_cursorInRange && isActive() && m_cursorPos >= 0) { @@ -136,6 +137,9 @@ void UITextEdit::update(bool focusCursor) if(m_rect.isEmpty()) return; + // recache coords buffers + recacheGlyphs(); + // map glyphs positions Size textBoxSize; const std::vector& glyphsPositions = m_font->calculateGlyphsPositions(text, m_textAlign, &textBoxSize); @@ -360,6 +364,7 @@ void UITextEdit::setSelection(int start, int end) m_selectionStart = stdext::clamp(start, 0, (int)m_text.length()); m_selectionEnd = stdext::clamp(end, 0, (int)m_text.length()); + recacheGlyphs(); } void UITextEdit::setTextHidden(bool hidden) @@ -654,7 +659,7 @@ void UITextEdit::onFocusChange(bool focused, Fw::FocusReason reason) else blinkCursor(); update(true); - } else + } else if(m_selectable) clearSelection(); UIWidget::onFocusChange(focused, reason); } @@ -796,7 +801,6 @@ bool UITextEdit::onMousePress(const Point& mousePos, Fw::MouseButton button) } return true; } - return false; } @@ -807,6 +811,9 @@ bool UITextEdit::onMouseRelease(const Point& mousePos, Fw::MouseButton button) bool UITextEdit::onMouseMove(const Point& mousePos, const Point& mouseMoved) { + if(UIWidget::onMouseMove(mousePos, mouseMoved)) + return true; + if(m_selectable && isPressed()) { int pos = getTextPos(mousePos); if(pos >= 0) { @@ -815,7 +822,7 @@ bool UITextEdit::onMouseMove(const Point& mousePos, const Point& mouseMoved) } return true; } - return UIWidget::onMouseMove(mousePos, mouseMoved); + return false; } bool UITextEdit::onDoubleClick(const Point& mousePos) diff --git a/src/framework/ui/uitextedit.h b/src/framework/ui/uitextedit.h index 2b9bb93b..79508bb4 100644 --- a/src/framework/ui/uitextedit.h +++ b/src/framework/ui/uitextedit.h @@ -108,6 +108,7 @@ protected: private: void disableUpdates() { m_updatesEnabled = false; } void enableUpdates() { m_updatesEnabled = true; } + void recacheGlyphs() { m_glyphsMustRecache = true; } Rect m_drawArea; int m_cursorPos; @@ -137,6 +138,10 @@ private: std::vector m_glyphsCoords; std::vector m_glyphsTexCoords; + + CoordsBuffer m_glyphsTextCoordsBuffer; + CoordsBuffer m_glyphsSelectCoordsBuffer; + bool m_glyphsMustRecache; }; #endif diff --git a/vc12/otclient.vcxproj b/vc12/otclient.vcxproj index e9ab5357..ad7f6e22 100644 --- a/vc12/otclient.vcxproj +++ b/vc12/otclient.vcxproj @@ -66,19 +66,19 @@ - D:\otclient-msvc13-libs\libogg-1.3.1\include;D:\otclient-msvc13-libs\libvorbis-1.3.3\include;D:\otclient-msvc13-libs\physfs-2.0.3\include;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;D:\otclient-msvc13-libs\zlib-1.2.5\include;D:\otclient-msvc13-libs\OpenAL\include\AL;D:\otclient-msvc13-libs\glew-1.10.0\include;D:\otclient-msvc13-libs\LuaJIT-2.0.2\include;D:\otclient-msvc13-libs\boost_1_55_0\include;D:\otclient\src;$(IncludePath) + D:\otclient-msvc13-libs\libogg-1.3.1\include;D:\otclient-msvc13-libs\libvorbis-1.3.3\include;D:\otclient-msvc13-libs\physfs-2.0.3\include;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;D:\otclient-msvc13-libs\zlib-1.2.5\include;D:\otclient-msvc13-libs\OpenAL\include\AL;D:\otclient-msvc13-libs\glew-1.10.0\include;D:\otclient-msvc13-libs\LuaJIT-2.0.2\include;D:\otclient-msvc13-libs\boost_1_55_0\include;D:\otclient\src;..\src;$(IncludePath) D:\otclient-msvc13-libs\libogg-1.3.1\lib;D:\otclient-msvc13-libs\libvorbis-1.3.3\lib;D:\otclient-msvc13-libs\physfs-2.0.3\lib;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\lib\VC;D:\otclient-msvc13-libs\zlib-1.2.5\lib;D:\otclient-msvc13-libs\OpenAL\lib;D:\otclient-msvc13-libs\LuaJIT-2.0.2\lib;D:\otclient-msvc13-libs\glew-1.10.0\lib;D:\otclient-msvc13-libs\boost_1_55_0\lib;$(LibraryPath) - D:\otclient-msvc13-libs\libogg-1.3.1\include;D:\otclient-msvc13-libs\libvorbis-1.3.3\include;D:\otclient-msvc13-libs\physfs-2.0.3\include;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;D:\otclient-msvc13-libs\zlib-1.2.5\include;D:\otclient-msvc13-libs\OpenAL\include\AL;D:\otclient-msvc13-libs\glew-1.10.0\include;D:\otclient-msvc13-libs\LuaJIT-2.0.2\include;D:\otclient-msvc13-libs\boost_1_55_0\include;D:\otclient\src;$(IncludePath) + D:\otclient-msvc13-libs\libogg-1.3.1\include;D:\otclient-msvc13-libs\libvorbis-1.3.3\include;D:\otclient-msvc13-libs\physfs-2.0.3\include;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;D:\otclient-msvc13-libs\zlib-1.2.5\include;D:\otclient-msvc13-libs\OpenAL\include\AL;D:\otclient-msvc13-libs\glew-1.10.0\include;D:\otclient-msvc13-libs\LuaJIT-2.0.2\include;D:\otclient-msvc13-libs\boost_1_55_0\include;D:\otclient\src;..\src;$(IncludePath) D:\otclient-msvc13-libs\libogg-1.3.1\lib;D:\otclient-msvc13-libs\libvorbis-1.3.3\lib;D:\otclient-msvc13-libs\physfs-2.0.3\lib;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\lib\VC;D:\otclient-msvc13-libs\zlib-1.2.5\lib;D:\otclient-msvc13-libs\OpenAL\lib;D:\otclient-msvc13-libs\LuaJIT-2.0.2\lib;D:\otclient-msvc13-libs\glew-1.10.0\lib;D:\otclient-msvc13-libs\boost_1_55_0\lib;$(LibraryPath) - C:\otclient-msvc13-libs\libogg-1.3.1\include;C:\otclient-msvc13-libs\libvorbis-1.3.3\include;C:\otclient-msvc13-libs\physfs-2.0.3\include;C:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;C:\otclient-msvc13-libs\zlib-1.2.5\include;C:\Users\Pedro\Documents\GitHub\otclient\src;C:\otclient-msvc13-libs\OpenAL\include\AL;C:\otclient-msvc13-libs\glew-1.10.0\include;C:\otclient-msvc13-libs\LuaJIT-2.0.2\include;C:\otclient-msvc13-libs\boost_1_55_0\include;$(IncludePath) + C:\otclient-msvc13-libs\libogg-1.3.1\include;C:\otclient-msvc13-libs\libvorbis-1.3.3\include;C:\otclient-msvc13-libs\physfs-2.0.3\include;C:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;C:\otclient-msvc13-libs\zlib-1.2.5\include;C:\otclient-msvc13-libs\OpenAL\include\AL;C:\otclient-msvc13-libs\glew-1.10.0\include;C:\otclient-msvc13-libs\LuaJIT-2.0.2\include;C:\otclient-msvc13-libs\boost_1_55_0\include;..\src;$(IncludePath) C:\otclient-msvc13-libs\libogg-1.3.1\lib;C:\otclient-msvc13-libs\libvorbis-1.3.3\lib;C:\otclient-msvc13-libs\physfs-2.0.3\lib;C:\otclient-msvc13-libs\OpenSSL-1.0.1e\lib\VC;C:\otclient-msvc13-libs\zlib-1.2.5\lib;C:\otclient-msvc13-libs\OpenAL\lib;C:\otclient-msvc13-libs\LuaJIT-2.0.2\lib;C:\otclient-msvc13-libs\glew-1.10.0\lib;C:\otclient-msvc13-libs\boost_1_55_0\lib;$(LibraryPath) - D:\otclient-msvc13-libs\libogg-1.3.1\include;D:\otclient-msvc13-libs\libvorbis-1.3.3\include;D:\otclient-msvc13-libs\physfs-2.0.3\include;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;D:\otclient-msvc13-libs\zlib-1.2.5\include;D:\otclient-msvc13-libs\OpenAL\include\AL;D:\otclient-msvc13-libs\glew-1.10.0\include;D:\otclient-msvc13-libs\LuaJIT-2.0.2\include;D:\otclient-msvc13-libs\boost_1_55_0\include;D:\otclient\src;$(IncludePath) + D:\otclient-msvc13-libs\libogg-1.3.1\include;D:\otclient-msvc13-libs\libvorbis-1.3.3\include;D:\otclient-msvc13-libs\physfs-2.0.3\include;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\include;D:\otclient-msvc13-libs\zlib-1.2.5\include;D:\otclient-msvc13-libs\OpenAL\include\AL;D:\otclient-msvc13-libs\glew-1.10.0\include;D:\otclient-msvc13-libs\LuaJIT-2.0.2\include;D:\otclient-msvc13-libs\boost_1_55_0\include;D:\otclient\src;..\src;$(IncludePath) D:\otclient-msvc13-libs\libogg-1.3.1\lib;D:\otclient-msvc13-libs\libvorbis-1.3.3\lib;D:\otclient-msvc13-libs\physfs-2.0.3\lib;D:\otclient-msvc13-libs\OpenSSL-1.0.1e\lib\VC;D:\otclient-msvc13-libs\zlib-1.2.5\lib;D:\otclient-msvc13-libs\OpenAL\lib;D:\otclient-msvc13-libs\LuaJIT-2.0.2\lib;D:\otclient-msvc13-libs\glew-1.10.0\lib;D:\otclient-msvc13-libs\boost_1_55_0\lib;$(LibraryPath) @@ -491,4 +491,4 @@ - \ No newline at end of file +